diff --git a/.github/workflows/docs-pr-amend.yml b/.github/workflows/docs-pr-amend.yml index 27b3d8a600..00dc7c211d 100644 --- a/.github/workflows/docs-pr-amend.yml +++ b/.github/workflows/docs-pr-amend.yml @@ -112,15 +112,48 @@ jobs: since: ${{ steps.base.outputs.since }} github-token: ${{ github.token }} - # Commit only the regenerated companions back onto the PR branch. The - # action no-ops when nothing changed (e.g. every changed package was - # skipped by the content-hash gate), which is what makes a re-triggered - # run terminate instead of looping. README.md is never touched. + # Regenerate the global command reference. Unlike the per-package + # READMEs above, this is a deterministic transform of the dispatcher + + # agent command descriptors (no LLM, no API keys), so it reuses the + # docs-autogen CLI the composite already built and needs no GitHub + # Models token. writeCommandReference only writes when the content + # changed, so a re-triggered run regenerates nothing and the amend loop + # still terminates. Advisory like the README step: a docs failure here + # must never block the code PR. + - name: Regenerate command reference + continue-on-error: true + working-directory: ts + run: node tools/docsAutogen/bin/docs-autogen.cjs --command-reference + + # Regenerate the interactive Action Browser: a self-contained HTML + # catalog (plus its raw JSON) of every agent action, parameter, example + # phrasing, and system command. Like the command reference it is a + # static transform of the bundled agent manifests/schemas/grammar (no + # LLM, no API keys). Its tool is separate from docs-autogen, so build it + # here - tsc -b is incremental, so deps the composite already built are + # no-ops. Advisory: a docs failure must never block the code PR. + - name: Regenerate action browser + continue-on-error: true + working-directory: ts + run: | + pnpm --filter '@typeagent/action-browser...' build + node tools/actionBrowser/dist/cli.js --json + + # Commit the regenerated companions, command reference, and action + # browser back onto the PR branch. The commit no-ops when nothing + # changed (e.g. every changed package was skipped by the content-hash + # gate and the generated docs were already current), which is what makes + # a re-triggered run terminate instead of looping. README.md is never + # touched. - name: Commit regenerated docs uses: stefanzweifel/git-auto-commit-action@v5 with: - commit_message: "docs: regenerate README.AUTOGEN.md for changed packages" + commit_message: "docs: regenerate README.AUTOGEN.md, command reference, and action browser" commit_user_name: typeagent-bot commit_user_email: typeagent-bot[bot]@users.noreply.github.com commit_author: "typeagent-bot " - file_pattern: "ts/packages/**/README.AUTOGEN.md" + file_pattern: | + ts/packages/**/README.AUTOGEN.md + ts/docs/overview/command-reference.md + ts/docs/overview/action-browser.html + ts/docs/overview/action-browser.json diff --git a/ts/.prettierignore b/ts/.prettierignore index a7b5882023..a66db064bd 100644 --- a/ts/.prettierignore +++ b/ts/.prettierignore @@ -37,6 +37,12 @@ packages/shell/playwright-report/* # Generated cache files examples/websiteAliases/cache +# Generated Action Browser (committed HTML + JSON catalog, regenerated and +# committed per-PR by docs-pr-amend.yml; not hand-edited, so exclude from +# formatting to avoid churn with the generator's own output). +docs/overview/action-browser.html +docs/overview/action-browser.json + # VS Studio Files .vs/** diff --git a/ts/docs/.gitignore b/ts/docs/.gitignore index 1b608962ea..681ba1ebce 100644 --- a/ts/docs/.gitignore +++ b/ts/docs/.gitignore @@ -20,6 +20,8 @@ log.txt /agents/* !/agents/index.md -# --- Generated interactive Action Browser (built by tools/actionBrowser) --- -/overview/action-browser.html -/overview/action-browser.json +# --- Interactive Action Browser (built by tools/actionBrowser) --- +# Intentionally NOT ignored: docs-pr-amend.yml regenerates the generated HTML +# + JSON and commits them onto the PR branch so the catalog is reviewable in +# the same PR that changed the agents. The docs-site deploy builds its own +# copy separately. diff --git a/ts/docs/overview/action-browser.html b/ts/docs/overview/action-browser.html new file mode 100644 index 0000000000..db38b02809 --- /dev/null +++ b/ts/docs/overview/action-browser.html @@ -0,0 +1,599 @@ + + + + + + +TypeAgent Action Browser + + + +
+
+

🧭 TypeAgent Action Browser

+32 agents Β· 553 actions Β· 325 system commands Β· generated 2026-07-17 18:06:54 UTC +
+
+
+ + +
+ + +
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/ts/docs/overview/action-browser.json b/ts/docs/overview/action-browser.json new file mode 100644 index 0000000000..eeaa43b177 --- /dev/null +++ b/ts/docs/overview/action-browser.json @@ -0,0 +1,14881 @@ +{ + "generatedAt": "2026-07-17T18:06:54.320Z", + "agents": [ + { + "name": "browser", + "category": "Web & Knowledge", + "emoji": "🌐", + "description": "Agent that allows you control an existing browser window", + "schemas": [ + { + "schemaName": "browser", + "description": "Browser agent that allows you control browser windows and perform actions such as web browsing (opening, closing, reading pages), opening a new tab, closing a tab, scrolling, zooming and navigating to a specific URL.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "captureScreenshot", + "description": "", + "parameters": [], + "phrasings": [ + "screenshot", + "capture screen", + "take screenshot" + ] + }, + { + "actionName": "changeSearchProvider", + "description": "change the default search provider", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the search provider to switch to" + } + ], + "phrasings": [] + }, + { + "actionName": "changeTab", + "description": "Make another tab the active tab", + "parameters": [ + { + "name": "tabDescription", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "tabIndex", + "type": "number", + "optional": true, + "description": "The numerical index referred to by the description if applicable. (i.e. first = 1, second = 2, etc.)" + } + ], + "phrasings": [ + "next tab", + "go to tab {tabDescription}", + "switch to tab {tabDescription}" + ] + }, + { + "actionName": "closeAllWebPages", + "description": "Close all web page views", + "parameters": [], + "phrasings": [ + "tabs", + "pages" + ] + }, + { + "actionName": "closeWebPage", + "description": "Close the current web site view", + "parameters": [], + "phrasings": [ + "close tab", + "close page" + ] + }, + { + "actionName": "createInferredFlow", + "description": "Create WebFlows from previously inferred actions. Used after @browser actions infer to select which actions to create.", + "parameters": [ + { + "name": "selection", + "type": "string", + "optional": false, + "description": "The indices of the inferred actions to create (1-based), or \"all\" to create all" + } + ], + "phrasings": [ + "all flows" + ] + }, + { + "actionName": "executeAdHocScript", + "description": "Execute an ad-hoc WebFlow script. The script must be a valid async function using the WebFlow browser API (browser.click, browser.enterText, etc.). Used by reasoning agents to perform browser interactions without a saved WebFlow.", + "parameters": [ + { + "name": "script", + "type": "string", + "optional": false, + "description": "The JavaScript source for an async function: async function execute(browser, params) { ... }" + }, + { + "name": "params", + "type": "string", + "optional": true, + "description": "JSON-encoded parameters to pass to the script" + }, + { + "name": "timeout", + "type": "number", + "optional": true, + "description": "Execution timeout in milliseconds (default: 120000)" + } + ], + "phrasings": [] + }, + { + "actionName": "followLinkByPosition", + "description": "", + "parameters": [ + { + "name": "position", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "openInNewTab", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "followLinkByText", + "description": "follow a link on the page to open a related page or article Example: user: Open the Haiti link agent: { \"actionName\": \"followLinkByText\", \"parameters\": { \"keywords\": \"Haiti\" } }", + "parameters": [ + { + "name": "keywords", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "openInNewTab", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "getWebsiteStats", + "description": "Get statistics about imported website data", + "parameters": [ + { + "name": "groupBy", + "type": "\"domain\" | \"pageType\" | \"source\"", + "optional": true, + "description": "Group stats by domain, pageType, or source" + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Limit number of groups returned" + } + ], + "phrasings": [] + }, + { + "actionName": "goBack", + "description": "", + "parameters": [], + "phrasings": [ + "back" + ] + }, + { + "actionName": "goForward", + "description": "", + "parameters": [], + "phrasings": [ + "forward" + ] + }, + { + "actionName": "openSearchResult", + "description": "Open a specific search result from previous search", + "parameters": [ + { + "name": "position", + "type": "number", + "optional": true, + "description": "Position/index of the search result (1-based)" + }, + { + "name": "title", + "type": "string", + "optional": true, + "description": "Name or title of the search result to open" + }, + { + "name": "url", + "type": "string", + "optional": true, + "description": "URL of the search result to open (if user specifies exact URL)" + }, + { + "name": "openInNewTab", + "type": "boolean", + "optional": true, + "description": "Open in new tab (default: false)" + } + ], + "phrasings": [] + }, + { + "actionName": "openWebPage", + "description": "show/open/display web page in the current view.", + "parameters": [ + { + "name": "site", + "type": "\"paleobiodb\" | \"crossword\" | \"commerce\" | \"montage\" | \"markdown\" | \"turtleGraphics\" | \"annotationsLibrary\" | \"knowledgeLibrary\" | …", + "optional": false, + "description": "Name/Description/search terms of the site to open" + }, + { + "name": "tab", + "type": "\"new\" | \"current\" | \"existing\"", + "optional": true, + "description": "Enum indicating if the page to open in the new tab or the current tab. Default value is \"current\"" + } + ], + "phrasings": [ + "visit {site}", + "browse to {site}", + "navigate to {site}", + "go to {site}", + "open {site}" + ] + }, + { + "actionName": "readPageContent", + "description": "Read the web page contents aloud", + "parameters": [], + "phrasings": [ + "page" + ] + }, + { + "actionName": "reloadPage", + "description": "", + "parameters": [], + "phrasings": [ + "refresh page", + "reload page" + ] + }, + { + "actionName": "scrollDown", + "description": "", + "parameters": [], + "phrasings": [ + "down" + ] + }, + { + "actionName": "scrollUp", + "description": "", + "parameters": [], + "phrasings": [ + "up" + ] + }, + { + "actionName": "searchImageAction", + "description": "Searches (finds) for images on the internet to show the user if the user asks doesn't specify a quantity, randomly select anywhere between 3 and 10 images", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "searchTerm", + "type": "string", + "optional": false, + "description": "the search term for the image(s) to find" + }, + { + "name": "numImages", + "type": "number", + "optional": false, + "description": "the number of images to show the user" + } + ], + "phrasings": [] + }, + { + "actionName": "stopReadPageContent", + "description": "", + "parameters": [], + "phrasings": [ + "stop reading" + ] + }, + { + "actionName": "zoomIn", + "description": "", + "parameters": [], + "phrasings": [ + "in" + ] + }, + { + "actionName": "zoomOut", + "description": "", + "parameters": [], + "phrasings": [ + "out" + ] + }, + { + "actionName": "zoomReset", + "description": "", + "parameters": [], + "phrasings": [ + "reset zoom" + ] + } + ] + }, + { + "schemaName": "browser.actionDiscovery", + "description": "This helps users to inspect a page and find the possible actions they can take on it.", + "defaultEnabled": false, + "transient": true, + "actions": [ + { + "actionName": "createInferredFlows", + "description": "", + "parameters": [ + { + "name": "selectedIndices", + "type": "number[]", + "optional": false, + "description": "" + }, + { + "name": "inferredActions", + "type": "{ … }[]", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "createWebFlowFromRecording", + "description": "", + "parameters": [ + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "actionDescription", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "recordedSteps", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "existingActionNames", + "type": "string[]", + "optional": true, + "description": "" + }, + { + "name": "startUrl", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "screenshots", + "type": "string[]", + "optional": true, + "description": "" + }, + { + "name": "fragments", + "type": "{ … }[]", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "deleteWebFlow", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "detectPageActions", + "description": "", + "parameters": [ + { + "name": "registerAgent", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "getAllWebFlows", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "getWebFlowsForDomain", + "description": "", + "parameters": [ + { + "name": "domain", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "inferActions", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "registerPageDynamicAgent", + "description": "", + "parameters": [ + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "summarizePage", + "description": "", + "parameters": [], + "phrasings": [] + } + ] + }, + { + "schemaName": "browser.external", + "description": "Actions to control open, close and switching external browser windows and tabs, add bookmarks and open websites based on history and bookmarks.", + "defaultEnabled": false, + "transient": true, + "actions": [ + { + "actionName": "addToBookmarks", + "description": "", + "parameters": [ + { + "name": "url", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "closeTab", + "description": "This closes a tab in an existing browser window. IMPORTANT: This does NOT close browser programs.", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "closeWindow", + "description": "", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "openFromBookmarks", + "description": "", + "parameters": [ + { + "name": "keywords", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "openFromHistory", + "description": "", + "parameters": [ + { + "name": "keywords", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "startDate", + "type": "{ displayText, timestamp }", + "optional": true, + "description": "" + }, + { + "name": "endDate", + "type": "{ displayText, timestamp }", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "openTab", + "description": "This opens a new tab in an existing browser window. IMPORTANT: This does NOT launch new browser windows.", + "parameters": [ + { + "name": "url", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "query", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "switchToTabByPosition", + "description": "", + "parameters": [ + { + "name": "position", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "switchToTabByText", + "description": "Switch to an open tab, based on the tab's title Example: user: Switch to the Haiti tab agent: { \"actionName\": \"switchToTabByText\", \"parameters\": { \"keywords\": \"Haiti\" } }", + "parameters": [ + { + "name": "keywords", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "browser.lookupAndAnswer", + "description": "Actions to lookup information from the web and answer user queries.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "lookupAndAnswerInternet", + "description": "The user request is a question about general knowledge that can be found from the internet. (e.g. \"what is the current price of Microsoft stock?\") look up for contemporary internet information including sports scores, news events, or current commerce offerings, use the lookups parameter to request a lookup of the information on the user's behalf; the assistant will generate a response based on the lookup results Lookup *facts* you don't know or if your facts are out of date. E.g. stock prices, time sensitive data, etc the search strings to look up on the user's behalf should be specific enough to return the correct information it is recommended to include the same entities as in the user request", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "internetLookups", + "type": "string[]", + "optional": false, + "description": "the internet search terms to use" + }, + { + "name": "sites", + "type": "string[]", + "optional": true, + "description": "specific sites to look up in." + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "browser.webFlows", + "description": "Manage and execute reusable browser automation flows. List, delete, and edit scope of saved WebFlows.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "deleteWebFlow", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "editWebFlow", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "script", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "editWebFlowScope", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "scopeType", + "type": "\"site\" | \"global\"", + "optional": false, + "description": "" + }, + { + "name": "domains", + "type": "string[]", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "generateWebFlow", + "description": "Generate a reusable WebFlow script from a saved trace", + "parameters": [ + { + "name": "traceId", + "type": "string", + "optional": false, + "description": "Trace ID (from a previous goal-driven task) to generate a script from" + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional name for the generated flow" + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "Optional description override" + } + ], + "phrasings": [] + }, + { + "actionName": "generateWebFlowFromRecording", + "description": "Generate a WebFlow from recorded user interactions", + "parameters": [ + { + "name": "description", + "type": "string", + "optional": false, + "description": "Description of what the recording does" + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional name for the generated flow" + } + ], + "phrasings": [] + }, + { + "actionName": "listWebFlows", + "description": "", + "parameters": [ + { + "name": "scope", + "type": "\"site\" | \"global\" | \"all\"", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "startGoalDrivenTask", + "description": "Goal-driven mode: describe what you want and a reasoning model completes it", + "parameters": [ + { + "name": "goal", + "type": "string", + "optional": false, + "description": "The goal to achieve (e.g. \"search for wireless headphones and add the cheapest to cart\")" + }, + { + "name": "startUrl", + "type": "string", + "optional": true, + "description": "Optional starting URL; uses current page if not specified" + }, + { + "name": "maxSteps", + "type": "number", + "optional": true, + "description": "Maximum reasoning steps (default 30)" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "calendar", + "category": "Productivity", + "emoji": "πŸ“…", + "description": "Agent integration with MS Graph's calendar", + "schemas": [ + { + "schemaName": "calendar", + "description": "Calendar agent that keeps track of important dates and events. Use it to schedule appointments, set reminders, and organize activities.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addParticipant", + "description": "Add a participant to an event Examples: \"add John to the meeting\", \"invite Sarah to lunch\"", + "parameters": [ + { + "name": "description", + "type": "string", + "optional": false, + "description": "Which event to add to (required) - plain string" + }, + { + "name": "participant", + "type": "string", + "optional": false, + "description": "Who to add (required) - plain string" + } + ], + "phrasings": [ + "include {participant} in {description}" + ] + }, + { + "actionName": "findEvents", + "description": "Find events on the calendar Examples: \"find my meetings today\", \"show meetings with Bob\"", + "parameters": [ + { + "name": "date", + "type": "string", + "optional": true, + "description": "When to search (optional, defaults to future events) Use the user's EXACT words like \"today\", \"tomorrow\", \"this week\" - do NOT convert to ISO date" + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "What type of event to find (optional, finds all if not specified) - plain string" + }, + { + "name": "participant", + "type": "string", + "optional": true, + "description": "Find events with a specific participant (optional) - plain string" + } + ], + "phrasings": [ + "what events do I have with {participant} on {date} about {description}", + "show me meetings about {description} scheduled for {date}", + "find all events on {date} that include {participant}" + ] + }, + { + "actionName": "findThisWeeksEvents", + "description": "Find all events happening this week Examples: \"what do I have this week\", \"show my week\"", + "parameters": [], + "phrasings": [ + "find what I have scheduled this week", + "show me all my events for this week starting {startDay}", + "what's happening this week" + ] + }, + { + "actionName": "findTodaysEvents", + "description": "Find all events happening today Examples: \"what do I have today\", \"show me today's schedule\"", + "parameters": [], + "phrasings": [ + "what I have scheduled for today", + "show me all my appointments and meetings for {date}", + "what's on my calendar {date}" + ] + }, + { + "actionName": "removeEvent", + "description": "Remove an event from the calendar Examples: \"delete my meeting\", \"remove the dentist appointment\", \"cancel lunch with Bob\" Behavior based on number of matches: - 1 match: prompts for confirmation - 2-5 matches: prompts with list to select which event to delete - More than 5 matches: returns error asking user to be more specific", + "parameters": [ + { + "name": "description", + "type": "string", + "optional": false, + "description": "What event to remove (required) - plain string describing the event" + }, + { + "name": "date", + "type": "string", + "optional": true, + "description": "Optional date filter to narrow down which event to remove Use the user's EXACT words like \"today\", \"tomorrow\", \"this week\" - do NOT convert to ISO date" + } + ], + "phrasings": [] + }, + { + "actionName": "scheduleEvent", + "description": "Schedule a new event on the calendar Examples: \"schedule a meeting tomorrow at 2pm\", \"add dentist appointment on Friday at 3pm\"", + "parameters": [ + { + "name": "description", + "type": "string", + "optional": false, + "description": "What the event is about (required) - plain string, not an entity type" + }, + { + "name": "date", + "type": "string", + "optional": false, + "description": "When the event occurs (required) - use the user's EXACT words like \"today\", \"tomorrow\", \"Friday\", \"next week\" Do NOT convert to ISO date format - the handler will parse temporal expressions" + }, + { + "name": "time", + "type": "string", + "optional": true, + "description": "What time the event starts (optional, defaults to all-day if not specified) Use the user's EXACT words like \"2pm\", \"3:30\", \"noon\" - do NOT convert formats" + }, + { + "name": "location", + "type": "string", + "optional": true, + "description": "Where the event takes place (optional) - plain string, not an entity type" + }, + { + "name": "participant", + "type": "string", + "optional": true, + "description": "Who else should attend (optional, single participant) - plain string, not an entity type" + } + ], + "phrasings": [ + "book {description} with {participant} at {location} {date} at {time}", + "set up {description} on {date} at {time}", + "schedule a {description} for {date} at {time} in {location} with {participant}" + ] + } + ] + } + ] + }, + { + "name": "chat", + "category": "Communication", + "emoji": "πŸ’¬", + "description": "Agent for conversation and information lookup", + "schemas": [ + { + "schemaName": "chat", + "description": "Chat agent that helps you with general conversation and answering questions by looking up answers in past conversations or the internet.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "generateResponse", + "description": "this is the way to handle requests for known information that is not stored in application memory or conversation memory, such as facts, definitions, explanations, captioning, or other information that can be generated without a lookup if the user request is a known phrase that is unrelated to the context, use this action to generate an explanation for the known phrase. this action is never used when the request is for private information from past conversations including private events, plans, projects in progress, and other items from discussions with team members or the assistant, unless the information is present in the chat history", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request from the user" + }, + { + "name": "generatedText", + "type": "string", + "optional": false, + "description": "the generated text to show the user; this should be a complete response to the user's request" + }, + { + "name": "userRequestEntities", + "type": "{ … }[]", + "optional": false, + "description": "ALL the actions and entities present in the text of the user's request including attachments" + }, + { + "name": "generatedTextEntities", + "type": "{ … }[]", + "optional": false, + "description": "ALL the actions and entities present in the generated text" + }, + { + "name": "relatedFiles", + "type": "string[]", + "optional": true, + "description": "The file names of any attachments" + } + ], + "phrasings": [] + }, + { + "actionName": "showImageFile", + "description": "", + "parameters": [ + { + "name": "files", + "type": "string[]", + "optional": false, + "description": "file entities." + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "code", + "category": "Development & Automation", + "emoji": "βš›οΈ", + "description": "Agent for VSCode integration", + "schemas": [ + { + "schemaName": "code", + "description": "Code agent helps you with productivity in using an editor and performing actions like creating files, editor customization, writing code, intelligent code suggestions, real-time error detection, debugging, and source control tasks etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "changeColorScheme", + "description": "Change the VS Code editor's color theme. Example: User: change my vscode color theme to Monokai Agent: { actionName: \"changeColorScheme\", parameters: { theme: \"Monokai\" } }", + "parameters": [ + { + "name": "theme", + "type": "\"Default Light+\" | \"Default Dark+\" | \"Monokai\" | \"Monokai Dimmed\" | \"Solarized Dark\" | \"Solarized Light\" | \"Quiet Light\" | \"Red\" | …", + "optional": false, + "description": "The VS Code color theme name, e.g. \"Monokai\", \"Solarized Dark\", \"Dark+\"." + } + ], + "phrasings": [] + }, + { + "actionName": "changeEditorLayout", + "description": "Change the editor layout to single, double or three column layout", + "parameters": [ + { + "name": "columnCount", + "type": "\"single\" | \"double\" | \"three\"", + "optional": true, + "description": "e.g., \"single\", \"double\", \"three\" column editor layout. Fill only if specified by the user" + } + ], + "phrasings": [] + }, + { + "actionName": "getActiveEditor", + "description": "── Read / introspection actions ──────────────────────────────────────────── These return the current VS Code editor state to the caller and modify nothing. They are meant to be invoked by the reasoning agent (via discover_actions/execute_action) to inspect what the user is looking at. Results are returned as JSON in the action's display output. Get a coarse snapshot of the active editor: file path, language, dirty state, cursor position, selection range, visible range, workspace folders, open editor count, and diagnostic counts. Does NOT include file or selection text.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "getDiagnostics", + "description": "Get the diagnostics (errors, warnings, info, hints) for a file. Defaults to the active file when fileName is omitted.", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "Workspace-relative path or file name. Omit to use the active file." + } + ], + "phrasings": [] + }, + { + "actionName": "getFileContent", + "description": "Read the contents of a workspace file. Optionally restrict to a line range (0-based, inclusive). The file must be inside an open workspace folder.", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": false, + "description": "Workspace-relative path or file name to read." + }, + { + "name": "startLine", + "type": "number", + "optional": true, + "description": "Optional 0-based start line (inclusive)." + }, + { + "name": "endLine", + "type": "number", + "optional": true, + "description": "Optional 0-based end line (inclusive)." + } + ], + "phrasings": [] + }, + { + "actionName": "getSelection", + "description": "Get the text currently selected in the active editor, along with its range. Returns an empty selection marker when nothing is selected.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "getWorkspaceChanges", + "description": "Get a summary of pending source-control (git) changes in the workspace: per-repository branch, working-tree changes, and staged changes.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "launchVSCode", + "description": "Launch or Start VSCode", + "parameters": [ + { + "name": "mode", + "type": "\"last\" | \"folder\" | \"workspace\"", + "optional": false, + "description": "" + }, + { + "name": "path", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "listOpenEditors", + "description": "List the open editor tabs across all editor groups (path, label, active, dirty). Does not include file contents.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "newCodeFile", + "description": "Create a new code file (Python, JavaScript, TypeScript, HTML, CSS, JSON, etc.) in the editor. Example: User: create a new typescript file called scratch.ts with a hello world function Agent: { actionName: \"newCodeFile\", parameters: { fileName: \"scratch.ts\", language: \"typeScript\", content: \"function helloWorld() { console.log('Hello, World!'); }\\n\" } }", + "parameters": [ + { + "name": "fileName", + "type": "string | \"untitled\"", + "optional": false, + "description": "The file name; if omitted or empty, \"untitled\" is used." + }, + { + "name": "language", + "type": "\"html\" | \"css\" | \"json\" | \"python\" | \"javaScript\" | \"typeScript\"", + "optional": false, + "description": "The programming language of the file." + }, + { + "name": "content", + "type": "string", + "optional": false, + "description": "Initial file content; empty string for an empty scratch file." + } + ], + "phrasings": [] + }, + { + "actionName": "newMarkdownFile", + "description": "Create a new Markdown (.md) file in the editor. Example: User: create a markdown file with a list of top ten AI papers Agent: { actionName: \"newMarkdownFile\", parameters: { fileName: \"papers.md\", content: \"# Top 10 AI Papers\\n...\" } }", + "parameters": [ + { + "name": "fileName", + "type": "string | \"untitled\"", + "optional": false, + "description": "The file name; if omitted or empty, \"untitled\" is used." + }, + { + "name": "content", + "type": "string", + "optional": false, + "description": "Initial Markdown content; empty string for an empty scratch file." + } + ], + "phrasings": [] + }, + { + "actionName": "newTextFile", + "description": "Create a new plain text (.txt) file in the editor. Example: User: make a new text file called notes Agent: { actionName: \"newTextFile\", parameters: { fileName: \"notes.txt\", content: \"\" } }", + "parameters": [ + { + "name": "fileName", + "type": "string | \"untitled\"", + "optional": false, + "description": "The file name; if omitted or empty, \"untitled\" is used." + }, + { + "name": "content", + "type": "string", + "optional": false, + "description": "Initial text content; empty string for an empty scratch file." + } + ], + "phrasings": [] + }, + { + "actionName": "splitEditor", + "description": "Split an editor window into multiple panes showing the same file or different files side-by-side. Example: User: split editor to the right Agent: { actionName: \"splitEditor\", parameters: { direction: \"right\" } } Example: User: split the editor with utils.ts Agent: { actionName: \"splitEditor\", parameters: { fileName: \"utils.ts\" } }", + "parameters": [ + { + "name": "direction", + "type": "\"right\" | \"left\" | \"up\" | \"down\"", + "optional": true, + "description": "Direction to split: \"right\", \"left\", \"up\", \"down\". Only include if user specifies direction." + }, + { + "name": "editorPosition", + "type": "\"first\" | \"last\" | \"active\" | number", + "optional": true, + "description": "Which editor to split by position: \"first\" (leftmost), \"last\" (rightmost), \"active\" (current), or a 0-based index." + }, + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "Which editor to split by file name. Extract the file name or pattern from user request." + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "code.code-debug", + "description": "Code agent that helps you debug code including handling actions like showing the debug panel, adding/toggling breakpoints, stepping in/out of code etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "removeAllBreakpoints", + "description": "", + "parameters": [], + "phrasings": [ + "'remove' 'all' 'breakpoints'" + ] + }, + { + "actionName": "removeBreakpoint", + "description": "", + "parameters": [ + { + "name": "line", + "type": "number", + "optional": false, + "description": "Line number where to remove the breakpoint" + }, + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "Optional: target file" + }, + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "Optional: folder relative to workspace" + } + ], + "phrasings": [] + }, + { + "actionName": "setBreakpoint", + "description": "", + "parameters": [ + { + "name": "line", + "type": "number", + "optional": false, + "description": "Line number where to set the breakpoint" + }, + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "Optional: target file" + }, + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "Optional: folder relative to workspace" + } + ], + "phrasings": [] + }, + { + "actionName": "showDebugPanel", + "description": "Show debug panel or window in the editor or code window, if not already visible", + "parameters": [], + "phrasings": [ + "'show' 'debug' 'panel'" + ] + }, + { + "actionName": "showHover", + "description": "Show hover", + "parameters": [], + "phrasings": [ + "'show' 'hover'" + ] + }, + { + "actionName": "startDebugging", + "description": "Start/Continue debugging", + "parameters": [ + { + "name": "configurationName", + "type": "string", + "optional": true, + "description": "Name of the launch configuration to start" + }, + { + "name": "noDebug", + "type": "boolean", + "optional": true, + "description": "Optional: run without debugging" + } + ], + "phrasings": [ + "'debug'", + "'start' 'debugging'" + ] + }, + { + "actionName": "step", + "description": "Step into/out/over", + "parameters": [ + { + "name": "stepType", + "type": "\"into\" | \"out\" | \"over\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "'over'", + "'out'", + "'into'", + "'step in'" + ] + }, + { + "actionName": "stopDebugging", + "description": "Stop debugging", + "parameters": [], + "phrasings": [ + "'stop' 'debugging'" + ] + }, + { + "actionName": "toggleBreakpoint", + "description": "", + "parameters": [ + { + "name": "line", + "type": "number", + "optional": false, + "description": "Line number where to toggle the breakpoint" + }, + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "Optional: target file" + }, + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "Optional: folder relative to workspace" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "code.code-display", + "description": "Code agent that helps you work with display keyboard bindings, Zoom in, Zoom out, do text search and replace, show extensions etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "closeEditor", + "description": "Close the current editor", + "parameters": [], + "phrasings": [ + "'close' 'editor'" + ] + }, + { + "actionName": "fontZoomReset", + "description": "Zoom reset", + "parameters": [], + "phrasings": [ + "'zoom' 'reset'", + "'reset' 'zoom'" + ] + }, + { + "actionName": "openMarkdownPreview", + "description": "Open markdown preview", + "parameters": [], + "phrasings": [ + "'open' 'markdown preview'" + ] + }, + { + "actionName": "openMarkdownPreviewToSide", + "description": "Open markdown preview to the side", + "parameters": [], + "phrasings": [ + "'open' 'markdown preview' 'to' 'side'" + ] + }, + { + "actionName": "openSettings", + "description": "Open settings", + "parameters": [], + "phrasings": [ + "'open' 'settings'" + ] + }, + { + "actionName": "replaceInFiles", + "description": "Replace in files", + "parameters": [], + "phrasings": [ + "'replace in' 'files'" + ] + }, + { + "actionName": "showExplorer", + "description": "Show explorer/show the file explorer", + "parameters": [], + "phrasings": [ + "'show' 'explorer'" + ] + }, + { + "actionName": "showOutputPanel", + "description": "Show output panel", + "parameters": [], + "phrasings": [ + "'show' 'output'" + ] + }, + { + "actionName": "showSearch", + "description": "Show search or replace pane/window in the sidebar to search for text in the files Note that this is not for searching or finding files", + "parameters": [], + "phrasings": [ + "'show' 'search'" + ] + }, + { + "actionName": "showSourceControl", + "description": "Show source control", + "parameters": [], + "phrasings": [ + "'show' 'source control'" + ] + }, + { + "actionName": "toggleSearchDetails", + "description": "Toggle search details", + "parameters": [], + "phrasings": [ + "'toggle' 'search details'" + ] + }, + { + "actionName": "zenMode", + "description": "Code in Zen Mode", + "parameters": [], + "phrasings": [ + "'zen mode'" + ] + }, + { + "actionName": "zoomIn", + "description": "Zoom in the editor", + "parameters": [], + "phrasings": [ + "in'" + ] + }, + { + "actionName": "zoomOut", + "description": "Zoom out the editor", + "parameters": [], + "phrasings": [ + "out'" + ] + } + ] + }, + { + "schemaName": "code.code-editor", + "description": "Code agent for in-editor refactor actions: insert/move cursor, fix code problems, generate code with Copilot, save current/all files.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "createCodeBlock", + "description": "", + "parameters": [ + { + "name": "language", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "docstring", + "type": "string", + "optional": true, + "description": "A short natural language prompt or description of what the code block should do. Used for generating context-aware completions (e.g., via Copilot). Example: \"Loop over x in descending order\"" + }, + { + "name": "declaration", + "type": "string", + "optional": true, + "description": "The starting line of a structured code block, such as a for-loop, if-statement, etc. Example: \"for (let i = 0; i < arr.length; i++) {\"" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "Optional body of the code block, excluding the closing brace. Can be omitted if Copilot or the agent will generate it." + }, + { + "name": "codeSnippet", + "type": "string", + "optional": true, + "description": "A short or one-liner code expression to insert directly or use as a Copilot prompt. Example: \"const total = prices.reduce(...)\" If provided, takes precedence over declaration/body." + }, + { + "name": "isPartial", + "type": "boolean", + "optional": true, + "description": "True if the request is partial or uncertain (e.g., from speech). Used to guide how much the agent or Copilot should infer." + }, + { + "name": "file", + "type": "{ fileName, folderName, folderRelativeTo, createIfNotExists, fallbackToActiveFile }", + "optional": true, + "description": "Optional target file where the code should be inserted. Only emit the file if specified by the user. If omitted, defaults to the active editor." + }, + { + "name": "position", + "type": "{ … }", + "optional": true, + "description": "Position in the file where the code block should be inserted. Defaults to { type: \"atCursor\" }." + } + ], + "phrasings": [] + }, + { + "actionName": "createFile", + "description": "@deprecated Use `NewCodeFileAction`, `NewMarkdownFileAction`, or `NewTextFileAction` instead.", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "Name of the folder to create the file in (e.g., \"src\")" + }, + { + "name": "folderRelativeTo", + "type": "string", + "optional": true, + "description": "Optional: restrict to folders under this path or name" + }, + { + "name": "language", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "untitled", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "openInEditor", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "content", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "overwriteIfExists", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "focusExistingIfOpen", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "createFunction", + "description": "Schema to generate a function", + "parameters": [ + { + "name": "language", + "type": "\"typescript\" | \"python\" | \"javascript\" | string", + "optional": false, + "description": "Programming language of the function (determines syntax rules and formatting)" + }, + { + "name": "functionDeclaration", + "type": "string", + "optional": false, + "description": "The full function declaration or signature line (e.g., \"function foo(x: number): number {\") This must include the opening brace (for JS/TS) or colon (for Python) and match the language's syntax" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "Function implementation body (excluding the closing brace or dedent, if applicable) This should contain valid code that performs the intended task. If left as an empty string (\"\"), the extension will attempt to trigger GitHub Copilot to complete the body. Prefer generating this value if the function behavior is well understood." + }, + { + "name": "docstring", + "type": "string", + "optional": true, + "description": "A one-line docstring or comment that explains what the function does. This should describe the function’s intent based on the declaration and user request. Used to improve readability and may help tools like Copilot complete the function more accurately." + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional: the name of the function. If omitted, may be inferred from the declaration." + }, + { + "name": "args", + "type": "{ … }[]", + "optional": true, + "description": "Optional: an array of function parameters with their names and types (for display or analysis)" + }, + { + "name": "returnType", + "type": "string", + "optional": true, + "description": "Optional: the function's return type (e.g., \"string\", \"void\", \"Promise\")" + }, + { + "name": "isAsync", + "type": "boolean", + "optional": true, + "description": "Optional: whether the function should be marked as async" + }, + { + "name": "file", + "type": "{ fileName, folderName, folderRelativeTo, createIfNotExists, fallbackToActiveFile }", + "optional": true, + "description": "Optional: the target file where the function should be inserted" + }, + { + "name": "position", + "type": "{ … }", + "optional": true, + "description": "Optional: where in the file to insert the function. Defaults to current cursor location if not provided." + } + ], + "phrasings": [] + }, + { + "actionName": "fixCodeProblem", + "description": "schema to fix a problem (diagnostic) in the code. The target can be the first problem, the next one, all, or a specific location/index in the file. Optional hints from the user (e.g. \"fix the second error\") or file scoping may guide the choice. Used when the user requests: \"fix this problem\", \"fix the second error\", \"fix all issues in this file\".", + "parameters": [ + { + "name": "target", + "type": "{ … }", + "optional": false, + "description": "Which problem to fix (e.g., \"first\", \"next\", \"atCursor\", or \"second problem in file\")" + }, + { + "name": "hint", + "type": "string", + "optional": true, + "description": "Optional context hint from the agent (parsed from user request)" + }, + { + "name": "file", + "type": "{ fileName, folderName, folderRelativeTo, createIfNotExists, fallbackToActiveFile }", + "optional": true, + "description": "File scope (defaults to active editor)" + } + ], + "phrasings": [] + }, + { + "actionName": "generateWithCopilot", + "description": "", + "parameters": [ + { + "name": "strategy", + "type": "\"inlineCompletion\" | \"commentToCode\" | \"generateFix\" | \"generateInsideFunction\" | \"suggestAlternatives\"", + "optional": false, + "description": "The generation strategy to use" + }, + { + "name": "position", + "type": "{ … }", + "optional": false, + "description": "Where to generate the code" + }, + { + "name": "language", + "type": "string", + "optional": true, + "description": "Optional language hint" + }, + { + "name": "prompt", + "type": "string", + "optional": true, + "description": "Optional: natural language guidance (if not using inline)" + }, + { + "name": "context", + "type": "{ … }[]", + "optional": true, + "description": "Optional: provide surrounding code or references" + }, + { + "name": "attemptLimit", + "type": "number", + "optional": true, + "description": "Optional: how many suggestions to try (default: 1)" + }, + { + "name": "autoAccept", + "type": "boolean", + "optional": true, + "description": "Optional: whether to auto-accept Copilot's first suggestion" + }, + { + "name": "explanationMode", + "type": "boolean", + "optional": true, + "description": "Optional: let agent explain what Copilot might generate" + } + ], + "phrasings": [] + }, + { + "actionName": "insertComment", + "description": "", + "parameters": [ + { + "name": "text", + "type": "string", + "optional": false, + "description": "The comment content, excluding the comment syntax itself" + }, + { + "name": "language", + "type": "string", + "optional": true, + "description": "Optional, helps format comments (e.g., // vs #)" + }, + { + "name": "commentStyle", + "type": "\"line\" | \"block\"", + "optional": true, + "description": "Comment style (default: \"line\")" + }, + { + "name": "position", + "type": "{ … }", + "optional": false, + "description": "Where to insert the comment" + }, + { + "name": "newlineBefore", + "type": "boolean", + "optional": true, + "description": "Whether to add a blank line before the comment" + }, + { + "name": "newlineAfter", + "type": "boolean", + "optional": true, + "description": "Whether to add a blank line after the comment" + } + ], + "phrasings": [] + }, + { + "actionName": "insertOrDeleteLines", + "description": "Action for inserting or deleting lines in a file.", + "parameters": [ + { + "name": "operation", + "type": "\"insert\" | \"delete\"", + "optional": false, + "description": "Operation to perform: \"insert\": add new empty lines at the target position \"delete\": remove lines starting from the target position" + }, + { + "name": "count", + "type": "number", + "optional": true, + "description": "Number of lines to insert or delete (default: 1)." + }, + { + "name": "position", + "type": "{ … }", + "optional": true, + "description": "Position where the operation should happen. Examples: { type: \"atCursor\" } { type: \"afterLine\", line: 10 } { type: \"atEndOfFile\" }" + }, + { + "name": "file", + "type": "{ fileName, folderName, folderRelativeTo, createIfNotExists, fallbackToActiveFile }", + "optional": true, + "description": "Optional target file (defaults to the active editor)." + }, + { + "name": "force", + "type": "boolean", + "optional": true, + "description": "When deleting: - true: allow deleting non-empty lines (default) - false: only delete if lines are empty/whitespace" + } + ], + "phrasings": [] + }, + { + "actionName": "launchCopilotChat", + "description": "Hand the current TypeAgent conversation (and optional developer-mode captures + a screenshot) to native GitHub Copilot Chat in VS Code so it can diagnose and fix the underlying problem, via `workbench.action.chat.open`. By default it starts a fresh chat session in a new chat editor and submits the request automatically.", + "parameters": [ + { + "name": "query", + "type": "string", + "optional": false, + "description": "The prompt to send to the Copilot Chat input." + }, + { + "name": "mode", + "type": "\"agent\" | \"ask\"", + "optional": true, + "description": "Copilot chat mode. \"agent\" lets Copilot edit the workspace to apply a fix; \"ask\" is conversational only. Defaults to \"agent\"." + }, + { + "name": "isPartialQuery", + "type": "boolean", + "optional": true, + "description": "When true, the query is pre-filled but not auto-submitted so the user can review the attached context first. When false (default for `@copilot fix`), the request is submitted automatically." + }, + { + "name": "attachScreenshot", + "type": "boolean", + "optional": true, + "description": "When true, attach a screenshot of the focused VS Code window as an image (no temp file, no file-permission prompt)." + }, + { + "name": "attachFiles", + "type": "string[]", + "optional": true, + "description": "Absolute paths to files (e.g., conversation.json and dev-capture JSON) to attach to the chat request." + }, + { + "name": "newSession", + "type": "boolean", + "optional": true, + "description": "When true (default), start a fresh chat session for the handoff instead of appending to whatever chat is already open." + }, + { + "name": "newSessionLocation", + "type": "\"view\" | \"editor\" | \"window\"", + "optional": true, + "description": "Where to open the new session (only when newSession is true): \"editor\" (a new chat editor in the editor area, the default), \"view\" (the chat panel/side view), or \"window\" (a separate chat window)." + } + ], + "phrasings": [] + }, + { + "actionName": "moveCursorInFile", + "description": "ACTION: Move the cursor to a specific position within a file (for navigation or editing preparation). This moves the cursor position, NOT split/duplicate the editor view. USE THIS for: \"go to file X\", \"jump to line 50\", \"go to function foo\", \"move cursor to X\" DO NOT USE for: \"split editor\", \"split X\", \"duplicate editor\" (use splitEditor action instead) Examples: - \"go to line 50\" β†’ { target: { type: \"onLine\", line: 50 } } - \"jump to function main\" β†’ { target: { type: \"insideFunction\", name: \"main\" } } - \"go to app.tsx\" β†’ { target: { type: \"inFile\", filePath: \"app.tsx\" } } - \"move cursor to the end of file\" β†’ { target: { type: \"atEndOfFile\" } }", + "parameters": [ + { + "name": "target", + "type": "{ … }", + "optional": false, + "description": "Target position for the cursor. Supports symbolic locations, line-based positions, or file-relative positions." + }, + { + "name": "file", + "type": "{ fileName, folderName, folderRelativeTo, createIfNotExists, fallbackToActiveFile }", + "optional": true, + "description": "Optional file where the cursor should be moved. Defaults to the active editor if not provided." + }, + { + "name": "hint", + "type": "string", + "optional": true, + "description": "Optional user or agent hint to clarify ambiguous positioning. Example: \"after function declaration\", \"before import statements\"." + } + ], + "phrasings": [] + }, + { + "actionName": "saveAllFiles", + "description": "", + "parameters": [ + { + "name": "onlyDirty", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "excludeUntitled", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "logResult", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "saveCurrentFile", + "description": "", + "parameters": [ + { + "name": "showErrorIfNoActiveEditor", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "onlyDirty", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "excludeUntitled", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "code.code-extension", + "description": "Code agent that helps you perform vscode extension actions like show, install, update, enable, disable, uninstall extensions etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "checkExtensionAvailable", + "description": "The action checks/searches if an extension is available based on user requests like is the copilot extension available? or Show me all extensions related to AI", + "parameters": [ + { + "name": "filterByUserQuery", + "type": "string", + "optional": true, + "description": "Free text (e.g. \"copilot\")" + }, + { + "name": "filterByKnownQuery", + "type": "\"@updates\" | \"@recentlyPublished\" | \"@workspaceUnsupported\" | \"@builtin\" | \"@installed\" | \"@popular\" | \"@recommended\" | \"@enabled\" | …", + "optional": true, + "description": "Known query (e.g. \"@installed\")" + }, + { + "name": "filterByCategory", + "type": "\"ai\" | \"azure\" | \"data science\" | \"formatters\" | \"programming languages\" | \"extension packs\" | \"machine learning\" | \"education\" | …", + "optional": true, + "description": "Category filter (e.g. \"@category:ai\")" + } + ], + "phrasings": [] + }, + { + "actionName": "disableExtension", + "description": "", + "parameters": [ + { + "name": "extensionQuery", + "type": "string", + "optional": false, + "description": "e.g. \"prettier\", \"azure functions\"" + }, + { + "name": "promptUser", + "type": "boolean", + "optional": true, + "description": "whether to prompt user for confirmation before disabling, default: true" + }, + { + "name": "autoReload", + "type": "boolean", + "optional": true, + "description": "whether to automatically reload the window after disabling, default: false" + } + ], + "phrasings": [] + }, + { + "actionName": "enableExtension", + "description": "", + "parameters": [ + { + "name": "extensionQuery", + "type": "string", + "optional": false, + "description": "e.g. \"prettier\", \"azure functions\"" + }, + { + "name": "promptUser", + "type": "boolean", + "optional": true, + "description": "whether to prompt user for confirmation before enabling, default: true" + }, + { + "name": "autoReload", + "type": "boolean", + "optional": true, + "description": "whether to automatically reload the window after enabling, default: false" + } + ], + "phrasings": [] + }, + { + "actionName": "installExtension", + "description": "", + "parameters": [ + { + "name": "extensionQuery", + "type": "string", + "optional": false, + "description": "natural language query, e.g. \"copilot\"" + }, + { + "name": "promptUser", + "type": "boolean", + "optional": true, + "description": "whether to prompt user for confirmation before installing, default: true" + }, + { + "name": "autoReload", + "type": "boolean", + "optional": true, + "description": "whether to automatically reload the window after installation, default: false" + } + ], + "phrasings": [] + }, + { + "actionName": "reloadWindow", + "description": "", + "parameters": [], + "phrasings": [ + "'reload' 'window'" + ] + }, + { + "actionName": "showExtensions", + "description": "Show the extensions panel", + "parameters": [], + "phrasings": [ + "'show' 'extensions'" + ] + } + ] + }, + { + "schemaName": "code.code-general", + "description": "Code agent that helps you navigate within an already-open editor: find a file in the workspace, go to a symbol or line, open a new vscode window, show the command palette, open user settings json, etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "gotoFileOrLineOrSymbol", + "description": "Quick option to access a file, alternate way to search a file by name using keyboard shortcuts search by file name, or append : go to line, or @ to go to symbol If the user request is open/show the search pane this action should not be triggered", + "parameters": [ + { + "name": "goto", + "type": "\"file\" | \"line\" | \"symbol\"", + "optional": true, + "description": "" + }, + { + "name": "ref", + "type": "string", + "optional": true, + "description": "file name, line number, or symbol name, don't fill this property if the file name, line number or symbol name is not provided in the user request" + } + ], + "phrasings": [ + "'symbol'", + "'line'", + "'file'" + ] + }, + { + "actionName": "showCommandPalette", + "description": "Show or open the command palette that allows to search and execute commands Trigger this action only when asked to show/open the command palette explicitly", + "parameters": [], + "phrasings": [ + "'show' 'command palette'" + ] + }, + { + "actionName": "showKeyboardShortcuts", + "description": "Show keyboard shortcuts", + "parameters": [], + "phrasings": [ + "'show' 'keyboard shortcuts'" + ] + }, + { + "actionName": "showUserSettings", + "description": "Show user settings", + "parameters": [], + "phrasings": [ + "'show' 'settings'" + ] + } + ] + }, + { + "schemaName": "code.code-vscode-shell", + "description": "Manage TypeAgent Shell conversations (chat tabs in the TypeAgent VS Code Shell extension): create a new conversation, rename the current conversation, or switch to an existing conversation by name.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "deleteConversation", + "description": "Delete a TypeAgent Shell conversation by display name. The current active conversation cannot be deleted. The extension will prompt the user for confirmation before deleting. Example: User: delete the \"design review\" conversation Agent: { actionName: \"deleteConversation\", parameters: { name: \"design review\" } } Example: User: delete a conversation Agent: { actionName: \"deleteConversation\", parameters: {} }", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "The display name of the conversation to delete. If omitted, the user will be shown a picker in the extension UI." + } + ], + "phrasings": [ + "'delete' 'chat'" + ] + }, + { + "actionName": "newConversation", + "description": "Create a brand-new TypeAgent Shell conversation (a new chat tab in the TypeAgent Shell VS Code extension) and switch the current tab to it. Example: User: start a new conversation Agent: { actionName: \"newConversation\", parameters: {} } Example: User: new conversation called \"design review\" Agent: { actionName: \"newConversation\", parameters: { name: \"design review\" } }", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional display name for the new conversation. If omitted, the user will be prompted in the extension UI." + } + ], + "phrasings": [ + "'new' 'chat'" + ] + }, + { + "actionName": "renameConversation", + "description": "Rename the TypeAgent Shell conversation that is currently active in this chat tab. Example: User: rename this conversation to \"vscode shell PR\" Agent: { actionName: \"renameConversation\", parameters: { newName: \"vscode shell PR\" } }", + "parameters": [ + { + "name": "newName", + "type": "string", + "optional": false, + "description": "The new display name to apply to the current conversation." + } + ], + "phrasings": [] + }, + { + "actionName": "switchConversation", + "description": "Switch the current TypeAgent Shell chat tab to a different existing conversation, identified by its display name. If no conversation with that name exists, a new one will be created with that name and the current tab will switch to it. Example: User: switch to the \"design review\" conversation Agent: { actionName: \"switchConversation\", parameters: { name: \"design review\" } } Example: User: switch conversation Agent: { actionName: \"switchConversation\", parameters: {} }", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "The display name of the conversation to switch to. If omitted, the user will be shown a picker in the extension UI. If the named conversation does not exist, a new one will be created." + } + ], + "phrasings": [ + "'switch' 'chat'" + ] + } + ] + }, + { + "schemaName": "code.code-workbench", + "description": "Code agent that helps you perform vscode workbench actions like open a file, close a file etc.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "openInIntegratedTerminal", + "description": "", + "parameters": [ + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "Optional: folder to open terminal in" + }, + { + "name": "commandToExecute", + "type": "string", + "optional": true, + "description": "Optional: command to execute immediately" + }, + { + "name": "commandRiskLevel", + "type": "\"low\" | \"medium\" | \"high\"", + "optional": true, + "description": "Optional: risk of command" + }, + { + "name": "reuseExistingTerminal", + "type": "boolean", + "optional": true, + "description": "Optional: reuse current terminal or open new one, default is true" + } + ], + "phrasings": [ + "'open' 'terminal'" + ] + }, + { + "actionName": "workbenchBuildRelatedTask", + "description": "", + "parameters": [ + { + "name": "task", + "type": "\"build\" | \"rebuild\" | \"clean\"", + "optional": false, + "description": "" + }, + { + "name": "folderName", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "taskSelection", + "type": "string | number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "workbenchCreateFolderFromExplorer", + "description": "", + "parameters": [ + { + "name": "folderName", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "relativeTo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "resolutionHint", + "type": "\"inferFromName\" | \"workspaceRoot\" | \"activeSelection\"", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "workbenchOpenFile", + "description": "", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": false, + "description": "The name of the file to open (e.g., \"main.ts\")" + }, + { + "name": "matchStrategy", + "type": "\"exact\" | \"fuzzy\"", + "optional": true, + "description": "Optional: control how strict the match is (default: \"exact\")" + }, + { + "name": "extensions", + "type": "string[]", + "optional": true, + "description": "Optional: restrict to certain extensions (e.g., [\".ts\", \".js\"])" + }, + { + "name": "includeGenerated", + "type": "boolean", + "optional": true, + "description": "Optional: whether to include files in dist/build/etc (default: false)" + } + ], + "phrasings": [] + }, + { + "actionName": "workbenchOpenFolder", + "description": "", + "parameters": [ + { + "name": "folderName", + "type": "string", + "optional": false, + "description": "Name of the folder to reveal in Explorer" + }, + { + "name": "folderRelativeTo", + "type": "string", + "optional": true, + "description": "Optional: restrict to folders under this path or name" + }, + { + "name": "includeGenerated", + "type": "boolean", + "optional": true, + "description": "Optional: whether to include folders in node_modules, dist, etc. default: false" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "desktop", + "category": "System & Devices", + "emoji": "πŸͺŸ", + "description": "Agent to control the desktop", + "schemas": [ + { + "schemaName": "desktop", + "description": "Desktop agent that allows you to manage the programs running on a computer, change Windows settings, control the desktop environment, and perform actions such as opening a file, closing a window, tiling windows, launching a program, maximizing windows, adjusting display settings, managing privacy settings, and controlling system features.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "AdjustScreenBrightness", + "description": "Adjusts screen brightness (increase or decrease)", + "parameters": [ + { + "name": "brightnessLevel", + "type": "\"increase\" | \"decrease\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "brighten screen", + "dim screen", + "screen dimmer", + "screen brighter", + "decrease brightness", + "increase brightness" + ] + }, + { + "actionName": "AdjustVolume", + "description": "Adjusts system volume up or down by a relative amount", + "parameters": [ + { + "name": "direction", + "type": "\"up\" | \"down\"", + "optional": false, + "description": "" + }, + { + "name": "amount", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "ApplyTheme", + "description": "Apply a Windows desktop theme by name (e.g. \"Captured Motion\", \"Glow\", \"Sunrise\") or .theme file path. Affects the Windows wallpaper / sounds / cursors / window chrome. Example: User: change my windows theme to Glow Agent: { actionName: \"ApplyTheme\", parameters: { filePath: \"Glow\", themeName: \"Glow\" } }", + "parameters": [ + { + "name": "filePath", + "type": "string", + "optional": false, + "description": "The Windows theme name or .theme file path to apply (use \"previous\" to revert)." + }, + { + "name": "themeName", + "type": "string", + "optional": true, + "description": "Optional theme name parroted by the LLM when the user mentions a specific theme; never read by the action β€” exists only to give the model a slot for the name so it doesn't squeeze a VS Code editor theme into `filePath`." + } + ], + "phrasings": [] + }, + { + "actionName": "BluetoothToggle", + "description": "===== Common Settings Actions ===== Toggles Bluetooth radio on or off", + "parameters": [ + { + "name": "enableBluetooth", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "toggle bluetooth", + "disable bluetooth", + "enable bluetooth", + "off bluetooth", + "on bluetooth" + ] + }, + { + "actionName": "CloseProgram", + "description": "Closes a program window on a Windows Desktop", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "exit {program}", + "quit {program}", + "close {program}" + ] + }, + { + "actionName": "ConnectWifi", + "description": "", + "parameters": [ + { + "name": "ssid", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "password", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "CreateDesktop", + "description": "creates a new Windows Desktop", + "parameters": [ + { + "name": "names", + "type": "string[]", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "desktop" + ] + }, + { + "actionName": "Debug", + "description": "Attaches the debugger to the AutoShell process", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "DisconnectWifi", + "description": "Disconnects from the current wifi network", + "parameters": [], + "phrasings": [ + "disconnect wifi" + ] + }, + { + "actionName": "EnableWifi", + "description": "Enables or disables WiFi adapter", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "disable wifi", + "enable wifi", + "wi-fi", + "wifi" + ] + }, + { + "actionName": "LaunchProgram", + "description": "Launches a new program window on a Windows Desktop Example: user: Launch edge agent: { \"actionName\": \"LaunchProgram\", \"parameters\": { \"name\": \"edge\" } }", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "run {program}", + "start {program}", + "open {program}", + "launch {program}" + ] + }, + { + "actionName": "ListThemes", + "description": "Lists all installed Windows themes", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "ListWifiNetworks", + "description": "Lists available WiFi networks", + "parameters": [], + "phrasings": [ + "what wifi networks", + "show wifi networks", + "list wifi networks" + ] + }, + { + "actionName": "Maximize", + "description": "Maximizes a program window on a Windows Desktop", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "make {program} full screen", + "maximize {program}" + ] + }, + { + "actionName": "Minimize", + "description": "Minimizes a program window on a Windows Desktop", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "hide {program}", + "minimize {program}" + ] + }, + { + "actionName": "MoveWindowToDesktop", + "description": "", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + }, + { + "name": "desktopId", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "Mute", + "description": "", + "parameters": [ + { + "name": "on", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "unmute", + "mute" + ] + }, + { + "actionName": "NextDesktop", + "description": "switches to the next Windows Desktop", + "parameters": [], + "phrasings": [ + "next desktop" + ] + }, + { + "actionName": "PinWindow", + "description": "", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "PreviousDesktop", + "description": "switches to the previous Windows Desktop", + "parameters": [], + "phrasings": [ + "previous desktop" + ] + }, + { + "actionName": "RestartService", + "description": "Restarts a Windows service (stops it and starts it again). The target can be identified either by its service name / display name, or by a phrase found in the service's description. Example: User: restart the print spooler service Agent: { actionName: \"RestartService\", parameters: { service: \"Print Spooler\", matchBy: \"name\" } } Example: User: restart the service that manages windows updates Agent: { actionName: \"RestartService\", parameters: { service: \"windows update\", matchBy: \"description\" } }", + "parameters": [ + { + "name": "service", + "type": "string", + "optional": false, + "description": "The Windows service name or display name (e.g. \"Spooler\" or \"Print Spooler\") when matchBy is \"name\"; or a phrase to search for within service descriptions when matchBy is \"description\"." + }, + { + "name": "matchBy", + "type": "\"name\" | \"description\"", + "optional": true, + "description": "How to locate the service. \"name\" (the default) matches the service name or display name; \"description\" searches each service's description text for the provided phrase." + }, + { + "name": "elevate", + "type": "boolean", + "optional": true, + "description": "Internal use only: set to true by the agent after the user has agreed to run the restart with administrator privileges. Never set this from the user's initial request β€” leave it unset so the agent asks for consent first." + } + ], + "phrasings": [] + }, + { + "actionName": "RestoreVolume", + "description": "", + "parameters": [], + "phrasings": [ + "restore volume" + ] + }, + { + "actionName": "SetScreenResolution", + "description": "Change screen resolution", + "parameters": [ + { + "name": "width", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "height", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "refreshRate", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "SetTextSize", + "description": "Changes the text size that appears throughout Windows and your apps", + "parameters": [ + { + "name": "size", + "type": "number", + "optional": false, + "description": "small changes are 5% increments, large changes are 25% increments" + } + ], + "phrasings": [] + }, + { + "actionName": "SetThemeMode", + "description": "Sets the theme mode of the current [windows] desktop", + "parameters": [ + { + "name": "mode", + "type": "\"light\" | \"dark\" | \"toggle\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "light", + "dark", + "light mode", + "dark mode", + "toggle mode" + ] + }, + { + "actionName": "SetWallpaper", + "description": "", + "parameters": [ + { + "name": "filePath", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "url", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "SwitchDesktop", + "description": "", + "parameters": [ + { + "name": "desktopId", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "SwitchTo", + "description": "Sets focus to a program window on a Windows Desktop. Example: User: switch to Chrome Agent: { actionName: \"SwitchTo\", parameters: { name: \"Chrome\" } }", + "parameters": [ + { + "name": "name", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "go to {program}", + "focus {program}", + "switch to {program}" + ] + }, + { + "actionName": "Tile", + "description": "Positions program windows on a program window on a Windows Desktop", + "parameters": [ + { + "name": "leftWindow", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + }, + { + "name": "rightWindow", + "type": "\"chrome\" | \"word\" | \"excel\" | \"powerpoint\" | \"outlook\" | \"edge\" | \"visual studio\" | \"visual studio code\" | … | string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "ToggleAirplaneMode", + "description": "", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off airplane mode", + "on airplane mode", + "disable airplane mode", + "enable airplane mode" + ] + }, + { + "actionName": "ToggleNotifications", + "description": "Shows/hides windows notification center", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "toggle notifications", + "hide notifications", + "show notifications" + ] + }, + { + "actionName": "Volume", + "description": "", + "parameters": [ + { + "name": "targetVolume", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "volume {level}", + "set volume to {level}" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-display", + "description": "Desktop agent for display and screen settings including night light, color temperature, scaling, orientation, and rotation lock.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "AdjustColorTemperature", + "description": "Adjusts the color temperature for Night Light", + "parameters": [ + { + "name": "filterEffect", + "type": "\"reduce\" | \"increase\"", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "cooler colors", + "warmer colors", + "increase blue light", + "reduce blue light", + "adjust color temperature" + ] + }, + { + "actionName": "AdjustScreenOrientation", + "description": "Adjusts screen orientation between portrait and landscape", + "parameters": [ + { + "name": "orientation", + "type": "\"portrait\" | \"landscape\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "portrait", + "landscape", + "orientation to portrait", + "orientation to landscape" + ] + }, + { + "actionName": "DisplayResolutionAndAspectRatio", + "description": "Opens display settings to adjust resolution and aspect ratio", + "parameters": [], + "phrasings": [ + "open display settings", + "adjust resolution", + "aspect ratio", + "resolution" + ] + }, + { + "actionName": "DisplayScaling", + "description": "Sets display scaling percentage (100, 125, 150, 175, 200)", + "parameters": [ + { + "name": "sizeOverride", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "EnableBlueLightFilterSchedule", + "description": "Enables or disables blue light filter schedule (Night Light)", + "parameters": [ + { + "name": "schedule", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "nightLightScheduleDisabled", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off night light", + "on night light", + "blue light filter", + "night light" + ] + }, + { + "actionName": "ListResolutions", + "description": "Lists available display resolutions for the current monitor", + "parameters": [], + "phrasings": [ + "what resolutions", + "show resolutions", + "list resolutions" + ] + }, + { + "actionName": "RotationLock", + "description": "Locks or unlocks screen rotation", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "disable rotation lock", + "enable rotation lock", + "unlock rotation", + "lock rotation" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-input", + "description": "Desktop agent for mouse and touchpad settings including cursor speed, scroll lines, button configuration, pointer precision, and touchpad controls.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "AdjustMousePointerSize", + "description": "Adjusts mouse pointer size", + "parameters": [ + { + "name": "sizeAdjustment", + "type": "\"increase\" | \"decrease\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "smaller cursor", + "bigger cursor", + "smaller", + "bigger", + "decrease pointer size", + "increase pointer size" + ] + }, + { + "actionName": "CursorTrail", + "description": "Enables or disables the mouse cursor trail and sets its length", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + }, + { + "name": "length", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off cursor trail", + "on cursor trail", + "mouse trail", + "cursor trail" + ] + }, + { + "actionName": "EnableTouchPad", + "description": "Enables or disables the touchpad", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off touchpad", + "on touchpad", + "disable touchpad", + "enable touchpad" + ] + }, + { + "actionName": "EnhancePointerPrecision", + "description": "Enables or disables enhanced pointer precision (mouse acceleration)", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off mouse acceleration", + "on mouse acceleration", + "mouse acceleration", + "enhanced pointer precision" + ] + }, + { + "actionName": "MouseCursorSpeed", + "description": "Adjusts mouse cursor speed", + "parameters": [ + { + "name": "speedLevel", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "reduceSpeed", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "mouse speed {level}", + "change mouse sensitivity to {level}", + "adjust mouse speed to {level}", + "set mouse speed to {level}" + ] + }, + { + "actionName": "MousePointerCustomization", + "description": "Customizes mouse pointer color", + "parameters": [ + { + "name": "color", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "style", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "set mouse pointer color to {color}", + "change mouse pointer to {color}", + "customize mouse pointer to {color}" + ] + }, + { + "actionName": "MouseWheelScrollLines", + "description": "Sets the number of lines to scroll per mouse wheel notch", + "parameters": [ + { + "name": "scrollLines", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "scroll {lines} lines", + "set scroll lines to {lines}" + ] + }, + { + "actionName": "SetPrimaryMouseButton", + "description": "Sets the primary mouse button", + "parameters": [ + { + "name": "primaryButton", + "type": "\"left\" | \"right\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "right handed mouse", + "left handed mouse", + "swap mouse buttons", + "right", + "left" + ] + }, + { + "actionName": "ToggleMouseSonar", + "description": "Enables or disables the \"Find my pointer\" sonar ring when pressing Ctrl", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "TouchpadCursorSpeed", + "description": "Adjusts touchpad cursor speed", + "parameters": [ + { + "name": "speed", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "touchpad speed {speed}", + "adjust touchpad sensitivity", + "set touchpad speed to {speed}" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-personalization", + "description": "Desktop agent for personalization settings including transparency effects, title bar colors, and high contrast themes.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "ApplyColorToTitleBar", + "description": "Applies accent color to title bars", + "parameters": [ + { + "name": "enableColor", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "remove title bar color", + "disable title bar color", + "enable title bar color", + "show accent color on title bars", + "apply color to title bar" + ] + }, + { + "actionName": "EnableTransparency", + "description": "Enables or disables transparency effects", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off transparency", + "on transparency", + "disable transparency", + "enable transparency" + ] + }, + { + "actionName": "HighContrastTheme", + "description": "Opens the high contrast accessibility settings page. Only use when the user explicitly asks for high contrast settings.", + "parameters": [], + "phrasings": [ + "use high contrast", + "open high contrast", + "turn on high contrast", + "enable high contrast" + ] + }, + { + "actionName": "SystemThemeMode", + "description": "Sets the system-wide theme mode to light or dark (affects apps, taskbar, and Start menu). Only for light/dark mode, not for named themes.", + "parameters": [ + { + "name": "mode", + "type": "\"light\" | \"dark\"", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "desktop.desktop-power", + "description": "Desktop agent for power management settings including battery saver and power mode configuration.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "BatterySaverActivationLevel", + "description": "Sets the battery saver activation threshold", + "parameters": [ + { + "name": "thresholdValue", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "turn on battery saver at {level}", + "activate battery saver at {level}", + "battery saver at {level}", + "set battery saver to {level}" + ] + }, + { + "actionName": "SetPowerModeOnBattery", + "description": "Sets power mode when on battery", + "parameters": [ + { + "name": "mode", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "power saver", + "best performance", + "set battery power mode to balanced", + "power efficiency", + "performance" + ] + }, + { + "actionName": "SetPowerModePluggedIn", + "description": "Sets power mode when plugged in", + "parameters": [ + { + "name": "powerMode", + "type": "\"bestPerformance\" | \"balanced\" | \"bestPowerEfficiency\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "use best performance", + "power saver mode", + "balanced mode", + "best performance mode", + "set power mode to balanced", + "power efficiency", + "performance" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-privacy", + "description": "Desktop agent for privacy settings to manage microphone, camera, and location access for applications.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "ManageCameraAccess", + "description": "Manages camera access for apps", + "parameters": [ + { + "name": "accessSetting", + "type": "\"allow\" | \"deny\"", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off camera", + "on camera", + "disable camera", + "enable camera", + "deny camera access", + "allow camera access" + ] + }, + { + "actionName": "ManageLocationAccess", + "description": "Manages location access for apps", + "parameters": [ + { + "name": "accessSetting", + "type": "\"allow\" | \"deny\"", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off location", + "on location", + "disable location", + "enable location", + "deny location access", + "allow location access" + ] + }, + { + "actionName": "ManageMicrophoneAccess", + "description": "Manages microphone access for apps", + "parameters": [ + { + "name": "accessSetting", + "type": "\"allow\" | \"deny\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off microphone", + "on microphone", + "disable microphone", + "enable microphone", + "deny microphone access", + "allow microphone access" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-system", + "description": "Desktop agent for system settings including accessibility features, file explorer options, time settings, focus assist, multi-monitor configuration, and other system-level controls.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "AutomaticDSTAdjustment", + "description": "Enables or disables automatic DST adjustment", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "automatically adjust for dst", + "disable automatic dst", + "daylight saving time adjustment", + "automatic dst" + ] + }, + { + "actionName": "AutomaticTimeSettingAction", + "description": "Enables or disables automatic time synchronization", + "parameters": [ + { + "name": "enableAutoTimeSync", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "sync time automatically", + "automatically set time", + "disable automatic time sync", + "enable automatic time sync" + ] + }, + { + "actionName": "EnableFilterKeysAction", + "description": "Enables or disables Filter Keys", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off filter keys", + "on filter keys", + "disable filter keys", + "enable filter keys" + ] + }, + { + "actionName": "EnableGameMode", + "description": "Enables or disables Game Mode", + "parameters": [], + "phrasings": [ + "turn on game mode", + "open game mode", + "enable game mode" + ] + }, + { + "actionName": "EnableMagnifier", + "description": "Enables or disables Magnifier", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off magnifier", + "on magnifier", + "stop magnifier", + "start magnifier", + "disable magnifier", + "enable magnifier" + ] + }, + { + "actionName": "EnableMeteredConnections", + "description": "Enables or disables metered connection settings", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off metered connection", + "on metered connection", + "disable metered connection", + "enable metered connection" + ] + }, + { + "actionName": "EnableNarratorAction", + "description": "Enables or disables Narrator", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off narrator", + "on narrator", + "stop narrator", + "start narrator", + "disable narrator", + "enable narrator" + ] + }, + { + "actionName": "EnableQuietHours", + "description": "Enables or disables Focus Assist (Quiet Hours)", + "parameters": [ + { + "name": "startHour", + "type": "number", + "optional": true, + "description": "" + }, + { + "name": "endHour", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "do not disturb", + "open focus assist", + "turn on quiet hours", + "focus assist", + "quiet hours" + ] + }, + { + "actionName": "EnableStickyKeys", + "description": "Enables or disables Sticky Keys", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "off sticky keys", + "on sticky keys", + "disable sticky keys", + "enable sticky keys" + ] + }, + { + "actionName": "MinimizeWindowsOnMonitorDisconnectAction", + "description": "Minimizes windows when a monitor is disconnected", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "keep windows when monitor disconnects", + "when monitor disconnects", + "on disconnect" + ] + }, + { + "actionName": "MonoAudioToggle", + "description": "Enables or disables mono audio", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "off mono audio", + "on mono audio", + "disable mono audio", + "enable mono audio" + ] + }, + { + "actionName": "RememberWindowLocations", + "description": "Remembers window locations based on monitor configuration", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "forget window locations", + "positions", + "locations" + ] + }, + { + "actionName": "ShowFileExtensions", + "description": "Shows or hides file extensions in File Explorer", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "display file extensions", + "hide file extensions", + "show file extensions" + ] + }, + { + "actionName": "ShowHiddenAndSystemFiles", + "description": "Shows or hides hidden and system files in File Explorer", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "display hidden files", + "hide hidden files", + "system files", + "hidden files" + ] + } + ] + }, + { + "schemaName": "desktop.desktop-taskbar", + "description": "Desktop agent for taskbar settings including auto-hide, alignment, task view, widgets, badges, and system clock options.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "AutoHideTaskbar", + "description": "Auto-hides the taskbar", + "parameters": [ + { + "name": "hideWhenNotUsing", + "type": "boolean", + "optional": false, + "description": "" + }, + { + "name": "alwaysShow", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "show taskbar", + "hide taskbar", + "always show taskbar", + "automatically hide taskbar", + "auto hide taskbar" + ] + }, + { + "actionName": "DisplaySecondsInSystrayClock", + "description": "Shows seconds in the system tray clock", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "disable seconds in clock", + "turn off seconds in clock", + "display seconds in clock", + "hide seconds in clock", + "only hours and minutes", + "seconds in clock" + ] + }, + { + "actionName": "DisplayTaskbarOnAllMonitors", + "description": "Shows taskbar on all monitors", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "hide taskbar from other monitors", + "show taskbar only on main monitor", + "displays", + "monitors" + ] + }, + { + "actionName": "ShowBadgesOnTaskbar", + "description": "Shows or hides badges on taskbar icons", + "parameters": [ + { + "name": "enableBadging", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "disable taskbar badges", + "enable taskbar badges", + "hide taskbar badges", + "show taskbar badges" + ] + }, + { + "actionName": "TaskbarAlignment", + "description": "Sets taskbar alignment (left or center)", + "parameters": [ + { + "name": "alignment", + "type": "\"left\" | \"center\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "left align taskbar", + "left", + "center", + "center taskbar" + ] + }, + { + "actionName": "TaskViewVisibility", + "description": "Shows or hides the Task View button", + "parameters": [ + { + "name": "visibility", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "disable task view", + "enable task view", + "hide task view", + "show task view" + ] + }, + { + "actionName": "ToggleWidgetsButtonVisibility", + "description": "Shows or hides the Widgets button", + "parameters": [ + { + "name": "visibility", + "type": "\"show\" | \"hide\"", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "disable widgets", + "enable widgets", + "hide widgets", + "show widgets" + ] + } + ] + } + ] + }, + { + "name": "discord", + "category": "Communication", + "emoji": "πŸ’¬", + "description": "Discord chat agent β€” send messages, draft messages from intent, list channels, browse channel history, manage server/channel invites.", + "schemas": [ + { + "schemaName": "discord", + "description": "Discord agent. Use for anything involving Discord channels, messages, members, invites, or servers β€” e.g. sending a chat message to a channel, drafting a message from a high-level intent (\"send a message to #general that welcomes everyone\" or \"...to remind everyone about tonight's plans\"), listing channels, fetching recent messages, or creating channel invites.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addThreadMember", + "description": "Add another member to a thread.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread." + }, + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user to add." + } + ], + "phrasings": [ + "bring {user_id} into thread {channel_id}", + "include {user_id} in thread {channel_id}", + "invite {user_id} to thread {channel_id}", + "add user {user_id} to thread {channel_id}" + ] + }, + { + "actionName": "craftMessage", + "description": "Drafts a message via LLM from a high-level intent, then posts it. Use when the user describes what the message should convey (\"that ...\", \"to ...\", \"about ...\", \"reminding ...\", \"asking ...\") instead of supplying literal content. Sample phrases: - \"Send a message to #general that welcomes everyone to the discord\" - \"Send a message to #general to remind everyone about tonight's plans\" - \"Post a message in #announcements about the new release\" - \"Write a message in #random asking who's free tomorrow\" - \"Send a message in #events reminding people to RSVP\"", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "Single channel token (ID, bare name, or \"#name\"). Must not contain spaces β€” anything after the channel belongs in `intent`." + }, + { + "name": "intent", + "type": "string", + "optional": false, + "description": "What the message should convey β€” everything the user said about the message after the channel and connector word." + }, + { + "name": "tts", + "type": "boolean", + "optional": true, + "description": "Whether the message should be sent as text-to-speech." + } + ], + "phrasings": [ + "create a message in {channel_id} that {intent}", + "write a message to {channel_id} that {intent}", + "post a message to {channel_id} that {intent}", + "send a message to {channel_id} that {intent}" + ] + }, + { + "actionName": "createChannelInvite", + "description": "Create a new invite for a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID or name of the channel." + }, + { + "name": "max_age", + "type": "number", + "optional": true, + "description": "Duration of invite in seconds (0 = never expires)." + }, + { + "name": "never_expires", + "type": "boolean", + "optional": true, + "description": "If true, the invite never expires (sets max_age to 0)." + }, + { + "name": "max_uses", + "type": "number", + "optional": true, + "description": "Max number of uses (0 = unlimited)." + }, + { + "name": "temporary", + "type": "boolean", + "optional": true, + "description": "Whether the invite is temporary." + }, + { + "name": "unique", + "type": "boolean", + "optional": true, + "description": "Whether the invite is unique." + } + ], + "phrasings": [ + "I need an invite for channel {channel_id}", + "generate a invite for channel {channel_id}", + "make a invite for {channel_id} that doesn't expire", + "make a invite for channel {channel_id}", + "create a permanent invite for {channel_id}", + "create a invite for {channel_id} that doesn't expire", + "create a invite for channel {channel_id}" + ] + }, + { + "actionName": "createDM", + "description": "Create a new DM channel with a user. Returns a DM channel object. Sample phrases: - \"Can you start a direct message with user ID 123456?\" - \"I'd like to create a DM channel with the user whose ID is 123456.\" - \"Please open a new DM with the user having the ID 123456.\"", + "parameters": [ + { + "name": "recipient_id", + "type": "string", + "optional": false, + "description": "The ID of the recipient user." + } + ], + "phrasings": [ + "message user {recipient_id}", + "DM user {recipient_id}", + "create a DM with user {recipient_id}", + "start a direct message with {recipient_id}" + ] + }, + { + "actionName": "createGroupDM", + "description": "Create a new group DM channel with multiple users. Returns a DM channel object. Sample phrases: - \"Can you start a new group DM with these friends?\" - \"Create a group chat with these people, please.\" - \"I'd like to set up a group DM with my buddies.\"", + "parameters": [ + { + "name": "access_tokens", + "type": "string[]", + "optional": true, + "description": "The access tokens for the users to be added to the group DM." + }, + { + "name": "nicks", + "type": "string", + "optional": true, + "description": "Comma-separated nickname mappings (e.g. \"userId1:nick1,userId2:nick2\")." + } + ], + "phrasings": [ + "make a group DM", + "start a group direct message {access_tokens}", + "set up a group chat {access_tokens}", + "start a group DM" + ] + }, + { + "actionName": "createGuild", + "description": "Creates a new guild. Sample phrases: - \"Can you create a new guild for me called 'Knights of Valor' in the US region with this icon?\" - \"I need a new guild named 'Dragon Slayers' in the EU region. Use this icon for it.\" - \"Please set up a guild called 'Mystic Warriors' in the Asia region and use this icon.\"", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the guild." + }, + { + "name": "region", + "type": "string", + "optional": true, + "description": "The region where the guild will be created." + }, + { + "name": "icon", + "type": "string", + "optional": true, + "description": "The icon for the guild." + } + ], + "phrasings": [ + "make a guild called {name}", + "I need a new guild called {name}", + "set up a guild called {name}", + "create a guild called {name}" + ] + }, + { + "actionName": "createMessage", + "description": "Creates a new message in a specified channel. Sample phrases: - \"Send a message to the general channel saying 'Hello everyone!'\" - \"Post 'Meeting starts at 3 PM' in the announcements channel, please.\" - \"Can you create a new message in the #updates channel with the text 'New features are live!'?\"", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel where the message will be sent." + }, + { + "name": "content", + "type": "string", + "optional": false, + "description": "The content of the message." + }, + { + "name": "nonce", + "type": "string", + "optional": true, + "description": "A unique identifier for the message." + }, + { + "name": "tts", + "type": "boolean", + "optional": true, + "description": "Whether the message should be sent as text-to-speech." + } + ], + "phrasings": [] + }, + { + "actionName": "createWebhook", + "description": "Creates a new webhook in a specified channel. Sample phrases: - \"Can you set up a new webhook in the channel with ID 12345? Name it 'Updates' and use the avatar image from this link.\" - \"Please create a webhook named 'Alerts' in the channel 67890 and use this avatar for it.\" - \"I need a new webhook in channel 54321 called 'Notifications'. Use this avatar image for it.\"", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel where the webhook will be created." + }, + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the webhook." + }, + { + "name": "avatar", + "type": "string", + "optional": true, + "description": "The avatar for the webhook." + } + ], + "phrasings": [ + "add a webhook called {name} to channel {channel_id}", + "I need a webhook called {name} in channel {channel_id}", + "make a webhook called {name} in channel {channel_id}", + "create a webhook called {name} in channel {channel_id}" + ] + }, + { + "actionName": "deleteChannel", + "description": "Delete a channel or close a private message.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel to delete." + } + ], + "phrasings": [ + "destroy channel {channel_id}", + "I want to delete channel {channel_id}", + "remove channel {channel_id}", + "delete channel {channel_id}" + ] + }, + { + "actionName": "deleteChannelPermission", + "description": "Delete a permission overwrite from a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + }, + { + "name": "overwrite_id", + "type": "string", + "optional": false, + "description": "The ID of the overwrite to delete." + } + ], + "phrasings": [ + "clear permission {overwrite_id} from channel {channel_id}", + "revoke permission {overwrite_id} from channel {channel_id}", + "remove permission override {overwrite_id} from channel {channel_id}", + "delete permission {overwrite_id} from channel {channel_id}" + ] + }, + { + "actionName": "deleteInvite", + "description": "Delete an invite. Requires the MANAGE_CHANNELS permission on the channel this invite belongs to, or MANAGE_GUILD to remove any invite across the guild. Sample phrases: - \"Can you delete the invite with code ABC123?\" - \"Please remove the invite link with the code XYZ789.\" - \"I need you to delete the invite that has the code LMN456.\"", + "parameters": [ + { + "name": "invite_code", + "type": "string", + "optional": false, + "description": "The code of the invite to delete." + } + ], + "phrasings": [ + "revoke invite {invite_code}", + "I need you to delete invite {invite_code}", + "remove invite {invite_code}", + "delete invite {invite_code}" + ] + }, + { + "actionName": "editChannelPermissions", + "description": "Edit the permissions of a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + }, + { + "name": "overwrite_id", + "type": "string", + "optional": true, + "description": "The ID of the overwrite (user or role)." + }, + { + "name": "allow", + "type": "string", + "optional": true, + "description": "The allow permission bitfield." + }, + { + "name": "deny", + "type": "string", + "optional": true, + "description": "The deny permission bitfield." + }, + { + "name": "type", + "type": "number", + "optional": true, + "description": "The type of the overwrite (0 = role, 1 = member)." + } + ], + "phrasings": [ + "configure permissions for channel {channel_id}", + "set permissions for channel {channel_id}", + "update permissions for channel {channel_id}", + "edit permissions for channel {channel_id}" + ] + }, + { + "actionName": "executeWebhook", + "description": "Executes a webhook, sending a message to the channel. Sample phrases: - \"Send a message using the webhook with ID 12345 and token abcde. The content should be 'Hello everyone!', and use the username 'Bot' with the avatar URL 'http://example.com/avatar.png'. Make it a TTS message.\" - \"Can you execute the webhook with ID 12345 and token abcde? The message should say 'Hello everyone!', and use the username 'Bot' with the avatar 'http://example.com/avatar.png'. Also, make it a TTS message.\" - \"Please trigger the webhook with ID 12345 and token abcde to send a message saying 'Hello everyone!'. Use 'Bot' as the username and 'http://example.com/avatar.png' as the avatar. Enable TTS for this message.\"", + "parameters": [ + { + "name": "webhook_id", + "type": "string", + "optional": false, + "description": "The ID of the webhook to execute." + }, + { + "name": "webhook_token", + "type": "string", + "optional": false, + "description": "The token for the webhook." + }, + { + "name": "content", + "type": "string", + "optional": true, + "description": "The content of the message." + }, + { + "name": "username", + "type": "string", + "optional": true, + "description": "The username to use for the message." + }, + { + "name": "avatar_url", + "type": "string", + "optional": true, + "description": "The avatar URL to use for the message." + }, + { + "name": "tts", + "type": "boolean", + "optional": true, + "description": "Whether the message should be sent as text-to-speech." + } + ], + "phrasings": [ + "fire webhook {webhook_id} token {webhook_token} with message {content}", + "trigger webhook {webhook_id} token {webhook_token} to send {content}", + "send a message using webhook {webhook_id} token {webhook_token} saying {content}", + "execute webhook {webhook_id} with token {webhook_token} saying {content}" + ] + }, + { + "actionName": "followAnnouncementChannel", + "description": "Follow an announcement channel to receive crossposted messages.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the announcement channel to follow." + }, + { + "name": "webhook_channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel to receive crossposted messages." + } + ], + "phrasings": [ + "crosspost announcement channel {channel_id} to {webhook_channel_id}", + "forward announcements from {channel_id} to {webhook_channel_id}", + "subscribe to announcement channel {channel_id} sending to {webhook_channel_id}", + "follow announcement channel {channel_id} in {webhook_channel_id}" + ] + }, + { + "actionName": "getChannel", + "description": "Get a channel by ID. Returns a channel object.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel to fetch." + } + ], + "phrasings": [ + "look up channel {channel_id}", + "I need channel details for {channel_id}", + "show me channel {channel_id}", + "get details for channel {channel_id}" + ] + }, + { + "actionName": "getChannelInvites", + "description": "Get all invites for a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + } + ], + "phrasings": [ + "what invites are there for channel {channel_id}", + "get invites for channel {channel_id}", + "list invites on channel {channel_id}", + "show channel {channel_id} invites" + ] + }, + { + "actionName": "getChannelMessages", + "description": "Fetches messages from a specified channel. Sample phrases: - \"Can you show me the latest messages from the channel with ID 12345?\" - \"Fetch the last 50 messages from the channel 67890, please.\" - \"Get messages from the channel 54321, but only those sent after yesterday.\"", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel from which to fetch messages." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "The maximum number of messages to return." + }, + { + "name": "before", + "type": "string", + "optional": true, + "description": "Get messages before this message ID." + }, + { + "name": "after", + "type": "string", + "optional": true, + "description": "Get messages after this message ID." + } + ], + "phrasings": [] + }, + { + "actionName": "getCurrentUser", + "description": "Returns the user object of the requester's account. Sample phrases: - \"Can you show me my account details?\" - \"What's my user info?\" - \"Get my profile information, please.\"", + "parameters": [], + "phrasings": [ + "who am I", + "get my profile", + "what's my user info", + "show me my account" + ] + }, + { + "actionName": "getCurrentUserApplicationRoleConnection", + "description": "Returns the application role connection for the user. Sample phrases: - \"Can you show me my role connection for the app with ID 123456?\" - \"What's my application role connection for the app with ID 123456?\" - \"Please get my role connection for the application with ID 123456.\"", + "parameters": [ + { + "name": "application_id", + "type": "string", + "optional": false, + "description": "The ID of the application to fetch the role connection for." + } + ], + "phrasings": [ + "fetch my role connection for app {application_id}", + "get my role connection for application {application_id}", + "what's my role connection for app {application_id}", + "show me my role connection for app {application_id}" + ] + }, + { + "actionName": "getCurrentUserConnections", + "description": "Returns a list of connection objects. Sample phrases: - \"Can you show me my current connections?\" - \"What are my linked accounts right now?\" - \"I'd like to see my active connections, please.\"", + "parameters": [], + "phrasings": [ + "list my connections", + "see my connections", + "what are my linked accounts", + "show me my connections" + ] + }, + { + "actionName": "getCurrentUserGuildMember", + "description": "Returns a guild member object for the current user. Sample phrases: - \"Can you show me my member details for the server with ID 123456?\" - \"Get my guild member info for the server 123456, please.\" - \"I need my member object for the guild with ID 123456.\"", + "parameters": [ + { + "name": "guild_id", + "type": "string", + "optional": false, + "description": "The ID of the guild to fetch the member object from." + } + ], + "phrasings": [ + "what's my role in server {guild_id}", + "I need my member object for guild {guild_id}", + "get my guild member info for server {guild_id}", + "show me my member details for server {guild_id}" + ] + }, + { + "actionName": "getCurrentUserGuilds", + "description": "Returns a list of partial guild objects the current user is a member of. Sample phrases: - \"Can you show me the servers I'm part of?\" - \"List all the guilds I'm currently in, please.\" - \"What are the Discord servers I belong to?\"", + "parameters": [ + { + "name": "before", + "type": "string", + "optional": true, + "description": "Get guilds before this ID." + }, + { + "name": "after", + "type": "string", + "optional": true, + "description": "Get guilds after this ID." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "The maximum number of guilds to return." + }, + { + "name": "with_counts", + "type": "boolean", + "optional": true, + "description": "Whether to include member counts." + } + ], + "phrasings": [ + "what servers I belong to", + "list guilds I'm in", + "show my server list", + "show me servers I'm part of" + ] + }, + { + "actionName": "getGuild", + "description": "Fetches a guild object for a given guild ID. Sample phrases: - \"Can you get the details for the guild with ID 12345?\" - \"Fetch the guild information for guild ID 67890, please.\" - \"I need the guild object for the guild with ID abcdef.\"", + "parameters": [ + { + "name": "guild_id", + "type": "string", + "optional": false, + "description": "The ID of the guild to fetch." + } + ], + "phrasings": [ + "I need guild object for ID {guild_id}", + "show me guild {guild_id}", + "fetch guild info for {guild_id}", + "get details for guild {guild_id}" + ] + }, + { + "actionName": "getInvite", + "description": "Returns an invite object for the given code. Sample phrases: - \"Can you fetch the invite details for the code ABC123?\" - \"I need the invite info for code XYZ789, please.\" - \"Get me the invite object for the code DEF456, and include the counts.\"", + "parameters": [ + { + "name": "invite_code", + "type": "string", + "optional": false, + "description": "The code of the invite to fetch." + }, + { + "name": "with_counts", + "type": "boolean", + "optional": true, + "description": "Whether to include member counts." + }, + { + "name": "guild_scheduled_event_id", + "type": "string", + "optional": true, + "description": "The ID of the scheduled event for the invite." + } + ], + "phrasings": [ + "look up invite {invite_code}", + "I need invite info for code {invite_code}", + "show me invite code {invite_code}", + "get invite details for {invite_code}" + ] + }, + { + "actionName": "getTargetUsers", + "description": "Gets the users allowed to see and accept this invite. Response is a CSV file with the header user_id and each user ID from the original file passed to invite create on its own line. Requires the caller to be the inviter, or have MANAGE_GUILD permission, or have VIEW_AUDIT_LOG permission. Sample phrases: - \"Can you show me the list of users who can accept this invite?\" - \"Get me the users allowed to see this invite, please.\" - \"I need the user IDs for the invite with code [invite.code].\"", + "parameters": [ + { + "name": "invite_code", + "type": "string", + "optional": false, + "description": "The code of the invite to fetch target users for." + } + ], + "phrasings": [ + "who can accept invite {invite_code}", + "I need user IDs for invite {invite_code}", + "get users allowed to see invite {invite_code}", + "show me users who can accept invite {invite_code}" + ] + }, + { + "actionName": "getTargetUsersJobStatus", + "description": "Processing target users from a CSV when creating or updating an invite is done asynchronously. This endpoint allows you to check the status of that job. Requires the caller to be the inviter, or have MANAGE_GUILD permission, or have VIEW_AUDIT_LOG permission. Sample phrases: - \"Can you check the status of the job for the invite with code ABC123?\" - \"What's the current status of the processing job for the invite code ABC123?\" - \"Please tell me the job status for the invite code ABC123.\"", + "parameters": [ + { + "name": "invite_code", + "type": "string", + "optional": false, + "description": "The code of the invite to check the job status for." + } + ], + "phrasings": [ + "is target users processing done for invite {invite_code}", + "tell me job status for invite {invite_code}", + "what's job for invite {invite_code}", + "check job for invite {invite_code}" + ] + }, + { + "actionName": "getThreadMember", + "description": "Get a thread member.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread." + }, + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user." + } + ], + "phrasings": [ + "is {user_id} in thread {channel_id}", + "fetch thread member {user_id} from thread {channel_id}", + "show me member {user_id} in thread {channel_id}", + "get member info for {user_id} in thread {channel_id}" + ] + }, + { + "actionName": "getUser", + "description": "Returns a user object for a given user ID. Sample phrases: - \"Can you fetch the user details for ID 123456?\" - \"I need information on the user with ID 123456.\" - \"Please get me the user object for ID 123456.\"", + "parameters": [ + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user to fetch." + } + ], + "phrasings": [ + "look up user {user_id}", + "I need information on user {user_id}", + "show me user {user_id}", + "get user details for {user_id}" + ] + }, + { + "actionName": "getWebhook", + "description": "Fetches a webhook object for a given webhook ID. Sample phrases: - \"Can you fetch the webhook details for ID 12345?\" - \"I need the webhook information for webhook ID 12345.\" - \"Please get the webhook object for the ID 12345.\"", + "parameters": [ + { + "name": "webhook_id", + "type": "string", + "optional": false, + "description": "The ID of the webhook to fetch." + } + ], + "phrasings": [ + "get webhook {webhook_id}", + "I need webhook object for {webhook_id}", + "show me webhook {webhook_id}", + "get webhook details for {webhook_id}" + ] + }, + { + "actionName": "groupDmAddRecipient", + "description": "Add a recipient to a Group DM.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the group DM channel." + }, + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user to add." + }, + { + "name": "access_token", + "type": "string", + "optional": true, + "description": "The access token of the user." + }, + { + "name": "nick", + "type": "string", + "optional": true, + "description": "The nickname for the user in the group DM." + } + ], + "phrasings": [ + "bring {user_id} into group chat {channel_id}", + "include {user_id} in group DM {channel_id}", + "invite {user_id} to group DM {channel_id}", + "add user {user_id} to group DM {channel_id}" + ] + }, + { + "actionName": "groupDmRemoveRecipient", + "description": "Remove a recipient from a Group DM.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the group DM channel." + }, + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user to remove." + } + ], + "phrasings": [ + "exclude {user_id} from group chat {channel_id}", + "take {user_id} out of group DM {channel_id}", + "kick {user_id} from group DM {channel_id}", + "remove user {user_id} from group DM {channel_id}" + ] + }, + { + "actionName": "joinThread", + "description": "Join a thread.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread to join." + } + ], + "phrasings": [ + "enter thread {channel_id}", + "I want to join thread {channel_id}", + "add me to thread {channel_id}", + "join thread {channel_id}" + ] + }, + { + "actionName": "leaveGuild", + "description": "Leave a guild. Returns a 204 empty response on success. Sample phrases: - \"I want to leave the server with ID 123456789.\" - \"Please remove me from the guild with ID 123456789.\" - \"Can you help me exit the guild? The ID is 123456789.\"", + "parameters": [ + { + "name": "guild_id", + "type": "string", + "optional": false, + "description": "The ID of the guild to leave." + } + ], + "phrasings": [ + "quit server {guild_id}", + "exit guild {guild_id}", + "remove me from guild {guild_id}", + "I want to leave server {guild_id}" + ] + }, + { + "actionName": "leaveThread", + "description": "Leave a thread.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread to leave." + } + ], + "phrasings": [ + "exit thread {channel_id}", + "remove me from thread {channel_id}", + "I want to leave thread {channel_id}", + "leave thread {channel_id}" + ] + }, + { + "actionName": "listChannels", + "description": "List all channels in the current Discord server.", + "parameters": [], + "phrasings": [ + "what channels", + "list channels" + ] + }, + { + "actionName": "listJoinedPrivateArchivedThreads", + "description": "List private archived threads joined by the current user.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + }, + { + "name": "before", + "type": "string", + "optional": true, + "description": "Returns threads before this timestamp." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of threads to return." + } + ], + "phrasings": [ + "what private archived threads have I joined in channel {channel_id}", + "get my joined private archived threads from channel {channel_id}", + "show me private archived threads I'm in for channel {channel_id}", + "list private archived threads I've joined in channel {channel_id}" + ] + }, + { + "actionName": "listPrivateArchivedThreads", + "description": "List private archived threads in a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + }, + { + "name": "before", + "type": "string", + "optional": true, + "description": "Returns threads before this timestamp." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of threads to return." + } + ], + "phrasings": [ + "what private threads are archived in channel {channel_id}", + "get archived private threads from channel {channel_id}", + "show me private archived threads in channel {channel_id}", + "list private archived threads in channel {channel_id}" + ] + }, + { + "actionName": "listPublicArchivedThreads", + "description": "List public archived threads in a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + }, + { + "name": "before", + "type": "string", + "optional": true, + "description": "Returns threads before this timestamp." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of threads to return." + } + ], + "phrasings": [ + "what public threads are archived in channel {channel_id}", + "get archived public threads from channel {channel_id}", + "show me public archived threads in channel {channel_id}", + "list public archived threads in channel {channel_id}" + ] + }, + { + "actionName": "listThreadMembers", + "description": "List members of a thread.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread." + } + ], + "phrasings": [ + "get member list for thread {channel_id}", + "who is in thread {channel_id}", + "show me members of thread {channel_id}", + "list members of thread {channel_id}" + ] + }, + { + "actionName": "modifyChannel", + "description": "Update a channel's settings.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel to modify." + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "The new name for the channel." + }, + { + "name": "topic", + "type": "string", + "optional": true, + "description": "The new topic for the channel." + }, + { + "name": "nsfw", + "type": "boolean", + "optional": true, + "description": "Whether the channel is NSFW." + } + ], + "phrasings": [ + "rename channel {channel_id} to {name}", + "modify channel {channel_id} name to {name}", + "change settings of channel {channel_id}", + "update channel {channel_id}" + ] + }, + { + "actionName": "modifyCurrentUser", + "description": "Modify the requester's user account settings. Returns a user object on success. Sample phrases: - \"Can you update my username and profile picture?\" - \"I'd like to change my username and avatar, please.\" - \"Please modify my account settings with a new username and banner image.\"", + "parameters": [ + { + "name": "username", + "type": "string", + "optional": true, + "description": "The new username for the account." + }, + { + "name": "avatar", + "type": "string", + "optional": true, + "description": "The new avatar for the account." + }, + { + "name": "banner", + "type": "string", + "optional": true, + "description": "The new banner image for the account." + } + ], + "phrasings": [ + "update my account settings", + "set my username to {username}", + "change my profile username {username}", + "update my username to {username}" + ] + }, + { + "actionName": "refreshChannels", + "description": "Refresh the channel cache from the Discord server.", + "parameters": [], + "phrasings": [ + "update channel cache", + "refresh cache" + ] + }, + { + "actionName": "removeThreadMember", + "description": "Remove another member from a thread.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the thread." + }, + { + "name": "user_id", + "type": "string", + "optional": false, + "description": "The ID of the user to remove." + } + ], + "phrasings": [ + "exclude {user_id} from thread {channel_id}", + "take {user_id} out of thread {channel_id}", + "kick {user_id} from thread {channel_id}", + "remove user {user_id} from thread {channel_id}" + ] + }, + { + "actionName": "setGuild", + "description": "Set the default Discord server (guild) for all operations.", + "parameters": [ + { + "name": "guild_id", + "type": "string", + "optional": false, + "description": "The Discord server ID to use as default" + } + ], + "phrasings": [ + "use server {guild_id}", + "my discord server {guild_id}", + "set my discord server {guild_id}" + ] + }, + { + "actionName": "setVoiceChannelStatus", + "description": "Set the status of a voice channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the voice channel." + }, + { + "name": "status", + "type": "string", + "optional": false, + "description": "The new status for the voice channel." + } + ], + "phrasings": [ + "put {status} as status for voice channel {channel_id}", + "change status of voice channel {channel_id} to {status}", + "update voice channel {channel_id} status to {status}", + "set channel {channel_id} status to {status}" + ] + }, + { + "actionName": "startThreadFromMessage", + "description": "Start a thread from an existing message.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel containing the message." + }, + { + "name": "message_id", + "type": "string", + "optional": false, + "description": "The ID of the message to start a thread from." + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "The name of the thread." + } + ], + "phrasings": [ + "make a thread from message {message_id} in channel {channel_id}", + "open a thread on message {message_id} in channel {channel_id} called {name}", + "create a thread called {name} from message {message_id} in channel {channel_id}", + "start a thread from message {message_id} in channel {channel_id} called {name}" + ] + }, + { + "actionName": "startThreadInForumOrMediaChannel", + "description": "Start a thread in a forum or media channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the forum or media channel." + }, + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the thread." + }, + { + "name": "message", + "type": "string", + "optional": true, + "description": "The initial message for the thread." + } + ], + "phrasings": [ + "make a forum post called {name} in channel {channel_id}", + "post a forum thread called {name} in channel {channel_id}", + "create a forum post called {name} in channel {channel_id}", + "start a forum thread called {name} in channel {channel_id}" + ] + }, + { + "actionName": "startThreadWithoutMessage", + "description": "Start a thread without an existing message.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel to start the thread in." + }, + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the thread." + }, + { + "name": "auto_archive_duration", + "type": "number", + "optional": true, + "description": "Duration in minutes to automatically archive the thread." + }, + { + "name": "type", + "type": "number", + "optional": true, + "description": "The type of thread to create." + } + ], + "phrasings": [ + "make a thread called {name} in channel {channel_id}", + "open a thread in channel {channel_id} called {name}", + "create a thread called {name} in channel {channel_id}", + "start a thread called {name} in channel {channel_id}" + ] + }, + { + "actionName": "triggerTypingIndicator", + "description": "Trigger the typing indicator in a channel.", + "parameters": [ + { + "name": "channel_id", + "type": "string", + "optional": false, + "description": "The ID of the channel." + } + ], + "phrasings": [ + "send typing indicator to channel {channel_id}", + "start typing in channel {channel_id}", + "show typing in channel {channel_id}", + "trigger typing indicator in channel {channel_id}" + ] + }, + { + "actionName": "updateCurrentUserApplicationRoleConnection", + "description": "Updates and returns the application role connection for the user. Sample phrases: - \"Can you update my role connection for the app with my new username?\" - \"Please change my application role connection details to reflect my updated platform name and username.\" - \"I need to update my role connection for the app. My platform name is [platform_name] and my username is [platform_username].\"", + "parameters": [ + { + "name": "application_id", + "type": "string", + "optional": true, + "description": "The ID of the application." + }, + { + "name": "platform_name", + "type": "string", + "optional": true, + "description": "The new platform name for the role connection." + }, + { + "name": "platform_username", + "type": "string", + "optional": true, + "description": "The new platform username for the role connection." + }, + { + "name": "metadata", + "type": "string", + "optional": true, + "description": "The metadata for the role connection (JSON string)." + } + ], + "phrasings": [ + "modify my role connection for app {application_id}", + "update my role connection for app {application_id} {platform_name}", + "change my role connection for app {application_id}", + "update my role connection for app {application_id}" + ] + }, + { + "actionName": "updateTargetUsers", + "description": "Updates the users allowed to see and accept this invite. Uploading a file with invalid user IDs will result in a 400 with the invalid IDs described. Requires the caller to be the inviter or have the MANAGE_GUILD permission. Sample phrases: - \"Can you update the invite with these new target users? Here's the file.\" - \"Please refresh the list of users who can access this invite using this file.\" - \"I'd like to change the allowed users for this invite. Uploading the file now.\"", + "parameters": [ + { + "name": "invite_code", + "type": "string", + "optional": false, + "description": "The code of the invite to update target users for." + }, + { + "name": "target_users_file", + "type": "string", + "optional": true, + "description": "The file containing the new target users (file path or URL)." + } + ], + "phrasings": [ + "set target users for invite {invite_code}", + "change allowed users for invite {invite_code}", + "refresh users who can access invite {invite_code}", + "update users for invite {invite_code}" + ] + } + ] + } + ] + }, + { + "name": "dispatcher", + "category": "Core", + "emoji": "πŸ€–", + "description": "", + "schemas": [ + { + "schemaName": "dispatcher", + "description": "", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "unknown", + "description": "Use UnknownAction when all the available actions in the schema is not relevant to the user request", + "parameters": [ + { + "name": "request", + "type": "string", + "optional": false, + "description": "user request that couldn't be matched to any action or schema group to look up from." + }, + { + "name": "reason", + "type": "string", + "optional": false, + "description": "Why none of the available actions fit this request. Name the action(s) that came closest and say what was missing. Be specific: this is used to find gaps in the action schemas." + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "dispatcher.activity", + "description": "Action that manages activity context.", + "defaultEnabled": true, + "transient": true, + "actions": [ + { + "actionName": "exitActivity", + "description": "Exit the current activity.", + "parameters": [], + "phrasings": [] + } + ] + }, + { + "schemaName": "dispatcher.clarify", + "description": "Action that helps you clarify your request.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "clarifyMissingParameter", + "description": "The user request is for an action, but some of the parameter is not specified or cannot be infer from the context. Ask the user for clarification for the missing parameter of an known action. Don't clarify unknown action.", + "parameters": [ + { + "name": "request", + "type": "string", + "optional": false, + "description": "the current understood user request that needs to be clarified" + }, + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "A known actionName to be clarify. Don't clarify \"unknown\" action." + }, + { + "name": "parameterName", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "clarifyingQuestion", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "clarifyMultipleAgentMatches", + "description": "The user request was matched by more than one agent's schema/grammar simultaneously (a cross-agent action collision). List the candidate (schemaName, actionName) pairs so the user can disambiguate. This differs from ClarifyMultiplePossibleActionName, which only lists action names within a single schema.", + "parameters": [ + { + "name": "request", + "type": "string", + "optional": false, + "description": "the current understood user request that needs to be clarified" + }, + { + "name": "candidates", + "type": "{ … }[]", + "optional": false, + "description": "" + }, + { + "name": "clarifyingQuestion", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "clarifyMultiplePossibleActionName", + "description": "Ask the user for clarification for ambiguous request that have multiple possible known action as interpretation. Don't clarify \"unknown\" action.", + "parameters": [ + { + "name": "request", + "type": "string", + "optional": false, + "description": "the current understood user request that needs to be clarified" + }, + { + "name": "possibleActionNames", + "type": "string[]", + "optional": false, + "description": "Known actionNames to be clarify. Don't clarify \"unknown\" action." + }, + { + "name": "clarifyingQuestion", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "clarifyUnresolvedReference", + "description": "The user request is for an action, but parameters are referring to unresolved pronouns or references that is not found in the request or recent chat context.", + "parameters": [ + { + "name": "request", + "type": "string", + "optional": false, + "description": "the current understood user request that needs to be clarified" + }, + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "parameterName", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "reference", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "clarifyingQuestion", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "dispatcher.lookup", + "description": "Action that helps you look up information to answer user questions.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "lookupAndAnswerConversation", + "description": "The user request is a question about previous conversations. (e.g. \"what did we say about the project last week?\") The user expects only the answer, and not an action to be taken.", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request from the user" + }, + { + "name": "question", + "type": "string", + "optional": false, + "description": "The question to get answer for." + }, + { + "name": "conversationLookupFilters", + "type": "{ … }[]", + "optional": false, + "description": "look up for private information from past conversations (i.e. chat history) including private events, plans, projects in progress, attachments, files, file names, and other items from discussions with team members or the assistant, use the conversation lookup filters" + } + ], + "phrasings": [] + }, + { + "actionName": "startLookup", + "description": "The user want to start looking information for a specific source without specifying what to look for. Don't use this action if the request includes what to look for.", + "parameters": [ + { + "name": "lookup", + "type": "{ … }", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "dispatcher.reasoning", + "description": "Action that helps you reason through requests that require multiple steps.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "reasoningAction", + "description": "If the user requests requires multiple action or steps, use the reasoning action.", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "The original user request" + }, + { + "name": "attemptedAction", + "type": "string", + "optional": true, + "description": "JSON-serialized action object β€” agents populate this when redirecting a translated action so the reasoning loop knows what was intended." + }, + { + "name": "contextEntities", + "type": "string", + "optional": true, + "description": "JSON-serialized entities on the stack at the time of redirect." + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "email", + "category": "Productivity", + "emoji": "πŸ“©", + "description": "Agent integration with MS Graph's email", + "schemas": [ + { + "schemaName": "email", + "description": "Email agent helps manage email messages. Use it to send, reply, forward, find messages etc.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "findEmail", + "description": "Type for finding an email message (search for emails)", + "parameters": [ + { + "name": "messageRef", + "type": "{ senders, subject, content, receivedDateTime, srcFolder, destfolder }", + "optional": false, + "description": "Reference to the email message to find" + } + ], + "phrasings": [] + }, + { + "actionName": "forwardEmail", + "description": "Type for forwarding an email", + "parameters": [ + { + "name": "to", + "type": "string[]", + "optional": false, + "description": "Recipients" + }, + { + "name": "cc", + "type": "string[]", + "optional": true, + "description": "Carbon copy recipients (optional)" + }, + { + "name": "bcc", + "type": "string[]", + "optional": true, + "description": "Blind carbon copy recipients (optional)" + }, + { + "name": "additionalMessage", + "type": "string", + "optional": true, + "description": "Additional message (optional), can be plain text or HTML and inferred from the user request" + }, + { + "name": "messageRef", + "type": "{ senders, subject, content, receivedDateTime, srcFolder, destfolder }", + "optional": false, + "description": "Reference to the email message to forward" + } + ], + "phrasings": [] + }, + { + "actionName": "replyEmail", + "description": "Type for replying to an email", + "parameters": [ + { + "name": "body", + "type": "string", + "optional": true, + "description": "Body content of the reply email (optional), can be plain text or HTML, generate the body based on the user input" + }, + { + "name": "cc", + "type": "string[]", + "optional": true, + "description": "Carbon copy recipients (optional)" + }, + { + "name": "bcc", + "type": "string[]", + "optional": true, + "description": "Blind carbon copy recipients (optional)" + }, + { + "name": "attachments", + "type": "string[]", + "optional": true, + "description": "File paths or URLs of attachments (optional)" + }, + { + "name": "messageRef", + "type": "{ senders, subject, content, receivedDateTime, srcFolder, destfolder }", + "optional": false, + "description": "Reference to the email message to reply to" + } + ], + "phrasings": [] + }, + { + "actionName": "sendEmail", + "description": "Type for sending a simple email", + "parameters": [ + { + "name": "subject", + "type": "string", + "optional": false, + "description": "Subject of the email, infer the subject based on the user input" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "Body content of the email, can be plain text or HTML, generate the body based on the user input" + }, + { + "name": "to", + "type": "string[]", + "optional": false, + "description": "Recipients, infer the recipients based on the user input but don't change strings so if the user types \"send email to Jen\" don't change it to an email address, just infer that \"Jen\" is one of the recipients" + }, + { + "name": "cc", + "type": "string[]", + "optional": true, + "description": "Carbon copy recipients (optional), infere CC recipients based on the user input" + }, + { + "name": "bcc", + "type": "string[]", + "optional": true, + "description": "Blind carbon copy recipients (optional), infer the BCC recipients based on the user input" + }, + { + "name": "attachments", + "type": "string[]", + "optional": true, + "description": "File paths or URLs of attachments (optional)" + }, + { + "name": "genContent", + "type": "{ generateBody, bodySearchQuery }", + "optional": false, + "description": "Information needed to generate the body content based on the user input" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "github-cli", + "category": "Development & Automation", + "emoji": "πŸ™", + "description": "Agent for github-cli", + "schemas": [ + { + "schemaName": "github-cli", + "description": "GithubCli agent actions", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "agentTaskRun", + "description": "", + "parameters": [ + { + "name": "task", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "aliasSet", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "command", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "apiRequest", + "description": "", + "parameters": [ + { + "name": "method", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "endpoint", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "top {limit} contributors to {endpoint}", + "list contributors to {endpoint}" + ] + }, + { + "actionName": "attestationCreate", + "description": "", + "parameters": [ + { + "name": "artifact", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "type", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "authLogin", + "description": "", + "parameters": [ + { + "name": "hostname", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "web", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "token", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "into GitHub" + ] + }, + { + "actionName": "authLogout", + "description": "", + "parameters": [ + { + "name": "hostname", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "out of GitHub" + ] + }, + { + "actionName": "authStatus", + "description": "", + "parameters": [ + { + "name": "hostname", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "showToken", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "check my GitHub authentication" + ] + }, + { + "actionName": "browseIssue", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "browsePr", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "browseRepo", + "description": "", + "parameters": [ + { + "name": "branch", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "commit", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "tag", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "cacheDelete", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "cacheList", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "codespaceCreate", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "branch", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "location", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "codespaceDelete", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "codespaceList", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "completionGenerate", + "description": "", + "parameters": [ + { + "name": "shell", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "configSet", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "value", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "copilotRun", + "description": "", + "parameters": [ + { + "name": "task", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "dependabotAlerts", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "owner/repo" + }, + { + "name": "severity", + "type": "string", + "optional": true, + "description": "Filter by severity: critical, high, medium, low" + }, + { + "name": "state", + "type": "string", + "optional": true, + "description": "Filter by state: open, dismissed, fixed" + } + ], + "phrasings": [] + }, + { + "actionName": "extensionInstall", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "gistCreate", + "description": "", + "parameters": [ + { + "name": "public", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "gistDelete", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "gistList", + "description": "", + "parameters": [ + { + "name": "public", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "my gists" + ] + }, + { + "actionName": "gpgKeyAdd", + "description": "", + "parameters": [ + { + "name": "key", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueAddLabel", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "label", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueClose", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueCreate", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "owner/repo" + }, + { + "name": "title", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "assignee", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "label", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "file an issue about {title}", + "create issue {title}" + ] + }, + { + "actionName": "issueDelete", + "description": "Permanently delete a GitHub issue (uses `gh issue delete --yes`).", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueList", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "state", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "label", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "author", + "type": "string", + "optional": true, + "description": "Filter by author (issue creator): a GitHub login (e.g. \"octocat\") or \"@me\" for the current user. Use this for \"issues I opened\"." + }, + { + "name": "assignee", + "type": "string", + "optional": true, + "description": "Filter by assignee: a GitHub login (e.g. \"octocat\"), \"@me\" for the current user, or \"none\" to list only unassigned issues. Use this for \"issues assigned to me\"." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueReopen", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "issueView", + "description": "View / open a specific GitHub issue by number. Example: User: show issue 2222 Agent: { actionName: \"issueView\", parameters: { number: 2222 } } Example: User: view issue #42 in microsoft/TypeAgent Agent: { actionName: \"issueView\", parameters: { number: 42, repo: \"microsoft/TypeAgent\" } }", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "The issue number. Omit when the user references an issue via entity resolution (\"that issue\", \"the issue we just opened\") β€” the dispatcher will substitute it." + }, + { + "name": "repo", + "type": "string", + "optional": true, + "description": "OWNER/REPO slug (e.g. \"microsoft/TypeAgent\"). Omit unless the user names the repo." + } + ], + "phrasings": [] + }, + { + "actionName": "labelCreate", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "color", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "licensesView", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "myAssignedIssues", + "description": "List issues assigned to the current authenticated user across all repositories. Maps to `gh search issues --assignee @me --state open`.", + "parameters": [ + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of issues to return (default 20)" + } + ], + "phrasings": [ + "what issues are assigned to me", + "show issues assigned to me", + "show my assigned issues" + ] + }, + { + "actionName": "myPullRequests", + "description": "List the current user's own pull requests across ALL repositories β€” the PRs they authored. Maps to `gh search prs --author @me`. Use this for \"my PRs\", \"PRs I opened\", or \"my open PRs everywhere\" when no specific repo is named. For PRs within a single repo, use `prList` with author: \"@me\" instead.", + "parameters": [ + { + "name": "state", + "type": "string", + "optional": true, + "description": "Filter by state: \"open\" (default) or \"closed\"." + }, + { + "name": "owner", + "type": "string", + "optional": true, + "description": "Scope the search to a single org/user's repositories (e.g. \"microsoft\"). Omit to search every repository the user can see." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of pull requests to return (default 20)." + } + ], + "phrasings": [ + "what pull requests have I opened", + "show pull requests I opened", + "show my pull requests", + "show my open pull requests" + ] + }, + { + "actionName": "orgList", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "orgView", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prCheckout", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + }, + { + "name": "branch", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prChecks", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": false, + "description": "" + }, + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prClose", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prCreate", + "description": "", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "base", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "head", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "draft", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "previewExecute", + "description": "", + "parameters": [ + { + "name": "feature", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prList", + "description": "List pull requests, optionally filtered by repo, state, label, author, or assignee. A PR is \"mine\" when the user *authored* it. On GitHub the owner of a PR is its author, not its assignee (self-assignment is rare), so \"my PRs\", \"PRs I opened\", and \"are any of those mine?\" map to author: \"@me\" β€” NOT assignee. Example: User: are any of those mine? (after listing open PRs in microsoft/TypeAgent) Agent: { actionName: \"prList\", parameters: { repo: \"microsoft/TypeAgent\", state: \"open\", author: \"@me\" } } Example: User: show my open pull requests Agent: { actionName: \"prList\", parameters: { state: \"open\", author: \"@me\" } }", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "state", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "label", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "author", + "type": "string", + "optional": true, + "description": "Filter by author (PR creator): a GitHub login (e.g. \"octocat\") or \"@me\" for the current user. Use this for \"my PRs\" / \"PRs I opened\"." + }, + { + "name": "assignee", + "type": "string", + "optional": true, + "description": "Filter by assignee: a GitHub login (e.g. \"octocat\"), \"@me\" for the current user, or \"none\" to list only unassigned pull requests. Most PRs have no assignee β€” prefer `author` for \"my PRs\"." + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "prMerge", + "description": "", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "" + }, + { + "name": "mergeMethod", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "projectCreate", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "body", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "projectDelete", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "projectList", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "prView", + "description": "View / open a specific GitHub pull request by number. Example: User: show PR 2196 Agent: { actionName: \"prView\", parameters: { number: 2196 } } Example: User: view pull request #42 in microsoft/TypeAgent Agent: { actionName: \"prView\", parameters: { number: 42, repo: \"microsoft/TypeAgent\" } }", + "parameters": [ + { + "name": "number", + "type": "number", + "optional": true, + "description": "The pull request number. Omit when the user references a PR via entity resolution (\"that PR\", \"the PR we just opened\") β€” the dispatcher will substitute it." + }, + { + "name": "repo", + "type": "string", + "optional": true, + "description": "OWNER/REPO slug (e.g. \"microsoft/TypeAgent\"). Omit unless the user names the repo." + } + ], + "phrasings": [] + }, + { + "actionName": "releaseCreate", + "description": "", + "parameters": [ + { + "name": "tag", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "title", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "notes", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "releaseDelete", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "releaseList", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "repoClone", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "branch", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "repoCreate", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "public", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "private", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "repoDelete", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "repoFork", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "repoView", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "field", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "how many stars does {repo} have", + "show repository {repo}" + ] + }, + { + "actionName": "rulesetView", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "runView", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "searchRepos", + "description": "", + "parameters": [ + { + "name": "query", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "secretCreate", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "value", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "sshKeyAdd", + "description": "", + "parameters": [ + { + "name": "key", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "add an SSH key for me" + ] + }, + { + "actionName": "starRepo", + "description": "", + "parameters": [ + { + "name": "repo", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "unstar", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "unstar {repo}", + "star {repo}" + ] + }, + { + "actionName": "statusPrint", + "description": "", + "parameters": [], + "phrasings": [ + "summary of issues and pull requests", + "print latest updates on my repositories", + "show status of issues and pull requests", + "show my GitHub status" + ] + }, + { + "actionName": "variableCreate", + "description": "", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "value", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "workflowView", + "description": "", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "image", + "category": "Creative & Media", + "emoji": "πŸ–ΌοΈ", + "description": "Agent to create AI images", + "schemas": [ + { + "schemaName": "image", + "description": "Image agent that creates images using AI image modes.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "createImageAction", + "description": "creates an image based on the supplied description", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "caption", + "type": "string", + "optional": false, + "description": "the image caption" + }, + { + "name": "numImages", + "type": "number", + "optional": false, + "description": "the number of images to generate" + } + ], + "phrasings": [] + }, + { + "actionName": "editImageAction", + "description": "Edits / transforms an image the user has already supplied (typically the most recent attachment). Use this for requests like \"cartoonize that image\", \"make a watercolor version of this photo\", \"stylize this as a pencil sketch\", etc.", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "editPrompt", + "type": "string", + "optional": false, + "description": "The natural-language description of the edit to perform (e.g. \"cartoonize\", \"make this a watercolor painting\")." + }, + { + "name": "sourceImage", + "type": "string", + "optional": false, + "description": "The file name of the source image to edit. This MUST be the exact name of an image that is either (a) attached to the current request, or (b) listed as an image entity from a recent turn (e.g. `generated_images/.png` from a prior editImageAction / createImageAction result). NEVER invent a filename β€” if no such image is available, do not emit this action; ask the user to attach an image instead." + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "ipconfig", + "category": "System & Devices", + "emoji": "πŸ”Ž", + "description": "Windows IP configuration command-line tool that displays and manages network adapter settings", + "schemas": [ + { + "schemaName": "ipconfig", + "description": "Ipconfig agent actions", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "displayDHCPClassIDs", + "description": "User: \"Can you show me the DHCP class IDs for the adapter?\" Agent: \"Displaying the DHCP class IDs for the adapter.\" User: \"Please display the DHCP class IDs for the network adapter.\" Agent: \"Displaying the DHCP class IDs for the network adapter.\" User: \"I need to see the DHCP class IDs for my adapter.\" Agent: \"Displaying the DHCP class IDs for the adapter.\" Displays all the DHCP class IDs allowed for the adapter.", + "parameters": [], + "phrasings": [ + "what are the dhcp class ids for the specified adapter", + "i need to see the dhcp class ids for my adapter", + "please display the dhcp class ids for the network adapter", + "can you show me the dhcp class ids for the adapter" + ] + }, + { + "actionName": "displayDNSResolverCacheContents", + "description": "User: \"Show me the DNS cache contents.\" Agent: \"Displaying the contents of the DNS resolver cache.\" User: \"Can you display the DNS resolver cache?\" Agent: \"Displaying the contents of the DNS resolver cache.\" User: \"I'd like to see the DNS resolver cache details.\" Agent: \"Displaying the contents of the DNS resolver cache.\" Displays the contents of the DNS Resolver Cache.", + "parameters": [], + "phrasings": [ + "please provide the contents of the dns cache", + "i'd like to see the dns resolver cache details", + "can you display the dns resolver cache", + "show me the dns cache contents" + ] + }, + { + "actionName": "displayFullConfigurationInformation", + "description": "User: \"Show me the full network configuration details.\" Agent: \"Displaying full network configuration details.\" User: \"Can you display all the configuration information for my network?\" Agent: \"Displaying full network configuration details.\" User: \"I need to see the complete IP configuration.\" Agent: \"Displaying full network configuration details.\" Displays full configuration information.", + "parameters": [], + "phrasings": [ + "please provide the full configuration info for my network adapters", + "i need to see the complete ip configuration", + "what is my ip address", + "what's my ip address", + "can you display all the configuration information for my network", + "show me the full network configuration details" + ] + }, + { + "actionName": "displayHelpMessage", + "description": "User: \"Can you show me the help message for ipconfig?\" Agent: \"Displaying the help message for ipconfig.\" User: \"I need some help with ipconfig, please display the help message.\" Agent: \"Displaying the help message for ipconfig.\" User: \"What's the help message for ipconfig?\" Agent: \"Displaying the help message for ipconfig.\" Displays the help message for ipconfig.", + "parameters": [], + "phrasings": [ + "could you display the ipconfig help message", + "what's the help message for ipconfig", + "i need some help with ipconfig please display the help message", + "can you show me the help message for ipconfig" + ] + }, + { + "actionName": "displayIPv6DHCPClassIDs", + "description": "User: \"Can you show me the IPv6 DHCP class IDs for the adapter?\" Agent: \"Displaying the IPv6 DHCP class IDs for the adapter.\" User: \"Please display the IPv6 DHCP class IDs for the network adapter.\" Agent: \"Displaying the IPv6 DHCP class IDs for the network adapter.\" User: \"What are the IPv6 DHCP class IDs allowed for the adapter?\" Agent: \"Displaying the IPv6 DHCP class IDs for the adapter.\" Displays all the IPv6 DHCP class IDs allowed for the adapter.", + "parameters": [], + "phrasings": [ + "i'd like to see the ipv6 dhcp class ids for my adapter", + "what are the ipv6 dhcp class ids allowed for the adapter", + "please display the ipv6 dhcp class ids for the network adapter", + "can you show me the ipv6 dhcp class ids for the adapter" + ] + }, + { + "actionName": "modifyDHCPClassID", + "description": "User: \"Can you change the DHCP class ID for the Ethernet adapter to 'newClassID'?\" Agent: \"Modifying the DHCP class ID for the Ethernet adapter to 'newClassID'.\" User: \"Please update the DHCP class ID on the Wi-Fi adapter to 'newClassID'.\" Agent: \"Modifying the DHCP class ID for the Wi-Fi adapter to 'newClassID'.\" User: \"Set the DHCP class ID for the 'Local Area Connection' adapter to 'newClassID'.\" Agent: \"Modifying the DHCP class ID for the 'Local Area Connection' adapter to 'newClassID'.\" Modifies the DHCP class ID.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": false, + "description": "The name of the network adapter." + }, + { + "name": "classID", + "type": "string", + "optional": true, + "description": "The new DHCP class ID." + } + ], + "phrasings": [ + "i'd like to modify the dhcp class id for the {adapter} adapter use {classID}", + "set the dhcp class id for the {adapter} adapter to {classID}", + "please update the dhcp class id on the {adapter} adapter to {classID}", + "can you change the dhcp class id for the {adapter} adapter to {classID}" + ] + }, + { + "actionName": "modifyIPv6DHCPClassID", + "description": "User: \"Can you change the IPv6 DHCP class ID for the adapter named 'Ethernet' to 'newClassID'?\" Agent: \"Modifying the IPv6 DHCP class ID for the 'Ethernet' adapter to 'newClassID'.\" User: \"Please update the IPv6 DHCP class ID for my Wi-Fi adapter to 'classID123'.\" Agent: \"Modifying the IPv6 DHCP class ID for the Wi-Fi adapter to 'classID123'.\" User: \"Set the IPv6 DHCP class ID to 'classID456' for the network adapter 'Local Area Connection'.\" Agent: \"Modifying the IPv6 DHCP class ID for the 'Local Area Connection' adapter to 'classID456'.\" Modifies the IPv6 DHCP class ID.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": false, + "description": "The name of the network adapter." + }, + { + "name": "classID", + "type": "string", + "optional": true, + "description": "The new IPv6 DHCP class ID." + } + ], + "phrasings": [ + "i'd like to modify the ipv6 dhcp class id for {adapter} adapter to {classID}", + "set the ipv6 dhcp class id to {classID} for the network adapter {adapter}", + "please update the ipv6 dhcp class id for my {adapter} adapter to {classID}", + "can you change the ipv6 dhcp class id for the adapter named {adapter} to {classID}" + ] + }, + { + "actionName": "purgeDNSResolverCache", + "description": "User: \"Clear the DNS cache, please.\" Agent: \"Purging the DNS resolver cache.\" User: \"Can you purge the DNS resolver cache?\" Agent: \"Purging the DNS resolver cache.\" User: \"I need to flush the DNS cache.\" Agent: \"Purging the DNS resolver cache.\" Purges the DNS Resolver cache.", + "parameters": [], + "phrasings": [ + "please empty the dns resolver cache", + "i need to flush the dns cache", + "can you purge the dns resolver cache", + "clear the dns cache please" + ] + }, + { + "actionName": "refreshDHCPLeasesAndReRegisterDNSNames", + "description": "User: \"Can you refresh the DHCP leases and re-register the DNS names for me?\" Agent: \"Refreshing all DHCP leases and re-registering DNS names.\" User: \"Please update the DHCP leases and DNS registrations.\" Agent: \"Refreshing all DHCP leases and re-registering DNS names.\" User: \"I need to refresh all DHCP leases and re-register the DNS names.\" Agent: \"Refreshing all DHCP leases and re-registering DNS names.\" Refreshes all DHCP leases and re-registers DNS names.", + "parameters": [], + "phrasings": [ + "could you refresh the dhcp leases and re register the dns names", + "i need to refresh all dhcp leases and re register the dns names", + "please update the dhcp leases and dns registrations", + "can you refresh the dhcp leases and re register the dns names for me" + ] + }, + { + "actionName": "releaseIPv4Address", + "description": "User: \"Please release the IPv4 address for the Ethernet adapter.\" Agent: \"Releasing the IPv4 address for the Ethernet adapter.\" User: \"Can you release the IPv4 address on my Wi-Fi adapter?\" Agent: \"Releasing the IPv4 address for the Wi-Fi adapter.\" User: \"Release the IPv4 address for the network adapter named 'Local Area Connection'.\" Agent: \"Releasing the IPv4 address for the 'Local Area Connection' adapter.\" Releases the IPv4 address for the specified adapter.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": true, + "description": "The name of the network adapter." + } + ], + "phrasings": [ + "i need to release the ipv4 address for the adapter called {adapter}", + "release the ipv4 address for the network adapter named {adapter}", + "can you release the ipv4 address on my {adapter} adapter", + "please release the ipv4 address for the {adapter} adapter" + ] + }, + { + "actionName": "releaseIPv6Address", + "description": "User: \"Please release the IPv6 address for the adapter named 'Ethernet'.\" Agent: \"Releasing the IPv6 address for the 'Ethernet' adapter.\" User: \"Can you release the IPv6 address on my Wi-Fi adapter?\" Agent: \"Releasing the IPv6 address for the Wi-Fi adapter.\" User: \"I need to release the IPv6 address for the 'Local Area Connection' adapter.\" Agent: \"Releasing the IPv6 address for the 'Local Area Connection' adapter.\" Releases the IPv6 address for the specified adapter.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": true, + "description": "The name of the network adapter." + } + ], + "phrasings": [ + "release the ipv6 address for the {adapter} please", + "i need to release the ipv6 address for the {adapter} adapter", + "can you release the ipv6 address on my {adapter} adapter", + "please release the ipv6 address for the adapter named {adapter}" + ] + }, + { + "actionName": "renewIPv4Address", + "description": "User: \"Can you renew the IPv4 address for the Ethernet adapter?\" Agent: \"Renewing the IPv4 address for the Ethernet adapter.\" User: \"Please refresh the IPv4 address for my Wi-Fi adapter.\" Agent: \"Renewing the IPv4 address for the Wi-Fi adapter.\" User: \"Renew the IPv4 address for the network adapter named 'Local Area Connection'.\" Agent: \"Renewing the IPv4 address for the 'Local Area Connection' adapter.\" Renews the IPv4 address for the specified adapter.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": true, + "description": "The name of the network adapter." + } + ], + "phrasings": [ + "i need to renew the ipv4 address for the {adapter}", + "renew the ipv4 address for the network adapter named {adapter}", + "please refresh the ipv4 address for my {adapter} adapter", + "can you renew the ipv4 address for the {adapter} adapter" + ] + }, + { + "actionName": "renewIPv6Address", + "description": "User: \"Can you renew the IPv6 address for the adapter named 'Ethernet'?\" Agent: \"Renewing the IPv6 address for the 'Ethernet' adapter.\" User: \"Please refresh the IPv6 address for my Wi-Fi adapter.\" Agent: \"Renewing the IPv6 address for the Wi-Fi adapter.\" User: \"Renew the IPv6 address for the 'Local Area Connection' adapter.\" Agent: \"Renewing the IPv6 address for the 'Local Area Connection' adapter.\" Renews the IPv6 address for the specified adapter.", + "parameters": [ + { + "name": "adapter", + "type": "string", + "optional": true, + "description": "The name of the network adapter." + } + ], + "phrasings": [ + "i need to update the ipv6 address for the {adapter}", + "renew the ipv6 address for the {adapter} adapter", + "please refresh the ipv6 address for my {adapter} adapter", + "can you renew the ipv6 address for the adapter named {adapter}" + ] + } + ] + } + ] + }, + { + "name": "list", + "category": "Productivity", + "emoji": "πŸ“", + "description": "Agent to create and manage lists", + "schemas": [ + { + "schemaName": "list", + "description": "List agent with actions to create lists, show list items, add and remove list items", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addItems", + "description": "add one or more items to a list; if the list does not exist, create it", + "parameters": [ + { + "name": "items", + "type": "string[]", + "optional": false, + "description": "" + }, + { + "name": "listName", + "type": "string", + "optional": false, + "description": "name of the list such as \"grocery\", \"to do\", \"shopping\", \"packing\", \"gift\",\"book\",\"idea\",\"movie\",\"garden task\",\"place to visit\" names should be lower case and should be stemmed to the singular form (e.g., \"movies\" should be \"movie\") IMPORTANT: Do not invent a list name. If the user uses a reference phrase (\"the list\", \"that list\",etc.) we should clarify with the user which list they meant unless it is obvious from the conversation history." + } + ], + "phrasings": [] + }, + { + "actionName": "clearList", + "description": "", + "parameters": [ + { + "name": "listName", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "delete from {listName} list", + "empty {listName}", + "clear {listName} list" + ] + }, + { + "actionName": "createList", + "description": "", + "parameters": [ + { + "name": "listName", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "start {listName} list", + "make {listName} list", + "create {listName} list" + ] + }, + { + "actionName": "getList", + "description": "use this action to show the user what's on the list, for example, \"What's on my grocery list?\" or \"what are the contents of my to do list?\"", + "parameters": [ + { + "name": "listName", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "removeItems", + "description": "remove one or more items from a list", + "parameters": [ + { + "name": "items", + "type": "string[]", + "optional": false, + "description": "" + }, + { + "name": "listName", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [ + "take {item} off {listName}", + "delete {item} from {listName}", + "remove {item} from {listName} list" + ] + }, + { + "actionName": "startEditList", + "description": "", + "parameters": [ + { + "name": "listName", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "localPlayer", + "category": "Music & Entertainment", + "emoji": "🎡", + "description": "Agent to play local music files", + "schemas": [ + { + "schemaName": "localPlayer", + "description": "Local Music Player agent that lets you play and control local audio files.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addToQueue", + "description": "Add a file or files to the playback queue", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": false, + "description": "File name or path to add to queue" + } + ], + "phrasings": [] + }, + { + "actionName": "changeVolume", + "description": "Change volume by a relative amount", + "parameters": [ + { + "name": "amount", + "type": "number", + "optional": false, + "description": "Amount to change volume by (can be negative)" + } + ], + "phrasings": [ + "turn down volume", + "turn up volume" + ] + }, + { + "actionName": "clearQueue", + "description": "Clear the playback queue", + "parameters": [], + "phrasings": [ + "clear queue" + ] + }, + { + "actionName": "listFiles", + "description": "List audio files in the music folder or a specified folder", + "parameters": [ + { + "name": "folderPath", + "type": "string", + "optional": true, + "description": "Folder to list (defaults to music folder)" + } + ], + "phrasings": [] + }, + { + "actionName": "mute", + "description": "Mute or unmute audio", + "parameters": [ + { + "name": "isMuted", + "type": "boolean", + "optional": false, + "description": "true to mute, false to unmute" + } + ], + "phrasings": [ + "unmute", + "mute" + ] + }, + { + "actionName": "next", + "description": "Skip to next track", + "parameters": [], + "phrasings": [ + "next" + ] + }, + { + "actionName": "pause", + "description": "Pause playback", + "parameters": [], + "phrasings": [ + "pause" + ] + }, + { + "actionName": "playFile", + "description": "Play a specific audio file by path or name", + "parameters": [ + { + "name": "fileName", + "type": "string", + "optional": false, + "description": "File name or path to play (can be partial match)" + } + ], + "phrasings": [] + }, + { + "actionName": "playFolder", + "description": "Play all audio files in a folder", + "parameters": [ + { + "name": "folderPath", + "type": "string", + "optional": true, + "description": "Folder path to play from" + }, + { + "name": "shuffle", + "type": "boolean", + "optional": true, + "description": "Whether to shuffle the tracks" + } + ], + "phrasings": [] + }, + { + "actionName": "playFromQueue", + "description": "Play a specific track from the current queue by index", + "parameters": [ + { + "name": "trackNumber", + "type": "number", + "optional": false, + "description": "1-based index of the track in the queue" + } + ], + "phrasings": [] + }, + { + "actionName": "previous", + "description": "Go to previous track", + "parameters": [], + "phrasings": [ + "previous" + ] + }, + { + "actionName": "repeat", + "description": "Set repeat mode", + "parameters": [ + { + "name": "mode", + "type": "\"off\" | \"one\" | \"all\"", + "optional": false, + "description": "Repeat mode: \"off\", \"one\" (repeat current track), \"all\" (repeat queue)" + } + ], + "phrasings": [] + }, + { + "actionName": "resume", + "description": "Resume playback", + "parameters": [], + "phrasings": [ + "resume" + ] + }, + { + "actionName": "searchFiles", + "description": "Search for audio files by name", + "parameters": [ + { + "name": "query", + "type": "string", + "optional": false, + "description": "Search query to match against file names" + } + ], + "phrasings": [] + }, + { + "actionName": "setMusicFolder", + "description": "Set the default music folder path", + "parameters": [ + { + "name": "folderPath", + "type": "string", + "optional": false, + "description": "Path to the music folder" + } + ], + "phrasings": [] + }, + { + "actionName": "setVolume", + "description": "Set volume to a specific level (0-100)", + "parameters": [ + { + "name": "level", + "type": "number", + "optional": false, + "description": "New volume level (0-100)" + } + ], + "phrasings": [] + }, + { + "actionName": "showMusicFolder", + "description": "Show the current music folder path", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "showQueue", + "description": "Show the current playback queue", + "parameters": [], + "phrasings": [ + "show queue" + ] + }, + { + "actionName": "shuffle", + "description": "Turn shuffle on or off", + "parameters": [ + { + "name": "on", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "status", + "description": "Show now playing status including track information and playback state", + "parameters": [], + "phrasings": [ + "what 's" + ] + }, + { + "actionName": "stop", + "description": "Stop playback completely", + "parameters": [], + "phrasings": [ + "stop" + ] + } + ] + } + ] + }, + { + "name": "markdown", + "category": "Creative & Media", + "emoji": "πŸ—Ž", + "description": "Agent to create and update markdown files", + "schemas": [ + { + "schemaName": "markdown", + "description": "Markdown editing agent that updates documents using Github markdown flavor with AI streaming support.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "createDocument", + "description": "creates a new markdown document", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "the name to use for the document" + } + ], + "phrasings": [] + }, + { + "actionName": "openDocument", + "description": "opens an existing markdown document", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "the name to use for the document" + } + ], + "phrasings": [] + }, + { + "actionName": "streamingUpdateDocument", + "description": "Updates the document with streaming support for real-time AI operations", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "cursorPosition", + "type": "number", + "optional": true, + "description": "cursor position where the user invoked the command" + }, + { + "name": "context", + "type": "string", + "optional": true, + "description": "additional context information from the editor (serialized as JSON string)" + }, + { + "name": "generatedContent", + "type": "string", + "optional": true, + "description": "Stream-friendly properties that can be updated incrementally" + }, + { + "name": "progressStatus", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "validationResults", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "aiCommand", + "type": "\"continue\" | \"diagram\" | \"augment\" | \"research\"", + "optional": true, + "description": "AI command type for specialized processing" + } + ], + "phrasings": [] + }, + { + "actionName": "updateDocument", + "description": "Updates the document by adding, removing or editing parts of the document.", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "cursorPosition", + "type": "number", + "optional": true, + "description": "cursor position where the user invoked the command" + }, + { + "name": "context", + "type": "string", + "optional": true, + "description": "additional context information from the editor (serialized as JSON string)" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "montage", + "category": "Creative & Media", + "emoji": "🎞", + "description": "Agent to create photo montages.", + "schemas": [ + { + "schemaName": "montage", + "description": "Photo montage agent to help the user with image management.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addPhotos", + "description": "add photos to the montage", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "title of the montage" + }, + { + "name": "files", + "type": "string[]", + "optional": true, + "description": "placeholder for images to be populated later" + }, + { + "name": "search_filters", + "type": "string[]", + "optional": true, + "description": "always empty" + } + ], + "phrasings": [] + }, + { + "actionName": "changeTitle", + "description": "Changes the montage title", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "current title of the montage to change" + }, + { + "name": "newTitle", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "clearSelectedPhotos", + "description": "Clears the currently selected images", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "title of the montage" + } + ], + "phrasings": [] + }, + { + "actionName": "createNewMontage", + "description": "Creates a new montage", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "Montage title, defaults to \"Untitled\"" + }, + { + "name": "search_filters", + "type": "string[]", + "optional": true, + "description": "any search terms to use to seed the montage based off of the title" + }, + { + "name": "focus", + "type": "boolean", + "optional": false, + "description": "a flag indicating if the UI should immediately switch to this montage, defaults to true" + }, + { + "name": "files", + "type": "string[]", + "optional": true, + "description": "placeholder for images to be populated later" + } + ], + "phrasings": [] + }, + { + "actionName": "deleteAllMontages", + "description": "", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "deleteMontage", + "description": "Deletes the identified montage", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "The title of the montage to delete" + } + ], + "phrasings": [] + }, + { + "actionName": "listMontages", + "description": "Lists all montages by name", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "mergeMontages", + "description": "Merges two or more montages together", + "parameters": [ + { + "name": "mergedMontageTitle", + "type": "string", + "optional": false, + "description": "The title of the merged montage" + }, + { + "name": "titles", + "type": "string[]", + "optional": true, + "description": "The titles of the montages to merge" + }, + { + "name": "ids", + "type": "number[]", + "optional": true, + "description": "THe ids of the montages to merge" + } + ], + "phrasings": [] + }, + { + "actionName": "openMontage", + "description": "Open a montage for viewing and editing", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "title of the montage" + } + ], + "phrasings": [] + }, + { + "actionName": "removePhotos", + "description": "Removes the images that match the supplied criteria", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "title of the montage" + }, + { + "name": "search_filters", + "type": "string[]", + "optional": true, + "description": "any search terms to use indicating the photos to remove" + }, + { + "name": "indices", + "type": "number[]", + "optional": true, + "description": "any indices provided indicating the photos to remove from the set of available images" + }, + { + "name": "selected", + "type": "\"selected\" | \"inverse\" | \"all\"", + "optional": true, + "description": "flag indicating if we are remove selected images or inverse" + }, + { + "name": "files", + "type": "string[]", + "optional": true, + "description": "placeholder for images to be populated later" + } + ], + "phrasings": [] + }, + { + "actionName": "selectPhotos", + "description": "Select images in an existing montage", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "title of the montage" + }, + { + "name": "search_filters", + "type": "string[]", + "optional": true, + "description": "any search terms to use indicating the photos to remove" + }, + { + "name": "indices", + "type": "number[]", + "optional": true, + "description": "any indices provided indicating the photos to remove from the set of available images" + }, + { + "name": "files", + "type": "string[]", + "optional": true, + "description": "placeholder for images to be populated later" + } + ], + "phrasings": [] + }, + { + "actionName": "setMontageViewMode", + "description": "", + "parameters": [ + { + "name": "viewMode", + "type": "\"grid\" | \"filmstrip\"", + "optional": false, + "description": "The view mode to set" + } + ], + "phrasings": [] + }, + { + "actionName": "setSearchParameters", + "description": "Update search parameters", + "parameters": [ + { + "name": "minSearchScore", + "type": "number", + "optional": true, + "description": "search score value starts at 0" + }, + { + "name": "exactMatch", + "type": "boolean", + "optional": true, + "description": "only return exact matches?" + } + ], + "phrasings": [] + }, + { + "actionName": "showSearchParameters", + "description": "Shows search parameters", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "startSlideShow", + "description": "Starts a slide show with the images in the active montage", + "parameters": [ + { + "name": "title", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "onboarding", + "category": "Development & Automation", + "emoji": "πŸ› οΈ", + "description": "Agent for onboarding new applications and APIs into TypeAgent", + "schemas": [ + { + "schemaName": "onboarding", + "description": "Top-level onboarding coordination: start, resume, and check status of integration onboarding workflows", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "getOnboardingStatus", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to check status for" + } + ], + "phrasings": [ + "how far along is {integrationName}", + "show {integrationName} onboarding status", + "what's status {integrationName} onboarding" + ] + }, + { + "actionName": "listIntegrations", + "description": "", + "parameters": [ + { + "name": "status", + "type": "\"in-progress\" | \"complete\"", + "optional": true, + "description": "Filter by phase status; omit to list all" + } + ], + "phrasings": [ + "what integrations", + "show integrations", + "list integrations" + ] + }, + { + "actionName": "resumeOnboarding", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration to resume" + }, + { + "name": "fromPhase", + "type": "\"discovery\" | \"phraseGen\" | \"schemaGen\" | \"grammarGen\" | \"scaffolder\" | \"testing\" | \"packaging\"", + "optional": true, + "description": "Optional: override which phase to start from (defaults to current phase in state.json)" + } + ], + "phrasings": [ + "continue {integrationName} onboarding", + "resume onboarding {integrationName}" + ] + }, + { + "actionName": "startOnboarding", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Unique name for this integration (e.g. \"slack\", \"jira\", \"my-rest-api\"). Used as the workspace folder name β€” lowercase, no spaces." + }, + { + "name": "description", + "type": "string", + "optional": true, + "description": "Human-readable description of what the integration does" + }, + { + "name": "apiType", + "type": "\"rest\" | \"graphql\" | \"websocket\" | \"ipc\" | \"sdk\"", + "optional": true, + "description": "The type of API being integrated; helps select appropriate templates and bridge patterns" + } + ], + "phrasings": [ + "begin {integrationName} integration", + "onboard {integrationName}", + "start onboarding {integrationName}" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-discovery", + "description": "Phase 1: Enumerate the API surface of the target application by crawling documentation or parsing an OpenAPI spec", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "approveApiSurface", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to approve" + }, + { + "name": "includeActions", + "type": "string[]", + "optional": true, + "description": "If provided, only these action names are included in the approved surface (excludes all others)" + }, + { + "name": "excludeActions", + "type": "string[]", + "optional": true, + "description": "Action names to exclude from the approved surface" + } + ], + "phrasings": [ + "confirm {integrationName} surface", + "approve surface for {integrationName}" + ] + }, + { + "actionName": "crawlCliHelp", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration being onboarded" + }, + { + "name": "command", + "type": "string", + "optional": false, + "description": "The CLI command to crawl (e.g. \"gh\", \"az\", \"kubectl\")" + }, + { + "name": "maxDepth", + "type": "number", + "optional": true, + "description": "Maximum recursion depth into subcommands (default: 4)" + } + ], + "phrasings": [ + "discover {command} cli as {integrationName}", + "crawl cli help {command} as {integrationName}" + ] + }, + { + "actionName": "crawlDocUrl", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration being onboarded" + }, + { + "name": "url", + "type": "string", + "optional": false, + "description": "URL of the API documentation page to crawl (e.g. \"https://api.slack.com/methods\")" + }, + { + "name": "maxDepth", + "type": "number", + "optional": true, + "description": "Maximum link-follow depth (default: 2)" + } + ], + "phrasings": [ + "fetch {integrationName} docs {url}", + "crawl docs {url} for {integrationName}" + ] + }, + { + "actionName": "listDiscoveredActions", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to list discovered actions for" + } + ], + "phrasings": [ + "show actions {integrationName}", + "list actions for {integrationName}" + ] + }, + { + "actionName": "parseOpenApiSpec", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration being onboarded" + }, + { + "name": "specSource", + "type": "string", + "optional": false, + "description": "URL or absolute file path to the OpenAPI 3.x or Swagger 2.x spec" + } + ], + "phrasings": [ + "load {integrationName} openapi spec {specSource}", + "parse openapi spec {specSource} for {integrationName}" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-grammargen", + "description": "Phase 4: Generate .agr grammar files from action schemas and sample phrases, then compile and validate them", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "approveGrammar", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to approve grammar for" + } + ], + "phrasings": [ + "confirm {integrationName} grammar", + "approve {integrationName} grammar" + ] + }, + { + "actionName": "compileGrammar", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name whose grammar to compile and validate" + } + ], + "phrasings": [ + "validate {integrationName} grammar", + "compile {integrationName} grammar" + ] + }, + { + "actionName": "generateGrammar", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to generate grammar for" + } + ], + "phrasings": [ + "create {integrationName} grammar", + "generate grammar for {integrationName}" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-packaging", + "description": "Phase 7: Package the completed agent for distribution and register it with the TypeAgent dispatcher", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "generateDemo", + "description": "Generates a demo script and narration for the onboarded agent", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration" + }, + { + "name": "durationMinutes", + "type": "string", + "optional": true, + "description": "Duration target in minutes (default: 3-5)" + } + ], + "phrasings": [ + "create demo for {integrationName}", + "generate demo for {integrationName}" + ] + }, + { + "actionName": "generateReadme", + "description": "Generates a README.md for the onboarded agent", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Name of the integration" + } + ], + "phrasings": [ + "create readme for {integrationName}", + "generate readme for {integrationName}" + ] + }, + { + "actionName": "packageAgent", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to package" + }, + { + "name": "register", + "type": "boolean", + "optional": true, + "description": "If true, also register the agent with the local TypeAgent dispatcher config" + } + ], + "phrasings": [ + "build {integrationName}", + "package {integrationName} agent" + ] + }, + { + "actionName": "validatePackage", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name whose package to validate" + } + ], + "phrasings": [ + "check {integrationName} package", + "validate {integrationName} package" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-phrasegen", + "description": "Phase 2: Generate natural language sample phrases that users would say to invoke each discovered action", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addPhrase", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name" + }, + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "The action name this phrase should map to" + }, + { + "name": "phrase", + "type": "string", + "optional": false, + "description": "The natural language phrase to add" + } + ], + "phrasings": [ + "add phrase {phrase} for {actionName} in {integrationName}" + ] + }, + { + "actionName": "approvePhrases", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to approve phrases for" + } + ], + "phrasings": [ + "confirm {integrationName} phrases", + "approve phrases for {integrationName}" + ] + }, + { + "actionName": "generatePhrases", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to generate phrases for" + }, + { + "name": "phrasesPerAction", + "type": "number", + "optional": true, + "description": "Number of phrases to generate per action (default: 5)" + }, + { + "name": "forActions", + "type": "string[]", + "optional": true, + "description": "Generate phrases only for these specific action names (generates for all if omitted)" + } + ], + "phrasings": [] + }, + { + "actionName": "removePhrase", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name" + }, + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "The action name to remove the phrase from" + }, + { + "name": "phrase", + "type": "string", + "optional": false, + "description": "The exact phrase to remove" + } + ], + "phrasings": [ + "remove phrase {phrase} from {actionName} in {integrationName}" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-scaffolder", + "description": "Phase 5: Scaffold the complete TypeAgent agent package infrastructure including manifest, handler, package.json, and any required plugins", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "listPatterns", + "description": "", + "parameters": [], + "phrasings": [ + "what patterns", + "show patterns", + "list patterns" + ] + }, + { + "actionName": "listTemplates", + "description": "", + "parameters": [], + "phrasings": [ + "show templates", + "list templates" + ] + }, + { + "actionName": "scaffoldAgent", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to scaffold agent for" + }, + { + "name": "pattern", + "type": "\"schema-grammar\" | \"external-api\" | \"llm-streaming\" | \"sub-agent-orchestrator\" | \"websocket-bridge\" | \"state-machine\" | \"native-platform\" | \"view-ui\" | …", + "optional": true, + "description": "Architectural pattern to use (defaults to \"schema-grammar\")" + }, + { + "name": "outputDir", + "type": "string", + "optional": true, + "description": "Target directory for the agent package (defaults to ts/packages/agents/)" + }, + { + "name": "emojiChar", + "type": "string", + "optional": true, + "description": "Emoji character for the agent icon (defaults to \"πŸ”Ž\")" + } + ], + "phrasings": [] + }, + { + "actionName": "scaffoldPlugin", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to scaffold the host-side plugin for" + }, + { + "name": "template", + "type": "\"office-addin\" | \"vscode-extension\" | \"electron-app\" | \"browser-extension\" | \"rest-client\"", + "optional": false, + "description": "Template to use for the plugin side" + }, + { + "name": "outputDir", + "type": "string", + "optional": true, + "description": "Target directory for the plugin (defaults to ts/packages/agents//plugin)" + } + ], + "phrasings": [ + "create {integrationName} plugin", + "scaffold {integrationName} plugin" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-schemagen", + "description": "Phase 3: Generate TypeScript action schema types with comments that map user requests to the target API surface", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "approveSchema", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to approve schema for" + } + ], + "phrasings": [ + "confirm {integrationName} schema", + "approve {integrationName} schema" + ] + }, + { + "actionName": "generateSchema", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to generate schema for" + } + ], + "phrasings": [ + "create schema for {integrationName}", + "generate schema for {integrationName}" + ] + }, + { + "actionName": "refineSchema", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name" + }, + { + "name": "instructions", + "type": "string", + "optional": false, + "description": "Specific instructions for the LLM about what to change e.g. \"make the listName parameter optional\" or \"add a sortOrder parameter to sortAction\"" + } + ], + "phrasings": [ + "update {integrationName} schema {instructions}", + "refine {integrationName} schema {instructions}" + ] + } + ] + }, + { + "schemaName": "onboarding.onboarding-testing", + "description": "Phase 6: Generate test cases from sample phrases and run a phrase-to-action validation loop, proposing schema and grammar repairs for failures", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "approveRepair", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to approve the proposed repair for" + } + ], + "phrasings": [ + "apply {integrationName} repair", + "approve {integrationName} repair" + ] + }, + { + "actionName": "generateTests", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to generate tests for" + } + ], + "phrasings": [ + "create test cases for {integrationName}", + "generate tests for {integrationName}" + ] + }, + { + "actionName": "getTestResults", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to get test results for" + }, + { + "name": "filter", + "type": "\"passing\" | \"failing\"", + "optional": true, + "description": "Filter to show only passing or failing tests" + } + ], + "phrasings": [ + "what are results for {integrationName}", + "get {integrationName} test results" + ] + }, + { + "actionName": "proposeRepair", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to propose repairs for" + }, + { + "name": "forActions", + "type": "string[]", + "optional": true, + "description": "If provided, propose repairs only for these specific failing action names" + } + ], + "phrasings": [ + "suggest fix for {integrationName} failures", + "propose repair for {integrationName}" + ] + }, + { + "actionName": "runTests", + "description": "", + "parameters": [ + { + "name": "integrationName", + "type": "string", + "optional": false, + "description": "Integration name to run tests for" + }, + { + "name": "forActions", + "type": "string[]", + "optional": true, + "description": "Run only tests for these specific action names" + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of tests to run (runs all if omitted)" + } + ], + "phrasings": [ + "execute {integrationName} tests", + "run tests for {integrationName}" + ] + } + ] + } + ] + }, + { + "name": "osNotifications", + "category": "System & Devices", + "emoji": "πŸ””", + "description": "Forwards OS-level notifications (Windows Action Center, Linux freedesktop) into chat. Off by default; opt in via @config agent enable osNotifications.", + "schemas": [ + { + "schemaName": "osNotifications", + "description": "Diagnostic actions for the OS notifications agent: sync currently-present notifications and inject synthetic test notifications.", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "syncOsNotifications", + "description": "Re-emit currently-present OS notifications through the agent pipeline. Triggers the build prompt (yes/no card) when the helper exe is missing. Windows only; surfaces a warning on Linux/macOS.", + "parameters": [], + "phrasings": [ + "'sync' 'notification' 's'?" + ] + }, + { + "actionName": "testOsNotification", + "description": "Inject a synthetic notification through the agent pipeline (filters, rate limit, dismiss tracking) β€” useful for verifying the agent end-to-end without an OS notification source.", + "parameters": [ + { + "name": "message", + "type": "string", + "optional": false, + "description": "Notification body text." + }, + { + "name": "app", + "type": "string", + "optional": true, + "description": "Optional app name to attach (matched against allowList/blockList)." + }, + { + "name": "title", + "type": "string", + "optional": true, + "description": "Optional notification title." + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "photo", + "category": "Creative & Media", + "emoji": "πŸ“·", + "description": "Agent to take and upload photos", + "schemas": [ + { + "schemaName": "photo", + "description": "Photo agent that helps the user provide images to the system either by taking pictures or uploading existing images.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "takePhoto", + "description": "uses a camera attached to the system to take a photo", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "player", + "category": "Music & Entertainment", + "emoji": "🎧", + "description": "Agent to play music", + "schemas": [ + { + "schemaName": "player", + "description": "Music Player agent that lets you search for, play and control music.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addCurrentTrackToPlaylist", + "description": "add the currently playing track to a playlist", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to add the current track to" + } + ], + "phrasings": [] + }, + { + "actionName": "addSongsToPlaylist", + "description": "add songs to a playlist by specifying track names and optional artists this action searches for each song and adds it to the playlist", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to add songs to" + }, + { + "name": "songs", + "type": "{ … }[]", + "optional": false, + "description": "list of songs to add, each specified by track name and optional artist/album" + } + ], + "phrasings": [] + }, + { + "actionName": "addToPlaylistFromCurrentTrackList", + "description": "add to the named playlist one or more tracks starting at index 'trackNumber' in the current track list", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to add the track to" + }, + { + "name": "trackNumber", + "type": "number", + "optional": false, + "description": "1-based index of the first track to add" + }, + { + "name": "trackCount", + "type": "number", + "optional": true, + "description": "number of tracks to add (default 1) if specified, adds this many tracks starting from trackNumber; for example, to add tracks 3,4,5 set trackNumber=3 and trackCount=3" + } + ], + "phrasings": [] + }, + { + "actionName": "changeVolume", + "description": "change volume plus or minus a specified percentage", + "parameters": [ + { + "name": "volumeChangePercentage", + "type": "number", + "optional": false, + "description": "volume change percentage" + } + ], + "phrasings": [] + }, + { + "actionName": "createPlaylist", + "description": "create a new playlist, optionally with a list of songs specified by title and artist", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to create" + }, + { + "name": "songs", + "type": "{ … }[]", + "optional": true, + "description": "optional list of songs to add to the playlist when creating it" + } + ], + "phrasings": [] + }, + { + "actionName": "deletePlaylist", + "description": "delete a playlist", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to delete" + } + ], + "phrasings": [] + }, + { + "actionName": "findMusic", + "description": "Find / search for / look up music and show it as the current track list WITHOUT starting playback. Use this for \"find\", \"look for\", \"search for\", \"show me\" music β€” a specific song, artist, album, genre, a free-form description (e.g. \"the best guitar solos\"), or playlists. To start playing immediately instead, use PlayMusicAction.", + "parameters": [ + { + "name": "target", + "type": "PlayByTrack | PlayByArtist | PlayByAlbum | PlayByGenre | PlayByPlaylist | PlayByDescription | PlayAnyMusic", + "optional": false, + "description": "What music to look for. Same choices as PlayMusicAction's target." + }, + { + "name": "play", + "type": "boolean", + "optional": true, + "description": "Set true ONLY when the user asks to find something AND play it in the same breath (e.g. \"find some Bach and play it\"). Leave unset for a plain search / \"show me\"." + }, + { + "name": "quantity", + "type": "number", + "optional": true, + "description": "number of tracks when the user asks for a specific amount" + } + ], + "phrasings": [] + }, + { + "actionName": "getAlbum", + "description": "get currently playing album set the current track list the tracks in the album", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "getFavorites", + "description": "result of this action is an entity of type 'track-list' with the user's favorites", + "parameters": [ + { + "name": "count", + "type": "number", + "optional": true, + "description": "number of favorites to get" + } + ], + "phrasings": [] + }, + { + "actionName": "getFromCurrentPlaylistList", + "description": "", + "parameters": [ + { + "name": "playlistNumber", + "type": "number", + "optional": false, + "description": "1-base index of playlist in the current playlist list to get" + } + ], + "phrasings": [] + }, + { + "actionName": "getPlaylist", + "description": "get playlist by name", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to get" + } + ], + "phrasings": [] + }, + { + "actionName": "getQueue", + "description": "set the current track list to the queue of upcoming tracks", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "listDevices", + "description": "list available playback devices", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "listPlaylists", + "description": "list all playlists", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "next", + "description": "next track", + "parameters": [], + "phrasings": [ + "skip track", + "skip", + "next" + ] + }, + { + "actionName": "pause", + "description": "pause playback", + "parameters": [], + "phrasings": [ + "pause the music", + "pause music", + "pause" + ] + }, + { + "actionName": "playFromCurrentTrackList", + "description": "play the track single track at index 'trackNumber' in the current track list report if there is no current track list", + "parameters": [ + { + "name": "trackNumber", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "playMusic", + "description": "Start playing music now. Use this when the user wants to play / put on / start / listen to music β€” a specific song, artist, album, genre, a free-form description (e.g. \"chill music for studying\"), or just \"some music\". To search for or browse music WITHOUT starting playback, use FindMusicAction.", + "parameters": [ + { + "name": "target", + "type": "PlayByTrack | PlayByArtist | PlayByAlbum | PlayByGenre | PlayByPlaylist | PlayByDescription | PlayAnyMusic", + "optional": false, + "description": "What music to select. Pick the ONE variant that best matches what the user named. If it isn't clearly one artist / album / genre, use { kind: \"description\" } with the user's own words as the query." + }, + { + "name": "quantity", + "type": "number", + "optional": true, + "description": "number of tracks when the user asks for a specific amount" + } + ], + "phrasings": [] + }, + { + "actionName": "playPlaylist", + "description": "play the named play list", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "name of playlist to play" + } + ], + "phrasings": [] + }, + { + "actionName": "previous", + "description": "previous track User: play the last song again", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "resumePlayback", + "description": "Resume or continue playback of the current track after it was paused or stopped. This simply un-pauses and keeps the current music going; it does not select a new song, so no specific song needs to be identified.", + "parameters": [], + "phrasings": [ + "resume the music", + "resume music", + "resume" + ] + }, + { + "actionName": "selectDevice", + "description": "select playback device by keyword", + "parameters": [ + { + "name": "deviceName", + "type": "string", + "optional": false, + "description": "device name such as \"bedroom\", \"living room\", \"whole house\", \"office computer\", etc." + } + ], + "phrasings": [] + }, + { + "actionName": "setDefaultDevice", + "description": "", + "parameters": [ + { + "name": "deviceName", + "type": "string", + "optional": true, + "description": "device name. If not specified, the current selected device is set as the default." + } + ], + "phrasings": [] + }, + { + "actionName": "setMaxVolume", + "description": "set max volume for the current device", + "parameters": [ + { + "name": "newMaxVolumeLevel", + "type": "number", + "optional": false, + "description": "new volume level" + } + ], + "phrasings": [] + }, + { + "actionName": "setVolume", + "description": "set volume", + "parameters": [ + { + "name": "newVolumeLevel", + "type": "number", + "optional": false, + "description": "new volume level" + } + ], + "phrasings": [] + }, + { + "actionName": "showSelectedDevice", + "description": "show the selected playback device", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "shuffle", + "description": "turn shuffle on or off", + "parameters": [ + { + "name": "on", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "status", + "description": "show now playing including track information, and playback status including playback device", + "parameters": [], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "powershell", + "category": "Development & Automation", + "emoji": "πŸ“œ", + "description": "Agent to create and execute PowerShell workflows", + "schemas": [ + { + "schemaName": "powershell", + "description": "Core flow management actions for creating, listing, and executing PowerShell scripts.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "createPowerShellFlow", + "description": "Create a new PowerShell flow with grammar rules for future reuse", + "parameters": [ + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "camelCase identifier for the new flow" + }, + { + "name": "description", + "type": "string", + "optional": false, + "description": "What this script does" + }, + { + "name": "displayName", + "type": "string", + "optional": false, + "description": "Human-readable name" + }, + { + "name": "script", + "type": "string", + "optional": false, + "description": "PowerShell script body (should include param() block)" + }, + { + "name": "scriptParameters", + "type": "{ name, type, required, description, default }[]", + "optional": false, + "description": "Script parameters" + }, + { + "name": "grammarPatterns", + "type": "{ pattern, isAlias }[]", + "optional": false, + "description": "Grammar patterns for matching" + }, + { + "name": "allowedCmdlets", + "type": "string[]", + "optional": false, + "description": "PowerShell cmdlets the script uses" + }, + { + "name": "allowedModules", + "type": "string[]", + "optional": true, + "description": "PowerShell modules to import for the script's cmdlets (e.g. [\"NetTCPIP\"] for Get-NetTCPConnection). Include every module required by allowedCmdlets β€” use the same list that made testPowerShellFlow pass, or the flow will fail at invocation with \"not recognized\"." + } + ], + "phrasings": [] + }, + { + "actionName": "deletePowerShellFlow", + "description": "Delete a PowerShell flow by name", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "Name of the PowerShell flow to delete" + } + ], + "phrasings": [ + "delete powershell flow {name}" + ] + }, + { + "actionName": "editPowerShellFlow", + "description": "Edit an existing PowerShell flow's script body (preserves grammar patterns and parameters)", + "parameters": [ + { + "name": "flowName", + "type": "string", + "optional": false, + "description": "Name of the PowerShell flow to edit" + }, + { + "name": "script", + "type": "string", + "optional": false, + "description": "New PowerShell script body (should include param() block matching existing parameters)" + }, + { + "name": "allowedCmdlets", + "type": "string[]", + "optional": false, + "description": "Updated list of PowerShell cmdlets the script uses" + }, + { + "name": "allowedModules", + "type": "string[]", + "optional": true, + "description": "Updated list of PowerShell modules to import (optional; preserved if omitted)" + } + ], + "phrasings": [] + }, + { + "actionName": "executePowerShellFlow", + "description": "Execute a registered PowerShell flow by name with parameters", + "parameters": [ + { + "name": "flowName", + "type": "string", + "optional": false, + "description": "Name of the PowerShell flow to execute (use listPowerShellFlows to see available flows)" + }, + { + "name": "flowArgs", + "type": "string", + "optional": true, + "description": "Captured arguments from the user's request (e.g. a path or filter)" + }, + { + "name": "flowParametersJson", + "type": "string", + "optional": true, + "description": "JSON string of named parameters e.g. '{\"Directory\":\"C:\\\\Users\",\"Pattern\":\"*.txt\"}'" + } + ], + "phrasings": [] + }, + { + "actionName": "importPowerShellFlow", + "description": "Import an existing PowerShell script file as a new PowerShell flow", + "parameters": [ + { + "name": "filePath", + "type": "string", + "optional": false, + "description": "Absolute or relative path to the .ps1 file to import" + }, + { + "name": "actionName", + "type": "string", + "optional": true, + "description": "Optional: override the generated action name" + } + ], + "phrasings": [ + "import script {filePath}" + ] + }, + { + "actionName": "listPowerShellFlows", + "description": "Lists all registered PowerShell flows", + "parameters": [], + "phrasings": [ + "show me powershell flows" + ] + } + ] + } + ] + }, + { + "name": "screencapture", + "category": "Creative & Media", + "emoji": "πŸŽ₯", + "description": "Take screenshots and record the screen or specific application windows", + "schemas": [ + { + "schemaName": "screencapture", + "description": "Screen capture and recording. Take screenshots of the full screen or a named window. Start and stop screen recordings. List visible windows by name.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "listWindows", + "description": "List all currently visible windows so the user can target them by name.", + "parameters": [], + "phrasings": [ + "programs are running", + "windows are open", + "show me open windows", + "open windows", + "windows" + ] + }, + { + "actionName": "recording", + "description": "Activity type tracked while a recording is in progress.", + "parameters": [ + { + "name": "target", + "type": "string", + "optional": true, + "description": "" + }, + { + "name": "outputPath", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "startedAtMs", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "startRecording", + "description": "Start a screen recording. Same `target` semantics as takeScreenshot. Only one recording can be active at a time.", + "parameters": [ + { + "name": "target", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "record {target}", + "begin recording {target}", + "start recording {target}", + "desktop", + "screen", + "begin recording screen", + "a screen recording", + "recording" + ] + }, + { + "actionName": "stopRecording", + "description": "Stop the currently active screen recording.", + "parameters": [], + "phrasings": [ + "finish recording", + "end recording", + "the recording", + "recording" + ] + }, + { + "actionName": "takeScreenshot", + "description": "Take a screenshot. If `target` is omitted the whole primary screen is captured; otherwise `target` is a program or window name to fuzzy-match against the currently visible windows (e.g. \"Chrome\", \"Visual Studio\").", + "parameters": [ + { + "name": "target", + "type": "string", + "optional": true, + "description": "" + } + ], + "phrasings": [ + "screenshot {target}", + "capture {target}", + "take a screenshot of {target}", + "desktop", + "screen", + "capture screen", + "take a screenshot" + ] + } + ] + } + ] + }, + { + "name": "settings", + "category": "System & Devices", + "emoji": "βš™οΈ", + "description": "Agent to manipulate system settings", + "schemas": [ + { + "schemaName": "settings", + "description": "Settings agent that helps the user change system settings.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "adjustMultiMonitorLayoutAction", + "description": "An action to adjust multi-monitor layout", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + } + ], + "phrasings": [] + }, + { + "actionName": "dimBrightNessAction", + "description": "An action to dim or brighten the screen", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "studio", + "category": "Development & Automation", + "emoji": "🎨", + "description": "TypeAgent Studio agent β€” the AI / conversational presenter of TypeAgent Studio, driving the author β†’ tune β†’ validate developer loop over the Studio engine. Today it surfaces inspection of Studio's environment, grammar collisions, and event stream; the rest of the loop (sandboxes, run, validate, author) is being added.", + "schemas": [ + { + "schemaName": "studio", + "description": "Converse with TypeAgent Studio β€” the AI presenter of the author β†’ tune β†’ validate loop a developer runs in the Studio extension. Currently available: report Studio's environment and agent locations, list known cross-schema grammar collisions, and show recent Studio events. Run, validate, and author actions (over the same typed Studio service the extension uses) are being added.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "getStudioInfo", + "description": "Report Studio's environment: the repository root it is inspecting and the directories (\"agent locations\") it scans for agents, with how many agent packages each contains. Use this to confirm Studio is pointed at the right place. Read-only.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "listCollisions", + "description": "List the cross-schema grammar collisions Studio currently knows about (newest first). These are populated by collision scans; the list is empty until a scan has run. Read-only.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "queryEvents", + "description": "Show the most recent entries from Studio's structured event stream (sandbox/collision/replay/feedback events), oldest-to-newest. Read-only.", + "parameters": [ + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of recent events to return (default 20)." + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "system", + "category": "Core", + "emoji": "πŸ”§", + "description": "", + "schemas": [ + { + "schemaName": "system.config", + "description": "System agent that helps you manage system settings and preferences.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "enterAgentPriorityMode", + "description": "Puts a specific agent into priority mode", + "parameters": [ + { + "name": "agentName", + "type": "string", + "optional": false, + "description": "the agent name or wildcard match string (* to match all agents)" + } + ], + "phrasings": [] + }, + { + "actionName": "exitAgentPriorityMode", + "description": "Leaves agent priority mode", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "listAgents", + "description": "Shows the list of available agents", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "toggleAgent", + "description": "Toggle use of LLM, agent or AI.", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + }, + { + "name": "agentNames", + "type": "string[]", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "toggleDeveloperMode", + "description": "Toggle developer mode.", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "toggleExplanation", + "description": "Toggle explanation.", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "system.conversation", + "description": "System agent that manages TypeAgent shell conversations. Use this agent when the user wants to: CREATE a new conversation (e.g. 'create a new conversation', 'start a new conversation called test', 'new conversation named work', 'open a new conversation test'), LIST conversations (e.g. 'list our conversations', 'list my conversations', 'show all conversations', 'what conversations do I have'), SWITCH to an existing conversation by name (e.g. 'switch to conversation test', 'go to my work conversation', 'switch to test'), advance to the NEXT conversation (e.g. 'switch to next conversation', 'next conversation', 'go to the next conversation'), go to the PREVIOUS conversation (e.g. 'switch to previous conversation', 'previous conversation'), DELETE a conversation (e.g. 'delete conversation test', 'remove the work conversation'), RENAME the current conversation, or SHOW info about the current conversation. Use this for TypeAgent shell conversations only β€” NOT for media playlists, songs, files, or browser tabs.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "deleteConversation", + "description": "Delete a conversation by name. Use this when the user wants to remove, delete, or destroy a conversation. Examples: \"delete conversation test\", \"remove the work conversation\", \"delete test2\".", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the conversation to delete" + } + ], + "phrasings": [ + "{polite} delete conversation {name}", + "{polite} delete {name} conversation" + ] + }, + { + "actionName": "listConversation", + "description": "List all conversations in this TypeAgent shell session. Use this when the user wants to see, show, or list their TypeAgent conversations (NOT files, songs, or any other kind of list). Examples: \"list our conversations\", \"list my conversations\", \"show all conversations\", \"what conversations do I have\", \"show me my conversations\", \"show conversation list\".", + "parameters": [], + "phrasings": [ + "{polite} list conversations", + "{polite} what conversations have" + ] + }, + { + "actionName": "newConversation", + "description": "Create a new conversation and optionally give it a name. Use this when the user wants to create, start, make, or open a brand-new conversation. Examples: \"create a new conversation\", \"start a new conversation called test\", \"make a new conversation\", \"new conversation named work\", \"open a new conversation test\". IMPORTANT: use this only when the user is creating something new, not switching to an existing one.", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional name for the new conversation" + } + ], + "phrasings": [ + "{polite} create new conversation called {name}", + "{polite} create new conversation", + "{polite} new conversation" + ] + }, + { + "actionName": "nextConversation", + "description": "Switch to the NEXT TypeAgent conversation in the list (cycles around). Use this when the user wants to advance to the next TypeAgent shell conversation (NOT the next song, next track, next page, or any other kind of \"next\"). Examples: \"switch to next conversation\", \"next conversation\", \"go to the next conversation\", \"cycle to the next conversation\".", + "parameters": [], + "phrasings": [ + "{polite} next conversation", + "{polite} advance to next conversation" + ] + }, + { + "actionName": "prevConversation", + "description": "Switch to the PREVIOUS TypeAgent conversation in the list (cycles around). Use this when the user wants to go to the previous TypeAgent shell conversation (NOT the previous song, previous track, or any other kind of \"previous\"). Examples: \"switch to previous conversation\", \"previous conversation\", \"go to the previous conversation\", \"cycle to the previous conversation\".", + "parameters": [], + "phrasings": [ + "{polite} previous conversation", + "{polite} back to previous conversation" + ] + }, + { + "actionName": "renameConversation", + "description": "Rename a conversation. Use this when the user wants to rename, relabel, or give a new name to a conversation. If the user specifies which conversation to rename, capture it as 'name'. If the user only says \"rename this conversation\" or \"rename current conversation\", omit 'name'. Examples: \"rename this conversation to work\", \"rename test7 to test5\", \"call this conversation research\", \"rename current conversation to my project\".", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Optional: the current name of the conversation to rename. Omit to rename the active conversation." + }, + { + "name": "newName", + "type": "string", + "optional": false, + "description": "The new name for the conversation" + } + ], + "phrasings": [ + "{polite} rename this conversation to {newName}", + "{polite} call this conversation {newName}", + "{polite} rename conversation {name} to {newName}" + ] + }, + { + "actionName": "showConversationInfo", + "description": "Show information about the current conversation. Use this when the user asks about the current conversation info. Examples: \"show conversation info\", \"what conversation am I in\", \"current conversation info\".", + "parameters": [], + "phrasings": [ + "{polite} show conversation info", + "{polite} what conversation am i in", + "{polite} current conversation info" + ] + }, + { + "actionName": "switchConversation", + "description": "Switch to an existing conversation by name. Use this when the user wants to switch to, go to, open, or change to an EXISTING conversation identified by name. For \"next\"/\"previous\" without a specific name, use NextConversationAction or PrevConversationAction instead. Examples: \"switch to conversation test\", \"go to my work conversation\", \"switch to test\", \"open conversation named work\", \"change to the test conversation\". IMPORTANT: use this only when switching to an already-existing conversation, not creating a new one.", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the conversation to switch to" + } + ], + "phrasings": [ + "{polite} switch to conversation {name}", + "{polite} switch conversation to {name}", + "{polite} switch to {name} conversation", + "{polite} open conversation {name}" + ] + } + ] + }, + { + "schemaName": "system.describe", + "description": "Describe what an agent or action can do β€” capability discovery, not execution. Use this when the user wants to know WHAT an agent or action does, e.g. 'what can the spotify agent do', 'describe the calendar agent', 'show me all of spotify's actions', 'what does the play action do', 'describe spotify's play action'. This works even for installed-but-disabled agents. Do NOT use this to execute an action or to list all agents (that's a config command).", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "describeAction", + "description": "Explain a single action in detail, beyond its one-line schema description. Examples: \"what does the play action do\", \"describe spotify's play action\", \"describe the play action from spotify\".", + "parameters": [ + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "the action to describe" + }, + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "optional owning agent, when the user names one" + } + ], + "phrasings": [] + }, + { + "actionName": "describeAgent", + "description": "Describe what an agent can do: a natural-language summary plus its actions. Works for installed-but-disabled agents too β€” describing is informational and does not require the agent to be enabled. Examples: \"what can the spotify agent do\", \"describe spotify\", \"show me all of spotify's actions\", \"list everything calendar can do\".", + "parameters": [ + { + "name": "agentName", + "type": "string", + "optional": false, + "description": "the agent to describe" + }, + { + "name": "all", + "type": "boolean", + "optional": true, + "description": "true when the user asks for ALL actions, not the default set" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "system.grammar", + "description": "Manage dynamic grammar rules learned from user interactions. List all rules or filter by agent (e.g. 'show grammar rules for calendar', 'list player grammar rules'). Show rule details or delete individual rules by ID ('show grammar rule 3', 'delete grammar rule 5'). Clear all rules or rules for one agent ('clear calendar grammar rules').", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "clearRules", + "description": "", + "parameters": [ + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "Agent/schema name to clear rules for (e.g. \"player\", \"calendar\", \"email\"). Omit to clear ALL rules across all agents." + } + ], + "phrasings": [] + }, + { + "actionName": "deleteRule", + "description": "", + "parameters": [ + { + "name": "id", + "type": "number", + "optional": false, + "description": "Numeric ID of the rule to delete, as shown in listRules output" + } + ], + "phrasings": [] + }, + { + "actionName": "listRules", + "description": "", + "parameters": [ + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "Agent/schema name to filter by (e.g. \"player\", \"calendar\", \"email\", \"browser\"). Omit to list all rules across all agents. Use the short agent name, not the npm package name (e.g. \"player\" not \"music\")." + } + ], + "phrasings": [] + }, + { + "actionName": "showRule", + "description": "", + "parameters": [ + { + "name": "id", + "type": "number", + "optional": false, + "description": "Numeric ID of the rule to inspect, as shown in listRules output" + } + ], + "phrasings": [] + } + ] + }, + { + "schemaName": "system.history", + "description": "System agent that helps manage chat history.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "clearHistory", + "description": "Clears the chat history", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "deleteHistory", + "description": "Deletes a specific message from the chat history", + "parameters": [ + { + "name": "messageNumber", + "type": "number", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "listHistory", + "description": "Shows the chat history", + "parameters": [], + "phrasings": [] + } + ] + }, + { + "schemaName": "system.notify", + "description": "System agent that helps manage notifications.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "clearNotifications", + "description": "Clears the notifications", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "showNotifications", + "description": "Shows notifications based on the supplied filter", + "parameters": [ + { + "name": "filter", + "type": "\"all\" | \"unread\"", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "showNotificationSummary", + "description": "Shows notification summary", + "parameters": [], + "phrasings": [] + } + ] + }, + { + "schemaName": "system.settings", + "description": "Manage persistent user settings for the TypeAgent system. Set whether the agent server starts hidden in the background (e.g. 'start the server hidden', 'run the server in the background', 'show the server window'). Set the idle timeout for the agent server (e.g. 'shut down the server after 5 minutes of inactivity', 'set idle timeout to 300 seconds', 'disable idle timeout'). Set whether to resume the last conversation on startup (e.g. 'always resume my last conversation', 'pick up where I left off', 'start fresh each time'). Enable or disable inline autocomplete suggestions in the CLI and Shell (e.g. 'turn off autocomplete', 'disable tab completion', 'enable autocomplete', 'turn on inline suggestions').", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "setAutoComplete", + "description": "Enable or disable inline autocomplete suggestions in the CLI input. Use when the user says things like \"turn off autocomplete\", \"disable tab completion\", \"stop showing completions\", \"enable autocomplete\", \"turn on inline suggestions\".", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "true = show inline completions as the user types; false = disable autocomplete" + } + ], + "phrasings": [] + }, + { + "actionName": "setConversationResume", + "description": "Set whether to resume the last conversation on startup. Use when the user says things like \"always resume my last conversation\", \"pick up where I left off\", \"don't resume my last conversation\", \"start fresh each time\".", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "true = resume the last conversation on startup; false = always start with the default conversation" + } + ], + "phrasings": [] + }, + { + "actionName": "setIdleTimeout", + "description": "Set the idle timeout for the agent server in seconds (0 = disabled). Use when the user says things like \"shut down the server after 5 minutes of inactivity\", \"set idle timeout to 300 seconds\", \"disable idle timeout\", \"never shut down automatically\".", + "parameters": [ + { + "name": "seconds", + "type": "number", + "optional": false, + "description": "Timeout in seconds. 0 means the server never shuts down automatically due to inactivity." + } + ], + "phrasings": [] + }, + { + "actionName": "setServerHidden", + "description": "Set whether the agent server starts as a hidden background process. Use when the user says things like \"start the server hidden\", \"run the server in the background\", \"don't show a server window\", \"show the server window on startup\".", + "parameters": [ + { + "name": "enable", + "type": "boolean", + "optional": false, + "description": "true = server starts as hidden background process; false = server starts with a visible window" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "taskflow", + "category": "Development & Automation", + "emoji": "πŸ”", + "description": "User-taught macros over TypeAgent actions", + "schemas": [ + { + "schemaName": "taskflow", + "description": "Task flow actions. Flows are taught from examples and registered for reuse.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "deleteTaskFlow", + "description": "Delete a task flow by name", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "listTaskFlows", + "description": "Lists all registered task flows", + "parameters": [], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "timer", + "category": "Productivity", + "emoji": "⏰", + "description": "Agent for setting reminders that fire as agent-initiated messages", + "schemas": [ + { + "schemaName": "timer", + "description": "Timer agent: set, list, and cancel reminders. When a reminder fires, the agent pushes a message to the chat via SessionContext.beginAgentThread (no preceding user request).", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "cancelReminder", + "description": "Cancel a pending reminder by id, or all reminders.", + "parameters": [ + { + "name": "id", + "type": "string", + "optional": false, + "description": "The reminder id (returned from setReminder), or \"all\" to cancel every pending reminder." + } + ], + "phrasings": [ + "clear reminders", + "all reminders" + ] + }, + { + "actionName": "listReminders", + "description": "List all pending reminders.", + "parameters": [], + "phrasings": [ + "what reminders", + "reminders" + ] + }, + { + "actionName": "repeatReminder", + "description": "Set a repeating reminder. Fires every `every` interval until cancelled (or until `count` fires have elapsed if specified). Useful for stress testing rapid-fire / back-to-back agent-initiated messages.", + "parameters": [ + { + "name": "message", + "type": "string", + "optional": false, + "description": "What to remind the user about." + }, + { + "name": "every", + "type": "string", + "optional": false, + "description": "Interval between fires. Same format as SetReminder.when β€” a duration like \"5s\", \"10m\", \"1h\"." + }, + { + "name": "kind", + "type": "\"bubble\" | \"toast\" | \"inline\"", + "optional": true, + "description": "Render style for each fire. Defaults to \"bubble\"." + }, + { + "name": "count", + "type": "number", + "optional": true, + "description": "Optional max number of times to fire. If omitted, fires until explicitly cancelled." + } + ], + "phrasings": [ + "inline", + "toast" + ] + }, + { + "actionName": "setReminder", + "description": "Set a reminder. When it fires, the timer agent pushes a message to the user via SessionContext.beginAgentThread (an agent-initiated thread, not a response to a user request).", + "parameters": [ + { + "name": "message", + "type": "string", + "optional": false, + "description": "What to remind the user about." + }, + { + "name": "when", + "type": "string", + "optional": false, + "description": "When to fire. Either a duration like \"5s\", \"10m\", \"1h\", \"30 minutes\", or an absolute ISO 8601 timestamp like \"2026-05-04T15:30:00\"." + }, + { + "name": "kind", + "type": "\"bubble\" | \"toast\" | \"inline\"", + "optional": true, + "description": "How the fired reminder should render. Defaults to \"bubble\"." + } + ], + "phrasings": [ + "bubble", + "inline", + "toast" + ] + } + ] + } + ] + }, + { + "name": "utility", + "category": "Development & Automation", + "emoji": "πŸ”§", + "description": "Utility agent β€” web search, web fetch, and file I/O for compiled task flows", + "schemas": [ + { + "schemaName": "utility", + "description": "Utility operations: search the web, fetch page content, read and write files", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "claudeTask", + "description": "", + "parameters": [ + { + "name": "goal", + "type": "string", + "optional": false, + "description": "Goal or question for Claude to accomplish using its tools (web search, fetch, etc.)" + }, + { + "name": "parseJson", + "type": "boolean", + "optional": true, + "description": "Parse the result as JSON and store in historyText (default: false)" + }, + { + "name": "model", + "type": "string", + "optional": true, + "description": "Claude model ID (default: claude-haiku-4-5-20251001)" + }, + { + "name": "maxTurns", + "type": "number", + "optional": true, + "description": "Maximum agentic turns (default: 10)" + } + ], + "phrasings": [] + }, + { + "actionName": "llmTransform", + "description": "", + "parameters": [ + { + "name": "input", + "type": "string", + "optional": false, + "description": "Text or HTML to transform" + }, + { + "name": "prompt", + "type": "string", + "optional": false, + "description": "Instructions for the transformation" + }, + { + "name": "parseJson", + "type": "boolean", + "optional": true, + "description": "Parse the result as JSON and store in historyText (default: false)" + }, + { + "name": "htmlOutput", + "type": "boolean", + "optional": true, + "description": "When true, LLM is asked to return HTML; result is stored in historyText as HTML (default: false)" + }, + { + "name": "model", + "type": "string", + "optional": true, + "description": "Claude model ID (default: claude-haiku-4-5-20251001)" + } + ], + "phrasings": [] + }, + { + "actionName": "readFile", + "description": "", + "parameters": [ + { + "name": "path", + "type": "string", + "optional": false, + "description": "Absolute or relative path to the file" + } + ], + "phrasings": [ + "get {path}", + "read {path}" + ] + }, + { + "actionName": "webFetch", + "description": "", + "parameters": [ + { + "name": "url", + "type": "string", + "optional": false, + "description": "URL to fetch" + } + ], + "phrasings": [ + "get {url}", + "fetch {url}" + ] + }, + { + "actionName": "webSearch", + "description": "", + "parameters": [ + { + "name": "query", + "type": "string", + "optional": false, + "description": "Search query text" + }, + { + "name": "numResults", + "type": "number", + "optional": true, + "description": "Number of results to return (default: 5)" + } + ], + "phrasings": [ + "look up {query} online", + "find {query} on the web", + "web search {query}", + "search web for {query}" + ] + }, + { + "actionName": "writeFile", + "description": "", + "parameters": [ + { + "name": "path", + "type": "string", + "optional": false, + "description": "Absolute or relative path to the file" + }, + { + "name": "content", + "type": "string", + "optional": false, + "description": "Text content to write" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "video", + "category": "Creative & Media", + "emoji": "πŸ“Ή", + "description": "Agent to create AI videos", + "schemas": [ + { + "schemaName": "video", + "description": "Video agent that creates videos using AI text to video modes.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "createVideoAction", + "description": "creates a video based on the supplied description", + "parameters": [ + { + "name": "originalRequest", + "type": "string", + "optional": false, + "description": "the original request of the user" + }, + { + "name": "caption", + "type": "string", + "optional": false, + "description": "the video caption" + }, + { + "name": "relatedFiles", + "type": "string[]", + "optional": true, + "description": "The file names of any attachments the user provided" + }, + { + "name": "duration", + "type": "\"4\" | \"8\" | \"12\"", + "optional": true, + "description": "The duration in seconds (default is 4 seconds)" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "visualStudio", + "category": "Development & Automation", + "emoji": "πŸ’»", + "description": "Visual Studio integration for editor, solution, build, and debug actions via the EnvDTE automation API", + "schemas": [ + { + "schemaName": "visualStudio", + "description": "Visual Studio agent actions: file ops, edit, solution build, and debugger control", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addBreakpoint", + "description": "Adds a new breakpoint to the debugger. IMPORTANT: Ensure the file path and line number are correct. User: \"Can you add a breakpoint at line 42 in main.cpp?\" Agent: \"Adding a breakpoint at line 42 in main.cpp.\"", + "parameters": [ + { + "name": "file", + "type": "string", + "optional": false, + "description": "The file where the breakpoint will be added." + }, + { + "name": "line", + "type": "string", + "optional": false, + "description": "The line number where the breakpoint will be added (as a string; handler parses to int)." + }, + { + "name": "condition", + "type": "string", + "optional": true, + "description": "Optional condition for the breakpoint." + } + ], + "phrasings": [] + }, + { + "actionName": "break", + "description": "Causes the given process to pause its execution so that its current state can be analyzed. User: \"Pause the process right now.\" Agent: \"Pausing the process immediately.\"", + "parameters": [], + "phrasings": [ + "halt the process instantly", + "stop the current process for debugging", + "break the execution immediately", + "pause the process right now" + ] + }, + { + "actionName": "build", + "description": "Causes the active solution configuration to begin building. User: \"Can you start building the solution, please?\" Agent: \"Starting to build the solution.\"", + "parameters": [ + { + "name": "waitForCompletion", + "type": "boolean", + "optional": true, + "description": "Whether to wait for the build to complete." + } + ], + "phrasings": [ + "begin building the solution", + "initiate the build process for the current solution", + "build the active solution configuration", + "start building the solution" + ] + }, + { + "actionName": "clean", + "description": "Deletes all compiler-generated support files for marked projects. User: \"Can you clean the solution, please?\" Agent: \"Cleaning the solution.\"", + "parameters": [ + { + "name": "waitForCompletion", + "type": "boolean", + "optional": true, + "description": "Whether to wait for the clean operation to complete." + } + ], + "phrasings": [ + "delete all the build files for the projects", + "clean up the compiler generated files", + "marked projects", + "solution" + ] + }, + { + "actionName": "closeAll", + "description": "Closes all open documents in the environment and optionally saves them. User: \"Close all open files, please.\" Agent: \"Closing all open files.\"", + "parameters": [ + { + "name": "saveChanges", + "type": "boolean", + "optional": true, + "description": "Whether to save changes before closing." + } + ], + "phrasings": [ + "shut all the documents", + "close everything that's open and save any changes", + "the open documents", + "open files" + ] + }, + { + "actionName": "debug", + "description": "Starts debugging the solution. User: \"Can you start debugging the solution?\" Agent: \"Starting to debug the solution.\"", + "parameters": [], + "phrasings": [ + "debug the solution", + "begin debugging the project", + "the debug process for the solution", + "debugging the solution" + ] + }, + { + "actionName": "executeCommand", + "description": "Executes a command in the Visual Studio environment. User: \"Can you run the command in Visual Studio for me?\" Agent: \"Executing the command in Visual Studio.\"", + "parameters": [ + { + "name": "commandName", + "type": "string", + "optional": false, + "description": "The name of the command to execute." + }, + { + "name": "commandArgs", + "type": "string", + "optional": true, + "description": "Optional arguments for the command." + } + ], + "phrasings": [] + }, + { + "actionName": "findInFiles", + "description": "Performs a search operation across files in the solution. User: \"Can you search for 'searchTerm' in all the files with 'fileTypes' extensions?\" Agent: \"Searching for 'searchTerm' in files with 'fileTypes' extensions.\"", + "parameters": [ + { + "name": "searchTerm", + "type": "string", + "optional": false, + "description": "The term to search for in the files." + }, + { + "name": "fileTypes", + "type": "string", + "optional": true, + "description": "Optional file types to limit the search." + } + ], + "phrasings": [] + }, + { + "actionName": "findText", + "description": "Searches for the given text from the active point to the end of the document. User: \"Can you search for the text 'example' from here to the end of the document?\" Agent: \"Searching for 'example' from the current position to the end of the document.\"", + "parameters": [ + { + "name": "text", + "type": "string", + "optional": false, + "description": "The text to search for in the document." + }, + { + "name": "caseSensitive", + "type": "boolean", + "optional": true, + "description": "Optional flags for the search operation." + }, + { + "name": "wholeWord", + "type": "boolean", + "optional": true, + "description": "" + }, + { + "name": "useRegex", + "type": "boolean", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "go", + "description": "Starts executing the program from the current statement. User: \"Run the program from here.\" Agent: \"Starting execution from the current statement.\"", + "parameters": [], + "phrasings": [ + "begin execution from the current statement", + "go ahead and run the program from the current line", + "start executing the code from this point", + "run the program from here" + ] + }, + { + "actionName": "gotoLine", + "description": "Moves to the beginning of the indicated line and selects the line if requested. User: \"Can you go to line 42 and select it?\" Agent: \"Going to line 42 and selecting it.\"", + "parameters": [ + { + "name": "line", + "type": "string", + "optional": false, + "description": "The line number to go to (as a string; handler parses to int)." + }, + { + "name": "select", + "type": "boolean", + "optional": true, + "description": "Whether to select the line after moving to it." + } + ], + "phrasings": [ + "navigate to line {line} without selecting it", + "jump to line {line} and highlight it", + "move to line {line}, but don't select it", + "go to line {line} and select it" + ] + }, + { + "actionName": "openFile", + "description": "Opens a file in the Visual Studio environment. User: \"Can you open the file located at [filePath] in Visual Studio?\" Agent: \"Opening the file at [filePath] in Visual Studio.\"", + "parameters": [ + { + "name": "filePath", + "type": "string", + "optional": false, + "description": "The path of the file to open." + }, + { + "name": "viewKind", + "type": "\"text\" | \"code\" | \"designer\" | \"debug\"", + "optional": true, + "description": "Default behavior is \"text\". Valid values: \"text\" | \"code\" | \"designer\" | \"debug\". The view kind to use when opening the file." + } + ], + "phrasings": [] + }, + { + "actionName": "redo", + "description": "Re-executes the last action that was undone by the Undo() method or the user. User: \"Can you redo the last change I undid?\" Agent: \"Redoing the last undone change.\"", + "parameters": [], + "phrasings": [ + "reapply the last undone action", + "redo the previous action", + "thing I reversed", + "change I undid" + ] + }, + { + "actionName": "removeBreakpoint", + "description": "Removes an existing breakpoint from the debugger. User: \"Please remove the breakpoint with ID 12345.\" Agent: \"Removing the breakpoint with ID 12345.\"", + "parameters": [ + { + "name": "breakpointId", + "type": "string", + "optional": true, + "description": "One of {breakpointId} or {file+line} must be provided. The ID of the breakpoint to be removed." + }, + { + "name": "file", + "type": "string", + "optional": true, + "description": "The file where the breakpoint is located." + }, + { + "name": "line", + "type": "string", + "optional": true, + "description": "The line number where the breakpoint is located (as a string; handler parses to int)." + } + ], + "phrasings": [] + }, + { + "actionName": "run", + "description": "Causes the active solution configuration to execute. User: \"Can you run the current solution for me?\" Agent: \"Running the current solution.\"", + "parameters": [], + "phrasings": [ + "start running the solution", + "execute the active solution configuration", + "active configuration", + "current solution" + ] + }, + { + "actionName": "saveAll", + "description": "Saves all documents currently open in the environment. User: \"Can you save all my open files, please?\" Agent: \"Saving all open files.\"", + "parameters": [], + "phrasings": [ + "save everything I'm working on", + "the files", + "documents currently open", + "my open files" + ] + }, + { + "actionName": "stepInto", + "description": "Steps into the next function call, if possible. User: \"Can you step into the next function call, please?\" Agent: \"Stepping into the next function call.\"", + "parameters": [], + "phrasings": [ + "function", + "function call" + ] + }, + { + "actionName": "stepOut", + "description": "Steps out of the current function. User: \"Can you step out of the current function, please?\" Agent: \"Stepping out of the current function.\"", + "parameters": [], + "phrasings": [ + "step out of this function", + "step out of the function", + "function" + ] + }, + { + "actionName": "stepOver", + "description": "Steps over the next function call. User: \"Can you step over the next function call, please?\" Agent: \"Stepping over the next function call.\"", + "parameters": [], + "phrasings": [ + "call" + ] + }, + { + "actionName": "stop", + "description": "Stops debugging, terminating, or detaching from all attached processes. User: \"Please stop the debugging process.\" Agent: \"Stopping the debugging process.\"", + "parameters": [], + "phrasings": [ + "end the current debugging session", + "terminate all attached processes", + "debugging and detach from everything", + "the debugging process" + ] + }, + { + "actionName": "undo", + "description": "Reverses the action last performed by the user in the document. User: \"Can you undo my last change?\" Agent: \"Undoing the last change.\"", + "parameters": [], + "phrasings": [ + "reverse the last action I did", + "last thing I did", + "previous edit", + "undo my last change" + ] + } + ] + } + ] + }, + { + "name": "weather", + "category": "Web & Knowledge", + "emoji": "β›…", + "description": "Agent to get weather information including current conditions, forecasts, and alerts", + "schemas": [ + { + "schemaName": "weather", + "description": "Weather agent with actions to get current conditions, forecasts, and alerts", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "getAlerts", + "description": "", + "parameters": [ + { + "name": "location", + "type": "string", + "optional": false, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "getCurrentConditions", + "description": "", + "parameters": [ + { + "name": "location", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "units", + "type": "\"celsius\" | \"fahrenheit\"", + "optional": true, + "description": "" + } + ], + "phrasings": [] + }, + { + "actionName": "getForecast", + "description": "", + "parameters": [ + { + "name": "location", + "type": "string", + "optional": false, + "description": "" + }, + { + "name": "days", + "type": "number", + "optional": true, + "description": "" + }, + { + "name": "units", + "type": "\"celsius\" | \"fahrenheit\"", + "optional": true, + "description": "" + } + ], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "windowsClock", + "category": "System & Devices", + "emoji": "⏰", + "description": "Windows Clock agent β€” set alarms, start timers, run the stopwatch, and add world clocks via the built-in Windows Alarms & Clock app.", + "schemas": [ + { + "schemaName": "windowsClock", + "description": "Windows Clock agent β€” set alarms, start timers, run the stopwatch, and add world clocks via the built-in Windows Alarms & Clock app.", + "defaultEnabled": true, + "transient": false, + "actions": [ + { + "actionName": "addWorldClock", + "description": "Add a new world clock by searching for a city and selecting it from suggestions.", + "parameters": [ + { + "name": "cityQuery", + "type": "string", + "optional": false, + "description": "Text to search for a matching city in the Add Clock box." + }, + { + "name": "suggestionItem", + "type": "string", + "optional": false, + "description": "The specific suggestion to select from the list (city, country)." + } + ], + "phrasings": [] + }, + { + "actionName": "createAlarm", + "description": "Create a new alarm with a specified name and time.", + "parameters": [ + { + "name": "alarmName", + "type": "string", + "optional": false, + "description": "Displayed name of the alarm." + }, + { + "name": "hour", + "type": "number", + "optional": false, + "description": "Hour component of the alarm time." + }, + { + "name": "minute", + "type": "number", + "optional": false, + "description": "Minute component of the alarm time." + } + ], + "phrasings": [] + }, + { + "actionName": "navigateToAlarmTab", + "description": "Switch to the Alarms tab in the app.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "navigateToFocusTab", + "description": "Switch to the Focus tab from the app's navigation.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "navigateToStopwatchTab", + "description": "Switch to the Stopwatch tab.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "navigateToTimerTab", + "description": "Switch to the Timer tab from another section.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "navigateToWorldClockTab", + "description": "Switch to the World Clock tab from another tab.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "recordLap", + "description": "Record a lap while the stopwatch continues running.", + "parameters": [], + "phrasings": [] + }, + { + "actionName": "renameTimer", + "description": "Rename an existing timer and save the changes.", + "parameters": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The new name to assign to the timer." + } + ], + "phrasings": [] + }, + { + "actionName": "setAlarmEnabled", + "description": "Turn on an existing alarm. [merged from: enableAlarm, disableAlarm]", + "parameters": [ + { + "name": "enabled", + "type": "boolean", + "optional": false, + "description": "Distinguishes enableAlarm / disableAlarm variants." + } + ], + "phrasings": [] + }, + { + "actionName": "setFocusSessionRunning", + "description": "Start the focus session when it is paused. [merged from: startFocusSession, pauseFocusSession]", + "parameters": [ + { + "name": "running", + "type": "boolean", + "optional": false, + "description": "Distinguishes startFocusSession / pauseFocusSession variants." + } + ], + "phrasings": [] + }, + { + "actionName": "setStopwatchRunning", + "description": "Pause the running stopwatch. [merged from: pauseStopwatch, resumeStopwatch]", + "parameters": [ + { + "name": "running", + "type": "boolean", + "optional": false, + "description": "Distinguishes pauseStopwatch / resumeStopwatch variants." + } + ], + "phrasings": [] + }, + { + "actionName": "setTimerViewMode", + "description": "Expand the timer into its alternate/compact view. [merged from: expandTimerView, restoreTimerView]", + "parameters": [ + { + "name": "mode", + "type": "\"compact\" | \"full\"", + "optional": false, + "description": "Distinguishes expandTimerView / restoreTimerView variants." + } + ], + "phrasings": [] + }, + { + "actionName": "startTimer", + "description": "Start or resume the timer from list or paused states.", + "parameters": [], + "phrasings": [] + } + ] + } + ] + }, + { + "name": "workflow", + "category": "Development & Automation", + "emoji": "πŸ”„", + "description": "Run automated developer workflows via chat", + "schemas": [ + { + "schemaName": "workflow", + "description": "Workflow agent: each discovered workflow is a distinct action with typed parameters", + "defaultEnabled": false, + "transient": false, + "actions": [ + { + "actionName": "noWorkflowsLoaded", + "description": "Placeholder: replaced at runtime by getDynamicSchema() with per-workflow action types derived from discovered workflow files.", + "parameters": [], + "phrasings": [] + } + ] + } + ] + } + ], + "systemCommands": [ + { + "path": "action", + "description": "Execute an action", + "group": false, + "args": [ + { + "name": "schemaName", + "type": "string", + "optional": false, + "description": "Action schema name" + }, + { + "name": "actionName", + "type": "string", + "optional": false, + "description": "Action name" + } + ], + "flags": [ + { + "name": "parameters", + "char": "", + "type": "json", + "default": "", + "description": "Action parameter" + }, + { + "name": "naturalLanguage", + "char": "", + "type": "string", + "default": "", + "description": "Natural language phrase to associate with this action for cache population" + } + ] + }, + { + "path": "clear", + "description": "Clear the console", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "clear deep", + "description": "Clear the console and wipe chat history, reasoning, activity, and persistent display log so nothing replays on rejoin", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "collision", + "description": "Inspect collision detection telemetry and run static collision analyses", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "collision corpus", + "description": "Generate phrase corpora, probe through the embedding ranker, and build the collision-hotspot visualizations", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "collision corpus generate", + "description": "Generate an LLM-authored phrase corpus for every action in this dispatcher's loaded schemas (slow: ~12 min for the full set)", + "group": false, + "args": [], + "flags": [ + { + "name": "schemas", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated schemas to scan. Empty = all loaded schemas." + }, + { + "name": "models", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated chat-model names from ts/.env. Default: GPT_4_1,GPT_5,GPT_5_NANO" + }, + { + "name": "styles", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated phrase styles to generate. Available: imperative,conversational,casual,polite,curt,slang,typos. Default: imperative,conversational,casual." + }, + { + "name": "concurrency", + "char": "", + "type": "number", + "default": "8", + "description": "Concurrent LLM calls (default 8)" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output corpus JSON file path (file name, not directory β€” use --workdir to choose the directory). Default: /collisions/corpus.json" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named output files. Default: /collisions" + } + ] + }, + { + "path": "collision corpus probe", + "description": "Replay a phrase corpus through the embedding ranker and classify each phrase as CLEAN / TIGHT / MISROUTE", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input corpus JSON path. Default: /corpus.json" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output probe-results JSON path. Default: /probe-results.json" + }, + { + "name": "top", + "char": "", + "type": "number", + "default": "5", + "description": "Candidate rows kept per probe (default 5)" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "Tight-vs-clean threshold (default 0.05)" + }, + { + "name": "concurrency", + "char": "", + "type": "number", + "default": "8", + "description": "Concurrent probes (default 8)" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision corpus reanalyze", + "description": "Re-classify saved probe results with prefix-aware action matching (recovers misroutes that were just naming differences)", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input probe-results JSON. Default: /probe-results.json" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output reclassified JSON. Default: /probe-results-reclassified.json" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "Tight-vs-clean threshold (default 0.05)" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision corpus recovery", + "description": "Decompose MISROUTE results by where the correct target ranks among the top-K candidates (which fix lever applies?)", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input reclassified probe-results JSON. Default: /probe-results-reclassified.json" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "llmSelect threshold for the rank-2 tight/wide split (default 0.05)" + } + ] + }, + { + "path": "collision corpus run", + "description": "Run the full corpus pipeline (generate β†’ probe β†’ reanalyze β†’ visualize) with consistent file naming", + "group": false, + "args": [], + "flags": [ + { + "name": "from", + "char": "", + "type": "string", + "default": "generate", + "description": "Resume from a step: generate | probe | reanalyze | visualize (default generate)" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for intermediate files. Default: /collisions" + }, + { + "name": "schemas", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated schemas (corpus only)" + }, + { + "name": "models", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated model names (corpus only)" + }, + { + "name": "styles", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated phrase styles (corpus only). Available: imperative,conversational,casual,polite,curt,slang,typos. Default: imperative,conversational,casual." + }, + { + "name": "concurrency", + "char": "", + "type": "number", + "default": "8", + "description": "LLM concurrency (corpus only, default 8)" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "Tight-vs-clean threshold (probe + reanalyze, default 0.05)" + }, + { + "name": "top", + "char": "", + "type": "number", + "default": "5", + "description": "Probe candidate rows (default 5)" + }, + { + "name": "sankey-top", + "char": "", + "type": "number", + "default": "60", + "description": "Sankey edge count (default 60)" + } + ] + }, + { + "path": "collision corpus translate", + "description": "Replay a phrase corpus through the LLM translator (cache/grammar/exec/fuzzy off) and classify each phrase as CLEAN / MISROUTE / CLARIFY / INVALID / ERROR. Distinct from 'corpus probe' β€” that one runs the embedding ranker; this runs the actual translator.", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input corpus JSON path. Default: /corpus.json" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output translation-results JSON file path (file name, not directory β€” use --workdir to choose the directory). Default: /translation-results.json (or translation-results-.json when --output-suffix is set)." + }, + { + "name": "concurrency", + "char": "", + "type": "number", + "default": "4", + "description": "Concurrent translator calls (default 4 β€” chat completions are expensive)" + }, + { + "name": "strategy", + "char": "", + "type": "string", + "default": "first-match", + "description": "llmSelect strategy to force during the run. Default 'first-match' (suppresses user-clarify short-circuit). Reserved: future runs will sweep multiple strategies in one go." + }, + { + "name": "max-phrases", + "char": "", + "type": "number", + "default": "", + "description": "Cap the run to N phrases (deterministic prefix). Useful for smoke tests." + }, + { + "name": "model-label", + "char": "", + "type": "string", + "default": "", + "description": "Label recorded in each row's `model` field. Reserved for future multi-model sweeps; defaults to 'default'." + }, + { + "name": "user-context-mode", + "char": "", + "type": "string", + "default": "none", + "description": "How userContext is attached per phrase: 'none' (baseline, no injection), 'expected-schema' (derive from each phrase's expected schema via manifest), 'fixed' (use --user-context-json for every phrase)." + }, + { + "name": "user-context-json", + "char": "", + "type": "string", + "default": "", + "description": "JSON object parsed as UserContext when --user-context-mode=fixed. E.g. '{\"activeApp\":\"spotify\",\"activeAppDescription\":\"Spotify music agent\"}'." + }, + { + "name": "output-suffix", + "char": "", + "type": "string", + "default": "", + "description": "When set and --out is not given, write to /translation-results-.json so baseline and context runs coexist in one workdir." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision corpus visualize", + "description": "Build an interactive HTML visualization of misroute hotspots from reclassified probe results, overlaid with a cross-schema similarity scan", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input reclassified probe-results JSON. Default: /probe-results-reclassified.json" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output HTML path. Default: /collisions-viz.html" + }, + { + "name": "top", + "char": "", + "type": "number", + "default": "60", + "description": "Sankey edge count (default 60)" + }, + { + "name": "similarity-strategy", + "char": "", + "type": "string", + "default": "", + "description": "Similarity strategy for the overlay (default balanced)" + }, + { + "name": "similarity-threshold", + "char": "", + "type": "string", + "default": "", + "description": "Similarity threshold for the overlay, decimal in [0,1] (default 0.85)" + }, + { + "name": "no-similarity", + "char": "", + "type": "boolean", + "default": "false", + "description": "Skip the similarity overlay; produce a corpus-only viz" + }, + { + "name": "translator", + "char": "", + "type": "string", + "default": "", + "description": "Translator-probe results JSON to overlay (enables the 'translator-confirmed' source filter). Default: /translation-results.json when present. Use --no-translator to skip." + }, + { + "name": "no-translator", + "char": "", + "type": "boolean", + "default": "false", + "description": "Skip the translator overlay even if translation-results.json is present" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision corpus visualize-recovery", + "description": "Build an interactive HTML visualization of recovery-rank analysis (which fix lever applies, per action and per agent)", + "group": false, + "args": [], + "flags": [ + { + "name": "in", + "char": "", + "type": "string", + "default": "", + "description": "Input reclassified probe-results JSON. Default: /probe-results-reclassified.json" + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output HTML path. Default: /recovery-viz.html" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "llmSelect threshold for the rank-2 tight/wide split (default 0.05)" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision events", + "description": "Show recent collision events captured in the current session's ring buffer", + "group": false, + "args": [], + "flags": [ + { + "name": "limit", + "char": "n", + "type": "number", + "default": "10", + "description": "Maximum number of events to show (default 10)" + }, + { + "name": "kind", + "char": "k", + "type": "string", + "default": "", + "description": "Filter by detection point (one of: static, grammarMatch, llmSelect, fuzzy)" + } + ] + }, + { + "path": "collision keywords", + "description": "Inspect/tune contextSelector keyword vectors, or backfill the committed keyword files.", + "group": true, + "args": [ + { + "name": "tokens", + "type": "string", + "optional": true, + "description": "e.g. \"list.addItems\", \"list.addItems add grocery shopping\", or omit to list all overrides." + } + ], + "flags": [] + }, + { + "path": "collision keywords backfill", + "description": "Backfill/refresh committed keyword files for agent actions. Lexical by default; --llm uses the preferred LLM distillation pass.", + "group": false, + "args": [ + { + "name": "schemas", + "type": "string", + "optional": true, + "description": "Schema names to backfill; omit to backfill every loaded schema." + } + ], + "flags": [ + { + "name": "llm", + "char": "", + "type": "boolean", + "default": "false", + "description": "Use LLM distillation (the preferred producer) instead of the deterministic lexical floor." + }, + { + "name": "force", + "char": "", + "type": "boolean", + "default": "false", + "description": "Overwrite an existing LLM-distilled file with a lexical one (a lexical backfill preserves llm files by default)." + } + ] + }, + { + "path": "collision list-strategies", + "description": "List the named strategies available for `@collision similar -s `", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "collision neighborhoods", + "description": "Build neighborhoods directly from translator misroute edges and write a persisted JSON index plus an HTML viz.", + "group": false, + "args": [], + "flags": [ + { + "name": "corpus", + "char": "", + "type": "string", + "default": "", + "description": "Translator probe results JSON (default /translation-results.json)" + }, + { + "name": "min-misroute", + "char": "", + "type": "number", + "default": "2", + "description": "Drop edges below this count (default 2)" + }, + { + "name": "include-same-schema", + "char": "", + "type": "boolean", + "default": "true", + "description": "Include same-schema misroute edges (e.g. email.send + email.reply). Default: true" + }, + { + "name": "samples-per-category", + "char": "", + "type": "number", + "default": "5", + "description": "Per-category cap on edge sample phrases (default 5)." + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output JSON path (default /neighborhoods.json)" + }, + { + "name": "out-html", + "char": "", + "type": "string", + "default": "", + "description": "Output HTML path (default /neighborhoods.html)" + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + } + ] + }, + { + "path": "collision optimize", + "description": "Optimize translator-collision neighborhoods: propose verified schema/manifest fixes, accumulate cross-run evidence about which lever and mechanism work for which collision class.", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "collision optimize browse", + "description": "Generate browse.html for one or more optimization-run-* directories. Walks the run, writes a sortable case index plus a self-contained case.html per case showing every attempt with before/after diffs.", + "group": false, + "args": [], + "flags": [ + { + "name": "run", + "char": "", + "type": "string", + "default": "", + "description": "Run timestamp suffix (the in optimization-run-/). Default: latest run under ." + }, + { + "name": "all", + "char": "", + "type": "boolean", + "default": "false", + "description": "Generate browse.html for ALL optimization-run-* directories under . Overrides --run." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory containing optimization-run-* subdirectories. Default: /collisions." + } + ] + }, + { + "path": "collision optimize distill", + "description": "Distill winning attempts in patterns.jsonl into candidate schemaGuidelines additions. Groups winners by (mechanism, guidelineHook), calls the LLM with the current schemaGuidelines as context, writes schemaGuidelines.candidates.md for operator review.", + "group": false, + "args": [], + "flags": [ + { + "name": "min-attempts", + "char": "", + "type": "number", + "default": "10", + "description": "Minimum winners in patterns.jsonl before distill runs (default 10)." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Workdir containing patterns.jsonl." + } + ] + }, + { + "path": "collision optimize explore", + "description": "Run the optimize loop on the top-N collision neighborhoods. Writes an attempts archive under /optimization-run-/.", + "group": false, + "args": [], + "flags": [ + { + "name": "corpus", + "char": "", + "type": "string", + "default": "", + "description": "Path to neighborhoods.json (default /neighborhoods.json)" + }, + { + "name": "baseline", + "char": "", + "type": "string", + "default": "", + "description": "Path to translation-results.json (default /translation-results.json)" + }, + { + "name": "top", + "char": "", + "type": "number", + "default": "5", + "description": "Top-N cases by gravity to run (default 5)" + }, + { + "name": "hypotheses-per-lever", + "char": "", + "type": "number", + "default": "3", + "description": "K hypotheses per lever per case (default 3). Reserved β€” levers pick K from this flag in a future revision." + }, + { + "name": "depth", + "char": "", + "type": "number", + "default": "2", + "description": "Recursion depth budget (default 2). When all hypotheses at depth N regress, the case loop re-prompts the LLM with the failed mechanisms and asks for a different approach." + }, + { + "name": "lever", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated lever names. Default: all registered levers." + }, + { + "name": "severity", + "char": "", + "type": "string", + "default": "blocker,leaky", + "description": "Comma-separated severity tiers to include (default blocker,leaky). Allowed: blocker, leaky, minor." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for default-named files. Default: /collisions" + }, + { + "name": "dry-run", + "char": "", + "type": "boolean", + "default": "false", + "description": "Write attempt scaffolding only β€” no LLM calls, no apply, no probe." + }, + { + "name": "concurrency", + "char": "", + "type": "number", + "default": "8", + "description": "Reserved for future per-case parallelism." + } + ] + }, + { + "path": "collision optimize list-levers", + "description": "List all registered optimization levers with their description, consumes, and probeType.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "collision optimize patterns", + "description": "Mine patterns.jsonl across all accumulated optimize runs. Emits patterns.{json,html} with three groupings (mechanism Γ— pattern, per-lever, lever-effectiveness) plus classifier agreement.", + "group": false, + "args": [], + "flags": [ + { + "name": "patterns-file", + "char": "", + "type": "string", + "default": "", + "description": "Path to patterns.jsonl. Default: /patterns.jsonl" + }, + { + "name": "min-attempts", + "char": "", + "type": "number", + "default": "5", + "description": "Cells with fewer attempts than this render as 'β€”' (default 5)." + }, + { + "name": "surface-disagreement", + "char": "", + "type": "string", + "default": "0.5", + "description": "Highlight classifier-disagreement cells above this rate (0-1, default 0.5)." + }, + { + "name": "out", + "char": "", + "type": "string", + "default": "", + "description": "Output JSON path (default /patterns.json)." + }, + { + "name": "out-html", + "char": "", + "type": "string", + "default": "", + "description": "Output HTML path (default /patterns.html)." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory containing patterns.jsonl. Default: /collisions." + } + ] + }, + { + "path": "collision optimize run", + "description": "Run the full optimize pipeline (neighborhoods β†’ explore β†’ validate β†’ patterns β†’ distill) with --from gating. Each step's predecessor must exist before it runs.", + "group": false, + "args": [], + "flags": [ + { + "name": "from", + "char": "", + "type": "string", + "default": "neighborhoods", + "description": "Resume from a step: neighborhoods | explore | validate | patterns | distill (default neighborhoods)" + }, + { + "name": "top", + "char": "", + "type": "number", + "default": "5", + "description": "Top-N cases (forwarded to explore, default 5)" + }, + { + "name": "depth", + "char": "", + "type": "number", + "default": "2", + "description": "Recursion depth (forwarded to explore, default 2)." + }, + { + "name": "lever", + "char": "", + "type": "string", + "default": "", + "description": "Lever filter (forwarded to explore)" + }, + { + "name": "severity", + "char": "", + "type": "string", + "default": "blocker,leaky", + "description": "Severity tiers (forwarded to explore, default blocker,leaky)" + }, + { + "name": "dry-run", + "char": "", + "type": "boolean", + "default": "false", + "description": "Dry-run mode (forwarded to explore)." + }, + { + "name": "skip-distill", + "char": "", + "type": "boolean", + "default": "false", + "description": "Skip the distill step regardless of attempt count." + }, + { + "name": "distill-min-attempts", + "char": "", + "type": "number", + "default": "10", + "description": "Minimum winners in patterns.jsonl before distill runs (default 10)." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory for pipeline intermediates. Default: /collisions." + } + ] + }, + { + "path": "collision optimize validate", + "description": "Stack all winners from an optimization run and re-probe the full baseline corpus. Emits optimization-impact.{json,html} with cross-neighborhood regression flags.", + "group": false, + "args": [], + "flags": [ + { + "name": "run", + "char": "", + "type": "string", + "default": "", + "description": "Run timestamp (the in optimization-run-/). Default: latest under ." + }, + { + "name": "phrases", + "char": "", + "type": "string", + "default": "", + "description": "Restrict re-probing to phrases for a single neighborhood id. Faster for targeted iteration." + }, + { + "name": "baseline", + "char": "", + "type": "string", + "default": "", + "description": "Override the baseline path recorded in optimization-run.json (useful when the original baseline moved)." + }, + { + "name": "workdir", + "char": "", + "type": "string", + "default": "", + "description": "Directory containing optimization-run-* subdirectories. Default: /collisions." + }, + { + "name": "winners", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated attemptIds to include. Stacks ONLY these winners. Mutually exclusive with --leave-one-out." + }, + { + "name": "leave-one-out", + "char": "", + "type": "string", + "default": "", + "description": "Comma-separated attemptIds to EXCLUDE. Stacks every winner except these. Useful for ablation β€” drop a suspected harmful winner and see whether the global numbers improve." + } + ] + }, + { + "path": "collision preferences", + "description": "Inspect and edit Tier-1 collision preferences (used by the preference-clarify strategy)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "collision preferences clear", + "description": "Remove every stored collision preference", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "collision preferences list", + "description": "List stored collision preferences (Tier-1)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "collision preferences remove", + "description": "Remove a stored collision preference by key (see `@collision preferences list`)", + "group": false, + "args": [ + { + "name": "key", + "type": "string", + "optional": false, + "description": "The preference key to remove." + } + ], + "flags": [] + }, + { + "path": "collision preferences set", + "description": "Set an explicit collision preference: among a candidate set, always pick the chosen option", + "group": false, + "args": [ + { + "name": "candidates", + "type": "string", + "optional": false, + "description": "Comma-separated competing options as schema.action, e.g. \"player.play,list.play\"." + }, + { + "name": "chosen", + "type": "string", + "optional": false, + "description": "The option to always pick, as schema.action. Must be one of the candidates." + } + ], + "flags": [] + }, + { + "path": "collision probe", + "description": "Probe what action(s) a hand-crafted utterance would route to via the embedding ranker (top-K with cosine deltas)", + "group": false, + "args": [ + { + "name": "phrase", + "type": "string", + "optional": false, + "description": "The utterance to probe, e.g. \"turn on wifi\"" + } + ], + "flags": [ + { + "name": "top", + "char": "n", + "type": "number", + "default": "5", + "description": "Top-K candidates to render (default 5)" + }, + { + "name": "expected", + "char": "e", + "type": "string", + "default": "", + "description": "Expected target as \"schema.actionName\" β€” flagged in the output if the top-1 candidate matches" + }, + { + "name": "delta", + "char": "", + "type": "number", + "default": "0.05", + "description": "Score delta below which the top two are flagged ambiguous (default 0.05, matches llmSelect.scoreDeltaThreshold)" + }, + { + "name": "include-inactive", + "char": "", + "type": "boolean", + "default": "false", + "description": "Include schemas that aren't currently active in this session" + } + ] + }, + { + "path": "collision similar", + "description": "Find semantically similar actions across agents (multi-vector embedding similarity, clusters by default)", + "group": false, + "args": [], + "flags": [ + { + "name": "threshold", + "char": "t", + "type": "number", + "default": "0.85", + "description": "Per-strategy score threshold (default 0.85; raw cosine scale)" + }, + { + "name": "strategy", + "char": "s", + "type": "string", + "default": "balanced", + "description": "Named scoring strategy (use `@collision similar list-strategies` to see all). Default: balanced" + }, + { + "name": "all-strategies", + "char": "", + "type": "boolean", + "default": "false", + "description": "Run every strategy and render a comparison view" + }, + { + "name": "pairs", + "char": "", + "type": "boolean", + "default": "false", + "description": "Render pairwise (legacy view) instead of clusters" + }, + { + "name": "top", + "char": "n", + "type": "number", + "default": "50", + "description": "Maximum clusters / pairs to render (default 50)" + }, + { + "name": "json", + "char": "", + "type": "string", + "default": "", + "description": "Write the structured scan result + applied-strategy data to this path as JSON" + }, + { + "name": "no-cache", + "char": "", + "type": "boolean", + "default": "false", + "description": "Skip the on-disk embedding cache (forces re-embed)" + } + ] + }, + { + "path": "config", + "description": "Configuration commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config action", + "description": "Toggle agent actions", + "group": false, + "args": [ + { + "name": "agentNames", + "type": "string", + "optional": true, + "description": "enable pattern" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "reset to default" + }, + { + "name": "off", + "char": "x", + "type": "string", + "default": "", + "description": "disable pattern" + }, + { + "name": "priority", + "char": "f", + "type": "string", + "default": "", + "description": "priority pattern" + } + ] + }, + { + "path": "config agent", + "description": "Manage agents (enable/disable, setup, refresh)", + "group": true, + "args": [ + { + "name": "agentNames", + "type": "string", + "optional": true, + "description": "enable pattern" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "reset to default" + }, + { + "name": "off", + "char": "x", + "type": "string", + "default": "", + "description": "disable pattern" + }, + { + "name": "priority", + "char": "f", + "type": "string", + "default": "", + "description": "priority pattern" + } + ] + }, + { + "path": "config agent refresh", + "description": "Re-check an agent's readiness state (or all agents)", + "group": false, + "args": [ + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "agent to refresh (omit for all enabled agents)" + } + ], + "flags": [] + }, + { + "path": "config agent setup", + "description": "Run setup for an agent that needs configuration before use", + "group": false, + "args": [ + { + "name": "agentName", + "type": "string", + "optional": true, + "description": "agent to set up (omit to list agents that need setup)" + } + ], + "flags": [] + }, + { + "path": "config cache", + "description": "Configure cache behavior", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config cache grammarSystem", + "description": "Set grammar system (completionBased or nfa)", + "group": false, + "args": [ + { + "name": "system", + "type": "string", + "optional": false, + "description": "Grammar system to use" + } + ], + "flags": [] + }, + { + "path": "config cache useDFA", + "description": "Enable or disable DFA matching within the NFA grammar system (faster; requires grammarSystem=nfa)", + "group": false, + "args": [ + { + "name": "enabled", + "type": "string", + "optional": false, + "description": "true or false" + } + ], + "flags": [] + }, + { + "path": "config collision", + "description": "Configure action collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision contextSelector", + "description": "Configure the context-weighted resolution tier (deterministic topical tiebreaker on the grammar path)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision contextSelector decay", + "description": "Get/set contextSelector decay (per-turn recency decay lambda (0 < lambda <= 1))", + "group": false, + "args": [ + { + "name": "value", + "type": "number", + "optional": true, + "description": "New value; omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config collision contextSelector detect", + "description": "Toggle context-weighted resolution (contextSelector)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision contextSelector detect off", + "description": "Turn off context-weighted resolution (contextSelector)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision contextSelector detect on", + "description": "Turn on context-weighted resolution (contextSelector)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision contextSelector margin", + "description": "Get/set contextSelector margin (clear-winner margin the winner must beat the runner-up by)", + "group": false, + "args": [ + { + "name": "value", + "type": "number", + "optional": true, + "description": "New value; omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config collision contextSelector minMass", + "description": "Get/set contextSelector minMass (evidence gate: min winner matched mass)", + "group": false, + "args": [ + { + "name": "value", + "type": "number", + "optional": true, + "description": "New value; omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config collision contextSelector minUniqueTokens", + "description": "Get/set contextSelector minUniqueTokens (evidence gate: min distinct distinguishing tokens the winner must match)", + "group": false, + "args": [ + { + "name": "value", + "type": "number", + "optional": true, + "description": "New value; omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config collision contextSelector windowTurns", + "description": "Get/set contextSelector windowTurns (ring-buffer look-back N over recent user turns)", + "group": false, + "args": [ + { + "name": "value", + "type": "number", + "optional": true, + "description": "New value; omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config collision fuzzy", + "description": "Configure fuzzy collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision fuzzy detect", + "description": "Toggle fuzzy collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision fuzzy detect off", + "description": "Turn off fuzzy collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision fuzzy detect on", + "description": "Turn on fuzzy collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision fuzzy strategy", + "description": "Set fuzzy resolution strategy (one of: first-match, score-rank, priority, user-clarify, preference-clarify)", + "group": false, + "args": [ + { + "name": "strategy", + "type": "string", + "optional": false, + "description": "strategy name" + } + ], + "flags": [] + }, + { + "path": "config collision grammarMatch", + "description": "Configure grammarMatch collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision grammarMatch detect", + "description": "Toggle grammarMatch collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision grammarMatch detect off", + "description": "Turn off grammarMatch collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision grammarMatch detect on", + "description": "Turn on grammarMatch collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision grammarMatch strategy", + "description": "Set grammarMatch resolution strategy (one of: first-match, score-rank, priority, user-clarify, preference-clarify)", + "group": false, + "args": [ + { + "name": "strategy", + "type": "string", + "optional": false, + "description": "strategy name" + } + ], + "flags": [] + }, + { + "path": "config collision llmSelect", + "description": "Configure llmSelect collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision llmSelect detect", + "description": "Toggle llmSelect collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision llmSelect detect off", + "description": "Turn off llmSelect collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision llmSelect detect on", + "description": "Turn on llmSelect collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision llmSelect strategy", + "description": "Set llmSelect resolution strategy (one of: first-match, score-rank, priority, user-clarify, preference-clarify)", + "group": false, + "args": [ + { + "name": "strategy", + "type": "string", + "optional": false, + "description": "strategy name" + } + ], + "flags": [] + }, + { + "path": "config collision preference", + "description": "Configure the preference-clarify strategy (Tier-1 preferences + Tier-2 registry)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision preference enabled", + "description": "Toggle preference-clarify resolution", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision preference enabled off", + "description": "Turn off preference-clarify resolution", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision preference enabled on", + "description": "Turn on preference-clarify resolution", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision preference registry", + "description": "Set the filesystem path to the known-ambiguous neighborhoods registry (neighborhoods.json). Empty string clears it.", + "group": false, + "args": [ + { + "name": "path", + "type": "string", + "optional": true, + "description": "Absolute path to neighborhoods.json. Use the empty string \"\" to clear." + } + ], + "flags": [] + }, + { + "path": "config collision preference registryFirst", + "description": "Toggle registry-first detection (scan all embedding candidates against the neighborhood registry, independent of the score-delta detector)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision preference registryFirst off", + "description": "Turn off registry-first detection (scan all embedding candidates against the neighborhood registry, independent of the score-delta detector)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision preference registryFirst on", + "description": "Turn on registry-first detection (scan all embedding candidates against the neighborhood registry, independent of the score-delta detector)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision preference remember", + "description": "Set how learned preferences are captured for the `preference-clarify` strategy. Empty argument shows the current value.", + "group": false, + "args": [ + { + "name": "mode", + "type": "string", + "optional": true, + "description": "One of: prompt, always, never." + } + ], + "flags": [] + }, + { + "path": "config collision preference source", + "description": "Set which ambiguity source feeds the `preference-clarify` strategy. Empty argument shows the current value.", + "group": false, + "args": [ + { + "name": "source", + "type": "string", + "optional": true, + "description": "One of: runtime, registry, both." + } + ], + "flags": [] + }, + { + "path": "config collision priority", + "description": "Set priorityOrder (comma-separated agent names) used by the `priority` resolution strategy. Empty argument shows the current value.", + "group": false, + "args": [ + { + "name": "order", + "type": "string", + "optional": true, + "description": "Comma-separated agent names, e.g. \"list,player,calendar\". Use the empty string \"\" to clear." + } + ], + "flags": [] + }, + { + "path": "config collision show", + "description": "Show the current collision detection config", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision static", + "description": "Configure static collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision static detect", + "description": "Toggle static collision detection", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision static detect off", + "description": "Turn off static collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision static detect on", + "description": "Turn on static collision detection", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision static strategy", + "description": "Set static resolution strategy (one of: warn, error)", + "group": false, + "args": [ + { + "name": "strategy", + "type": "string", + "optional": false, + "description": "strategy name" + } + ], + "flags": [] + }, + { + "path": "config collision telemetry", + "description": "Configure collision telemetry", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry debugLog", + "description": "Toggle collision telemetry debug log", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry debugLog off", + "description": "Turn off collision telemetry debug log", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry debugLog on", + "description": "Turn on collision telemetry debug log", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry emit", + "description": "Toggle collision telemetry ring buffer", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry emit off", + "description": "Turn off collision telemetry ring buffer", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry emit on", + "description": "Turn on collision telemetry ring buffer", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config collision telemetry experimentId", + "description": "Set the experimentId tag attached to every emitted collision event. Empty string clears it.", + "group": false, + "args": [ + { + "name": "id", + "type": "string", + "optional": true, + "description": "Experiment tag, e.g. \"E1.2-2026-05-12\". Empty string \"\" clears." + } + ], + "flags": [] + }, + { + "path": "config command", + "description": "Toggle agent commands", + "group": false, + "args": [ + { + "name": "agentNames", + "type": "string", + "optional": true, + "description": "enable pattern" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "reset to default" + }, + { + "name": "off", + "char": "x", + "type": "string", + "default": "", + "description": "disable pattern" + }, + { + "name": "priority", + "char": "f", + "type": "string", + "default": "", + "description": "priority pattern" + } + ] + }, + { + "path": "config dev", + "description": "Toggle development mode", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config dev off", + "description": "Turn off development mode", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config dev on", + "description": "Turn on development mode (records conversation + translation data)", + "group": false, + "args": [], + "flags": [ + { + "name": "confirm", + "char": "c", + "type": "boolean", + "default": "false", + "description": "Also confirm each translated action via the client before running it" + } + ] + }, + { + "path": "config execution", + "description": "Execution configuration", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config execution activity", + "description": "Toggle activity context", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config execution activity off", + "description": "Turn off activity context", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config execution activity on", + "description": "Turn on activity context", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config execution conversationAnswer", + "description": "How conversation questions are answered: 'lookup' (conversation-memory lookup, reasoning as fallback), 'reasoning-first' (reasoning agent primary, lookup as fallback), or 'reasoning-only' (remove the lookup action; reasoning handles conversation Q&A)", + "group": false, + "args": [ + { + "name": "strategy", + "type": "string", + "optional": true, + "description": "'lookup' (default), 'reasoning-first', or 'reasoning-only'. Omit to show the current strategy." + } + ], + "flags": [] + }, + { + "path": "config execution entityPromptShape", + "description": "Shape used when serializing Entity objects into LLM prompts", + "group": false, + "args": [ + { + "name": "shape", + "type": "string", + "optional": true, + "description": "'facets' (default, name+value array), 'flat' (collapse facets into a properties object), or 'facets-with-schema' (facets + append the Entity TS type to the reasoning system prompt). Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution planReuse", + "description": "Enable or disable workflow plan reuse for reasoning actions", + "group": false, + "args": [ + { + "name": "mode", + "type": "string", + "optional": true, + "description": "Plan reuse mode: 'enabled' to cache and reuse workflow plans, 'disabled' for standard reasoning. Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution reasoning", + "description": "Set reasoning engine", + "group": false, + "args": [ + { + "name": "engine", + "type": "string", + "optional": true, + "description": "Reasoning engine to use (claude, copilot, or none). Omit to show the current engine." + } + ], + "flags": [] + }, + { + "path": "config execution reasoningEffort", + "description": "Set the Copilot reasoning effort (low, medium, high, xhigh). Only applies to models that support it. Omit to show the current value.", + "group": false, + "args": [ + { + "name": "effort", + "type": "string", + "optional": true, + "description": "'low', 'medium', 'high', or 'xhigh'. Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution reasoningHistory", + "description": "Number of recent conversation turns included as context in the reasoning prompt", + "group": false, + "args": [ + { + "name": "turns", + "type": "number", + "optional": true, + "description": "Number of recent conversation turns to include (e.g. 4). 0 disables history. Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution reasoningModel", + "description": "Set the Copilot reasoning model (e.g. claude-opus-4.8). Omit to show the current value.", + "group": false, + "args": [ + { + "name": "model", + "type": "string", + "optional": true, + "description": "Model identifier for Copilot reasoning. Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution recordUserMessages", + "description": "Toggle record the user's own messages in the conversation transcript (chat history)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config execution recordUserMessages off", + "description": "Turn off record the user's own messages in the conversation transcript (chat history)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config execution recordUserMessages on", + "description": "Turn on record the user's own messages in the conversation transcript (chat history)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config execution scriptReuse", + "description": "Enable or disable PowerShell script reuse for reasoning actions", + "group": false, + "args": [ + { + "name": "mode", + "type": "string", + "optional": true, + "description": "Script reuse mode: 'enabled' to capture and reuse PowerShell scripts, 'disabled' for standard reasoning. Omit to show the current value." + } + ], + "flags": [] + }, + { + "path": "config execution setupOnFirstUse", + "description": "Toggle auto-run agent setup on first use (otherwise emit a hint to run @config agent setup)", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config execution setupOnFirstUse off", + "description": "Turn off auto-run agent setup on first use (otherwise emit a hint to run @config agent setup)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config execution setupOnFirstUse on", + "description": "Turn on auto-run agent setup on first use (otherwise emit a hint to run @config agent setup)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer", + "description": "Explainer configuration", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer async", + "description": "Toggle asynchronous explanation", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer async off", + "description": "Turn off asynchronous explanation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer async on", + "description": "Turn on asynchronous explanation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter", + "description": "Toggle explanation filter", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter multiple", + "description": "Toggle explanation filter multiple actions", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter multiple off", + "description": "Turn off explanation filter multiple actions", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter multiple on", + "description": "Turn on explanation filter multiple actions", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter off", + "description": "Turn off all explanation filters", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter on", + "description": "Turn on all explanation filters", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference", + "description": "Toggle reference filter", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference list", + "description": "Toggle explainer filter reference using word lists", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference list off", + "description": "Turn off explainer filter reference using word lists", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference list on", + "description": "Turn on explainer filter reference using word lists", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference off", + "description": "Turn off all explanation reference filters", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference on", + "description": "Turn on all explanation reference filters", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference translate", + "description": "Toggle explainer filter reference by translate without context", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference translate off", + "description": "Turn off explainer filter reference by translate without context", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference translate on", + "description": "Turn on explainer filter reference by translate without context", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference value", + "description": "Toggle explainer filter reference by value in the request", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference value off", + "description": "Turn off explainer filter reference by value in the request", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer filter reference value on", + "description": "Turn on explainer filter reference by value in the request", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer model", + "description": "Set model", + "group": false, + "args": [ + { + "name": "model", + "type": "string", + "optional": true, + "description": "Model name" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "Reset to default model" + } + ] + }, + { + "path": "config explainer name", + "description": "Set explainer", + "group": false, + "args": [ + { + "name": "explainerName", + "type": "string", + "optional": false, + "description": "name of the explainer" + } + ], + "flags": [] + }, + { + "path": "config explainer off", + "description": "Turn off explanation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config explainer on", + "description": "Turn on explanation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config log", + "description": "Toggle logging", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config log db", + "description": "Toggle logging", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config log db off", + "description": "Turn off logging", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config log db on", + "description": "Turn on logging", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config match", + "description": "Configure match behavior", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config match grammar", + "description": "Toggle grammar cache usage", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config match grammar off", + "description": "Turn off grammar cache usage", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config match grammar on", + "description": "Turn on grammar cache usage", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config modelProvider", + "description": "Show or set the active model provider (azure | openai | ollama | copilot)", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Provider to activate (azure | openai | ollama | copilot)" + }, + { + "name": "action", + "type": "string", + "optional": true, + "description": "Optional 'list' to list provider's models" + } + ], + "flags": [] + }, + { + "path": "config request", + "description": "Set the agent that handle natural language requests", + "group": false, + "args": [ + { + "name": "appAgentName", + "type": "string", + "optional": false, + "description": "name of the agent" + } + ], + "flags": [] + }, + { + "path": "config schema", + "description": "Toggle agent schemas", + "group": false, + "args": [ + { + "name": "agentNames", + "type": "string", + "optional": true, + "description": "enable pattern" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "reset to default" + }, + { + "name": "off", + "char": "x", + "type": "string", + "default": "", + "description": "disable pattern" + }, + { + "name": "priority", + "char": "f", + "type": "string", + "default": "", + "description": "priority pattern" + } + ] + }, + { + "path": "config scrub", + "description": "Toggle outbound secret scrubbing", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config scrub off", + "description": "Turn off outbound secret scrubbing", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config scrub on", + "description": "Turn on outbound secret scrubbing", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation", + "description": "Translation configuration", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation entity", + "description": "Entity translation configuration", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation entity clarify", + "description": "Toggle entity clarification", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation entity clarify off", + "description": "Turn off entity clarification", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation entity clarify on", + "description": "Turn on entity clarification", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation entity filter", + "description": "Toggle entity filter using LLM", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation entity filter off", + "description": "Turn off entity filter using LLM", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation entity filter on", + "description": "Turn on entity filter using LLM", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation entity resolve", + "description": "Toggle entity resolution", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation entity resolve off", + "description": "Turn off entity resolution", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation entity resolve on", + "description": "Turn on entity resolution", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation history", + "description": "Configure chat history usage in translation", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation history limit", + "description": "Set the limit of chat history usage in translation", + "group": false, + "args": [ + { + "name": "limit", + "type": "number", + "optional": false, + "description": "Number of actions" + } + ], + "flags": [] + }, + { + "path": "config translation history off", + "description": "Turn off history", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation history on", + "description": "Turn on history", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation model", + "description": "Set model", + "group": false, + "args": [ + { + "name": "model", + "type": "string", + "optional": true, + "description": "Model name" + } + ], + "flags": [ + { + "name": "reset", + "char": "r", + "type": "boolean", + "default": "false", + "description": "Reset to default model" + } + ] + }, + { + "path": "config translation multi", + "description": "multiple actions", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation multi off", + "description": "Turn off multiple action translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation multi on", + "description": "Turn on multiple action translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation multi pending", + "description": "Toggle pending request in multiple action", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation multi pending off", + "description": "Turn off pending request in multiple action", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation multi pending on", + "description": "Turn on pending request in multiple action", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation multi result", + "description": "Toggle result id in multiple action", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation multi result off", + "description": "Turn off result id in multiple action", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation multi result on", + "description": "Turn on result id in multiple action", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation off", + "description": "Turn off translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation on", + "description": "Turn on translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema", + "description": "Action schema configuration", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation", + "description": "Generated action schema", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation json", + "description": "Toggle use generate json schema if model supports it", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation json off", + "description": "Turn off use generate json schema if model supports it", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation json on", + "description": "Turn on use generate json schema if model supports it", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation jsonFunc", + "description": "Toggle use generate json schema function if model supports it", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation jsonFunc off", + "description": "Turn off use generate json schema function if model supports it", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation jsonFunc on", + "description": "Turn on use generate json schema function if model supports it", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation off", + "description": "Turn off generated action schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema generation on", + "description": "Turn on generated action schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema optimize", + "description": "Optimize schema", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation schema optimize actions", + "description": "Set number of actions to use for initial translation", + "group": false, + "args": [ + { + "name": "count", + "type": "number", + "optional": false, + "description": "Number of actions" + } + ], + "flags": [] + }, + { + "path": "config translation schema optimize off", + "description": "Turn off schema optimization", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation schema optimize on", + "description": "Turn on schema optimization", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation stream", + "description": "Toggle streaming translation", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation stream off", + "description": "Turn off streaming translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation stream on", + "description": "Turn on streaming translation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch", + "description": "auto switch schemas", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation switch embedding", + "description": "Toggle Use embedding for initial pick of schema", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation switch embedding off", + "description": "Turn off Use embedding for initial pick of schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch embedding on", + "description": "Turn on Use embedding for initial pick of schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch fix", + "description": "Set a fixed schema disable switching", + "group": false, + "args": [ + { + "name": "schemaName", + "type": "string", + "optional": false, + "description": "name of the schema" + } + ], + "flags": [] + }, + { + "path": "config translation switch inline", + "description": "Toggle inject inline switch", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation switch inline off", + "description": "Turn off inject inline switch", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch inline on", + "description": "Turn on inject inline switch", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch off", + "description": "Turn off switch schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch on", + "description": "Turn on switch schema", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch search", + "description": "Toggle search switch", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "config translation switch search off", + "description": "Turn off search switch", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "config translation switch search on", + "description": "Turn on search switch", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const", + "description": "Command to manage the construction store", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const auto", + "description": "Toggle construction auto save", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const auto off", + "description": "Turn off construction auto save", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const auto on", + "description": "Turn on construction auto save", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const builtin", + "description": "Toggle construction built-in cache", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const builtin off", + "description": "Turn off construction built-in cache", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const builtin on", + "description": "Turn on construction built-in cache", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const delete", + "description": "Delete a construction by id", + "group": false, + "args": [ + { + "name": "namespace", + "type": "string", + "optional": false, + "description": "namespace the construction in" + }, + { + "name": "id", + "type": "number", + "optional": false, + "description": "construction id to delete" + } + ], + "flags": [] + }, + { + "path": "const import", + "description": "Import constructions from test data", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": true, + "description": "Path to the construction file to import from. Load host specified test files if not specified." + } + ], + "flags": [ + { + "name": "extended", + "char": "t", + "type": "string", + "default": "false", + "description": "Load host specified extended test files if no file argument is specified" + } + ] + }, + { + "path": "const info", + "description": "Show current construction store info", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const list", + "description": "List constructions", + "group": false, + "args": [], + "flags": [ + { + "name": "verbose", + "char": "v", + "type": "string", + "default": "false", + "description": "Verbose only. Includes part index, and list all string in match set" + }, + { + "name": "all", + "char": "a", + "type": "string", + "default": "false", + "description": "List all string in match set" + }, + { + "name": "builtin", + "char": "b", + "type": "string", + "default": "false", + "description": "List the construction in the built-in cache" + }, + { + "name": "match", + "char": "m", + "type": "string", + "default": "", + "description": "Filter to constructions that has the string in the match set" + }, + { + "name": "part", + "char": "p", + "type": "string", + "default": "", + "description": "Filter to constructions that has the string match in the part name" + }, + { + "name": "id", + "char": "", + "type": "number", + "default": "", + "description": "Construction id to list" + } + ] + }, + { + "path": "const load", + "description": "Load a construction store from disk", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": true, + "description": "Construction file in the session directory or path to file" + } + ], + "flags": [] + }, + { + "path": "const merge", + "description": "Toggle construction merge", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const merge off", + "description": "Turn off construction merge", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const merge on", + "description": "Turn on construction merge", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const new", + "description": "Create a new construction store", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": true, + "description": "File name to be created in the session directory or path to the file to be created." + } + ], + "flags": [] + }, + { + "path": "const off", + "description": "Disable construction store", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const prune", + "description": "Prune out of date construction from the cache", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const save", + "description": "Save construction store to disk", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": true, + "description": "Construction file in the session directory or path to file" + } + ], + "flags": [] + }, + { + "path": "const wildcard", + "description": "wildcard matching", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const wildcard entity", + "description": "Toggle entity wildcard matching", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "const wildcard entity off", + "description": "Turn off entity wildcard matching", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const wildcard entity on", + "description": "Turn on entity wildcard matching", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const wildcard off", + "description": "Turn off wildcard matching", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "const wildcard on", + "description": "Turn on wildcard matching", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation", + "description": "Conversation management commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "conversation delete", + "description": "Delete a conversation by name", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "Name of the conversation to delete" + } + ], + "flags": [] + }, + { + "path": "conversation help", + "description": "Show conversation command help", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation info", + "description": "Show info about the current conversation", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation list", + "description": "List all conversations", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation new", + "description": "Create a new conversation, optionally with a name", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Name for the new conversation (optional)" + } + ], + "flags": [] + }, + { + "path": "conversation next", + "description": "Switch to the next conversation in the list (wraps around)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation prev", + "description": "Switch to the previous conversation in the list (wraps around)", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "conversation rename", + "description": "Rename a conversation. With one argument, renames the current conversation; with two, renames the named conversation.", + "group": false, + "args": [ + { + "name": "nameOrNewName", + "type": "string", + "optional": false, + "description": "New name (renames current) or existing name (when newName given)" + }, + { + "name": "newName", + "type": "string", + "optional": true, + "description": "New name when renaming a specific conversation" + } + ], + "flags": [] + }, + { + "path": "conversation switch", + "description": "Switch to a conversation by name (defaults to the next conversation in the list)", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": true, + "description": "Name of the conversation to switch to (omit to cycle to the next)" + } + ], + "flags": [] + }, + { + "path": "copilot", + "description": "GitHub Copilot session commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "copilot fix", + "description": "Hand the current conversation to GitHub Copilot Chat in VS Code to diagnose and fix", + "group": false, + "args": [ + { + "name": "instructions", + "type": "string", + "optional": true, + "description": "Optional extra instructions to include in the Copilot prompt" + } + ], + "flags": [ + { + "name": "mode", + "char": "", + "type": "string", + "default": "agent", + "description": "Copilot chat mode: 'agent' (can edit the workspace) or 'ask'" + }, + { + "name": "no-screenshot", + "char": "", + "type": "boolean", + "default": "false", + "description": "Do not attach a screenshot of the VS Code window" + }, + { + "name": "dev-captures", + "char": "", + "type": "string", + "default": "auto", + "description": "Include developer-mode translation captures: 'auto' (when developer mode is on), 'on', or 'off'" + }, + { + "name": "target", + "char": "", + "type": "string", + "default": "native", + "description": "Copilot target (reserved; only native GitHub Copilot is supported)" + }, + { + "name": "no-send", + "char": "", + "type": "boolean", + "default": "false", + "description": "Pre-fill the Copilot prompt but do not auto-submit it (review before sending)" + }, + { + "name": "reuse-session", + "char": "", + "type": "boolean", + "default": "false", + "description": "Send into the current Copilot Chat session instead of starting a new one" + }, + { + "name": "location", + "char": "", + "type": "string", + "default": "editor", + "description": "Where to open the new session: 'editor' (new chat editor), 'view' (chat panel), or 'window' (separate chat window)" + } + ] + }, + { + "path": "copilot import", + "description": "Import GitHub Copilot Chat sessions as conversation mirrors", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "debug", + "description": "Start node inspector", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "describe", + "description": "Describe what an agent or action can do (installed-but-disabled agents included)", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "Agent name, or (if no action given) action name" + }, + { + "name": "actionName", + "type": "string", + "optional": true, + "description": "Action name, when `name` is an agent" + } + ], + "flags": [ + { + "name": "all", + "char": "a", + "type": "boolean", + "default": "false", + "description": "Show the full action table instead of the top 10" + } + ] + }, + { + "path": "display", + "description": "Send text to display", + "group": false, + "args": [ + { + "name": "text", + "type": "string", + "optional": false, + "description": "text to display" + } + ], + "flags": [ + { + "name": "speak", + "char": "", + "type": "string", + "default": "false", + "description": "Speak the display for the host that supports TTS" + }, + { + "name": "type", + "char": "", + "type": "string", + "default": "text", + "description": "Display type" + }, + { + "name": "inline", + "char": "", + "type": "string", + "default": "false", + "description": "Display inline" + } + ] + }, + { + "path": "env", + "description": "Environment variable commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "env all", + "description": "Echos environment variables to the user interface.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "env get", + "description": "Echos the value of a named environment variable to the user interface", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "The name of the environment variable." + } + ], + "flags": [] + }, + { + "path": "exit", + "description": "Exit the program", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "feedback", + "description": "Inspect and export user-feedback entries", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "feedback count", + "description": "Show the total number of feedback entries.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "feedback export", + "description": "Export user-feedback entries to a local file (JSON or JSONL).", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": false, + "description": "Destination path (extension picks the format if --format is omitted: .jsonl β†’ JSONL, anything else β†’ JSON)" + } + ], + "flags": [ + { + "name": "format", + "char": "", + "type": "string", + "default": "", + "description": "json | jsonl (overrides the path extension)" + }, + { + "name": "all", + "char": "", + "type": "boolean", + "default": "false", + "description": "Include every entry; otherwise only the latest rating per request" + } + ] + }, + { + "path": "feedback filter", + "description": "Filter feedback by rating, category, and/or date range.", + "group": false, + "args": [], + "flags": [ + { + "name": "rating", + "char": "", + "type": "string", + "default": "", + "description": "up | down | cleared" + }, + { + "name": "category", + "char": "", + "type": "string", + "default": "", + "description": "wrong-agent | didnt-understand | bad-response | other" + }, + { + "name": "since", + "char": "", + "type": "string", + "default": "", + "description": "ISO date (YYYY-MM-DD) β€” entries on/after this date" + }, + { + "name": "until", + "char": "", + "type": "string", + "default": "", + "description": "ISO date (YYYY-MM-DD) β€” entries on/before this date" + }, + { + "name": "limit", + "char": "", + "type": "number", + "default": "50", + "description": "Maximum number of entries to show" + }, + { + "name": "all", + "char": "", + "type": "boolean", + "default": "false", + "description": "Include every entry; otherwise only the latest rating per request" + } + ] + }, + { + "path": "feedback list", + "description": "List recent user-feedback entries (most recent first).", + "group": false, + "args": [], + "flags": [ + { + "name": "limit", + "char": "", + "type": "number", + "default": "20", + "description": "Maximum number of entries to show" + }, + { + "name": "all", + "char": "", + "type": "boolean", + "default": "false", + "description": "Include every entry; otherwise only the latest rating per request is shown" + } + ] + }, + { + "path": "feedback top", + "description": "Aggregate user feedback β€” counts by rating and category.", + "group": false, + "args": [], + "flags": [ + { + "name": "limit", + "char": "", + "type": "number", + "default": "10", + "description": "Top-N depth for the per-category breakdown" + } + ] + }, + { + "path": "grammar", + "description": "Grammar rule and collision commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "grammar clear", + "description": "Clear stored grammar rules (optionally for a specific agent)", + "group": false, + "args": [ + { + "name": "agent", + "type": "string", + "optional": true, + "description": "Agent name to clear rules for. Omit to clear all stored rules." + } + ], + "flags": [] + }, + { + "path": "grammar collisions", + "description": "Scan all loaded agent grammars for cross-agent collisions, with concrete witness inputs", + "group": false, + "args": [], + "flags": [ + { + "name": "json", + "char": "", + "type": "string", + "default": "", + "description": "Write the structured scan result to this path as JSON (in addition to rendering the report)" + } + ] + }, + { + "path": "grammar delete", + "description": "Delete a stored grammar rule by ID", + "group": false, + "args": [ + { + "name": "id", + "type": "number", + "optional": false, + "description": "Numeric ID of the rule to delete" + } + ], + "flags": [] + }, + { + "path": "grammar list", + "description": "List grammar rules learned at runtime (optionally filtered by agent)", + "group": false, + "args": [ + { + "name": "agent", + "type": "string", + "optional": true, + "description": "Agent name to filter by (e.g. 'list', 'player')" + } + ], + "flags": [] + }, + { + "path": "grammar show", + "description": "Show a stored grammar rule by ID", + "group": false, + "args": [ + { + "name": "id", + "type": "number", + "optional": false, + "description": "Numeric ID of the rule to inspect" + } + ], + "flags": [] + }, + { + "path": "help", + "description": "Show help", + "group": false, + "args": [ + { + "name": "command", + "type": "string", + "optional": true, + "description": "command to get help for" + } + ], + "flags": [ + { + "name": "all", + "char": "a", + "type": "boolean", + "default": "false", + "description": "shows all commands" + } + ] + }, + { + "path": "history", + "description": "History commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "history clear", + "description": "Clear the history", + "group": false, + "args": [], + "flags": [ + { + "name": "activity", + "char": "", + "type": "boolean", + "default": "true", + "description": "Clear the current activity context" + } + ] + }, + { + "path": "history delete", + "description": "Delete a specific message from the chat history", + "group": false, + "args": [ + { + "name": "index", + "type": "number", + "optional": false, + "description": "Chat history index to delete." + } + ], + "flags": [] + }, + { + "path": "history entities", + "description": "History entity commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "history entities delete", + "description": "Delete entities from the chat history (working memory).", + "group": false, + "args": [ + { + "name": "entityId", + "type": "string", + "optional": false, + "description": "The UniqueId of the entity" + } + ], + "flags": [] + }, + { + "path": "history entities list", + "description": "Shows all of the entities currently in 'working memory.'", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "history insert", + "description": "Insert messages to chat history", + "group": false, + "args": [ + { + "name": "messages", + "type": "json", + "optional": false, + "description": "Chat history messages to insert" + } + ], + "flags": [] + }, + { + "path": "history list", + "description": "List history", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "history save", + "description": "Save the chat history to a file", + "group": false, + "args": [ + { + "name": "file", + "type": "string", + "optional": false, + "description": "File to save the chat history to" + } + ], + "flags": [] + }, + { + "path": "index", + "description": "Indexing commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "index create", + "description": "Create a new index", + "group": false, + "args": [ + { + "name": "type", + "type": "string", + "optional": false, + "description": "The type of index to create [image, email, website]" + }, + { + "name": "name", + "type": "string", + "optional": false, + "description": "Name of the index" + }, + { + "name": "location", + "type": "string", + "optional": false, + "description": "Location of the index" + } + ], + "flags": [] + }, + { + "path": "index delete", + "description": "Delete an index", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "Name of the index to delete" + } + ], + "flags": [] + }, + { + "path": "index info", + "description": "Show index details", + "group": false, + "args": [ + { + "name": "name", + "type": "string", + "optional": false, + "description": "Name of the index" + } + ], + "flags": [] + }, + { + "path": "index list", + "description": "List indexes", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "memory", + "description": "Memory commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "memory answer", + "description": "Answer a question using conversation memory", + "group": false, + "args": [ + { + "name": "question", + "type": "string", + "optional": false, + "description": "Question to ask the conversation memory" + } + ], + "flags": [ + { + "name": "asc", + "char": "", + "type": "string", + "default": "true", + "description": "Sort results in ascending order" + }, + { + "name": "message", + "char": "", + "type": "string", + "default": "false", + "description": "Display message" + }, + { + "name": "knowledge", + "char": "", + "type": "string", + "default": "false", + "description": "Display knowledge" + }, + { + "name": "count", + "char": "", + "type": "string", + "default": "25", + "description": "Display count of results" + }, + { + "name": "distinct", + "char": "", + "type": "string", + "default": "false", + "description": "Display distinct results" + } + ] + }, + { + "path": "memory legacy", + "description": "Toggle legacy", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "memory legacy off", + "description": "Turn off legacy", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "memory legacy on", + "description": "Turn on legacy", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "memory query", + "description": "Search conversation memory", + "group": false, + "args": [ + { + "name": "terms", + "type": "string", + "optional": false, + "description": "Terms to search in conversation memory" + } + ], + "flags": [ + { + "name": "asc", + "char": "", + "type": "string", + "default": "true", + "description": "Sort results in ascending order" + }, + { + "name": "message", + "char": "", + "type": "string", + "default": "true", + "description": "Display message" + }, + { + "name": "knowledge", + "char": "", + "type": "string", + "default": "true", + "description": "Display knowledge" + }, + { + "name": "count", + "char": "", + "type": "string", + "default": "25", + "description": "Display count of results" + }, + { + "name": "distinct", + "char": "", + "type": "string", + "default": "false", + "description": "Display distinct results" + } + ] + }, + { + "path": "memory search", + "description": "Answer a question using conversation memory", + "group": false, + "args": [ + { + "name": "question", + "type": "string", + "optional": false, + "description": "Question to ask the conversation memory" + } + ], + "flags": [ + { + "name": "asc", + "char": "", + "type": "string", + "default": "true", + "description": "Sort results in ascending order" + }, + { + "name": "message", + "char": "", + "type": "string", + "default": "false", + "description": "Display message" + }, + { + "name": "knowledge", + "char": "", + "type": "string", + "default": "false", + "description": "Display knowledge" + }, + { + "name": "count", + "char": "", + "type": "string", + "default": "25", + "description": "Display count of results" + }, + { + "name": "distinct", + "char": "", + "type": "string", + "default": "false", + "description": "Display distinct results" + } + ] + }, + { + "path": "notify", + "description": "Notify commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "notify clear", + "description": "Clears notifications", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "notify info", + "description": "Shows the number of notifications available", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "notify show", + "description": "Show notifications", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "notify show all", + "description": "Shows all notifications", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "notify show unread", + "description": "Shows unread notifications", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "notify test", + "description": "Fire a synthetic notification through the channel β€” for verifying chat rendering without an agent", + "group": false, + "args": [ + { + "name": "message", + "type": "string", + "optional": false, + "description": "Notification body text" + } + ], + "flags": [ + { + "name": "mode", + "char": "", + "type": "string", + "default": "toast", + "description": "Render mode: toast | inline | info | warning | error" + } + ] + }, + { + "path": "open", + "description": "Shortcut for opening system related folders", + "group": false, + "args": [ + { + "name": "folder", + "type": "string", + "optional": false, + "description": "The name or path of the folder to open" + } + ], + "flags": [] + }, + { + "path": "ports", + "description": "Lists ports registered by agents and the number of clients connected to each.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "random", + "description": "Random request commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "random offline", + "description": "Issues a random request from a dataset of pre-generated requests.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "random online", + "description": "Uses the LLM to generate random requests.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "run", + "description": "Run a command script file", + "group": false, + "args": [ + { + "name": "input", + "type": "string", + "optional": false, + "description": "command script file path" + } + ], + "flags": [] + }, + { + "path": "session", + "description": "Session commands", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "session clear", + "description": "Delete all data on the current sessions, keeping current settings", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "session delete", + "description": "Delete a session. If no session is specified, delete the current session and start a new session.\n-a to delete all sessions", + "group": false, + "args": [ + { + "name": "session", + "type": "string", + "optional": true, + "description": "Session name to delete" + } + ], + "flags": [ + { + "name": "all", + "char": "a", + "type": "boolean", + "default": "", + "description": "Delete all sessions" + } + ] + }, + { + "path": "session info", + "description": "Show info about the current session", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "session list", + "description": "List all sessions. The current session is marked green.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "session new", + "description": "Create a new empty session", + "group": false, + "args": [], + "flags": [ + { + "name": "keep", + "char": "", + "type": "string", + "default": "false", + "description": "Copy the current session settings in the new session" + }, + { + "name": "persist", + "char": "", + "type": "boolean", + "default": "", + "description": "Persist the new session. Default to whether the current session is persisted." + } + ] + }, + { + "path": "session open", + "description": "Open an existing session", + "group": false, + "args": [ + { + "name": "session", + "type": "string", + "optional": false, + "description": "Name of the session to open." + } + ], + "flags": [] + }, + { + "path": "session reset", + "description": "Reset config on session and keep the data", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "settings", + "description": "Persistent user settings", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "settings conversation", + "description": "Conversation settings", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "settings conversation resume", + "description": "Set whether to resume the last conversation on startup (true/false)", + "group": false, + "args": [ + { + "name": "value", + "type": "string", + "optional": false, + "description": "true or false" + } + ], + "flags": [] + }, + { + "path": "settings reset", + "description": "Reset all settings to defaults", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "settings server", + "description": "Server startup settings", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "settings server hidden", + "description": "Set whether the AgentServer starts hidden (true/false)", + "group": false, + "args": [ + { + "name": "value", + "type": "string", + "optional": false, + "description": "true or false" + } + ], + "flags": [] + }, + { + "path": "settings server idleTimeout", + "description": "Set idle timeout in seconds (0 to disable)", + "group": false, + "args": [ + { + "name": "seconds", + "type": "number", + "optional": false, + "description": "Timeout in seconds (0 = disabled)" + } + ], + "flags": [] + }, + { + "path": "settings show", + "description": "Show all persistent user settings", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "settings ui", + "description": "UI settings", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "settings ui autoComplete", + "description": "Set whether inline autocompletion is enabled in the CLI (true/false)", + "group": false, + "args": [ + { + "name": "value", + "type": "string", + "optional": false, + "description": "true or false" + } + ], + "flags": [] + }, + { + "path": "shutdown", + "description": "Shut down the agent server and exit", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "token", + "description": "Get LLM token usage statistics for this session.", + "group": true, + "args": [], + "flags": [] + }, + { + "path": "token details", + "description": "Gets detailed LLM usage statistics.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "token summary", + "description": "Get overall LLM usage statistics.", + "group": false, + "args": [], + "flags": [] + }, + { + "path": "trace", + "description": "Enable or disable trace namespaces", + "group": false, + "args": [ + { + "name": "namespaces", + "type": "string", + "optional": true, + "description": "Namespaces to enable" + } + ], + "flags": [ + { + "name": "clear", + "char": "*", + "type": "boolean", + "default": "false", + "description": "Clear all trace namespaces" + } + ] + } + ], + "counts": { + "agents": 32, + "actions": 553, + "commands": 325 + } +} \ No newline at end of file diff --git a/ts/docs/plans/vscode-devx/copilot-parity-assessment-2026-07-16.md b/ts/docs/plans/vscode-devx/copilot-parity-assessment-2026-07-16.md new file mode 100644 index 0000000000..360693fd58 --- /dev/null +++ b/ts/docs/plans/vscode-devx/copilot-parity-assessment-2026-07-16.md @@ -0,0 +1,113 @@ +# TypeAgent VS Code Shell ⇄ GitHub Copilot β€” Parity & Gap Assessment + +> **Status:** Baseline snapshot β€” **2026-07-16**. Point-in-time inventory to track progress toward unifying the TypeAgent VS Code experience with GitHub Copilot. +> **Audience:** Planning material for closing the code-editing inner-loop gap while preserving TypeAgent's multi-agent / voice / cross-surface strengths. +> **How to use:** Each gap has a stable ID (`G-A1`, `G-B2`, …). Update the **Status** column and append to the **Progress log** as work lands. Re-run the assessment and add a new dated section rather than overwriting rows. + +--- + +## 0. Scope & framing + +"TypeAgent in VS Code" is actually **three extensions plus a reasoning backend**, assessed together because they ship one underlying experience: + +| Surface | Role | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| [`packages/vscode-shell`](../../../packages/vscode-shell/README.md) | Custom webview chat panel (sidebar + editor tabs), talks to the agent server over WebSocket. The main "shell in VS Code." Marketplace-eligible. | +| [`packages/vscode-chat`](../../../packages/vscode-chat/README.md) | Registers TypeAgent as a native Chat-view session provider (proposed `chatSessionsProvider` API; Insiders-only). | +| [`packages/coda`](../../../packages/coda/README.md) | The in-editor actuator for the `code` agent (create files, split editors, themes) β€” and it **delegates code generation to Copilot**. | +| [`reasoning/copilot.ts`](../../../packages/dispatcher/dispatcher/src/reasoning/copilot.ts) + [`packages/copilot-plugin`](../../../packages/copilot-plugin/README.md) | TypeAgent _uses_ `@github/copilot-sdk` as a reasoning engine, and can _embed into_ Copilot CLI as a plugin. | + +**Central structural finding:** TypeAgent's VS Code coding capability is an NL/voice orchestration layer **on top of** Copilot, not a replacement for it. [`packages/coda/src/helpers.ts`](../../../packages/coda/src/helpers.ts) calls `isCopilotEnabled()`, `triggerAndMaybeAcceptInlineSuggestion()` (`editor.action.inlineSuggest.trigger`), `requestCopilotFix()`, and hands off to `workbench.action.chat.open`. TypeAgent does no code completion of its own. + +Legend β€” **Gap:** Large / Medium / Small / None Β· **Status:** Open Β· In progress Β· Closed Β· Met (TypeAgent already at/above parity). + +--- + +## A. In-editor coding assistance + +| ID | Capability | GitHub Copilot | TypeAgent VS Code shell | Gap | Status | +| ---- | ---------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------- | ------------------ | ------ | +| G-A1 | Inline completions (ghost text) | Yes | Partial (triggers _Copilot's_ via coda) | Depends on Copilot | Open | +| G-A2 | Next-edit suggestions (NES) | Yes | No | Large | Open | +| G-A3 | Inline chat (Ctrl+I in editor) | Yes | No | Large | Open | +| G-A4 | Multi-file edits w/ diff β†’ review β†’ apply | Yes (Edit/Agent) | No (coda writes whole files; no diff/apply loop) | Large | Open | +| G-A5 | Autonomous agent loop (edit + tools + iterate) | Yes (Agent mode) | Partial (reasoning=copilot has `github/fs/*`, `shell`; no in-editor diff/checkpoint UX) | Medium | Open | +| G-A6 | Undo / checkpoints / edit history | Yes | No (conversation mgmt only) | Medium | Open | + +## B. Context & grounding + +| ID | Capability | GitHub Copilot | TypeAgent VS Code shell | Gap | Status | +| ---- | ----------------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------- | ------ | ------ | +| G-B1 | `#file` / `#selection` / `#editor` context | Yes | **No** β€” shell reads no editor state (no `activeTextEditor`/`selection` in extension source) | Large | Open | +| G-B2 | `@workspace` / `#codebase` semantic index | Yes | Partial (`github/search/*` via reasoning SDK; no persistent index) | Medium | Open | +| G-B3 | `#problems` / diagnostics, test-failure context | Yes | No (coda can _ask Copilot_ to fix a diagnostic) | Medium | Open | +| G-B4 | `#changes` / git/staged diff context | Yes | No | Medium | Open | +| G-B5 | Vision / image input | Yes | Partial (chat-ui has `attachments` plumbing; not editor/vision-oriented) | Medium | Open | +| G-B6 | Terminal selection / command context | Yes (`@terminal`) | No | Small | Open | + +## C. Chat UX & modes + +| ID | Capability | GitHub Copilot | TypeAgent VS Code shell | Gap | Status | +| ---- | ---------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- | ------ | ------ | +| G-C1 | Distinct Ask / Edit / Agent modes | Yes | No (one conversational mode + optional reasoning) | Medium | Open | +| G-C2 | Code slash commands (`/explain`, `/fix`, `/tests`, `/doc`) | Yes | No (has `@agent` routing + `@config`, not code slashes) | Medium | Open | +| G-C3 | In-chat model picker | Yes (many models) | Partial (config/env `reasoningModel`, not a UI picker) | Small | Open | +| G-C4 | Streaming + cancellation/queue | Yes | Yes (rich queue/cancel, cross-client) | None | Met | +| G-C5 | Voice input | Yes | Yes (Azure Speech, [`azureSpeechProvider.ts`](../../../packages/vscode-shell/src/webview/azureSpeechProvider.ts)) | None | Met | + +## D. Customization & extensibility + +| ID | Capability | GitHub Copilot | TypeAgent VS Code shell | Gap | Status | +| ---- | ------------------------------------------------------------ | ---------------- | ----------------------------------------------------------------------------- | --------------- | ------ | +| G-D1 | Custom instructions (`copilot-instructions.md`, `AGENTS.md`) | Yes | No shell equivalent | Medium | Open | +| G-D2 | Prompt files (`.prompt.md`) | Yes | No (taskflow recipes are auto-generated, not authored) | Medium | Open | +| G-D3 | Custom chat modes / agents (`.chatmode.md`, `.agent.md`) | Yes (file-based) | Partial (agents are compiled TS plugins β€” heavier) | Medium | Open | +| G-D4 | Skills (`SKILL.md`) | Yes | No | Small | Open | +| G-D5 | MCP servers (as client) | Yes | Partial (mcp agent + copilot-plugin "mcp mode"; no shell-level MCP client UI) | Medium | Open | +| G-D6 | Per-tool approval / auto-approve controls | Yes (granular) | No (`onPermissionRequest: approveAll` in reasoning) | Medium (safety) | Open | + +## E. Platform / ecosystem + +| ID | Capability | GitHub Copilot | TypeAgent VS Code shell | Gap | Status | +| ---- | ------------------------------------------ | -------------- | ------------------------------------------------------------- | ------------------------- | ------ | +| G-E1 | Cloud coding agent (issue β†’ PR) | Yes | No | Large (different product) | Open | +| G-E2 | Code review (`/review`, PR review) | Yes | No | Medium | Open | +| G-E3 | Marketplace distribution | Yes | No (vscode-shell unpublished; vscode-chat needs proposed API) | Medium | Open | +| G-E4 | Enterprise auth, content exclusion, policy | Yes | No (own keys via `config.local.yaml`) | Medium | Open | + +--- + +## F. Where TypeAgent leads (differentiators to preserve, not close) + +- **Multi-domain agents beyond code** β€” [`packages/agents`](../../../packages/agents) ships browser automation, calendar, email, music/player, desktop, discord, image, montage, weather, lists, etc. Copilot is code-scoped. +- **One conversation across surfaces** β€” the same session is joinable from CLI, Electron shell, `vscode-shell`, and the native Chat view, with real-time shared queue/cancel state. +- **Deterministic action grammar + translation cache** β€” matches NL to typed actions without an LLM call where possible (`actionGrammar` / `cache`). +- **Conversation memory as a tool** β€” `search_memory` / `remember` over knowPro RAG, exposed to the reasoning loop. +- **Taskflow recipe auto-generation** β€” successful reasoning traces are distilled into reusable, grammar-matchable flows (`saveTaskFlowRecipeToStorage`). +- **Interoperates with Copilot both directions** β€” uses the Copilot SDK as a backend _and_ plugs into Copilot CLI. + +--- + +## G. Integration / leverage opportunities (unify, don't rebuild) + +Because the relationship is symbiotic, several gaps are cheaper to close by wiring in Copilot than by reimplementing it. Ordered by ROI: + +1. **Feed editor context into the shell (highest ROI).** Closes most of Β§B (`G-B1`–`G-B4`) with an extension-host change only β€” no server changes. Pipe `activeTextEditor` / selection / visible files / diagnostics into the prompt. +2. **Surface a per-tool approval UI (`G-D6`).** `approveAll` is the biggest safety gap vs. Copilot's confirmation model β€” route `onPermissionRequest` to a webview prompt. +3. **Adopt file-based customization (`G-D1`, `G-D2`).** Honor `AGENTS.md` / `copilot-instructions.md` and support `.prompt.md`-style saved prompts, mapping them onto the existing taskflow/recipe machinery. +4. **Expose Copilot's Edit/Agent surfaces from NL/voice (`G-A3`–`G-A5`).** coda already triggers inline suggest and Copilot Chat; extend it to drive Edit/Agent mode so repo-wide changes get Copilot's diff/apply/checkpoint UX for free. +5. **MCP client at the shell level (`G-D5`)** to reach parity with Copilot's tool ecosystem. + +--- + +## Bottom line + +The **largest true gaps** are all in the _code-editing inner loop_: no editor context (`G-B1`), no inline/edit/agent-mode diff-and-apply UX (`G-A3`–`G-A5`), and no code-aware slash commands (`G-C2`). Everything else is meaningful but secondary. TypeAgent's shell is best understood not as a Copilot competitor but as a **voice-first, multi-agent, cross-surface conversational layer** that can _orchestrate_ Copilot β€” so the pragmatic unification path is to pipe editor context in and delegate deep coding work to Copilot's existing surfaces. + +--- + +## Progress log + +| Date | Change | Gaps touched | Notes | +| ---------- | ---------------------------- | ------------ | ------------------------------------------------------------ | +| 2026-07-16 | Baseline assessment captured | β€” | Initial snapshot; all rows Open except `G-C4`, `G-C5` (Met). | diff --git a/ts/packages/agentServer/server/README.AUTOGEN.md b/ts/packages/agentServer/server/README.AUTOGEN.md index 2f3c571ec9..82c7e02702 100644 --- a/ts/packages/agentServer/server/README.AUTOGEN.md +++ b/ts/packages/agentServer/server/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # agent-server β€” AI-generated documentation @@ -12,15 +12,15 @@ ## Overview -The `agent-server` package is a TypeScript library that provides a long-running WebSocket server for hosting TypeAgent dispatchers. It manages client connections, facilitates communication between clients and agents, and handles conversation management. The server is designed to support multiple clients and conversations simultaneously, with features such as conversation persistence, idle timeouts, and graceful shutdown. +The `agent-server` package is a TypeScript library that provides a long-running WebSocket server for hosting TypeAgent dispatchers. It manages client connections, facilitates communication between clients and agents, and handles conversation management. The server supports multiple clients and conversations simultaneously, with features such as conversation persistence, idle timeouts, and graceful shutdown. ## What it does -The `agent-server` package serves as the backbone for managing conversations and facilitating communication between clients and agents. It provides the following key functionalities: +The `agent-server` package is a core component of the TypeAgent ecosystem, enabling communication between clients and agents through a WebSocket server. Its primary responsibilities include: -- **Conversation Management**: The server supports actions like `joinConversation`, `leaveConversation`, `createConversation`, `listConversations`, `renameConversation`, and `deleteConversation`. These actions allow clients to manage and interact with conversations effectively. -- **Server Control**: The `shutdown` action enables a controlled shutdown of the server, ensuring that resources are released properly. -- **Idle Timeout**: The server can be configured to automatically shut down after a specified period of inactivity using the `--idle-timeout` flag. +- **Conversation Management**: The server supports actions such as `joinConversation`, `leaveConversation`, `createConversation`, `listConversations`, `renameConversation`, and `deleteConversation`. These actions allow clients to manage and interact with conversations. +- **Server Control**: The `shutdown` action provides a mechanism for controlled server termination. +- **Idle Timeout**: The server can be configured to shut down automatically after a specified period of inactivity using the `--idle-timeout` flag. - **Ephemeral Conversation Cleanup**: On startup, the server removes temporary conversations (e.g., those created by CLI processes) to free up resources. The server listens on a WebSocket endpoint and processes these actions by interacting with its core components, such as the `ConversationManager` and `SharedDispatcher`. It integrates with other TypeAgent packages, including `@typeagent/agent-server-client` and `@typeagent/agent-server-protocol`, to provide a consistent framework for agent-based communication. @@ -165,6 +165,6 @@ _7 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-server docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-server docs:verify-links` to spot-check._ diff --git a/ts/packages/agentServer/server/src/sharedDispatcher.ts b/ts/packages/agentServer/server/src/sharedDispatcher.ts index 6392a1b8a1..2d94be6c6f 100644 --- a/ts/packages/agentServer/server/src/sharedDispatcher.ts +++ b/ts/packages/agentServer/server/src/sharedDispatcher.ts @@ -277,6 +277,56 @@ export async function createSharedDispatcher( callback(requestId, (clientIO) => clientIO.closeLocalView(requestId, ...args), ), + // Editor context (the get_user_context reasoning tool). Prefer the + // client that ISSUED the request (requestId.connectionId) so a user + // with two windows on one conversation gets *their* editor, not + // whichever answers first. Then fall back to any other connected + // editor client - a headless originator (CLI, MCP command-executor) + // relies on a co-joined shell. Clients that don't host an editor omit + // getUserContext and are skipped. + getUserContext: async (requestId) => { + const tryClient = async ( + connectionId: string, + record: ClientRecord, + ) => { + const provider = record.clientIO.getUserContext; + if (provider === undefined) { + return undefined; + } + try { + return await provider.call(record.clientIO); + } catch (error) { + debugClientIOError( + `getUserContext failed for client ${connectionId}: ${error}`, + ); + return undefined; + } + }; + + const originatorId = requestId?.connectionId; + if (originatorId !== undefined) { + const originator = clients.get(originatorId); + if (originator !== undefined) { + const fromOriginator = await tryClient( + originatorId, + originator, + ); + if (fromOriginator !== undefined) { + return fromOriginator; + } + } + } + for (const [connectionId, record] of clients) { + if (connectionId === originatorId) { + continue; + } + const userContext = await tryClient(connectionId, record); + if (userContext !== undefined) { + return userContext; + } + } + return undefined; + }, requestChoice: (requestId, ...args) => callback(requestId, (clientIO) => clientIO.requestChoice(requestId, ...args), diff --git a/ts/packages/agents/code/README.AUTOGEN.md b/ts/packages/agents/code/README.AUTOGEN.md index 2650e34fbc..68c4a0ccc8 100644 --- a/ts/packages/agents/code/README.AUTOGEN.md +++ b/ts/packages/agents/code/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # code-agent β€” AI-generated documentation @@ -12,11 +12,11 @@ ## Overview -The `code-agent` package is a TypeAgent application agent designed to automate tasks within Visual Studio Code (VSCode). It serves as a dispatcher for code-related actions, enabling users to interact with VSCode through natural language commands. This agent is tightly integrated with the [coda](../../coda/README.md) VSCode extension, which must be deployed for the `code-agent` to function. The agent is not enabled by default and requires explicit configuration to activate. +The `code-agent` package is a TypeAgent application agent designed to automate tasks within Visual Studio Code (VSCode). It acts as a dispatcher for code-related actions, enabling users to interact with VSCode through natural language commands. This agent is integrated with the [coda](../../coda/README.md) VSCode extension, which must be deployed for the `code-agent` to function. The agent is not enabled by default and requires explicit configuration to activate. ## What it does -The `code-agent` currently implements the `launchVSCode` action, which allows users to launch or start VSCode in various modes: +The `code-agent` currently implements the `launchVSCode` action, which allows users to launch or start VSCode in different modes: - **last**: Opens the last session. - **folder**: Opens a specific folder (requires a `path` parameter). @@ -56,12 +56,12 @@ For more details on the setup process, refer to the hand-written README. The `code-agent` package is organized into several key files, each serving a specific purpose: -- **[codeManifest.json](./src/codeManifest.json)**: This file defines the agent's manifest, including its description, schema, and sub-agents. It is the starting point for understanding the agent's structure and its capabilities. -- **[codeActionsSchema.ts](./src/codeActionsSchema.ts)**: This file specifies the schema for all actions supported by the agent, including their names and parameters. For example, the `launchVSCode` action is defined here. -- **[codeSchema.agr](./src/codeSchema.agr)**: This file contains the natural language grammar for mapping user commands to actions. It enables the agent to interpret user input and match it to the appropriate action. -- **[codeActionHandler.ts](./src/codeActionHandler.ts)**: This file implements the logic for handling actions. For instance, the `launchVSCode` action is processed here. -- **[codeAgentWebSocketServer.ts](./src/codeAgentWebSocketServer.ts)**: This file implements the WebSocket server that facilitates communication between the `code-agent` and the [coda](../../coda/README.md) VSCode extension. -- **[originAllowlist.ts](./src/originAllowlist.ts)**: This file defines the origin allowlist for the WebSocket server, ensuring secure communication by restricting connections to trusted origins. +- **[codeManifest.json](./src/codeManifest.json)**: Defines the agent's manifest, including its description, schema, and sub-agents. It is the starting point for understanding the agent's structure and capabilities. +- **[codeActionsSchema.ts](./src/codeActionsSchema.ts)**: Specifies the schema for all actions supported by the agent, including their names and parameters. For example, the `launchVSCode` action is defined here. +- **[codeSchema.agr](./src/codeSchema.agr)**: Contains the natural language grammar for mapping user commands to actions. This enables the agent to interpret user input and match it to the appropriate action. +- **[codeActionHandler.ts](./src/codeActionHandler.ts)**: Implements the logic for handling actions. For instance, the `launchVSCode` action is processed here. +- **[codeAgentWebSocketServer.ts](./src/codeAgentWebSocketServer.ts)**: Implements the WebSocket server that facilitates communication between the `code-agent` and the [coda](../../coda/README.md) VSCode extension. +- **[originAllowlist.ts](./src/originAllowlist.ts)**: Defines the origin allowlist for the WebSocket server, ensuring secure communication by restricting connections to trusted origins. ## How to extend @@ -150,7 +150,7 @@ _1 environment variable referenced from `./src/` (set in `ts/.env` or your shell ### Actions -_1 action implemented by this agent, parsed deterministically from `./src/codeActionsSchema.ts`. Sample utterances and parameter shapes are illustrative; consult the schema for the full signature. 6 additional actions are declared in the schema but not yet implemented; not shown._ +_1 action implemented by this agent, parsed deterministically from `./src/codeActionsSchema.ts`. Sample utterances and parameter shapes are illustrative; consult the schema for the full signature. 12 additional actions are declared in the schema but not yet implemented; not shown._ | User says | Action | | ------------------------ | ------------------------------------- | @@ -158,6 +158,6 @@ _1 action implemented by this agent, parsed deterministically from `./src/codeAc --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter code-agent docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter code-agent docs:verify-links` to spot-check._ diff --git a/ts/packages/agents/code/src/codeActionHandler.ts b/ts/packages/agents/code/src/codeActionHandler.ts index 0cefac12fe..038571825d 100644 --- a/ts/packages/agents/code/src/codeActionHandler.ts +++ b/ts/packages/agents/code/src/codeActionHandler.ts @@ -225,6 +225,15 @@ function attachSharedOnMessage(server: CodeAgentWebSocketServer): void { "default", sc === primary ? count : 0, ); + // Refresh the dispatcher's cached readiness as the Coda + // extension connects/disconnects. Readiness is cached when a + // schema is enabled (before the extension has connected), so it + // stays `setup-required` until something re-probes it. Without + // this, the first code action after enabling (e.g. `@copilot + // fix`) trips the setupOnFirstUse gate, which runs `setup` + // ("VS Code is already connected.") in place of the action and + // drops it. Best-effort; swallows errors internally. + void sc.notifyReadinessChanged(); } }; } diff --git a/ts/packages/agents/code/src/codeActionsSchema.ts b/ts/packages/agents/code/src/codeActionsSchema.ts index 21f3f4cade..bb49f44f97 100644 --- a/ts/packages/agents/code/src/codeActionsSchema.ts +++ b/ts/packages/agents/code/src/codeActionsSchema.ts @@ -7,7 +7,13 @@ export type CodeActions = | ChangeEditorLayoutAction | NewCodeFileAction | NewMarkdownFileAction - | NewTextFileAction; + | NewTextFileAction + | GetActiveEditorAction + | GetSelectionAction + | GetDiagnosticsAction + | ListOpenEditorsAction + | GetFileContentAction + | GetWorkspaceChangesAction; export type CodeActivity = LaunchVSCodeAction; @@ -138,3 +144,62 @@ export type NewTextFileAction = { content: string; }; }; + +// ── Read / introspection actions ──────────────────────────────────────────── +// These return the current VS Code editor state to the caller and modify +// nothing. They are meant to be invoked by the reasoning agent (via +// discover_actions/execute_action) to inspect what the user is looking at. +// Results are returned as JSON in the action's display output. + +// Get a coarse snapshot of the active editor: file path, language, dirty state, +// cursor position, selection range, visible range, workspace folders, open +// editor count, and diagnostic counts. Does NOT include file or selection text. +export type GetActiveEditorAction = { + actionName: "getActiveEditor"; + parameters: {}; +}; + +// Get the text currently selected in the active editor, along with its range. +// Returns an empty selection marker when nothing is selected. +export type GetSelectionAction = { + actionName: "getSelection"; + parameters: {}; +}; + +// Get the diagnostics (errors, warnings, info, hints) for a file. Defaults to +// the active file when fileName is omitted. +export type GetDiagnosticsAction = { + actionName: "getDiagnostics"; + parameters: { + // Workspace-relative path or file name. Omit to use the active file. + fileName?: string; + }; +}; + +// List the open editor tabs across all editor groups (path, label, active, +// dirty). Does not include file contents. +export type ListOpenEditorsAction = { + actionName: "listOpenEditors"; + parameters: {}; +}; + +// Read the contents of a workspace file. Optionally restrict to a line range +// (0-based, inclusive). The file must be inside an open workspace folder. +export type GetFileContentAction = { + actionName: "getFileContent"; + parameters: { + // Workspace-relative path or file name to read. + fileName: string; + // Optional 0-based start line (inclusive). + startLine?: number; + // Optional 0-based end line (inclusive). + endLine?: number; + }; +}; + +// Get a summary of pending source-control (git) changes in the workspace: +// per-repository branch, working-tree changes, and staged changes. +export type GetWorkspaceChangesAction = { + actionName: "getWorkspaceChanges"; + parameters: {}; +}; diff --git a/ts/packages/agents/code/test/codeUpdateContext.spec.ts b/ts/packages/agents/code/test/codeUpdateContext.spec.ts index 2f4d181f55..837488f7d3 100644 --- a/ts/packages/agents/code/test/codeUpdateContext.spec.ts +++ b/ts/packages/agents/code/test/codeUpdateContext.spec.ts @@ -24,6 +24,7 @@ import { instantiate, getSharedCodePort } from "../src/codeActionHandler.js"; import type { SessionContext } from "@typeagent/agent-sdk"; +import { WebSocket } from "ws"; // Minimal stub of SessionContext β€” only the surface the code agent uses // during updateAgentContext. registerPort records every call so we can @@ -35,11 +36,13 @@ type StubContext = { registerCalls: RegisterCall[]; releaseSpy: () => void; releaseCount: number; + readinessNotifyCount: number; }; function makeStubContext(agentContext: any): StubContext { const registerCalls: RegisterCall[] = []; let releaseCount = 0; + let readinessNotifyCount = 0; const releaseSpy = () => { releaseCount++; }; @@ -52,6 +55,11 @@ function makeStubContext(agentContext: any): StubContext { notifyClientCountChanged(_role: string, _count: number) { // no-op stub; tested elsewhere via registrar unit tests }, + async notifyReadinessChanged() { + // Records the readiness-refresh pushes the code agent fires from + // the server's onClientCountChanged fanout on connect/disconnect. + readinessNotifyCount++; + }, // The rest of SessionContext isn't touched by updateCodeContext. } as unknown as SessionContext; return { @@ -60,10 +68,30 @@ function makeStubContext(agentContext: any): StubContext { get releaseCount() { return releaseCount; }, + get readinessNotifyCount() { + return readinessNotifyCount; + }, releaseSpy, } as any; } +// Poll a predicate until it holds or the timeout elapses. Used to await the +// asynchronous onClientCountChanged fanout that fires after a ws client +// connects or closes. +async function waitFor( + predicate: () => boolean, + timeoutMs = 2000, +): Promise { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + if (predicate()) return; + await new Promise((r) => setTimeout(r, 20)); + } + if (!predicate()) { + throw new Error(`waitFor timed out after ${timeoutMs}ms`); + } +} + describe("code agent shared server + per-session registration", () => { const agent = instantiate(); // Track all sessions created during a test so afterEach can defensively @@ -219,4 +247,49 @@ describe("code agent shared server + per-session registration", () => { await agent.updateAgentContext!(false, b.sessionContext, "code"); expect(getSharedCodePort()).toBeUndefined(); }); + + test("a client connecting and disconnecting refreshes cached readiness", async () => { + // Regression: the code agent used to fan out only client-count + // updates on connect/disconnect, leaving the dispatcher's cached + // readiness stuck at the value probed on enable (usually + // `setup-required`, before the Coda extension connected). The first + // code action after enabling then tripped the setupOnFirstUse gate + // and ran `setup` in place of the action. The onClientCountChanged + // fanout must also push a readiness refresh so the cache tracks the + // live connection state. + const s = await newSession(); + await agent.updateAgentContext!(true, s.sessionContext, "code"); + try { + const port = getSharedCodePort(); + expect(port).toBeDefined(); + // No client yet, so no readiness push has fired from a count change. + expect(s.readinessNotifyCount).toBe(0); + + // A Node `ws` client sends no Origin header, which the code + // agent's allowlist permits (loopback / no-Origin baseline). + const client = new WebSocket(`ws://127.0.0.1:${port}`); + try { + await new Promise((resolve, reject) => { + client.once("open", () => resolve()); + client.once("error", reject); + }); + // Connecting flips the cached readiness toward `ready`. + await waitFor(() => s.readinessNotifyCount >= 1); + const afterConnect = s.readinessNotifyCount; + expect(afterConnect).toBeGreaterThanOrEqual(1); + + // Disconnecting flips it back toward `setup-required`. + client.close(); + await waitFor(() => s.readinessNotifyCount > afterConnect); + expect(s.readinessNotifyCount).toBeGreaterThan(afterConnect); + } finally { + // Double close is a no-op; guards against a thrown assertion + // leaking the socket. + client.close(); + } + } finally { + await agent.updateAgentContext!(false, s.sessionContext, "code"); + expect(getSharedCodePort()).toBeUndefined(); + } + }); }); diff --git a/ts/packages/agents/utility/README.AUTOGEN.md b/ts/packages/agents/utility/README.AUTOGEN.md index 81d3994733..78abe2a575 100644 --- a/ts/packages/agents/utility/README.AUTOGEN.md +++ b/ts/packages/agents/utility/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # utility-typeagent β€” AI-generated documentation @@ -12,47 +12,47 @@ ## Overview -The `utility-typeagent` package is a TypeAgent application agent designed to provide utility operations for use in compiled task flows. It enables automated workflows to perform web searches, fetch web content, handle file input/output operations, and interact with language models for text transformations. This package integrates with external libraries and services such as `puppeteer`, `html-to-text`, and `@anthropic-ai/claude-agent-sdk` to deliver its functionality. +The `utility-typeagent` package is a TypeAgent application agent that provides utility operations for compiled task flows. It enables workflows to perform web searches, fetch web content, handle file input/output operations, and interact with language models for text transformations. This package integrates with tools like `puppeteer`, `html-to-text`, and `@anthropic-ai/claude-agent-sdk` to deliver its functionality. ## What it does -The `utility-typeagent` package supports a variety of actions that can be grouped into three main categories: +The `utility-typeagent` package supports a range of actions grouped into three primary categories: ### Web Operations -- **`webSearch`**: Executes a web search based on a query string and returns the results. This action is useful for retrieving information from the internet. -- **`webFetch`**: Fetches the content of a specified URL, such as a webpage or other online resource. This action is often used to retrieve raw HTML or other data from web pages. +- **`webSearch`**: Executes a web search based on a query string and returns results. This is useful for retrieving information from the internet. +- **`webFetch`**: Fetches the content of a specified URL, such as a webpage or other online resource. This action retrieves raw HTML or other data from web pages. ### File Input/Output -- **`readFile`**: Reads the contents of a file from the filesystem. This action is useful for accessing local data or configuration files. -- **`writeFile`**: Writes specified content to a file at a given path. This action is commonly used to save data or logs to the local filesystem. +- **`readFile`**: Reads the contents of a file from the filesystem. This is useful for accessing local data or configuration files. +- **`writeFile`**: Writes specified content to a file at a given path. This is commonly used to save data or logs to the local filesystem. ### Language Model Interactions - **`llmTransform`**: Uses a language model to transform text or HTML based on a given prompt. It supports options for JSON parsing and HTML output, making it versatile for tasks like summarization, rewriting, or data extraction. - **`claudeTask`**: Executes tasks or answers questions using the Claude language model. This action is tailored for more complex or conversational AI tasks. -These actions enable the agent to interact with web content, perform file operations, and utilize advanced language model capabilities, making it a versatile tool for a wide range of utility tasks. +These actions allow the agent to interact with web content, perform file operations, and utilize advanced language model capabilities, making it a versatile tool for utility tasks. ## Setup -To use the `utility-typeagent` package, you need to configure the following: +To use the `utility-typeagent` package, ensure the following setup steps are completed: 1. **Environment Variables**: - - `CLAUDE_API_KEY`: Required to access the Claude language model. You can obtain this key from the provider's developer portal. + - `CLAUDE_API_KEY`: Required to access the Claude language model. Obtain this key from the provider's developer portal. 2. **Dependencies**: - - Ensure that all required dependencies are installed. These include: + - Install the required dependencies: - Workspace dependencies: `@typeagent/agent-sdk`, `@typeagent/action-grammar-compiler`, `@typeagent/action-schema-compiler`, and `browser-typeagent`. - External libraries: `puppeteer`, `html-to-text`, and `@anthropic-ai/claude-agent-sdk`. -For additional setup details, refer to the hand-written README. +Refer to the hand-written README for additional setup details if needed. ## Key Files -The `utility-typeagent` package is structured around several key files, each serving a specific purpose: +The `utility-typeagent` package is organized into several key files, each with a specific role: - **[utilitySchema.agr](./src/utilitySchema.agr)**: @@ -143,7 +143,7 @@ By following these steps, you can extend the `utility-typeagent` package to supp ### Entry points - `./agent/manifest` β†’ [./manifest.json](./manifest.json) -- `./agent/handlers` β†’ `./dist/actionHandler.mjs` _(not found on disk)_ +- `./agent/handlers` β†’ [./dist/actionHandler.mjs](./dist/actionHandler.mjs) ### Dependencies @@ -170,6 +170,6 @@ External: `@anthropic-ai/claude-agent-sdk`, `@types/html-to-text`, `html-to-text --- -_Auto-generated against commit `44b34a9ac8794b6f90489ff7e55fe57283c34960` on `2026-07-13T09:04:14.089Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter utility-typeagent docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter utility-typeagent docs:verify-links` to spot-check._ diff --git a/ts/packages/agents/utility/src/actionHandler.mts b/ts/packages/agents/utility/src/actionHandler.mts index 5935da6d3f..673c50b631 100644 --- a/ts/packages/agents/utility/src/actionHandler.mts +++ b/ts/packages/agents/utility/src/actionHandler.mts @@ -13,6 +13,7 @@ import { createActionResultFromError, } from "@typeagent/agent-sdk/helpers/action"; import { + mkdir, readFile as fsReadFile, writeFile as fsWriteFile, } from "node:fs/promises"; @@ -227,9 +228,12 @@ async function handleReadFile(path: string) { return createActionResultFromTextDisplay(content); } -async function handleWriteFile(path: string, content: string) { - await fsWriteFile(path, content, "utf-8"); - return createActionResultFromTextDisplay(`File written: ${path}`); +async function handleWriteFile(filePath: string, content: string) { + // fs.writeFile does not create missing parent directories, so create them + // first - otherwise writing into a brand-new folder fails with ENOENT. + await mkdir(path.dirname(filePath), { recursive: true }); + await fsWriteFile(filePath, content, "utf-8"); + return createActionResultFromTextDisplay(`File written: ${filePath}`); } async function handleLlmTransform( diff --git a/ts/packages/aiclient/README.AUTOGEN.md b/ts/packages/aiclient/README.AUTOGEN.md index 4672322dc1..dba3988040 100644 --- a/ts/packages/aiclient/README.AUTOGEN.md +++ b/ts/packages/aiclient/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # @typeagent/aiclient β€” AI-generated documentation @@ -40,6 +40,7 @@ The package includes debug logging capabilities for monitoring endpoint selectio To use the `@typeagent/aiclient` package, you need to configure the following environment variables: +- **`COPILOT_ALLOW_GET_PROVIDER_ENDPOINT`**: Controls whether the Copilot SDK is allowed to retrieve provider endpoints dynamically. - **`TYPEAGENT_COPILOT_SDK_LOG_LEVEL`**: Sets the log level for the Copilot SDK. Valid values include `none`, `error`, `warning`, `info`, `debug`, and `all`. Additionally, the package relies on a set of environment variables for endpoint pools and API keys. These are dynamically discovered based on the model and region. For example: @@ -109,6 +110,7 @@ By following these steps, you can effectively extend the functionality of the `a Workspace: +- [@typeagent/common-utils](../../packages/utils/commonUtils/README.md) - [@typeagent/config](../../packages/config/README.md) External: `@azure/identity`, `@github/copilot-sdk`, `@huggingface/transformers`, `async`, `debug`, `typechat` @@ -129,16 +131,17 @@ External: `@azure/identity`, `@github/copilot-sdk`, `@huggingface/transformers`, ### Files of interest -`./src/index.ts`, `./src/apiSettingsFromConfig.ts`, `./src/auth.ts`, …and 20 more under `./src/`. +`./src/index.ts`, `./src/apiSettingsFromConfig.ts`, `./src/apiTypes.ts`, …and 22 more under `./src/`. ### Environment variables -_1 environment variable referenced from `./src/` (set in `ts/.env` or your shell). See the `## Setup` section above for guidance on obtaining each value._ +_2 environment variables referenced from `./src/` (set in `ts/.env` or your shell). See the `## Setup` section above for guidance on obtaining each value._ +- `COPILOT_ALLOW_GET_PROVIDER_ENDPOINT` - `TYPEAGENT_COPILOT_SDK_LOG_LEVEL` --- -_Auto-generated against commit `15ef5aa0362e3296bd9d6bd2f001fab704375d27` on `2026-07-06T09:20:03.630Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/aiclient docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/aiclient docs:verify-links` to spot-check._ diff --git a/ts/packages/aiclient/package.json b/ts/packages/aiclient/package.json index d393eb3526..9f3b7fb47f 100644 --- a/ts/packages/aiclient/package.json +++ b/ts/packages/aiclient/package.json @@ -36,6 +36,7 @@ "@azure/identity": "^4.10.0", "@github/copilot-sdk": "1.0.5", "@huggingface/transformers": "^3.8.1", + "@typeagent/common-utils": "workspace:*", "@typeagent/config": "workspace:*", "async": "^3.2.5", "debug": "^4.4.0", diff --git a/ts/packages/aiclient/src/restClient.ts b/ts/packages/aiclient/src/restClient.ts index fef8eeda9e..21abf49864 100644 --- a/ts/packages/aiclient/src/restClient.ts +++ b/ts/packages/aiclient/src/restClient.ts @@ -3,6 +3,7 @@ import { success, error, Result } from "typechat"; import registerDebug from "debug"; +import { filterSecretsFromJsonString } from "@typeagent/common-utils"; import type { EndpointPool, EndpointPoolMember } from "./endpointPool.js"; import { markSuccess, @@ -233,15 +234,57 @@ export async function* readResponseStream( } } +// In-memory, non-persisted switch for scrubbing secrets out of outbound request +// bodies. Defaults ON at every process start and is never read from or written +// to disk; the `@config scrub` command flips it for the current session only. +let egressSecretRedactionEnabled = true; + +/** Enable or disable outbound request-body secret scrubbing for this process. */ +export function setEgressSecretRedactionEnabled(enabled: boolean): void { + egressSecretRedactionEnabled = enabled; +} + +/** Whether outbound request-body secret scrubbing is currently enabled. */ +export function isEgressSecretRedactionEnabled(): boolean { + return egressSecretRedactionEnabled; +} + +// Base64 `data:` URLs (e.g. inline images in chat payloads) are opaque blobs: +// scanning a multi-MB base64 string is pure hot-path cost, and its random +// base64 can coincidentally match a key pattern - redacting that would corrupt +// the image. Leave them untouched. +const isBase64DataUrl = (value: string): boolean => + /^data:[^,]*;base64,/.test(value); + +// Scrub secrets out of an outbound request body before it leaves the process. +// Only string bodies are touched (the JSON payloads for chat / embeddings); the +// JSON-aware pass keeps the document valid. Non-string bodies (FormData, +// streams, undefined) and the request headers - which carry the API key - are +// left untouched. Returns the original options unless the body actually changed. +function redactRequestBody(options?: RequestInit): RequestInit | undefined { + if ( + !egressSecretRedactionEnabled || + options === undefined || + typeof options.body !== "string" + ) { + return options; + } + const redacted = filterSecretsFromJsonString(options.body, { + skipValue: isBase64DataUrl, + }); + return redacted === options.body ? options : { ...options, body: redacted }; +} + async function callFetch( url: string, options?: RequestInit, timeout?: number, throttler?: FetchThrottler, ) { + const safeOptions = redactRequestBody(options); return throttler - ? throttler(() => fetchWithTimeout(url, options, timeout)) - : fetchWithTimeout(url, options, timeout); + ? throttler(() => fetchWithTimeout(url, safeOptions, timeout)) + : fetchWithTimeout(url, safeOptions, timeout); } export type FetchThrottler = (fn: () => Promise) => Promise; diff --git a/ts/packages/chat-ui/README.AUTOGEN.md b/ts/packages/chat-ui/README.AUTOGEN.md index e9780585d7..0150c895aa 100644 --- a/ts/packages/chat-ui/README.AUTOGEN.md +++ b/ts/packages/chat-ui/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # chat-ui β€” AI-generated documentation @@ -12,16 +12,16 @@ ## Overview -The `chat-ui` package provides a shared, framework-free chat user interface for TypeAgent applications. It is designed to be used across multiple platforms, including the VS Code shell extension, the browser extension chat panel, and the Visual Studio extension webview. The package supports user and agent interactions, streaming updates, chat history management, command completions, feedback collection, and connection status indicators. Its platform-agnostic design ensures consistent functionality and appearance across different environments. +The `chat-ui` package provides a shared, framework-free chat user interface for TypeAgent applications. It is designed to work across multiple platforms, including the VS Code shell extension, the browser extension chat panel, and the Visual Studio extension webview. The package supports user and agent interactions, streaming updates, chat history management, command completions, feedback collection, and connection status indicators. Its platform-agnostic design ensures consistent functionality and appearance across different environments. ## What it does -The `chat-ui` package offers the following key features: +The `chat-ui` package offers a range of features to support interactive chat experiences: -- **ChatPanel**: The core component for rendering the chat interface. It supports: +- **ChatPanel**: The primary component for rendering the chat interface. It supports: - - Adding user and agent messages with `addAgentMessage`. - - Updating display metadata using `setDisplayInfo`. + - Adding user and agent messages using `addAgentMessage`. + - Updating display metadata with `setDisplayInfo`. - Replaying historical chat entries via `replayHistory`. - Streaming updates for dynamic content display. @@ -35,7 +35,7 @@ The `chat-ui` package offers the following key features: - **Shared Styles**: Includes a CSS file (`styles/chat.css`) to ensure a consistent appearance for the chat UI across all host applications. -The package is utilized by several TypeAgent components, such as the VS Code shell, the browser extension, and the Visual Studio extension webview. +The package is used by several TypeAgent components, such as the VS Code shell, the browser extension, and the Visual Studio extension webview. ## Setup @@ -119,6 +119,6 @@ External: `ansi_up`, `dompurify`, `markdown-it` --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter chat-ui docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter chat-ui docs:verify-links` to spot-check._ diff --git a/ts/packages/chat-ui/src/connectionStatus.ts b/ts/packages/chat-ui/src/connectionStatus.ts index b7caa81293..29b453bfcd 100644 --- a/ts/packages/chat-ui/src/connectionStatus.ts +++ b/ts/packages/chat-ui/src/connectionStatus.ts @@ -38,8 +38,18 @@ export const DEFAULT_ACTION_LABELS: Record = { start: "Start server", }; -/** Human-readable status line (without any action links). */ -export function formatConnectionStatusText(status: ConnectionStatus): string { +/** + * Human-readable status line (without any action links). + * + * Pass `omitCountdown` for the `waiting` phase to drop the volatile "in Ns" + * countdown, yielding a stable string suitable for a hover tooltip / accessible + * label that would otherwise flicker (native tooltips dismiss themselves when + * their text changes) on every one-second tick. + */ +export function formatConnectionStatusText( + status: ConnectionStatus, + options?: { omitCountdown?: boolean }, +): string { const attemptSuffix = status.attempt && status.attempt > 0 ? ` (attempt ${status.attempt})` @@ -49,6 +59,9 @@ export function formatConnectionStatusText(status: ConnectionStatus): string { case "connecting": return `Disconnected β€” connecting${attemptSuffix}…`; case "waiting": { + if (options?.omitCountdown) { + return `Disconnected β€” retrying${attemptSuffix}${errorSuffix}…`; + } const sec = status.secondsRemaining ?? 0; return `Disconnected β€” retrying in ${sec}s${attemptSuffix}${errorSuffix}`; } diff --git a/ts/packages/chat-ui/src/conversationBar.ts b/ts/packages/chat-ui/src/conversationBar.ts index 63492fef51..4e99e29053 100644 --- a/ts/packages/chat-ui/src/conversationBar.ts +++ b/ts/packages/chat-ui/src/conversationBar.ts @@ -158,6 +158,15 @@ export class ConversationBar { private isRenamingCurrent = false; private inlineRenameConversationId: string | undefined; + // Structural signature of the last-rendered status indicator. A reconnect + // countdown ticks once a second but only changes the hidden status text, so + // a matching key takes the in-place refresh path in renderStatusIndicator() + // instead of rebuilding the node - which would restart the pulse animation + // and dismiss the native tooltip the user is hovering. + private lastStatusKey: string | undefined; + // The visually-hidden status-text node, refreshed in place on those ticks. + private statusSrEl: HTMLElement | undefined; + private readonly rootEl: HTMLDivElement; private readonly nameBtn: HTMLButtonElement; private readonly renameEditorEl: HTMLDivElement; @@ -540,12 +549,19 @@ export class ConversationBar { */ private renderConnectionIndicator(connection: ConnectionStatus): void { const fullText = formatConnectionStatusText(connection); + // Stable tooltip / accessible-label text: the same status WITHOUT the + // per-second countdown, so hovering the reconnecting plug doesn't + // flicker as the "retrying in Ns" line ticks down. + const titleText = formatConnectionStatusText(connection, { + omitCountdown: true, + }); if (connection.phase === "stopped") { const actions = connection.actions ?? []; if (actions.length === 0) { this.renderStatusIndicator({ stateClass: "disconnected", fullText, + titleText, }); return; } @@ -556,6 +572,7 @@ export class ConversationBar { this.renderStatusIndicator({ stateClass: "disconnected", fullText, + titleText, primaryAction: primary, secondaryActions, }); @@ -567,6 +584,7 @@ export class ConversationBar { stateClass: "connecting", pulsing: true, fullText, + titleText, badge: attempt && attempt > 0 ? { text: `-${attempt}`, attempt: true } @@ -587,6 +605,14 @@ export class ConversationBar { private renderStatusIndicator(opts: { stateClass: string; fullText: string; + /** + * Stable tooltip / accessible-label text. Defaults to {@link fullText}. + * The reconnect countdown passes a version WITHOUT the per-second + * "retrying in Ns", so the native `title` stays put (native tooltips + * dismiss themselves when the attribute changes) while only the hidden + * live-status text is refreshed each tick. + */ + titleText?: string; pulsing?: boolean; badge?: { text: string; attempt?: boolean }; primaryAction?: ConnectionActionId; @@ -594,6 +620,32 @@ export class ConversationBar { }): void { const secondaryActions = opts.secondaryActions ?? []; const hasActions = secondaryActions.length > 0; + const titleText = opts.titleText ?? opts.fullText; + + // Everything that shapes the DOM (tint, pulse, badge, actions, tooltip) + // except the volatile full-status text. A reconnect countdown only + // changes that text, so successive ticks share this key and refresh in + // place below instead of rebuilding the node (which restarts the pulse + // and dismisses the tooltip the user is hovering). + const key = [ + opts.stateClass, + opts.pulsing ? "1" : "0", + opts.badge + ? `${opts.badge.text}/${opts.badge.attempt ? 1 : 0}` + : "-", + opts.primaryAction ?? "-", + secondaryActions.join(","), + titleText, + ].join("|"); + if (key === this.lastStatusKey) { + // Structure unchanged - refresh only the hidden live-status text. + if (this.statusSrEl) { + this.statusSrEl.textContent = opts.fullText; + } + return; + } + this.lastStatusKey = key; + this.statusSrEl = undefined; this.statusEl.className = `conversation-status-summary ${opts.stateClass} as-indicator` + @@ -607,7 +659,7 @@ export class ConversationBar { action === "start" ? "click to start the server" : "click to reconnect"; - const label = `${opts.fullText} β€” ${hint}`; + const label = `${titleText} β€” ${hint}`; const plugBtn = document.createElement("button"); plugBtn.type = "button"; plugBtn.className = "conversation-status-plug"; @@ -625,7 +677,7 @@ export class ConversationBar { }); this.statusEl.appendChild(plugBtn); } else { - this.statusEl.title = opts.fullText; + this.statusEl.title = titleText; const iconEl = document.createElement("span"); iconEl.className = "conversation-status-icon" + (opts.pulsing ? " pulsing" : ""); @@ -647,6 +699,7 @@ export class ConversationBar { srEl.className = "conversation-status-sr"; srEl.textContent = opts.fullText; this.statusEl.appendChild(srEl); + this.statusSrEl = srEl; } for (const id of secondaryActions) { diff --git a/ts/packages/chat-ui/src/providers.ts b/ts/packages/chat-ui/src/providers.ts index 7ec0cb5f3c..78f159541d 100644 --- a/ts/packages/chat-ui/src/providers.ts +++ b/ts/packages/chat-ui/src/providers.ts @@ -76,16 +76,19 @@ export interface TtsProvider { } /** - * Image input capability. ChatPanel renders an attach-file button when - * pickFile is present and a camera button when openCamera is present. - * Both return base64 data URLs added to the next message's attachments. + * Image input capability. The attach-file button always renders (falling back + * to a web-native file input); supplying `pickFile` overrides that with a + * host-native picker. The camera button renders only when `openCamera` is + * present. Both hooks are independent and optional. Both return base64 data + * URLs added to the next message's attachments. */ export interface ImageCaptureProvider { /** - * Open a native file picker and return the selected image(s) as base64 - * data URLs, or undefined if cancelled. + * Optional host-native file picker returning the selected image(s) as + * base64 data URLs, or undefined if cancelled. When omitted, ChatPanel + * uses a built-in web-native `` for the attach button. */ - pickFile(): Promise; + pickFile?(): Promise; /** * Optional in-app camera capture returning a single base64 data URL, * or undefined if cancelled. diff --git a/ts/packages/chat-ui/test/conversationBar.spec.ts b/ts/packages/chat-ui/test/conversationBar.spec.ts index 088b452485..934de2e1a7 100644 --- a/ts/packages/chat-ui/test/conversationBar.spec.ts +++ b/ts/packages/chat-ui/test/conversationBar.spec.ts @@ -260,6 +260,47 @@ describe("ConversationBar", () => { ).toBe(0); }); + it("refreshes the reconnect countdown in place without rebuilding the plug", () => { + const { root, bar } = makeBar(); + const status = root.querySelector( + ".conversation-status-summary", + )!; + + bar.setStatus({ + connected: false, + connection: { phase: "waiting", attempt: 2, secondsRemaining: 5 }, + }); + const iconBefore = status.querySelector(".conversation-status-icon"); + const titleBefore = status.getAttribute("title"); + + // Next one-second tick: same attempt, one fewer second remaining. + bar.setStatus({ + connected: false, + connection: { phase: "waiting", attempt: 2, secondsRemaining: 4 }, + }); + + // The pulsing icon node is reused (not torn down), so its animation and + // the hovered native tooltip don't restart on every countdown tick. + expect(status.querySelector(".conversation-status-icon")).toBe( + iconBefore, + ); + // The tooltip text is the stable, countdown-free variant, so it never + // carries the ticking seconds that made it flicker. + expect(titleBefore).toBe("Disconnected β€” retrying (attempt 2)…"); + expect(status.getAttribute("title")).toBe(titleBefore); + // ...while the visually-hidden live text still reflects the latest count. + expect(status.textContent).toContain("retrying in 4s"); + + // A structural change (new attempt) still rebuilds the node. + bar.setStatus({ + connected: false, + connection: { phase: "waiting", attempt: 3, secondsRemaining: 8 }, + }); + expect(status.querySelector(".conversation-status-icon")).not.toBe( + iconBefore, + ); + }); + it("makes the plug a reconnect button once auto-retry has stopped and routes clicks", () => { const { root, bar, controller } = makeBar(); const status = root.querySelector( diff --git a/ts/packages/coda/README.AUTOGEN.md b/ts/packages/coda/README.AUTOGEN.md index cabe600e2c..a6d0927f13 100644 --- a/ts/packages/coda/README.AUTOGEN.md +++ b/ts/packages/coda/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # agent-coda β€” AI-generated documentation @@ -12,19 +12,19 @@ ## Overview -The `agent-coda` package is a TypeScript library that powers the Coda Operated Assistance (CODA) system, enabling voice-controlled coding assistance within Visual Studio Code (VSCode). By integrating with the TypeAgent shell or CLI, this package allows users to perform a wide range of coding and workspace management tasks using natural language commands. It is designed to enhance productivity and accessibility for developers. +The `agent-coda` package is a TypeScript library that powers the Coda Operated Assistance (CODA) system, a voice-controlled coding assistant for Visual Studio Code (VSCode). It integrates with the TypeAgent shell or CLI to enable developers to perform coding and workspace management tasks using natural language commands. This package is designed to enhance productivity and accessibility by enabling hands-free interaction with the VSCode environment. ## What it does -The `agent-coda` package provides a set of actions that enable users to interact with VSCode through voice commands. These actions are executed via the TypeAgent shell or CLI and cover a variety of use cases: +The `agent-coda` package provides a wide range of actions that allow users to interact with VSCode through voice commands. These actions are grouped into several categories: -- **File Management**: Actions like `createFile` and `openFile` allow users to create, open, and manage files directly from voice commands. -- **Editor Layout Management**: The `changeEditorColumns` action enables users to adjust the editor layout to single, double, or triple columns. -- **Extension Management**: Actions such as `checkExtensionAvailable` allow users to search for, install, and manage VSCode extensions. -- **Debugging**: The `startDebugging` action facilitates the initiation of debugging sessions with specific configurations. -- **Workbench Operations**: Actions like managing workspace folders and opening files are handled by `handleWorkBenchActions`. +- **File and Code Management**: Actions such as `createFile`, `openFile`, and `getFileContent` allow users to create, open, and manage files. Additional actions like `getSelection` and `getDiagnostics` provide insights into the current state of the editor. +- **Editor Layout and Navigation**: The `changeEditorColumns` action enables users to adjust the editor layout (e.g., single, double, or triple columns). Other actions facilitate navigation and management of open editors and workspace folders. +- **Extension Management**: Actions like `checkExtensionAvailable` and `fetchTopExtensions` allow users to search for, install, and manage VSCode extensions. +- **Debugging**: The `startDebugging` action enables users to initiate debugging sessions with specific configurations. +- **Workbench Operations**: Actions such as `handleWorkBenchActions` allow users to manage workspace folders, open files, and perform other workbench-related tasks. -These features make it possible to perform common development tasks hands-free, improving accessibility and streamlining workflows. +These capabilities allow developers to perform common development tasks hands-free, streamlining workflows and improving accessibility. ## Setup @@ -58,6 +58,7 @@ You can set these variables in your shell environment or in a `.env` file locate You should see `aisystems.copilot-coda` in the list of installed extensions. 4. **Uninstall the Extension**: If needed, you can uninstall the extension using: + ```bash code --uninstall-extension aisystems.copilot-coda ``` @@ -70,7 +71,7 @@ The `agent-coda` extension is designed to work in conjunction with the TypeAgent The `agent-coda` package is organized into several key files, each responsible for specific functionalities: -- [codeUtils.ts](./src/codeUtils.ts): Provides utility functions for code manipulation, such as ensuring proper syntax closures and generating documentation comments. +- [codeUtils.ts](./src/codeUtils.ts): Contains utility functions for code manipulation, such as ensuring proper syntax closures and generating documentation comments. - [commandAliasMgr.ts](./src/commandAliasMgr.ts): Manages command aliases to simplify the recognition and execution of voice commands. - [extension.ts](./src/extension.ts): The main entry point for the VSCode extension, responsible for activation, command registration, and initializing WebSocket connections. - [handleDebugActions.ts](./src/handleDebugActions.ts): Implements debugging-related actions, such as starting debugging sessions with specific configurations. @@ -125,7 +126,7 @@ External: `body-parser`, `chalk`, `cors`, `debug`, `dotenv`, `ws` ### Files of interest -`./src/codeUtils.ts`, `./src/commandAliasMgr.ts`, `./src/extension.ts`, …and 10 more under `./src/`. +`./src/codeUtils.ts`, `./src/commandAliasMgr.ts`, `./src/extension.ts`, …and 11 more under `./src/`. ### Environment variables @@ -136,6 +137,6 @@ _2 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-coda docs:verify-links` to spot-check._ +_Auto-generated against commit `c9ef0d288f10874ca031db370793375b7b88a8bc` on `2026-07-17T23:22:28.266Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-coda docs:verify-links` to spot-check._ diff --git a/ts/packages/coda/src/handleReadActions.ts b/ts/packages/coda/src/handleReadActions.ts new file mode 100644 index 0000000000..c81d44a4a2 --- /dev/null +++ b/ts/packages/coda/src/handleReadActions.ts @@ -0,0 +1,349 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as vscode from "vscode"; +import * as path from "path"; +import { ActionResult } from "./helpers"; + +// Read/introspection action names served here. Kept in sync with the read +// actions in packages/agents/code/src/codeActionsSchema.ts. +const READ_ACTIONS = new Set([ + "getActiveEditor", + "getSelection", + "getDiagnostics", + "listOpenEditors", + "getFileContent", + "getWorkspaceChanges", +]); + +type ReadActionParameters = { + fileName?: string; + startLine?: number; + endLine?: number; +}; + +type ReadAction = { + actionName?: string; + fullActionName?: string; + parameters?: ReadActionParameters; +}; + +type GitChange = { + uri: vscode.Uri; + status: number; +}; + +type GitRepository = { + rootUri: vscode.Uri; + state: { + HEAD?: { + name?: string; + ahead?: number; + behind?: number; + }; + workingTreeChanges: GitChange[]; + indexChanges: GitChange[]; + }; +}; + +type GitApi = { + repositories: GitRepository[]; +}; + +type GitExtensionExports = { + getAPI(version: number): GitApi; +}; + +/** + * Handle the code agent's read/introspection actions. Each returns the current + * VS Code editor state as JSON in the ActionResult message (which the code + * agent relays back and the reasoning agent captures). Returns handled:false + * for any other action so the parallel-dispatch in handleVSCodeActions moves on. + */ +export async function handleReadActions( + action: ReadAction, +): Promise { + const actionName: string | undefined = + action.actionName ?? action.fullActionName?.split(".").at(-1); + if (!actionName || !READ_ACTIONS.has(actionName)) { + return { handled: false, message: "" }; + } + const params = action.parameters ?? {}; + try { + switch (actionName) { + case "getActiveEditor": + return ok(getActiveEditor()); + case "getSelection": + return ok(getSelection()); + case "getDiagnostics": + return ok(getDiagnostics(params.fileName)); + case "listOpenEditors": + return ok(listOpenEditors()); + case "getFileContent": + return ok(await getFileContent(params)); + case "getWorkspaceChanges": + return ok(await getWorkspaceChanges()); + default: + return { handled: false, message: "" }; + } + } catch (err) { + return ok({ + error: err instanceof Error ? err.message : String(err), + }); + } +} + +function ok(data: unknown): ActionResult { + return { handled: true, message: JSON.stringify(data, null, 2) }; +} + +function positionOf(p: vscode.Position) { + return { line: p.line, character: p.character }; +} + +function severityName(s: vscode.DiagnosticSeverity): string { + switch (s) { + case vscode.DiagnosticSeverity.Error: + return "error"; + case vscode.DiagnosticSeverity.Warning: + return "warning"; + case vscode.DiagnosticSeverity.Information: + return "info"; + case vscode.DiagnosticSeverity.Hint: + return "hint"; + default: + return "unknown"; + } +} + +function countDiagnostics(uri: vscode.Uri) { + const counts = { errors: 0, warnings: 0, infos: 0, hints: 0 }; + for (const d of vscode.languages.getDiagnostics(uri)) { + switch (d.severity) { + case vscode.DiagnosticSeverity.Error: + counts.errors++; + break; + case vscode.DiagnosticSeverity.Warning: + counts.warnings++; + break; + case vscode.DiagnosticSeverity.Information: + counts.infos++; + break; + case vscode.DiagnosticSeverity.Hint: + counts.hints++; + break; + } + } + return counts; +} + +function getActiveEditor() { + const editor = vscode.window.activeTextEditor; + const workspaceFolders = vscode.workspace.workspaceFolders?.map( + (f) => f.name, + ); + const openEditorCount = vscode.window.tabGroups.all.reduce( + (sum, g) => sum + g.tabs.length, + 0, + ); + if (!editor) { + return { activeEditor: null, workspaceFolders, openEditorCount }; + } + const doc = editor.document; + const visible = editor.visibleRanges[0]; + return { + activeFilePath: vscode.workspace.asRelativePath(doc.uri, false), + languageId: doc.languageId, + isUntitled: doc.isUntitled, + isDirty: doc.isDirty, + lineCount: doc.lineCount, + cursor: positionOf(editor.selection.active), + selection: { + isEmpty: editor.selection.isEmpty, + start: positionOf(editor.selection.start), + end: positionOf(editor.selection.end), + }, + visibleRange: visible + ? { startLine: visible.start.line, endLine: visible.end.line } + : undefined, + workspaceFolders, + diagnostics: countDiagnostics(doc.uri), + openEditorCount, + }; +} + +function getSelection() { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return { selection: null, reason: "No active editor" }; + } + const sel = editor.selection; + return { + filePath: vscode.workspace.asRelativePath(editor.document.uri, false), + isEmpty: sel.isEmpty, + start: positionOf(sel.start), + end: positionOf(sel.end), + text: editor.document.getText(sel), + }; +} + +function getDiagnostics(fileName?: string) { + let uri: vscode.Uri | undefined; + if (typeof fileName === "string" && fileName.trim().length > 0) { + uri = resolveWorkspaceFile(fileName); + if (!uri) { + return { + error: `File path is not inside an open workspace folder: ${fileName}`, + }; + } + } else { + uri = vscode.window.activeTextEditor?.document.uri; + } + if (!uri) { + return { diagnostics: [], reason: "No active editor" }; + } + const diagnostics = vscode.languages.getDiagnostics(uri).map((d) => ({ + severity: severityName(d.severity), + message: d.message, + source: d.source, + code: + d.code && typeof d.code === "object" + ? String((d.code as { value: string | number }).value) + : d.code, + start: positionOf(d.range.start), + end: positionOf(d.range.end), + })); + return { + filePath: vscode.workspace.asRelativePath(uri, false), + diagnostics, + }; +} + +function listOpenEditors() { + const editors = vscode.window.tabGroups.all.flatMap((group, groupIndex) => + group.tabs.map((tab) => { + const input = tab.input; + const uri = + input instanceof vscode.TabInputText + ? input.uri + : input instanceof vscode.TabInputTextDiff + ? input.modified + : undefined; + return { + group: groupIndex, + label: tab.label, + active: tab.isActive, + dirty: tab.isDirty, + path: uri + ? vscode.workspace.asRelativePath(uri, false) + : undefined, + }; + }), + ); + return { openEditors: editors }; +} + +async function getFileContent(params: { + fileName?: string; + startLine?: number; + endLine?: number; +}) { + const fileName = params.fileName; + if (typeof fileName !== "string" || fileName.trim().length === 0) { + return { error: "getFileContent requires a fileName." }; + } + const uri = resolveWorkspaceFile(fileName); + if (!uri) { + return { + error: `File path is not inside an open workspace folder: ${fileName}`, + }; + } + const doc = await vscode.workspace.openTextDocument(uri); + let content: string; + if (typeof params.startLine === "number") { + const startLine = Math.max(0, params.startLine); + const endLine = Math.min( + doc.lineCount - 1, + typeof params.endLine === "number" ? params.endLine : startLine, + ); + content = doc.getText( + new vscode.Range( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, Number.MAX_SAFE_INTEGER), + ), + ); + } else { + content = doc.getText(); + } + return { + filePath: vscode.workspace.asRelativePath(uri, false), + languageId: doc.languageId, + lineCount: doc.lineCount, + content, + }; +} + +async function getWorkspaceChanges() { + const gitExtension = + vscode.extensions.getExtension("vscode.git"); + if (!gitExtension) { + return { error: "The built-in git extension is not available." }; + } + const exports = gitExtension.isActive + ? gitExtension.exports + : await gitExtension.activate(); + const api = exports.getAPI(1); + const repositories = api.repositories.map((repo) => ({ + root: vscode.workspace.asRelativePath(repo.rootUri, false), + branch: repo.state.HEAD?.name, + ahead: repo.state.HEAD?.ahead, + behind: repo.state.HEAD?.behind, + workingTreeChanges: repo.state.workingTreeChanges.map((c) => ({ + path: vscode.workspace.asRelativePath(c.uri, false), + status: gitStatusName(c.status), + })), + indexChanges: repo.state.indexChanges.map((c) => ({ + path: vscode.workspace.asRelativePath(c.uri, false), + status: gitStatusName(c.status), + })), + })); + return { repositories }; +} + +// Resolve a workspace-relative path or bare file name to a Uri inside an open +// workspace folder. Rejects paths that escape the workspace root via `..` or +// absolute components (matching the containment check used when creating files). +function resolveWorkspaceFile(fileName: string): vscode.Uri | undefined { + const folders = vscode.workspace.workspaceFolders; + if (!folders || folders.length === 0) { + return undefined; + } + const trimmed = fileName.trim(); + for (const folder of folders) { + const candidate = vscode.Uri.joinPath(folder.uri, trimmed); + const root = folder.uri.fsPath; + const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep; + const target = candidate.fsPath; + if (target === root || target.startsWith(rootWithSep)) { + return candidate; + } + } + return undefined; +} + +// Map the VS Code git API Status enum (numeric) to a readable name. +function gitStatusName(status: number): string { + const names: Record = { + 0: "index_modified", + 1: "index_added", + 2: "index_deleted", + 3: "index_renamed", + 4: "index_copied", + 5: "modified", + 6: "deleted", + 7: "untracked", + 8: "ignored", + 9: "intent_to_add", + }; + return names[status] ?? `status_${status}`; +} diff --git a/ts/packages/coda/src/handleVSCodeActions.ts b/ts/packages/coda/src/handleVSCodeActions.ts index 7e75e213b4..e53c70ab5d 100644 --- a/ts/packages/coda/src/handleVSCodeActions.ts +++ b/ts/packages/coda/src/handleVSCodeActions.ts @@ -8,6 +8,7 @@ import { handleWorkbenchActions } from "./handleWorkBenchActions"; import { handleDebugActions } from "./handleDebugActions"; import { handleExtensionActions } from "./handleExtensionActions"; import { handleEditorCodeActions } from "./handleEditorCodeActions"; +import { handleReadActions } from "./handleReadActions"; async function execChangeEditorColumns(actionData: any): Promise { const actionResult: ActionResult = { @@ -576,6 +577,7 @@ export async function handleVSCodeActions(action: any) { handleWorkbenchActions, handleExtensionActions, handleEditorCodeActions, + handleReadActions, ]; const results = await Promise.all( diff --git a/ts/packages/commandExecutor/README.AUTOGEN.md b/ts/packages/commandExecutor/README.AUTOGEN.md index 54fb38033b..4a7c2981da 100644 --- a/ts/packages/commandExecutor/README.AUTOGEN.md +++ b/ts/packages/commandExecutor/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # command-executor-mcp β€” AI-generated documentation @@ -12,19 +12,19 @@ ## Overview -The `command-executor-mcp` package is an MCP (Model Context Protocol) server that facilitates the execution of user commands such as playing music, managing lists, and working with calendars. It acts as a bridge between MCP clients (e.g., Claude Code) and the TypeAgent system, translating natural language commands into structured actions that the TypeAgent dispatcher can process. +The `command-executor-mcp` package is an MCP (Model Context Protocol) server designed to execute user commands such as playing music, managing lists, and working with calendars. It acts as an intermediary between MCP clients (e.g., Claude Code) and the TypeAgent system, translating natural language commands into structured actions that the TypeAgent dispatcher can process. ## What it does -This package provides the following core functionalities: +The `command-executor-mcp` package provides the following key functionalities: -1. **Natural Language Command Execution**: Using the `execute_command` action, the server processes user commands like "play Bohemian Rhapsody by Queen" or "add milk to my shopping list" and forwards them to the TypeAgent dispatcher for execution. +1. **Natural Language Command Execution**: The `execute_command` action processes user commands like "play Bohemian Rhapsody by Queen" or "add milk to my shopping list" and forwards them to the TypeAgent dispatcher for execution. 2. **Schema Discovery**: The `discover_schemas` action allows clients to query the available capabilities of the TypeAgent system, such as weather information, email handling, or calendar management. -3. **Dynamic Schema Loading**: With the `load_schema` action, new schemas can be dynamically loaded at runtime, enabling the server to register and expose new agent actions without restarting. +3. **Dynamic Schema Loading**: Using the `load_schema` action, new schemas can be dynamically loaded at runtime, enabling the server to register and expose new agent actions without requiring a restart. 4. **Direct Action Invocation**: The `typeagent_action` action provides a fallback mechanism for invoking structured actions that are not exposed as individual tools. 5. **Debugging and Connectivity**: The `ping` action is available for testing server connectivity and debugging purposes. -The server connects to the TypeAgent dispatcher via WebSocket and supports automatic reconnection, ensuring reliability even if the dispatcher becomes temporarily unavailable. +The server connects to the TypeAgent dispatcher via WebSocket and includes automatic reconnection capabilities, ensuring it remains operational even if the dispatcher becomes temporarily unavailable. ## Setup @@ -142,6 +142,6 @@ _3 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `366aaf867a7e8e5d130b6c87a365516bab725269` on `2026-07-07T09:05:05.703Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter command-executor-mcp docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter command-executor-mcp docs:verify-links` to spot-check._ diff --git a/ts/packages/commandExecutor/src/commandServer.ts b/ts/packages/commandExecutor/src/commandServer.ts index dea7b72861..6600c2eb9e 100644 --- a/ts/packages/commandExecutor/src/commandServer.ts +++ b/ts/packages/commandExecutor/src/commandServer.ts @@ -522,6 +522,22 @@ export class CommandServer { async (request: ExecuteActionRequest) => this.executeAction(request), ); + + // 4. User/editor context - delegates to the code agent (VS Code CODA + // extension). This headless MCP server has no editor of its own, so + // it returns data only when a VS Code `code` agent is connected to + // the same agent server; otherwise it reports none available. + this.server.registerTool( + "get_user_context", + { + inputSchema: {}, + description: + "Get a coarse snapshot of the user's current VS Code editor context (active file, language, cursor/selection ranges, workspace, diagnostic counts). Contains NO file or selection text.\n\n" + + "Served by the TypeAgent `code` agent (VS Code CODA extension); returns data only when VS Code with the code agent is connected to this agent server, otherwise reports no editor context.\n\n" + + "For actual file/selection text, use execute_action with the code agent's read actions (getSelection, getFileContent, getDiagnostics).", + }, + async () => this.getUserContext(), + ); } private addDiagnosticTools() { @@ -809,6 +825,18 @@ export class CommandServer { ); } + private async getUserContext(): Promise { + // The command-executor is headless; the live editor state lives in the + // VS Code CODA extension, reachable through the code agent's read + // action. executeAction returns a clear error when the code agent is + // not enabled / VS Code is not connected. + return this.executeAction({ + schemaName: "code", + actionName: "getActiveEditor", + parameters: {}, + }); + } + private async executeAction( request: ExecuteActionRequest, ): Promise { diff --git a/ts/packages/dispatcher/dispatcher/README.AUTOGEN.md b/ts/packages/dispatcher/dispatcher/README.AUTOGEN.md index 94b8948771..1ab04b6fe2 100644 --- a/ts/packages/dispatcher/dispatcher/README.AUTOGEN.md +++ b/ts/packages/dispatcher/dispatcher/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # agent-dispatcher β€” AI-generated documentation @@ -12,7 +12,7 @@ ## Overview -The TypeAgent Dispatcher is a TypeScript library that serves as the central hub for processing user requests in the TypeAgent ecosystem. It translates natural language inputs into structured actions using large language models (LLMs) and coordinates interactions between various application agents. The Dispatcher is designed to be extensible, scalable, and capable of integrating with multiple front ends, such as the TypeAgent Shell and CLI. +The TypeAgent Dispatcher is a TypeScript library that acts as the central hub for processing user requests within the TypeAgent ecosystem. It translates natural language inputs into structured actions using large language models (LLMs) and coordinates interactions between various application agents. The Dispatcher is designed to integrate with multiple front ends, such as the TypeAgent Shell and CLI, and supports an extensible architecture for building personal agents with natural language interfaces. ## What it does @@ -20,7 +20,7 @@ The Dispatcher provides a framework for interpreting user inputs and orchestrati ### Natural Language Requests -The Dispatcher leverages LLMs to process natural language inputs and translate them into structured actions defined by application agent schemas. For example: +The Dispatcher uses LLMs to process natural language inputs and translate them into structured actions defined by application agent schemas. For example: ```bash [calendar]πŸ€–> can you setup a meeting between 2-3PM @@ -218,7 +218,7 @@ External: `@anthropic-ai/claude-agent-sdk`, `@azure/core-client`, `@azure/core-r - [./src/context/dispatcher/schema/clarifyActionSchema.ts](./src/context/dispatcher/schema/clarifyActionSchema.ts) - [./src/context/dispatcher/schema/dispatcherActionSchema.ts](./src/context/dispatcher/schema/dispatcherActionSchema.ts) - [./src/context/dispatcher/schema/lookupActionSchema.ts](./src/context/dispatcher/schema/lookupActionSchema.ts) -- _…and 210 more under `./src/`._ +- _…and 211 more under `./src/`._ ### Environment variables @@ -236,6 +236,6 @@ _9 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `ee4eba45bcb87911335cb938a0ced6a001aa3882` on `2026-07-17T22:05:48.260Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-dispatcher docs:verify-links` to spot-check._ +_Auto-generated against commit `c9ef0d288f10874ca031db370793375b7b88a8bc` on `2026-07-17T23:22:28.266Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-dispatcher docs:verify-links` to spot-check._ diff --git a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts index b6419a0ce2..d0e02ed118 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts @@ -21,6 +21,7 @@ import { getCopilotClient, getActiveModelProvider, setActiveModelProvider, + setEgressSecretRedactionEnabled, getRuntimeConfig, PROVIDER_MODES, type ProviderMode, @@ -3259,6 +3260,12 @@ export function getConfigCommandHandlers(): CommandHandlerTable { }, }, request: new ConfigRequestCommandHandler(), + scrub: getToggleHandlerTable( + "outbound secret scrubbing", + async (_context, enable: boolean) => { + setEgressSecretRedactionEnabled(enable); + }, + ), match: { description: "Configure match behavior", commands: { diff --git a/ts/packages/dispatcher/dispatcher/src/reasoning/claude.ts b/ts/packages/dispatcher/dispatcher/src/reasoning/claude.ts index 43407e6dee..7335194264 100644 --- a/ts/packages/dispatcher/dispatcher/src/reasoning/claude.ts +++ b/ts/packages/dispatcher/dispatcher/src/reasoning/claude.ts @@ -16,6 +16,7 @@ import { TypeAgentAction, } from "@typeagent/agent-sdk"; import { createActionResultNoDisplay } from "@typeagent/agent-sdk/helpers/action"; +import { formatUserContextForPrompt } from "./userContextPrompt.js"; import { ClientIO, IAgentMessage } from "@typeagent/dispatcher-types"; import { ConversationMessage, @@ -208,6 +209,12 @@ function buildPromptWithContext( if (chatContext) { parts.push(chatContext); } + const editorContext = formatUserContextForPrompt( + context.sessionContext.agentContext.currentOptions?.userContext, + ); + if (editorContext) { + parts.push(editorContext); + } if (fallbackContext) { const lines = ["[Fallback context β€” a prior action failed]"]; if (fallbackContext.failedSchema && fallbackContext.failedAction) { @@ -347,6 +354,11 @@ function getClaudeOptions( context: ActionContext, ): Options { const systemContext = context.sessionContext.agentContext; + // Stable clientIO reference for get_user_context (see copilot.ts). + const baseClientIO = systemContext.clientIO; + // Originating request id (carries connectionId) so the agent-server routing + // can prefer this client's editor context (see copilot.ts). + const originatorRequestId = systemContext.currentRequestId; const config = systemContext.session.getConfig(); const activeSchemas = systemContext.agents.getActiveSchemas(); const schemaDescriptions: string[] = []; @@ -668,6 +680,31 @@ function getClaudeOptions( }, }; + const userContextSchema = {}; + const getUserContextTool: SdkMcpToolDefinition = { + name: "get_user_context", + description: [ + "Get a fresh, coarse snapshot of the user's current editor context:", + "active file path, language, cursor and selection ranges, workspace", + "folders, the open-editor list, the active file's diagnostics (with", + "messages), and the active selection's text (all bounded).", + "Does NOT include full file contents. To read those, use the code", + "agent's read actions (getActiveEditor, getSelection, getFileContent,", + "getDiagnostics) via discover_actions/execute_action.", + ].join("\n"), + inputSchema: userContextSchema, + handler: async () => { + const userContext = + await baseClientIO.getUserContext?.(originatorRequestId); + const text = userContext + ? JSON.stringify(userContext, null, 2) + : "No editor context is available (the client is not an editor, or there is no active editor)."; + return { + content: [{ type: "text", text }], + }; + }, + }; + const sessionId = getSessionId(context); // Experimental override: if CLAUDE_CUSTOM_PROMPT_FILE is set, read that file @@ -722,6 +759,7 @@ function getClaudeOptions( "- `remember`: Durably save a new memory so it can be recalled later", "- `get_conversation_info`: Get transcript metadata (message count, contributing agents)", "- `read_conversation`: Page through the raw conversation transcript (offset/limit)", + "- `get_user_context`: Fresh coarse snapshot of the user's editor (active file, language, cursor/selection ranges, workspace, open editors, the active file's diagnostic messages) and the user's selected text (bounded) when present; use the code agent's read actions for full file contents", "", 'For follow-up requests that refer to earlier turns (e.g. "those", "it", "mine"), first consult the [Recent conversation context] block included with the request; call search_memory only when you need older history not shown there.', "", @@ -1113,6 +1151,7 @@ function getClaudeOptions( rememberTool, getConversationInfoTool, readConversationTool, + getUserContextTool, ], }), }, diff --git a/ts/packages/dispatcher/dispatcher/src/reasoning/copilot.ts b/ts/packages/dispatcher/dispatcher/src/reasoning/copilot.ts index fa67bf9b3a..f7fd3132e8 100644 --- a/ts/packages/dispatcher/dispatcher/src/reasoning/copilot.ts +++ b/ts/packages/dispatcher/dispatcher/src/reasoning/copilot.ts @@ -36,8 +36,10 @@ import { import { nullClientIO } from "../context/interactiveIO.js"; import { ClientIO, IAgentMessage } from "@typeagent/dispatcher-types"; import { createActionResultNoDisplay } from "@typeagent/agent-sdk/helpers/action"; +import { createLimiter } from "@typeagent/common-utils"; import { ReasoningTraceCollector } from "./tracing/traceCollector.js"; import { ReasoningRecipeGenerator } from "./recipeGenerator.js"; +import { formatUserContextForPrompt } from "./userContextPrompt.js"; const debug = registerDebug("typeagent:dispatcher:reasoning:copilot"); @@ -70,6 +72,35 @@ const FALLBACK_MODEL = "claude-opus-4.8"; // instead of narrating an intent ("Let me confirm…") and stopping. const FALLBACK_REASONING_EFFORT = "high" as const; +// Largest delay Node's setTimeout accepts without overflowing (~24.8 days). The +// Copilot SDK feeds sendAndWait's timeout straight into setTimeout, so a larger +// value (or Infinity) would wrap around to a near-zero delay and fire immediately. +const MAX_SETTIMEOUT_MS = 2_147_483_647; + +// Client-side cap on how long session.sendAndWait blocks waiting for the Copilot +// session to go idle (finish one full agentic turn). The SDK default is only 60s, +// which spuriously fails legitimate multi-tool reasoning turns that run longer: +// the wait does not abort the agent's in-flight work, so a too-short cap just +// rejects a turn that is still making progress. Genuine cancellation stays with +// context.abortSignal. Matches the Claude path's DEFAULT_REASONING_TIMEOUT_MS and +// honors the same TYPEAGENT_REASONING_TIMEOUT_MS override. +const DEFAULT_REASONING_TIMEOUT_MS = 20 * 60 * 1000; + +// Resolve the sendAndWait idle-wait timeout (ms) from TYPEAGENT_REASONING_TIMEOUT_MS, +// falling back to DEFAULT_REASONING_TIMEOUT_MS. 0 means "disabled"; since the SDK +// cannot take 0/Infinity (setTimeout would fire immediately), disabled and any +// too-large value are clamped to the largest delay setTimeout accepts. +export function resolveReasoningTimeoutMs(): number { + const raw = process.env.TYPEAGENT_REASONING_TIMEOUT_MS; + const parsed = raw !== undefined ? Number(raw) : NaN; + if (!Number.isFinite(parsed) || parsed < 0) { + return DEFAULT_REASONING_TIMEOUT_MS; + } + return parsed === 0 + ? MAX_SETTIMEOUT_MS + : Math.min(parsed, MAX_SETTIMEOUT_MS); +} + function resolveModel(context: ActionContext): string { // Live @config override wins, then the COPILOT_REASONING_MODEL env var // (from config.yaml), then the built-in fallback. @@ -399,18 +430,29 @@ function getRecentChatContext( } /** - * Build the full prompt with chat history context prepended. + * Build the full prompt with chat history and editor context prepended. * (Same implementation as Claude) */ function buildPromptWithContext( originalRequest: string, context: ActionContext, ): string { + const parts: string[] = []; const chatContext = getRecentChatContext(context); if (chatContext) { - return `${chatContext}\n\n[Current request]\n${originalRequest}`; + parts.push(chatContext); + } + const editorContext = formatUserContextForPrompt( + context.sessionContext.agentContext.currentOptions?.userContext, + ); + if (editorContext) { + parts.push(editorContext); + } + if (parts.length === 0) { + return originalRequest; } - return originalRequest; + parts.push(`[Current request]\n${originalRequest}`); + return parts.join("\n\n"); } /** @@ -431,33 +473,87 @@ function formatThinkingDisplay(thinking: string): string { ].join(""); } +type ToolInput = Record; + +function asToolInput(value: unknown): ToolInput | undefined { + return typeof value === "object" && value !== null + ? (value as ToolInput) + : undefined; +} + +function getStringProp( + input: ToolInput | undefined, + key: string, +): string | undefined { + const value = input?.[key]; + return typeof value === "string" ? value : undefined; +} + /** * Format a tool call as a persistent display line. */ -function formatToolCallDisplay(toolName: string, input: any): string { - if (toolName === "discover_actions") { - const schema = input?.schemaName ?? JSON.stringify(input); - return `**Tool:** discover_actions β€” schema: \`${schema}\``; - } else if (toolName === "execute_action") { - const schema = input?.schemaName ?? "?"; - const actionName = input?.action?.actionName ?? "?"; - return `**Tool:** execute_action β€” \`${schema}.${actionName}\``; - } else if (toolName === "search_memory") { - const question = input?.question ?? JSON.stringify(input); - return `**Tool:** search_memory β€” \`${question}\``; - } else if (toolName === "remember") { - return `**Tool:** remember`; +function parseActionInput(action: unknown): ToolInput | undefined { + if (typeof action !== "string") { + return asToolInput(action); + } + + try { + return asToolInput(JSON.parse(action)); + } catch { + const match = action.match(/"actionName"\s*:\s*"([^"]+)"/); + return match ? { actionName: match[1] } : undefined; } +} + +function formatExecuteActionDisplay(input: unknown): string { + const toolInput = asToolInput(input); + const schema = getStringProp(toolInput, "schemaName") ?? "?"; + // The model sometimes supplies `action` as a JSON string rather than an + // object, and at tool.execution_start that string can still be + // mid-stream (truncated). Parse it when possible, otherwise pull the + // actionName out directly so the label shows the real action, not "?". + const action = parseActionInput(toolInput?.action); + const actionName = getStringProp(action, "actionName") ?? "?"; + return `**Tool:** execute_action β€” \`${schema}.${actionName}\``; +} + +function getPrimaryToolArg(input: unknown): string | undefined { + const toolInput = asToolInput(input); + const primaryArg = + getStringProp(toolInput, "command") ?? + getStringProp(toolInput, "path") ?? + getStringProp(toolInput, "filePath") ?? + getStringProp(toolInput, "query") ?? + getStringProp(toolInput, "pattern"); + return typeof primaryArg === "string" && primaryArg.length > 0 + ? primaryArg + : undefined; +} + +function formatToolCallDisplay(toolName: string, input: unknown): string { + const toolInput = asToolInput(input); + switch (toolName) { + case "discover_actions": { + const schema = + getStringProp(toolInput, "schemaName") ?? JSON.stringify(input); + return `**Tool:** discover_actions β€” schema: \`${schema}\``; + } + case "execute_action": + return formatExecuteActionDisplay(input); + case "search_memory": { + const question = + getStringProp(toolInput, "question") ?? JSON.stringify(input); + return `**Tool:** search_memory β€” \`${question}\``; + } + case "remember": + return `**Tool:** remember`; + } + // Built-in tools (shell, github/fs/*, github/search/*, ...): show the // primary argument so parallel or similar calls are distinguishable // instead of rendering as identical "Tool: " bubbles. - const primaryArg = - input?.command ?? - input?.path ?? - input?.filePath ?? - input?.query ?? - input?.pattern; - if (typeof primaryArg === "string" && primaryArg.length > 0) { + const primaryArg = getPrimaryToolArg(input); + if (primaryArg) { return `**Tool:** ${toolName} β€” \`${primaryArg}\``; } return `**Tool:** ${toolName}`; @@ -478,6 +574,15 @@ function getCopilotSessionConfig( context: ActionContext, ): SessionConfig { const systemContext = context.sessionContext.agentContext; + // Capture the request's clientIO now, before execute_action transiently + // swaps systemContext.clientIO for display capture. get_user_context reads + // through this stable reference so a parallel execute_action can't strand + // it on the capturing clientIO. + const baseClientIO = systemContext.clientIO; + // The originating request id (carries connectionId) so the agent-server + // routing can prefer THIS client's editor context over other clients on + // the same conversation. + const originatorRequestId = systemContext.currentRequestId; const activeSchemas = systemContext.agents.getActiveSchemas(); // Build validators for action schemas (same as Claude) @@ -503,6 +608,24 @@ function getCopilotSessionConfig( // Define custom tools using Copilot SDK (mirrors Claude's MCP tools) let actionIndex = 1; + // Serialize execute_action invocations (see its handler below): the display + // capture there swaps the shared systemContext.clientIO around an await, and + // the Copilot SDK dispatches parallel tool calls concurrently. Overlapping + // handlers would clobber that slot and strand clientIO on an abandoned + // capture buffer, dropping the final answer and hanging the UI spinner. + // + // TODO: Revisit parallel action execution in the dispatcher. This mutex + // makes concurrent execute_action calls safe by running them one at a time, + // which is fine for fast actions but loses real overlap when a turn fires + // multiple slow I/O actions (e.g. two webFetch or claudeTask calls). Doing + // it properly means capturing per call instead of swapping the shared + // clientIO (thread a clientIO sink through executeAction/getActionContext), + // AND isolating the other per-session state executeAction mutates + // (lastActionSchemaName, streamingActionContext, commandResult accumulation, + // activityContext, actionIndex). The dispatcher is serial by design today + // (commandLock = createLimiter(1)), so this is a broader change. + const executeActionLock = createLimiter(1); + const discoverTool = defineTool("discover_actions", { description: [ "Discover actions available with a schema name.", @@ -570,67 +693,70 @@ function getCopilotSessionConfig( }, required: ["schemaName", "action"], }, - handler: async (args: any) => { - const { schemaName, action: actionJson } = args; - debug(`Executing action: ${schemaName}.${actionJson.actionName}`); - const validator = validators.get(schemaName); - if (!validator) { - throw new Error(`Invalid schema name '${schemaName}'`); - } + handler: async (args: any) => + executeActionLock(async () => { + const { schemaName, action: actionJson } = args; + debug( + `Executing action: ${schemaName}.${actionJson.actionName}`, + ); + const validator = validators.get(schemaName); + if (!validator) { + throw new Error(`Invalid schema name '${schemaName}'`); + } - const validationResult = validator.validate(actionJson); - if (!validationResult.success) { - throw new Error(validationResult.message); - } + const validationResult = validator.validate(actionJson); + if (!validationResult.success) { + throw new Error(validationResult.message); + } - // Capture action execution results (same as Claude) - const result: IAgentMessage[] = []; - const capturingClientIO: ClientIO = { - ...nullClientIO, - setDisplay: (message) => { - result.push(message); - }, - appendDisplay: (message, mode) => { - if (mode !== "temporary") { + // Capture action execution results (same as Claude) + const result: IAgentMessage[] = []; + const capturingClientIO: ClientIO = { + ...nullClientIO, + setDisplay: (message) => { result.push(message); - } - }, - }; - - const savedClientIO = systemContext.clientIO; - try { - systemContext.clientIO = capturingClientIO; - await executeAction( - { - action: { - schemaName, - ...actionJson, - }, }, - context, - actionIndex++, - ); - systemContext.clientIO = savedClientIO; - - // Return result in Copilot SDK format - return { - textResultForLlm: JSON.stringify(result), - resultType: "success" as const, + appendDisplay: (message, mode) => { + if (mode !== "temporary") { + result.push(message); + } + }, }; - } catch (error) { - systemContext.clientIO = savedClientIO; - const errorMessage = - error instanceof Error ? error.message : String(error); - debug(`Error executing action: ${errorMessage}`); - // Return error in Copilot SDK format - return { - textResultForLlm: `Error executing ${schemaName}.${actionJson.actionName}: ${errorMessage}`, - resultType: "failure" as const, - error: errorMessage, - }; - } - }, + const savedClientIO = systemContext.clientIO; + try { + systemContext.clientIO = capturingClientIO; + await executeAction( + { + action: { + schemaName, + ...actionJson, + }, + }, + context, + actionIndex++, + ); + systemContext.clientIO = savedClientIO; + + // Return result in Copilot SDK format + return { + textResultForLlm: JSON.stringify(result), + resultType: "success" as const, + }; + } catch (error) { + systemContext.clientIO = savedClientIO; + const errorMessage = + error instanceof Error ? error.message : String(error); + debug(`Error executing action: ${errorMessage}`); + + // Return error in Copilot SDK format + return { + textResultForLlm: `Error executing ${schemaName}.${actionJson.actionName}: ${errorMessage}`, + resultType: "failure" as const, + error: errorMessage, + }; + } + }), }); const searchMemoryTool = defineTool("search_memory", { @@ -803,6 +929,37 @@ function getCopilotSessionConfig( }, }); + const getUserContextTool = defineTool("get_user_context", { + description: [ + "Get a fresh, coarse snapshot of the user's current editor context:", + "active file path, language, cursor and selection ranges, workspace", + "folders, open editor count, and diagnostic counts.", + "Contains NO file or selection text. To read actual contents, use the", + "code agent's read actions (getActiveEditor, getSelection,", + "getFileContent, getDiagnostics) via discover_actions/execute_action.", + ].join("\n"), + parameters: { + type: "object", + properties: {}, + required: [], + }, + handler: async () => { + const userContext = + await baseClientIO.getUserContext?.(originatorRequestId); + if (!userContext) { + return { + textResultForLlm: + "No editor context is available (the client is not an editor, or there is no active editor).", + resultType: "success" as const, + }; + } + return { + textResultForLlm: JSON.stringify(userContext, null, 2), + resultType: "success" as const, + }; + }, + }); + const model = resolveModel(context); const reasoningEffort = resolveReasoningEffort(context); @@ -818,6 +975,7 @@ function getCopilotSessionConfig( rememberTool, getConversationInfoTool, readConversationTool, + getUserContextTool, ], availableTools: [ "discover_actions", @@ -826,6 +984,7 @@ function getCopilotSessionConfig( "remember", "get_conversation_info", "read_conversation", + "get_user_context", "github/fs/*", "github/search/*", "shell", @@ -857,6 +1016,10 @@ function getCopilotSessionConfig( "- `get_conversation_info`: Get transcript metadata (message count, contributing agents)", "- `read_conversation`: Page through the raw conversation transcript (offset/limit)", "", + "## Editor Context Tools", + "- `get_user_context`: Fresh coarse snapshot of the user's editor (active file path, language, cursor/selection ranges, workspace, diagnostic counts). Contains NO file or selection text.", + "- For actual file/selection **text**, use the code agent's read actions (getActiveEditor, getSelection, getFileContent, getDiagnostics) via discover_actions/execute_action.", + "", "## Guidelines", '- **For follow-up questions** that refer to earlier turns (e.g. "those", "it", "mine"), consult the [Recent conversation context] block first; use `search_memory` only for older history not shown there', "- **PREFER built-in tools** for web search, file operations, and code investigation", @@ -1114,7 +1277,7 @@ async function executeReasoningWithoutPlanning( } const response: any = await withAbortSignal( - session.sendAndWait({ prompt }), + session.sendAndWait({ prompt }, resolveReasoningTimeoutMs()), context.abortSignal, ); debug("Received response from Copilot"); @@ -1393,7 +1556,7 @@ async function executeReasoningWithTracing( debug(`Sending prompt: ${prompt.substring(0, 100)}...`); const response: any = await withAbortSignal( - session.sendAndWait({ prompt }), + session.sendAndWait({ prompt }, resolveReasoningTimeoutMs()), context.abortSignal, ); debug("Received response from Copilot"); diff --git a/ts/packages/dispatcher/dispatcher/src/reasoning/userContextPrompt.ts b/ts/packages/dispatcher/dispatcher/src/reasoning/userContextPrompt.ts new file mode 100644 index 0000000000..63c1e871f8 --- /dev/null +++ b/ts/packages/dispatcher/dispatcher/src/reasoning/userContextPrompt.ts @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { UserContext } from "@typeagent/dispatcher-types"; + +type EditorContext = NonNullable; + +/** + * Pick a backtick fence longer than any backtick run in `text`, so a selection + * that itself contains a ``` line can't prematurely close the block. + */ +function codeFence(text: string): string { + const longestRun = (text.match(/`+/g) ?? []).reduce( + (max, run) => Math.max(max, run.length), + 0, + ); + return "`".repeat(Math.max(3, longestRun + 1)); +} + +function toEditorLine(line: number): number { + return line + 1; +} + +function toEditorColumn(column: number): number { + return column + 1; +} + +function selectionLineRange(startLine: number, endLine: number): string { + return startLine === endLine ? `${startLine}` : `${startLine}-${endLine}`; +} + +function selectionWhere( + editor: EditorContext, + startLine: number, + endLine: number, +): string { + const lineRange = selectionLineRange(startLine, endLine); + return editor.activeFilePath + ? `${editor.activeFilePath}:${lineRange}` + : `lines ${lineRange}`; +} + +function appendActiveFile(lines: string[], editor: EditorContext): void { + if (!editor.activeFilePath) { + return; + } + const lang = editor.languageId ? ` [${editor.languageId}]` : ""; + const dirty = editor.isDirty ? " (unsaved changes)" : ""; + lines.push(`Active file: ${editor.activeFilePath}${lang}${dirty}`); +} + +function appendCursor(lines: string[], editor: EditorContext): void { + if (!editor.cursor) { + return; + } + lines.push( + `Cursor: line ${toEditorLine(editor.cursor.line)}, col ${toEditorColumn(editor.cursor.character)}`, + ); +} + +function appendSelectionText(lines: string[], editor: EditorContext): void { + const selection = editor.selection; + if (!selection?.text) { + return; + } + + const fence = codeFence(selection.text); + const startLine = toEditorLine(selection.start.line); + const endLine = toEditorLine(selection.end.line); + const where = selectionWhere(editor, startLine, endLine); + const truncated = selection.truncated ? ", truncated" : ""; + lines.push( + `Selected text (${where}${truncated}):`, + `${fence}${editor.languageId ?? ""}`, + selection.text, + fence, + ); + + if (selection.truncated) { + lines.push( + "[Selection truncated - only the first part is shown above; use the code agent's getSelection read action for the full selection.]", + ); + } +} + +function appendSelection(lines: string[], editor: EditorContext): void { + const selection = editor.selection; + if (!selection || selection.isEmpty) { + return; + } + + lines.push( + `Selection: line ${toEditorLine(selection.start.line)} col ${toEditorColumn(selection.start.character)} to line ${toEditorLine(selection.end.line)} col ${toEditorColumn(selection.end.character)}`, + ); + appendSelectionText(lines, editor); +} + +function appendDiagnostics(lines: string[], editor: EditorContext): void { + const diagnostics = editor.diagnostics; + if (!diagnostics) { + return; + } + + const { errors, warnings, infos, hints, items, omitted } = diagnostics; + if (!errors && !warnings && !infos && !hints) { + return; + } + + lines.push( + `Diagnostics in file: ${errors} error(s), ${warnings} warning(s), ${infos} info, ${hints} hint(s)`, + ); + for (const d of items ?? []) { + const src = d.source ? ` (${d.source})` : ""; + lines.push( + ` - [${d.severity}] line ${toEditorLine(d.line)}: ${d.message}${src}`, + ); + } + if (omitted) { + lines.push(` - ...and ${omitted} more`); + } +} + +function appendWorkspace(lines: string[], editor: EditorContext): void { + if (!editor.workspaceFolders?.length) { + return; + } + lines.push(`Workspace: ${editor.workspaceFolders.join(", ")}`); +} + +function appendOpenEditors(lines: string[], editor: EditorContext): void { + if (typeof editor.openEditorCount !== "number") { + return; + } + + lines.push(`Open editors: ${editor.openEditorCount}`); + for (const openEditor of editor.openEditors ?? []) { + const flags = [ + openEditor.active ? "active" : undefined, + openEditor.dirty ? "unsaved" : undefined, + ].filter(Boolean); + const suffix = flags.length ? ` (${flags.join(", ")})` : ""; + lines.push(` - ${openEditor.path}${suffix}`); + } + if (editor.openEditorsOmitted) { + lines.push(` - ...and ${editor.openEditorsOmitted} more`); + } +} + +/** + * Render the coarse editor context into an "[Editor context]" prompt block for + * the reasoning engines. Emits nothing unless structured editor detail is + * present (the bare activeApp name alone isn't useful to the model). Includes + * the active selection's text (bounded by the host) when present, but not full + * file contents - it points the model at the code agent's read actions to fetch + * fuller contents on demand. Line/column numbers are converted from the host's + * 0-based indexing to 1-based for readability. + */ +export function formatUserContextForPrompt( + userContext: UserContext | undefined, +): string { + const editor = userContext?.editor; + if (!editor) { + return ""; + } + + const lines: string[] = ["[Editor context]"]; + appendActiveFile(lines, editor); + appendCursor(lines, editor); + appendSelection(lines, editor); + appendDiagnostics(lines, editor); + appendWorkspace(lines, editor); + appendOpenEditors(lines, editor); + + lines.push( + "Note: the selected text above (when shown) is included, but full file contents are NOT. Call get_user_context for a fresh snapshot, or the code agent's read actions (getSelection, getActiveEditor, getFileContent, getDiagnostics) to fetch fuller contents when needed.", + ); + + return lines.join("\n"); +} diff --git a/ts/packages/dispatcher/dispatcher/test/reasoningTimeout.spec.ts b/ts/packages/dispatcher/dispatcher/test/reasoningTimeout.spec.ts new file mode 100644 index 0000000000..60e707c591 --- /dev/null +++ b/ts/packages/dispatcher/dispatcher/test/reasoningTimeout.spec.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { resolveReasoningTimeoutMs } from "../src/reasoning/copilot.js"; + +const ENV_KEY = "TYPEAGENT_REASONING_TIMEOUT_MS"; +const DEFAULT_MS = 20 * 60 * 1000; +const MAX_SETTIMEOUT_MS = 2_147_483_647; + +describe("resolveReasoningTimeoutMs", () => { + let saved: string | undefined; + beforeEach(() => { + saved = process.env[ENV_KEY]; + delete process.env[ENV_KEY]; + }); + afterEach(() => { + if (saved === undefined) { + delete process.env[ENV_KEY]; + } else { + process.env[ENV_KEY] = saved; + } + }); + + it("falls back to the 20 minute default when unset", () => { + expect(resolveReasoningTimeoutMs()).toBe(DEFAULT_MS); + }); + + it("uses an explicit positive value as-is", () => { + process.env[ENV_KEY] = "90000"; + expect(resolveReasoningTimeoutMs()).toBe(90000); + }); + + it("treats 0 as disabled by clamping to the max setTimeout delay", () => { + // The SDK feeds the value straight into setTimeout, so 0 (or Infinity) + // would fire immediately. Disabled must map to the largest safe delay. + process.env[ENV_KEY] = "0"; + expect(resolveReasoningTimeoutMs()).toBe(MAX_SETTIMEOUT_MS); + }); + + it("clamps values above the max setTimeout delay", () => { + process.env[ENV_KEY] = String(MAX_SETTIMEOUT_MS + 1000); + expect(resolveReasoningTimeoutMs()).toBe(MAX_SETTIMEOUT_MS); + }); + + it("falls back to the default for negative values", () => { + process.env[ENV_KEY] = "-5"; + expect(resolveReasoningTimeoutMs()).toBe(DEFAULT_MS); + }); + + it("falls back to the default for non-numeric values", () => { + process.env[ENV_KEY] = "not-a-number"; + expect(resolveReasoningTimeoutMs()).toBe(DEFAULT_MS); + }); + + it("never returns the SDK's spurious 60s idle-wait default", () => { + // Regression guard: the original bug was session.sendAndWait using its + // built-in 60000ms idle-wait cap, which rejected legitimate long + // multi-tool reasoning turns that were still making progress. + expect(resolveReasoningTimeoutMs()).toBeGreaterThan(60000); + }); +}); diff --git a/ts/packages/dispatcher/dispatcher/test/userContextPrompt.spec.ts b/ts/packages/dispatcher/dispatcher/test/userContextPrompt.spec.ts new file mode 100644 index 0000000000..bee14f4fb8 --- /dev/null +++ b/ts/packages/dispatcher/dispatcher/test/userContextPrompt.spec.ts @@ -0,0 +1,290 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { describe, it, expect } from "@jest/globals"; +import type { UserContext } from "@typeagent/dispatcher-types"; +import { formatUserContextForPrompt } from "../src/reasoning/userContextPrompt.js"; + +// formatUserContextForPrompt renders the coarse editor snapshot into the +// "[Editor context]" prompt block shared by the copilot/claude reasoning +// engines. It must: emit nothing without editor detail, convert the host's +// 0-based positions to 1-based for display, omit an empty selection, include +// the active selection's text (bounded) when present, and never carry full file +// contents (only metadata + a pointer to the code agent for those). + +describe("formatUserContextForPrompt", () => { + it("returns empty string when userContext is undefined", () => { + expect(formatUserContextForPrompt(undefined)).toBe(""); + }); + + it("returns empty string when there is no editor detail", () => { + const ctx: UserContext = { activeApp: "vscode" }; + expect(formatUserContextForPrompt(ctx)).toBe(""); + }); + + it("renders the active file with language and dirty marker", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "src/foo.ts", + languageId: "typescript", + isDirty: true, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("[Editor context]"); + expect(block).toContain("Active file: src/foo.ts [typescript]"); + expect(block).toContain("(unsaved changes)"); + }); + + it("converts 0-based cursor and selection to 1-based", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + cursor: { line: 0, character: 4 }, + selection: { + isEmpty: false, + start: { line: 9, character: 0 }, + end: { line: 11, character: 2 }, + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Cursor: line 1, col 5"); + expect(block).toContain("Selection: line 10 col 1 to line 12 col 3"); + }); + + it("omits the selection line when the selection is empty", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "a.ts", + selection: { + isEmpty: true, + start: { line: 3, character: 0 }, + end: { line: 3, character: 0 }, + }, + }, + }; + expect(formatUserContextForPrompt(ctx)).not.toContain("Selection:"); + }); + + it("shows diagnostics only when there are any", () => { + const none: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "a.ts", + diagnostics: { errors: 0, warnings: 0, infos: 0, hints: 0 }, + }, + }; + expect(formatUserContextForPrompt(none)).not.toContain( + "Diagnostics in file", + ); + + const some: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "a.ts", + diagnostics: { errors: 2, warnings: 1, infos: 0, hints: 0 }, + }, + }; + expect(formatUserContextForPrompt(some)).toContain( + "Diagnostics in file: 2 error(s), 1 warning(s)", + ); + }); + + it("inlines diagnostic messages (1-based line) with an omitted count", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "a.ts", + diagnostics: { + errors: 1, + warnings: 1, + infos: 0, + hints: 0, + items: [ + { + severity: "error", + line: 11, + message: "Cannot find name 'foo'.", + source: "ts", + }, + { + severity: "warning", + line: 3, + message: "'x' is declared but never used.", + }, + ], + omitted: 4, + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain( + " - [error] line 12: Cannot find name 'foo'. (ts)", + ); + expect(block).toContain( + " - [warning] line 4: 'x' is declared but never used.", + ); + expect(block).toContain(" - ...and 4 more"); + }); + + it("lists workspace folders and open editor count", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + workspaceFolders: ["TypeAgent", "docs"], + openEditorCount: 3, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Workspace: TypeAgent, docs"); + expect(block).toContain("Open editors: 3"); + }); + + it("lists open editors with active/unsaved flags and an omitted count", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + openEditorCount: 25, + openEditors: [ + { path: "src/a.ts", active: true, dirty: false }, + { path: "src/b.ts", active: false, dirty: true }, + { path: "src/c.ts", active: false, dirty: false }, + ], + openEditorsOmitted: 2, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Open editors: 25"); + expect(block).toContain(" - src/a.ts (active)"); + expect(block).toContain(" - src/b.ts (unsaved)"); + expect(block).toContain(" - src/c.ts"); + expect(block).toContain(" - ...and 2 more"); + }); + + it("points the model at the code agent for fuller contents", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { activeFilePath: "a.ts" }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("full file contents are NOT"); + expect(block).toContain("getSelection"); + }); + + it("includes the selected text in a fenced block with the language", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: "src/foo.ts", + languageId: "typescript", + selection: { + isEmpty: false, + start: { line: 14, character: 0 }, + end: { line: 14, character: 67 }, + text: 'import * as speechSDK from "microsoft-cognitiveservices-speech-sdk";', + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Selected text (src/foo.ts:15):"); + expect(block).toContain("```typescript"); + expect(block).toContain( + 'import * as speechSDK from "microsoft-cognitiveservices-speech-sdk";', + ); + }); + + it("labels the selected text with its file path and line range", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + activeFilePath: + "packages/vscode-shell/src/webview/cameraView.ts", + languageId: "typescript", + selection: { + isEmpty: false, + start: { line: 78, character: 8 }, + end: { line: 82, character: 10 }, + text: "this.acceptButton.onclick = () => {};", + }, + }, + }; + expect(formatUserContextForPrompt(ctx)).toContain( + "Selected text (packages/vscode-shell/src/webview/cameraView.ts:79-83):", + ); + }); + + it("marks truncated selection text with an explicit notice", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + selection: { + isEmpty: false, + start: { line: 0, character: 0 }, + end: { line: 99, character: 0 }, + text: "const x = 1;", + truncated: true, + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toMatch(/Selected text \([^)]*truncated\):/); + expect(block).toContain("Selection truncated"); + expect(block).toContain("only the first part is shown"); + }); + + it("does not add a truncation notice for a complete selection", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + selection: { + isEmpty: false, + start: { line: 0, character: 0 }, + end: { line: 0, character: 12 }, + text: "const x = 1;", + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Selected text (lines 1):"); + expect(block).not.toContain("truncated"); + }); + + it("renders the range but no text block when text is absent", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + selection: { + isEmpty: false, + start: { line: 0, character: 0 }, + end: { line: 0, character: 3 }, + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + expect(block).toContain("Selection: line 1 col 1 to line 1 col 4"); + expect(block).not.toContain("Selected text"); + }); + + it("uses a longer fence when the selection contains a backtick run", () => { + const ctx: UserContext = { + activeApp: "vscode", + editor: { + languageId: "markdown", + selection: { + isEmpty: false, + start: { line: 0, character: 0 }, + end: { line: 2, character: 3 }, + text: "```ts\nconst x = 1;\n```", + }, + }, + }; + const block = formatUserContextForPrompt(ctx); + // Fence must be longer than the ``` inside the selection so it does not + // close early. + expect(block).toContain("````markdown"); + expect(block).toContain("```ts\nconst x = 1;\n```"); + }); +}); diff --git a/ts/packages/dispatcher/rpc/README.AUTOGEN.md b/ts/packages/dispatcher/rpc/README.AUTOGEN.md index 98dda04629..89f2a73bae 100644 --- a/ts/packages/dispatcher/rpc/README.AUTOGEN.md +++ b/ts/packages/dispatcher/rpc/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # @typeagent/dispatcher-rpc β€” AI-generated documentation @@ -12,63 +12,69 @@ ## Overview -The `@typeagent/dispatcher-rpc` package provides Remote Procedure Call (RPC) functionality for the TypeAgent dispatcher. It enables communication between different components of the TypeAgent system, allowing them to invoke methods and exchange data over RPC channels. +The `@typeagent/dispatcher-rpc` package provides Remote Procedure Call (RPC) functionality for the TypeAgent dispatcher. It serves as a bridge for communication between different components of the TypeAgent system, enabling them to invoke methods and exchange data over RPC channels. This package is a core part of the TypeAgent architecture, facilitating both user-facing interactions and backend command processing. ## What it does -This package implements RPC clients and servers for two primary components: `ClientIO` and `Dispatcher`. These components facilitate communication and coordination within the TypeAgent system. +This package implements RPC clients and servers for two primary components: `ClientIO` and `Dispatcher`. These components are responsible for distinct but complementary roles within the TypeAgent system. ### ClientIO -The `ClientIO` component is responsible for managing user interactions and display updates. It supports actions such as: +The `ClientIO` component handles user interactions and display updates. It supports a range of actions, including: -- `clear`, `exit`, and `shutdown` for managing session state. -- `setUserRequest` for handling user input. -- `setDisplayInfo`, `setDisplay`, and `appendDisplay` for updating display content. -- `appendDiagnosticData` and `setDynamicDisplay` for managing diagnostic and dynamic display data. +- **Session Management**: Actions like `clear`, `exit`, and `shutdown` manage the session state. +- **User Input**: The `setUserRequest` action processes user commands or inputs. +- **Display Updates**: Actions such as `setDisplayInfo`, `setDisplay`, and `appendDisplay` update the user interface with new information. +- **Diagnostics and Dynamic Displays**: Actions like `appendDiagnosticData` and `setDynamicDisplay` manage diagnostic information and dynamic display elements. -These actions allow the `ClientIO` component to handle user-facing operations and maintain the display state. +These actions enable `ClientIO` to act as the interface between the user and the system, ensuring a responsive and interactive experience. ### Dispatcher -The `Dispatcher` component focuses on processing commands and managing dynamic displays. It includes actions such as: +The `Dispatcher` component is focused on command processing and dynamic display management. Key actions include: -- `submitCommand` for submitting commands to the dispatcher. -- `getDynamicDisplay`, `getTemplateSchema`, and `getTemplateCompletion` for retrieving display and template-related data. -- `getCommandCompletion` and `checkCache` for managing command execution and caching. -- `close` and `getStatus` for managing the dispatcher lifecycle and retrieving its status. +- **Command Submission**: The `submitCommand` action allows clients to send commands to the dispatcher for processing. +- **Display and Template Management**: Actions like `getDynamicDisplay`, `getTemplateSchema`, and `getTemplateCompletion` provide access to display and template-related data. +- **Command Execution and Caching**: The `getCommandCompletion` and `checkCache` actions manage the execution of commands and their caching. +- **Lifecycle Management**: Actions such as `close` and `getStatus` handle the lifecycle and status of the dispatcher. -The `Dispatcher` component is central to coordinating command execution and managing templates within the system. +The `Dispatcher` is a central component for coordinating command execution and managing templates, ensuring that the system operates efficiently and effectively. ## Setup -To use the `@typeagent/dispatcher-rpc` package, ensure the following dependencies are installed: +To use the `@typeagent/dispatcher-rpc` package, ensure the following dependencies are installed in your project: - `@typeagent/agent-rpc` - `@typeagent/agent-sdk` - `@typeagent/dispatcher-types` -You can install these dependencies using `pnpm install`. No additional environment variables or external accounts are required. For more details, refer to the hand-written README. +You can install these dependencies using the following command: + +```bash +pnpm install +``` + +No additional environment variables or external accounts are required for setup. For more details, refer to the hand-written README. ## Key Files -The package is organized into several key files, each serving a specific purpose: +The package is organized into several key files, each with a specific role in implementing the RPC functionality: -- [clientIOClient.ts](./src/clientIOClient.ts): Implements the RPC client for the `ClientIO` component, enabling it to send requests and receive responses. -- [clientIOServer.ts](./src/clientIOServer.ts): Implements the RPC server for the `ClientIO` component, handling incoming requests and invoking the appropriate actions. -- [dispatcherClient.ts](./src/dispatcherClient.ts): Implements the RPC client for the `Dispatcher` component, providing methods to interact with the dispatcher remotely. -- [dispatcherServer.ts](./src/dispatcherServer.ts): Implements the RPC server for the `Dispatcher` component, processing incoming commands and managing dispatcher state. -- [clientIOTypes.ts](./src/clientIOTypes.ts): Defines type information for `ClientIO` RPC functions, including both callable and invokable actions. -- [dispatcherTypes.ts](./src/dispatcherTypes.ts): Defines type information for `Dispatcher` RPC functions, including wire-side variants of certain types. -- [types.ts](./src/types.ts): Re-exports types from `@typeagent/dispatcher-types` and `@typeagent/agent-sdk` for convenience. +- **[clientIOClient.ts](./src/clientIOClient.ts)**: Implements the RPC client for the `ClientIO` component, enabling it to send requests and receive responses. +- **[clientIOServer.ts](./src/clientIOServer.ts)**: Implements the RPC server for the `ClientIO` component, handling incoming requests and invoking the appropriate actions. +- **[dispatcherClient.ts](./src/dispatcherClient.ts)**: Implements the RPC client for the `Dispatcher` component, providing methods to interact with the dispatcher remotely. +- **[dispatcherServer.ts](./src/dispatcherServer.ts)**: Implements the RPC server for the `Dispatcher` component, processing incoming commands and managing dispatcher state. +- **[clientIOTypes.ts](./src/clientIOTypes.ts)**: Defines type information for `ClientIO` RPC functions, including callable and invokable actions. +- **[dispatcherTypes.ts](./src/dispatcherTypes.ts)**: Defines type information for `Dispatcher` RPC functions, including wire-side variants of certain types. +- **[types.ts](./src/types.ts)**: Re-exports types from `@typeagent/dispatcher-types` and `@typeagent/agent-sdk` for convenience. -The package uses the `createRpc` function from `@typeagent/agent-rpc` to establish RPC communication between clients and servers. This function is central to the implementation of the RPC layer. +The package relies on the `createRpc` function from `@typeagent/agent-rpc` to establish and manage RPC communication between clients and servers. This function is a cornerstone of the package's architecture. ## How to extend -To extend the functionality of the `@typeagent/dispatcher-rpc` package, follow these steps: +To extend the `@typeagent/dispatcher-rpc` package, follow these steps: -1. **Determine the target component**: Decide whether you need to extend the `ClientIO` or `Dispatcher` component based on your requirements. +1. **Identify the target component**: Determine whether your extension applies to the `ClientIO` or `Dispatcher` component. 2. **Define new actions**: Add the new actions to the appropriate type definition file: @@ -86,7 +92,7 @@ To extend the functionality of the `@typeagent/dispatcher-rpc` package, follow t 6. **Run tests**: Execute the test suite to confirm that your changes work as expected and do not introduce regressions. -By following these steps, you can extend the `@typeagent/dispatcher-rpc` package to support additional functionality or integrate new features into the TypeAgent system. +By following these steps, you can effectively extend the `@typeagent/dispatcher-rpc` package to meet new requirements or integrate additional features into the TypeAgent system. ## Reference @@ -94,11 +100,11 @@ By following these steps, you can extend the `@typeagent/dispatcher-rpc` package ### Entry points -- `./types` β†’ `./dist/types.js` _(not found on disk)_ -- `./clientio/client` β†’ `./dist/clientIOClient.js` _(not found on disk)_ -- `./clientio/server` β†’ `./dist/clientIOServer.js` _(not found on disk)_ -- `./dispatcher/client` β†’ `./dist/dispatcherClient.js` _(not found on disk)_ -- `./dispatcher/server` β†’ `./dist/dispatcherServer.js` _(not found on disk)_ +- `./types` β†’ [./dist/types.js](./dist/types.js) +- `./clientio/client` β†’ [./dist/clientIOClient.js](./dist/clientIOClient.js) +- `./clientio/server` β†’ [./dist/clientIOServer.js](./dist/clientIOServer.js) +- `./dispatcher/client` β†’ [./dist/dispatcherClient.js](./dist/dispatcherClient.js) +- `./dispatcher/server` β†’ [./dist/dispatcherServer.js](./dist/dispatcherServer.js) ### Dependencies @@ -134,6 +140,6 @@ External: _None at runtime._ --- -_Auto-generated against commit `ff379b098decfab4eb45f78b6fa318358d7fbd75` on `2026-07-01T09:05:58.471Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/dispatcher-rpc docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/dispatcher-rpc docs:verify-links` to spot-check._ diff --git a/ts/packages/dispatcher/rpc/src/clientIOClient.ts b/ts/packages/dispatcher/rpc/src/clientIOClient.ts index 312ba0462a..8962a9cc1d 100644 --- a/ts/packages/dispatcher/rpc/src/clientIOClient.ts +++ b/ts/packages/dispatcher/rpc/src/clientIOClient.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { createRpc } from "@typeagent/agent-rpc/rpc"; -import type { ClientIO } from "@typeagent/dispatcher-types"; +import type { ClientIO, UserContext } from "@typeagent/dispatcher-types"; import type { ClientIOCallFunctions, ClientIOInvokeFunctions, @@ -50,6 +50,12 @@ export function createClientIORpcClient(channel: RpcChannel): ClientIO { proposeAction(...args): Promise { return rpc.invoke("proposeAction", ...args); }, + // requestId (when a caller passes one) selects a client at the routing + // layer; the per-client leaf reports its own editor state, so nothing + // is forwarded over the wire here. + getUserContext(): Promise { + return rpc.invoke("getUserContext"); + }, notify(...args): void { return rpc.send("notify", ...args); }, diff --git a/ts/packages/dispatcher/rpc/src/clientIOServer.ts b/ts/packages/dispatcher/rpc/src/clientIOServer.ts index 84ef51bb69..53b189d51a 100644 --- a/ts/packages/dispatcher/rpc/src/clientIOServer.ts +++ b/ts/packages/dispatcher/rpc/src/clientIOServer.ts @@ -26,6 +26,9 @@ export function createClientIORpcServer( closeLocalView: async (...args) => { return clientIO.closeLocalView(...args); }, + getUserContext: async (...args) => { + return clientIO.getUserContext?.(...args); + }, }; const clientIOCallFunctions: ClientIOCallFunctions = { diff --git a/ts/packages/dispatcher/rpc/src/clientIOTypes.ts b/ts/packages/dispatcher/rpc/src/clientIOTypes.ts index fc9deb443e..9cc8c7215e 100644 --- a/ts/packages/dispatcher/rpc/src/clientIOTypes.ts +++ b/ts/packages/dispatcher/rpc/src/clientIOTypes.ts @@ -11,6 +11,7 @@ import type { RequestId, TemplateEditConfig, PendingInteractionRequest, + UserContext, UserFeedbackEntry, UserMessageHiddenEntry, } from "@typeagent/dispatcher-types"; @@ -30,6 +31,7 @@ export type ClientIOInvokeFunctions = { ): Promise; openLocalView(requestId: RequestId, port: number): Promise; closeLocalView(requestId: RequestId, port: number): Promise; + getUserContext(): Promise; }; export type ClientIOCallFunctions = { diff --git a/ts/packages/dispatcher/types/README.AUTOGEN.md b/ts/packages/dispatcher/types/README.AUTOGEN.md index b5c139a7bb..0186f56473 100644 --- a/ts/packages/dispatcher/types/README.AUTOGEN.md +++ b/ts/packages/dispatcher/types/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # @typeagent/dispatcher-types β€” AI-generated documentation @@ -12,29 +12,29 @@ ## Overview -The `@typeagent/dispatcher-types` package provides a centralized collection of TypeScript type definitions for the TypeAgent dispatcher. These types are essential for ensuring consistent and type-safe communication between various components of the TypeAgent ecosystem, such as agents, clients, and dispatchers. By standardizing the data structures and interfaces, this package helps maintain compatibility and reliability across the system. +The `@typeagent/dispatcher-types` package provides a collection of TypeScript type definitions for the TypeAgent dispatcher. These types are used to standardize communication and data structures across the TypeAgent ecosystem, ensuring type safety and consistency between components such as agents, clients, and dispatchers. ## What it does -This package defines and exports TypeScript types that are used throughout the TypeAgent dispatcher and its related components. These types are consumed by multiple packages in the TypeAgent monorepo, including `@typeagent/agent-server-protocol`, `@typeagent/copilot-plugin`, and `agent-dispatcher`. The key functionalities provided by this package include: +This package defines and exports TypeScript types that are integral to the operation of the TypeAgent dispatcher and its related components. These types are widely consumed by other packages in the TypeAgent monorepo, such as `@typeagent/agent-server-protocol`, `@typeagent/copilot-plugin`, and `agent-dispatcher`. Key areas covered by the package include: -- **Dispatcher Requests and Responses**: Types such as `RequestId`, `PendingInteractionRequest`, and `PendingInteractionResponse` define the structure of requests and responses handled by the dispatcher. -- **Client Input/Output Operations**: Types like `IAgentMessage`, `TemplateEditConfig`, and `NotifyExplainedData` are used to manage client interactions, including message formatting and data exchange. -- **Dispatcher Status Management**: The package includes types and helper functions (e.g., `getStatusSummary` in [status.ts](./src/helpers/status.ts)) to represent and summarize the state of the dispatcher. -- **Queue Management**: Types such as `QueuedRequest`, `QueueCancelReason`, and `QueueRequestState` define the structure and lifecycle of server-side message queues. -- **Logging and Display**: Types like `SetDisplayEntry` and `AppendDisplayEntry` in [displayLogEntry.ts](./src/displayLogEntry.ts) are used for managing and formatting log entries for display purposes. +- **Dispatcher Requests and Responses**: Types like `RequestId`, `PendingInteractionRequest`, and `PendingInteractionResponse` define the structure of requests and responses managed by the dispatcher. +- **Client Input/Output Operations**: Types such as `IAgentMessage`, `TemplateEditConfig`, and `NotifyExplainedData` are used to handle client interactions, including message formatting and data exchange. +- **Dispatcher Status Management**: Includes types and helper functions (e.g., `getStatusSummary` in [status.ts](./src/helpers/status.ts)) to represent and summarize the state of the dispatcher. +- **Queue Management**: Types like `QueuedRequest`, `QueueCancelReason`, and `QueueRequestState` define the structure and lifecycle of server-side message queues. +- **Logging and Display**: Types such as `SetDisplayEntry` and `AppendDisplayEntry` in [displayLogEntry.ts](./src/displayLogEntry.ts) are used for managing and formatting log entries for display purposes. -These types are foundational for the operation of the TypeAgent dispatcher and its integration with other components in the system. +These types are foundational for the operation of the dispatcher and its integration with other components in the system. ## Setup -This package does not require any special setup beyond installation. To include it in your project, simply run: +This package does not require any special setup beyond installation. To include it in your project, run: ```bash pnpm install ``` -For additional details, refer to the hand-written README. +For further details, refer to the hand-written README. ## Key Files @@ -103,6 +103,6 @@ External: _None at runtime._ --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/dispatcher-types docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/dispatcher-types docs:verify-links` to spot-check._ diff --git a/ts/packages/dispatcher/types/src/clientIO.ts b/ts/packages/dispatcher/types/src/clientIO.ts index db1cd808a2..a001650a95 100644 --- a/ts/packages/dispatcher/types/src/clientIO.ts +++ b/ts/packages/dispatcher/types/src/clientIO.ts @@ -8,7 +8,7 @@ import { TemplateSchema, TypeAgentAction, } from "@typeagent/agent-sdk"; -import { RequestId, RequestMetrics } from "./dispatcher.js"; +import { RequestId, RequestMetrics, UserContext } from "./dispatcher.js"; import type { UserFeedbackEntry, UserMessageHiddenEntry, @@ -105,6 +105,19 @@ export interface ClientIO { source: string, ): Promise; + /** + * Return a fresh, coarse snapshot of the host editor state (no file or + * selection text). Optional: only editor-hosted clients (e.g. the VS Code + * shell) implement it. Others omit it and the reasoning `get_user_context` + * tool reports that no editor context is available. + * + * `requestId` carries the originating client's connectionId so a routing + * implementation (the agent-server SharedDispatcher) can prefer the client + * that issued the request when several are joined to one conversation. Leaf + * clients ignore it and report their own editor state. + */ + getUserContext?(requestId?: RequestId): Promise; + // Notification (TODO: turn these in to dispatcher events) // Default behavior is ephemeral: notifications are broadcast to clients // but not written to the DisplayLog. Pass options.persist=true to opt in diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index e38d2ef584..9418be1b55 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -159,6 +159,94 @@ export type AgentSchemaInfo = { subSchemas: AgentSubSchemaInfo[]; }; +/** + * A line/character position. Uses the host editor's native 0-based indexing + * (VS Code), so ranges line up with the CODA read actions that return text. + */ +export type EditorPosition = { + line: number; + character: number; +}; + +/** A single diagnostic (compiler/linter message) on the active file. */ +export type DiagnosticItem = { + severity: "error" | "warning" | "info" | "hint"; + /** 0-based line of the diagnostic's start position. */ + line: number; + message: string; + /** Producer of the diagnostic (e.g. "ts", "eslint"), when known. */ + source?: string; +}; + +/** One open file editor (tab) in the host. */ +export type OpenEditorInfo = { + /** Workspace-relative path of the open file. */ + path: string; + /** Whether this is the active tab. */ + active: boolean; + /** Whether the tab has unsaved changes. */ + dirty: boolean; +}; + +/** + * Coarse snapshot of the host editor state. Carries lightweight metadata + * (paths, ranges, counts) plus bounded samples of what the user is likely + * pointing at - the active selection's text, the active file's diagnostic + * messages, and the open-editor list. Heavier text (full file contents, git + * diff, on-screen range) is still pulled on demand through the CODA read + * actions so nothing large is attached by default. + */ +export type EditorContext = { + /** Workspace-relative path of the active file, if any. */ + activeFilePath?: string; + /** Language id of the active file (e.g. "typescript"). */ + languageId?: string; + /** Whether the active file has unsaved changes. */ + isDirty?: boolean; + /** Cursor position (0-based). */ + cursor?: EditorPosition; + /** Selection range (0-based). `isEmpty` true means it is just the caret. */ + selection?: { + isEmpty: boolean; + start: EditorPosition; + end: EditorPosition; + /** + * The selected text, present only when the selection is non-empty. + * Bounded to a cap by the host (large highlights are truncated with + * `truncated: true`) so a big selection can't bloat the prompt. Full + * file contents are still pulled on demand via the CODA read actions. + */ + text?: string; + /** True when `text` was truncated to the host's cap. */ + truncated?: boolean; + }; + /** Names of the open workspace folders. */ + workspaceFolders?: string[]; + /** + * Diagnostics for the active file: severity counts plus a bounded, + * severity-ordered sample of the actual messages so the model can act on + * "fix the error" without a separate pull. + */ + diagnostics?: { + errors: number; + warnings: number; + infos: number; + hints: number; + /** Bounded sample of the actual diagnostics (errors first). */ + items?: DiagnosticItem[]; + /** Count of diagnostics beyond the sampled `items`. */ + omitted?: number; + }; + /** Number of open editor tabs across all groups. */ + openEditorCount?: number; + /** + * Bounded list of open file editors so the model can resolve references + * like "the other file". `openEditorsOmitted` counts any beyond the cap. + */ + openEditors?: OpenEditorInfo[]; + openEditorsOmitted?: number; +}; + /** * User-environment context for translation prompts. * Provides information about the host application context to improve translation accuracy. @@ -168,6 +256,18 @@ export type UserContext = { activeApp: string; /** Free-text description of the app, typically copied from the agent manifest. Optional. */ activeAppDescription?: string; + /** + * Coarse editor state: lightweight metadata plus a bounded copy of the + * active selection's text (no full file contents). Present when the host is + * an editor (e.g. the VS Code shell). Pull heavier text via CODA. + */ + editor?: EditorContext; + // TODO (client-capability context): UserContext models only the + // editor/environment state today. A separate "client capability" context - + // what the originating client can do (render images, apply diffs, voice, + // whether it hosts an editor) - is not modeled yet. Add it here (or as a + // sibling type) when reasoning needs capability-aware responses. See + // docs/plans/vscode-devx. }; export type ProcessCommandOptions = { diff --git a/ts/packages/shell/README.AUTOGEN.md b/ts/packages/shell/README.AUTOGEN.md index 86b7433f4f..3dd6773162 100644 --- a/ts/packages/shell/README.AUTOGEN.md +++ b/ts/packages/shell/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # agent-shell β€” AI-generated documentation @@ -16,7 +16,7 @@ The `agent-shell` package is a TypeScript library that serves as the graphical u ## What it does -The `agent-shell` package offers a range of features to enable interactive and conversational agent experiences: +The `agent-shell` package provides a rich set of features to enable interactive and conversational agent experiences: ### Conversation Management @@ -172,6 +172,6 @@ _5 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `5c9fc637c2f0a96d75d41a3bc9054d06247d26d8` on `2026-07-15T08:50:41.068Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-shell docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter agent-shell docs:verify-links` to spot-check._ diff --git a/ts/packages/shell/src/main/instance.ts b/ts/packages/shell/src/main/instance.ts index 559147df62..f3a4e6e2dc 100644 --- a/ts/packages/shell/src/main/instance.ts +++ b/ts/packages/shell/src/main/instance.ts @@ -286,8 +286,8 @@ async function initializeDispatcher( // brief server hiccups (server restart, transient network // blip when running with --connect to a remote host). // Backoff schedule mirrors the vscode-shell extension's - // AgentServerBridge.scheduleReconnect (4/6/8/...30s cap). - const MAX_RECONNECT_ATTEMPTS = 12; // ~5 minutes total at the 30s cap + // AgentServerBridge.scheduleReconnect (5/10/20/40/60s cap). + const MAX_RECONNECT_ATTEMPTS = 5; // ~135s total (5/10/20/40/60s backoff) let reconnectAttempt = 0; let reconnecting = false; // Set once auto-reconnect has exhausted MAX_RECONNECT_ATTEMPTS. The @@ -336,9 +336,11 @@ async function initializeDispatcher( reconnectAttempt < MAX_RECONNECT_ATTEMPTS ) { reconnectAttempt++; + // Exponential backoff (5/10/20/40/60s, capped at 60s), + // matching the vscode-shell AgentServerBridge schedule. const backoffSec = Math.min( - 30, - 2 + reconnectAttempt * 2, + 60, + 5 * 2 ** (reconnectAttempt - 1), ); debugShellInit( `Reconnect attempt ${reconnectAttempt} in ${backoffSec}s`, diff --git a/ts/packages/telemetry/README.AUTOGEN.md b/ts/packages/telemetry/README.AUTOGEN.md index a6700c2f1b..2e46690637 100644 --- a/ts/packages/telemetry/README.AUTOGEN.md +++ b/ts/packages/telemetry/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # telemetry β€” AI-generated documentation @@ -12,19 +12,17 @@ ## Overview -The `telemetry` package is a TypeScript library designed to provide logging and monitoring capabilities for the TypeAgent project. It enables the collection and storage of telemetry data, such as events, logs, and performance metrics, by offering a variety of logger sinks that integrate with different storage backends, including MongoDB and Azure Cosmos DB. - -This package is a core dependency for many other packages and examples within the TypeAgent monorepo, making it a critical component for tracking and analyzing system behavior. +The `telemetry` package is a TypeScript library that provides logging and monitoring capabilities for the TypeAgent project. It enables the collection, storage, and analysis of telemetry data, such as events, logs, and performance metrics. By offering a variety of logger sinks and utilities, the package supports both development and production use cases, making it a core dependency for many other packages and examples in the TypeAgent monorepo. ## What it does -The `telemetry` package provides a flexible and extensible logging framework. Its primary functionality revolves around logger sinks, which are responsible for handling and storing log events. The package includes the following key features: +The `telemetry` package is designed to handle diverse logging and monitoring needs through a modular and extensible architecture. Its primary features include: - **Database Logging**: - `createMongoDBLoggerSink`: Logs events to a MongoDB collection. - `createCosmosDBLoggerSink`: Logs events to an Azure Cosmos DB container. - - `createDatabaseLoggerSink`: Dynamically selects between MongoDB and Cosmos DB based on the availability of environment variables. + - `createDatabaseLoggerSink`: Dynamically selects between MongoDB and Cosmos DB based on the configured environment variables. - **Console Logging**: @@ -32,13 +30,13 @@ The `telemetry` package provides a flexible and extensible logging framework. It - **Specialized Loggers**: - - `createPromptLogger`: Logs LLM (Large Language Model) prompts and related data. + - `createPromptLogger`: Captures and logs prompts and related data for Large Language Models (LLMs). - `createProfileLogger`: Captures and logs profiling data for performance analysis. - **Multi-Sink Logging**: - `MultiSinkLogger`: Combines multiple logger sinks, enabling simultaneous logging to different destinations. -These features allow the `telemetry` package to support a wide range of use cases, from debugging during development to monitoring production systems. +These features allow the `telemetry` package to support a wide range of use cases, from debugging during development to monitoring and analyzing production systems. The package is used extensively across the TypeAgent monorepo, including in components like `agent-api`, `agent-cache`, and `chat-agent`. ## Setup @@ -47,13 +45,13 @@ To use the `telemetry` package, you need to configure the following environment - `COSMOSDB_CONNECTION_STRING`: The connection string for Azure Cosmos DB. This is required if you plan to use the `createCosmosDBLoggerSink` or `createDatabaseLoggerSink` with Cosmos DB as the backend. - `MONGODB_CONNECTION_STRING`: The connection string for MongoDB. This is required if you plan to use the `createMongoDBLoggerSink` or `createDatabaseLoggerSink` with MongoDB as the backend. -Ensure these environment variables are set in your environment or in a `.env` file at the root of the project. For more details on obtaining these values, refer to the hand-written README. +These environment variables can be set in your shell or in a `.env` file located at the root of the project. For detailed instructions on obtaining these values, refer to the hand-written README. ## Key Files The `telemetry` package is organized into several key files, each responsible for specific functionality: -- [indexNode.ts](./src/indexNode.ts): The main entry point of the package. It exports all the logger sinks, utilities, and core components. +- [indexNode.ts](./src/indexNode.ts): The main entry point of the package, exporting all logger sinks, utilities, and core components. - [logger/logger.ts](./src/logger/logger.ts): Defines the core logging interfaces and classes, such as `Logger`, `LoggerSink`, `ChildLogger`, and `MultiSinkLogger`. - [logger/cosmosDBLoggerSink.ts](./src/logger/cosmosDBLoggerSink.ts): Implements the `CosmosDBLoggerSink`, which logs events to an Azure Cosmos DB container. - [logger/mongoLoggerSink.ts](./src/logger/mongoLoggerSink.ts): Implements the `MongoDBLoggerSink`, which logs events to a MongoDB collection. @@ -64,7 +62,7 @@ The `telemetry` package is organized into several key files, each responsible fo ## How to extend -The `telemetry` package is designed to be extensible, allowing contributors to add new functionality or modify existing components. Here are some guidelines for extending the package: +The `telemetry` package is designed to be extensible, allowing contributors to add new functionality or modify existing components. Below are some guidelines for extending the package: ### Adding a New Logger Sink @@ -128,11 +126,13 @@ By following these guidelines, you can extend the `telemetry` package to meet ne ### Entry points -- default β†’ `./dist/indexNode.js` _(not found on disk)_ +- default β†’ [./dist/indexNode.js](./dist/indexNode.js) ### Dependencies -Workspace: _None._ +Workspace: + +- [@typeagent/common-utils](../../packages/utils/commonUtils/README.md) External: `chalk`, `debug`, `dotenv`, `find-config`, `mongodb` @@ -163,6 +163,6 @@ _2 environment variables referenced from `./src/` (set in `ts/.env` or your shel --- -_Auto-generated against commit `366aaf867a7e8e5d130b6c87a365516bab725269` on `2026-07-07T09:05:05.703Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter telemetry docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter telemetry docs:verify-links` to spot-check._ diff --git a/ts/packages/telemetry/package.json b/ts/packages/telemetry/package.json index e732e176d9..1b4f999e70 100644 --- a/ts/packages/telemetry/package.json +++ b/ts/packages/telemetry/package.json @@ -33,6 +33,7 @@ "tsc": "tsc -b" }, "dependencies": { + "@typeagent/common-utils": "workspace:*", "chalk": "^5.4.1", "debug": "^4.4.0", "dotenv": "^16.3.1", diff --git a/ts/packages/telemetry/src/logger/cosmosDBLoggerSink.ts b/ts/packages/telemetry/src/logger/cosmosDBLoggerSink.ts index 6ffa79f15b..65e8ed5d8a 100644 --- a/ts/packages/telemetry/src/logger/cosmosDBLoggerSink.ts +++ b/ts/packages/telemetry/src/logger/cosmosDBLoggerSink.ts @@ -9,6 +9,7 @@ import { CosmosPartitionKeyBuilder, CosmosBulkOperation, } from "./logger.js"; +import { filterSecretsFromObject } from "@typeagent/common-utils"; import registerDebug from "debug"; import { randomUUID } from "crypto"; @@ -120,7 +121,7 @@ class CosmosDBLoggerSink implements LoggerSink { operationType: "Create" as const, partitionKey: partitionKey, resourceBody: { - ...event, + ...filterSecretsFromObject(event), id: partitionKey.toString(), }, }; diff --git a/ts/packages/telemetry/src/logger/mongoLoggerSink.ts b/ts/packages/telemetry/src/logger/mongoLoggerSink.ts index 47b92b4eb4..2c72ac95f2 100644 --- a/ts/packages/telemetry/src/logger/mongoLoggerSink.ts +++ b/ts/packages/telemetry/src/logger/mongoLoggerSink.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { LoggerSink, LogEvent } from "./logger.js"; +import { filterSecretsFromObject } from "@typeagent/common-utils"; import { MongoClient } from "mongodb"; import registerDebug from "debug"; @@ -84,7 +85,10 @@ class MongoDBLoggerSink implements LoggerSink { this.pendingEvents = []; try { - await collection.insertMany(events); + const docs = events.map((event) => + filterSecretsFromObject(event), + ); + await collection.insertMany(docs); debugMongo(`${events.length} events uploaded`); } catch (e: any) { if (this.pendingEvents.length !== 0) { diff --git a/ts/packages/utils/commonUtils/README.AUTOGEN.md b/ts/packages/utils/commonUtils/README.AUTOGEN.md index 231a7c3e1f..bd23bf13aa 100644 --- a/ts/packages/utils/commonUtils/README.AUTOGEN.md +++ b/ts/packages/utils/commonUtils/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # @typeagent/common-utils β€” AI-generated documentation @@ -12,11 +12,11 @@ ## Overview -The `@typeagent/common-utils` package provides a set of utility functions and types that are shared across the TypeAgent project. These utilities are designed to simplify common tasks such as object property manipulation, base64 encoding/decoding, promise handling, and rate limiting. The package is used extensively across the TypeAgent monorepo, serving as a foundational library for other packages and tools. +The `@typeagent/common-utils` package provides a collection of utility functions and types that are shared across the TypeAgent project. These utilities address common programming needs such as object property manipulation, base64 encoding/decoding, promise handling, and rate limiting. The package is a foundational library used by many other packages within the TypeAgent monorepo. ## What it does -The `@typeagent/common-utils` package includes a variety of utility functions and types that can be categorized as follows: +The `@typeagent/common-utils` package offers a variety of utilities grouped into the following categories: 1. **Object Property Utilities**: @@ -31,7 +31,7 @@ The `@typeagent/common-utils` package includes a variety of utility functions an 3. **Promise Handling**: - - `createPromiseWithResolvers`: Creates a promise along with its associated `resolve` and `reject` functions, enabling more flexible promise handling. + - `createPromiseWithResolvers`: Creates a promise along with its associated `resolve` and `reject` functions, enabling more flexible promise management. 4. **Rate Limiting**: @@ -50,17 +50,17 @@ The `@typeagent/common-utils` package includes a variety of utility functions an - `resolveCliOnPath`: Resolves the absolute path of a CLI executable on the system's PATH. - `claudeExecutableOption`: Provides options for resolving the path to the Claude CLI executable. -These utilities are designed to work in both Node.js and browser environments, with separate implementations for platform-specific functionality, such as base64 encoding/decoding. +These utilities are designed to work in both Node.js and browser environments. Platform-specific functionality, such as base64 encoding/decoding, is implemented separately for each environment. ## Setup -This package does not require any special setup beyond installing its dependencies. To install the package, use the following command: +This package does not require any special setup beyond installing its dependencies. To install the package, run: ```sh pnpm install ``` -For more details on the installation process or prerequisites, refer to the hand-written README. +For additional details, refer to the hand-written README. ## Key Files @@ -68,7 +68,7 @@ The `@typeagent/common-utils` package is organized into several key files, each - **Entry Points**: - - [indexNode.ts](./src/indexNode.ts): The main entry point for Node.js environments. It exports utilities specific to Node.js, such as `resolveCliOnPath` and `claudeExecutableOption`. + - [indexNode.ts](./src/indexNode.ts): The main entry point for Node.js environments. It exports Node.js-specific utilities, such as `resolveCliOnPath` and `claudeExecutableOption`. - [indexBrowser.ts](./src/indexBrowser.ts): The main entry point for browser environments. It exports browser-compatible utilities, such as base64 encoding/decoding functions. - **Source Files**: @@ -121,8 +121,8 @@ By following these steps, you can contribute effectively to the `@typeagent/comm ### Entry points -- default β†’ `./dist/indexNode.js` _(not found on disk)_ -- default β†’ `./dist/indexBrowser.js` _(not found on disk)_ +- default β†’ [./dist/indexNode.js](./dist/indexNode.js) +- default β†’ [./dist/indexBrowser.js](./dist/indexBrowser.js) ### Dependencies @@ -135,21 +135,21 @@ External: `chalk`, `debug` - [@typeagent/action-grammar](../../../packages/actionGrammar/README.md) - [@typeagent/agent-rpc](../../../packages/agentRpc/README.md) - [@typeagent/agent-sdk](../../../packages/agentSdk/README.md) +- [@typeagent/aiclient](../../../packages/aiclient/README.md) - [agent-cache](../../../packages/cache/README.md) - [agent-cli](../../../packages/cli/README.md) - [agent-dispatcher](../../../packages/dispatcher/dispatcher/README.md) - [agent-server](../../../packages/agentServer/server/README.md) - [agent-shell](../../../packages/shell/README.md) - [browser-typeagent](../../../packages/agents/browser/README.md) -- [cache-rest-endpoint](../../../examples/cacheRESTEndpoint/README.md) -- _…and 14 more workspace consumers._ +- _…and 16 more workspace consumers._ ### Files of interest -`./src/base64Browser.ts`, `./src/base64Node.ts`, `./src/cliPath.ts`, …and 9 more under `./src/`. +`./src/base64Browser.ts`, `./src/base64Node.ts`, `./src/cliPath.ts`, …and 10 more under `./src/`. --- -_Auto-generated against commit `366aaf867a7e8e5d130b6c87a365516bab725269` on `2026-07-07T09:05:05.703Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/common-utils docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter @typeagent/common-utils docs:verify-links` to spot-check._ diff --git a/ts/packages/utils/commonUtils/src/indexBrowser.ts b/ts/packages/utils/commonUtils/src/indexBrowser.ts index 6d958b526d..6ed8c3e1c1 100644 --- a/ts/packages/utils/commonUtils/src/indexBrowser.ts +++ b/ts/packages/utils/commonUtils/src/indexBrowser.ts @@ -11,3 +11,16 @@ export { createLimiter } from "./limiter.js"; export { uint8ArrayToBase64, base64ToUint8Array } from "./base64Browser.js"; export { createPromiseWithResolvers } from "./promiseWithResolvers.js"; + +export { + filterSecrets, + filterSecretsFromObject, + filterSecretsFromJsonString, + createSecretFilter, + SECRET_PATTERNS, + DEFAULT_SECRET_REPLACEMENT, + type SecretPattern, + type SecretFilter, + type FilterSecretsOptions, + type CreateSecretFilterOptions, +} from "./secretFilter.js"; diff --git a/ts/packages/utils/commonUtils/src/indexNode.ts b/ts/packages/utils/commonUtils/src/indexNode.ts index 2be6063b11..686fc11bcb 100644 --- a/ts/packages/utils/commonUtils/src/indexNode.ts +++ b/ts/packages/utils/commonUtils/src/indexNode.ts @@ -19,3 +19,16 @@ export { uint8ArrayToBase64, base64ToUint8Array } from "./base64Node.js"; export { createPromiseWithResolvers } from "./promiseWithResolvers.js"; export { resolveCliOnPath, claudeExecutableOption } from "./cliPath.js"; + +export { + filterSecrets, + filterSecretsFromObject, + filterSecretsFromJsonString, + createSecretFilter, + SECRET_PATTERNS, + DEFAULT_SECRET_REPLACEMENT, + type SecretPattern, + type SecretFilter, + type FilterSecretsOptions, + type CreateSecretFilterOptions, +} from "./secretFilter.js"; diff --git a/ts/packages/utils/commonUtils/src/secretFilter.ts b/ts/packages/utils/commonUtils/src/secretFilter.ts new file mode 100644 index 0000000000..e6489a0295 --- /dev/null +++ b/ts/packages/utils/commonUtils/src/secretFilter.ts @@ -0,0 +1,316 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// A dependency-free secret scrubber for redacting credentials out of text +// before it is logged, persisted, or sent to a third party (e.g. an LLM +// prompt). It combines two independent layers: +// +// 1. Known-value redaction - literal occurrences of secret *values* the +// caller already holds (API keys read from config, tokens, etc.). +// 2. Pattern redaction - a curated list of regexes that match common secret +// *formats* (GitHub tokens, JWTs, cloud keys, `Bearer ...`, etc.). +// +// The pattern list is authored from publicly documented secret formats +// (GitHub secret-scanning, gitleaks/detect-secrets style detectors); it is not +// derived from any proprietary source. It catches formatted or known secrets, +// but cannot detect an arbitrary high-entropy value with no recognizable +// prefix - treat it as defense-in-depth, not a guarantee. + +/** Sentinel written in place of a redacted secret. */ +export const DEFAULT_SECRET_REPLACEMENT = "******"; + +// Known values shorter than this are ignored, both to avoid redacting trivial +// tokens that legitimately appear everywhere and to prevent a stray short/empty +// value from blanking out unrelated text. +const MIN_VALUE_LENGTH = 4; + +/** + * A named secret-format detector. + * + * `regex` MUST carry the global (`g`) flag so every occurrence is replaced. + * When `group` is set, only that capture group is redacted and the surrounding + * match (e.g. a `Bearer ` prefix or a `password=` key) is preserved; otherwise + * the whole match is replaced. + */ +export interface SecretPattern { + readonly name: string; + readonly regex: RegExp; + readonly group?: number; +} + +/** + * Curated detectors for common secret formats. Exported so callers can inspect, + * subset, or extend the list (e.g. `[...SECRET_PATTERNS, myPattern]`). + */ +export const SECRET_PATTERNS: readonly SecretPattern[] = [ + // PEM-encoded private keys (RSA / EC / DSA / OpenSSH / PGP / generic). + { + name: "private-key", + regex: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g, + }, + // JSON Web Tokens: three base64url segments separated by dots. + { + name: "jwt", + regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, + }, + // GitHub classic PAT / OAuth / app / refresh tokens (ghp_, gho_, ghu_, ghs_, ghr_). + { name: "github-token", regex: /\bgh[opsur]_[A-Za-z0-9]{36,}\b/g }, + // GitHub fine-grained personal-access tokens. + { + name: "github-fine-grained-pat", + regex: /\bgithub_pat_[A-Za-z0-9]{22}_[A-Za-z0-9]{59}\b/g, + }, + // GitHub OAuth access tokens of the form v1.<40 hex>. + { name: "github-oauth-v1", regex: /\bv1\.[0-9a-f]{40}\b/g }, + // OpenAI API keys, including project-scoped `sk-proj-` keys. + { name: "openai-key", regex: /\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}/g }, + // AWS access key IDs. + { + name: "aws-access-key-id", + regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA)[0-9A-Z]{16}\b/g, + }, + // Google API keys. + { name: "google-api-key", regex: /\bAIza[0-9A-Za-z_-]{35}\b/g }, + // Slack tokens (bot / user / app / refresh / legacy). + { name: "slack-token", regex: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/g }, + // npm access tokens. + { name: "npm-token", regex: /\bnpm_[A-Za-z0-9]{36}\b/g }, + // Azure shared-key connection-string fragments (Storage / Service Bus / etc.). + { + name: "azure-account-key", + regex: /\b(AccountKey=)([A-Za-z0-9+/=]{40,})/gi, + group: 2, + }, + // Credentials embedded in a URL: scheme://user:password@host - redact the password. + { + name: "url-basic-auth", + regex: /\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)([^\s/@]+)@/gi, + group: 2, + }, + // HTTP Bearer authorization tokens - redact the token, keep the `Bearer ` prefix. + { + name: "bearer-token", + regex: /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{8,})/gi, + group: 2, + }, + // password / passwd / pwd assignments (optionally quoted key and value) - + // redact the value, keep the key so the surrounding text stays readable. + { + name: "password-assignment", + regex: /(?:password|passwd|pwd)"?\s*[:=]\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s'";,)}]+)/gi, + group: 1, + }, + // Command-line password flags: -Password value / --pwd "value". + { + name: "password-flag", + regex: /(?:^|\s)-{1,2}(?:password|passwd|pwd)\s+("[^"]*"|'[^']*'|[^\s'";]+)/gi, + group: 1, + }, +]; + +/** Options for {@link filterSecrets}. */ +export interface FilterSecretsOptions { + /** Literal secret values to redact wherever they appear. */ + readonly values?: Iterable | undefined; + /** Format detectors to apply. Defaults to {@link SECRET_PATTERNS}. */ + readonly patterns?: readonly SecretPattern[] | undefined; + /** Replacement sentinel. Defaults to {@link DEFAULT_SECRET_REPLACEMENT}. */ + readonly replacement?: string | undefined; + /** + * Predicate to skip a string entirely (returned unchanged when it returns + * true). Use it to leave opaque blobs alone - e.g. base64 `data:` image + * URLs, which are large (hot-path cost) and whose random base64 can + * coincidentally match a key pattern and be corrupted by redaction. + */ + readonly skipValue?: ((value: string) => boolean) | undefined; +} + +function applyPattern( + text: string, + pattern: SecretPattern, + replacement: string, +): string { + if (pattern.group === undefined) { + return text.replace(pattern.regex, replacement); + } + const group = pattern.group; + return text.replace(pattern.regex, (match, ...args) => { + // args = [g1, g2, ..., offset, wholeString, (namedGroups?)] + const captured = args[group - 1]; + if (typeof captured !== "string" || captured.length === 0) { + return match; + } + // Redact only the captured credential within the match, leaving any + // prefix (e.g. `Bearer ` or `password=`) intact. lastIndexOf keeps the + // right target when the prefix cannot contain the captured value. + const at = match.lastIndexOf(captured); + if (at < 0) { + return match; + } + return ( + match.slice(0, at) + replacement + match.slice(at + captured.length) + ); + }); +} + +/** + * Return `text` with known secret values and recognizable secret formats + * replaced by a redaction sentinel. Non-string input is returned unchanged. + */ +export function filterSecrets( + text: string, + options?: FilterSecretsOptions, +): string { + if (typeof text !== "string" || text.length === 0) { + return text; + } + if (options?.skipValue?.(text)) { + return text; + } + const replacement = options?.replacement ?? DEFAULT_SECRET_REPLACEMENT; + const patterns = options?.patterns ?? SECRET_PATTERNS; + + let out = text; + + // Known values first, longest-first so a value that is a substring of a + // longer value does not partially redact and prevent the longer match. + if (options?.values) { + const values = [...options.values] + .map((v) => (typeof v === "string" ? v.trim() : "")) + .filter((v) => v.length >= MIN_VALUE_LENGTH) + .sort((a, b) => b.length - a.length); + for (const value of values) { + out = out.replaceAll(value, replacement); + } + } + + for (const pattern of patterns) { + out = applyPattern(out, pattern, replacement); + } + + return out; +} + +function walkValue(value: unknown, options?: FilterSecretsOptions): unknown { + if (typeof value === "string") { + return filterSecrets(value, options); + } + if (value === null || typeof value !== "object") { + return value; + } + if (Array.isArray(value)) { + return value.map((item) => walkValue(item, options)); + } + // Only recurse into plain objects. Class instances, Date, RegExp, Map, + // etc. pass through untouched - rebuilding them from entries would drop + // their prototype, and structured telemetry / request payloads are plain + // JSON-shaped objects anyway. + const proto = Object.getPrototypeOf(value); + if (proto !== Object.prototype && proto !== null) { + return value; + } + const out: Record = {}; + for (const [key, item] of Object.entries( + value as Record, + )) { + out[key] = walkValue(item, options); + } + return out; +} + +/** + * Recursively redact secrets from every string in `value`, returning a new + * value of the same shape. Arrays and plain objects are copied; numbers, + * booleans, null, and non-plain objects (Date, RegExp, class instances) pass + * through unchanged. Use this to scrub a structured payload (a log event, a + * request body) without disturbing its structure or non-string fields. + */ +export function filterSecretsFromObject( + value: T, + options?: FilterSecretsOptions, +): T { + return walkValue(value, options) as T; +} + +/** + * Redact secrets from a JSON string while keeping it valid JSON: parse it, + * scrub every string value, and re-serialize. If `json` is not valid JSON it + * is scrubbed as raw text instead. Always returns a string. + */ +export function filterSecretsFromJsonString( + json: string, + options?: FilterSecretsOptions, +): string { + if (typeof json !== "string" || json.length === 0) { + return json; + } + try { + const parsed: unknown = JSON.parse(json); + return JSON.stringify(filterSecretsFromObject(parsed, options)); + } catch { + return filterSecrets(json, options); + } +} + +/** + * A stateful scrubber that accumulates known secret values and applies them + * (plus the configured format detectors) on every {@link SecretFilter.filter} + * call. Use this when secret values become known incrementally - for example + * registering each credential as it is read from config. + */ +export interface SecretFilter { + /** Register a secret value. Returns false if ignored (too short / duplicate). */ + addValue(value: string): boolean; + /** Register several secret values. */ + addValues(values: Iterable): void; + /** Redact registered values and format detectors from `text`. */ + filter(text: string): string; + /** Number of distinct values currently registered. */ + readonly size: number; +} + +/** Options for {@link createSecretFilter}. */ +export interface CreateSecretFilterOptions { + readonly patterns?: readonly SecretPattern[] | undefined; + readonly replacement?: string | undefined; + readonly initialValues?: Iterable | undefined; +} + +/** Create a stateful {@link SecretFilter}. */ +export function createSecretFilter( + options?: CreateSecretFilterOptions, +): SecretFilter { + const values = new Set(); + const patterns = options?.patterns ?? SECRET_PATTERNS; + const replacement = options?.replacement ?? DEFAULT_SECRET_REPLACEMENT; + + const addValue = (value: string): boolean => { + const trimmed = typeof value === "string" ? value.trim() : ""; + if (trimmed.length < MIN_VALUE_LENGTH || values.has(trimmed)) { + return false; + } + values.add(trimmed); + return true; + }; + + if (options?.initialValues) { + for (const value of options.initialValues) { + addValue(value); + } + } + + return { + addValue, + addValues(vs: Iterable): void { + for (const value of vs) { + addValue(value); + } + }, + filter(text: string): string { + return filterSecrets(text, { values, patterns, replacement }); + }, + get size(): number { + return values.size; + }, + }; +} diff --git a/ts/packages/utils/commonUtils/test/secretFilter.spec.ts b/ts/packages/utils/commonUtils/test/secretFilter.spec.ts new file mode 100644 index 0000000000..be6eb30fd6 --- /dev/null +++ b/ts/packages/utils/commonUtils/test/secretFilter.spec.ts @@ -0,0 +1,301 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + filterSecrets, + filterSecretsFromObject, + filterSecretsFromJsonString, + createSecretFilter, + SECRET_PATTERNS, + DEFAULT_SECRET_REPLACEMENT, +} from "../src/secretFilter.js"; + +const R = DEFAULT_SECRET_REPLACEMENT; + +describe("filterSecrets - format patterns", () => { + test.each([ + ["github classic token", "token=ghp_" + "a".repeat(36)], + ["github oauth token", "gho_" + "b".repeat(36)], + [ + "github fine-grained pat", + "github_pat_" + "A".repeat(22) + "_" + "B".repeat(59), + ], + ["github v1 oauth", "v1." + "a".repeat(40)], + ["openai key", "sk-" + "A1b2C3d4E5f6G7h8I9j0"], + ["openai project key", "sk-proj-" + "A1b2C3d4E5f6G7h8I9j0kk"], + ["aws access key id", "AKIAIOSFODNN7EXAMPLE"], + ["google api key", "AIza" + "Sy0123456789_-abcdefghijklmnopqrstu"], + ["slack token", "xoxb-1234567890-abcdefABCDEF"], + ["npm token", "npm_" + "z".repeat(36)], + [ + "jwt", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0." + + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + ], + ])("redacts %s", (_label, secret) => { + const out = filterSecrets(`before ${secret} after`); + expect(out).not.toContain(secret); + expect(out).toContain(R); + // Surrounding text is preserved. + expect(out.startsWith("before ")).toBe(true); + expect(out.endsWith(" after")).toBe(true); + }); + + test("redacts a PEM private key block", () => { + const key = [ + "-----BEGIN RSA PRIVATE KEY-----", + "MIIEowIBAAKCAQEA1234567890", + "abcdefghijklmnopqrstuvwxyz", + "-----END RSA PRIVATE KEY-----", + ].join("\n"); + const out = filterSecrets(`key:\n${key}\ndone`); + expect(out).toBe(`key:\n${R}\ndone`); + }); + + test("redacts Azure AccountKey but keeps surrounding fields", () => { + const conn = + "Endpoint=sb://x.net/;SharedAccessKeyName=k;AccountKey=" + + "A".repeat(64) + + "=="; + const out = filterSecrets(conn); + expect(out).toContain("Endpoint=sb://x.net/;"); + expect(out).toContain("SharedAccessKeyName=k;"); + expect(out).toContain("AccountKey=" + R); + }); +}); + +describe("filterSecrets - group-scoped patterns keep context", () => { + test("bearer token keeps the Bearer prefix", () => { + const out = filterSecrets("authorization: Bearer abcdef0123456789xyz"); + expect(out).toBe(`authorization: Bearer ${R}`); + }); + + test("URL basic-auth redacts only the password", () => { + const out = filterSecrets("db at https://user:hunter2pass@host:5432/x"); + expect(out).toBe(`db at https://user:${R}@host:5432/x`); + }); + + test("password assignment keeps the key", () => { + expect(filterSecrets("password=hunter2secret")).toBe(`password=${R}`); + expect(filterSecrets("--Pwd 's3cr3t value'")).toBe(`--Pwd ${R}`); + }); + + test("JSON-style quoted password redacts the value only", () => { + const out = filterSecrets('{ "password": "hunter2secret" }'); + expect(out).toBe(`{ "password": ${R} }`); + }); +}); + +describe("filterSecrets - known values", () => { + test("redacts registered literal values", () => { + const out = filterSecrets("api key is SUPER-secret-value-123 ok", { + values: ["SUPER-secret-value-123"], + }); + expect(out).toBe(`api key is ${R} ok`); + }); + + test("redacts longer overlapping value fully (longest-first)", () => { + const out = filterSecrets("token abcdef-1234 here", { + values: ["abcdef", "abcdef-1234"], + }); + expect(out).toBe(`token ${R} here`); + expect(out).not.toContain("1234"); + }); + + test("ignores empty / whitespace / too-short values", () => { + const text = "keep ab cd this text intact"; + const out = filterSecrets(text, { values: ["", " ", "ab"] }); + expect(out).toBe(text); + }); + + test("honors a custom replacement", () => { + const out = filterSecrets("v = ghp_" + "a".repeat(36), { + replacement: "[SECRET]", + }); + expect(out).toBe("v = [SECRET]"); + }); +}); + +describe("filterSecrets - no false positives", () => { + test.each([ + "the password is on the sticky note", + "commit 0123456789abcdef0123456789abcdef01234567", + "just some ordinary prose without any secrets", + "email me at user@example.com about the bearer of news", + ])("leaves %j unchanged", (text) => { + expect(filterSecrets(text)).toBe(text); + }); + + test("returns empty string unchanged", () => { + expect(filterSecrets("")).toBe(""); + }); +}); + +describe("filterSecrets - reuse safety", () => { + test("global-flag patterns behave consistently across calls", () => { + const secret = "ghp_" + "c".repeat(36); + const first = filterSecrets(`a ${secret} b`); + const second = filterSecrets(`a ${secret} b`); + expect(first).toBe(second); + expect(first).toBe(`a ${R} b`); + }); + + test("redacts multiple occurrences in one string", () => { + const secret = "npm_" + "d".repeat(36); + const out = filterSecrets(`${secret} and ${secret}`); + expect(out).toBe(`${R} and ${R}`); + }); +}); + +describe("createSecretFilter", () => { + test("accumulates values and applies patterns", () => { + const filter = createSecretFilter(); + expect(filter.addValue("my-registered-token-xyz")).toBe(true); + const out = filter.filter( + "my-registered-token-xyz and ghp_" + "a".repeat(36), + ); + expect(out).toBe(`${R} and ${R}`); + }); + + test("addValue dedups and rejects too-short values", () => { + const filter = createSecretFilter(); + expect(filter.addValue("abcd")).toBe(true); + expect(filter.addValue("abcd")).toBe(false); // duplicate + expect(filter.addValue("ab")).toBe(false); // too short + expect(filter.size).toBe(1); + }); + + test("seeds from initialValues and supports addValues", () => { + const filter = createSecretFilter({ + initialValues: ["seed-value-1234"], + }); + filter.addValues(["another-value-5678", "x"]); + expect(filter.size).toBe(2); + const out = filter.filter("seed-value-1234 / another-value-5678"); + expect(out).toBe(`${R} / ${R}`); + }); + + test("honors a custom replacement and pattern subset", () => { + const onlyNpm = SECRET_PATTERNS.filter((p) => p.name === "npm-token"); + const filter = createSecretFilter({ + patterns: onlyNpm, + replacement: "XX", + }); + // npm token is redacted; github token is not (excluded from subset). + const gh = "ghp_" + "a".repeat(36); + const out = filter.filter(`npm_${"e".repeat(36)} ${gh}`); + expect(out).toBe(`XX ${gh}`); + }); +}); + +describe("filterSecretsFromObject", () => { + test("redacts strings in nested objects and arrays, keeps other types", () => { + const token = "ghp_" + "a".repeat(36); + const input = { + model: "gpt", + count: 7, + ok: true, + nothing: null, + messages: [ + { role: "user", content: `here is ${token}` }, + { role: "system", content: "no secrets" }, + ], + }; + const out = filterSecretsFromObject(input); + expect(out.messages[0].content).toBe(`here is ${R}`); + expect(out.messages[1].content).toBe("no secrets"); + expect(out.count).toBe(7); + expect(out.ok).toBe(true); + expect(out.nothing).toBeNull(); + }); + + test("returns a new object and does not mutate the input", () => { + const token = "npm_" + "b".repeat(36); + const input = { secret: token }; + const out = filterSecretsFromObject(input); + expect(out).not.toBe(input); + expect(input.secret).toBe(token); // original untouched + expect(out.secret).toBe(R); + }); + + test("leaves non-plain objects (Date) untouched", () => { + const d = new Date(0); + const out = filterSecretsFromObject({ when: d }); + expect(out.when).toBe(d); + }); + + test("passes through non-object primitives", () => { + expect(filterSecretsFromObject(42 as unknown)).toBe(42); + expect(filterSecretsFromObject(null as unknown)).toBeNull(); + expect(filterSecretsFromObject("Bearer abcdef0123456789")).toBe( + `Bearer ${R}`, + ); + }); +}); + +describe("filterSecretsFromJsonString", () => { + test("keeps JSON valid and redacts string values", () => { + const token = "ghp_" + "c".repeat(36); + const json = JSON.stringify({ + model: "gpt", + messages: [{ role: "user", content: `key ${token}` }], + }); + const out = filterSecretsFromJsonString(json); + const parsed = JSON.parse(out); // must still parse + expect(parsed.messages[0].content).toBe(`key ${R}`); + expect(parsed.model).toBe("gpt"); + }); + + test("does not corrupt a JSON value that itself looks like an assignment", () => { + // Raw-string redaction would strip the surrounding quotes here; the + // JSON-aware path must keep the document parseable. + const json = JSON.stringify({ password: "hunter2secret" }); + const out = filterSecretsFromJsonString(json); + expect(() => JSON.parse(out)).not.toThrow(); + }); + + test("falls back to raw-text redaction for non-JSON input", () => { + const out = filterSecretsFromJsonString("password=hunter2secret"); + expect(out).toBe(`password=${R}`); + }); + + test("returns empty string unchanged", () => { + expect(filterSecretsFromJsonString("")).toBe(""); + }); +}); + +describe("skipValue option", () => { + const skipDataUrl = (s: string) => /^data:[^,]*;base64,/.test(s); + + test("filterSecrets leaves a skipped string untouched", () => { + const dataUrl = "data:image/png;base64,AKIAIOSFODNN7EXAMPLE+abc/def=="; + expect(filterSecrets(dataUrl, { skipValue: skipDataUrl })).toBe( + dataUrl, + ); + // Without the predicate the coincidental AKIA match would be redacted. + expect(filterSecrets(dataUrl)).not.toBe(dataUrl); + }); + + test("walk skips base64 image data URLs but still redacts siblings", () => { + const token = "ghp_" + "a".repeat(36); + const dataUrl = "data:image/png;base64,AKIAIOSFODNN7EXAMPLE+abc/def=="; + const body = JSON.stringify({ + messages: [ + { + content: [ + { type: "text", text: `key ${token}` }, + { type: "image_url", image_url: { url: dataUrl } }, + ], + }, + ], + }); + const out = filterSecretsFromJsonString(body, { + skipValue: skipDataUrl, + }); + const parsed = JSON.parse(out); + // Image left intact, text secret still redacted. + expect(parsed.messages[0].content[1].image_url.url).toBe(dataUrl); + expect(parsed.messages[0].content[0].text).toBe(`key ${R}`); + }); +}); diff --git a/ts/packages/vscode-shell/.vscodeignore b/ts/packages/vscode-shell/.vscodeignore index 9c1a602adc..5dc0b2cb2f 100644 --- a/ts/packages/vscode-shell/.vscodeignore +++ b/ts/packages/vscode-shell/.vscodeignore @@ -1,6 +1,7 @@ .gitignore .vscode/** src/** +prototypes/** tsconfig.json esbuild.mjs node_modules/** diff --git a/ts/packages/vscode-shell/README.AUTOGEN.md b/ts/packages/vscode-shell/README.AUTOGEN.md index 796403b636..b404f1da55 100644 --- a/ts/packages/vscode-shell/README.AUTOGEN.md +++ b/ts/packages/vscode-shell/README.AUTOGEN.md @@ -3,7 +3,7 @@ - + # vscode-shell β€” AI-generated documentation @@ -12,11 +12,11 @@ ## Overview -The `vscode-shell` package integrates the TypeAgent shell chat into Visual Studio Code, providing a way to interact with TypeAgent conversations directly within the editor. It offers a side panel and editor tabs for managing conversations hosted by a running TypeAgent agent server. +The `vscode-shell` package integrates the TypeAgent shell chat into Visual Studio Code, allowing users to interact with TypeAgent conversations directly within the editor. It provides a dedicated side panel and editor tabs for managing conversations hosted by a running TypeAgent agent server. ## What it does -The `vscode-shell` package enables a feature-rich chat experience within Visual Studio Code, tightly integrated with the TypeAgent ecosystem. Its main capabilities include: +The `vscode-shell` package provides a comprehensive chat interface within Visual Studio Code, enabling users to interact with the TypeAgent ecosystem. Key features include: - **Chat Interface**: @@ -170,10 +170,10 @@ External: `ansi_up`, `debug`, `dompurify`, `isomorphic-ws`, `markdown-it`, `micr ### Files of interest -`./src/webview/main.ts`, `./src/agentServerBridge.ts`, `./src/bridge/clientIO.ts`, …and 9 more under `./src/`. +`./src/webview/main.ts`, `./src/agentServerBridge.ts`, `./src/bridge/clientIO.ts`, …and 11 more under `./src/`. --- -_Auto-generated against commit `defc71271dc68db47e0d376be7aa9f755da0ac91` on `2026-07-14T08:47:00.044Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter vscode-shell docs:verify-links` to spot-check._ +_Auto-generated against commit `5cbcf613f047f08749d0451296eb1cdc610ae414` on `2026-07-17T18:24:18.404Z` by `docs-generate.yml`. Links validated at that commit; the working tree may have drifted by up to 24h. Re-run `pnpm --filter vscode-shell docs:verify-links` to spot-check._ diff --git a/ts/packages/vscode-shell/prototypes/message-width-scrollbars.html b/ts/packages/vscode-shell/prototypes/message-width-scrollbars.html new file mode 100644 index 0000000000..e43f88540b --- /dev/null +++ b/ts/packages/vscode-shell/prototypes/message-width-scrollbars.html @@ -0,0 +1,737 @@ + + + + + + + + + + vscode-shell chat β€” width & scrollbar prototypes + + + +
+
+ + + + + +
+
+ + + + +
+
+ +
+ +
+
+
Panel width: 900px
+
+
+
+
+ +
+
+ you- 4:53:44 PM +
+
R
+
+
+ run the powershell flow test and fix whatever is + failing +
+
+
+ + +
+
+ dispatcher.reasoningAction.copilot- 4:53:51 PM +
+
✨
+
+
+
+ Thinking +
+The testPowerShellFlow is failing because the sandbox doesn't allow external executables to spawn child processes without an explicit allowlist entry, so the conditional branch never runs and the here-string is parsed as a literal.
+
+
+
+
+ + +
+
+ dispatcher.reasoningAction.copilot- 4:53:51 PM +
+
✨
+
+
+

+ Tool: execute_action β€” + powershell.? +

+
+
+
+ + +
+
+ dispatcher.reasoningAction.copilot- 4:53:55 PM +
+
✨
+
+
+
+ Thinking +
+The PowerShell test keeps breakingβ€”maybe the sandbox doesn't support conditional statements in the way the harness expects, or the mocked Invoke-Expression shim swallows the exit code before assertion.
+
+
+
+
+ + +
+
+ dispatcher- 4:54:03 PM +
+
πŸ€–
+
+
+
+ Here is the failing assertion and a suggested fix for + the allowlist entry: +
+
Describe "testPowerShellFlow" {
+    It "runs the conditional branch when the allowlist entry is present" {
+        $result = Invoke-Flow -Name 'powershell.conditional' -AllowList @('pwsh.exe','cmd.exe')
+        $result.ExitCode | Should -Be 0        # was failing: sandbox blocked child process spawn
+    }
+}
+
+
+
+{
+  "actionName": "executePowerShell",
+  "parameters": { "script": "if ($ok) { Write-Output 'done' } else { throw 'sandbox blocked external executable spawn without allowlist entry' }", "timeoutMs": 60000 }
+}
+
+
+
+ + +
+
+ dispatcher- 4:54:03 PM +
+
πŸ€–
+
+
+ Error: Timeout after 60000ms waiting for session.idle +
+
+
+
+
+
+
+
+
+ + + + diff --git a/ts/packages/vscode-shell/src/agentServerBridge.ts b/ts/packages/vscode-shell/src/agentServerBridge.ts index 2133e2457b..9330f32d06 100644 --- a/ts/packages/vscode-shell/src/agentServerBridge.ts +++ b/ts/packages/vscode-shell/src/agentServerBridge.ts @@ -36,6 +36,7 @@ import type { import { createBridgeClientIO } from "./bridge/clientIO.js"; import { clientIdOf } from "./bridge/requestIds.js"; import { toHistoryReplayMessage } from "./bridge/historyReplay.js"; +import { gatherUserContext } from "./bridge/userContext.js"; import { createCompletionController, @@ -43,7 +44,6 @@ import { } from "agent-dispatcher/helpers/completion"; import type { CompletionDirection } from "@typeagent/agent-sdk"; import type { - UserContext, ProcessCommandOptions, QueueSnapshot, } from "@typeagent/dispatcher-types"; @@ -56,9 +56,9 @@ export type { // Auto-reconnect gives up after this many failed attempts and surfaces a // "stopped" ribbon with manual Retry / Start links instead of retrying -// forever. With the 2sβ†’30s backoff this is roughly a 5-minute window; a +// forever. With the 5sβ†’60s backoff this is roughly a 2-minute window; a // manual retry resets the counter and resumes from here. -const MAX_RECONNECT_ATTEMPTS = 12; +const MAX_RECONNECT_ATTEMPTS = 5; /** HTML-escape untrusted strings (names, ids, errors) for inline notification HTML. */ function escapeHtml(str: string): string { @@ -155,12 +155,12 @@ export class AgentServerBridge { // wait between reconnect attempts. Replaces the old behavior of // broadcasting a fresh error/disconnect message every retry cycle. private reconnectCountdown: NodeJS.Timeout | undefined; - // Exponential backoff (2s, 4s, 8s, … capped at 30s) shared with the Studio - // service connection. Quick first retries recover fast when the server - // restarts; the cap keeps the long-tail polite for genuinely-down servers. + // Exponential backoff (5s, 10s, 20s, 40s, capped at 60s). Longer gaps keep + // retries polite for a genuinely-down server; the quick-ish first retry + // still recovers fast when the server just restarted. private readonly reconnectBackoff: Backoff = createBackoff({ - baseMs: 2000, - maxMs: 30000, + baseMs: 5000, + maxMs: 60000, }); private reconnectRemainingSec: number | undefined; private lastConnectError: string | undefined; @@ -1935,31 +1935,6 @@ export class AgentServerBridge { } } - /** - * Gather user context from VS Code (active editor, workspace, etc.) - */ - private gatherUserContext(): UserContext { - const activeEditor = vscode.window.activeTextEditor; - const activeWorkspaceFolder = - vscode.workspace.workspaceFolders?.[0]?.name ?? undefined; - - // Build description from active document/language - let activeAppDescription: string | undefined; - if (activeEditor) { - const languageId = activeEditor.document.languageId; - const fileName = - activeEditor.document.fileName.split(/[\\/]/).pop() ?? "file"; - activeAppDescription = `${fileName} (${languageId})`; - } else if (activeWorkspaceFolder) { - activeAppDescription = `Project: ${activeWorkspaceFolder}`; - } - - return { - activeApp: "vscode", - activeAppDescription, - }; - } - private async sendCommand( command: string, requestId?: string, @@ -1997,7 +1972,7 @@ export class AgentServerBridge { } try { - const userContext = this.gatherUserContext(); + const userContext = gatherUserContext(); const options: ProcessCommandOptions = { userContext, }; diff --git a/ts/packages/vscode-shell/src/bridge/clientIO.ts b/ts/packages/vscode-shell/src/bridge/clientIO.ts index 807bc39397..6a79b62f2a 100644 --- a/ts/packages/vscode-shell/src/bridge/clientIO.ts +++ b/ts/packages/vscode-shell/src/bridge/clientIO.ts @@ -16,6 +16,7 @@ import type { DisplayAppendMode, TypeAgentAction } from "@typeagent/agent-sdk"; import type { BridgeToWebviewMessage } from "./messages.js"; import { clientIdOf } from "./requestIds.js"; +import { gatherUserContext } from "./userContext.js"; /** * Narrow callback surface needed by the bridge ClientIO. Keeping this @@ -112,6 +113,7 @@ export function createBridgeClientIO(ctx: BridgeClientIOContext): ClientIO { }, openLocalView: async () => {}, closeLocalView: async () => {}, + getUserContext: async () => gatherUserContext(), // ClientIO call functions (fire-and-forget notifications) clear: (requestId: RequestId) => { diff --git a/ts/packages/vscode-shell/src/bridge/userContext.ts b/ts/packages/vscode-shell/src/bridge/userContext.ts new file mode 100644 index 0000000000..96a5bf7f7a --- /dev/null +++ b/ts/packages/vscode-shell/src/bridge/userContext.ts @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as vscode from "vscode"; +import type { + DiagnosticItem, + EditorContext, + OpenEditorInfo, + UserContext, +} from "@typeagent/dispatcher-types"; + +// Cap on how much selected text we attach. A selection is normally a line or a +// few (an import, a signature, a small block); this keeps the reasoning prompt +// lean and truncates anything larger. Full contents remain available via the +// CODA read actions. +const MAX_SELECTION_TEXT_CHARS = 500; + +// Caps on the diagnostics + open-editor samples we inline. Errors are kept +// first, so the cap drops the least actionable entries. Fuller detail (all +// diagnostics, every tab) is still available via the CODA read actions. +const MAX_DIAGNOSTIC_ITEMS = 10; +const MAX_DIAGNOSTIC_MESSAGE_CHARS = 200; +const MAX_OPEN_EDITORS = 20; + +/** + * Gather a coarse snapshot of the current VS Code editor state. Carries + * lightweight metadata (paths, ranges, counts) plus a bounded copy of the + * active selection's text - the thing the user is directly pointing at. Full + * file contents are still pulled on demand via the CODA read actions, so + * nothing large is attached by default. + * + * Called at send time (attached to every request as + * ProcessCommandOptions.userContext) and again, live, for the + * `get_user_context` reasoning tool. + */ +export function gatherUserContext(): UserContext { + const activeEditor = vscode.window.activeTextEditor; + const workspaceFolders = vscode.workspace.workspaceFolders?.map( + (f) => f.name, + ); + + let activeAppDescription: string | undefined; + let editor: EditorContext | undefined; + + if (activeEditor) { + const doc = activeEditor.document; + const languageId = doc.languageId; + const fileName = doc.fileName.split(/[\\/]/).pop() ?? "file"; + activeAppDescription = `${fileName} (${languageId})`; + editor = { + activeFilePath: vscode.workspace.asRelativePath(doc.uri, false), + languageId, + isDirty: doc.isDirty, + cursor: { + line: activeEditor.selection.active.line, + character: activeEditor.selection.active.character, + }, + selection: { + isEmpty: activeEditor.selection.isEmpty, + start: { + line: activeEditor.selection.start.line, + character: activeEditor.selection.start.character, + }, + end: { + line: activeEditor.selection.end.line, + character: activeEditor.selection.end.character, + }, + ...gatherSelectionText(doc, activeEditor.selection), + }, + ...(workspaceFolders?.length ? { workspaceFolders } : {}), + diagnostics: gatherDiagnostics(doc.uri), + ...gatherOpenEditors(), + }; + } else if (workspaceFolders?.length) { + activeAppDescription = `Project: ${workspaceFolders[0]}`; + editor = { + workspaceFolders, + ...gatherOpenEditors(), + }; + } + + return { + activeApp: "vscode", + ...(activeAppDescription ? { activeAppDescription } : {}), + ...(editor ? { editor } : {}), + }; +} + +/** + * Copy the selected text into the editor context, bounded to + * MAX_SELECTION_TEXT_CHARS. Returns an empty object for an empty selection + * (just the caret) so the `text`/`truncated` fields stay absent. + */ +function gatherSelectionText( + doc: vscode.TextDocument, + selection: vscode.Selection, +): { text?: string; truncated?: boolean } { + if (selection.isEmpty) { + return {}; + } + const full = doc.getText(selection); + if (full.length <= MAX_SELECTION_TEXT_CHARS) { + return { text: full }; + } + return { text: full.slice(0, MAX_SELECTION_TEXT_CHARS), truncated: true }; +} + +function clip(text: string, max: number): string { + return text.length > max ? `${text.slice(0, max)}…` : text; +} + +function severityName( + severity: vscode.DiagnosticSeverity, +): DiagnosticItem["severity"] { + switch (severity) { + case vscode.DiagnosticSeverity.Error: + return "error"; + case vscode.DiagnosticSeverity.Warning: + return "warning"; + case vscode.DiagnosticSeverity.Information: + return "info"; + default: + return "hint"; + } +} + +/** + * Severity counts for the active file plus a bounded, errors-first sample of + * the actual diagnostic messages, so the reasoning agent can act on "fix the + * error" without a separate CODA pull. `omitted` counts anything past the cap. + */ +function gatherDiagnostics(uri: vscode.Uri): EditorContext["diagnostics"] { + const counts = { errors: 0, warnings: 0, infos: 0, hints: 0 }; + const all = vscode.languages.getDiagnostics(uri); + for (const d of all) { + switch (d.severity) { + case vscode.DiagnosticSeverity.Error: + counts.errors++; + break; + case vscode.DiagnosticSeverity.Warning: + counts.warnings++; + break; + case vscode.DiagnosticSeverity.Information: + counts.infos++; + break; + case vscode.DiagnosticSeverity.Hint: + counts.hints++; + break; + } + } + // Errors (severity 0) first, then by line, so the cap keeps the most + // actionable messages. + const items: DiagnosticItem[] = [...all] + .sort( + (a, b) => + a.severity - b.severity || + a.range.start.line - b.range.start.line, + ) + .slice(0, MAX_DIAGNOSTIC_ITEMS) + .map((d) => ({ + severity: severityName(d.severity), + line: d.range.start.line, + message: clip(d.message, MAX_DIAGNOSTIC_MESSAGE_CHARS), + ...(d.source ? { source: d.source } : {}), + })); + const omitted = all.length - items.length; + return { + ...counts, + ...(items.length ? { items } : {}), + ...(omitted > 0 ? { omitted } : {}), + }; +} + +/** + * The open file editors (tabs backed by a text/diff document), as a bounded + * list plus the total tab count, so the model can resolve references like "the + * other file" without a pull. Non-file tabs (terminals, previews) are counted + * but not listed. + */ +function gatherOpenEditors(): Pick< + EditorContext, + "openEditorCount" | "openEditors" | "openEditorsOmitted" +> { + const tabs = vscode.window.tabGroups.all.flatMap((group) => group.tabs); + const files: OpenEditorInfo[] = []; + for (const tab of tabs) { + const input = tab.input; + const uri = + input instanceof vscode.TabInputText + ? input.uri + : input instanceof vscode.TabInputTextDiff + ? input.modified + : undefined; + if (uri) { + files.push({ + path: vscode.workspace.asRelativePath(uri, false), + active: tab.isActive, + dirty: tab.isDirty, + }); + } + } + return { + openEditorCount: tabs.length, + ...(files.length + ? { openEditors: files.slice(0, MAX_OPEN_EDITORS) } + : {}), + ...(files.length > MAX_OPEN_EDITORS + ? { openEditorsOmitted: files.length - MAX_OPEN_EDITORS } + : {}), + }; +} diff --git a/ts/packages/vscode-shell/src/chatViewProvider.ts b/ts/packages/vscode-shell/src/chatViewProvider.ts index 22bdf6027d..10fccd6b0b 100644 --- a/ts/packages/vscode-shell/src/chatViewProvider.ts +++ b/ts/packages/vscode-shell/src/chatViewProvider.ts @@ -134,6 +134,7 @@ export class ChatViewProvider implements vscode.WebviewViewProvider { style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}'; img-src ${webview.cspSource} data:; + media-src blob: mediastream: data:; font-src ${webview.cspSource};"> diff --git a/ts/packages/vscode-shell/src/webview/cameraView.ts b/ts/packages/vscode-shell/src/webview/cameraView.ts new file mode 100644 index 0000000000..0b4fc3219c --- /dev/null +++ b/ts/packages/vscode-shell/src/webview/cameraView.ts @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * In-webview camera capture overlay for the VS Code shell, mirroring the + * Electron shell's CameraView (packages/shell/src/renderer/src/cameraView.ts). + * getUserMedia works inside the VS Code webview - the Azure speech mic path + * relies on it too - so the same live-preview + snapshot flow applies here. + * + * The overlay is a full-screen scrim with a