@xingular/harness 0.1.6 → 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 +107 -0
- package/LICENSE +202 -0
- package/NOTICE.md +60 -0
- package/README.md +265 -71
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +14842 -9445
- 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 +1170 -62
- package/docs/README.md +34 -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-0001-owned-terminal.md +87 -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 +671 -0
- package/package.json +48 -12
- package/src/accounts/accounts.test.ts +0 -223
- package/src/accounts/accounts.ts +0 -522
- 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 -114
- 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 -299
- package/src/index.ts +0 -70
- 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 -289
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -406
- package/src/memory/caller.ts +0 -57
- 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 -330
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -138
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -576
- package/src/memory/recall.test.ts +0 -104
- package/src/memory/recall.ts +0 -271
- 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 -314
- 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 -310
- package/src/sessions/conversation.test.ts +0 -150
- 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 -32
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -294
- 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 -569
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -455
- package/src/sessions/sessions.ts +0 -295
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -407
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/controller.ts +0 -227
- package/src/terminal/launch.test.ts +0 -34
- package/src/terminal/launch.ts +0 -88
- package/src/terminal/piExtension.ts +0 -210
- 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 -221
- package/src/terminal/standalone.test.ts +0 -130
- package/src/terminal/standalone.ts +0 -144
- package/src/terminal/terminal.test.ts +0 -330
- package/src/terminal/terminals.ts +0 -443
- package/src/tools/askUser.ts +0 -45
- package/src/tools/definitions.ts +0 -187
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- package/src/tools/types.ts +0 -39
- package/src/web/safeRequest.ts +0 -171
- package/src/web/text.ts +0 -53
- package/src/web/tools.ts +0 -134
- package/src/web/web.test.ts +0 -141
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// The session socket: how a process the harness did not start in-process (the `pi` command in
|
|
2
|
+
// a terminal, with its extension and the `xagent-wiki` / `xagent-mcp` commands) reaches the
|
|
3
|
+
// session it belongs to. Owner-only Unix socket (a named pipe on Windows), newline-delimited JSON.
|
|
4
|
+
// A connection carries one request unless the request asks to `keep` it open, in which case the
|
|
5
|
+
// reply leaves it open for the next request. Each terminal session holds a random token bound to
|
|
6
|
+
// its caller, its connected-apps grant, and its controller; the token stops working when the
|
|
7
|
+
// terminal ends.
|
|
8
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
9
|
+
import { chmodSync, mkdirSync, rmSync, statSync } from 'node:fs';
|
|
10
|
+
import { createServer } from 'node:net';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { log } from "../core/bus.js";
|
|
14
|
+
import { HarnessError, toErrorShape } from "../core/errors.js";
|
|
15
|
+
import { newId } from "../core/fs.js";
|
|
16
|
+
const MAX_REQUEST_BYTES = 1024 * 1024;
|
|
17
|
+
/** macOS refuses a socket path longer than 103 bytes. */
|
|
18
|
+
const MAX_SOCKET_PATH = 100;
|
|
19
|
+
/**
|
|
20
|
+
* Windows has no socket files for Node's `net`: it listens on a named pipe, which lives in a
|
|
21
|
+
* machine-wide namespace. A random name keeps another local process from guessing it and standing
|
|
22
|
+
* in front of the session; the token still decides what a caller may do.
|
|
23
|
+
*/
|
|
24
|
+
export function socketPath(stateDir) {
|
|
25
|
+
if (process.platform === 'win32')
|
|
26
|
+
return `\\\\.\\pipe\\xingular-harness-${randomBytes(12).toString('hex')}`;
|
|
27
|
+
const path = join(stateDir, 'run', 'harness.sock');
|
|
28
|
+
if (Buffer.byteLength(path) <= MAX_SOCKET_PATH)
|
|
29
|
+
return path;
|
|
30
|
+
return join(tmpdir(), `xh-${createHash('sha256').update(path).digest('hex').slice(0, 16)}.sock`);
|
|
31
|
+
}
|
|
32
|
+
function alternateSocketPath(path) {
|
|
33
|
+
const candidate = join(dirname(path), `harness-${process.pid}-${randomBytes(8).toString('hex')}.sock`);
|
|
34
|
+
return Buffer.byteLength(candidate) <= MAX_SOCKET_PATH ? candidate : join(tmpdir(), `xh-${randomBytes(16).toString('hex')}.sock`);
|
|
35
|
+
}
|
|
36
|
+
/** A named pipe has no folder, file mode, or owner to check or clean up. */
|
|
37
|
+
const isPipePath = (path) => path.startsWith('\\\\.\\pipe\\');
|
|
38
|
+
/** Names the folder and the user when the operating system refuses the socket, so the cause shows. */
|
|
39
|
+
function refusedBind(path, error) {
|
|
40
|
+
const code = error?.code;
|
|
41
|
+
if (code !== 'EACCES' && code !== 'EPERM')
|
|
42
|
+
return error;
|
|
43
|
+
if (isPipePath(path)) {
|
|
44
|
+
return new HarnessError('session_socket_denied', `The session pipe ${path} could not be created (${code}).`, 'Start the harness again from your own user session. If it keeps failing, report this code and the Windows version.');
|
|
45
|
+
}
|
|
46
|
+
let folder;
|
|
47
|
+
try {
|
|
48
|
+
const stat = statSync(dirname(path));
|
|
49
|
+
folder = `${dirname(path)} is owned by uid ${stat.uid} with mode ${(stat.mode & 0o777).toString(8)}`;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
folder = `${dirname(path)} could not be read`;
|
|
53
|
+
}
|
|
54
|
+
const uid = process.getuid?.() ?? 'unknown';
|
|
55
|
+
return new HarnessError('session_socket_denied', `The session socket ${path} could not be created (${code}). ${folder}; this process runs as uid ${uid}.`, 'Run the harness from your own terminal, as the user that owns its state folder, or choose a state folder you own with --state.');
|
|
56
|
+
}
|
|
57
|
+
export class SessionSocket {
|
|
58
|
+
handlers;
|
|
59
|
+
server = null;
|
|
60
|
+
listening = null;
|
|
61
|
+
socketPathValue;
|
|
62
|
+
grants = new Map();
|
|
63
|
+
/**
|
|
64
|
+
* A second key per token, handed out once. The token is in the session's environment, where a
|
|
65
|
+
* command the model runs can read it; this key is claimed by the session's extension as it
|
|
66
|
+
* loads, before any model turn, and kept in its memory only.
|
|
67
|
+
*/
|
|
68
|
+
controls = new Map();
|
|
69
|
+
clients = new Set();
|
|
70
|
+
constructor(path, handlers) {
|
|
71
|
+
this.handlers = handlers;
|
|
72
|
+
this.socketPathValue = path;
|
|
73
|
+
}
|
|
74
|
+
get path() { return this.socketPathValue; }
|
|
75
|
+
issue(grant) {
|
|
76
|
+
const token = `${newId()}${newId()}`.replaceAll('-', '');
|
|
77
|
+
this.grants.set(token, grant);
|
|
78
|
+
return token;
|
|
79
|
+
}
|
|
80
|
+
revoke(token) {
|
|
81
|
+
this.grants.delete(token);
|
|
82
|
+
this.controls.delete(token);
|
|
83
|
+
}
|
|
84
|
+
revokeSession(sessionId) {
|
|
85
|
+
for (const [token, grant] of this.grants)
|
|
86
|
+
if (grant.sessionId === sessionId)
|
|
87
|
+
this.revoke(token);
|
|
88
|
+
}
|
|
89
|
+
/** Runs one request line and returns the reply line's object. */
|
|
90
|
+
async handle(line, connection) {
|
|
91
|
+
try {
|
|
92
|
+
let request;
|
|
93
|
+
try {
|
|
94
|
+
request = JSON.parse(line);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new HarnessError('invalid_request', 'The request is not JSON.');
|
|
98
|
+
}
|
|
99
|
+
connection?.keep?.(request.keep === true);
|
|
100
|
+
const token = typeof request.token === 'string' ? request.token : '';
|
|
101
|
+
const grant = this.grants.get(token);
|
|
102
|
+
if (!grant)
|
|
103
|
+
throw new HarnessError('unauthorized', 'This session token is not valid. It ends when its terminal session ends.');
|
|
104
|
+
if (typeof request.event === 'string') {
|
|
105
|
+
if (!grant.terminal)
|
|
106
|
+
throw new HarnessError('unknown_method', 'This session has no terminal controller.');
|
|
107
|
+
connection?.waiting(grant);
|
|
108
|
+
try {
|
|
109
|
+
return { result: (await grant.terminal.accept(request.event, (request.data ?? {}))) ?? {} };
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
connection?.waiting(null);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const call = typeof request.call === 'string' ? request.call : '';
|
|
116
|
+
const params = (request.params && typeof request.params === 'object' ? request.params : {});
|
|
117
|
+
if (['wiki.', 'skill.', 'recall.', 'trajectory.'].some((prefix) => call.startsWith(prefix)))
|
|
118
|
+
return { result: this.handlers.memory(grant.caller(), call, params) };
|
|
119
|
+
if (call === 'tools.list' && !grant.broker)
|
|
120
|
+
return { result: { tools: [] } };
|
|
121
|
+
if (['mcp.', 'tools.'].some((prefix) => call.startsWith(prefix))) {
|
|
122
|
+
if (!grant.broker)
|
|
123
|
+
throw new HarnessError('mcp_unauthorized', 'Connected apps are not available to this session.');
|
|
124
|
+
return { result: await this.handlers.apps(grant.broker, call, params) };
|
|
125
|
+
}
|
|
126
|
+
if (call === 'control.claim') {
|
|
127
|
+
if (this.controls.has(token))
|
|
128
|
+
throw new HarnessError('unauthorized', "This session's control key was already claimed.");
|
|
129
|
+
const key = `${newId()}${newId()}`.replaceAll('-', '');
|
|
130
|
+
this.controls.set(token, key);
|
|
131
|
+
return { result: { key } };
|
|
132
|
+
}
|
|
133
|
+
if (call.startsWith('terminal.') && grant.terminal?.control) {
|
|
134
|
+
const key = this.controls.get(token);
|
|
135
|
+
if (!key || request.control !== key)
|
|
136
|
+
throw new HarnessError('unauthorized', 'Only a person at this terminal can answer decisions or change its draft state.');
|
|
137
|
+
return { result: await grant.terminal.control(call, params) };
|
|
138
|
+
}
|
|
139
|
+
if ((call.startsWith('plugins.') || call.startsWith('hub.')) && this.handlers.plugins) {
|
|
140
|
+
const key = this.controls.get(token);
|
|
141
|
+
return { result: await this.handlers.plugins(grant, call, params, key !== undefined && request.control === key) };
|
|
142
|
+
}
|
|
143
|
+
if (call.startsWith('accounts.') && this.handlers.accounts) {
|
|
144
|
+
const key = this.controls.get(token);
|
|
145
|
+
return { result: await this.handlers.accounts(grant, call, params, key !== undefined && request.control === key) };
|
|
146
|
+
}
|
|
147
|
+
throw new HarnessError('unknown_method', `Unknown call ${call}.`);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return { error: toErrorShape(error) };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
serve(client) {
|
|
154
|
+
let buffer = '';
|
|
155
|
+
let busy = false;
|
|
156
|
+
let ended = false;
|
|
157
|
+
let waiting = null;
|
|
158
|
+
this.clients.add(client);
|
|
159
|
+
client.on('error', () => { });
|
|
160
|
+
client.once('close', () => {
|
|
161
|
+
this.clients.delete(client);
|
|
162
|
+
// A decision the native process was waiting on can no longer reach it.
|
|
163
|
+
waiting?.terminal?.disconnected();
|
|
164
|
+
});
|
|
165
|
+
// Requests on one connection run one at a time, in order.
|
|
166
|
+
const next = () => {
|
|
167
|
+
if (busy || ended)
|
|
168
|
+
return;
|
|
169
|
+
const newline = buffer.indexOf('\n');
|
|
170
|
+
if (newline < 0)
|
|
171
|
+
return;
|
|
172
|
+
const line = buffer.slice(0, newline);
|
|
173
|
+
buffer = buffer.slice(newline + 1);
|
|
174
|
+
busy = true;
|
|
175
|
+
client.pause();
|
|
176
|
+
let keep = false;
|
|
177
|
+
void this.handle(line, { waiting: (grant) => void (waiting = grant), keep: (value) => void (keep = value) }).then((reply) => {
|
|
178
|
+
busy = false;
|
|
179
|
+
const text = `${JSON.stringify(reply)}\n`;
|
|
180
|
+
if (!keep || client.destroyed) {
|
|
181
|
+
ended = true;
|
|
182
|
+
client.end(text);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
client.write(text);
|
|
186
|
+
client.resume();
|
|
187
|
+
next();
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
client.on('data', (chunk) => {
|
|
191
|
+
buffer += chunk.toString('utf8');
|
|
192
|
+
if (Buffer.byteLength(buffer) > MAX_REQUEST_BYTES) {
|
|
193
|
+
client.destroy();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
next();
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
bind(path) {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
const server = createServer((client) => this.serve(client));
|
|
202
|
+
server.once('error', reject);
|
|
203
|
+
server.listen(path, () => {
|
|
204
|
+
if (!isPipePath(path)) {
|
|
205
|
+
try {
|
|
206
|
+
chmodSync(path, 0o600);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
log('session socket permissions', error instanceof Error ? error.message : String(error));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.server = server;
|
|
213
|
+
resolve();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/** Binds the usual path, or an instance-specific path if another Harness owns it. */
|
|
218
|
+
async bindAvailablePath() {
|
|
219
|
+
if (!isPipePath(this.path))
|
|
220
|
+
mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
221
|
+
try {
|
|
222
|
+
await this.bind(this.path);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (isPipePath(this.path) || error.code !== 'EADDRINUSE')
|
|
226
|
+
throw refusedBind(this.path, error);
|
|
227
|
+
this.socketPathValue = alternateSocketPath(this.path);
|
|
228
|
+
try {
|
|
229
|
+
await this.bind(this.path);
|
|
230
|
+
}
|
|
231
|
+
catch (alternateError) {
|
|
232
|
+
throw refusedBind(this.path, alternateError);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** Binds the socket once; later calls wait for the same bind. */
|
|
237
|
+
listen() {
|
|
238
|
+
this.listening ??= this.bindAvailablePath().catch(error => {
|
|
239
|
+
this.listening = null;
|
|
240
|
+
throw error;
|
|
241
|
+
});
|
|
242
|
+
return this.listening;
|
|
243
|
+
}
|
|
244
|
+
async close() {
|
|
245
|
+
this.grants.clear();
|
|
246
|
+
const server = this.server;
|
|
247
|
+
this.server = null;
|
|
248
|
+
this.listening = null;
|
|
249
|
+
for (const client of this.clients)
|
|
250
|
+
client.destroy();
|
|
251
|
+
if (server)
|
|
252
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
253
|
+
if (!isPipePath(this.path))
|
|
254
|
+
rmSync(this.path, { force: true });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
2
|
+
import { type Harness, type HarnessOptions } from "../harness.js";
|
|
3
|
+
export interface StandalonePaths {
|
|
4
|
+
dataRoot: string;
|
|
5
|
+
stateDir: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StandaloneDependencies {
|
|
8
|
+
home?: string;
|
|
9
|
+
cwd?: string;
|
|
10
|
+
stdinIsTTY?: boolean;
|
|
11
|
+
stdoutIsTTY?: boolean;
|
|
12
|
+
createSessionId?: () => string;
|
|
13
|
+
createHarness?: (options: HarnessOptions) => Harness;
|
|
14
|
+
spawn?: typeof nodeSpawn;
|
|
15
|
+
}
|
|
16
|
+
export declare function standaloneArguments(argv: string[]): string[] | null;
|
|
17
|
+
export declare function parseStandaloneOptions(argv: string[], home?: string, cwd?: string): StandalonePaths;
|
|
18
|
+
export declare function runStandaloneTerminal(argv: string[], dependencies?: StandaloneDependencies): Promise<number>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// The standalone host prepares an isolated Harness terminal and supervises its frontend.
|
|
2
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { appendFileSync } from 'node:fs';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
7
|
+
import { setLogSink } from "../core/bus.js";
|
|
8
|
+
import { createHarness } from "../harness.js";
|
|
9
|
+
export function standaloneArguments(argv) {
|
|
10
|
+
const command = argv[0];
|
|
11
|
+
if (!command || command.startsWith('--') || command === '-h')
|
|
12
|
+
return argv;
|
|
13
|
+
return command === 'terminal' ? argv.slice(1) : null;
|
|
14
|
+
}
|
|
15
|
+
export function parseStandaloneOptions(argv, home = homedir(), cwd = process.cwd()) {
|
|
16
|
+
const root = join(home, '.xingular-harness');
|
|
17
|
+
const paths = {
|
|
18
|
+
dataRoot: join(root, 'data'),
|
|
19
|
+
stateDir: join(root, 'state')
|
|
20
|
+
};
|
|
21
|
+
const given = new Set();
|
|
22
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
23
|
+
const argument = argv[index];
|
|
24
|
+
if (argument !== '--data' && argument !== '--state')
|
|
25
|
+
throw new Error(`Unknown standalone option: ${argument}`);
|
|
26
|
+
const value = argv[index + 1];
|
|
27
|
+
if (!value || value.startsWith('--'))
|
|
28
|
+
throw new Error(`${argument} needs a directory path.`);
|
|
29
|
+
paths[argument === '--data' ? 'dataRoot' : 'stateDir'] = resolve(cwd, value);
|
|
30
|
+
given.add(argument);
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
// A home that is not a folder path (for example HOME set to the word "HOME") would put the
|
|
34
|
+
// defaults, and the session socket, in whatever folder the command was started from.
|
|
35
|
+
if (!isAbsolute(home) && !(given.has('--data') && given.has('--state'))) {
|
|
36
|
+
throw new Error(`The home folder "${home}" is not an absolute path, so the default data and state folders cannot be placed. Set HOME to your home folder, or pass --data and --state with absolute folders.`);
|
|
37
|
+
}
|
|
38
|
+
return paths;
|
|
39
|
+
}
|
|
40
|
+
function processEnvironment(overrides) {
|
|
41
|
+
const environment = { ...process.env };
|
|
42
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
43
|
+
if (value === null)
|
|
44
|
+
delete environment[name];
|
|
45
|
+
else
|
|
46
|
+
environment[name] = value;
|
|
47
|
+
}
|
|
48
|
+
return environment;
|
|
49
|
+
}
|
|
50
|
+
function exitCode(code, signal) {
|
|
51
|
+
if (code !== null)
|
|
52
|
+
return code;
|
|
53
|
+
if (signal === 'SIGINT')
|
|
54
|
+
return 130;
|
|
55
|
+
if (signal === 'SIGTERM')
|
|
56
|
+
return 143;
|
|
57
|
+
if (signal === 'SIGHUP')
|
|
58
|
+
return 129;
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
function spawnError(error, launch) {
|
|
62
|
+
const code = error.code;
|
|
63
|
+
if (code !== 'ENOENT' && code !== 'EACCES' && code !== 'EPERM')
|
|
64
|
+
return error;
|
|
65
|
+
const problem = code === 'ENOENT' ? 'was not found' : `could not start (${code})`;
|
|
66
|
+
return new Error(`Could not start the Harness terminal. The Bun executable "${launch.program}" ${problem}; install Bun or set XINGULAR_HARNESS_BUN, then run npm run build:tui.`, { cause: error });
|
|
67
|
+
}
|
|
68
|
+
function spawnFrontend(launch, cwd, spawn = nodeSpawn) {
|
|
69
|
+
return new Promise((resolveExit, reject) => {
|
|
70
|
+
const child = spawn(launch.program, launch.args, {
|
|
71
|
+
cwd,
|
|
72
|
+
env: processEnvironment(launch.env),
|
|
73
|
+
stdio: 'inherit'
|
|
74
|
+
});
|
|
75
|
+
let settled = false;
|
|
76
|
+
const cleanup = () => {
|
|
77
|
+
process.off('SIGINT', onInterrupt);
|
|
78
|
+
process.off('SIGTERM', onTerminate);
|
|
79
|
+
process.off('SIGHUP', onHangup);
|
|
80
|
+
};
|
|
81
|
+
const finish = (action) => {
|
|
82
|
+
if (settled)
|
|
83
|
+
return;
|
|
84
|
+
settled = true;
|
|
85
|
+
cleanup();
|
|
86
|
+
action();
|
|
87
|
+
};
|
|
88
|
+
// Ctrl+C is delivered to both processes in the foreground process group. Keep this host
|
|
89
|
+
// alive while the frontend handles it so the account credentials and harness socket are cleaned up.
|
|
90
|
+
const onInterrupt = () => undefined;
|
|
91
|
+
const onTerminate = () => {
|
|
92
|
+
child.kill('SIGTERM');
|
|
93
|
+
};
|
|
94
|
+
const onHangup = () => undefined;
|
|
95
|
+
process.on('SIGINT', onInterrupt);
|
|
96
|
+
process.on('SIGTERM', onTerminate);
|
|
97
|
+
process.on('SIGHUP', onHangup);
|
|
98
|
+
child.once('error', (error) => finish(() => reject(spawnError(error, launch))));
|
|
99
|
+
child.once('exit', (code, signal) => finish(() => resolveExit(exitCode(code, signal))));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export async function runStandaloneTerminal(argv, dependencies = {}) {
|
|
103
|
+
if (!(dependencies.stdinIsTTY ?? process.stdin.isTTY) || !(dependencies.stdoutIsTTY ?? process.stdout.isTTY)) {
|
|
104
|
+
throw new Error('The interactive Harness terminal needs a TTY. Run it from a terminal, or use `xharness serve` for a host process.');
|
|
105
|
+
}
|
|
106
|
+
const cwd = resolve(dependencies.cwd ?? process.cwd());
|
|
107
|
+
const paths = parseStandaloneOptions(argv, dependencies.home, cwd);
|
|
108
|
+
const makeHarness = dependencies.createHarness ?? createHarness;
|
|
109
|
+
const harness = makeHarness({ dataRoot: paths.dataRoot, stateDir: paths.stateDir, productName: 'Xingular Harness' });
|
|
110
|
+
const newSessionId = dependencies.createSessionId ?? randomUUID;
|
|
111
|
+
let current = { sessionId: newSessionId(), accountId: null, nativeSessionId: null };
|
|
112
|
+
// The frontend draws on this terminal, so the host's diagnostics go to a file the person can read instead.
|
|
113
|
+
const logFile = join(paths.stateDir, 'harness.log');
|
|
114
|
+
setLogSink((text) => {
|
|
115
|
+
try {
|
|
116
|
+
appendFileSync(logFile, `${new Date().toISOString()} ${text}\n`);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Diagnostics are best effort; a full disk must not stop the terminal.
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
await harness.ready;
|
|
124
|
+
if (harness.accounts.overview().accounts.length === 0)
|
|
125
|
+
harness.accounts.create('Default', 'subscription');
|
|
126
|
+
const cwdHash = createHash('sha256').update(cwd).digest('hex');
|
|
127
|
+
const sessionFolder = join(paths.dataRoot, 'sessions', 'terminal', cwdHash);
|
|
128
|
+
for (;;) {
|
|
129
|
+
const launch = await harness.terminals.prepare({ ...current, cwd, sessionFolder });
|
|
130
|
+
const code = await spawnFrontend(launch, cwd, dependencies.spawn);
|
|
131
|
+
// `/accounts` asks for a switch and ends the frontend. The conversation resumes on the chosen account.
|
|
132
|
+
const next = harness.terminals.takeRelaunch(current.sessionId);
|
|
133
|
+
if (!next)
|
|
134
|
+
return code;
|
|
135
|
+
const label = harness.accounts.find(next.accountId).label;
|
|
136
|
+
process.stdout.write(`Switching this session to account ${label}.\n`);
|
|
137
|
+
await harness.terminals.dispose(current.sessionId);
|
|
138
|
+
current = { sessionId: newSessionId(), accountId: next.accountId, nativeSessionId: next.nativeSessionId };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await harness.terminals.dispose(current.sessionId);
|
|
143
|
+
await harness.close();
|
|
144
|
+
setLogSink(null);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { CapabilitySet } from "../capabilities/types.js";
|
|
2
|
+
import type { Subagents } from "../sessions/subagentService.js";
|
|
3
|
+
import { type SubagentOptions } from "../sessions/subagents.js";
|
|
4
|
+
import type { Accounts } from "../accounts/accounts.js";
|
|
5
|
+
import type { Approvals } from "../sessions/approvals.js";
|
|
6
|
+
import type { Bus } from "../core/bus.js";
|
|
7
|
+
import type { HarnessEnv } from "../core/env.js";
|
|
8
|
+
import type { ConnectedApps } from "../mcp/service.js";
|
|
9
|
+
import type { CallerContext } from "../core/caller.js";
|
|
10
|
+
import { type VariantChoice } from "../variant/materialise.js";
|
|
11
|
+
import type { VariantTrust } from "../variant/trust.js";
|
|
12
|
+
import type { AttachParams, PluginScope } from "../plugins/api.js";
|
|
13
|
+
import type { Plugins } from "../plugins/plugins.js";
|
|
14
|
+
import type { Plugin } from "../plugins/types.js";
|
|
15
|
+
import type { Hub } from "../hub/hub.js";
|
|
16
|
+
import { type Launch } from "../prompt/compile.js";
|
|
17
|
+
import type { RunRecord } from "../sessions/contracts.js";
|
|
18
|
+
import type { ToolPolicy } from "../sessions/policy.js";
|
|
19
|
+
import type { RunRegistry } from "../sessions/registry.js";
|
|
20
|
+
import type { World } from "../world/world.js";
|
|
21
|
+
/** The files a terminal session needs on disk, outside this process. */
|
|
22
|
+
export interface TerminalAssets {
|
|
23
|
+
/** The Node binary that runs the commands. */
|
|
24
|
+
node: string;
|
|
25
|
+
/** The built `xharness` command (`dist/cli.js`). */
|
|
26
|
+
cli: string;
|
|
27
|
+
/** The built Pi extension (`dist/terminal-pi.js`). */
|
|
28
|
+
piExtension: string;
|
|
29
|
+
/** The built OpenTUI frontend (`dist/terminal-opentui.js`). */
|
|
30
|
+
openTui: string;
|
|
31
|
+
}
|
|
32
|
+
/** Where the package's own build puts them. An application that bundles the harness passes its own. */
|
|
33
|
+
export declare function defaultAssets(): TerminalAssets;
|
|
34
|
+
export interface TerminalPrepareInput {
|
|
35
|
+
subagents?: SubagentOptions | null;
|
|
36
|
+
sessionId: string;
|
|
37
|
+
/** The account the session is pinned to. Omitted only for a session that has none yet; it then takes the default and keeps it. */
|
|
38
|
+
accountId?: string | null;
|
|
39
|
+
projectId?: string | null;
|
|
40
|
+
cwd: string;
|
|
41
|
+
/** The session's own folder. Its compiled instructions and native history are kept under it. */
|
|
42
|
+
sessionFolder: string;
|
|
43
|
+
nativeSessionId?: string | null;
|
|
44
|
+
model?: string | null;
|
|
45
|
+
readOnly?: boolean;
|
|
46
|
+
/** What the application adds to the instructions, and which of its tools it describes. */
|
|
47
|
+
launch?: Launch;
|
|
48
|
+
/** The variant the session runs; its version is pinned for the session. */
|
|
49
|
+
variant?: VariantChoice | null;
|
|
50
|
+
/** Give the session the connected-apps broker. Defaults to yes. */
|
|
51
|
+
connections?: boolean;
|
|
52
|
+
/** Fields copied onto each run this session records. */
|
|
53
|
+
run?: Partial<Pick<RunRecord, 'mode' | 'worktreeId'>>;
|
|
54
|
+
}
|
|
55
|
+
export interface TerminalLaunch {
|
|
56
|
+
program: string;
|
|
57
|
+
args: string[];
|
|
58
|
+
/** Variables to set; `null` removes an inherited one. */
|
|
59
|
+
env: Record<string, string | null>;
|
|
60
|
+
accountId: string;
|
|
61
|
+
nativeSessionId: string | null;
|
|
62
|
+
}
|
|
63
|
+
export interface TerminalsOptions {
|
|
64
|
+
doctor?: import("../doctor/service.js").Doctor;
|
|
65
|
+
/** Which plugin code in variants a person trusted. */
|
|
66
|
+
trust?: VariantTrust | null;
|
|
67
|
+
subagents?: Subagents;
|
|
68
|
+
resolveCapabilities?: (caller: CallerContext, tools: readonly string[], cwd: string) => Promise<CapabilitySet>;
|
|
69
|
+
env: HarnessEnv;
|
|
70
|
+
stateDir: string;
|
|
71
|
+
accounts: Accounts;
|
|
72
|
+
runs: RunRegistry;
|
|
73
|
+
approvals: Approvals;
|
|
74
|
+
apps: ConnectedApps;
|
|
75
|
+
policy: ToolPolicy;
|
|
76
|
+
bus: Bus;
|
|
77
|
+
/** Chooses what a terminal loads. Without it Pi discovers extensions and skills on its own. */
|
|
78
|
+
plugins?: Plugins;
|
|
79
|
+
/** Lists plugins and applies a person's choices, for the session's `/plugins` command. */
|
|
80
|
+
pluginControl?: PluginControl;
|
|
81
|
+
hub?: Hub;
|
|
82
|
+
assets?: TerminalAssets;
|
|
83
|
+
/** Web, browser and computer tools, offered when their plugins are on. */
|
|
84
|
+
world?: World;
|
|
85
|
+
}
|
|
86
|
+
/** What `/plugins` in a terminal can do. Each takes the session's folder, account and project. */
|
|
87
|
+
export interface PluginControl {
|
|
88
|
+
list(scope: PluginScope): Promise<Plugin[]>;
|
|
89
|
+
attach(params: AttachParams, on: boolean): Promise<Plugin[]>;
|
|
90
|
+
trust(scope: PluginScope & {
|
|
91
|
+
hash: string;
|
|
92
|
+
}): Promise<Plugin[]>;
|
|
93
|
+
untrust(scope: PluginScope & {
|
|
94
|
+
hash: string;
|
|
95
|
+
}): Promise<Plugin[]>;
|
|
96
|
+
/** Saves numbers a person set on a plugin; each is checked against the plugin's settings. */
|
|
97
|
+
configure(params: PluginScope & {
|
|
98
|
+
pluginId: string;
|
|
99
|
+
settings: Record<string, unknown>;
|
|
100
|
+
}): Promise<Plugin[]>;
|
|
101
|
+
}
|
|
102
|
+
export declare class Terminals {
|
|
103
|
+
private readonly options;
|
|
104
|
+
private readonly sessions;
|
|
105
|
+
private readonly diagnosticOwners;
|
|
106
|
+
/** The variant each live session runs, for loading its skills. */
|
|
107
|
+
private readonly variantDirs;
|
|
108
|
+
/** The world tools each session was given, so a call can only reach one of them. */
|
|
109
|
+
private readonly worldNames;
|
|
110
|
+
private readonly worldReadOnly;
|
|
111
|
+
private readonly socket;
|
|
112
|
+
private readonly assets;
|
|
113
|
+
/** A session's `/accounts` switch, taken once its Pi process has ended. */
|
|
114
|
+
private readonly relaunches;
|
|
115
|
+
constructor(options: TerminalsOptions);
|
|
116
|
+
/** The commands a terminal session finds on PATH. Rewritten only when they change. */
|
|
117
|
+
private ensureCommands;
|
|
118
|
+
/** The broker's tools, then the world tools whose plugins are on for this session. */
|
|
119
|
+
private tools;
|
|
120
|
+
/** The world tools a new session will have, named in its instructions. */
|
|
121
|
+
private worldToolNames;
|
|
122
|
+
/** Whether a world tool's plugin is switched on for a session's folder, account and project. */
|
|
123
|
+
private worldOn;
|
|
124
|
+
private appsCall;
|
|
125
|
+
/**
|
|
126
|
+
* `/plugins` in a terminal. Listing is open to the session. A change is taken only from the
|
|
127
|
+
* session's own extension, which asks the person in the terminal first: the model can reach
|
|
128
|
+
* this socket from a shell command, and must not be able to trust code or switch it on.
|
|
129
|
+
*/
|
|
130
|
+
private pluginsCall;
|
|
131
|
+
/**
|
|
132
|
+
* `/accounts` in a terminal. Every call needs the session's control key: adding, signing in,
|
|
133
|
+
* or removing an account is a person's choice, never the model's shell command.
|
|
134
|
+
*/
|
|
135
|
+
private accountsCall;
|
|
136
|
+
/** The account a session asked to switch to. Taken once, after its Pi process has ended and before `dispose`. */
|
|
137
|
+
takeRelaunch(sessionId: string): {
|
|
138
|
+
accountId: string;
|
|
139
|
+
nativeSessionId: string | null;
|
|
140
|
+
} | null;
|
|
141
|
+
private live;
|
|
142
|
+
private historyDir;
|
|
143
|
+
private runRecord;
|
|
144
|
+
/** The run a native turn reports into: the one reserved by `send`, or a new one for what the person typed. */
|
|
145
|
+
private begin;
|
|
146
|
+
private decide;
|
|
147
|
+
private subagentsCall;
|
|
148
|
+
private componentsCall;
|
|
149
|
+
private trajectoryCall;
|
|
150
|
+
/** The only terminal frontend: the Bun-run OpenTUI application. */
|
|
151
|
+
private frontendCommand;
|
|
152
|
+
/**
|
|
153
|
+
* Prepares an OpenTUI terminal session and returns what to spawn. The caller must report the
|
|
154
|
+
* process's end with `dispose`, also when it never starts.
|
|
155
|
+
*/
|
|
156
|
+
prepare(input: TerminalPrepareInput): Promise<TerminalLaunch>;
|
|
157
|
+
/** Hands the terminal an instruction as a new run. Resolves once the native session has taken it. */
|
|
158
|
+
send(input: {
|
|
159
|
+
sessionId: string;
|
|
160
|
+
runId?: string | null;
|
|
161
|
+
text: string;
|
|
162
|
+
}): Promise<{
|
|
163
|
+
runId: string;
|
|
164
|
+
}>;
|
|
165
|
+
/** What the person typed into the terminal. */
|
|
166
|
+
input(sessionId: string, data: string): void;
|
|
167
|
+
state(sessionId: string): {
|
|
168
|
+
ready: boolean;
|
|
169
|
+
dirty: boolean;
|
|
170
|
+
active: boolean;
|
|
171
|
+
};
|
|
172
|
+
has(sessionId: string): boolean;
|
|
173
|
+
/** The terminal process ended (or never started): its turn, tokens, and written credentials go with it. */
|
|
174
|
+
dispose(sessionId: string): Promise<void>;
|
|
175
|
+
close(): Promise<void>;
|
|
176
|
+
}
|