@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
|
@@ -1,553 +0,0 @@
|
|
|
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
|
-
|
|
8
|
-
import type { Accounts } from '../accounts/accounts'
|
|
9
|
-
import { configDir, launchEnv } from '../accounts/env'
|
|
10
|
-
import { readAudit, type Account } from '../accounts/store'
|
|
11
|
-
import type { Bus } from '../core/bus'
|
|
12
|
-
import { log } from '../core/bus'
|
|
13
|
-
import type { HarnessEnv } from '../core/env'
|
|
14
|
-
import { HarnessError, invalid } from '../core/errors'
|
|
15
|
-
import { newId, now } from '../core/fs'
|
|
16
|
-
import { prependSearchPath, searchPath } from '../core/path'
|
|
17
|
-
import type { BrokerSession, ConnectedApps } from '../mcp/service'
|
|
18
|
-
import type { CallerContext } from '../memory/caller'
|
|
19
|
-
import { dispatch } from '../memory/dispatch'
|
|
20
|
-
import type { AttachParams, PluginScope } from '../plugins/api'
|
|
21
|
-
import type { Loading, Plugins } from '../plugins/plugins'
|
|
22
|
-
import type { Plugin } from '../plugins/types'
|
|
23
|
-
import type { Hub } from '../hub/hub'
|
|
24
|
-
import { compile, copyToRun, memoryDelta, record, render, saveForSession, type Launch } from '../prompt/compile'
|
|
25
|
-
import type { RunRecord } from '../sessions/contracts'
|
|
26
|
-
import { isActiveStatus } from '../sessions/contracts'
|
|
27
|
-
import type { ToolPolicy } from '../sessions/policy'
|
|
28
|
-
import type { ActiveRun, RunRegistry } from '../sessions/registry'
|
|
29
|
-
import { findRun, loadRun, saveRun, updateRun } from '../sessions/runStore'
|
|
30
|
-
import { resources } from '../skills/registry'
|
|
31
|
-
import type { ToolSpec } from '../tools/types'
|
|
32
|
-
import { APPS_COMMAND, WIKI_COMMAND } from './cli'
|
|
33
|
-
import { SOCKET_ENV, TOKEN_ENV } from './client'
|
|
34
|
-
import { TerminalController, type PolicyReply } from './controller'
|
|
35
|
-
import { resolvePiCommand, terminalCommandFiles } from './launch'
|
|
36
|
-
import { SessionSocket, socketPath } from './socket'
|
|
37
|
-
|
|
38
|
-
/** Identity of the exact resource decision that this terminal was prepared with. */
|
|
39
|
-
function resourceIdentity(loading: Loading | null): string {
|
|
40
|
-
if (!loading) return 'null'
|
|
41
|
-
return JSON.stringify({
|
|
42
|
-
extensions: loading.extensionPaths, builtins: loading.builtins, skills: loading.skillPaths,
|
|
43
|
-
prompts: loading.promptPaths, themes: loading.themePaths, detached: loading.detached,
|
|
44
|
-
project: loading.projectCode, packages: loading.packageCode
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The files a terminal session needs on disk, outside this process. */
|
|
49
|
-
export interface TerminalAssets {
|
|
50
|
-
/** The Node binary that runs the commands. */
|
|
51
|
-
node: string
|
|
52
|
-
/** The built `xingular-harness` command (`dist/cli.js`). */
|
|
53
|
-
cli: string
|
|
54
|
-
/** The built Pi extension (`dist/terminal-pi.js`). */
|
|
55
|
-
piExtension: string
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Where the package's own build puts them. An application that bundles the harness passes its own. */
|
|
59
|
-
export function defaultAssets(): TerminalAssets {
|
|
60
|
-
const here = dirname(fileURLToPath(import.meta.url))
|
|
61
|
-
const dist = [here, join(here, '..', '..', 'dist'), join(here, '..', 'dist')].find((folder) => existsSync(join(folder, 'terminal-pi.js'))) ?? here
|
|
62
|
-
return { node: process.execPath, cli: join(dist, 'cli.js'), piExtension: join(dist, 'terminal-pi.js') }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface TerminalPrepareInput {
|
|
66
|
-
sessionId: string
|
|
67
|
-
/** The account the session is pinned to. Omitted only for a session that has none yet; it then takes the default and keeps it. */
|
|
68
|
-
accountId?: string | null
|
|
69
|
-
projectId?: string | null
|
|
70
|
-
cwd: string
|
|
71
|
-
/** The session's own folder. Its compiled instructions and native history are kept under it. */
|
|
72
|
-
sessionFolder: string
|
|
73
|
-
nativeSessionId?: string | null
|
|
74
|
-
model?: string | null
|
|
75
|
-
readOnly?: boolean
|
|
76
|
-
/** What the application adds to the instructions, and which of its tools it describes. */
|
|
77
|
-
launch?: Launch
|
|
78
|
-
/** Give the session the connected-apps broker. Defaults to yes. */
|
|
79
|
-
connections?: boolean
|
|
80
|
-
/** Fields copied onto each run this session records. */
|
|
81
|
-
run?: Partial<Pick<RunRecord, 'mode' | 'worktreeId'>>
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface TerminalLaunch {
|
|
85
|
-
program: string
|
|
86
|
-
args: string[]
|
|
87
|
-
/** Variables to set; `null` removes an inherited one. */
|
|
88
|
-
env: Record<string, string | null>
|
|
89
|
-
accountId: string
|
|
90
|
-
nativeSessionId: string | null
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface Live {
|
|
94
|
-
input: TerminalPrepareInput
|
|
95
|
-
account: Account
|
|
96
|
-
controller: TerminalController
|
|
97
|
-
token: string
|
|
98
|
-
broker: BrokerSession | null
|
|
99
|
-
caller: CallerContext
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface TerminalsOptions {
|
|
103
|
-
env: HarnessEnv
|
|
104
|
-
stateDir: string
|
|
105
|
-
accounts: Accounts
|
|
106
|
-
runs: RunRegistry
|
|
107
|
-
apps: ConnectedApps
|
|
108
|
-
policy: ToolPolicy
|
|
109
|
-
bus: Bus
|
|
110
|
-
/** Chooses what a terminal loads. Without it Pi discovers extensions and skills on its own. */
|
|
111
|
-
plugins?: Plugins
|
|
112
|
-
/** Lists plugins and applies a person's choices, for the session's `/plugins` command. */
|
|
113
|
-
pluginControl?: PluginControl
|
|
114
|
-
hub?: Hub
|
|
115
|
-
assets?: TerminalAssets
|
|
116
|
-
/** The `pi` executable. Defaults to the pinned bundled CLI through the active Node runtime. */
|
|
117
|
-
program?: () => string | null
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** What `/plugins` in a terminal can do. Each takes the session's folder, account and project. */
|
|
121
|
-
export interface PluginControl {
|
|
122
|
-
list(scope: PluginScope): Promise<Plugin[]>
|
|
123
|
-
attach(params: AttachParams, on: boolean): Promise<Plugin[]>
|
|
124
|
-
trust(scope: PluginScope & { hash: string }): Promise<Plugin[]>
|
|
125
|
-
untrust(scope: PluginScope & { hash: string }): Promise<Plugin[]>
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const READ_ONLY_TOOLS = new Set(['read', 'grep', 'find', 'ls'])
|
|
129
|
-
|
|
130
|
-
export class Terminals {
|
|
131
|
-
private readonly sessions = new Map<string, Live>()
|
|
132
|
-
private readonly socket: SessionSocket
|
|
133
|
-
private readonly assets: TerminalAssets
|
|
134
|
-
/** A session's `/accounts` switch, taken once its Pi process has ended. */
|
|
135
|
-
private readonly relaunches = new Map<string, { accountId: string; nativeSessionId: string | null }>()
|
|
136
|
-
|
|
137
|
-
constructor(private readonly options: TerminalsOptions) {
|
|
138
|
-
this.assets = options.assets ?? defaultAssets()
|
|
139
|
-
options.runs.listen(event => this.sessions.get(event.sessionId)?.controller.observe(event))
|
|
140
|
-
this.socket = new SessionSocket(socketPath(options.stateDir), {
|
|
141
|
-
memory: (caller, call, params) => dispatch(options.env, caller, call, params),
|
|
142
|
-
apps: (session, call, params) => this.appsCall(session, call, params),
|
|
143
|
-
plugins: (grant, call, params, control) => this.pluginsCall(grant.sessionId, call, params, control),
|
|
144
|
-
accounts: (grant, call, params, control) => this.accountsCall(grant.sessionId, call, params, control)
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** The commands a terminal session finds on PATH. Rewritten only when they change. */
|
|
149
|
-
private ensureCommands(): string {
|
|
150
|
-
const bin = join(this.options.stateDir, 'run', 'bin')
|
|
151
|
-
mkdirSync(bin, { recursive: true })
|
|
152
|
-
for (const name of ['agentx-wiki', 'agentx-mcp', 'agentx-wiki.cmd', 'agentx-mcp.cmd']) {
|
|
153
|
-
const path = join(bin, name)
|
|
154
|
-
try {
|
|
155
|
-
if (lstatSync(path).isFile()) unlinkSync(path)
|
|
156
|
-
} catch {
|
|
157
|
-
// These names belonged to the previous generated wrappers.
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
for (const [name, mode] of [[WIKI_COMMAND, 'wiki'], [APPS_COMMAND, 'mcp']] as const) {
|
|
161
|
-
for (const file of terminalCommandFiles(process.platform, this.assets.node, this.assets.cli, name, mode)) {
|
|
162
|
-
const path = join(bin, file.name)
|
|
163
|
-
let current: string | null = null
|
|
164
|
-
try {
|
|
165
|
-
current = readFileSync(path, 'utf8')
|
|
166
|
-
} catch {
|
|
167
|
-
current = null
|
|
168
|
-
}
|
|
169
|
-
if (current !== file.content) {
|
|
170
|
-
writeFileSync(path, file.content)
|
|
171
|
-
chmodSync(path, 0o755)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return bin
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private tools(session: BrokerSession): ToolSpec[] {
|
|
179
|
-
return this.options.apps.brokerTools(session)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private async appsCall(session: BrokerSession, call: string, params: Record<string, unknown>): Promise<unknown> {
|
|
183
|
-
const { apps } = this.options
|
|
184
|
-
switch (call) {
|
|
185
|
-
case 'mcp.list':
|
|
186
|
-
return apps.list(session)
|
|
187
|
-
case 'mcp.call':
|
|
188
|
-
return apps.invoke(session, String(params.name ?? ''), (params.args && typeof params.args === 'object' ? params.args : {}) as Record<string, unknown>)
|
|
189
|
-
case 'mcp.revision':
|
|
190
|
-
return apps.revision(session)
|
|
191
|
-
case 'tools.list': {
|
|
192
|
-
const { Type } = await import('typebox')
|
|
193
|
-
return { tools: this.tools(session).map((tool) => ({ name: tool.name, label: tool.label, description: tool.description, parameters: tool.parameters(Type) })) }
|
|
194
|
-
}
|
|
195
|
-
case 'tools.call': {
|
|
196
|
-
const tool = this.tools(session).find((candidate) => candidate.name === params.name)
|
|
197
|
-
if (!tool) throw new HarnessError('unknown_method', `Unknown tool ${String(params.name)}.`)
|
|
198
|
-
const result = await tool.execute((params.input && typeof params.input === 'object' ? params.input : {}) as Record<string, unknown>)
|
|
199
|
-
return { text: typeof result === 'string' ? result : result.text }
|
|
200
|
-
}
|
|
201
|
-
default:
|
|
202
|
-
throw new HarnessError('unknown_method', `Unknown call ${call}.`)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
|
|
208
|
-
* session's own extension, which asks the person in the terminal first: the model can reach
|
|
209
|
-
* this socket from a shell command, and must not be able to trust code or switch it on.
|
|
210
|
-
*/
|
|
211
|
-
private async pluginsCall(sessionId: string, call: string, params: Record<string, unknown>, control: boolean): Promise<unknown> {
|
|
212
|
-
const api = this.options.pluginControl
|
|
213
|
-
const live = this.sessions.get(sessionId)
|
|
214
|
-
if (!api || !live) throw new HarnessError('unknown_method', `Unknown call ${call}.`)
|
|
215
|
-
const projectId = live.input.projectId ?? null
|
|
216
|
-
const scope = { cwd: live.input.cwd, accountId: live.account.id, projectId }
|
|
217
|
-
if (call === 'hub.list' && this.options.hub) return this.options.hub.list(params.refresh === true)
|
|
218
|
-
if (call === 'hub.install' && this.options.hub) {
|
|
219
|
-
if (!control) throw new HarnessError('unauthorized', 'Only a person at this terminal can install items. Type /hub.')
|
|
220
|
-
if (typeof params.id !== 'string' || typeof params.version !== 'string') throw invalid('An item id and version are required.')
|
|
221
|
-
return this.options.hub.install(params.id, params.version)
|
|
222
|
-
}
|
|
223
|
-
if (call === 'plugins.list') return api.list(scope)
|
|
224
|
-
if (call === 'plugins.session') return { projectId }
|
|
225
|
-
if (!control) throw new HarnessError('unauthorized', 'Only a person at this terminal can change plugins. Type /plugins.')
|
|
226
|
-
const text = (name: string): string => {
|
|
227
|
-
if (typeof params[name] !== 'string' || !params[name]) throw invalid(`${name} is required.`)
|
|
228
|
-
return params[name] as string
|
|
229
|
-
}
|
|
230
|
-
switch (call) {
|
|
231
|
-
case 'plugins.attach':
|
|
232
|
-
case 'plugins.detach':
|
|
233
|
-
return api.attach({ ...scope, id: text('id'), scope: params.everywhere === true || !projectId ? {} : { projectId } }, call === 'plugins.attach')
|
|
234
|
-
case 'plugins.trust':
|
|
235
|
-
return api.trust({ ...scope, hash: text('hash') })
|
|
236
|
-
case 'plugins.untrust':
|
|
237
|
-
return api.untrust({ ...scope, hash: text('hash') })
|
|
238
|
-
default:
|
|
239
|
-
throw new HarnessError('unknown_method', `Unknown call ${call}.`)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
|
|
245
|
-
* or removing an account is a person's choice, never the model's shell command.
|
|
246
|
-
*/
|
|
247
|
-
private async accountsCall(sessionId: string, call: string, params: Record<string, unknown>, control: boolean): Promise<unknown> {
|
|
248
|
-
const { accounts } = this.options
|
|
249
|
-
const live = this.sessions.get(sessionId)
|
|
250
|
-
if (!live) throw new HarnessError('unknown_method', `Unknown call ${call}.`)
|
|
251
|
-
if (!control) throw new HarnessError('unauthorized', 'Only a person at this terminal can manage accounts. Type /accounts.')
|
|
252
|
-
const text = (name: string): string => {
|
|
253
|
-
if (typeof params[name] !== 'string' || !params[name]) throw invalid(`${name} is required.`)
|
|
254
|
-
return params[name] as string
|
|
255
|
-
}
|
|
256
|
-
const optional = (name: string): string | null => (typeof params[name] === 'string' ? (params[name] as string) : null)
|
|
257
|
-
switch (call) {
|
|
258
|
-
case 'accounts.deviceId':
|
|
259
|
-
return accounts.installationDeviceId()
|
|
260
|
-
case 'accounts.list':
|
|
261
|
-
return accounts.overview()
|
|
262
|
-
case 'accounts.session':
|
|
263
|
-
return { accountId: live.account.id, label: live.account.label }
|
|
264
|
-
case 'accounts.create':
|
|
265
|
-
return accounts.create(text('label'), optional('authKind') ?? 'subscription')
|
|
266
|
-
case 'accounts.rename':
|
|
267
|
-
return accounts.rename(text('accountId'), text('label'))
|
|
268
|
-
case 'accounts.setDefault':
|
|
269
|
-
return accounts.setDefault(text('accountId'))
|
|
270
|
-
case 'accounts.remove': {
|
|
271
|
-
const accountId = text('accountId')
|
|
272
|
-
// Every terminal in this process counts, this session's own included. Another process's terminals are not visible here.
|
|
273
|
-
if (accounts.terminalCount(accountId) > 0) 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.')
|
|
274
|
-
return accounts.remove(accountId)
|
|
275
|
-
}
|
|
276
|
-
case 'accounts.refresh':
|
|
277
|
-
return accounts.refresh(optional('accountId'))
|
|
278
|
-
case 'accounts.providers':
|
|
279
|
-
return accounts.providers()
|
|
280
|
-
case 'accounts.loginStart':
|
|
281
|
-
return accounts.loginStart(text('accountId'), text('provider'), optional('method'))
|
|
282
|
-
case 'accounts.loginStatus':
|
|
283
|
-
return accounts.loginStatus(text('accountId'))
|
|
284
|
-
case 'accounts.loginRespond':
|
|
285
|
-
accounts.loginRespond(text('loginId'), text('promptId'), optional('value'))
|
|
286
|
-
return {}
|
|
287
|
-
case 'accounts.loginCancel':
|
|
288
|
-
accounts.loginCancel(text('accountId'), optional('loginId'))
|
|
289
|
-
return {}
|
|
290
|
-
case 'accounts.setApiKey':
|
|
291
|
-
return accounts.setApiKey(text('accountId'), text('key'), text('provider'))
|
|
292
|
-
case 'accounts.removeProvider':
|
|
293
|
-
return accounts.removeProvider(text('accountId'), text('provider'))
|
|
294
|
-
case 'accounts.signOut':
|
|
295
|
-
return accounts.signOut(text('accountId'))
|
|
296
|
-
case 'accounts.savePool': {
|
|
297
|
-
const pool = params.pool as { id?: unknown; label?: unknown; accountIds?: unknown } | undefined
|
|
298
|
-
if (!pool || typeof pool.label !== 'string' || !Array.isArray(pool.accountIds) || !pool.accountIds.every((id) => typeof id === 'string')) throw invalid('A pool needs a label and its account ids.')
|
|
299
|
-
return accounts.savePool({ id: typeof pool.id === 'string' ? pool.id : undefined, label: pool.label, accountIds: pool.accountIds as string[] })
|
|
300
|
-
}
|
|
301
|
-
case 'accounts.removePool':
|
|
302
|
-
return accounts.removePool(text('poolId'))
|
|
303
|
-
case 'accounts.audit':
|
|
304
|
-
return readAudit(accounts.dir, optional('accountId'), typeof params.limit === 'number' ? params.limit : 100)
|
|
305
|
-
case 'accounts.relaunch': {
|
|
306
|
-
// Checked here so a bad choice is refused before the session is asked to end.
|
|
307
|
-
const target = accounts.resolve(text('accountId'))
|
|
308
|
-
this.relaunches.set(sessionId, { accountId: target.id, nativeSessionId: live.controller.nativeSessionId })
|
|
309
|
-
return { accountId: target.id, label: target.label }
|
|
310
|
-
}
|
|
311
|
-
default:
|
|
312
|
-
throw new HarnessError('unknown_method', `Unknown call ${call}.`)
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
|
|
317
|
-
takeRelaunch(sessionId: string): { accountId: string; nativeSessionId: string | null } | null {
|
|
318
|
-
const request = this.relaunches.get(sessionId) ?? null
|
|
319
|
-
this.relaunches.delete(sessionId)
|
|
320
|
-
return request
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
private live(sessionId: string): Live {
|
|
324
|
-
const live = this.sessions.get(sessionId)
|
|
325
|
-
if (!live) throw new HarnessError('terminal_not_ready', 'The terminal controller is unavailable. Reopen this session.')
|
|
326
|
-
return live
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private historyDir(input: TerminalPrepareInput): string {
|
|
330
|
-
return join(input.sessionFolder, 'pi')
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
private runRecord(live: Live, runId: string, text: string | null): RunRecord {
|
|
334
|
-
const { input } = live
|
|
335
|
-
return {
|
|
336
|
-
resumedFromRunId: null,
|
|
337
|
-
parentRunId: null,
|
|
338
|
-
relation: null,
|
|
339
|
-
terminalReason: null,
|
|
340
|
-
agentId: null,
|
|
341
|
-
trigger: null,
|
|
342
|
-
id: runId,
|
|
343
|
-
projectId: input.projectId ?? null,
|
|
344
|
-
mode: input.run?.mode ?? 'code',
|
|
345
|
-
harness: 'pi',
|
|
346
|
-
sessionType: 'terminal',
|
|
347
|
-
sessionId: input.sessionId,
|
|
348
|
-
nativeSessionId: live.controller.nativeSessionId,
|
|
349
|
-
workingDirectory: input.cwd,
|
|
350
|
-
worktreeId: input.run?.worktreeId ?? null,
|
|
351
|
-
goal: text,
|
|
352
|
-
attachmentCount: 0,
|
|
353
|
-
startedAt: now(),
|
|
354
|
-
endedAt: null,
|
|
355
|
-
status: 'queued',
|
|
356
|
-
exitCode: null,
|
|
357
|
-
handoffId: null,
|
|
358
|
-
accountId: live.account.id
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
|
|
363
|
-
private begin(live: Live, reservedRunId: string | null, text: string): ActiveRun {
|
|
364
|
-
const { root } = this.options.env
|
|
365
|
-
const { sessionId } = live.input
|
|
366
|
-
if (this.options.runs.activeForSession(sessionId)) throw new HarnessError('session_busy', 'A native turn already owns this session.')
|
|
367
|
-
let runId = reservedRunId
|
|
368
|
-
if (runId) {
|
|
369
|
-
const reserved = loadRun(root, runId)
|
|
370
|
-
if (reserved.sessionId !== sessionId || reserved.status !== 'queued') throw invalid('Delivery does not own this queued run.')
|
|
371
|
-
} else {
|
|
372
|
-
runId = newId()
|
|
373
|
-
saveRun(root, this.runRecord(live, runId, text || null))
|
|
374
|
-
copyToRun(root, live.input.sessionFolder, runId)
|
|
375
|
-
}
|
|
376
|
-
return this.options.runs.start(sessionId, runId)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private async decide(live: Live, run: ActiveRun, tool: string, args: unknown): Promise<PolicyReply> {
|
|
380
|
-
const record = loadRun(this.options.env.root, run.runId)
|
|
381
|
-
if (!isActiveStatus(record.status)) throw invalid('The run is no longer active.')
|
|
382
|
-
const call = { run: record, caller: { ...live.caller, runId: run.runId }, tool, args }
|
|
383
|
-
const { policy } = this.options
|
|
384
|
-
if (policy.scope) {
|
|
385
|
-
const scope = await policy.scope(call)
|
|
386
|
-
if (!scope.allowed && !scope.requiresApproval) return { allowed: false, reason: scope.reason ?? 'Outside what this session may do.' }
|
|
387
|
-
try {
|
|
388
|
-
await policy.authorize?.(call)
|
|
389
|
-
} catch (error) {
|
|
390
|
-
return { allowed: false, reason: error instanceof Error ? error.message : 'Tool unavailable in the settings.' }
|
|
391
|
-
}
|
|
392
|
-
return { allowed: scope.allowed, requiresApproval: !!scope.requiresApproval, reason: scope.reason ?? null }
|
|
393
|
-
}
|
|
394
|
-
// With no application policy: reads run, a read-only session refuses the rest, and anything else asks.
|
|
395
|
-
const name = tool.toLowerCase()
|
|
396
|
-
if (READ_ONLY_TOOLS.has(name) || this.options.apps.toolNames.includes(name as never)) return { allowed: true }
|
|
397
|
-
if (live.input.readOnly) return { allowed: false, reason: 'This session is read-only.' }
|
|
398
|
-
return { allowed: false, requiresApproval: true, reason: 'This native action requires approval.' }
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Prepares a `pi` terminal session and returns what to spawn. The caller must report the
|
|
403
|
-
* process's end with `dispose`, also when it never starts.
|
|
404
|
-
*/
|
|
405
|
-
async prepare(input: TerminalPrepareInput): Promise<TerminalLaunch> {
|
|
406
|
-
const { env, accounts, apps, bus } = this.options
|
|
407
|
-
const { root } = env
|
|
408
|
-
if (!input?.sessionId || !input.cwd || !input.sessionFolder) throw invalid('A terminal session needs an id, a working folder, and its own folder.')
|
|
409
|
-
await this.dispose(input.sessionId)
|
|
410
|
-
const command = resolvePiCommand({ platform: process.platform, node: this.assets.node, cli: this.assets.cli, programOverride: this.options.program })
|
|
411
|
-
// A pinned account is used as it is; it is never exchanged for another one.
|
|
412
|
-
const account = input.accountId ? accounts.find(input.accountId) : accounts.resolve(null, null)
|
|
413
|
-
const launch = launchEnv(accounts.dir, account)
|
|
414
|
-
const loading = (await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: true, caller: { projectId: input.projectId ?? null } })) ?? null
|
|
415
|
-
await this.socket.listen()
|
|
416
|
-
await accounts.terminalBegin(account)
|
|
417
|
-
|
|
418
|
-
const sessionId = input.sessionId
|
|
419
|
-
const caller: CallerContext = { sessionId, projectId: input.projectId ?? null, actor: 'Code', surface: 'cli' }
|
|
420
|
-
let token: string | null = null
|
|
421
|
-
try {
|
|
422
|
-
const broker = input.connections === false ? null : apps.grant({ sessionId, runId: null, caller, readOnly: !!input.readOnly, depth: 0 })
|
|
423
|
-
// The session keeps these instructions; each of its runs records a copy, and a memory
|
|
424
|
-
// block that changes later is sent along as a note.
|
|
425
|
-
const pack = compile(env, caller, { input: '', ...input.launch })
|
|
426
|
-
record(root, caller, pack)
|
|
427
|
-
mkdirSync(this.historyDir(input), { recursive: true })
|
|
428
|
-
saveForSession(input.sessionFolder, pack)
|
|
429
|
-
|
|
430
|
-
const live: Live = {
|
|
431
|
-
input,
|
|
432
|
-
account,
|
|
433
|
-
broker,
|
|
434
|
-
caller,
|
|
435
|
-
token: '',
|
|
436
|
-
controller: new TerminalController({
|
|
437
|
-
sessionId,
|
|
438
|
-
nativeSessionId: input.nativeSessionId ?? null,
|
|
439
|
-
begin: ({ reservedRunId, text }) => this.begin(live, reservedRunId, text),
|
|
440
|
-
policy: (run, tool, args) => this.decide(live, run, tool, args),
|
|
441
|
-
native: (nativeSessionId) => bus.emit({ type: 'terminal.native', sessionId, nativeSessionId }),
|
|
442
|
-
telemetry: (event) => bus.emit({ type: 'terminal.telemetry', sessionId, event }),
|
|
443
|
-
interrupt: runId => this.options.runs.interrupt(runId),
|
|
444
|
-
resolveApproval: (runId, id, decision) => this.options.runs.resolveApproval(runId, id, decision),
|
|
445
|
-
answerQuestion: (runId, id, answer) => this.options.runs.answerQuestion(runId, id, answer),
|
|
446
|
-
answerQuestions: (runId, batchId, answers) => this.options.runs.answerQuestions(runId, batchId, answers)
|
|
447
|
-
})
|
|
448
|
-
}
|
|
449
|
-
token = this.socket.issue({
|
|
450
|
-
sessionId,
|
|
451
|
-
caller: () => ({ ...caller, runId: live.controller.activeRun?.runId ?? null }),
|
|
452
|
-
broker,
|
|
453
|
-
terminal: { accept: (event, data) => live.controller.accept(event, data), disconnected: () => live.controller.disconnected(), control: async (call, params) => {
|
|
454
|
-
if (call !== 'terminal.resources') return live.controller.control(call, params)
|
|
455
|
-
const current = await this.options.plugins?.loading({ cwd: input.cwd, agentDir: configDir(accounts.dir, account), install: false, caller: { projectId: input.projectId ?? null } }) ?? null
|
|
456
|
-
if (resourceIdentity(current) !== resourceIdentity(loading)) 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.')
|
|
457
|
-
return {}
|
|
458
|
-
} }
|
|
459
|
-
})
|
|
460
|
-
live.token = token
|
|
461
|
-
this.sessions.set(sessionId, live)
|
|
462
|
-
|
|
463
|
-
const args = [...command.argsPrefix, '--append-system-prompt', render(env, pack)]
|
|
464
|
-
if (input.model) args.push('--model', input.model)
|
|
465
|
-
if (loading) {
|
|
466
|
-
// Pi's own discovery is off: the terminal loads what the harness found and nothing else.
|
|
467
|
-
args.push('--no-extensions', '--no-skills', '--no-prompt-templates', '--no-themes')
|
|
468
|
-
const enabled = resources(root, input.projectId ?? null)
|
|
469
|
-
for (const path of [...loading.extensionPaths, ...loading.builtins]) args.push('--extension', path)
|
|
470
|
-
for (const path of [...enabled.skillPaths, ...loading.skillPaths]) args.push('--skill', path)
|
|
471
|
-
for (const path of [...enabled.promptPaths, ...loading.promptPaths]) args.push('--prompt-template', path)
|
|
472
|
-
for (const path of loading.themePaths) args.push('--theme', path)
|
|
473
|
-
}
|
|
474
|
-
// Last, so its gate sees a tool call as every other extension left it.
|
|
475
|
-
args.push('--extension', this.assets.piExtension, '--session-dir', this.historyDir(input))
|
|
476
|
-
if (input.nativeSessionId) args.push('--session', input.nativeSessionId)
|
|
477
|
-
return {
|
|
478
|
-
program: command.program,
|
|
479
|
-
args,
|
|
480
|
-
env: {
|
|
481
|
-
...launch,
|
|
482
|
-
XINGULAR_HARNESS_NAME: env.productName,
|
|
483
|
-
...(process.platform === 'win32' ? { XINGULAR_HARNESS_NODE: this.assets.node, XINGULAR_HARNESS_CLI: this.assets.cli } : {}),
|
|
484
|
-
...prependSearchPath(this.ensureCommands(), searchPath()),
|
|
485
|
-
[SOCKET_ENV]: this.socket.path,
|
|
486
|
-
[TOKEN_ENV]: token
|
|
487
|
-
},
|
|
488
|
-
accountId: account.id,
|
|
489
|
-
nativeSessionId: input.nativeSessionId ?? null
|
|
490
|
-
}
|
|
491
|
-
} catch (error) {
|
|
492
|
-
if (token) this.socket.revoke(token)
|
|
493
|
-
this.sessions.delete(sessionId)
|
|
494
|
-
apps.revokeSession(sessionId)
|
|
495
|
-
await accounts.terminalEnd(account).catch(() => undefined)
|
|
496
|
-
throw error
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
|
|
501
|
-
async send(input: { sessionId: string; runId?: string | null; text: string }): Promise<{ runId: string }> {
|
|
502
|
-
const { env } = this.options
|
|
503
|
-
const { root } = env
|
|
504
|
-
if (!input.text?.trim()) throw invalid('Message is empty.')
|
|
505
|
-
const live = this.live(input.sessionId)
|
|
506
|
-
if (this.options.runs.activeForSession(input.sessionId) || live.controller.state().active) throw new HarnessError('session_busy', 'This session is still working.')
|
|
507
|
-
const runId = input.runId || newId()
|
|
508
|
-
if (findRun(root, runId)) throw invalid('This run already exists.')
|
|
509
|
-
saveRun(root, this.runRecord(live, runId, input.text))
|
|
510
|
-
const note = memoryDelta(env, live.input.sessionFolder, { ...live.caller, runId })
|
|
511
|
-
copyToRun(root, live.input.sessionFolder, runId)
|
|
512
|
-
try {
|
|
513
|
-
await live.controller.send(runId, note ? `${note}\n\n${input.text}` : input.text)
|
|
514
|
-
} catch (error) {
|
|
515
|
-
updateRun(root, runId, (run) => {
|
|
516
|
-
if (!isActiveStatus(run.status)) return
|
|
517
|
-
run.status = 'failed'
|
|
518
|
-
run.endedAt = now()
|
|
519
|
-
})
|
|
520
|
-
throw error
|
|
521
|
-
}
|
|
522
|
-
return { runId }
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/** What the person typed into the terminal. */
|
|
526
|
-
input(sessionId: string, data: string): void {
|
|
527
|
-
this.sessions.get(sessionId)?.controller.onInput(data)
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
state(sessionId: string): { ready: boolean; dirty: boolean; active: boolean } {
|
|
531
|
-
return this.live(sessionId).controller.state()
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
has(sessionId: string): boolean {
|
|
535
|
-
return this.sessions.has(sessionId)
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
|
|
539
|
-
async dispose(sessionId: string): Promise<void> {
|
|
540
|
-
const live = this.sessions.get(sessionId)
|
|
541
|
-
if (!live) return
|
|
542
|
-
this.sessions.delete(sessionId)
|
|
543
|
-
live.controller.dispose()
|
|
544
|
-
this.socket.revoke(live.token)
|
|
545
|
-
this.options.apps.revokeSession(sessionId)
|
|
546
|
-
await this.options.accounts.terminalEnd(live.account).catch((error) => log('terminal credentials', error instanceof Error ? error.message : String(error)))
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
async close(): Promise<void> {
|
|
550
|
-
await Promise.allSettled([...this.sessions.keys()].map((sessionId) => this.dispose(sessionId)))
|
|
551
|
-
await this.socket.close()
|
|
552
|
-
}
|
|
553
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { CustomEditor, type ExtensionUIContext, type Theme } from '@earendil-works/pi-coding-agent'
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
-
|
|
4
|
-
vi.mock('../control', () => ({ controlCall: vi.fn(async () => ({})) }))
|
|
5
|
-
import { controlCall } from '../control'
|
|
6
|
-
import { TerminalApplication } from './application'
|
|
7
|
-
import { terminalKeybindings } from './keybindings'
|
|
8
|
-
import { createHarnessTheme } from './theme'
|
|
9
|
-
|
|
10
|
-
function setup() {
|
|
11
|
-
const application = new TerminalApplication([])
|
|
12
|
-
const app = application as unknown as {
|
|
13
|
-
ui: ExtensionUIContext & { wrapAutocomplete<T>(value: T): T; dispose(): void }
|
|
14
|
-
keybindings: ReturnType<typeof terminalKeybindings>
|
|
15
|
-
editor: CustomEditor
|
|
16
|
-
runtime: { interrupt: ReturnType<typeof vi.fn>; dispose: ReturnType<typeof vi.fn>; session: unknown }
|
|
17
|
-
commands: { execute: ReturnType<typeof vi.fn> }
|
|
18
|
-
initialized: boolean
|
|
19
|
-
commandBusy: boolean
|
|
20
|
-
mountEditor(factory?: Parameters<ExtensionUIContext['setEditorComponent']>[0]): void
|
|
21
|
-
dispatchInput(data: string): { consume?: boolean } | undefined
|
|
22
|
-
submit(text: string): void
|
|
23
|
-
}
|
|
24
|
-
vi.spyOn(application.tui, 'requestRender').mockImplementation(() => {})
|
|
25
|
-
vi.spyOn(application.tui, 'stop').mockImplementation(() => {})
|
|
26
|
-
vi.spyOn(application.terminal, 'drainInput').mockResolvedValue()
|
|
27
|
-
const ui = { theme: createHarnessTheme({ getColorMode: () => 'truecolor' } as Theme), wrapAutocomplete: <T>(value: T) => value,
|
|
28
|
-
setToolsExpanded: vi.fn(), dispose: vi.fn() } as unknown as typeof app.ui
|
|
29
|
-
app.ui = ui
|
|
30
|
-
app.keybindings = terminalKeybindings('/unused/isolated-account')
|
|
31
|
-
app.initialized = true
|
|
32
|
-
const context = {}
|
|
33
|
-
app.runtime = { interrupt: vi.fn(async () => {}), dispose: vi.fn(async () => {}), session: {
|
|
34
|
-
extensionRunner: { getShortcuts: () => new Map(), createContext: () => context },
|
|
35
|
-
state: { messages: [] }, settingsManager: {}
|
|
36
|
-
} }
|
|
37
|
-
application.tui.addChild(application)
|
|
38
|
-
return { application, app, ui }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe('the owned terminal application', () => {
|
|
42
|
-
it('lets trusted custom editor modes consume Escape before delegating host actions', async () => {
|
|
43
|
-
const { application, app } = setup()
|
|
44
|
-
let normalMode = false
|
|
45
|
-
class ModalEditor extends CustomEditor {
|
|
46
|
-
override handleInput(data: string) {
|
|
47
|
-
if (data === '\x1b') { normalMode = true; return }
|
|
48
|
-
super.handleInput(data)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
app.mountEditor((tui, theme, keys) => new ModalEditor(tui, theme, keys))
|
|
52
|
-
expect(app.dispatchInput('\x1b')).toBeUndefined()
|
|
53
|
-
app.editor.handleInput('\x1b')
|
|
54
|
-
expect(normalMode).toBe(true)
|
|
55
|
-
expect(app.runtime.interrupt).not.toHaveBeenCalled()
|
|
56
|
-
expect(app.dispatchInput('\x0f')).toBeUndefined()
|
|
57
|
-
app.editor.handleInput('\x0f')
|
|
58
|
-
expect(app.ui.setToolsExpanded).toHaveBeenCalledWith(true)
|
|
59
|
-
await application.shutdown()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('preserves custom editor change callbacks and reports actual draft contents', async () => {
|
|
63
|
-
const { application, app } = setup()
|
|
64
|
-
const changed = vi.fn()
|
|
65
|
-
app.mountEditor((tui, theme, keys) => { const editor = new CustomEditor(tui, theme, keys); editor.onChange = changed; return editor })
|
|
66
|
-
app.dispatchInput('draft')
|
|
67
|
-
app.editor.handleInput('draft')
|
|
68
|
-
await vi.waitFor(() => expect(controlCall).toHaveBeenCalledWith('terminal.draft', { dirty: true }))
|
|
69
|
-
expect(changed).toHaveBeenCalledWith('draft')
|
|
70
|
-
expect(app.editor.getText()).toBe('draft')
|
|
71
|
-
await application.shutdown()
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('routes leading-space commands and serializes command dialogs', async () => {
|
|
75
|
-
const { application, app } = setup()
|
|
76
|
-
app.mountEditor()
|
|
77
|
-
let resolve!: (value: boolean) => void
|
|
78
|
-
app.commands = { execute: vi.fn(() => new Promise<boolean>(done => { resolve = done })) }
|
|
79
|
-
app.submit(' /settings')
|
|
80
|
-
await vi.waitFor(() => expect(app.commands.execute).toHaveBeenCalledWith(' /settings'))
|
|
81
|
-
app.submit('/model')
|
|
82
|
-
await Promise.resolve(); await Promise.resolve()
|
|
83
|
-
expect(app.commands.execute).toHaveBeenCalledTimes(1)
|
|
84
|
-
resolve(true)
|
|
85
|
-
await vi.waitFor(() => expect(app.commandBusy).toBe(false))
|
|
86
|
-
await application.shutdown()
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('restores terminal modes and finishes even when runtime and plugin cleanup fail', async () => {
|
|
90
|
-
const { application, app } = setup()
|
|
91
|
-
const header = { dispose: vi.fn(() => { throw new Error('header cleanup failed') }) }
|
|
92
|
-
const footer = { dispose: vi.fn() }
|
|
93
|
-
Object.assign(app, { header, footer })
|
|
94
|
-
app.ui.dispose = vi.fn(() => { throw new Error('dialog cleanup failed') })
|
|
95
|
-
app.runtime.dispose.mockRejectedValue(new Error('runtime cleanup failed'))
|
|
96
|
-
await expect(application.shutdown()).resolves.toBeUndefined()
|
|
97
|
-
expect(header.dispose).toHaveBeenCalledOnce()
|
|
98
|
-
expect(footer.dispose).toHaveBeenCalledOnce()
|
|
99
|
-
expect(app.runtime.dispose).toHaveBeenCalledOnce()
|
|
100
|
-
expect(application.terminal.drainInput).toHaveBeenCalledOnce()
|
|
101
|
-
expect(application.tui.stop).toHaveBeenCalledOnce()
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('has one shutdown that disposes the runtime and restores the terminal', async () => {
|
|
105
|
-
const { application, app } = setup()
|
|
106
|
-
const first = application.shutdown()
|
|
107
|
-
expect(application.shutdown()).toBe(first)
|
|
108
|
-
await first
|
|
109
|
-
expect(app.ui.dispose).toHaveBeenCalledOnce()
|
|
110
|
-
expect(app.runtime.dispose).toHaveBeenCalledOnce()
|
|
111
|
-
expect(application.terminal.drainInput).toHaveBeenCalledOnce()
|
|
112
|
-
expect(application.tui.stop).toHaveBeenCalledOnce()
|
|
113
|
-
})
|
|
114
|
-
})
|