@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,218 +0,0 @@
|
|
|
1
|
-
import type { AgentSession, ExtensionUIContext } from '@earendil-works/pi-coding-agent'
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
-
import { createTerminalCommands, type TerminalCommandHost } from './commands'
|
|
4
|
-
|
|
5
|
-
function setup() {
|
|
6
|
-
const ui = {
|
|
7
|
-
notify: vi.fn(), select: vi.fn(), confirm: vi.fn(), input: vi.fn(),
|
|
8
|
-
theme: { name: 'harness-titanium' }
|
|
9
|
-
} as unknown as ExtensionUIContext
|
|
10
|
-
const session = {
|
|
11
|
-
extensionRunner: { getRegisteredCommands: () => [{ name: 'accounts', description: 'Manage accounts' }] },
|
|
12
|
-
resourceLoader: {
|
|
13
|
-
getPrompts: () => ({ prompts: [{ name: 'review', description: 'Review code' }] }),
|
|
14
|
-
getSkills: () => ({ skills: [{ name: 'diagnose', description: 'Diagnose a problem' }] })
|
|
15
|
-
},
|
|
16
|
-
settingsManager: { getEnableSkillCommands: () => true },
|
|
17
|
-
compact: vi.fn(), reload: vi.fn(), setSessionName: vi.fn(),
|
|
18
|
-
getLastAssistantText: vi.fn(() => 'Actual assistant response'),
|
|
19
|
-
getSessionStats: () => ({ sessionId: 'real-session', tokens: { input: 7 } }),
|
|
20
|
-
exportToHtml: vi.fn(async (path: string) => path), exportToJsonl: vi.fn((path: string) => path)
|
|
21
|
-
} as unknown as AgentSession
|
|
22
|
-
const runtime = { newSession: vi.fn(), switchSession: vi.fn(), fork: vi.fn(), importFromJsonl: vi.fn(), reload: vi.fn(), navigateTree: vi.fn(), installationDeviceId: vi.fn(async () => '12345678-1234-4234-8234-123456789abc') }
|
|
23
|
-
const host: TerminalCommandHost = {
|
|
24
|
-
session: () => session, runtime: () => runtime as unknown as ReturnType<TerminalCommandHost['runtime']>, ui,
|
|
25
|
-
cwd: () => '/isolated/project', sessionDirectory: () => '/isolated/account/history',
|
|
26
|
-
refresh: vi.fn(), shutdown: vi.fn(), setEditorText: vi.fn(), copyText: vi.fn(async () => {}),
|
|
27
|
-
hotkeys: () => 'Escape: interrupt', changelog: async () => 'Harness release notes'
|
|
28
|
-
}
|
|
29
|
-
return { host, session, runtime, ui, commands: createTerminalCommands(host) }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('owned terminal commands', () => {
|
|
33
|
-
it('leaves ordinary prompts and supported extension/resource commands for the SDK', async () => {
|
|
34
|
-
const { commands, ui } = setup()
|
|
35
|
-
for (const text of ['hello', '/accounts', '/review module', '/skill:diagnose trace']) {
|
|
36
|
-
expect(await commands.execute(text)).toBe(false)
|
|
37
|
-
}
|
|
38
|
-
expect(ui.notify).not.toHaveBeenCalled()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('consumes unknown slash commands without sending them to the model', async () => {
|
|
42
|
-
const { commands, ui } = setup()
|
|
43
|
-
expect(await commands.execute('/typo dangerous operation')).toBe(true)
|
|
44
|
-
expect(ui.notify).toHaveBeenCalledWith(expect.stringContaining('Unknown command: /typo'), 'error')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('populates completion from builtins and loaded resources', () => {
|
|
48
|
-
const { commands } = setup()
|
|
49
|
-
const names = commands.completions().map(item => item.name)
|
|
50
|
-
expect(names).toEqual(expect.arrayContaining(['model', 'resume', 'accounts', 'review', 'skill:diagnose']))
|
|
51
|
-
expect(new Set(names).size).toBe(names.length)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('respects disabled skill commands', () => {
|
|
55
|
-
const { commands, session } = setup()
|
|
56
|
-
vi.spyOn(session.settingsManager, 'getEnableSkillCommands').mockReturnValue(false)
|
|
57
|
-
expect(commands.completions().map(item => item.name)).not.toContain('skill:diagnose')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('uses the scope checked runtime for explicit resume and import paths', async () => {
|
|
61
|
-
const { commands, runtime } = setup()
|
|
62
|
-
await commands.execute('/resume /isolated/account/history/session.jsonl')
|
|
63
|
-
expect(runtime.switchSession).toHaveBeenCalledWith('/isolated/account/history/session.jsonl')
|
|
64
|
-
await commands.execute('/import other.jsonl')
|
|
65
|
-
expect(runtime.importFromJsonl).toHaveBeenCalledWith('/isolated/project/other.jsonl')
|
|
66
|
-
await commands.execute('/new')
|
|
67
|
-
expect(runtime.newSession).toHaveBeenCalledOnce()
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('passes the Harness installation device ID to direct OAuth sign-in', async () => {
|
|
71
|
-
const { commands, session, runtime } = setup()
|
|
72
|
-
const login = vi.fn(async (_provider: string, _type: string, _interaction: unknown, options?: { getDeviceId?: () => string }) => options)
|
|
73
|
-
Object.assign(session, { modelRuntime: {
|
|
74
|
-
getProviders: () => [{ id: 'openai', name: 'OpenAI', auth: { oauth: {}, apiKey: undefined } }],
|
|
75
|
-
login
|
|
76
|
-
} })
|
|
77
|
-
|
|
78
|
-
await commands.execute('/login openai')
|
|
79
|
-
|
|
80
|
-
expect(runtime.installationDeviceId).toHaveBeenCalledOnce()
|
|
81
|
-
expect(login.mock.calls[0]?.[3]?.getDeviceId?.()).toBe('12345678-1234-4234-8234-123456789abc')
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('reports rejected navigation without claiming success', async () => {
|
|
85
|
-
const { commands, runtime, ui } = setup()
|
|
86
|
-
runtime.switchSession.mockRejectedValue(new Error('Session is outside the prepared project scope'))
|
|
87
|
-
expect(await commands.execute('/resume /isolated/account/history/session.jsonl')).toBe(true)
|
|
88
|
-
expect(ui.notify).toHaveBeenCalledWith('Session is outside the prepared project scope', 'error')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('copies actual assistant text and handles missing responses', async () => {
|
|
92
|
-
const { commands, host, session, ui } = setup()
|
|
93
|
-
await commands.execute('/copy')
|
|
94
|
-
expect(host.copyText).toHaveBeenCalledWith('Actual assistant response')
|
|
95
|
-
vi.mocked(session.getLastAssistantText).mockReturnValue(undefined)
|
|
96
|
-
await commands.execute('/copy')
|
|
97
|
-
expect(ui.notify).toHaveBeenCalledWith('No assistant response to copy yet', 'error')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('keeps multiline compaction instructions and session names', async () => {
|
|
101
|
-
const { commands, session } = setup()
|
|
102
|
-
await commands.execute('/compact Keep the API\nand authentication details')
|
|
103
|
-
expect(session.compact).toHaveBeenCalledWith('Keep the API\nand authentication details')
|
|
104
|
-
await commands.execute('/name Unicode 知識 🧠')
|
|
105
|
-
expect(session.setSessionName).toHaveBeenCalledWith('Unicode 知識 🧠')
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('exports supported formats through native session APIs', async () => {
|
|
109
|
-
const { commands, session } = setup()
|
|
110
|
-
await commands.execute('/export notes.jsonl')
|
|
111
|
-
expect(session.exportToJsonl).toHaveBeenCalledWith('/isolated/project/notes.jsonl')
|
|
112
|
-
await commands.execute('/export notes.html')
|
|
113
|
-
expect(session.exportToHtml).toHaveBeenCalledWith('/isolated/project/notes.html', { themeName: 'harness-titanium' })
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('does not upload anything after a declined share confirmation', async () => {
|
|
117
|
-
const { commands, ui, session } = setup()
|
|
118
|
-
vi.mocked(ui.confirm).mockResolvedValue(false)
|
|
119
|
-
await commands.execute('/share')
|
|
120
|
-
expect(ui.confirm).toHaveBeenCalledOnce()
|
|
121
|
-
expect(session.exportToHtml).not.toHaveBeenCalled()
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('shows actual application keyboard bindings and release notes', async () => {
|
|
125
|
-
const { commands, ui } = setup()
|
|
126
|
-
await commands.execute('/hotkeys')
|
|
127
|
-
expect(ui.notify).toHaveBeenCalledWith('Escape: interrupt', 'info')
|
|
128
|
-
await commands.execute('/changelog')
|
|
129
|
-
expect(ui.notify).toHaveBeenCalledWith('Harness release notes', 'info')
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('exits through the application lifecycle', async () => {
|
|
133
|
-
const { commands, host } = setup()
|
|
134
|
-
await commands.execute('/quit')
|
|
135
|
-
expect(host.shutdown).toHaveBeenCalledOnce()
|
|
136
|
-
})
|
|
137
|
-
it('validates thinking levels before mutating the session', async () => {
|
|
138
|
-
const { commands, session, ui } = setup()
|
|
139
|
-
Object.assign(session, { getAvailableThinkingLevels: () => ['off', 'high'], setThinkingLevel: vi.fn() })
|
|
140
|
-
await commands.execute('/thinking high')
|
|
141
|
-
expect(session.setThinkingLevel).toHaveBeenCalledWith('high', { persist: true })
|
|
142
|
-
await commands.execute('/thinking fabricated')
|
|
143
|
-
expect(session.setThinkingLevel).toHaveBeenCalledTimes(1)
|
|
144
|
-
expect(ui.notify).toHaveBeenCalledWith('Available thinking levels: off, high', 'error')
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('clones the real leaf through the runtime without inventing history', async () => {
|
|
148
|
-
const { commands, session, runtime, host } = setup()
|
|
149
|
-
Object.assign(session, { sessionManager: { getLeafId: () => 'leaf-id' } })
|
|
150
|
-
runtime.fork.mockResolvedValue({ cancelled: false })
|
|
151
|
-
await commands.execute('/clone')
|
|
152
|
-
expect(runtime.fork).toHaveBeenCalledWith('leaf-id', { position: 'at' })
|
|
153
|
-
expect(host.setEditorText).toHaveBeenCalledWith('')
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('uses masked input for secret provider credentials', async () => {
|
|
157
|
-
const { commands, session, host, ui } = setup()
|
|
158
|
-
const login = vi.fn(async (_provider, _type, interaction) => {
|
|
159
|
-
expect(await interaction.prompt({ type: 'secret', message: 'API key' })).toBe('private-key')
|
|
160
|
-
})
|
|
161
|
-
Object.assign(session, { modelRuntime: {
|
|
162
|
-
getProviders: () => [{ id: 'provider', name: 'Provider', auth: { apiKey: { login: true } } }], login
|
|
163
|
-
} })
|
|
164
|
-
host.secretInput = vi.fn(async () => 'private-key')
|
|
165
|
-
await commands.execute('/login provider')
|
|
166
|
-
expect(host.secretInput).toHaveBeenCalledWith('API key', undefined, { signal: undefined })
|
|
167
|
-
expect(ui.input).not.toHaveBeenCalled()
|
|
168
|
-
expect(ui.notify).not.toHaveBeenCalledWith(expect.stringContaining('private-key'), expect.anything())
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('fails closed when secure credential input is unavailable', async () => {
|
|
172
|
-
const { commands, session, ui } = setup()
|
|
173
|
-
Object.assign(session, { modelRuntime: {
|
|
174
|
-
getProviders: () => [{ id: 'provider', name: 'Provider', auth: { apiKey: { login: true } } }],
|
|
175
|
-
login: async (_provider: string, _type: string, interaction: { prompt(prompt: unknown): Promise<string> }) => interaction.prompt({ type: 'secret', message: 'API key' })
|
|
176
|
-
} })
|
|
177
|
-
await commands.execute('/login provider')
|
|
178
|
-
expect(ui.input).not.toHaveBeenCalled()
|
|
179
|
-
expect(ui.notify).toHaveBeenCalledWith('Secure credential entry is unavailable', 'error')
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('rejects resume paths outside account history before runtime navigation', async () => {
|
|
183
|
-
const { commands, runtime, ui } = setup()
|
|
184
|
-
await commands.execute('/resume /other-account/session.jsonl')
|
|
185
|
-
expect(runtime.switchSession).not.toHaveBeenCalled()
|
|
186
|
-
expect(ui.notify).toHaveBeenCalledWith(expect.stringContaining('this account history folder'), 'error')
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it('routes trust through the existing protected plugin workflow', async () => {
|
|
190
|
-
const { commands, session } = setup()
|
|
191
|
-
Object.assign(session, { prompt: vi.fn(async () => {}) })
|
|
192
|
-
vi.spyOn(session.extensionRunner, 'getRegisteredCommands').mockReturnValue([{ name: 'plugins' }] as never)
|
|
193
|
-
await commands.execute('/trust')
|
|
194
|
-
expect(session.prompt).toHaveBeenCalledWith('/plugins')
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('opens a reviewable Harness issue draft without attaching session history', async () => {
|
|
198
|
-
const { commands, host, ui } = setup()
|
|
199
|
-
host.openExternal = vi.fn(async () => {})
|
|
200
|
-
vi.mocked(ui.confirm).mockResolvedValue(false)
|
|
201
|
-
await commands.execute('/bug Unicode issue 知識')
|
|
202
|
-
expect(host.openExternal).not.toHaveBeenCalled()
|
|
203
|
-
vi.mocked(ui.confirm).mockResolvedValue(true)
|
|
204
|
-
await commands.execute('/bug Unicode issue 知識')
|
|
205
|
-
const url = new URL(vi.mocked(host.openExternal).mock.calls[0][0])
|
|
206
|
-
expect(url.pathname).toBe('/tanntnny/xingular-harness/issues/new')
|
|
207
|
-
expect(url.searchParams.get('title')).toBe('Unicode issue 知識')
|
|
208
|
-
expect(url.searchParams.get('body')).toContain('Harness')
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('reloads through the stable runtime lifecycle', async () => {
|
|
212
|
-
const { commands, runtime, session } = setup()
|
|
213
|
-
await commands.execute('/reload')
|
|
214
|
-
expect(runtime.reload).toHaveBeenCalledOnce()
|
|
215
|
-
expect(session.reload).not.toHaveBeenCalled()
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
})
|
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process'
|
|
2
|
-
import { mkdtemp, rm } from 'node:fs/promises'
|
|
3
|
-
import { tmpdir } from 'node:os'
|
|
4
|
-
import { isAbsolute, join, relative, resolve } from 'node:path'
|
|
5
|
-
import { promisify } from 'node:util'
|
|
6
|
-
import { ModelSelectorComponent, TreeSelectorComponent, SessionManager, type AgentSession, type ExtensionUIContext } from '@earendil-works/pi-coding-agent'
|
|
7
|
-
import { SettingsPanel, type TerminalSettingItem } from './settings'
|
|
8
|
-
import manifest from '../../../package.json'
|
|
9
|
-
import type { TerminalRuntime } from './runtime'
|
|
10
|
-
|
|
11
|
-
export interface TerminalCommandHost {
|
|
12
|
-
session(): AgentSession
|
|
13
|
-
/** Navigation is scope checked by the Harness runtime adapter. */
|
|
14
|
-
runtime(): Pick<TerminalRuntime, 'newSession' | 'switchSession' | 'fork' | 'importFromJsonl' | 'reload' | 'navigateTree' | 'installationDeviceId'>
|
|
15
|
-
ui: ExtensionUIContext
|
|
16
|
-
cwd(): string
|
|
17
|
-
sessionDirectory(): string
|
|
18
|
-
refresh(): void
|
|
19
|
-
shutdown(): void
|
|
20
|
-
setEditorText(text: string): void
|
|
21
|
-
copyText(text: string): Promise<void>
|
|
22
|
-
secretInput?(title: string, placeholder?: string, options?: { signal?: AbortSignal }): Promise<string | undefined>
|
|
23
|
-
openExternal?(url: string): Promise<void>
|
|
24
|
-
suspend?(): Promise<void>
|
|
25
|
-
hotkeys?(): string
|
|
26
|
-
changelog?(): Promise<string>
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface TerminalCommandCompletion { name: string; description?: string }
|
|
30
|
-
|
|
31
|
-
const BUILTINS: TerminalCommandCompletion[] = [
|
|
32
|
-
{ name: 'help', description: 'Available terminal commands' },
|
|
33
|
-
{ name: 'new', description: 'Start a new session' },
|
|
34
|
-
{ name: 'resume', description: 'Resume a session in this account and folder' },
|
|
35
|
-
{ name: 'tree', description: 'Navigate the session tree' },
|
|
36
|
-
{ name: 'fork', description: 'Fork from a user message' },
|
|
37
|
-
{ name: 'model', description: 'Choose a model' },
|
|
38
|
-
{ name: 'thinking', description: 'Choose thinking effort' },
|
|
39
|
-
{ name: 'clone', description: 'Clone the current branch to a new session' },
|
|
40
|
-
{ name: 'scoped-models', description: 'Choose models for cycling' },
|
|
41
|
-
{ name: 'settings', description: 'Session and terminal settings' },
|
|
42
|
-
{ name: 'name', description: 'Name the current session' },
|
|
43
|
-
{ name: 'session', description: 'Current session statistics' },
|
|
44
|
-
{ name: 'compact', description: 'Compact the current context' },
|
|
45
|
-
{ name: 'reload', description: 'Reload trusted resources and keyboard bindings' },
|
|
46
|
-
{ name: 'trust', description: 'Review plugin code and project trust' },
|
|
47
|
-
{ name: 'bug', description: 'Open a Harness issue draft' },
|
|
48
|
-
{ name: 'login', description: 'Sign in to a provider in this isolated account' },
|
|
49
|
-
{ name: 'logout', description: 'Remove a stored provider credential' },
|
|
50
|
-
{ name: 'export', description: 'Export this branch to HTML or JSONL' },
|
|
51
|
-
{ name: 'import', description: 'Import a session JSONL file' },
|
|
52
|
-
{ name: 'share', description: 'Share an HTML export as a secret GitHub gist' },
|
|
53
|
-
{ name: 'copy', description: 'Copy the last assistant response' },
|
|
54
|
-
{ name: 'changelog', description: 'Harness release notes' },
|
|
55
|
-
{ name: 'hotkeys', description: 'Keyboard controls' },
|
|
56
|
-
{ name: 'quit', description: 'Restore the terminal and exit' }
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
export function createTerminalCommands(host: TerminalCommandHost) {
|
|
60
|
-
const notify = (text: string) => host.ui.notify(text, 'info')
|
|
61
|
-
const choose = async <T>(title: string, items: T[], label: (item: T, index: number) => string) => {
|
|
62
|
-
const labels = items.map(label)
|
|
63
|
-
const selected = await host.ui.select(title, labels)
|
|
64
|
-
return selected === undefined ? undefined : items[labels.indexOf(selected)]
|
|
65
|
-
}
|
|
66
|
-
const completions = (): TerminalCommandCompletion[] => {
|
|
67
|
-
const session = host.session()
|
|
68
|
-
const commands = new Map(BUILTINS.map(command => [command.name, command]))
|
|
69
|
-
for (const command of session.extensionRunner.getRegisteredCommands()) {
|
|
70
|
-
if (!commands.has(command.name)) commands.set(command.name, { name: command.name, description: command.description })
|
|
71
|
-
}
|
|
72
|
-
for (const prompt of session.resourceLoader.getPrompts().prompts) {
|
|
73
|
-
if (!commands.has(prompt.name)) commands.set(prompt.name, { name: prompt.name, description: prompt.description })
|
|
74
|
-
}
|
|
75
|
-
if (session.settingsManager.getEnableSkillCommands()) {
|
|
76
|
-
for (const skill of session.resourceLoader.getSkills().skills) commands.set(`skill:${skill.name}`, { name: `skill:${skill.name}`, description: skill.description })
|
|
77
|
-
}
|
|
78
|
-
return [...commands.values()]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function settings() {
|
|
82
|
-
const session = host.session()
|
|
83
|
-
const settings = session.settingsManager
|
|
84
|
-
const booleanSettings = [
|
|
85
|
-
['Auto compaction', () => session.autoCompactionEnabled, (value: boolean) => session.setAutoCompactionEnabled(value)],
|
|
86
|
-
['Auto retry', () => session.autoRetryEnabled, (value: boolean) => session.setAutoRetryEnabled(value)],
|
|
87
|
-
['Show images', () => settings.getShowImages(), (value: boolean) => settings.setShowImages(value)],
|
|
88
|
-
['Resize images', () => settings.getImageAutoResize(), (value: boolean) => settings.setImageAutoResize(value)],
|
|
89
|
-
['Block images', () => settings.getBlockImages(), (value: boolean) => settings.setBlockImages(value)],
|
|
90
|
-
['Skill commands', () => settings.getEnableSkillCommands(), (value: boolean) => settings.setEnableSkillCommands(value)],
|
|
91
|
-
['Hide thinking', () => settings.getHideThinkingBlock(), (value: boolean) => settings.setHideThinkingBlock(value)],
|
|
92
|
-
['Hardware cursor', () => settings.getShowHardwareCursor(), (value: boolean) => settings.setShowHardwareCursor(value)],
|
|
93
|
-
['Terminal progress', () => settings.getShowTerminalProgress(), (value: boolean) => settings.setShowTerminalProgress(value)]
|
|
94
|
-
] as const
|
|
95
|
-
let selectedId: string | undefined
|
|
96
|
-
const locations = [
|
|
97
|
-
['Context', 'Compaction'], ['Model', 'Recovery'], ['Appearance', 'Images'], ['Appearance', 'Images'],
|
|
98
|
-
['Context', 'Images'], ['Interaction', 'Commands'], ['Appearance', 'Display'], ['Interaction', 'Editor'],
|
|
99
|
-
['Interaction', 'Terminal'], ['Model', 'Thinking'], ['Interaction', 'Queues'], ['Interaction', 'Queues'],
|
|
100
|
-
['Appearance', 'Theme'], ['Interaction', 'Keyboard']
|
|
101
|
-
]
|
|
102
|
-
for (;;) {
|
|
103
|
-
const options = [...booleanSettings.map(([label, get]) => `${label}: ${get() ? 'on' : 'off'}`),
|
|
104
|
-
`Thinking: ${session.thinkingLevel}`, `Steering: ${session.steeringMode}`, `Follow-up: ${session.followUpMode}`,
|
|
105
|
-
`Theme: ${host.ui.theme.name}`, 'Keyboard controls', 'Done']
|
|
106
|
-
const descriptions = [
|
|
107
|
-
'Compact the context automatically when the session approaches its limit.', 'Retry eligible provider failures automatically.',
|
|
108
|
-
'Display actual image content returned by tools.', 'Resize image attachments before provider requests.',
|
|
109
|
-
'Block image attachments in provider requests.', 'Expose commands for the skills loaded in this session.',
|
|
110
|
-
'Collapse assistant thinking blocks in the transcript.', 'Display the terminal hardware cursor for editing and input methods.',
|
|
111
|
-
'Report active work through supported terminal progress controls.', 'Reasoning effort for the current model.',
|
|
112
|
-
'Delivery mode for messages that steer an active run.', 'Delivery mode for messages queued after a run.',
|
|
113
|
-
'Theme loaded from the trusted resource catalog.', 'View the current application keyboard bindings.'
|
|
114
|
-
]
|
|
115
|
-
const items: TerminalSettingItem[] = options.slice(0, -1).map((option, index) => {
|
|
116
|
-
const split = option.indexOf(':')
|
|
117
|
-
return { id: String(index), tab: locations[index][0], group: locations[index][1], label: split < 0 ? option : option.slice(0, split), value: split < 0 ? '' : option.slice(split + 1).trim(), description: descriptions[index] }
|
|
118
|
-
})
|
|
119
|
-
const groups = ['Theme', 'Display', 'Images', 'Thinking', 'Recovery', 'Editor', 'Queues', 'Commands', 'Terminal', 'Keyboard', 'Compaction']
|
|
120
|
-
items.sort((a, b) => groups.indexOf(a.group) - groups.indexOf(b.group))
|
|
121
|
-
const id = await host.ui.custom<string | undefined>((tui, theme, _keys, done) => new SettingsPanel(tui, () => theme, items, done, selectedId), { overlay: true, overlayOptions: { width: '100%', maxHeight: '100%', anchor: 'top-left' } })
|
|
122
|
-
if (id === undefined) return
|
|
123
|
-
selectedId = id
|
|
124
|
-
const index = Number(id)
|
|
125
|
-
const selected = options[index]
|
|
126
|
-
if (index < booleanSettings.length) {
|
|
127
|
-
const [, get, set] = booleanSettings[index]!
|
|
128
|
-
set(!get())
|
|
129
|
-
} else if (selected.startsWith('Thinking:')) {
|
|
130
|
-
const level = await host.ui.select('Thinking effort', session.getAvailableThinkingLevels())
|
|
131
|
-
if (level) session.setThinkingLevel(level as AgentSession['thinkingLevel'], { persist: true })
|
|
132
|
-
} else if (selected.startsWith('Steering:') || selected.startsWith('Follow-up:')) {
|
|
133
|
-
const mode = await host.ui.select('Queue delivery', ['all', 'one-at-a-time'])
|
|
134
|
-
if (mode === 'all' || mode === 'one-at-a-time') {
|
|
135
|
-
if (selected.startsWith('Steering:')) session.setSteeringMode(mode)
|
|
136
|
-
else session.setFollowUpMode(mode)
|
|
137
|
-
}
|
|
138
|
-
} else if (selected.startsWith('Theme:')) {
|
|
139
|
-
const theme = await host.ui.select('Theme', host.ui.getAllThemes().map(item => item.name))
|
|
140
|
-
if (theme) {
|
|
141
|
-
const result = host.ui.setTheme(theme)
|
|
142
|
-
if (!result.success) throw new Error(result.error)
|
|
143
|
-
settings.setTheme(theme)
|
|
144
|
-
}
|
|
145
|
-
} else notify(host.hotkeys?.() ?? 'Use /help for commands. Keyboard bindings are managed by the terminal application.')
|
|
146
|
-
host.refresh()
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function login(providerRef: string) {
|
|
151
|
-
const runtime = host.session().modelRuntime
|
|
152
|
-
const providers = runtime.getProviders().filter(provider => provider.auth.oauth || provider.auth.apiKey?.login)
|
|
153
|
-
const provider = providerRef
|
|
154
|
-
? providers.find(item => item.id === providerRef || item.name.toLowerCase() === providerRef.toLowerCase())
|
|
155
|
-
: await choose('Sign in to provider', providers, item => `${item.name} (${item.id})`)
|
|
156
|
-
if (!provider) { if (providerRef) throw new Error(`No sign-in provider matches ${providerRef}`); return }
|
|
157
|
-
const types = [...(provider.auth.oauth ? ['oauth' as const] : []), ...(provider.auth.apiKey?.login ? ['api_key' as const] : [])]
|
|
158
|
-
const type = types.length === 1 ? types[0] : await host.ui.select('Authentication method', types)
|
|
159
|
-
if (type !== 'oauth' && type !== 'api_key') return
|
|
160
|
-
const deviceId = type === 'oauth' ? await host.runtime().installationDeviceId() : undefined
|
|
161
|
-
await runtime.login(provider.id, type, {
|
|
162
|
-
prompt: async prompt => {
|
|
163
|
-
let result: string | undefined
|
|
164
|
-
if (prompt.type === 'select') {
|
|
165
|
-
const option = await choose(prompt.message, [...prompt.options], item => item.label)
|
|
166
|
-
result = option?.id
|
|
167
|
-
} else if (prompt.type === 'secret') {
|
|
168
|
-
if (!host.secretInput) throw new Error('Secure credential entry is unavailable')
|
|
169
|
-
result = await host.secretInput(prompt.message, prompt.placeholder, { signal: prompt.signal })
|
|
170
|
-
} else result = await host.ui.input(prompt.message, prompt.placeholder, { signal: prompt.signal })
|
|
171
|
-
if (result === undefined) throw new Error('Sign-in cancelled')
|
|
172
|
-
return result
|
|
173
|
-
},
|
|
174
|
-
notify: event => {
|
|
175
|
-
if (event.type === 'auth_url') {
|
|
176
|
-
notify(`${event.instructions ?? 'Open this sign-in URL:'}\n${event.url}`)
|
|
177
|
-
void host.openExternal?.(event.url).catch(error => host.ui.notify(String(error), 'warning'))
|
|
178
|
-
} else if (event.type === 'device_code') notify(`${event.verificationUri}\nCode: ${event.userCode}`)
|
|
179
|
-
else notify(event.message)
|
|
180
|
-
}
|
|
181
|
-
}, deviceId ? { getDeviceId: () => deviceId } : undefined)
|
|
182
|
-
notify(`Signed in to ${provider.name}`)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function share() {
|
|
186
|
-
if (!await host.ui.confirm('Share session', 'Upload the current session as a secret GitHub gist? Anyone with its link can read its contents.')) return
|
|
187
|
-
const directory = await mkdtemp(join(tmpdir(), 'harness-share-'))
|
|
188
|
-
try {
|
|
189
|
-
const path = await host.session().exportToHtml(join(directory, 'session.html'), { themeName: host.ui.theme.name })
|
|
190
|
-
const result = await promisify(execFile)('gh', ['gist', 'create', '--public=false', path], { timeout: 60_000, maxBuffer: 1024 * 1024 })
|
|
191
|
-
notify(`Session shared: ${result.stdout.trim()}`)
|
|
192
|
-
} finally { await rm(directory, { recursive: true, force: true }) }
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function execute(text: string): Promise<boolean> {
|
|
196
|
-
const match = /^\/([^\s]+)(?:\s+([\s\S]*))?$/.exec(text.trim())
|
|
197
|
-
if (!match) return false
|
|
198
|
-
const name = match[1]!
|
|
199
|
-
const argument = match[2]?.trim() ?? ''
|
|
200
|
-
if (!BUILTINS.some(item => item.name === name)) {
|
|
201
|
-
if (completions().some(item => item.name === name)) return false
|
|
202
|
-
host.ui.notify(`Unknown command: /${name}. Use /help to list commands.`, 'error')
|
|
203
|
-
return true
|
|
204
|
-
}
|
|
205
|
-
try {
|
|
206
|
-
const session = host.session()
|
|
207
|
-
switch (name) {
|
|
208
|
-
case 'help': notify(completions().map(item => `/${item.name} ${item.description ?? ''}`).join('\n')); break
|
|
209
|
-
case 'quit': host.shutdown(); break
|
|
210
|
-
case 'new': await host.runtime().newSession(); break
|
|
211
|
-
case 'resume': {
|
|
212
|
-
let path = argument
|
|
213
|
-
if (!path) {
|
|
214
|
-
const sessions = await SessionManager.list(host.cwd(), host.sessionDirectory())
|
|
215
|
-
const selected = await choose('Resume session', sessions, (item, index) => `${index + 1}. ${item.name ?? item.firstMessage ?? item.id} ${item.modified.toLocaleString()}`)
|
|
216
|
-
if (!selected) break
|
|
217
|
-
path = selected.path
|
|
218
|
-
}
|
|
219
|
-
const target = resolve(host.cwd(), path)
|
|
220
|
-
const within = relative(host.sessionDirectory(), target)
|
|
221
|
-
if (within === '..' || within.startsWith('../') || within.startsWith('..\\') || isAbsolute(within)) {
|
|
222
|
-
throw new Error('Resume sessions from this account history folder. Use /import to explicitly import a session.')
|
|
223
|
-
}
|
|
224
|
-
await host.runtime().switchSession(target)
|
|
225
|
-
break
|
|
226
|
-
}
|
|
227
|
-
case 'fork': {
|
|
228
|
-
const message = await choose('Fork before message', session.getUserMessagesForForking(), (item, index) => `${index + 1}. ${item.text.replace(/\s+/g, ' ').slice(0, 160)}`)
|
|
229
|
-
if (message) {
|
|
230
|
-
const result = await host.runtime().fork(message.entryId)
|
|
231
|
-
if (!result.cancelled && result.selectedText) host.setEditorText(result.selectedText)
|
|
232
|
-
}
|
|
233
|
-
break
|
|
234
|
-
}
|
|
235
|
-
case 'clone': {
|
|
236
|
-
const leaf = session.sessionManager.getLeafId()
|
|
237
|
-
if (!leaf) throw new Error('Nothing to clone yet')
|
|
238
|
-
const result = await host.runtime().fork(leaf, { position: 'at' })
|
|
239
|
-
if (!result.cancelled) { host.setEditorText(''); notify('Cloned to new session') }
|
|
240
|
-
break
|
|
241
|
-
}
|
|
242
|
-
case 'tree': {
|
|
243
|
-
const tree = session.sessionManager.getTree()
|
|
244
|
-
if (!tree.length) throw new Error('No entries in this session')
|
|
245
|
-
const selected = await host.ui.custom<string | undefined>((tui, _theme, _keys, done) => {
|
|
246
|
-
const selector = new TreeSelectorComponent(tree, session.sessionManager.getLeafId(), tui.terminal.rows,
|
|
247
|
-
done, () => done(undefined), (entryId, label) => session.sessionManager.appendLabelChange(entryId, label),
|
|
248
|
-
undefined, session.settingsManager.getTreeFilterMode())
|
|
249
|
-
selector.onCopy = text => { if (text) void host.copyText(text).catch(error => host.ui.notify(String(error), 'error')) }
|
|
250
|
-
return selector
|
|
251
|
-
})
|
|
252
|
-
if (selected && selected !== session.sessionManager.getLeafId()) {
|
|
253
|
-
const summarize = await host.ui.confirm('Navigate session', 'Summarize the abandoned branch before navigating?')
|
|
254
|
-
const result = await host.runtime().navigateTree(selected, { summarize })
|
|
255
|
-
if (!result.cancelled && result.editorText) host.setEditorText(result.editorText)
|
|
256
|
-
}
|
|
257
|
-
break
|
|
258
|
-
}
|
|
259
|
-
case 'thinking': {
|
|
260
|
-
const levels = session.getAvailableThinkingLevels()
|
|
261
|
-
const level = argument || await host.ui.select('Thinking effort', levels)
|
|
262
|
-
if (!level) break
|
|
263
|
-
if (!levels.includes(level as AgentSession['thinkingLevel'])) throw new Error(`Available thinking levels: ${levels.join(', ')}`)
|
|
264
|
-
session.setThinkingLevel(level as AgentSession['thinkingLevel'], { persist: true })
|
|
265
|
-
break
|
|
266
|
-
}
|
|
267
|
-
case 'model': {
|
|
268
|
-
const provider = session.settingsManager.getDefaultProvider()
|
|
269
|
-
const id = session.settingsManager.getDefaultModel()
|
|
270
|
-
const selected = await host.ui.custom<{ model: NonNullable<AgentSession['model']>; persist: boolean } | undefined>((tui, _theme, _keys, done) =>
|
|
271
|
-
new ModelSelectorComponent(tui, session.model, session.modelRuntime, session.scopedModels,
|
|
272
|
-
model => done({ model, persist: false }), () => done(undefined), argument || undefined,
|
|
273
|
-
model => done({ model, persist: true }), provider && id ? { provider, id } : undefined))
|
|
274
|
-
if (selected) await session.setModel(selected.model, { persist: selected.persist })
|
|
275
|
-
break
|
|
276
|
-
}
|
|
277
|
-
case 'scoped-models': {
|
|
278
|
-
const models = await session.modelRuntime.getAvailable()
|
|
279
|
-
const selected = new Set(session.scopedModels.map(item => `${item.model.provider}/${item.model.id}`))
|
|
280
|
-
for (;;) {
|
|
281
|
-
const labels = models.map(model => `${selected.has(`${model.provider}/${model.id}`) ? '☑' : '☐'} ${model.provider}/${model.id}`)
|
|
282
|
-
const choice = await host.ui.select('Models for cycling', [...labels, 'Save', 'Cancel'])
|
|
283
|
-
if (!choice || choice === 'Cancel') break
|
|
284
|
-
if (choice === 'Save') {
|
|
285
|
-
session.setScopedModels(models.filter(model => selected.has(`${model.provider}/${model.id}`)).map(model => ({ model })))
|
|
286
|
-
session.settingsManager.setEnabledModels([...selected])
|
|
287
|
-
break
|
|
288
|
-
}
|
|
289
|
-
const model = models[labels.indexOf(choice)]
|
|
290
|
-
if (model) { const key = `${model.provider}/${model.id}`; if (selected.has(key)) selected.delete(key); else selected.add(key) }
|
|
291
|
-
}
|
|
292
|
-
break
|
|
293
|
-
}
|
|
294
|
-
case 'settings': await settings(); break
|
|
295
|
-
case 'name': {
|
|
296
|
-
const name = argument || await host.ui.input('Session name', session.sessionName)
|
|
297
|
-
if (name !== undefined) session.setSessionName(name)
|
|
298
|
-
break
|
|
299
|
-
}
|
|
300
|
-
case 'session': notify(JSON.stringify(session.getSessionStats(), null, 2)); break
|
|
301
|
-
case 'compact': await session.compact(argument || undefined); break
|
|
302
|
-
case 'reload': await host.runtime().reload(); notify('Trusted resources reloaded'); break
|
|
303
|
-
case 'trust': {
|
|
304
|
-
if (!session.extensionRunner.getRegisteredCommands().some(command => command.name === 'plugins')) throw new Error('Plugin trust controls are unavailable in this session.')
|
|
305
|
-
await session.prompt('/plugins')
|
|
306
|
-
break
|
|
307
|
-
}
|
|
308
|
-
case 'bug': {
|
|
309
|
-
const description = argument || await host.ui.input('Describe the Harness issue')
|
|
310
|
-
if (!description) break
|
|
311
|
-
const repository = manifest.repository.url.replace(/^git\+/, '').replace(/\.git$/, '')
|
|
312
|
-
const url = new URL(`${repository}/issues/new`)
|
|
313
|
-
url.searchParams.set('title', description.split('\n')[0]!.slice(0, 120))
|
|
314
|
-
url.searchParams.set('body', `${description}\n\nHarness ${manifest.version} · ${process.platform} ${process.arch} · Node ${process.version}`)
|
|
315
|
-
if (!host.openExternal) throw new Error(`Open the issue draft: ${url}`)
|
|
316
|
-
if (await host.ui.confirm('Open issue draft?', `${description}\n\nOpen the Harness issue form in your browser to review and submit.`)) await host.openExternal(url.href)
|
|
317
|
-
break
|
|
318
|
-
}
|
|
319
|
-
case 'login': await login(argument); break
|
|
320
|
-
case 'logout': {
|
|
321
|
-
const credentials = await session.modelRuntime.listCredentials()
|
|
322
|
-
const provider = argument || await host.ui.select('Sign out of provider', credentials.map(item => item.providerId))
|
|
323
|
-
if (provider && await host.ui.confirm('Sign out', `Remove the stored credential for ${provider} from this account?`)) {
|
|
324
|
-
await session.modelRuntime.logout(provider)
|
|
325
|
-
notify(`Signed out of ${provider}`)
|
|
326
|
-
}
|
|
327
|
-
break
|
|
328
|
-
}
|
|
329
|
-
case 'copy': {
|
|
330
|
-
const response = session.getLastAssistantText()
|
|
331
|
-
if (!response) throw new Error('No assistant response to copy yet')
|
|
332
|
-
await host.copyText(response)
|
|
333
|
-
notify('Copied last assistant response')
|
|
334
|
-
break
|
|
335
|
-
}
|
|
336
|
-
case 'export': {
|
|
337
|
-
const output = argument || await host.ui.input('Export path (.html or .jsonl)', 'session.html')
|
|
338
|
-
if (!output) break
|
|
339
|
-
const path = resolve(host.cwd(), output)
|
|
340
|
-
notify(`Exported to ${path.endsWith('.jsonl') ? session.exportToJsonl(path) : await session.exportToHtml(path, { themeName: host.ui.theme.name })}`)
|
|
341
|
-
break
|
|
342
|
-
}
|
|
343
|
-
case 'import': {
|
|
344
|
-
const input = argument || await host.ui.input('Import session JSONL path')
|
|
345
|
-
if (input) await host.runtime().importFromJsonl(resolve(host.cwd(), input))
|
|
346
|
-
break
|
|
347
|
-
}
|
|
348
|
-
case 'share': await share(); break
|
|
349
|
-
case 'hotkeys': notify(host.hotkeys?.() ?? 'Keyboard bindings are managed by the terminal application.'); break
|
|
350
|
-
case 'changelog': {
|
|
351
|
-
if (!host.changelog) throw new Error('No Harness release notes are installed')
|
|
352
|
-
notify(await host.changelog())
|
|
353
|
-
break
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
host.refresh()
|
|
357
|
-
} catch (error) { host.ui.notify(error instanceof Error ? error.message : String(error), 'error') }
|
|
358
|
-
return true
|
|
359
|
-
}
|
|
360
|
-
return { execute, completions }
|
|
361
|
-
}
|