@xingular/harness 0.1.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -1
- package/LICENSE +202 -0
- package/NOTICE.md +22 -0
- package/README.md +239 -86
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +17853 -13114
- package/dist/sdk/accounts/accounts.d.ts +165 -0
- package/dist/sdk/accounts/accounts.js +730 -0
- package/dist/sdk/accounts/choices.d.ts +23 -0
- package/dist/sdk/accounts/choices.js +62 -0
- package/dist/sdk/accounts/env.d.ts +78 -0
- package/dist/sdk/accounts/env.js +294 -0
- package/dist/sdk/accounts/secrets.d.ts +16 -0
- package/dist/sdk/accounts/secrets.js +40 -0
- package/dist/sdk/accounts/store.d.ts +114 -0
- package/dist/sdk/accounts/store.js +255 -0
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +34 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/profiles.d.ts +80 -0
- package/dist/sdk/agents/profiles.js +221 -0
- package/dist/sdk/annotations/bridge.d.ts +42 -0
- package/dist/sdk/annotations/bridge.js +264 -0
- package/dist/sdk/annotations/capture.d.ts +116 -0
- package/dist/sdk/annotations/capture.js +308 -0
- package/dist/sdk/annotations/delivery.d.ts +72 -0
- package/dist/sdk/annotations/delivery.js +130 -0
- package/dist/sdk/annotations/inspect.d.ts +10 -0
- package/dist/sdk/annotations/inspect.js +33 -0
- package/dist/sdk/annotations/isolated.d.ts +6 -0
- package/dist/sdk/annotations/isolated.js +51 -0
- package/dist/sdk/annotations/pageScript.d.ts +28 -0
- package/dist/sdk/annotations/pageScript.js +570 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +144 -0
- package/dist/sdk/annotations/queue.d.ts +4 -0
- package/dist/sdk/annotations/queue.js +40 -0
- package/dist/sdk/annotations/serverMethods.d.ts +3 -0
- package/dist/sdk/annotations/serverMethods.js +62 -0
- package/dist/sdk/annotations/store.d.ts +160 -0
- package/dist/sdk/annotations/store.js +386 -0
- package/dist/sdk/browser/config.d.ts +16 -0
- package/dist/sdk/browser/config.js +22 -0
- package/dist/sdk/browser/diagnostics.d.ts +4 -0
- package/dist/sdk/browser/diagnostics.js +38 -0
- package/dist/sdk/browser/index.d.ts +28 -0
- package/dist/sdk/browser/index.js +72 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +85 -0
- package/dist/sdk/browser/session.js +228 -0
- package/dist/sdk/browser/tools.d.ts +8 -0
- package/dist/sdk/browser/tools.js +329 -0
- package/dist/sdk/capabilities/api.d.ts +22 -0
- package/dist/sdk/capabilities/api.js +54 -0
- package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
- package/dist/sdk/capabilities/runSnapshot.js +47 -0
- package/dist/sdk/capabilities/types.d.ts +29 -0
- package/dist/sdk/capabilities/types.js +1 -0
- package/dist/sdk/cli.d.ts +2 -0
- package/dist/sdk/cli.js +150 -0
- package/dist/sdk/core/bus.d.ts +23 -0
- package/dist/sdk/core/bus.js +76 -0
- package/dist/sdk/core/caller.d.ts +22 -0
- package/dist/sdk/core/caller.js +16 -0
- package/dist/sdk/core/changes.d.ts +15 -0
- package/dist/sdk/core/changes.js +15 -0
- package/dist/sdk/core/effort.d.ts +2 -0
- package/dist/sdk/core/effort.js +1 -0
- package/dist/sdk/core/env.d.ts +18 -0
- package/dist/sdk/core/env.js +8 -0
- package/dist/sdk/core/errors.d.ts +15 -0
- package/dist/sdk/core/errors.js +24 -0
- package/dist/sdk/core/frontmatter.d.ts +12 -0
- package/dist/sdk/core/frontmatter.js +50 -0
- package/dist/sdk/core/fs.d.ts +18 -0
- package/dist/sdk/core/fs.js +106 -0
- package/dist/sdk/core/index.d.ts +24 -0
- package/dist/sdk/core/index.js +187 -0
- package/dist/sdk/core/path.d.ts +7 -0
- package/dist/sdk/core/path.js +79 -0
- package/dist/sdk/core/projects.d.ts +18 -0
- package/dist/sdk/core/projects.js +67 -0
- package/dist/sdk/core/secrets.d.ts +2 -0
- package/dist/sdk/core/secrets.js +5 -0
- package/dist/sdk/core/text.d.ts +7 -0
- package/dist/sdk/core/text.js +40 -0
- package/dist/sdk/doctor/checks.d.ts +5 -0
- package/dist/sdk/doctor/checks.js +208 -0
- package/dist/sdk/doctor/probe.d.ts +4 -0
- package/dist/sdk/doctor/probe.js +92 -0
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/doctor/service.d.ts +23 -0
- package/dist/sdk/doctor/service.js +144 -0
- package/dist/sdk/doctor/types.d.ts +141 -0
- package/dist/sdk/doctor/types.js +29 -0
- package/dist/sdk/harness.d.ts +148 -0
- package/dist/sdk/harness.js +338 -0
- package/dist/sdk/history/lessonPrompt.d.ts +2 -0
- package/dist/sdk/history/lessonPrompt.js +15 -0
- package/dist/sdk/history/lessons.d.ts +52 -0
- package/dist/sdk/history/lessons.js +173 -0
- package/dist/sdk/history/recall.d.ts +68 -0
- package/dist/sdk/history/recall.js +199 -0
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/history/trajectory.d.ts +7 -0
- package/dist/sdk/history/trajectory.js +216 -0
- package/dist/sdk/hub/catalog.json +123 -0
- package/dist/sdk/hub/cli.d.ts +7 -0
- package/dist/sdk/hub/cli.js +81 -0
- package/dist/sdk/hub/hub.d.ts +74 -0
- package/dist/sdk/hub/hub.js +474 -0
- package/dist/sdk/hub/schema.d.ts +280 -0
- package/dist/sdk/hub/schema.js +78 -0
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +73 -0
- package/dist/sdk/index.js +55 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +676 -0
- package/dist/sdk/mcp/manager.d.ts +26 -0
- package/dist/sdk/mcp/manager.js +124 -0
- package/dist/sdk/mcp/oauth.d.ts +35 -0
- package/dist/sdk/mcp/oauth.js +171 -0
- package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
- package/dist/sdk/mcp/scopeMigration.js +198 -0
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/mcp/service.d.ts +160 -0
- package/dist/sdk/mcp/service.js +510 -0
- package/dist/sdk/mcp/types.d.ts +131 -0
- package/dist/sdk/mcp/types.js +49 -0
- package/dist/sdk/pi/runtime.d.ts +13 -0
- package/dist/sdk/pi/runtime.js +31 -0
- package/dist/sdk/plugins/api.d.ts +66 -0
- package/dist/sdk/plugins/api.js +155 -0
- package/dist/sdk/plugins/attachments.d.ts +46 -0
- package/dist/sdk/plugins/attachments.js +111 -0
- package/dist/sdk/plugins/builtin.d.ts +7 -0
- package/dist/sdk/plugins/builtin.js +163 -0
- package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
- package/dist/sdk/plugins/builtinRegistry.js +49 -0
- package/dist/sdk/plugins/catalogue.d.ts +29 -0
- package/dist/sdk/plugins/catalogue.js +196 -0
- package/dist/sdk/plugins/cli.d.ts +13 -0
- package/dist/sdk/plugins/cli.js +188 -0
- package/dist/sdk/plugins/discover.d.ts +42 -0
- package/dist/sdk/plugins/discover.js +115 -0
- package/dist/sdk/plugins/firstParty.d.ts +50 -0
- package/dist/sdk/plugins/firstParty.js +113 -0
- package/dist/sdk/plugins/hooks.d.ts +94 -0
- package/dist/sdk/plugins/hooks.js +162 -0
- package/dist/sdk/plugins/identity.d.ts +19 -0
- package/dist/sdk/plugins/identity.js +48 -0
- package/dist/sdk/plugins/install.d.ts +20 -0
- package/dist/sdk/plugins/install.js +110 -0
- package/dist/sdk/plugins/inventory.d.ts +22 -0
- package/dist/sdk/plugins/inventory.js +110 -0
- package/dist/sdk/plugins/manifest.d.ts +13 -0
- package/dist/sdk/plugins/manifest.js +56 -0
- package/dist/sdk/plugins/plugins.d.ts +104 -0
- package/dist/sdk/plugins/plugins.js +164 -0
- package/dist/sdk/plugins/runtime.d.ts +9 -0
- package/dist/sdk/plugins/runtime.js +95 -0
- package/dist/sdk/plugins/trust.d.ts +44 -0
- package/dist/sdk/plugins/trust.js +120 -0
- package/dist/sdk/plugins/types.d.ts +159 -0
- package/dist/sdk/plugins/types.js +7 -0
- package/dist/sdk/prompt/compile.d.ts +177 -0
- package/dist/sdk/prompt/compile.js +413 -0
- package/dist/sdk/refine/cli.d.ts +4 -0
- package/dist/sdk/refine/cli.js +186 -0
- package/dist/sdk/refine/demo.d.ts +21 -0
- package/dist/sdk/refine/demo.js +114 -0
- package/dist/sdk/refine/evidence.d.ts +55 -0
- package/dist/sdk/refine/evidence.js +330 -0
- package/dist/sdk/refine/host.d.ts +51 -0
- package/dist/sdk/refine/host.js +76 -0
- package/dist/sdk/refine/ledger.d.ts +48 -0
- package/dist/sdk/refine/ledger.js +76 -0
- package/dist/sdk/refine/library.d.ts +55 -0
- package/dist/sdk/refine/library.js +177 -0
- package/dist/sdk/refine/pi.d.ts +72 -0
- package/dist/sdk/refine/pi.js +248 -0
- package/dist/sdk/refine/prompts.d.ts +45 -0
- package/dist/sdk/refine/prompts.js +390 -0
- package/dist/sdk/refine/recursion.d.ts +113 -0
- package/dist/sdk/refine/recursion.js +258 -0
- package/dist/sdk/refine/runner.d.ts +81 -0
- package/dist/sdk/refine/runner.js +150 -0
- package/dist/sdk/refine/selection.d.ts +57 -0
- package/dist/sdk/refine/selection.js +251 -0
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/refine/service.d.ts +84 -0
- package/dist/sdk/refine/service.js +165 -0
- package/dist/sdk/refine/suite.d.ts +67 -0
- package/dist/sdk/refine/suite.js +174 -0
- package/dist/sdk/refine/trajectory.d.ts +9 -0
- package/dist/sdk/refine/trajectory.js +68 -0
- package/dist/sdk/refine/types.d.ts +77 -0
- package/dist/sdk/refine/types.js +10 -0
- package/dist/sdk/server/methods.d.ts +3 -0
- package/dist/sdk/server/methods.js +293 -0
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/server/remote.d.ts +19 -0
- package/dist/sdk/server/remote.js +84 -0
- package/dist/sdk/server/rpc.d.ts +20 -0
- package/dist/sdk/server/rpc.js +102 -0
- package/dist/sdk/server/serve.d.ts +10 -0
- package/dist/sdk/server/serve.js +18 -0
- package/dist/sdk/sessions/approvals.d.ts +47 -0
- package/dist/sdk/sessions/approvals.js +119 -0
- package/dist/sdk/sessions/continuation.d.ts +48 -0
- package/dist/sdk/sessions/continuation.js +85 -0
- package/dist/sdk/sessions/contracts.d.ts +551 -0
- package/dist/sdk/sessions/contracts.js +338 -0
- package/dist/sdk/sessions/extensionUi.d.ts +19 -0
- package/dist/sdk/sessions/extensionUi.js +64 -0
- package/dist/sdk/sessions/history.d.ts +16 -0
- package/dist/sdk/sessions/history.js +68 -0
- package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
- package/dist/sdk/sessions/lineageJoin.js +61 -0
- package/dist/sdk/sessions/planMode.d.ts +39 -0
- package/dist/sdk/sessions/planMode.js +93 -0
- package/dist/sdk/sessions/policy.d.ts +43 -0
- package/dist/sdk/sessions/policy.js +1 -0
- package/dist/sdk/sessions/pricing.d.ts +34 -0
- package/dist/sdk/sessions/pricing.js +40 -0
- package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
- package/dist/sdk/sessions/reasoningSummaries.js +61 -0
- package/dist/sdk/sessions/registry.d.ts +103 -0
- package/dist/sdk/sessions/registry.js +386 -0
- package/dist/sdk/sessions/runIndex.d.ts +21 -0
- package/dist/sdk/sessions/runIndex.js +65 -0
- package/dist/sdk/sessions/runStore.d.ts +66 -0
- package/dist/sdk/sessions/runStore.js +337 -0
- package/dist/sdk/sessions/runner.d.ts +213 -0
- package/dist/sdk/sessions/runner.js +589 -0
- package/dist/sdk/sessions/sessions.d.ts +152 -0
- package/dist/sdk/sessions/sessions.js +461 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
- package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
- package/dist/sdk/sessions/sideQuestions.js +290 -0
- package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/subagentServerMethods.js +41 -0
- package/dist/sdk/sessions/subagentService.d.ts +52 -0
- package/dist/sdk/sessions/subagentService.js +265 -0
- package/dist/sdk/sessions/subagents.d.ts +58 -0
- package/dist/sdk/sessions/subagents.js +87 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -0
- package/dist/sdk/terminal/browser.d.ts +62 -0
- package/dist/sdk/terminal/browser.js +41 -0
- package/dist/sdk/terminal/browserControl.d.ts +29 -0
- package/dist/sdk/terminal/browserControl.js +186 -0
- package/dist/sdk/terminal/cli.d.ts +20 -0
- package/dist/sdk/terminal/cli.js +121 -0
- package/dist/sdk/terminal/client.d.ts +15 -0
- package/dist/sdk/terminal/client.js +126 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +78 -0
- package/dist/sdk/terminal/controller.js +383 -0
- package/dist/sdk/terminal/hubMenu.d.ts +10 -0
- package/dist/sdk/terminal/hubMenu.js +34 -0
- package/dist/sdk/terminal/launch.d.ts +5 -0
- package/dist/sdk/terminal/launch.js +27 -0
- package/dist/sdk/terminal/logs.d.ts +14 -0
- package/dist/sdk/terminal/logs.js +59 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +377 -0
- package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
- package/dist/sdk/terminal/pluginMenu.js +101 -0
- package/dist/sdk/terminal/socket.d.ts +73 -0
- package/dist/sdk/terminal/socket.js +287 -0
- package/dist/sdk/terminal/standalone.d.ts +20 -0
- package/dist/sdk/terminal/standalone.js +186 -0
- package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
- package/dist/sdk/terminal/subagentMonitor.js +121 -0
- package/dist/sdk/terminal/terminals.d.ts +212 -0
- package/dist/sdk/terminal/terminals.js +1108 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -0
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
- package/dist/sdk/terminal/tui/chrome.js +169 -0
- package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
- package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +11 -0
- package/dist/sdk/terminal/tui/commandPages.js +111 -0
- package/dist/sdk/terminal/tui/commands.d.ts +46 -0
- package/dist/sdk/terminal/tui/commands.js +399 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +52 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +80 -0
- package/dist/sdk/terminal/tui/dialogs.js +370 -0
- package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
- package/dist/sdk/terminal/tui/keybindings.js +49 -0
- package/dist/sdk/terminal/tui/list.d.ts +73 -0
- package/dist/sdk/terminal/tui/list.js +196 -0
- package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
- package/dist/sdk/terminal/tui/modeChoices.js +70 -0
- package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
- package/dist/sdk/terminal/tui/modelPicker.js +150 -0
- package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
- package/dist/sdk/terminal/tui/mouse.js +3 -0
- package/dist/sdk/terminal/tui/page.d.ts +17 -0
- package/dist/sdk/terminal/tui/page.js +1 -0
- package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +156 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +76 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +409 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
- package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +233 -0
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
- package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
- package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
- package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +63 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +103 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +65 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +450 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
- package/dist/sdk/terminal/tui/piAdapter.js +48 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
- package/dist/sdk/terminal/tui/planMode.js +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
- package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +21 -0
- package/dist/sdk/terminal/tui/questions.js +42 -0
- package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
- package/dist/sdk/terminal/tui/runtime.js +443 -0
- package/dist/sdk/terminal/tui/search.d.ts +22 -0
- package/dist/sdk/terminal/tui/search.js +60 -0
- package/dist/sdk/terminal/tui/selection.d.ts +16 -0
- package/dist/sdk/terminal/tui/selection.js +16 -0
- package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
- package/dist/sdk/terminal/tui/sessionUi.js +30 -0
- package/dist/sdk/terminal/tui/settingsModel.d.ts +14 -0
- package/dist/sdk/terminal/tui/settingsModel.js +114 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +230 -0
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
- package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
- package/dist/sdk/terminal/tui/status.d.ts +18 -0
- package/dist/sdk/terminal/tui/status.js +67 -0
- package/dist/sdk/terminal/tui/theme.d.ts +45 -0
- package/dist/sdk/terminal/tui/theme.js +176 -0
- package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
- package/dist/sdk/terminal/tui/treeModel.js +483 -0
- package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
- package/dist/sdk/terminal/tui/treePicker.js +251 -0
- package/dist/sdk/terminal/tui/ui.d.ts +49 -0
- package/dist/sdk/terminal/tui/ui.js +149 -0
- package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
- package/dist/sdk/terminal/tui/workingStatus.js +229 -0
- package/dist/sdk/tools/askUser.d.ts +6 -0
- package/dist/sdk/tools/askUser.js +92 -0
- package/dist/sdk/tools/checklist.d.ts +9 -0
- package/dist/sdk/tools/checklist.js +54 -0
- package/dist/sdk/tools/definitions.d.ts +30 -0
- package/dist/sdk/tools/definitions.js +117 -0
- package/dist/sdk/tools/dispatch.d.ts +16 -0
- package/dist/sdk/tools/dispatch.js +67 -0
- package/dist/sdk/tools/harnessTools.d.ts +8 -0
- package/dist/sdk/tools/harnessTools.js +20 -0
- package/dist/sdk/tools/sessionName.d.ts +7 -0
- package/dist/sdk/tools/sessionName.js +31 -0
- package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
- package/dist/sdk/tools/spawnSubagent.js +58 -0
- package/dist/sdk/tools/types.d.ts +48 -0
- package/dist/sdk/tools/types.js +1 -0
- package/dist/sdk/variant/components.d.ts +34 -0
- package/dist/sdk/variant/components.js +230 -0
- package/dist/sdk/variant/materialise.d.ts +66 -0
- package/dist/sdk/variant/materialise.js +81 -0
- package/dist/sdk/variant/migrate.d.ts +4 -0
- package/dist/sdk/variant/migrate.js +41 -0
- package/dist/sdk/variant/seed.d.ts +20 -0
- package/dist/sdk/variant/seed.js +116 -0
- package/dist/sdk/variant/store.d.ts +82 -0
- package/dist/sdk/variant/store.js +151 -0
- package/dist/sdk/variant/surface.d.ts +56 -0
- package/dist/sdk/variant/surface.js +81 -0
- package/dist/sdk/variant/trust.d.ts +27 -0
- package/dist/sdk/variant/trust.js +64 -0
- package/dist/sdk/variant/variant.d.ts +71 -0
- package/dist/sdk/variant/variant.js +331 -0
- package/dist/sdk/web/markdown.d.ts +10 -0
- package/dist/sdk/web/markdown.js +66 -0
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/safeRequest.d.ts +34 -0
- package/dist/sdk/web/safeRequest.js +175 -0
- package/dist/sdk/web/search.d.ts +53 -0
- package/dist/sdk/web/search.js +168 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -0
- package/dist/sdk/web/text.d.ts +2 -0
- package/dist/sdk/web/text.js +7 -0
- package/dist/sdk/web/tools.d.ts +22 -0
- package/dist/sdk/web/tools.js +195 -0
- package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
- package/dist/sdk/wiki/notes.d.ts +68 -0
- package/dist/sdk/wiki/notes.js +297 -0
- package/dist/sdk/wiki/serverMethods.d.ts +3 -0
- package/dist/sdk/wiki/serverMethods.js +26 -0
- package/dist/sdk/worktrees/cli.d.ts +11 -0
- package/dist/sdk/worktrees/cli.js +146 -0
- package/dist/sdk/worktrees/copy.d.ts +9 -0
- package/dist/sdk/worktrees/copy.js +111 -0
- package/dist/sdk/worktrees/git.d.ts +16 -0
- package/dist/sdk/worktrees/git.js +81 -0
- package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
- package/dist/sdk/worktrees/serverMethods.js +30 -0
- package/dist/sdk/worktrees/service.d.ts +73 -0
- package/dist/sdk/worktrees/service.js +483 -0
- package/dist/sdk/worktrees/setup.d.ts +23 -0
- package/dist/sdk/worktrees/setup.js +65 -0
- package/dist/sdk/worktrees/types.d.ts +91 -0
- package/dist/sdk/worktrees/types.js +1 -0
- package/dist/sdk/world/actions.d.ts +23 -0
- package/dist/sdk/world/actions.js +31 -0
- package/dist/sdk/world/cli.d.ts +12 -0
- package/dist/sdk/world/cli.js +166 -0
- package/dist/sdk/world/images.d.ts +20 -0
- package/dist/sdk/world/images.js +48 -0
- package/dist/sdk/world/untrusted.d.ts +12 -0
- package/dist/sdk/world/untrusted.js +20 -0
- package/dist/sdk/world/world.d.ts +67 -0
- package/dist/sdk/world/world.js +79 -0
- package/dist/terminal-opentui.js +20778 -0
- package/dist/terminal-pi.js +794 -518
- package/docs/README.md +42 -0
- package/docs/architecture/00-overview.md +171 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +203 -0
- package/docs/architecture/03-variants.md +103 -0
- package/docs/architecture/04-prompt.md +122 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +129 -0
- package/docs/architecture/07-tools-and-gate.md +103 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +98 -0
- package/docs/architecture/10-server-protocol.md +193 -0
- package/docs/architecture/11-terminal.md +261 -0
- package/docs/architecture/12-storage.md +75 -0
- package/docs/architecture/13-world-tools.md +45 -0
- package/docs/architecture/14-surfaces-and-parity.md +89 -0
- package/docs/architecture/README.md +125 -0
- package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
- package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
- package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
- package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
- package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +70 -0
- package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
- package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
- package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
- package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
- package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
- package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
- package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
- package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
- package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
- package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
- package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
- package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
- package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
- package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
- package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
- package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
- package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
- package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
- package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
- package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
- package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
- package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
- package/docs/architecture/proposals/README.md +101 -0
- package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
- package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
- package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
- package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
- package/docs/decisions/ADR-0007-reporting-section.md +33 -0
- package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
- package/docs/decisions/ADR-0009-xharness-command.md +39 -0
- package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
- package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
- package/docs/decisions/ADR-0012-native-session-names.md +34 -0
- package/docs/decisions/ADR-0013-doctor.md +27 -0
- package/docs/decisions/ADR-0014-public-release-baseline.md +22 -0
- package/docs/decisions/ADR-0015-resource-browser.md +28 -0
- package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
- package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
- package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
- package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
- package/docs/decisions/ADR-0020-page-annotations.md +37 -0
- package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
- package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
- package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
- package/docs/decisions/ADR-0024-xagent-command.md +40 -0
- package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
- package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
- package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
- package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
- package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
- package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
- package/docs/doctor.md +80 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
- package/docs/features/README.md +46 -0
- package/docs/features/automations/README.md +41 -0
- package/docs/features/cores/README.md +39 -0
- package/docs/features/google-harness2/README.md +52 -0
- package/docs/features/integrations/README.md +40 -0
- package/docs/features/interfaces/README.md +29 -0
- package/docs/features/memory/README.md +28 -0
- package/docs/future-work.md +48 -0
- package/docs/parity.md +78 -0
- package/docs/releases/1.0.0.md +40 -0
- package/docs/releases/4.0.0.md +46 -0
- package/docs/releases/README.md +41 -0
- package/docs/study/00-index.md +83 -0
- package/docs/study/arms/00-matrix.md +20 -0
- package/docs/study/arms/01-system-prompt.md +211 -0
- package/docs/study/arms/02-tools.md +79 -0
- package/docs/study/arms/03-subagents.md +90 -0
- package/docs/study/arms/04-skills.md +75 -0
- package/docs/study/arms/05-context-management.md +90 -0
- package/docs/study/arms/06-memory.md +90 -0
- package/docs/study/arms/07-governance.md +71 -0
- package/docs/study/arms/08-execution.md +69 -0
- package/docs/study/arms/09-observability.md +60 -0
- package/docs/study/arms/10-refinement.md +110 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/roadmap.md +72 -0
- package/docs/study/sources/claude-code.md +44 -0
- package/docs/study/sources/deepseek-harness.md +102 -0
- package/docs/study/sources/harness2.md +50 -0
- package/docs/study/sources/kimi-cli.md +42 -0
- package/docs/study/sources/omp.md +93 -0
- package/docs/study/sources/opencode.md +43 -0
- package/docs/study/sources/openhands.md +46 -0
- package/docs/study/sources/smolagents.md +40 -0
- package/docs/study/sources/swe-agent.md +34 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +707 -58
- package/package.json +43 -13
- package/docs/evidence/terminal-2026-10-17/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/verification.json +0 -130
- package/docs/evidence/terminal-2026-10-17-followup/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/README.md +0 -27
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command.json +0 -18
- package/docs/evidence/terminal-2026-10-17-followup/global-install/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-final-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-scenario.json +0 -60
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/README.md +0 -24
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/complete-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/verification.json +0 -57
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24-early.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-scenario.json +0 -58
- package/docs/evidence/terminal-2026-10-17-followup/omp-supplemental-static.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/rea-artifacts.json +0 -320
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.json +0 -30
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.md +0 -133
- package/docs/evidence/terminal-2026-10-17-followup/reference-source-observations.json +0 -20
- package/docs/evidence/terminal-2026-10-17-followup/verification.json +0 -207
- package/src/accounts/accounts.test.ts +0 -333
- package/src/accounts/accounts.ts +0 -658
- package/src/accounts/env.test.ts +0 -95
- package/src/accounts/env.ts +0 -338
- package/src/accounts/secrets.ts +0 -50
- package/src/accounts/store.ts +0 -338
- package/src/capabilities/api.ts +0 -75
- package/src/capabilities/runSnapshot.ts +0 -59
- package/src/capabilities/types.ts +0 -34
- package/src/cli.ts +0 -118
- package/src/core/bus.ts +0 -34
- package/src/core/changes.ts +0 -33
- package/src/core/env.ts +0 -27
- package/src/core/errors.ts +0 -32
- package/src/core/frontmatter.ts +0 -47
- package/src/core/fs.test.ts +0 -38
- package/src/core/fs.ts +0 -92
- package/src/core/index.ts +0 -193
- package/src/core/path.test.ts +0 -14
- package/src/core/path.ts +0 -79
- package/src/core/projects.ts +0 -90
- package/src/core/text.ts +0 -39
- package/src/harness.ts +0 -312
- package/src/hub/catalog.json +0 -123
- package/src/hub/cli.ts +0 -59
- package/src/hub/hub.test.ts +0 -205
- package/src/hub/hub.ts +0 -256
- package/src/hub/schema.ts +0 -81
- package/src/index.ts +0 -75
- package/src/mcp/connections.ts +0 -614
- package/src/mcp/manager.ts +0 -130
- package/src/mcp/oauth.ts +0 -195
- package/src/mcp/scopeMigration.test.ts +0 -79
- package/src/mcp/scopeMigration.ts +0 -204
- package/src/mcp/service.test.ts +0 -376
- package/src/mcp/service.ts +0 -549
- package/src/mcp/types.ts +0 -108
- package/src/memory/api.test.ts +0 -82
- package/src/memory/api.ts +0 -303
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -408
- package/src/memory/caller.ts +0 -69
- package/src/memory/curation.test.ts +0 -163
- package/src/memory/curation.ts +0 -64
- package/src/memory/dispatch.ts +0 -59
- package/src/memory/facts.test.ts +0 -41
- package/src/memory/facts.ts +0 -248
- package/src/memory/graph.ts +0 -103
- package/src/memory/health.test.ts +0 -130
- package/src/memory/health.ts +0 -74
- package/src/memory/maintainer.ts +0 -272
- package/src/memory/maintenance.test.ts +0 -176
- package/src/memory/maintenance.ts +0 -518
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -150
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -584
- package/src/memory/recall.test.ts +0 -115
- package/src/memory/recall.ts +0 -278
- package/src/memory/references.ts +0 -77
- package/src/memory/runContext.ts +0 -93
- package/src/memory/secrets.ts +0 -6
- package/src/memory/store.test.ts +0 -66
- package/src/memory/store.ts +0 -385
- package/src/memory/wiki.test.ts +0 -47
- package/src/memory/wiki.ts +0 -422
- package/src/memory/wikiIndex.test.ts +0 -88
- package/src/memory/wikiIndex.ts +0 -116
- package/src/plugins/api.ts +0 -181
- package/src/plugins/attachments.test.ts +0 -84
- package/src/plugins/attachments.ts +0 -141
- package/src/plugins/builtin.ts +0 -82
- package/src/plugins/catalogue.test.ts +0 -254
- package/src/plugins/catalogue.ts +0 -251
- package/src/plugins/cli.ts +0 -184
- package/src/plugins/discover.ts +0 -151
- package/src/plugins/firstParty.test.ts +0 -143
- package/src/plugins/firstParty.ts +0 -129
- package/src/plugins/hooks.test.ts +0 -250
- package/src/plugins/hooks.ts +0 -239
- package/src/plugins/identity.ts +0 -37
- package/src/plugins/install.test.ts +0 -107
- package/src/plugins/install.ts +0 -106
- package/src/plugins/inventory.ts +0 -123
- package/src/plugins/manifest.ts +0 -76
- package/src/plugins/plugins.test.ts +0 -205
- package/src/plugins/plugins.ts +0 -189
- package/src/plugins/proposal.ts +0 -121
- package/src/plugins/runtime.test.ts +0 -40
- package/src/plugins/runtime.ts +0 -82
- package/src/plugins/trust.ts +0 -142
- package/src/plugins/types.ts +0 -149
- package/src/prompt/compile.test.ts +0 -261
- package/src/prompt/compile.ts +0 -732
- package/src/server/methods.ts +0 -349
- package/src/server/remote.ts +0 -77
- package/src/server/rpc.ts +0 -116
- package/src/server/serve.ts +0 -28
- package/src/server/server.test.ts +0 -174
- package/src/sessions/approvals.ts +0 -118
- package/src/sessions/contracts.ts +0 -336
- package/src/sessions/conversation.test.ts +0 -177
- package/src/sessions/extensionSession.test.ts +0 -74
- package/src/sessions/extensionUi.test.ts +0 -65
- package/src/sessions/extensionUi.ts +0 -102
- package/src/sessions/history.ts +0 -51
- package/src/sessions/lineageJoin.ts +0 -75
- package/src/sessions/policy.ts +0 -36
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/questionBatches.test.ts +0 -110
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -381
- package/src/sessions/runIndex.ts +0 -99
- package/src/sessions/runStore.test.ts +0 -152
- package/src/sessions/runStore.ts +0 -332
- package/src/sessions/runner.ts +0 -604
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -549
- package/src/sessions/sessions.ts +0 -339
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -413
- package/src/terminal/accountsMenu.test.ts +0 -225
- package/src/terminal/accountsMenu.ts +0 -382
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/control.test.ts +0 -23
- package/src/terminal/control.ts +0 -23
- package/src/terminal/controller.ts +0 -291
- package/src/terminal/hubMenu.ts +0 -24
- package/src/terminal/launch.test.ts +0 -25
- package/src/terminal/launch.ts +0 -48
- package/src/terminal/piExtension.lifecycle.test.ts +0 -55
- package/src/terminal/piExtension.ts +0 -259
- package/src/terminal/pluginControl.test.ts +0 -146
- package/src/terminal/pluginMenu.ts +0 -112
- package/src/terminal/socket.test.ts +0 -71
- package/src/terminal/socket.ts +0 -232
- package/src/terminal/standalone.test.ts +0 -167
- package/src/terminal/standalone.ts +0 -153
- package/src/terminal/terminal.test.ts +0 -399
- package/src/terminal/terminals.ts +0 -553
- package/src/terminal/tui/application.test.ts +0 -114
- package/src/terminal/tui/application.ts +0 -491
- package/src/terminal/tui/commands.test.ts +0 -218
- package/src/terminal/tui/commands.ts +0 -361
- package/src/terminal/tui/dialogs.ts +0 -301
- package/src/terminal/tui/editor.test.ts +0 -84
- package/src/terminal/tui/editor.ts +0 -79
- package/src/terminal/tui/keybindings.test.ts +0 -22
- package/src/terminal/tui/keybindings.ts +0 -50
- package/src/terminal/tui/piAdapter.ts +0 -62
- package/src/terminal/tui/presentation.test.ts +0 -79
- package/src/terminal/tui/questions.test.ts +0 -41
- package/src/terminal/tui/questions.ts +0 -47
- package/src/terminal/tui/runtime.socket.test.ts +0 -104
- package/src/terminal/tui/runtime.test.ts +0 -215
- package/src/terminal/tui/runtime.ts +0 -388
- package/src/terminal/tui/sessionUi.ts +0 -24
- package/src/terminal/tui/settings.test.ts +0 -150
- package/src/terminal/tui/settings.ts +0 -188
- package/src/terminal/tui/status.test.ts +0 -70
- package/src/terminal/tui/status.ts +0 -109
- package/src/terminal/tui/theme.ts +0 -174
- package/src/terminal/tui/tools.test.ts +0 -145
- package/src/terminal/tui/tools.ts +0 -142
- package/src/terminal/tui/transcript.test.ts +0 -25
- package/src/terminal/tui/transcript.ts +0 -124
- package/src/terminal/tui/ui.test.ts +0 -313
- package/src/terminal/tui/ui.ts +0 -166
- package/src/terminal/tui/welcome.ts +0 -102
- package/src/tools/askUser.test.ts +0 -69
- package/src/tools/askUser.ts +0 -59
- package/src/tools/definitions.ts +0 -187
- package/src/tools/maintenance.ts +0 -24
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- package/src/tools/registryWrite.test.ts +0 -101
- package/src/tools/registryWrite.ts +0 -182
- package/src/tools/spawnSubagent.ts +0 -92
- package/src/tools/types.ts +0 -39
- package/src/web/safeRequest.ts +0 -171
- package/src/web/text.ts +0 -53
- package/src/web/tools.ts +0 -134
- package/src/web/web.test.ts +0 -141
|
@@ -0,0 +1,1108 @@
|
|
|
1
|
+
// A host owns the PTY and process supervision; Harness owns the terminal application inside it.
|
|
2
|
+
// This service prepares the isolated account, compiled context, trusted resource selection and
|
|
3
|
+
// socket bridge. The default launch runs our frontend with Pi's persistent SDK runtime.
|
|
4
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { Type } from 'typebox';
|
|
8
|
+
import { freezeRun } from "../capabilities/runSnapshot.js";
|
|
9
|
+
import { SPAWN_SUBAGENT, WAIT_SUBAGENTS } from "../sessions/subagents.js";
|
|
10
|
+
import { subagentTools } from "../tools/spawnSubagent.js";
|
|
11
|
+
import { configDir, launchEnv } from "../accounts/env.js";
|
|
12
|
+
import { readAudit } from "../accounts/store.js";
|
|
13
|
+
import { log } from "../core/bus.js";
|
|
14
|
+
import { HarnessError, invalid } from "../core/errors.js";
|
|
15
|
+
import { newId, now } from "../core/fs.js";
|
|
16
|
+
import { prependSearchPath, searchPath } from "../core/path.js";
|
|
17
|
+
import { dispatch } from "../tools/dispatch.js";
|
|
18
|
+
import { materialise } from "../variant/materialise.js";
|
|
19
|
+
import * as componentLibrary from "../variant/components.js";
|
|
20
|
+
import * as variantStore from "../variant/store.js";
|
|
21
|
+
import { compile, copyToRun, record, render, saveForSession } from "../prompt/compile.js";
|
|
22
|
+
import { isActiveStatus } from "../sessions/contracts.js";
|
|
23
|
+
import { findRun, loadRun, saveRun, updateRun } from "../sessions/runStore.js";
|
|
24
|
+
import { CHECKLIST_WRITE } from "../tools/checklist.js";
|
|
25
|
+
import { createProfile, listProfiles, profileDirs, profileSection, removeProfile, resolveProfile } from "../agents/profiles.js";
|
|
26
|
+
import { SESSION_NAME } from "../tools/sessionName.js";
|
|
27
|
+
import { contributionId } from "../plugins/identity.js";
|
|
28
|
+
import { setupScript } from "../worktrees/setup.js";
|
|
29
|
+
import { inspectStep } from "../annotations/inspect.js";
|
|
30
|
+
import * as pageNotes from "../annotations/store.js";
|
|
31
|
+
import { APPS_COMMAND, WIKI_COMMAND } from "./cli.js";
|
|
32
|
+
import { SOCKET_ENV, TOKEN_ENV } from "./client.js";
|
|
33
|
+
import { TerminalController } from "./controller.js";
|
|
34
|
+
import { terminalCommandFiles } from "./launch.js";
|
|
35
|
+
import { SessionSocket, socketPath } from "./socket.js";
|
|
36
|
+
import { harnessLogInfo, readHarnessLogChunk } from "./logs.js";
|
|
37
|
+
import { listTerminalTrajectoryRuns, readTerminalTrajectory } from "./trajectory.js";
|
|
38
|
+
import { BrowserControl } from "./browserControl.js";
|
|
39
|
+
/** Identity of the exact resource decision that this terminal was prepared with. */
|
|
40
|
+
function resourceIdentity(loading) {
|
|
41
|
+
if (!loading)
|
|
42
|
+
return 'null';
|
|
43
|
+
return JSON.stringify({
|
|
44
|
+
extensions: loading.extensionPaths, builtins: loading.builtins, skills: loading.skillPaths,
|
|
45
|
+
prompts: loading.promptPaths, themes: loading.themePaths, detached: loading.detached,
|
|
46
|
+
project: loading.projectCode, packages: loading.packageCode
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Where the package's own build puts them. An application that bundles the harness passes its own. */
|
|
50
|
+
export function defaultAssets() {
|
|
51
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
52
|
+
const dist = [here, join(here, '..', '..'), join(here, '..', '..', 'dist'), join(here, '..', 'dist')].find((folder) => existsSync(join(folder, 'terminal-pi.js'))) ?? here;
|
|
53
|
+
return {
|
|
54
|
+
node: process.execPath,
|
|
55
|
+
cli: join(dist, 'cli.js'),
|
|
56
|
+
piExtension: join(dist, 'terminal-pi.js'),
|
|
57
|
+
openTui: join(dist, 'terminal-opentui.js')
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const READ_ONLY_TOOLS = new Set(['read', 'grep', 'find', 'ls']);
|
|
61
|
+
export class Terminals {
|
|
62
|
+
options;
|
|
63
|
+
sessions = new Map();
|
|
64
|
+
diagnosticOwners = new Map();
|
|
65
|
+
/** The variant each live session runs, for loading its skills. */
|
|
66
|
+
variantDirs = new Map();
|
|
67
|
+
/** The world tools each session was given, so a call can only reach one of them. */
|
|
68
|
+
worldNames = new Map();
|
|
69
|
+
worldReadOnly = new Set();
|
|
70
|
+
socket;
|
|
71
|
+
assets;
|
|
72
|
+
/** A session's `/accounts` or `/worktree` switch, taken once its Pi process has ended. */
|
|
73
|
+
relaunches = new Map();
|
|
74
|
+
/** The managed worktree each live session holds. */
|
|
75
|
+
worktreeLeases = new Map();
|
|
76
|
+
/** Each child's profile name and colour, looked up once per child run. */
|
|
77
|
+
childProfiles = new Map();
|
|
78
|
+
subagentProfile(record) {
|
|
79
|
+
if (!record.profile)
|
|
80
|
+
return null;
|
|
81
|
+
if (!this.childProfiles.has(record.id)) {
|
|
82
|
+
if (this.childProfiles.size > 500)
|
|
83
|
+
this.childProfiles.clear();
|
|
84
|
+
let color = 'blue';
|
|
85
|
+
try {
|
|
86
|
+
color = resolveProfile(this.options.env.root, record.workingDirectory, record.profile)?.color ?? color;
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
this.childProfiles.set(record.id, { name: record.profile, color });
|
|
90
|
+
}
|
|
91
|
+
return this.childProfiles.get(record.id) ?? null;
|
|
92
|
+
}
|
|
93
|
+
constructor(options) {
|
|
94
|
+
this.options = options;
|
|
95
|
+
this.assets = options.assets ?? defaultAssets();
|
|
96
|
+
options.runs.listen(event => {
|
|
97
|
+
this.sessions.get(event.sessionId)?.controller.observe(event);
|
|
98
|
+
const record = findRun(options.env.root, event.runId);
|
|
99
|
+
if (record?.relation !== 'subagent')
|
|
100
|
+
return;
|
|
101
|
+
let parentId = record.parentRunId;
|
|
102
|
+
const seen = new Set();
|
|
103
|
+
while (parentId && !seen.has(parentId)) {
|
|
104
|
+
seen.add(parentId);
|
|
105
|
+
const parent = findRun(options.env.root, parentId);
|
|
106
|
+
if (!parent)
|
|
107
|
+
break;
|
|
108
|
+
const owner = this.sessions.get(parent.sessionId);
|
|
109
|
+
if (owner?.controller.activeRun?.runId === parent.id) {
|
|
110
|
+
owner.controller.observe(event, true);
|
|
111
|
+
owner.controller.observeSubagent(parent.id, record, event, this.subagentProfile(record));
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
parentId = parent.parentRunId;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
this.socket = new SessionSocket(socketPath(options.stateDir), {
|
|
118
|
+
memory: (caller, call, params) => dispatch(options.env, caller, call, params, { variantDir: (caller.sessionId && this.variantDirs.get(caller.sessionId)) || null }),
|
|
119
|
+
apps: (session, call, params) => this.appsCall(session, call, params),
|
|
120
|
+
plugins: (grant, call, params, control) => this.pluginsCall(grant.sessionId, call, params, control),
|
|
121
|
+
accounts: (grant, call, params, control) => this.accountsCall(grant.sessionId, call, params, control)
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** The commands a terminal session finds on PATH. Rewritten only when they change. */
|
|
125
|
+
ensureCommands() {
|
|
126
|
+
const bin = join(this.options.stateDir, 'run', 'bin');
|
|
127
|
+
mkdirSync(bin, { recursive: true });
|
|
128
|
+
for (const name of ['agentx-wiki', 'agentx-mcp', 'agentx-wiki.cmd', 'agentx-mcp.cmd']) {
|
|
129
|
+
const path = join(bin, name);
|
|
130
|
+
try {
|
|
131
|
+
if (lstatSync(path).isFile())
|
|
132
|
+
unlinkSync(path);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// These names belonged to the previous generated wrappers.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const [name, mode] of [[WIKI_COMMAND, 'wiki'], [APPS_COMMAND, 'mcp']]) {
|
|
139
|
+
for (const file of terminalCommandFiles(process.platform, this.assets.node, this.assets.cli, name, mode)) {
|
|
140
|
+
const path = join(bin, file.name);
|
|
141
|
+
let current = null;
|
|
142
|
+
try {
|
|
143
|
+
current = readFileSync(path, 'utf8');
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
current = null;
|
|
147
|
+
}
|
|
148
|
+
if (current !== file.content) {
|
|
149
|
+
writeFileSync(path, file.content);
|
|
150
|
+
chmodSync(path, 0o755);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return bin;
|
|
155
|
+
}
|
|
156
|
+
/** The broker's tools, then the world tools whose plugins are on for this session. */
|
|
157
|
+
async tools(session, listing) {
|
|
158
|
+
const brokered = this.options.apps.brokerTools(session);
|
|
159
|
+
const live = this.sessions.get(session.sessionId);
|
|
160
|
+
const { world } = this.options;
|
|
161
|
+
if (!world || !live)
|
|
162
|
+
return brokered;
|
|
163
|
+
const context = { run: live.controller.activeRun, caller: live.caller, attended: true, subagent: false, sessionKey: session.sessionId, cwd: live.input.cwd };
|
|
164
|
+
if (!listing)
|
|
165
|
+
return [...brokered, ...world.tools(context, (_plugin, tool) => this.worldNames.get(session.sessionId)?.has(tool) ?? false)];
|
|
166
|
+
const on = await this.worldOn(live);
|
|
167
|
+
const tools = world.tools(context, (plugin, tool) => on(plugin, tool));
|
|
168
|
+
this.worldNames.set(session.sessionId, new Set(tools.map((tool) => tool.name)));
|
|
169
|
+
for (const tool of tools)
|
|
170
|
+
if (tool.readOnly)
|
|
171
|
+
this.worldReadOnly.add(tool.name);
|
|
172
|
+
return [...brokered, ...tools];
|
|
173
|
+
}
|
|
174
|
+
/** The world tools a new session will have, named in its instructions. */
|
|
175
|
+
async worldToolNames(input, account) {
|
|
176
|
+
const { world } = this.options;
|
|
177
|
+
if (!world || input.connections === false)
|
|
178
|
+
return [];
|
|
179
|
+
const list = this.options.pluginControl?.list;
|
|
180
|
+
const plugins = list ? await list({ cwd: input.cwd, accountId: account.id, projectId: input.projectId ?? null }) : null;
|
|
181
|
+
const on = plugins ? new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id))) : null;
|
|
182
|
+
return world.plugins.flatMap((plugin) => (plugin.tools ?? []).map((tool) => tool.name).filter((name) => !on || on.has(contributionId(plugin.id, 'tool', name))));
|
|
183
|
+
}
|
|
184
|
+
/** Whether a world tool's plugin is switched on for a session's folder, account and project. */
|
|
185
|
+
async worldOn(live) {
|
|
186
|
+
const list = this.options.pluginControl?.list;
|
|
187
|
+
if (!list)
|
|
188
|
+
return () => true;
|
|
189
|
+
const plugins = await list({ cwd: live.input.cwd, accountId: live.account.id, projectId: live.input.projectId ?? null });
|
|
190
|
+
const on = new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id)));
|
|
191
|
+
return (plugin, tool) => on.has(contributionId(plugin, 'tool', tool));
|
|
192
|
+
}
|
|
193
|
+
async appsCall(session, call, params) {
|
|
194
|
+
const { apps } = this.options;
|
|
195
|
+
switch (call) {
|
|
196
|
+
case 'mcp.list':
|
|
197
|
+
return apps.list(session);
|
|
198
|
+
case 'mcp.call':
|
|
199
|
+
return apps.invoke(session, String(params.name ?? ''), (params.args && typeof params.args === 'object' ? params.args : {}));
|
|
200
|
+
case 'mcp.revision':
|
|
201
|
+
return apps.revision(session);
|
|
202
|
+
case 'tools.list': {
|
|
203
|
+
const { Type } = await import('typebox');
|
|
204
|
+
return { tools: (await this.tools(session, true)).map((tool) => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
|
|
205
|
+
}
|
|
206
|
+
case 'tools.call': {
|
|
207
|
+
const tool = (await this.tools(session, false)).find((candidate) => candidate.name === params.name);
|
|
208
|
+
if (!tool)
|
|
209
|
+
throw new HarnessError('unknown_method', `Unknown tool ${String(params.name)}.`);
|
|
210
|
+
const result = await tool.execute((params.input && typeof params.input === 'object' ? params.input : {}));
|
|
211
|
+
if (typeof result === 'string')
|
|
212
|
+
return { text: result };
|
|
213
|
+
const images = [...(result.image ? [result.image] : []), ...(result.images ?? [])];
|
|
214
|
+
return { text: result.text, ...(images.length ? { images } : {}), ...(result.details ? { details: result.details } : {}) };
|
|
215
|
+
}
|
|
216
|
+
default:
|
|
217
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
|
|
222
|
+
* session's own extension, which asks the person in the terminal first: the model can reach
|
|
223
|
+
* this socket from a shell command, and must not be able to trust code or switch it on.
|
|
224
|
+
*/
|
|
225
|
+
async pluginsCall(sessionId, call, params, control) {
|
|
226
|
+
const api = this.options.pluginControl;
|
|
227
|
+
const live = this.sessions.get(sessionId);
|
|
228
|
+
if (!api || !live)
|
|
229
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
230
|
+
const projectId = live.input.projectId ?? null;
|
|
231
|
+
const scope = { cwd: live.input.cwd, accountId: live.account.id, projectId };
|
|
232
|
+
if (call === 'hub.list' && this.options.hub)
|
|
233
|
+
return this.options.hub.list(params.refresh === true);
|
|
234
|
+
if (call === 'hub.install' && this.options.hub) {
|
|
235
|
+
if (!control)
|
|
236
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can install items. Type /hub.');
|
|
237
|
+
if (typeof params.id !== 'string' || typeof params.version !== 'string')
|
|
238
|
+
throw invalid('An item id and version are required.');
|
|
239
|
+
return this.options.hub.install(params.id, params.version);
|
|
240
|
+
}
|
|
241
|
+
if (call === 'plugins.list')
|
|
242
|
+
return api.list(scope);
|
|
243
|
+
if (call === 'plugins.session')
|
|
244
|
+
return { projectId };
|
|
245
|
+
if (!control)
|
|
246
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can change plugins. Type /plugins.');
|
|
247
|
+
const text = (name) => {
|
|
248
|
+
if (typeof params[name] !== 'string' || !params[name])
|
|
249
|
+
throw invalid(`${name} is required.`);
|
|
250
|
+
return params[name];
|
|
251
|
+
};
|
|
252
|
+
switch (call) {
|
|
253
|
+
case 'plugins.attach':
|
|
254
|
+
case 'plugins.detach':
|
|
255
|
+
return api.attach({ ...scope, id: text('id'), scope: params.everywhere === true || !projectId ? {} : { projectId } }, call === 'plugins.attach');
|
|
256
|
+
case 'plugins.trust':
|
|
257
|
+
return api.trust({ ...scope, hash: text('hash') });
|
|
258
|
+
case 'plugins.untrust':
|
|
259
|
+
return api.untrust({ ...scope, hash: text('hash') });
|
|
260
|
+
case 'plugins.configure': {
|
|
261
|
+
const settings = params.settings && typeof params.settings === 'object' && !Array.isArray(params.settings) ? params.settings : {};
|
|
262
|
+
return api.configure({ ...scope, pluginId: text('pluginId'), settings });
|
|
263
|
+
}
|
|
264
|
+
case 'plugins.install':
|
|
265
|
+
if (!api.install)
|
|
266
|
+
throw invalid('Package installation is unavailable in this host.');
|
|
267
|
+
return api.install({ ...scope, source: text('source') });
|
|
268
|
+
case 'plugins.remove':
|
|
269
|
+
if (!api.remove)
|
|
270
|
+
throw invalid('Package removal is unavailable in this host.');
|
|
271
|
+
return api.remove({ ...scope, pluginId: text('pluginId') });
|
|
272
|
+
default:
|
|
273
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
|
|
278
|
+
* or removing an account is a person's choice, never the model's shell command.
|
|
279
|
+
*/
|
|
280
|
+
async accountsCall(sessionId, call, params, control) {
|
|
281
|
+
const { accounts } = this.options;
|
|
282
|
+
const live = this.sessions.get(sessionId);
|
|
283
|
+
if (!live)
|
|
284
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
285
|
+
if (!control)
|
|
286
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can manage accounts. Type /accounts.');
|
|
287
|
+
const text = (name) => {
|
|
288
|
+
if (typeof params[name] !== 'string' || !params[name])
|
|
289
|
+
throw invalid(`${name} is required.`);
|
|
290
|
+
return params[name];
|
|
291
|
+
};
|
|
292
|
+
const optional = (name) => (typeof params[name] === 'string' ? params[name] : null);
|
|
293
|
+
switch (call) {
|
|
294
|
+
case 'accounts.deviceId':
|
|
295
|
+
return accounts.installationDeviceId();
|
|
296
|
+
case 'accounts.list':
|
|
297
|
+
return accounts.overview();
|
|
298
|
+
case 'accounts.session':
|
|
299
|
+
return { accountId: live.account.id, label: live.account.label };
|
|
300
|
+
case 'accounts.create':
|
|
301
|
+
return accounts.create(text('label'), optional('authKind') ?? 'subscription');
|
|
302
|
+
case 'accounts.rename':
|
|
303
|
+
return accounts.rename(text('accountId'), text('label'));
|
|
304
|
+
case 'accounts.setDefault':
|
|
305
|
+
return accounts.setDefault(text('accountId'));
|
|
306
|
+
case 'accounts.remove': {
|
|
307
|
+
const accountId = text('accountId');
|
|
308
|
+
// Every terminal in this process counts, this session's own included. Another process's terminals are not visible here.
|
|
309
|
+
if (accounts.terminalCount(accountId) > 0)
|
|
310
|
+
throw new HarnessError('account_in_use', 'A running terminal is using this account, this one included.', 'Switch this session to another account first, then remove it.');
|
|
311
|
+
return accounts.remove(accountId);
|
|
312
|
+
}
|
|
313
|
+
case 'accounts.refresh':
|
|
314
|
+
return accounts.refresh(optional('accountId'));
|
|
315
|
+
case 'accounts.providers':
|
|
316
|
+
return accounts.providers();
|
|
317
|
+
// A terminal reads and writes only its own account's choices, never the id a caller names.
|
|
318
|
+
case 'accounts.choices.get':
|
|
319
|
+
return accounts.choices(live.account.id);
|
|
320
|
+
case 'accounts.choices.set':
|
|
321
|
+
return accounts.choose(live.account.id, text('mode'), {
|
|
322
|
+
...(typeof params.model === 'string' ? { model: params.model } : {}),
|
|
323
|
+
...(typeof params.effort === 'string' ? { effort: params.effort } : {})
|
|
324
|
+
});
|
|
325
|
+
case 'accounts.loginStart':
|
|
326
|
+
return accounts.loginStart(text('accountId'), text('provider'), optional('method'));
|
|
327
|
+
case 'accounts.loginStatus':
|
|
328
|
+
return accounts.loginStatus(text('accountId'));
|
|
329
|
+
case 'accounts.loginRespond':
|
|
330
|
+
accounts.loginRespond(text('loginId'), text('promptId'), optional('value'));
|
|
331
|
+
return {};
|
|
332
|
+
case 'accounts.loginCancel':
|
|
333
|
+
accounts.loginCancel(text('accountId'), optional('loginId'));
|
|
334
|
+
return {};
|
|
335
|
+
case 'accounts.setApiKey':
|
|
336
|
+
return accounts.setApiKey(text('accountId'), text('key'), text('provider'));
|
|
337
|
+
case 'accounts.removeProvider':
|
|
338
|
+
return accounts.removeProvider(text('accountId'), text('provider'));
|
|
339
|
+
case 'accounts.signOut':
|
|
340
|
+
return accounts.signOut(text('accountId'));
|
|
341
|
+
case 'accounts.savePool': {
|
|
342
|
+
const pool = params.pool;
|
|
343
|
+
if (!pool || typeof pool.label !== 'string' || !Array.isArray(pool.accountIds) || !pool.accountIds.every((id) => typeof id === 'string'))
|
|
344
|
+
throw invalid('A pool needs a label and its account ids.');
|
|
345
|
+
return accounts.savePool({ id: typeof pool.id === 'string' ? pool.id : undefined, label: pool.label, accountIds: pool.accountIds });
|
|
346
|
+
}
|
|
347
|
+
case 'accounts.removePool':
|
|
348
|
+
return accounts.removePool(text('poolId'));
|
|
349
|
+
case 'accounts.audit':
|
|
350
|
+
return readAudit(accounts.dir, optional('accountId'), typeof params.limit === 'number' ? params.limit : 100);
|
|
351
|
+
case 'accounts.relaunch': {
|
|
352
|
+
// Checked here so a bad choice is refused before the session is asked to end.
|
|
353
|
+
const target = accounts.resolve(text('accountId'));
|
|
354
|
+
this.relaunches.set(sessionId, { accountId: target.id, nativeSessionId: live.controller.nativeSessionId });
|
|
355
|
+
return { accountId: target.id, label: target.label };
|
|
356
|
+
}
|
|
357
|
+
default:
|
|
358
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/** `/agents`: the agent profiles this session's folder can use (PROP-0029). */
|
|
362
|
+
agentsCall(live, call, params) {
|
|
363
|
+
const root = this.options.env.root;
|
|
364
|
+
const cwd = live.input.cwd;
|
|
365
|
+
const name = () => {
|
|
366
|
+
if (typeof params.name !== 'string' || !params.name.trim())
|
|
367
|
+
throw invalid('A profile name is required.');
|
|
368
|
+
return params.name.trim();
|
|
369
|
+
};
|
|
370
|
+
const scope = () => {
|
|
371
|
+
if (params.scope !== 'global' && params.scope !== 'project')
|
|
372
|
+
throw invalid('scope must be global or project.');
|
|
373
|
+
return params.scope;
|
|
374
|
+
};
|
|
375
|
+
switch (call.slice('terminal.agents.'.length)) {
|
|
376
|
+
case 'list': return { ...listProfiles(root, cwd, this.variantDirs.get(live.input.sessionId) ?? null), dirs: profileDirs(root, cwd) };
|
|
377
|
+
case 'create': return createProfile(root, cwd, { name: name(), scope: scope(), from: typeof params.from === 'string' && params.from ? params.from : null });
|
|
378
|
+
case 'remove':
|
|
379
|
+
removeProfile(root, cwd, { name: name(), scope: scope() });
|
|
380
|
+
return {};
|
|
381
|
+
default: throw invalid('Unknown profile operation.');
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/** `/worktree`: the managed worktrees of the repository this session works in. */
|
|
385
|
+
async worktreesCall(live, call, params) {
|
|
386
|
+
const worktrees = this.options.worktrees;
|
|
387
|
+
if (!worktrees)
|
|
388
|
+
throw invalid('Worktrees are unavailable in this host.');
|
|
389
|
+
const sessionId = live.input.sessionId;
|
|
390
|
+
const cwd = live.input.cwd;
|
|
391
|
+
const current = this.worktreeLeases.get(sessionId) ?? null;
|
|
392
|
+
const id = () => {
|
|
393
|
+
if (typeof params.id !== 'string' || !params.id)
|
|
394
|
+
throw invalid('A worktree id is required.');
|
|
395
|
+
return params.id;
|
|
396
|
+
};
|
|
397
|
+
const action = call.slice('terminal.worktrees.'.length);
|
|
398
|
+
switch (action) {
|
|
399
|
+
case 'list': {
|
|
400
|
+
let repo;
|
|
401
|
+
try {
|
|
402
|
+
repo = await worktrees.repositoryOf(cwd);
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
return { repo: null, current, worktrees: [], removed: [], reason: error instanceof Error ? error.message : String(error) };
|
|
406
|
+
}
|
|
407
|
+
const statuses = await Promise.all((await worktrees.list(repo)).map((record) => worktrees.status(record.id)));
|
|
408
|
+
return { repo, current, worktrees: statuses, removed: worktrees.removed(repo), reason: null };
|
|
409
|
+
}
|
|
410
|
+
case 'status': return worktrees.status(id());
|
|
411
|
+
case 'create': return worktrees.create({
|
|
412
|
+
repo: cwd,
|
|
413
|
+
...(typeof params.name === 'string' && params.name ? { name: params.name } : {}),
|
|
414
|
+
...(typeof params.branch === 'string' && params.branch ? { branch: params.branch } : {}),
|
|
415
|
+
kind: params.kind === 'permanent' ? 'permanent' : 'managed'
|
|
416
|
+
});
|
|
417
|
+
case 'branch':
|
|
418
|
+
if (typeof params.name !== 'string' || !params.name)
|
|
419
|
+
throw invalid('A branch name is required.');
|
|
420
|
+
return worktrees.branch(id(), params.name);
|
|
421
|
+
case 'handoff': {
|
|
422
|
+
const direction = params.direction === 'toSource' || params.direction === 'toWorktree' ? params.direction : null;
|
|
423
|
+
if (!direction)
|
|
424
|
+
throw invalid('direction is toSource or toWorktree.');
|
|
425
|
+
return worktrees.handoff(id(), direction, { move: params.move !== false });
|
|
426
|
+
}
|
|
427
|
+
case 'remove':
|
|
428
|
+
if (id() === current)
|
|
429
|
+
throw new HarnessError('worktree_in_use', 'This session works in that worktree. Switch to another folder first.');
|
|
430
|
+
return worktrees.remove(id(), { force: params.force === true });
|
|
431
|
+
case 'restore': return worktrees.restore(id());
|
|
432
|
+
case 'prune': return worktrees.prune({ repo: cwd, ...(typeof params.keep === 'number' ? { keep: params.keep } : {}) });
|
|
433
|
+
case 'trustSetup': {
|
|
434
|
+
const repo = await worktrees.repositoryOf(cwd);
|
|
435
|
+
const script = setupScript(repo);
|
|
436
|
+
if (!script)
|
|
437
|
+
throw invalid('This repository has no .xharness/worktree-setup.');
|
|
438
|
+
await worktrees.trustSetup(repo, script.hash);
|
|
439
|
+
return { hash: script.hash };
|
|
440
|
+
}
|
|
441
|
+
case 'switch': {
|
|
442
|
+
// A new conversation in the chosen folder: Pi keeps one history per working folder.
|
|
443
|
+
const target = params.main === true ? null : id();
|
|
444
|
+
const folder = target ? worktrees.pathOf(target) : await worktrees.repositoryOf(cwd);
|
|
445
|
+
if (!folder)
|
|
446
|
+
throw new HarnessError('worktree_missing', 'That worktree has no folder.');
|
|
447
|
+
this.relaunches.set(sessionId, { accountId: live.account.id, nativeSessionId: null, cwd: folder, worktreeId: target });
|
|
448
|
+
return { cwd: folder, worktreeId: target };
|
|
449
|
+
}
|
|
450
|
+
default:
|
|
451
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/** `/plugin:notes` and `/plugin:annotate`: the page notes a person keeps, and the visible browser they capture them in. */
|
|
455
|
+
async annotationsCall(live, call, params) {
|
|
456
|
+
const root = this.options.env.root;
|
|
457
|
+
const sessionId = live.input.sessionId;
|
|
458
|
+
const capture = this.options.noteCapture;
|
|
459
|
+
const id = () => {
|
|
460
|
+
if (typeof params.id !== 'string' || !params.id)
|
|
461
|
+
throw invalid('A note id is required.');
|
|
462
|
+
return params.id;
|
|
463
|
+
};
|
|
464
|
+
const version = () => {
|
|
465
|
+
if (typeof params.expectedVersion !== 'number')
|
|
466
|
+
throw invalid('The note version you last read is required.');
|
|
467
|
+
return params.expectedVersion;
|
|
468
|
+
};
|
|
469
|
+
const needCapture = () => {
|
|
470
|
+
if (!capture)
|
|
471
|
+
throw invalid('This host has no capture browser.');
|
|
472
|
+
return capture;
|
|
473
|
+
};
|
|
474
|
+
const needNotes = () => {
|
|
475
|
+
if (!this.options.pageNotes)
|
|
476
|
+
throw invalid('This host cannot send page notes.');
|
|
477
|
+
return this.options.pageNotes;
|
|
478
|
+
};
|
|
479
|
+
const action = call.slice('terminal.annotations.'.length);
|
|
480
|
+
switch (action) {
|
|
481
|
+
case 'list': {
|
|
482
|
+
const result = pageNotes.list(root, {
|
|
483
|
+
query: typeof params.query === 'string' ? params.query : null,
|
|
484
|
+
url: typeof params.url === 'string' ? params.url : null,
|
|
485
|
+
status: params.status === 'open' || params.status === 'resolved' ? params.status : null,
|
|
486
|
+
limit: 200
|
|
487
|
+
});
|
|
488
|
+
return { ...result, sessionId };
|
|
489
|
+
}
|
|
490
|
+
case 'read': return pageNotes.read(root, id());
|
|
491
|
+
case 'create': {
|
|
492
|
+
const note = pageNotes.create(root, { source: params.source, target: params.target, body: params.body, tags: params.tags });
|
|
493
|
+
return params.share === true ? pageNotes.share(root, note.id, { sessionId }) : note;
|
|
494
|
+
}
|
|
495
|
+
case 'update': return pageNotes.update(root, id(), version(), (params.patch ?? {}));
|
|
496
|
+
case 'delete':
|
|
497
|
+
pageNotes.remove(root, id(), version());
|
|
498
|
+
return {};
|
|
499
|
+
// A terminal shares with, and withdraws from, its own session only.
|
|
500
|
+
case 'share': return pageNotes.share(root, id(), { sessionId });
|
|
501
|
+
case 'unshare': return pageNotes.unshare(root, id(), { sessionId });
|
|
502
|
+
case 'open': return needCapture().open(sessionId, typeof params.url === 'string' && params.url ? params.url : null);
|
|
503
|
+
case 'identity': return needCapture().identity(sessionId);
|
|
504
|
+
case 'begin':
|
|
505
|
+
if (params.kind !== 'text' && params.kind !== 'element')
|
|
506
|
+
throw invalid('kind is text or element.');
|
|
507
|
+
return needCapture().begin(sessionId, params.kind);
|
|
508
|
+
case 'capture':
|
|
509
|
+
if (typeof params.captureId !== 'string')
|
|
510
|
+
throw invalid('A capture id is required.');
|
|
511
|
+
return needCapture().capture(sessionId, params.captureId);
|
|
512
|
+
case 'cancel':
|
|
513
|
+
await needCapture().cancel(sessionId, typeof params.captureId === 'string' ? params.captureId : undefined);
|
|
514
|
+
return {};
|
|
515
|
+
case 'reveal': {
|
|
516
|
+
const note = pageNotes.read(root, id());
|
|
517
|
+
return needCapture().reveal(sessionId, note.source.url, note.target);
|
|
518
|
+
}
|
|
519
|
+
// ---- The inspector and the queue (PROP-0018); a terminal queues and sends to its own session.
|
|
520
|
+
case 'target': return { ...needCapture().describe(sessionId), windows: await needCapture().windows(sessionId).catch(() => []) };
|
|
521
|
+
case 'attach': {
|
|
522
|
+
const kind = params.kind;
|
|
523
|
+
if (kind !== 'web' && kind !== 'cdp' && kind !== 'bridge')
|
|
524
|
+
throw invalid('kind is web, cdp or bridge.');
|
|
525
|
+
const spec = kind === 'cdp'
|
|
526
|
+
? { kind, endpoint: typeof params.endpoint === 'string' ? params.endpoint : '', name: typeof params.name === 'string' ? params.name : null }
|
|
527
|
+
: kind === 'bridge' ? { kind, name: typeof params.name === 'string' ? params.name : null } : { kind };
|
|
528
|
+
return needCapture().attach(sessionId, spec);
|
|
529
|
+
}
|
|
530
|
+
case 'windows': return needCapture().windows(sessionId);
|
|
531
|
+
case 'selectWindow':
|
|
532
|
+
if (typeof params.window !== 'string')
|
|
533
|
+
throw invalid('A window id is required.');
|
|
534
|
+
await needCapture().selectWindow(sessionId, params.window);
|
|
535
|
+
return {};
|
|
536
|
+
case 'inspect': return needCapture().inspect(sessionId);
|
|
537
|
+
case 'stopInspect':
|
|
538
|
+
await needCapture().stopInspect(sessionId);
|
|
539
|
+
return {};
|
|
540
|
+
case 'next': return inspectStep(needCapture(), needNotes(), sessionId, sessionId, 'terminal');
|
|
541
|
+
case 'queue': return needNotes().queue(id(), version(), sessionId);
|
|
542
|
+
case 'dequeue': return needNotes().dequeue(id(), version());
|
|
543
|
+
case 'confirm': return needNotes().confirm(id(), version());
|
|
544
|
+
case 'send': return needNotes().send(sessionId, { ids: Array.isArray(params.ids) ? params.ids.filter((entry) => typeof entry === 'string') : null });
|
|
545
|
+
default:
|
|
546
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
/** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
|
|
550
|
+
takeRelaunch(sessionId) {
|
|
551
|
+
const request = this.relaunches.get(sessionId) ?? null;
|
|
552
|
+
this.relaunches.delete(sessionId);
|
|
553
|
+
return request;
|
|
554
|
+
}
|
|
555
|
+
live(sessionId) {
|
|
556
|
+
const live = this.sessions.get(sessionId);
|
|
557
|
+
if (!live)
|
|
558
|
+
throw new HarnessError('terminal_not_ready', 'The terminal controller is unavailable. Reopen this session.');
|
|
559
|
+
return live;
|
|
560
|
+
}
|
|
561
|
+
historyDir(input) {
|
|
562
|
+
return join(input.sessionFolder, 'pi');
|
|
563
|
+
}
|
|
564
|
+
runRecord(live, runId, text) {
|
|
565
|
+
const { input } = live;
|
|
566
|
+
return {
|
|
567
|
+
resumedFromRunId: null,
|
|
568
|
+
parentRunId: null,
|
|
569
|
+
relation: null,
|
|
570
|
+
terminalReason: null,
|
|
571
|
+
agentId: null,
|
|
572
|
+
trigger: null,
|
|
573
|
+
id: runId,
|
|
574
|
+
projectId: input.projectId ?? null,
|
|
575
|
+
mode: input.run?.mode ?? 'code',
|
|
576
|
+
harness: 'pi',
|
|
577
|
+
sessionType: 'terminal',
|
|
578
|
+
sessionId: input.sessionId,
|
|
579
|
+
nativeSessionId: live.controller.nativeSessionId,
|
|
580
|
+
workingDirectory: input.cwd,
|
|
581
|
+
worktreeId: input.run?.worktreeId ?? null,
|
|
582
|
+
goal: text,
|
|
583
|
+
attachmentCount: 0,
|
|
584
|
+
startedAt: now(),
|
|
585
|
+
endedAt: null,
|
|
586
|
+
status: 'queued',
|
|
587
|
+
exitCode: null,
|
|
588
|
+
handoffId: null,
|
|
589
|
+
accountId: live.account.id
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
|
|
593
|
+
begin(live, reservedRunId, text) {
|
|
594
|
+
const { root } = this.options.env;
|
|
595
|
+
const { sessionId } = live.input;
|
|
596
|
+
if (this.options.runs.activeForSession(sessionId))
|
|
597
|
+
throw new HarnessError('session_busy', 'A native turn already owns this session.');
|
|
598
|
+
let runId = reservedRunId;
|
|
599
|
+
if (runId) {
|
|
600
|
+
const reserved = loadRun(root, runId);
|
|
601
|
+
if (reserved.sessionId !== sessionId || reserved.status !== 'queued')
|
|
602
|
+
throw invalid('Delivery does not own this queued run.');
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
runId = newId();
|
|
606
|
+
saveRun(root, this.runRecord(live, runId, text || null));
|
|
607
|
+
copyToRun(root, live.input.sessionFolder, runId);
|
|
608
|
+
}
|
|
609
|
+
return this.options.runs.start(sessionId, runId);
|
|
610
|
+
}
|
|
611
|
+
async decide(live, run, tool, args) {
|
|
612
|
+
const record = loadRun(this.options.env.root, run.runId);
|
|
613
|
+
if (!isActiveStatus(record.status))
|
|
614
|
+
throw invalid(`The run is no longer active (its status is ${record.status}).`);
|
|
615
|
+
const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args };
|
|
616
|
+
const { policy } = this.options;
|
|
617
|
+
if (policy.scope) {
|
|
618
|
+
const scope = await policy.scope(call);
|
|
619
|
+
if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
|
|
620
|
+
return { allowed: false, reason: scope.reason ?? 'Outside what this session may do.' };
|
|
621
|
+
try {
|
|
622
|
+
await policy.authorize?.(call);
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
return { allowed: false, reason: error instanceof Error ? error.message : 'Tool unavailable in the settings.' };
|
|
626
|
+
}
|
|
627
|
+
const requiresApproval = !!scope.requiresApproval || (scope.allowed && !!scope.humanOnly);
|
|
628
|
+
return {
|
|
629
|
+
allowed: scope.allowed,
|
|
630
|
+
requiresApproval,
|
|
631
|
+
reason: scope.reason ?? null,
|
|
632
|
+
rememberable: scope.allowed && !scope.humanOnly,
|
|
633
|
+
...(scope.humanOnly ? { standing: scope.allowGlobalApproval ? 'switch' : 'none' } : {})
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
// With no application policy: reads run, a read-only session refuses the rest, and anything else asks.
|
|
637
|
+
const name = tool.toLowerCase();
|
|
638
|
+
if (name === SESSION_NAME || name === CHECKLIST_WRITE || name === SPAWN_SUBAGENT || name === WAIT_SUBAGENTS || READ_ONLY_TOOLS.has(name) || this.worldReadOnly.has(name) || this.options.apps.toolNames.includes(name))
|
|
639
|
+
return { allowed: true };
|
|
640
|
+
if (live.input.readOnly)
|
|
641
|
+
return { allowed: false, reason: 'This session is read-only.' };
|
|
642
|
+
return { allowed: false, requiresApproval: true, rememberable: true, reason: 'This native action requires approval.' };
|
|
643
|
+
}
|
|
644
|
+
async subagentsCall(live, call, params) {
|
|
645
|
+
if (call === 'terminal.subagents.monitor')
|
|
646
|
+
return { children: live.controller.subagentSnapshot() };
|
|
647
|
+
const service = this.options.subagents;
|
|
648
|
+
if (!service || live.input.subagents === null) {
|
|
649
|
+
if (call === 'terminal.subagents.tools')
|
|
650
|
+
return { tools: [] };
|
|
651
|
+
throw invalid('Subagents are disabled for this terminal.');
|
|
652
|
+
}
|
|
653
|
+
if (call === 'terminal.subagents.tools')
|
|
654
|
+
return { tools: subagentTools(service, '').map(tool => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
|
|
655
|
+
const run = live.controller.activeRun;
|
|
656
|
+
if (!run)
|
|
657
|
+
throw new HarnessError('run_not_active', 'This terminal has no active parent run.');
|
|
658
|
+
if (call === 'terminal.subagents.register') {
|
|
659
|
+
if (!Array.isArray(params.tools) || params.tools.some((name) => typeof name !== 'string'))
|
|
660
|
+
throw invalid('Native tool names are required.');
|
|
661
|
+
const readOnly = live.input.readOnly === true;
|
|
662
|
+
const readTools = new Set([...READ_ONLY_TOOLS, SESSION_NAME, CHECKLIST_WRITE, SPAWN_SUBAGENT, WAIT_SUBAGENTS, 'ask_user', 'mcp_list_tools', 'mcp_call_tool', 'history_search', 'history_read', 'wiki_search', 'wiki_read', 'wiki_list', 'memory_facts', 'skill_load']);
|
|
663
|
+
const tools = params.tools.filter((name) => !readOnly || readTools.has(name));
|
|
664
|
+
const capabilities = this.options.resolveCapabilities ? await this.options.resolveCapabilities(live.caller, tools, live.input.cwd) : { version: 1, tools, connectors: [], contributions: [] };
|
|
665
|
+
freezeRun(this.options.env.root, run.runId, live.caller, capabilities);
|
|
666
|
+
service.register({
|
|
667
|
+
run: { id: run.runId, sessionId: live.input.sessionId, mode: live.input.run?.mode ?? 'code', projectId: live.input.projectId, worktreeId: live.input.run?.worktreeId },
|
|
668
|
+
cwd: live.input.cwd, sessionDir: this.historyDir(live.input), text: '', caller: live.caller,
|
|
669
|
+
model: typeof params.model === 'string' ? params.model : live.input.model,
|
|
670
|
+
effort: typeof params.effort === 'string' ? params.effort : null,
|
|
671
|
+
launch: live.input.launch, tools: { allow: capabilities.tools, gated: capabilities.tools.filter(name => !readTools.has(name)) },
|
|
672
|
+
connections: live.input.connections === false ? false : { readOnly }, subagents: live.input.subagents,
|
|
673
|
+
variant: this.variantDirs.has(live.input.sessionId) ? { dir: this.variantDirs.get(live.input.sessionId), scope: live.input.variant && 'scope' in live.input.variant ? live.input.variant.scope ?? null : null } : null,
|
|
674
|
+
capabilities
|
|
675
|
+
}, live.account.id, capabilities);
|
|
676
|
+
service.restore(run.runId, params.history ?? { owners: [], receipts: [] });
|
|
677
|
+
return { runId: run.runId };
|
|
678
|
+
}
|
|
679
|
+
if (call === 'terminal.subagents.verify') {
|
|
680
|
+
await service.verify(run.runId, params.history);
|
|
681
|
+
return {};
|
|
682
|
+
}
|
|
683
|
+
if (call === 'terminal.subagents.delivery')
|
|
684
|
+
return service.delivery(run.runId, params.history, params.settle === true);
|
|
685
|
+
if (call === 'terminal.subagents.call') {
|
|
686
|
+
const tool = subagentTools(service, run.runId).find(tool => tool.name === params.name);
|
|
687
|
+
if (!tool)
|
|
688
|
+
throw invalid('Unknown subagent tool.');
|
|
689
|
+
const reason = await this.decide(live, run, tool.name, params.input ?? {});
|
|
690
|
+
if (!reason.allowed)
|
|
691
|
+
throw new HarnessError('tool_denied', reason.reason ?? 'Subagents are unavailable.');
|
|
692
|
+
const result = await tool.execute(params.input ?? {});
|
|
693
|
+
return typeof result === 'string' ? { text: result } : result;
|
|
694
|
+
}
|
|
695
|
+
throw invalid('Unknown subagent operation.');
|
|
696
|
+
}
|
|
697
|
+
componentsCall(live, call, params) {
|
|
698
|
+
const root = this.options.env.root;
|
|
699
|
+
const choice = live.input.variant;
|
|
700
|
+
const scope = choice && 'scope' in choice ? choice.scope ?? null : null;
|
|
701
|
+
const head = scope ? variantStore.headVariant(root, scope) : null;
|
|
702
|
+
const items = componentLibrary.list(root);
|
|
703
|
+
if (call === 'terminal.components.list') {
|
|
704
|
+
return {
|
|
705
|
+
items,
|
|
706
|
+
addedIds: head ? items.filter(item => componentLibrary.isInVariant(root, head.variant, item.id)).map(item => item.id) : [],
|
|
707
|
+
writable: Boolean(scope && head),
|
|
708
|
+
fingerprint: head?.fingerprint ?? null
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
if (call === 'terminal.components.add') {
|
|
712
|
+
if (typeof params.id !== 'string' || !params.id)
|
|
713
|
+
throw invalid('A component id is required.');
|
|
714
|
+
if (!scope || !head)
|
|
715
|
+
throw invalid('This terminal has no writable Harness scope.');
|
|
716
|
+
const item = items.find(candidate => candidate.id === params.id);
|
|
717
|
+
if (!item)
|
|
718
|
+
throw invalid('That component is no longer in the library.');
|
|
719
|
+
if (item.kind === 'prompts')
|
|
720
|
+
throw invalid('Prompts stay in the library and cannot be added to a Variant.');
|
|
721
|
+
if (componentLibrary.isInVariant(root, head.variant, item.id)) {
|
|
722
|
+
return { fingerprint: head.fingerprint, alreadyPresent: true };
|
|
723
|
+
}
|
|
724
|
+
const variant = componentLibrary.addToVariant(root, head.variant, item.id);
|
|
725
|
+
const record = variantStore.commit(root, scope, variant, {
|
|
726
|
+
kind: 'edit', note: 'Added library component ' + item.id, components: [item.id]
|
|
727
|
+
});
|
|
728
|
+
this.options.bus.emit({ type: 'variants.changed', scope });
|
|
729
|
+
return { fingerprint: record.fingerprint, alreadyPresent: false };
|
|
730
|
+
}
|
|
731
|
+
throw new HarnessError('unknown_method', 'Unknown terminal component operation.');
|
|
732
|
+
}
|
|
733
|
+
trajectoryCall(live, call, params) {
|
|
734
|
+
if (call === 'terminal.trajectory.list') {
|
|
735
|
+
return { runs: listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId) };
|
|
736
|
+
}
|
|
737
|
+
if (call === 'terminal.trajectory.events') {
|
|
738
|
+
if (typeof params.runId !== 'string' || !params.runId)
|
|
739
|
+
throw invalid('A run id is required.');
|
|
740
|
+
const afterSequence = params.afterSequence;
|
|
741
|
+
if (afterSequence !== undefined && typeof afterSequence !== 'number')
|
|
742
|
+
throw invalid('The event sequence must be a number.');
|
|
743
|
+
return readTerminalTrajectory(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId, params.runId, afterSequence);
|
|
744
|
+
}
|
|
745
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
746
|
+
}
|
|
747
|
+
async sideQuestionCall(live, call, params) {
|
|
748
|
+
const service = this.options.sideQuestions;
|
|
749
|
+
if (!service)
|
|
750
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
751
|
+
if (call === 'terminal.sideQuestions.start') {
|
|
752
|
+
if (typeof params.question !== 'string')
|
|
753
|
+
throw invalid('A side question is required.');
|
|
754
|
+
const context = params.context && typeof params.context === 'object' && !Array.isArray(params.context)
|
|
755
|
+
? params.context
|
|
756
|
+
: undefined;
|
|
757
|
+
const latest = listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId)
|
|
758
|
+
.find(run => findRun(this.options.env.root, run.id)?.trigger !== 'side-question');
|
|
759
|
+
return service.start({
|
|
760
|
+
sessionId: live.input.sessionId,
|
|
761
|
+
parentRunId: live.controller.activeRun?.runId ?? latest?.id ?? null,
|
|
762
|
+
parentSessionId: live.input.sessionId,
|
|
763
|
+
accountId: live.account.id,
|
|
764
|
+
model: typeof params.model === 'string' ? params.model : live.input.model ?? null,
|
|
765
|
+
effort: typeof params.effort === 'string' ? params.effort : undefined,
|
|
766
|
+
cwd: live.input.cwd,
|
|
767
|
+
caller: live.caller,
|
|
768
|
+
question: params.question,
|
|
769
|
+
context
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
if (typeof params.runId !== 'string')
|
|
773
|
+
throw invalid('A side-question run id is required.');
|
|
774
|
+
if (call === 'terminal.sideQuestions.read') {
|
|
775
|
+
const afterSequence = params.afterSequence === undefined ? -1 : params.afterSequence;
|
|
776
|
+
if (typeof afterSequence !== 'number')
|
|
777
|
+
throw invalid('A side-question event sequence is required.');
|
|
778
|
+
return service.read(params.runId, live.input.sessionId, afterSequence);
|
|
779
|
+
}
|
|
780
|
+
if (call === 'terminal.sideQuestions.interrupt')
|
|
781
|
+
return service.interrupt(params.runId, live.input.sessionId);
|
|
782
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
783
|
+
}
|
|
784
|
+
/** The only terminal frontend: the Bun-run OpenTUI application. */
|
|
785
|
+
frontendCommand() {
|
|
786
|
+
return {
|
|
787
|
+
program: process.env.XINGULAR_HARNESS_BUN || 'bun',
|
|
788
|
+
// Solid must resolve to its client build for OpenTUI's effects, wherever it is imported.
|
|
789
|
+
argsPrefix: ['--conditions=browser', this.assets.openTui]
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Prepares an OpenTUI terminal session and returns what to spawn. The caller must report the
|
|
794
|
+
* process's end with `dispose`, also when it never starts.
|
|
795
|
+
*/
|
|
796
|
+
async prepare(input) {
|
|
797
|
+
input = structuredClone(input);
|
|
798
|
+
const { env, accounts, apps, bus } = this.options;
|
|
799
|
+
const { root } = env;
|
|
800
|
+
if (!input?.sessionId || !input.cwd || !input.sessionFolder)
|
|
801
|
+
throw invalid('A terminal session needs an id, a working folder, and its own folder.');
|
|
802
|
+
await this.dispose(input.sessionId);
|
|
803
|
+
const sessionId = input.sessionId;
|
|
804
|
+
let acquiredWorktreeId = null;
|
|
805
|
+
let account = null;
|
|
806
|
+
let accountStarted = false;
|
|
807
|
+
let token = null;
|
|
808
|
+
try {
|
|
809
|
+
const worktreeId = input.run?.worktreeId ?? null;
|
|
810
|
+
const worktreePath = worktreeId ? this.options.worktrees?.pathOf(worktreeId) ?? null : null;
|
|
811
|
+
if (worktreeId && worktreePath) {
|
|
812
|
+
await this.options.worktrees.acquire(worktreeId, sessionId);
|
|
813
|
+
acquiredWorktreeId = worktreeId;
|
|
814
|
+
this.worktreeLeases.set(sessionId, worktreeId);
|
|
815
|
+
input.cwd = worktreePath;
|
|
816
|
+
}
|
|
817
|
+
const command = this.frontendCommand();
|
|
818
|
+
// A pinned account is used as it is; it is never exchanged for another one.
|
|
819
|
+
const preparedAccount = input.accountId ? accounts.find(input.accountId) : accounts.resolve(null, null);
|
|
820
|
+
account = preparedAccount;
|
|
821
|
+
const launch = launchEnv(accounts.dir, preparedAccount);
|
|
822
|
+
const loading = (await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, preparedAccount), install: true, caller: { projectId: input.projectId ?? null } })) ?? null;
|
|
823
|
+
await this.socket.listen();
|
|
824
|
+
await accounts.terminalBegin(preparedAccount);
|
|
825
|
+
accountStarted = true;
|
|
826
|
+
const caller = { sessionId, projectId: input.projectId ?? null, actor: 'Code', surface: 'cli' };
|
|
827
|
+
const broker = input.connections === false ? null : apps.grant({ sessionId, runId: null, caller, readOnly: !!input.readOnly, depth: 0 });
|
|
828
|
+
// The session keeps these instructions; each of its runs records a copy, and a memory
|
|
829
|
+
// block that changes later is sent along as a note.
|
|
830
|
+
const variant = input.variant ? materialise(root, input.variant, { trust: this.options.trust ?? null }) : null;
|
|
831
|
+
const browser = new BrowserControl({ root, cwd: input.cwd, pinned: variant,
|
|
832
|
+
writable: Boolean(input.variant && !('dir' in input.variant)), apps, hub: this.options.hub,
|
|
833
|
+
plugins: () => this.options.pluginControl?.list({ cwd: input.cwd, accountId: preparedAccount.id, projectId: input.projectId ?? null }) ?? Promise.resolve([]) });
|
|
834
|
+
if (variant)
|
|
835
|
+
this.variantDirs.set(sessionId, variant.dir);
|
|
836
|
+
const worldTools = await this.worldToolNames(input, preparedAccount);
|
|
837
|
+
const launchTools = [...new Set([...(input.launch?.tools ?? []), CHECKLIST_WRITE, SESSION_NAME, ...worldTools])];
|
|
838
|
+
// The profiles replace the variant's own subagent list, which they include.
|
|
839
|
+
const profiles = this.options.subagents && input.subagents !== null ? profileSection(root, input.cwd, variant?.dir ?? null) : null;
|
|
840
|
+
const launchSections = profiles ? [...(input.launch?.sections ?? []).filter((section) => section.id !== profiles.id), profiles] : input.launch?.sections;
|
|
841
|
+
const pack = compile(env, caller, { ...input.launch, sections: launchSections, tools: launchTools }, variant && profiles ? { ...variant, sections: variant.sections.filter((section) => section.id !== profiles.id) } : variant);
|
|
842
|
+
record(root, pack);
|
|
843
|
+
mkdirSync(this.historyDir(input), { recursive: true });
|
|
844
|
+
saveForSession(input.sessionFolder, pack);
|
|
845
|
+
const live = {
|
|
846
|
+
input,
|
|
847
|
+
account: preparedAccount,
|
|
848
|
+
broker,
|
|
849
|
+
caller,
|
|
850
|
+
token: '',
|
|
851
|
+
controller: new TerminalController({
|
|
852
|
+
sessionId,
|
|
853
|
+
nativeSessionId: input.nativeSessionId ?? null,
|
|
854
|
+
begin: ({ reservedRunId, text }) => this.begin(live, reservedRunId, text),
|
|
855
|
+
policy: (run, tool, args) => this.decide(live, run, tool, args),
|
|
856
|
+
allowToolGlobally: target => { this.options.approvals.allowTool('pi', target); },
|
|
857
|
+
native: (nativeSessionId) => bus.emit({ type: 'terminal.native', sessionId, nativeSessionId }),
|
|
858
|
+
telemetry: (event) => bus.emit({ type: 'terminal.telemetry', sessionId, event }),
|
|
859
|
+
interrupt: runId => this.options.runs.interrupt(runId),
|
|
860
|
+
resolveApproval: (runId, id, decision) => this.options.runs.resolveApproval(runId, id, decision),
|
|
861
|
+
answerQuestion: (runId, id, answer) => this.options.runs.answerQuestion(runId, id, answer),
|
|
862
|
+
answerQuestions: (runId, batchId, answers) => this.options.runs.answerQuestions(runId, batchId, answers)
|
|
863
|
+
})
|
|
864
|
+
};
|
|
865
|
+
token = this.socket.issue({
|
|
866
|
+
sessionId,
|
|
867
|
+
caller: () => ({ ...caller, runId: live.controller.activeRun?.runId ?? null }),
|
|
868
|
+
broker,
|
|
869
|
+
terminal: { accept: (event, data) => live.controller.accept(event, data), disconnected: () => live.controller.disconnected(), control: async (call, params) => {
|
|
870
|
+
if (call === 'terminal.browser.config') {
|
|
871
|
+
const world = this.options.world;
|
|
872
|
+
if (!world)
|
|
873
|
+
throw invalid('Browser settings are unavailable in this host.');
|
|
874
|
+
return { allowPrivate: world.browserConfig().allowPrivate };
|
|
875
|
+
}
|
|
876
|
+
if (call === 'terminal.browser.configure') {
|
|
877
|
+
const world = this.options.world;
|
|
878
|
+
if (!world)
|
|
879
|
+
throw invalid('Browser settings are unavailable in this host.');
|
|
880
|
+
if (typeof params.allowPrivate !== 'boolean')
|
|
881
|
+
throw invalid('allowPrivate must be a boolean.');
|
|
882
|
+
return { allowPrivate: world.configureBrowser({ allowPrivate: params.allowPrivate }).allowPrivate };
|
|
883
|
+
}
|
|
884
|
+
if (call === 'terminal.browser.snapshot')
|
|
885
|
+
return browser.snapshot(params.refresh === true);
|
|
886
|
+
if (call === 'terminal.browser.read') {
|
|
887
|
+
if (typeof params.id !== 'string')
|
|
888
|
+
throw invalid('A resource id is required.');
|
|
889
|
+
return browser.read(params.id, params.latest === true);
|
|
890
|
+
}
|
|
891
|
+
if (call === 'terminal.browser.importSkill') {
|
|
892
|
+
if (typeof params.path !== 'string' || !params.path.trim())
|
|
893
|
+
throw invalid('A skill folder is required.');
|
|
894
|
+
browser.importSkill(params.path);
|
|
895
|
+
bus.emit({ type: 'plugins.changed' });
|
|
896
|
+
return {};
|
|
897
|
+
}
|
|
898
|
+
if (call.startsWith('terminal.browser.connections.'))
|
|
899
|
+
return browser.connection(call.slice('terminal.browser.connections.'.length), params);
|
|
900
|
+
if (call === 'terminal.browser.hubPreview') {
|
|
901
|
+
if (!this.options.hub || typeof params.id !== 'string' || typeof params.version !== 'string')
|
|
902
|
+
throw invalid('A catalog entry and version are required.');
|
|
903
|
+
return this.options.hub.preview(params.id, params.version, typeof params.offset === 'number' ? params.offset : 0);
|
|
904
|
+
}
|
|
905
|
+
if (call === 'terminal.browser.hubDocument') {
|
|
906
|
+
if (!this.options.hub || typeof params.id !== 'string' || typeof params.version !== 'string')
|
|
907
|
+
throw invalid('An installed document and version are required.');
|
|
908
|
+
return this.options.hub.readDocument(params.id, params.version);
|
|
909
|
+
}
|
|
910
|
+
if (call.startsWith('terminal.doctor.')) {
|
|
911
|
+
const doctor = this.options.doctor;
|
|
912
|
+
if (!doctor)
|
|
913
|
+
throw invalid('Diagnostics are unavailable in this host.');
|
|
914
|
+
if (call === 'terminal.doctor.start') {
|
|
915
|
+
const model = typeof params.model === 'string' ? params.model : live.input.model ?? null;
|
|
916
|
+
const tools = [...new Set([
|
|
917
|
+
...(Array.isArray(params.tools) ? params.tools.filter((name) => typeof name === 'string') : []),
|
|
918
|
+
...await this.worldToolNames(live.input, live.account)
|
|
919
|
+
])];
|
|
920
|
+
const variantDir = this.variantDirs.get(sessionId);
|
|
921
|
+
const report = doctor.start({
|
|
922
|
+
context: { caller: live.caller, cwd: live.input.cwd, accountId: live.account.id, model, tools,
|
|
923
|
+
variant: variantDir ? { dir: variantDir, scope: live.input.variant?.scope } : null },
|
|
924
|
+
mode: params.mode === 'live' ? 'live' : 'local',
|
|
925
|
+
checks: Array.isArray(params.checks) ? params.checks.filter((id) => typeof id === 'string') : [],
|
|
926
|
+
confirmed: params.confirmed === true
|
|
927
|
+
});
|
|
928
|
+
this.diagnosticOwners.set(report.id, sessionId);
|
|
929
|
+
return report;
|
|
930
|
+
}
|
|
931
|
+
const id = typeof params.id === 'string' ? params.id : '';
|
|
932
|
+
if (this.diagnosticOwners.get(id) !== sessionId)
|
|
933
|
+
throw invalid('This diagnostic job belongs to another terminal.');
|
|
934
|
+
if (call === 'terminal.doctor.read')
|
|
935
|
+
return doctor.read(id);
|
|
936
|
+
if (call === 'terminal.doctor.cancel')
|
|
937
|
+
return doctor.cancel(id);
|
|
938
|
+
throw invalid('Unknown diagnostic operation.');
|
|
939
|
+
}
|
|
940
|
+
if (call.startsWith('terminal.agents.'))
|
|
941
|
+
return this.agentsCall(live, call, params);
|
|
942
|
+
if (call.startsWith('terminal.subagents.'))
|
|
943
|
+
return this.subagentsCall(live, call, params);
|
|
944
|
+
if (call.startsWith('terminal.sideQuestions.'))
|
|
945
|
+
return this.sideQuestionCall(live, call, params);
|
|
946
|
+
if (call.startsWith('terminal.worktrees.'))
|
|
947
|
+
return this.worktreesCall(live, call, params);
|
|
948
|
+
if (call.startsWith('terminal.annotations.'))
|
|
949
|
+
return this.annotationsCall(live, call, params);
|
|
950
|
+
if (call === 'terminal.logs.info')
|
|
951
|
+
return harnessLogInfo(this.options.stateDir);
|
|
952
|
+
if (call === 'terminal.logs.read') {
|
|
953
|
+
const { snapshotSize, offset } = params;
|
|
954
|
+
if (typeof snapshotSize !== 'number' || !Number.isSafeInteger(snapshotSize) || snapshotSize < 0 || typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < 0 || offset >= snapshotSize) {
|
|
955
|
+
throw invalid('A valid diagnostics snapshot size and byte offset are required.');
|
|
956
|
+
}
|
|
957
|
+
const current = harnessLogInfo(this.options.stateDir).size;
|
|
958
|
+
if (current === null || snapshotSize > current)
|
|
959
|
+
throw invalid('The diagnostics snapshot is no longer available.');
|
|
960
|
+
return readHarnessLogChunk(this.options.stateDir, snapshotSize, offset);
|
|
961
|
+
}
|
|
962
|
+
if (call.startsWith('terminal.components.'))
|
|
963
|
+
return this.componentsCall(live, call, params);
|
|
964
|
+
if (call.startsWith('terminal.trajectory.'))
|
|
965
|
+
return this.trajectoryCall(live, call, params);
|
|
966
|
+
if (call === 'terminal.permissions.get') {
|
|
967
|
+
const policy = this.options.approvals.read();
|
|
968
|
+
return { full: this.options.approvals.isAuto('pi'), globalTools: policy.globalTools.pi ?? [] };
|
|
969
|
+
}
|
|
970
|
+
if (call === 'terminal.permissions.setFull') {
|
|
971
|
+
if (typeof params.enabled !== 'boolean')
|
|
972
|
+
throw invalid('A boolean permission state is required.');
|
|
973
|
+
return { full: this.options.approvals.setAuto('pi', params.enabled).auto.pi === true };
|
|
974
|
+
}
|
|
975
|
+
if (call === 'terminal.permissions.setTool') {
|
|
976
|
+
if (typeof params.tool !== 'string' || !params.tool.trim() || typeof params.allowed !== 'boolean')
|
|
977
|
+
throw invalid('A tool name and boolean permission state are required.');
|
|
978
|
+
const policy = params.allowed
|
|
979
|
+
? this.options.approvals.allowTool('pi', params.tool)
|
|
980
|
+
: this.options.approvals.revokeTool('pi', params.tool);
|
|
981
|
+
return { globalTools: policy.globalTools.pi ?? [] };
|
|
982
|
+
}
|
|
983
|
+
if (call !== 'terminal.resources')
|
|
984
|
+
return live.controller.control(call, params);
|
|
985
|
+
const current = await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, preparedAccount), install: false, caller: { projectId: input.projectId ?? null } }) ?? null;
|
|
986
|
+
if (resourceIdentity(current) !== resourceIdentity(loading))
|
|
987
|
+
throw new HarnessError('terminal_resources_changed', 'Trusted resources changed since this terminal was prepared. Review /plugins and reopen the terminal to load the new selection.');
|
|
988
|
+
return {};
|
|
989
|
+
} }
|
|
990
|
+
});
|
|
991
|
+
live.token = token;
|
|
992
|
+
this.sessions.set(sessionId, live);
|
|
993
|
+
const args = [...command.argsPrefix, '--append-system-prompt', render(env, pack)];
|
|
994
|
+
if (input.model)
|
|
995
|
+
args.push('--model', input.model);
|
|
996
|
+
if (loading) {
|
|
997
|
+
// Pi's own discovery is off: the terminal loads what the harness found and nothing else.
|
|
998
|
+
args.push('--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes');
|
|
999
|
+
for (const path of [...loading.extensionPaths, ...loading.builtins, ...(variant?.extensionPaths ?? [])])
|
|
1000
|
+
args.push('--extension', path);
|
|
1001
|
+
for (const path of [...(variant?.skillPaths ?? []), ...loading.skillPaths])
|
|
1002
|
+
args.push('--skill', path);
|
|
1003
|
+
for (const path of loading.promptPaths)
|
|
1004
|
+
args.push('--prompt-template', path);
|
|
1005
|
+
for (const path of loading.themePaths)
|
|
1006
|
+
args.push('--theme', path);
|
|
1007
|
+
}
|
|
1008
|
+
// Last, so its gate sees a tool call as every other extension left it.
|
|
1009
|
+
args.push('--extension', this.assets.piExtension, '--session-dir', this.historyDir(input));
|
|
1010
|
+
if (input.nativeSessionId)
|
|
1011
|
+
args.push('--session', input.nativeSessionId);
|
|
1012
|
+
return {
|
|
1013
|
+
program: command.program,
|
|
1014
|
+
args,
|
|
1015
|
+
env: {
|
|
1016
|
+
...launch,
|
|
1017
|
+
XINGULAR_HARNESS_NAME: env.productName,
|
|
1018
|
+
...(process.platform === 'win32' ? { XINGULAR_HARNESS_NODE: this.assets.node, XINGULAR_HARNESS_CLI: this.assets.cli } : {}),
|
|
1019
|
+
...prependSearchPath(this.ensureCommands(), searchPath()),
|
|
1020
|
+
[SOCKET_ENV]: this.socket.path,
|
|
1021
|
+
[TOKEN_ENV]: token
|
|
1022
|
+
},
|
|
1023
|
+
accountId: preparedAccount.id,
|
|
1024
|
+
nativeSessionId: input.nativeSessionId ?? null
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
if (token)
|
|
1029
|
+
this.socket.revoke(token);
|
|
1030
|
+
this.sessions.delete(sessionId);
|
|
1031
|
+
this.variantDirs.delete(sessionId);
|
|
1032
|
+
this.worldNames.delete(sessionId);
|
|
1033
|
+
if (this.worktreeLeases.get(sessionId) === acquiredWorktreeId)
|
|
1034
|
+
this.worktreeLeases.delete(sessionId);
|
|
1035
|
+
apps.revokeSession(sessionId);
|
|
1036
|
+
if (accountStarted && account)
|
|
1037
|
+
await accounts.terminalEnd(account).catch(() => undefined);
|
|
1038
|
+
if (acquiredWorktreeId)
|
|
1039
|
+
await this.options.worktrees?.release(acquiredWorktreeId, sessionId).catch(() => undefined);
|
|
1040
|
+
throw error;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
/** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
|
|
1044
|
+
async send(input) {
|
|
1045
|
+
const { env } = this.options;
|
|
1046
|
+
const { root } = env;
|
|
1047
|
+
if (!input.text?.trim())
|
|
1048
|
+
throw invalid('Message is empty.');
|
|
1049
|
+
const live = this.live(input.sessionId);
|
|
1050
|
+
if (this.options.runs.activeForSession(input.sessionId) || live.controller.state().active)
|
|
1051
|
+
throw new HarnessError('session_busy', 'This session is still working.');
|
|
1052
|
+
const runId = input.runId || newId();
|
|
1053
|
+
if (findRun(root, runId))
|
|
1054
|
+
throw invalid('This run already exists.');
|
|
1055
|
+
saveRun(root, this.runRecord(live, runId, input.text));
|
|
1056
|
+
copyToRun(root, live.input.sessionFolder, runId);
|
|
1057
|
+
try {
|
|
1058
|
+
await live.controller.send(runId, input.text);
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
updateRun(root, runId, (run) => {
|
|
1062
|
+
if (!isActiveStatus(run.status))
|
|
1063
|
+
return;
|
|
1064
|
+
run.status = 'failed';
|
|
1065
|
+
run.endedAt = now();
|
|
1066
|
+
});
|
|
1067
|
+
throw error;
|
|
1068
|
+
}
|
|
1069
|
+
return { runId };
|
|
1070
|
+
}
|
|
1071
|
+
/** What the person typed into the terminal. */
|
|
1072
|
+
input(sessionId, data) {
|
|
1073
|
+
this.sessions.get(sessionId)?.controller.onInput(data);
|
|
1074
|
+
}
|
|
1075
|
+
state(sessionId) {
|
|
1076
|
+
return this.live(sessionId).controller.state();
|
|
1077
|
+
}
|
|
1078
|
+
has(sessionId) {
|
|
1079
|
+
return this.sessions.has(sessionId);
|
|
1080
|
+
}
|
|
1081
|
+
/** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
|
|
1082
|
+
async dispose(sessionId) {
|
|
1083
|
+
const live = this.sessions.get(sessionId);
|
|
1084
|
+
if (!live)
|
|
1085
|
+
return;
|
|
1086
|
+
for (const [id, owner] of this.diagnosticOwners)
|
|
1087
|
+
if (owner === sessionId) {
|
|
1088
|
+
this.options.doctor?.cancel(id);
|
|
1089
|
+
this.diagnosticOwners.delete(id);
|
|
1090
|
+
}
|
|
1091
|
+
this.sessions.delete(sessionId);
|
|
1092
|
+
this.worldNames.delete(sessionId);
|
|
1093
|
+
const worktreeId = this.worktreeLeases.get(sessionId);
|
|
1094
|
+
this.worktreeLeases.delete(sessionId);
|
|
1095
|
+
if (worktreeId)
|
|
1096
|
+
await this.options.worktrees?.release(worktreeId, sessionId).catch(() => undefined);
|
|
1097
|
+
void this.options.noteCapture?.release(sessionId).catch(() => undefined);
|
|
1098
|
+
void this.options.world?.release(sessionId).catch(() => undefined);
|
|
1099
|
+
live.controller.dispose();
|
|
1100
|
+
this.socket.revoke(live.token);
|
|
1101
|
+
this.options.apps.revokeSession(sessionId);
|
|
1102
|
+
await this.options.accounts.terminalEnd(live.account).catch((error) => log('terminal credentials', error instanceof Error ? error.message : String(error)));
|
|
1103
|
+
}
|
|
1104
|
+
async close() {
|
|
1105
|
+
await Promise.allSettled([...this.sessions.keys()].map((sessionId) => this.dispose(sessionId)));
|
|
1106
|
+
await this.socket.close();
|
|
1107
|
+
}
|
|
1108
|
+
}
|