@xingular/harness 0.1.7 → 1.0.0
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 +97 -1
- package/LICENSE +202 -0
- package/NOTICE.md +22 -0
- package/README.md +187 -85
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +13701 -13498
- 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/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 +17 -0
- package/dist/sdk/browser/index.js +65 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/session.d.ts +84 -0
- package/dist/sdk/browser/session.js +192 -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 +142 -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/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 +133 -0
- package/dist/sdk/harness.js +299 -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/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/index.d.ts +63 -0
- package/dist/sdk/index.js +48 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +669 -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/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 +6 -0
- package/dist/sdk/plugins/builtin.js +68 -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 +187 -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 +174 -0
- package/dist/sdk/prompt/compile.js +396 -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/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 +370 -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 +534 -0
- package/dist/sdk/sessions/contracts.js +299 -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 +36 -0
- package/dist/sdk/sessions/planMode.js +84 -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 +102 -0
- package/dist/sdk/sessions/registry.js +383 -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 +335 -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 +140 -0
- package/dist/sdk/sessions/sessions.js +394 -0
- package/dist/sdk/sessions/subagentService.d.ts +41 -0
- package/dist/sdk/sessions/subagentService.js +247 -0
- package/dist/sdk/sessions/subagents.d.ts +50 -0
- package/dist/sdk/sessions/subagents.js +68 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -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/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +72 -0
- package/dist/sdk/terminal/controller.js +351 -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/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +392 -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 +70 -0
- package/dist/sdk/terminal/socket.js +256 -0
- package/dist/sdk/terminal/standalone.d.ts +18 -0
- package/dist/sdk/terminal/standalone.js +146 -0
- package/dist/sdk/terminal/terminals.d.ts +176 -0
- package/dist/sdk/terminal/terminals.js +767 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -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 +7 -0
- package/dist/sdk/terminal/tui/commandPages.js +63 -0
- package/dist/sdk/terminal/tui/commands.d.ts +42 -0
- package/dist/sdk/terminal/tui/commands.js +276 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +50 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +75 -0
- package/dist/sdk/terminal/tui/dialogs.js +363 -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 +155 -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 +14 -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 +145 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +54 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +181 -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 +37 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +145 -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/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 +55 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +88 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +58 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +377 -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/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +19 -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 +441 -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 +12 -0
- package/dist/sdk/terminal/tui/settingsModel.js +84 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +197 -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 +47 -0
- package/dist/sdk/terminal/tui/ui.js +148 -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 +74 -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 +6 -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 +49 -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/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/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/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 +65 -0
- package/dist/sdk/world/world.js +80 -0
- package/dist/terminal-opentui.js +13612 -0
- package/dist/terminal-pi.js +808 -151
- package/docs/README.md +22 -0
- package/docs/architecture/00-overview.md +159 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +146 -0
- package/docs/architecture/03-variants.md +95 -0
- package/docs/architecture/04-prompt.md +102 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +93 -0
- package/docs/architecture/07-tools-and-gate.md +88 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +90 -0
- package/docs/architecture/10-server-protocol.md +84 -0
- package/docs/architecture/11-terminal.md +81 -0
- package/docs/architecture/12-storage.md +61 -0
- package/docs/architecture/13-world-tools.md +23 -0
- package/docs/architecture/README.md +114 -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 +68 -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/README.md +76 -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 +38 -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 +21 -0
- package/docs/doctor.md +74 -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 +20 -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/releases/1.0.0.md +40 -0
- package/docs/releases/README.md +36 -0
- package/docs/study/00-index.md +78 -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/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -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/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +473 -56
- 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,767 @@
|
|
|
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 { SESSION_NAME } from "../tools/sessionName.js";
|
|
25
|
+
import { contributionId } from "../plugins/identity.js";
|
|
26
|
+
import { APPS_COMMAND, WIKI_COMMAND } from "./cli.js";
|
|
27
|
+
import { SOCKET_ENV, TOKEN_ENV } from "./client.js";
|
|
28
|
+
import { TerminalController } from "./controller.js";
|
|
29
|
+
import { terminalCommandFiles } from "./launch.js";
|
|
30
|
+
import { SessionSocket, socketPath } from "./socket.js";
|
|
31
|
+
import { harnessLogInfo, readHarnessLogChunk } from "./logs.js";
|
|
32
|
+
import { listTerminalTrajectoryRuns, readTerminalTrajectory } from "./trajectory.js";
|
|
33
|
+
/** Identity of the exact resource decision that this terminal was prepared with. */
|
|
34
|
+
function resourceIdentity(loading) {
|
|
35
|
+
if (!loading)
|
|
36
|
+
return 'null';
|
|
37
|
+
return JSON.stringify({
|
|
38
|
+
extensions: loading.extensionPaths, builtins: loading.builtins, skills: loading.skillPaths,
|
|
39
|
+
prompts: loading.promptPaths, themes: loading.themePaths, detached: loading.detached,
|
|
40
|
+
project: loading.projectCode, packages: loading.packageCode
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Where the package's own build puts them. An application that bundles the harness passes its own. */
|
|
44
|
+
export function defaultAssets() {
|
|
45
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
const dist = [here, join(here, '..', '..'), join(here, '..', '..', 'dist'), join(here, '..', 'dist')].find((folder) => existsSync(join(folder, 'terminal-pi.js'))) ?? here;
|
|
47
|
+
return {
|
|
48
|
+
node: process.execPath,
|
|
49
|
+
cli: join(dist, 'cli.js'),
|
|
50
|
+
piExtension: join(dist, 'terminal-pi.js'),
|
|
51
|
+
openTui: join(dist, 'terminal-opentui.js')
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const READ_ONLY_TOOLS = new Set(['read', 'grep', 'find', 'ls']);
|
|
55
|
+
export class Terminals {
|
|
56
|
+
options;
|
|
57
|
+
sessions = new Map();
|
|
58
|
+
diagnosticOwners = new Map();
|
|
59
|
+
/** The variant each live session runs, for loading its skills. */
|
|
60
|
+
variantDirs = new Map();
|
|
61
|
+
/** The world tools each session was given, so a call can only reach one of them. */
|
|
62
|
+
worldNames = new Map();
|
|
63
|
+
worldReadOnly = new Set();
|
|
64
|
+
socket;
|
|
65
|
+
assets;
|
|
66
|
+
/** A session's `/accounts` switch, taken once its Pi process has ended. */
|
|
67
|
+
relaunches = new Map();
|
|
68
|
+
constructor(options) {
|
|
69
|
+
this.options = options;
|
|
70
|
+
this.assets = options.assets ?? defaultAssets();
|
|
71
|
+
options.runs.listen(event => {
|
|
72
|
+
this.sessions.get(event.sessionId)?.controller.observe(event);
|
|
73
|
+
const record = findRun(options.env.root, event.runId);
|
|
74
|
+
if (record?.relation !== 'subagent')
|
|
75
|
+
return;
|
|
76
|
+
let parentId = record.parentRunId;
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
while (parentId && !seen.has(parentId)) {
|
|
79
|
+
seen.add(parentId);
|
|
80
|
+
const parent = findRun(options.env.root, parentId);
|
|
81
|
+
if (!parent)
|
|
82
|
+
break;
|
|
83
|
+
const owner = this.sessions.get(parent.sessionId);
|
|
84
|
+
if (owner?.controller.activeRun?.runId === parent.id) {
|
|
85
|
+
owner.controller.observe(event, true);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
parentId = parent.parentRunId;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
this.socket = new SessionSocket(socketPath(options.stateDir), {
|
|
92
|
+
memory: (caller, call, params) => dispatch(options.env, caller, call, params, { variantDir: (caller.sessionId && this.variantDirs.get(caller.sessionId)) || null }),
|
|
93
|
+
apps: (session, call, params) => this.appsCall(session, call, params),
|
|
94
|
+
plugins: (grant, call, params, control) => this.pluginsCall(grant.sessionId, call, params, control),
|
|
95
|
+
accounts: (grant, call, params, control) => this.accountsCall(grant.sessionId, call, params, control)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/** The commands a terminal session finds on PATH. Rewritten only when they change. */
|
|
99
|
+
ensureCommands() {
|
|
100
|
+
const bin = join(this.options.stateDir, 'run', 'bin');
|
|
101
|
+
mkdirSync(bin, { recursive: true });
|
|
102
|
+
for (const name of ['agentx-wiki', 'agentx-mcp', 'agentx-wiki.cmd', 'agentx-mcp.cmd']) {
|
|
103
|
+
const path = join(bin, name);
|
|
104
|
+
try {
|
|
105
|
+
if (lstatSync(path).isFile())
|
|
106
|
+
unlinkSync(path);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// These names belonged to the previous generated wrappers.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const [name, mode] of [[WIKI_COMMAND, 'wiki'], [APPS_COMMAND, 'mcp']]) {
|
|
113
|
+
for (const file of terminalCommandFiles(process.platform, this.assets.node, this.assets.cli, name, mode)) {
|
|
114
|
+
const path = join(bin, file.name);
|
|
115
|
+
let current = null;
|
|
116
|
+
try {
|
|
117
|
+
current = readFileSync(path, 'utf8');
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
current = null;
|
|
121
|
+
}
|
|
122
|
+
if (current !== file.content) {
|
|
123
|
+
writeFileSync(path, file.content);
|
|
124
|
+
chmodSync(path, 0o755);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return bin;
|
|
129
|
+
}
|
|
130
|
+
/** The broker's tools, then the world tools whose plugins are on for this session. */
|
|
131
|
+
async tools(session, listing) {
|
|
132
|
+
const brokered = this.options.apps.brokerTools(session);
|
|
133
|
+
const live = this.sessions.get(session.sessionId);
|
|
134
|
+
const { world } = this.options;
|
|
135
|
+
if (!world || !live)
|
|
136
|
+
return brokered;
|
|
137
|
+
const context = { run: live.controller.activeRun, caller: live.caller, attended: true, subagent: false, sessionKey: session.sessionId, cwd: live.input.cwd };
|
|
138
|
+
if (!listing)
|
|
139
|
+
return [...brokered, ...world.tools(context, (_plugin, tool) => this.worldNames.get(session.sessionId)?.has(tool) ?? false)];
|
|
140
|
+
const on = await this.worldOn(live);
|
|
141
|
+
const tools = world.tools(context, (plugin, tool) => on(plugin, tool));
|
|
142
|
+
this.worldNames.set(session.sessionId, new Set(tools.map((tool) => tool.name)));
|
|
143
|
+
for (const tool of tools)
|
|
144
|
+
if (tool.readOnly)
|
|
145
|
+
this.worldReadOnly.add(tool.name);
|
|
146
|
+
return [...brokered, ...tools];
|
|
147
|
+
}
|
|
148
|
+
/** The world tools a new session will have, named in its instructions. */
|
|
149
|
+
async worldToolNames(input, account) {
|
|
150
|
+
const { world } = this.options;
|
|
151
|
+
if (!world || input.connections === false)
|
|
152
|
+
return [];
|
|
153
|
+
const list = this.options.pluginControl?.list;
|
|
154
|
+
const plugins = list ? await list({ cwd: input.cwd, accountId: account.id, projectId: input.projectId ?? null }) : null;
|
|
155
|
+
const on = plugins ? new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id))) : null;
|
|
156
|
+
return world.plugins.flatMap((plugin) => (plugin.tools ?? []).map((tool) => tool.name).filter((name) => !on || on.has(contributionId(plugin.id, 'tool', name))));
|
|
157
|
+
}
|
|
158
|
+
/** Whether a world tool's plugin is switched on for a session's folder, account and project. */
|
|
159
|
+
async worldOn(live) {
|
|
160
|
+
const list = this.options.pluginControl?.list;
|
|
161
|
+
if (!list)
|
|
162
|
+
return () => true;
|
|
163
|
+
const plugins = await list({ cwd: live.input.cwd, accountId: live.account.id, projectId: live.input.projectId ?? null });
|
|
164
|
+
const on = new Set(plugins.flatMap((plugin) => plugin.contributions.filter((item) => item.state === 'on').map((item) => item.id)));
|
|
165
|
+
return (plugin, tool) => on.has(contributionId(plugin, 'tool', tool));
|
|
166
|
+
}
|
|
167
|
+
async appsCall(session, call, params) {
|
|
168
|
+
const { apps } = this.options;
|
|
169
|
+
switch (call) {
|
|
170
|
+
case 'mcp.list':
|
|
171
|
+
return apps.list(session);
|
|
172
|
+
case 'mcp.call':
|
|
173
|
+
return apps.invoke(session, String(params.name ?? ''), (params.args && typeof params.args === 'object' ? params.args : {}));
|
|
174
|
+
case 'mcp.revision':
|
|
175
|
+
return apps.revision(session);
|
|
176
|
+
case 'tools.list': {
|
|
177
|
+
const { Type } = await import('typebox');
|
|
178
|
+
return { tools: (await this.tools(session, true)).map((tool) => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
|
|
179
|
+
}
|
|
180
|
+
case 'tools.call': {
|
|
181
|
+
const tool = (await this.tools(session, false)).find((candidate) => candidate.name === params.name);
|
|
182
|
+
if (!tool)
|
|
183
|
+
throw new HarnessError('unknown_method', `Unknown tool ${String(params.name)}.`);
|
|
184
|
+
const result = await tool.execute((params.input && typeof params.input === 'object' ? params.input : {}));
|
|
185
|
+
if (typeof result === 'string')
|
|
186
|
+
return { text: result };
|
|
187
|
+
const images = [...(result.image ? [result.image] : []), ...(result.images ?? [])];
|
|
188
|
+
return { text: result.text, ...(images.length ? { images } : {}), ...(result.details ? { details: result.details } : {}) };
|
|
189
|
+
}
|
|
190
|
+
default:
|
|
191
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
|
|
196
|
+
* session's own extension, which asks the person in the terminal first: the model can reach
|
|
197
|
+
* this socket from a shell command, and must not be able to trust code or switch it on.
|
|
198
|
+
*/
|
|
199
|
+
async pluginsCall(sessionId, call, params, control) {
|
|
200
|
+
const api = this.options.pluginControl;
|
|
201
|
+
const live = this.sessions.get(sessionId);
|
|
202
|
+
if (!api || !live)
|
|
203
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
204
|
+
const projectId = live.input.projectId ?? null;
|
|
205
|
+
const scope = { cwd: live.input.cwd, accountId: live.account.id, projectId };
|
|
206
|
+
if (call === 'hub.list' && this.options.hub)
|
|
207
|
+
return this.options.hub.list(params.refresh === true);
|
|
208
|
+
if (call === 'hub.install' && this.options.hub) {
|
|
209
|
+
if (!control)
|
|
210
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can install items. Type /hub.');
|
|
211
|
+
if (typeof params.id !== 'string' || typeof params.version !== 'string')
|
|
212
|
+
throw invalid('An item id and version are required.');
|
|
213
|
+
return this.options.hub.install(params.id, params.version);
|
|
214
|
+
}
|
|
215
|
+
if (call === 'plugins.list')
|
|
216
|
+
return api.list(scope);
|
|
217
|
+
if (call === 'plugins.session')
|
|
218
|
+
return { projectId };
|
|
219
|
+
if (!control)
|
|
220
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can change plugins. Type /plugins.');
|
|
221
|
+
const text = (name) => {
|
|
222
|
+
if (typeof params[name] !== 'string' || !params[name])
|
|
223
|
+
throw invalid(`${name} is required.`);
|
|
224
|
+
return params[name];
|
|
225
|
+
};
|
|
226
|
+
switch (call) {
|
|
227
|
+
case 'plugins.attach':
|
|
228
|
+
case 'plugins.detach':
|
|
229
|
+
return api.attach({ ...scope, id: text('id'), scope: params.everywhere === true || !projectId ? {} : { projectId } }, call === 'plugins.attach');
|
|
230
|
+
case 'plugins.trust':
|
|
231
|
+
return api.trust({ ...scope, hash: text('hash') });
|
|
232
|
+
case 'plugins.untrust':
|
|
233
|
+
return api.untrust({ ...scope, hash: text('hash') });
|
|
234
|
+
case 'plugins.configure': {
|
|
235
|
+
const settings = params.settings && typeof params.settings === 'object' && !Array.isArray(params.settings) ? params.settings : {};
|
|
236
|
+
return api.configure({ ...scope, pluginId: text('pluginId'), settings });
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
|
|
244
|
+
* or removing an account is a person's choice, never the model's shell command.
|
|
245
|
+
*/
|
|
246
|
+
async accountsCall(sessionId, call, params, control) {
|
|
247
|
+
const { accounts } = this.options;
|
|
248
|
+
const live = this.sessions.get(sessionId);
|
|
249
|
+
if (!live)
|
|
250
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
251
|
+
if (!control)
|
|
252
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can manage accounts. Type /accounts.');
|
|
253
|
+
const text = (name) => {
|
|
254
|
+
if (typeof params[name] !== 'string' || !params[name])
|
|
255
|
+
throw invalid(`${name} is required.`);
|
|
256
|
+
return params[name];
|
|
257
|
+
};
|
|
258
|
+
const optional = (name) => (typeof params[name] === 'string' ? params[name] : null);
|
|
259
|
+
switch (call) {
|
|
260
|
+
case 'accounts.deviceId':
|
|
261
|
+
return accounts.installationDeviceId();
|
|
262
|
+
case 'accounts.list':
|
|
263
|
+
return accounts.overview();
|
|
264
|
+
case 'accounts.session':
|
|
265
|
+
return { accountId: live.account.id, label: live.account.label };
|
|
266
|
+
case 'accounts.create':
|
|
267
|
+
return accounts.create(text('label'), optional('authKind') ?? 'subscription');
|
|
268
|
+
case 'accounts.rename':
|
|
269
|
+
return accounts.rename(text('accountId'), text('label'));
|
|
270
|
+
case 'accounts.setDefault':
|
|
271
|
+
return accounts.setDefault(text('accountId'));
|
|
272
|
+
case 'accounts.remove': {
|
|
273
|
+
const accountId = text('accountId');
|
|
274
|
+
// Every terminal in this process counts, this session's own included. Another process's terminals are not visible here.
|
|
275
|
+
if (accounts.terminalCount(accountId) > 0)
|
|
276
|
+
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.');
|
|
277
|
+
return accounts.remove(accountId);
|
|
278
|
+
}
|
|
279
|
+
case 'accounts.refresh':
|
|
280
|
+
return accounts.refresh(optional('accountId'));
|
|
281
|
+
case 'accounts.providers':
|
|
282
|
+
return accounts.providers();
|
|
283
|
+
// A terminal reads and writes only its own account's choices, never the id a caller names.
|
|
284
|
+
case 'accounts.choices.get':
|
|
285
|
+
return accounts.choices(live.account.id);
|
|
286
|
+
case 'accounts.choices.set':
|
|
287
|
+
return accounts.choose(live.account.id, text('mode'), {
|
|
288
|
+
...(typeof params.model === 'string' ? { model: params.model } : {}),
|
|
289
|
+
...(typeof params.effort === 'string' ? { effort: params.effort } : {})
|
|
290
|
+
});
|
|
291
|
+
case 'accounts.loginStart':
|
|
292
|
+
return accounts.loginStart(text('accountId'), text('provider'), optional('method'));
|
|
293
|
+
case 'accounts.loginStatus':
|
|
294
|
+
return accounts.loginStatus(text('accountId'));
|
|
295
|
+
case 'accounts.loginRespond':
|
|
296
|
+
accounts.loginRespond(text('loginId'), text('promptId'), optional('value'));
|
|
297
|
+
return {};
|
|
298
|
+
case 'accounts.loginCancel':
|
|
299
|
+
accounts.loginCancel(text('accountId'), optional('loginId'));
|
|
300
|
+
return {};
|
|
301
|
+
case 'accounts.setApiKey':
|
|
302
|
+
return accounts.setApiKey(text('accountId'), text('key'), text('provider'));
|
|
303
|
+
case 'accounts.removeProvider':
|
|
304
|
+
return accounts.removeProvider(text('accountId'), text('provider'));
|
|
305
|
+
case 'accounts.signOut':
|
|
306
|
+
return accounts.signOut(text('accountId'));
|
|
307
|
+
case 'accounts.savePool': {
|
|
308
|
+
const pool = params.pool;
|
|
309
|
+
if (!pool || typeof pool.label !== 'string' || !Array.isArray(pool.accountIds) || !pool.accountIds.every((id) => typeof id === 'string'))
|
|
310
|
+
throw invalid('A pool needs a label and its account ids.');
|
|
311
|
+
return accounts.savePool({ id: typeof pool.id === 'string' ? pool.id : undefined, label: pool.label, accountIds: pool.accountIds });
|
|
312
|
+
}
|
|
313
|
+
case 'accounts.removePool':
|
|
314
|
+
return accounts.removePool(text('poolId'));
|
|
315
|
+
case 'accounts.audit':
|
|
316
|
+
return readAudit(accounts.dir, optional('accountId'), typeof params.limit === 'number' ? params.limit : 100);
|
|
317
|
+
case 'accounts.relaunch': {
|
|
318
|
+
// Checked here so a bad choice is refused before the session is asked to end.
|
|
319
|
+
const target = accounts.resolve(text('accountId'));
|
|
320
|
+
this.relaunches.set(sessionId, { accountId: target.id, nativeSessionId: live.controller.nativeSessionId });
|
|
321
|
+
return { accountId: target.id, label: target.label };
|
|
322
|
+
}
|
|
323
|
+
default:
|
|
324
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
|
|
328
|
+
takeRelaunch(sessionId) {
|
|
329
|
+
const request = this.relaunches.get(sessionId) ?? null;
|
|
330
|
+
this.relaunches.delete(sessionId);
|
|
331
|
+
return request;
|
|
332
|
+
}
|
|
333
|
+
live(sessionId) {
|
|
334
|
+
const live = this.sessions.get(sessionId);
|
|
335
|
+
if (!live)
|
|
336
|
+
throw new HarnessError('terminal_not_ready', 'The terminal controller is unavailable. Reopen this session.');
|
|
337
|
+
return live;
|
|
338
|
+
}
|
|
339
|
+
historyDir(input) {
|
|
340
|
+
return join(input.sessionFolder, 'pi');
|
|
341
|
+
}
|
|
342
|
+
runRecord(live, runId, text) {
|
|
343
|
+
const { input } = live;
|
|
344
|
+
return {
|
|
345
|
+
resumedFromRunId: null,
|
|
346
|
+
parentRunId: null,
|
|
347
|
+
relation: null,
|
|
348
|
+
terminalReason: null,
|
|
349
|
+
agentId: null,
|
|
350
|
+
trigger: null,
|
|
351
|
+
id: runId,
|
|
352
|
+
projectId: input.projectId ?? null,
|
|
353
|
+
mode: input.run?.mode ?? 'code',
|
|
354
|
+
harness: 'pi',
|
|
355
|
+
sessionType: 'terminal',
|
|
356
|
+
sessionId: input.sessionId,
|
|
357
|
+
nativeSessionId: live.controller.nativeSessionId,
|
|
358
|
+
workingDirectory: input.cwd,
|
|
359
|
+
worktreeId: input.run?.worktreeId ?? null,
|
|
360
|
+
goal: text,
|
|
361
|
+
attachmentCount: 0,
|
|
362
|
+
startedAt: now(),
|
|
363
|
+
endedAt: null,
|
|
364
|
+
status: 'queued',
|
|
365
|
+
exitCode: null,
|
|
366
|
+
handoffId: null,
|
|
367
|
+
accountId: live.account.id
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
|
|
371
|
+
begin(live, reservedRunId, text) {
|
|
372
|
+
const { root } = this.options.env;
|
|
373
|
+
const { sessionId } = live.input;
|
|
374
|
+
if (this.options.runs.activeForSession(sessionId))
|
|
375
|
+
throw new HarnessError('session_busy', 'A native turn already owns this session.');
|
|
376
|
+
let runId = reservedRunId;
|
|
377
|
+
if (runId) {
|
|
378
|
+
const reserved = loadRun(root, runId);
|
|
379
|
+
if (reserved.sessionId !== sessionId || reserved.status !== 'queued')
|
|
380
|
+
throw invalid('Delivery does not own this queued run.');
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
runId = newId();
|
|
384
|
+
saveRun(root, this.runRecord(live, runId, text || null));
|
|
385
|
+
copyToRun(root, live.input.sessionFolder, runId);
|
|
386
|
+
}
|
|
387
|
+
return this.options.runs.start(sessionId, runId);
|
|
388
|
+
}
|
|
389
|
+
async decide(live, run, tool, args) {
|
|
390
|
+
const record = loadRun(this.options.env.root, run.runId);
|
|
391
|
+
if (!isActiveStatus(record.status))
|
|
392
|
+
throw invalid(`The run is no longer active (its status is ${record.status}).`);
|
|
393
|
+
const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args };
|
|
394
|
+
const { policy } = this.options;
|
|
395
|
+
if (policy.scope) {
|
|
396
|
+
const scope = await policy.scope(call);
|
|
397
|
+
if (!scope.allowed && (scope.automatic || !scope.requiresApproval))
|
|
398
|
+
return { allowed: false, reason: scope.reason ?? 'Outside what this session may do.' };
|
|
399
|
+
try {
|
|
400
|
+
await policy.authorize?.(call);
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
return { allowed: false, reason: error instanceof Error ? error.message : 'Tool unavailable in the settings.' };
|
|
404
|
+
}
|
|
405
|
+
const requiresApproval = !!scope.requiresApproval || (scope.allowed && !!scope.humanOnly);
|
|
406
|
+
return {
|
|
407
|
+
allowed: scope.allowed,
|
|
408
|
+
requiresApproval,
|
|
409
|
+
reason: scope.reason ?? null,
|
|
410
|
+
rememberable: scope.allowed && !scope.humanOnly,
|
|
411
|
+
...(scope.humanOnly ? { standing: scope.allowGlobalApproval ? 'switch' : 'none' } : {})
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
// With no application policy: reads run, a read-only session refuses the rest, and anything else asks.
|
|
415
|
+
const name = tool.toLowerCase();
|
|
416
|
+
if (name === SESSION_NAME || name === SPAWN_SUBAGENT || name === WAIT_SUBAGENTS || READ_ONLY_TOOLS.has(name) || this.worldReadOnly.has(name) || this.options.apps.toolNames.includes(name))
|
|
417
|
+
return { allowed: true };
|
|
418
|
+
if (live.input.readOnly)
|
|
419
|
+
return { allowed: false, reason: 'This session is read-only.' };
|
|
420
|
+
return { allowed: false, requiresApproval: true, rememberable: true, reason: 'This native action requires approval.' };
|
|
421
|
+
}
|
|
422
|
+
async subagentsCall(live, call, params) {
|
|
423
|
+
const service = this.options.subagents;
|
|
424
|
+
if (!service || live.input.subagents === null) {
|
|
425
|
+
if (call === 'terminal.subagents.tools')
|
|
426
|
+
return { tools: [] };
|
|
427
|
+
throw invalid('Subagents are disabled for this terminal.');
|
|
428
|
+
}
|
|
429
|
+
if (call === 'terminal.subagents.tools')
|
|
430
|
+
return { tools: subagentTools(service, '').map(tool => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) };
|
|
431
|
+
const run = live.controller.activeRun;
|
|
432
|
+
if (!run)
|
|
433
|
+
throw new HarnessError('run_not_active', 'This terminal has no active parent run.');
|
|
434
|
+
if (call === 'terminal.subagents.register') {
|
|
435
|
+
if (!Array.isArray(params.tools) || params.tools.some((name) => typeof name !== 'string'))
|
|
436
|
+
throw invalid('Native tool names are required.');
|
|
437
|
+
const readOnly = live.input.readOnly === true;
|
|
438
|
+
const readTools = new Set([...READ_ONLY_TOOLS, SESSION_NAME, 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']);
|
|
439
|
+
const tools = params.tools.filter((name) => !readOnly || readTools.has(name));
|
|
440
|
+
const capabilities = this.options.resolveCapabilities ? await this.options.resolveCapabilities(live.caller, tools, live.input.cwd) : { version: 1, tools, connectors: [], contributions: [] };
|
|
441
|
+
freezeRun(this.options.env.root, run.runId, live.caller, capabilities);
|
|
442
|
+
service.register({
|
|
443
|
+
run: { id: run.runId, sessionId: live.input.sessionId, mode: live.input.run?.mode ?? 'code', projectId: live.input.projectId, worktreeId: live.input.run?.worktreeId },
|
|
444
|
+
cwd: live.input.cwd, sessionDir: this.historyDir(live.input), text: '', caller: live.caller,
|
|
445
|
+
model: typeof params.model === 'string' ? params.model : live.input.model,
|
|
446
|
+
effort: typeof params.effort === 'string' ? params.effort : null,
|
|
447
|
+
launch: live.input.launch, tools: { allow: capabilities.tools, gated: capabilities.tools.filter(name => !readTools.has(name)) },
|
|
448
|
+
connections: live.input.connections === false ? false : { readOnly }, subagents: live.input.subagents,
|
|
449
|
+
capabilities
|
|
450
|
+
}, live.account.id, capabilities);
|
|
451
|
+
service.restore(run.runId, params.history ?? { owners: [], receipts: [] });
|
|
452
|
+
return { runId: run.runId };
|
|
453
|
+
}
|
|
454
|
+
if (call === 'terminal.subagents.verify') {
|
|
455
|
+
await service.verify(run.runId, params.history);
|
|
456
|
+
return {};
|
|
457
|
+
}
|
|
458
|
+
if (call === 'terminal.subagents.delivery')
|
|
459
|
+
return service.delivery(run.runId, params.history, params.settle === true);
|
|
460
|
+
if (call === 'terminal.subagents.call') {
|
|
461
|
+
const tool = subagentTools(service, run.runId).find(tool => tool.name === params.name);
|
|
462
|
+
if (!tool)
|
|
463
|
+
throw invalid('Unknown subagent tool.');
|
|
464
|
+
const reason = await this.decide(live, run, tool.name, params.input ?? {});
|
|
465
|
+
if (!reason.allowed)
|
|
466
|
+
throw new HarnessError('tool_denied', reason.reason ?? 'Subagents are unavailable.');
|
|
467
|
+
const result = await tool.execute(params.input ?? {});
|
|
468
|
+
return typeof result === 'string' ? { text: result } : result;
|
|
469
|
+
}
|
|
470
|
+
throw invalid('Unknown subagent operation.');
|
|
471
|
+
}
|
|
472
|
+
componentsCall(live, call, params) {
|
|
473
|
+
const root = this.options.env.root;
|
|
474
|
+
const choice = live.input.variant;
|
|
475
|
+
const scope = choice && 'scope' in choice ? choice.scope ?? null : null;
|
|
476
|
+
const head = scope ? variantStore.headVariant(root, scope) : null;
|
|
477
|
+
const items = componentLibrary.list(root);
|
|
478
|
+
if (call === 'terminal.components.list') {
|
|
479
|
+
return {
|
|
480
|
+
items,
|
|
481
|
+
addedIds: head ? items.filter(item => componentLibrary.isInVariant(root, head.variant, item.id)).map(item => item.id) : [],
|
|
482
|
+
writable: Boolean(scope && head),
|
|
483
|
+
fingerprint: head?.fingerprint ?? null
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
if (call === 'terminal.components.add') {
|
|
487
|
+
if (typeof params.id !== 'string' || !params.id)
|
|
488
|
+
throw invalid('A component id is required.');
|
|
489
|
+
if (!scope || !head)
|
|
490
|
+
throw invalid('This terminal has no writable Harness scope.');
|
|
491
|
+
const item = items.find(candidate => candidate.id === params.id);
|
|
492
|
+
if (!item)
|
|
493
|
+
throw invalid('That component is no longer in the library.');
|
|
494
|
+
if (item.kind === 'prompts')
|
|
495
|
+
throw invalid('Prompts stay in the library and cannot be added to a Variant.');
|
|
496
|
+
if (componentLibrary.isInVariant(root, head.variant, item.id)) {
|
|
497
|
+
return { fingerprint: head.fingerprint, alreadyPresent: true };
|
|
498
|
+
}
|
|
499
|
+
const variant = componentLibrary.addToVariant(root, head.variant, item.id);
|
|
500
|
+
const record = variantStore.commit(root, scope, variant, {
|
|
501
|
+
kind: 'edit', note: 'Added library component ' + item.id, components: [item.id]
|
|
502
|
+
});
|
|
503
|
+
this.options.bus.emit({ type: 'variants.changed', scope });
|
|
504
|
+
return { fingerprint: record.fingerprint, alreadyPresent: false };
|
|
505
|
+
}
|
|
506
|
+
throw new HarnessError('unknown_method', 'Unknown terminal component operation.');
|
|
507
|
+
}
|
|
508
|
+
trajectoryCall(live, call, params) {
|
|
509
|
+
if (call === 'terminal.trajectory.list') {
|
|
510
|
+
return { runs: listTerminalTrajectoryRuns(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId) };
|
|
511
|
+
}
|
|
512
|
+
if (call === 'terminal.trajectory.events') {
|
|
513
|
+
if (typeof params.runId !== 'string' || !params.runId)
|
|
514
|
+
throw invalid('A run id is required.');
|
|
515
|
+
const afterSequence = params.afterSequence;
|
|
516
|
+
if (afterSequence !== undefined && typeof afterSequence !== 'number')
|
|
517
|
+
throw invalid('The event sequence must be a number.');
|
|
518
|
+
return readTerminalTrajectory(this.options.env.root, live.input.sessionId, live.controller.nativeSessionId, params.runId, afterSequence);
|
|
519
|
+
}
|
|
520
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
521
|
+
}
|
|
522
|
+
/** The only terminal frontend: the Bun-run OpenTUI application. */
|
|
523
|
+
frontendCommand() {
|
|
524
|
+
return {
|
|
525
|
+
program: process.env.XINGULAR_HARNESS_BUN || 'bun',
|
|
526
|
+
// Solid must resolve to its client build for OpenTUI's effects, wherever it is imported.
|
|
527
|
+
argsPrefix: ['--conditions=browser', this.assets.openTui]
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Prepares an OpenTUI terminal session and returns what to spawn. The caller must report the
|
|
532
|
+
* process's end with `dispose`, also when it never starts.
|
|
533
|
+
*/
|
|
534
|
+
async prepare(input) {
|
|
535
|
+
input = structuredClone(input);
|
|
536
|
+
const { env, accounts, apps, bus } = this.options;
|
|
537
|
+
const { root } = env;
|
|
538
|
+
if (!input?.sessionId || !input.cwd || !input.sessionFolder)
|
|
539
|
+
throw invalid('A terminal session needs an id, a working folder, and its own folder.');
|
|
540
|
+
await this.dispose(input.sessionId);
|
|
541
|
+
const command = this.frontendCommand();
|
|
542
|
+
// A pinned account is used as it is; it is never exchanged for another one.
|
|
543
|
+
const account = input.accountId ? accounts.find(input.accountId) : accounts.resolve(null, null);
|
|
544
|
+
const launch = launchEnv(accounts.dir, account);
|
|
545
|
+
const loading = (await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: true, caller: { projectId: input.projectId ?? null } })) ?? null;
|
|
546
|
+
await this.socket.listen();
|
|
547
|
+
await accounts.terminalBegin(account);
|
|
548
|
+
const sessionId = input.sessionId;
|
|
549
|
+
const caller = { sessionId, projectId: input.projectId ?? null, actor: 'Code', surface: 'cli' };
|
|
550
|
+
let token = null;
|
|
551
|
+
try {
|
|
552
|
+
const broker = input.connections === false ? null : apps.grant({ sessionId, runId: null, caller, readOnly: !!input.readOnly, depth: 0 });
|
|
553
|
+
// The session keeps these instructions; each of its runs records a copy, and a memory
|
|
554
|
+
// block that changes later is sent along as a note.
|
|
555
|
+
const variant = input.variant ? materialise(root, input.variant, { trust: this.options.trust ?? null }) : null;
|
|
556
|
+
if (variant)
|
|
557
|
+
this.variantDirs.set(sessionId, variant.dir);
|
|
558
|
+
const worldTools = await this.worldToolNames(input, account);
|
|
559
|
+
const launchTools = [...new Set([...(input.launch?.tools ?? []), SESSION_NAME, ...worldTools])];
|
|
560
|
+
const pack = compile(env, caller, { ...input.launch, tools: launchTools }, variant);
|
|
561
|
+
record(root, pack);
|
|
562
|
+
mkdirSync(this.historyDir(input), { recursive: true });
|
|
563
|
+
saveForSession(input.sessionFolder, pack);
|
|
564
|
+
const live = {
|
|
565
|
+
input,
|
|
566
|
+
account,
|
|
567
|
+
broker,
|
|
568
|
+
caller,
|
|
569
|
+
token: '',
|
|
570
|
+
controller: new TerminalController({
|
|
571
|
+
sessionId,
|
|
572
|
+
nativeSessionId: input.nativeSessionId ?? null,
|
|
573
|
+
begin: ({ reservedRunId, text }) => this.begin(live, reservedRunId, text),
|
|
574
|
+
policy: (run, tool, args) => this.decide(live, run, tool, args),
|
|
575
|
+
allowToolGlobally: target => { this.options.approvals.allowTool('pi', target); },
|
|
576
|
+
native: (nativeSessionId) => bus.emit({ type: 'terminal.native', sessionId, nativeSessionId }),
|
|
577
|
+
telemetry: (event) => bus.emit({ type: 'terminal.telemetry', sessionId, event }),
|
|
578
|
+
interrupt: runId => this.options.runs.interrupt(runId),
|
|
579
|
+
resolveApproval: (runId, id, decision) => this.options.runs.resolveApproval(runId, id, decision),
|
|
580
|
+
answerQuestion: (runId, id, answer) => this.options.runs.answerQuestion(runId, id, answer),
|
|
581
|
+
answerQuestions: (runId, batchId, answers) => this.options.runs.answerQuestions(runId, batchId, answers)
|
|
582
|
+
})
|
|
583
|
+
};
|
|
584
|
+
token = this.socket.issue({
|
|
585
|
+
sessionId,
|
|
586
|
+
caller: () => ({ ...caller, runId: live.controller.activeRun?.runId ?? null }),
|
|
587
|
+
broker,
|
|
588
|
+
terminal: { accept: (event, data) => live.controller.accept(event, data), disconnected: () => live.controller.disconnected(), control: async (call, params) => {
|
|
589
|
+
if (call.startsWith('terminal.doctor.')) {
|
|
590
|
+
const doctor = this.options.doctor;
|
|
591
|
+
if (!doctor)
|
|
592
|
+
throw invalid('Diagnostics are unavailable in this host.');
|
|
593
|
+
if (call === 'terminal.doctor.start') {
|
|
594
|
+
const model = typeof params.model === 'string' ? params.model : live.input.model ?? null;
|
|
595
|
+
const tools = [...new Set([
|
|
596
|
+
...(Array.isArray(params.tools) ? params.tools.filter((name) => typeof name === 'string') : []),
|
|
597
|
+
...await this.worldToolNames(live.input, live.account)
|
|
598
|
+
])];
|
|
599
|
+
const variantDir = this.variantDirs.get(sessionId);
|
|
600
|
+
const report = doctor.start({
|
|
601
|
+
context: { caller: live.caller, cwd: live.input.cwd, accountId: live.account.id, model, tools,
|
|
602
|
+
variant: variantDir ? { dir: variantDir, scope: live.input.variant?.scope } : null },
|
|
603
|
+
mode: params.mode === 'live' ? 'live' : 'local',
|
|
604
|
+
checks: Array.isArray(params.checks) ? params.checks.filter((id) => typeof id === 'string') : [],
|
|
605
|
+
confirmed: params.confirmed === true
|
|
606
|
+
});
|
|
607
|
+
this.diagnosticOwners.set(report.id, sessionId);
|
|
608
|
+
return report;
|
|
609
|
+
}
|
|
610
|
+
const id = typeof params.id === 'string' ? params.id : '';
|
|
611
|
+
if (this.diagnosticOwners.get(id) !== sessionId)
|
|
612
|
+
throw invalid('This diagnostic job belongs to another terminal.');
|
|
613
|
+
if (call === 'terminal.doctor.read')
|
|
614
|
+
return doctor.read(id);
|
|
615
|
+
if (call === 'terminal.doctor.cancel')
|
|
616
|
+
return doctor.cancel(id);
|
|
617
|
+
throw invalid('Unknown diagnostic operation.');
|
|
618
|
+
}
|
|
619
|
+
if (call.startsWith('terminal.subagents.'))
|
|
620
|
+
return this.subagentsCall(live, call, params);
|
|
621
|
+
if (call === 'terminal.logs.info')
|
|
622
|
+
return harnessLogInfo(this.options.stateDir);
|
|
623
|
+
if (call === 'terminal.logs.read') {
|
|
624
|
+
const { snapshotSize, offset } = params;
|
|
625
|
+
if (typeof snapshotSize !== 'number' || !Number.isSafeInteger(snapshotSize) || snapshotSize < 0 || typeof offset !== 'number' || !Number.isSafeInteger(offset) || offset < 0 || offset >= snapshotSize) {
|
|
626
|
+
throw invalid('A valid diagnostics snapshot size and byte offset are required.');
|
|
627
|
+
}
|
|
628
|
+
const current = harnessLogInfo(this.options.stateDir).size;
|
|
629
|
+
if (current === null || snapshotSize > current)
|
|
630
|
+
throw invalid('The diagnostics snapshot is no longer available.');
|
|
631
|
+
return readHarnessLogChunk(this.options.stateDir, snapshotSize, offset);
|
|
632
|
+
}
|
|
633
|
+
if (call.startsWith('terminal.components.'))
|
|
634
|
+
return this.componentsCall(live, call, params);
|
|
635
|
+
if (call.startsWith('terminal.trajectory.'))
|
|
636
|
+
return this.trajectoryCall(live, call, params);
|
|
637
|
+
if (call === 'terminal.permissions.get') {
|
|
638
|
+
const policy = this.options.approvals.read();
|
|
639
|
+
return { full: this.options.approvals.isAuto('pi'), globalTools: policy.globalTools.pi ?? [] };
|
|
640
|
+
}
|
|
641
|
+
if (call === 'terminal.permissions.setFull') {
|
|
642
|
+
if (typeof params.enabled !== 'boolean')
|
|
643
|
+
throw invalid('A boolean permission state is required.');
|
|
644
|
+
return { full: this.options.approvals.setAuto('pi', params.enabled).auto.pi === true };
|
|
645
|
+
}
|
|
646
|
+
if (call === 'terminal.permissions.setTool') {
|
|
647
|
+
if (typeof params.tool !== 'string' || !params.tool.trim() || typeof params.allowed !== 'boolean')
|
|
648
|
+
throw invalid('A tool name and boolean permission state are required.');
|
|
649
|
+
const policy = params.allowed
|
|
650
|
+
? this.options.approvals.allowTool('pi', params.tool)
|
|
651
|
+
: this.options.approvals.revokeTool('pi', params.tool);
|
|
652
|
+
return { globalTools: policy.globalTools.pi ?? [] };
|
|
653
|
+
}
|
|
654
|
+
if (call !== 'terminal.resources')
|
|
655
|
+
return live.controller.control(call, params);
|
|
656
|
+
const current = await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: false, caller: { projectId: input.projectId ?? null } }) ?? null;
|
|
657
|
+
if (resourceIdentity(current) !== resourceIdentity(loading))
|
|
658
|
+
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.');
|
|
659
|
+
return {};
|
|
660
|
+
} }
|
|
661
|
+
});
|
|
662
|
+
live.token = token;
|
|
663
|
+
this.sessions.set(sessionId, live);
|
|
664
|
+
const args = [...command.argsPrefix, '--append-system-prompt', render(env, pack)];
|
|
665
|
+
if (input.model)
|
|
666
|
+
args.push('--model', input.model);
|
|
667
|
+
if (loading) {
|
|
668
|
+
// Pi's own discovery is off: the terminal loads what the harness found and nothing else.
|
|
669
|
+
args.push('--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes');
|
|
670
|
+
for (const path of [...loading.extensionPaths, ...loading.builtins, ...(variant?.extensionPaths ?? [])])
|
|
671
|
+
args.push('--extension', path);
|
|
672
|
+
for (const path of [...(variant?.skillPaths ?? []), ...loading.skillPaths])
|
|
673
|
+
args.push('--skill', path);
|
|
674
|
+
for (const path of loading.promptPaths)
|
|
675
|
+
args.push('--prompt-template', path);
|
|
676
|
+
for (const path of loading.themePaths)
|
|
677
|
+
args.push('--theme', path);
|
|
678
|
+
}
|
|
679
|
+
// Last, so its gate sees a tool call as every other extension left it.
|
|
680
|
+
args.push('--extension', this.assets.piExtension, '--session-dir', this.historyDir(input));
|
|
681
|
+
if (input.nativeSessionId)
|
|
682
|
+
args.push('--session', input.nativeSessionId);
|
|
683
|
+
return {
|
|
684
|
+
program: command.program,
|
|
685
|
+
args,
|
|
686
|
+
env: {
|
|
687
|
+
...launch,
|
|
688
|
+
XINGULAR_HARNESS_NAME: env.productName,
|
|
689
|
+
...(process.platform === 'win32' ? { XINGULAR_HARNESS_NODE: this.assets.node, XINGULAR_HARNESS_CLI: this.assets.cli } : {}),
|
|
690
|
+
...prependSearchPath(this.ensureCommands(), searchPath()),
|
|
691
|
+
[SOCKET_ENV]: this.socket.path,
|
|
692
|
+
[TOKEN_ENV]: token
|
|
693
|
+
},
|
|
694
|
+
accountId: account.id,
|
|
695
|
+
nativeSessionId: input.nativeSessionId ?? null
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
if (token)
|
|
700
|
+
this.socket.revoke(token);
|
|
701
|
+
this.sessions.delete(sessionId);
|
|
702
|
+
apps.revokeSession(sessionId);
|
|
703
|
+
await accounts.terminalEnd(account).catch(() => undefined);
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
|
|
708
|
+
async send(input) {
|
|
709
|
+
const { env } = this.options;
|
|
710
|
+
const { root } = env;
|
|
711
|
+
if (!input.text?.trim())
|
|
712
|
+
throw invalid('Message is empty.');
|
|
713
|
+
const live = this.live(input.sessionId);
|
|
714
|
+
if (this.options.runs.activeForSession(input.sessionId) || live.controller.state().active)
|
|
715
|
+
throw new HarnessError('session_busy', 'This session is still working.');
|
|
716
|
+
const runId = input.runId || newId();
|
|
717
|
+
if (findRun(root, runId))
|
|
718
|
+
throw invalid('This run already exists.');
|
|
719
|
+
saveRun(root, this.runRecord(live, runId, input.text));
|
|
720
|
+
copyToRun(root, live.input.sessionFolder, runId);
|
|
721
|
+
try {
|
|
722
|
+
await live.controller.send(runId, input.text);
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
updateRun(root, runId, (run) => {
|
|
726
|
+
if (!isActiveStatus(run.status))
|
|
727
|
+
return;
|
|
728
|
+
run.status = 'failed';
|
|
729
|
+
run.endedAt = now();
|
|
730
|
+
});
|
|
731
|
+
throw error;
|
|
732
|
+
}
|
|
733
|
+
return { runId };
|
|
734
|
+
}
|
|
735
|
+
/** What the person typed into the terminal. */
|
|
736
|
+
input(sessionId, data) {
|
|
737
|
+
this.sessions.get(sessionId)?.controller.onInput(data);
|
|
738
|
+
}
|
|
739
|
+
state(sessionId) {
|
|
740
|
+
return this.live(sessionId).controller.state();
|
|
741
|
+
}
|
|
742
|
+
has(sessionId) {
|
|
743
|
+
return this.sessions.has(sessionId);
|
|
744
|
+
}
|
|
745
|
+
/** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
|
|
746
|
+
async dispose(sessionId) {
|
|
747
|
+
const live = this.sessions.get(sessionId);
|
|
748
|
+
if (!live)
|
|
749
|
+
return;
|
|
750
|
+
for (const [id, owner] of this.diagnosticOwners)
|
|
751
|
+
if (owner === sessionId) {
|
|
752
|
+
this.options.doctor?.cancel(id);
|
|
753
|
+
this.diagnosticOwners.delete(id);
|
|
754
|
+
}
|
|
755
|
+
this.sessions.delete(sessionId);
|
|
756
|
+
this.worldNames.delete(sessionId);
|
|
757
|
+
void this.options.world?.release(sessionId).catch(() => undefined);
|
|
758
|
+
live.controller.dispose();
|
|
759
|
+
this.socket.revoke(live.token);
|
|
760
|
+
this.options.apps.revokeSession(sessionId);
|
|
761
|
+
await this.options.accounts.terminalEnd(live.account).catch((error) => log('terminal credentials', error instanceof Error ? error.message : String(error)));
|
|
762
|
+
}
|
|
763
|
+
async close() {
|
|
764
|
+
await Promise.allSettled([...this.sessions.keys()].map((sessionId) => this.dispose(sessionId)));
|
|
765
|
+
await this.socket.close();
|
|
766
|
+
}
|
|
767
|
+
}
|