@xingular/harness 0.1.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -1
- package/LICENSE +202 -0
- package/NOTICE.md +22 -0
- package/README.md +239 -86
- package/THIRD_PARTY_LICENSES.md +942 -0
- package/dist/cli.js +17853 -13114
- package/dist/sdk/accounts/accounts.d.ts +165 -0
- package/dist/sdk/accounts/accounts.js +730 -0
- package/dist/sdk/accounts/choices.d.ts +23 -0
- package/dist/sdk/accounts/choices.js +62 -0
- package/dist/sdk/accounts/env.d.ts +78 -0
- package/dist/sdk/accounts/env.js +294 -0
- package/dist/sdk/accounts/secrets.d.ts +16 -0
- package/dist/sdk/accounts/secrets.js +40 -0
- package/dist/sdk/accounts/store.d.ts +114 -0
- package/dist/sdk/accounts/store.js +255 -0
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +34 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/profiles.d.ts +80 -0
- package/dist/sdk/agents/profiles.js +221 -0
- package/dist/sdk/annotations/bridge.d.ts +42 -0
- package/dist/sdk/annotations/bridge.js +264 -0
- package/dist/sdk/annotations/capture.d.ts +116 -0
- package/dist/sdk/annotations/capture.js +308 -0
- package/dist/sdk/annotations/delivery.d.ts +72 -0
- package/dist/sdk/annotations/delivery.js +130 -0
- package/dist/sdk/annotations/inspect.d.ts +10 -0
- package/dist/sdk/annotations/inspect.js +33 -0
- package/dist/sdk/annotations/isolated.d.ts +6 -0
- package/dist/sdk/annotations/isolated.js +51 -0
- package/dist/sdk/annotations/pageScript.d.ts +28 -0
- package/dist/sdk/annotations/pageScript.js +570 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +144 -0
- package/dist/sdk/annotations/queue.d.ts +4 -0
- package/dist/sdk/annotations/queue.js +40 -0
- package/dist/sdk/annotations/serverMethods.d.ts +3 -0
- package/dist/sdk/annotations/serverMethods.js +62 -0
- package/dist/sdk/annotations/store.d.ts +160 -0
- package/dist/sdk/annotations/store.js +386 -0
- package/dist/sdk/browser/config.d.ts +16 -0
- package/dist/sdk/browser/config.js +22 -0
- package/dist/sdk/browser/diagnostics.d.ts +4 -0
- package/dist/sdk/browser/diagnostics.js +38 -0
- package/dist/sdk/browser/index.d.ts +28 -0
- package/dist/sdk/browser/index.js +72 -0
- package/dist/sdk/browser/network.d.ts +10 -0
- package/dist/sdk/browser/network.js +59 -0
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +85 -0
- package/dist/sdk/browser/session.js +228 -0
- package/dist/sdk/browser/tools.d.ts +8 -0
- package/dist/sdk/browser/tools.js +329 -0
- package/dist/sdk/capabilities/api.d.ts +22 -0
- package/dist/sdk/capabilities/api.js +54 -0
- package/dist/sdk/capabilities/runSnapshot.d.ts +18 -0
- package/dist/sdk/capabilities/runSnapshot.js +47 -0
- package/dist/sdk/capabilities/types.d.ts +29 -0
- package/dist/sdk/capabilities/types.js +1 -0
- package/dist/sdk/cli.d.ts +2 -0
- package/dist/sdk/cli.js +150 -0
- package/dist/sdk/core/bus.d.ts +23 -0
- package/dist/sdk/core/bus.js +76 -0
- package/dist/sdk/core/caller.d.ts +22 -0
- package/dist/sdk/core/caller.js +16 -0
- package/dist/sdk/core/changes.d.ts +15 -0
- package/dist/sdk/core/changes.js +15 -0
- package/dist/sdk/core/effort.d.ts +2 -0
- package/dist/sdk/core/effort.js +1 -0
- package/dist/sdk/core/env.d.ts +18 -0
- package/dist/sdk/core/env.js +8 -0
- package/dist/sdk/core/errors.d.ts +15 -0
- package/dist/sdk/core/errors.js +24 -0
- package/dist/sdk/core/frontmatter.d.ts +12 -0
- package/dist/sdk/core/frontmatter.js +50 -0
- package/dist/sdk/core/fs.d.ts +18 -0
- package/dist/sdk/core/fs.js +106 -0
- package/dist/sdk/core/index.d.ts +24 -0
- package/dist/sdk/core/index.js +187 -0
- package/dist/sdk/core/path.d.ts +7 -0
- package/dist/sdk/core/path.js +79 -0
- package/dist/sdk/core/projects.d.ts +18 -0
- package/dist/sdk/core/projects.js +67 -0
- package/dist/sdk/core/secrets.d.ts +2 -0
- package/dist/sdk/core/secrets.js +5 -0
- package/dist/sdk/core/text.d.ts +7 -0
- package/dist/sdk/core/text.js +40 -0
- package/dist/sdk/doctor/checks.d.ts +5 -0
- package/dist/sdk/doctor/checks.js +208 -0
- package/dist/sdk/doctor/probe.d.ts +4 -0
- package/dist/sdk/doctor/probe.js +92 -0
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/doctor/service.d.ts +23 -0
- package/dist/sdk/doctor/service.js +144 -0
- package/dist/sdk/doctor/types.d.ts +141 -0
- package/dist/sdk/doctor/types.js +29 -0
- package/dist/sdk/harness.d.ts +148 -0
- package/dist/sdk/harness.js +338 -0
- package/dist/sdk/history/lessonPrompt.d.ts +2 -0
- package/dist/sdk/history/lessonPrompt.js +15 -0
- package/dist/sdk/history/lessons.d.ts +52 -0
- package/dist/sdk/history/lessons.js +173 -0
- package/dist/sdk/history/recall.d.ts +68 -0
- package/dist/sdk/history/recall.js +199 -0
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/history/trajectory.d.ts +7 -0
- package/dist/sdk/history/trajectory.js +216 -0
- package/dist/sdk/hub/catalog.json +123 -0
- package/dist/sdk/hub/cli.d.ts +7 -0
- package/dist/sdk/hub/cli.js +81 -0
- package/dist/sdk/hub/hub.d.ts +74 -0
- package/dist/sdk/hub/hub.js +474 -0
- package/dist/sdk/hub/schema.d.ts +280 -0
- package/dist/sdk/hub/schema.js +78 -0
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +73 -0
- package/dist/sdk/index.js +55 -0
- package/dist/sdk/mcp/connections.d.ts +83 -0
- package/dist/sdk/mcp/connections.js +676 -0
- package/dist/sdk/mcp/manager.d.ts +26 -0
- package/dist/sdk/mcp/manager.js +124 -0
- package/dist/sdk/mcp/oauth.d.ts +35 -0
- package/dist/sdk/mcp/oauth.js +171 -0
- package/dist/sdk/mcp/scopeMigration.d.ts +19 -0
- package/dist/sdk/mcp/scopeMigration.js +198 -0
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/mcp/service.d.ts +160 -0
- package/dist/sdk/mcp/service.js +510 -0
- package/dist/sdk/mcp/types.d.ts +131 -0
- package/dist/sdk/mcp/types.js +49 -0
- package/dist/sdk/pi/runtime.d.ts +13 -0
- package/dist/sdk/pi/runtime.js +31 -0
- package/dist/sdk/plugins/api.d.ts +66 -0
- package/dist/sdk/plugins/api.js +155 -0
- package/dist/sdk/plugins/attachments.d.ts +46 -0
- package/dist/sdk/plugins/attachments.js +111 -0
- package/dist/sdk/plugins/builtin.d.ts +7 -0
- package/dist/sdk/plugins/builtin.js +163 -0
- package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
- package/dist/sdk/plugins/builtinRegistry.js +49 -0
- package/dist/sdk/plugins/catalogue.d.ts +29 -0
- package/dist/sdk/plugins/catalogue.js +196 -0
- package/dist/sdk/plugins/cli.d.ts +13 -0
- package/dist/sdk/plugins/cli.js +188 -0
- package/dist/sdk/plugins/discover.d.ts +42 -0
- package/dist/sdk/plugins/discover.js +115 -0
- package/dist/sdk/plugins/firstParty.d.ts +50 -0
- package/dist/sdk/plugins/firstParty.js +113 -0
- package/dist/sdk/plugins/hooks.d.ts +94 -0
- package/dist/sdk/plugins/hooks.js +162 -0
- package/dist/sdk/plugins/identity.d.ts +19 -0
- package/dist/sdk/plugins/identity.js +48 -0
- package/dist/sdk/plugins/install.d.ts +20 -0
- package/dist/sdk/plugins/install.js +110 -0
- package/dist/sdk/plugins/inventory.d.ts +22 -0
- package/dist/sdk/plugins/inventory.js +110 -0
- package/dist/sdk/plugins/manifest.d.ts +13 -0
- package/dist/sdk/plugins/manifest.js +56 -0
- package/dist/sdk/plugins/plugins.d.ts +104 -0
- package/dist/sdk/plugins/plugins.js +164 -0
- package/dist/sdk/plugins/runtime.d.ts +9 -0
- package/dist/sdk/plugins/runtime.js +95 -0
- package/dist/sdk/plugins/trust.d.ts +44 -0
- package/dist/sdk/plugins/trust.js +120 -0
- package/dist/sdk/plugins/types.d.ts +159 -0
- package/dist/sdk/plugins/types.js +7 -0
- package/dist/sdk/prompt/compile.d.ts +177 -0
- package/dist/sdk/prompt/compile.js +413 -0
- package/dist/sdk/refine/cli.d.ts +4 -0
- package/dist/sdk/refine/cli.js +186 -0
- package/dist/sdk/refine/demo.d.ts +21 -0
- package/dist/sdk/refine/demo.js +114 -0
- package/dist/sdk/refine/evidence.d.ts +55 -0
- package/dist/sdk/refine/evidence.js +330 -0
- package/dist/sdk/refine/host.d.ts +51 -0
- package/dist/sdk/refine/host.js +76 -0
- package/dist/sdk/refine/ledger.d.ts +48 -0
- package/dist/sdk/refine/ledger.js +76 -0
- package/dist/sdk/refine/library.d.ts +55 -0
- package/dist/sdk/refine/library.js +177 -0
- package/dist/sdk/refine/pi.d.ts +72 -0
- package/dist/sdk/refine/pi.js +248 -0
- package/dist/sdk/refine/prompts.d.ts +45 -0
- package/dist/sdk/refine/prompts.js +390 -0
- package/dist/sdk/refine/recursion.d.ts +113 -0
- package/dist/sdk/refine/recursion.js +258 -0
- package/dist/sdk/refine/runner.d.ts +81 -0
- package/dist/sdk/refine/runner.js +150 -0
- package/dist/sdk/refine/selection.d.ts +57 -0
- package/dist/sdk/refine/selection.js +251 -0
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/refine/service.d.ts +84 -0
- package/dist/sdk/refine/service.js +165 -0
- package/dist/sdk/refine/suite.d.ts +67 -0
- package/dist/sdk/refine/suite.js +174 -0
- package/dist/sdk/refine/trajectory.d.ts +9 -0
- package/dist/sdk/refine/trajectory.js +68 -0
- package/dist/sdk/refine/types.d.ts +77 -0
- package/dist/sdk/refine/types.js +10 -0
- package/dist/sdk/server/methods.d.ts +3 -0
- package/dist/sdk/server/methods.js +293 -0
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/server/remote.d.ts +19 -0
- package/dist/sdk/server/remote.js +84 -0
- package/dist/sdk/server/rpc.d.ts +20 -0
- package/dist/sdk/server/rpc.js +102 -0
- package/dist/sdk/server/serve.d.ts +10 -0
- package/dist/sdk/server/serve.js +18 -0
- package/dist/sdk/sessions/approvals.d.ts +47 -0
- package/dist/sdk/sessions/approvals.js +119 -0
- package/dist/sdk/sessions/continuation.d.ts +48 -0
- package/dist/sdk/sessions/continuation.js +85 -0
- package/dist/sdk/sessions/contracts.d.ts +551 -0
- package/dist/sdk/sessions/contracts.js +338 -0
- package/dist/sdk/sessions/extensionUi.d.ts +19 -0
- package/dist/sdk/sessions/extensionUi.js +64 -0
- package/dist/sdk/sessions/history.d.ts +16 -0
- package/dist/sdk/sessions/history.js +68 -0
- package/dist/sdk/sessions/lineageJoin.d.ts +2 -0
- package/dist/sdk/sessions/lineageJoin.js +61 -0
- package/dist/sdk/sessions/planMode.d.ts +39 -0
- package/dist/sdk/sessions/planMode.js +93 -0
- package/dist/sdk/sessions/policy.d.ts +43 -0
- package/dist/sdk/sessions/policy.js +1 -0
- package/dist/sdk/sessions/pricing.d.ts +34 -0
- package/dist/sdk/sessions/pricing.js +40 -0
- package/dist/sdk/sessions/reasoningSummaries.d.ts +11 -0
- package/dist/sdk/sessions/reasoningSummaries.js +61 -0
- package/dist/sdk/sessions/registry.d.ts +103 -0
- package/dist/sdk/sessions/registry.js +386 -0
- package/dist/sdk/sessions/runIndex.d.ts +21 -0
- package/dist/sdk/sessions/runIndex.js +65 -0
- package/dist/sdk/sessions/runStore.d.ts +66 -0
- package/dist/sdk/sessions/runStore.js +337 -0
- package/dist/sdk/sessions/runner.d.ts +213 -0
- package/dist/sdk/sessions/runner.js +589 -0
- package/dist/sdk/sessions/sessions.d.ts +152 -0
- package/dist/sdk/sessions/sessions.js +461 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
- package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
- package/dist/sdk/sessions/sideQuestions.js +290 -0
- package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/subagentServerMethods.js +41 -0
- package/dist/sdk/sessions/subagentService.d.ts +52 -0
- package/dist/sdk/sessions/subagentService.js +265 -0
- package/dist/sdk/sessions/subagents.d.ts +58 -0
- package/dist/sdk/sessions/subagents.js +87 -0
- package/dist/sdk/terminal/accountsMenu.d.ts +46 -0
- package/dist/sdk/terminal/accountsMenu.js +392 -0
- package/dist/sdk/terminal/browser.d.ts +62 -0
- package/dist/sdk/terminal/browser.js +41 -0
- package/dist/sdk/terminal/browserControl.d.ts +29 -0
- package/dist/sdk/terminal/browserControl.js +186 -0
- package/dist/sdk/terminal/cli.d.ts +20 -0
- package/dist/sdk/terminal/cli.js +121 -0
- package/dist/sdk/terminal/client.d.ts +15 -0
- package/dist/sdk/terminal/client.js +126 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/control.d.ts +4 -0
- package/dist/sdk/terminal/control.js +25 -0
- package/dist/sdk/terminal/controller.d.ts +78 -0
- package/dist/sdk/terminal/controller.js +383 -0
- package/dist/sdk/terminal/hubMenu.d.ts +10 -0
- package/dist/sdk/terminal/hubMenu.js +34 -0
- package/dist/sdk/terminal/launch.d.ts +5 -0
- package/dist/sdk/terminal/launch.js +27 -0
- package/dist/sdk/terminal/logs.d.ts +14 -0
- package/dist/sdk/terminal/logs.js +59 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -0
- package/dist/sdk/terminal/piExtension.js +377 -0
- package/dist/sdk/terminal/pluginMenu.d.ts +34 -0
- package/dist/sdk/terminal/pluginMenu.js +101 -0
- package/dist/sdk/terminal/socket.d.ts +73 -0
- package/dist/sdk/terminal/socket.js +287 -0
- package/dist/sdk/terminal/standalone.d.ts +20 -0
- package/dist/sdk/terminal/standalone.js +186 -0
- package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
- package/dist/sdk/terminal/subagentMonitor.js +121 -0
- package/dist/sdk/terminal/terminals.d.ts +212 -0
- package/dist/sdk/terminal/terminals.js +1108 -0
- package/dist/sdk/terminal/trajectory.d.ts +10 -0
- package/dist/sdk/terminal/trajectory.js +30 -0
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/chrome.d.ts +64 -0
- package/dist/sdk/terminal/tui/chrome.js +169 -0
- package/dist/sdk/terminal/tui/clipboardFiles.d.ts +11 -0
- package/dist/sdk/terminal/tui/clipboardFiles.js +74 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +11 -0
- package/dist/sdk/terminal/tui/commandPages.js +111 -0
- package/dist/sdk/terminal/tui/commands.d.ts +46 -0
- package/dist/sdk/terminal/tui/commands.js +399 -0
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +27 -0
- package/dist/sdk/terminal/tui/dialogGeometry.js +52 -0
- package/dist/sdk/terminal/tui/dialogs.d.ts +80 -0
- package/dist/sdk/terminal/tui/dialogs.js +370 -0
- package/dist/sdk/terminal/tui/keybindings.d.ts +11 -0
- package/dist/sdk/terminal/tui/keybindings.js +49 -0
- package/dist/sdk/terminal/tui/list.d.ts +73 -0
- package/dist/sdk/terminal/tui/list.js +196 -0
- package/dist/sdk/terminal/tui/modeChoices.d.ts +42 -0
- package/dist/sdk/terminal/tui/modeChoices.js +70 -0
- package/dist/sdk/terminal/tui/modelPicker.d.ts +55 -0
- package/dist/sdk/terminal/tui/modelPicker.js +150 -0
- package/dist/sdk/terminal/tui/mouse.d.ts +3 -0
- package/dist/sdk/terminal/tui/mouse.js +3 -0
- package/dist/sdk/terminal/tui/page.d.ts +17 -0
- package/dist/sdk/terminal/tui/page.js +1 -0
- package/dist/sdk/terminal/tui/pageOverlay.d.ts +27 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +156 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +76 -0
- package/dist/sdk/terminal/tui/pages/accountsPage.js +409 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
- package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.d.ts +47 -0
- package/dist/sdk/terminal/tui/pages/componentsPage.js +119 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +233 -0
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +39 -0
- package/dist/sdk/terminal/tui/pages/hubPage.js +128 -0
- package/dist/sdk/terminal/tui/pages/logPage.d.ts +38 -0
- package/dist/sdk/terminal/tui/pages/logPage.js +189 -0
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
- package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.d.ts +44 -0
- package/dist/sdk/terminal/tui/pages/permissionsPage.js +124 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +57 -0
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +243 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +63 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +103 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +65 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +450 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +18 -0
- package/dist/sdk/terminal/tui/piAdapter.js +48 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +2 -0
- package/dist/sdk/terminal/tui/planMode.js +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
- package/dist/sdk/terminal/tui/questionPicker.d.ts +20 -0
- package/dist/sdk/terminal/tui/questionPicker.js +150 -0
- package/dist/sdk/terminal/tui/questions.d.ts +21 -0
- package/dist/sdk/terminal/tui/questions.js +42 -0
- package/dist/sdk/terminal/tui/runtime.d.ts +114 -0
- package/dist/sdk/terminal/tui/runtime.js +443 -0
- package/dist/sdk/terminal/tui/search.d.ts +22 -0
- package/dist/sdk/terminal/tui/search.js +60 -0
- package/dist/sdk/terminal/tui/selection.d.ts +16 -0
- package/dist/sdk/terminal/tui/selection.js +16 -0
- package/dist/sdk/terminal/tui/sessionUi.d.ts +3 -0
- package/dist/sdk/terminal/tui/sessionUi.js +30 -0
- package/dist/sdk/terminal/tui/settingsModel.d.ts +14 -0
- package/dist/sdk/terminal/tui/settingsModel.js +114 -0
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +53 -0
- package/dist/sdk/terminal/tui/sideQuestion.js +230 -0
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +9 -0
- package/dist/sdk/terminal/tui/skillInvocation.js +43 -0
- package/dist/sdk/terminal/tui/status.d.ts +18 -0
- package/dist/sdk/terminal/tui/status.js +67 -0
- package/dist/sdk/terminal/tui/theme.d.ts +45 -0
- package/dist/sdk/terminal/tui/theme.js +176 -0
- package/dist/sdk/terminal/tui/treeModel.d.ts +79 -0
- package/dist/sdk/terminal/tui/treeModel.js +483 -0
- package/dist/sdk/terminal/tui/treePicker.d.ts +49 -0
- package/dist/sdk/terminal/tui/treePicker.js +251 -0
- package/dist/sdk/terminal/tui/ui.d.ts +49 -0
- package/dist/sdk/terminal/tui/ui.js +149 -0
- package/dist/sdk/terminal/tui/workingStatus.d.ts +9 -0
- package/dist/sdk/terminal/tui/workingStatus.js +229 -0
- package/dist/sdk/tools/askUser.d.ts +6 -0
- package/dist/sdk/tools/askUser.js +92 -0
- package/dist/sdk/tools/checklist.d.ts +9 -0
- package/dist/sdk/tools/checklist.js +54 -0
- package/dist/sdk/tools/definitions.d.ts +30 -0
- package/dist/sdk/tools/definitions.js +117 -0
- package/dist/sdk/tools/dispatch.d.ts +16 -0
- package/dist/sdk/tools/dispatch.js +67 -0
- package/dist/sdk/tools/harnessTools.d.ts +8 -0
- package/dist/sdk/tools/harnessTools.js +20 -0
- package/dist/sdk/tools/sessionName.d.ts +7 -0
- package/dist/sdk/tools/sessionName.js +31 -0
- package/dist/sdk/tools/spawnSubagent.d.ts +4 -0
- package/dist/sdk/tools/spawnSubagent.js +58 -0
- package/dist/sdk/tools/types.d.ts +48 -0
- package/dist/sdk/tools/types.js +1 -0
- package/dist/sdk/variant/components.d.ts +34 -0
- package/dist/sdk/variant/components.js +230 -0
- package/dist/sdk/variant/materialise.d.ts +66 -0
- package/dist/sdk/variant/materialise.js +81 -0
- package/dist/sdk/variant/migrate.d.ts +4 -0
- package/dist/sdk/variant/migrate.js +41 -0
- package/dist/sdk/variant/seed.d.ts +20 -0
- package/dist/sdk/variant/seed.js +116 -0
- package/dist/sdk/variant/store.d.ts +82 -0
- package/dist/sdk/variant/store.js +151 -0
- package/dist/sdk/variant/surface.d.ts +56 -0
- package/dist/sdk/variant/surface.js +81 -0
- package/dist/sdk/variant/trust.d.ts +27 -0
- package/dist/sdk/variant/trust.js +64 -0
- package/dist/sdk/variant/variant.d.ts +71 -0
- package/dist/sdk/variant/variant.js +331 -0
- package/dist/sdk/web/markdown.d.ts +10 -0
- package/dist/sdk/web/markdown.js +66 -0
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/safeRequest.d.ts +34 -0
- package/dist/sdk/web/safeRequest.js +175 -0
- package/dist/sdk/web/search.d.ts +53 -0
- package/dist/sdk/web/search.js +168 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -0
- package/dist/sdk/web/text.d.ts +2 -0
- package/dist/sdk/web/text.js +7 -0
- package/dist/sdk/web/tools.d.ts +22 -0
- package/dist/sdk/web/tools.js +195 -0
- package/dist/sdk/web/turndown-plugin-gfm.d.ts +4 -0
- package/dist/sdk/wiki/notes.d.ts +68 -0
- package/dist/sdk/wiki/notes.js +297 -0
- package/dist/sdk/wiki/serverMethods.d.ts +3 -0
- package/dist/sdk/wiki/serverMethods.js +26 -0
- package/dist/sdk/worktrees/cli.d.ts +11 -0
- package/dist/sdk/worktrees/cli.js +146 -0
- package/dist/sdk/worktrees/copy.d.ts +9 -0
- package/dist/sdk/worktrees/copy.js +111 -0
- package/dist/sdk/worktrees/git.d.ts +16 -0
- package/dist/sdk/worktrees/git.js +81 -0
- package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
- package/dist/sdk/worktrees/serverMethods.js +30 -0
- package/dist/sdk/worktrees/service.d.ts +73 -0
- package/dist/sdk/worktrees/service.js +483 -0
- package/dist/sdk/worktrees/setup.d.ts +23 -0
- package/dist/sdk/worktrees/setup.js +65 -0
- package/dist/sdk/worktrees/types.d.ts +91 -0
- package/dist/sdk/worktrees/types.js +1 -0
- package/dist/sdk/world/actions.d.ts +23 -0
- package/dist/sdk/world/actions.js +31 -0
- package/dist/sdk/world/cli.d.ts +12 -0
- package/dist/sdk/world/cli.js +166 -0
- package/dist/sdk/world/images.d.ts +20 -0
- package/dist/sdk/world/images.js +48 -0
- package/dist/sdk/world/untrusted.d.ts +12 -0
- package/dist/sdk/world/untrusted.js +20 -0
- package/dist/sdk/world/world.d.ts +67 -0
- package/dist/sdk/world/world.js +79 -0
- package/dist/terminal-opentui.js +20778 -0
- package/dist/terminal-pi.js +794 -518
- package/docs/README.md +42 -0
- package/docs/architecture/00-overview.md +171 -0
- package/docs/architecture/01-extension-points.md +78 -0
- package/docs/architecture/02-sessions-and-runs.md +203 -0
- package/docs/architecture/03-variants.md +103 -0
- package/docs/architecture/04-prompt.md +122 -0
- package/docs/architecture/05-refinement.md +107 -0
- package/docs/architecture/06-plugins-and-capabilities.md +129 -0
- package/docs/architecture/07-tools-and-gate.md +103 -0
- package/docs/architecture/08-memory-and-history.md +94 -0
- package/docs/architecture/09-accounts-and-connectors.md +98 -0
- package/docs/architecture/10-server-protocol.md +193 -0
- package/docs/architecture/11-terminal.md +261 -0
- package/docs/architecture/12-storage.md +75 -0
- package/docs/architecture/13-world-tools.md +45 -0
- package/docs/architecture/14-surfaces-and-parity.md +89 -0
- package/docs/architecture/README.md +125 -0
- package/docs/architecture/proposals/PROP-0001-reporting-section.md +106 -0
- package/docs/architecture/proposals/PROP-0002-retain-rollout-workspaces.md +104 -0
- package/docs/architecture/proposals/PROP-0003-executor-settings.md +81 -0
- package/docs/architecture/proposals/PROP-0004-flexible-reporting-guidance.md +71 -0
- package/docs/architecture/proposals/PROP-0005-xharness-cli-name.md +78 -0
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +70 -0
- package/docs/architecture/proposals/PROP-0007-run-continuation.md +80 -0
- package/docs/architecture/proposals/PROP-0008-lessons-from-runs.md +80 -0
- package/docs/architecture/proposals/PROP-0009-foundation-and-variants.md +78 -0
- package/docs/architecture/proposals/PROP-0010-session-names.md +73 -0
- package/docs/architecture/proposals/PROP-0011-doctor.md +63 -0
- package/docs/architecture/proposals/PROP-0012-release-baseline.md +56 -0
- package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
- package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
- package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
- package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
- package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
- package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
- package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
- package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
- package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
- package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
- package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
- package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
- package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
- package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
- package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
- package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
- package/docs/architecture/proposals/README.md +101 -0
- package/docs/decisions/ADR-0002-native-subagent-lifecycle.md +49 -0
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +70 -0
- package/docs/decisions/ADR-0004-fullscreen-terminal-layout.md +50 -0
- package/docs/decisions/ADR-0005-app-provided-oauth-clients.md +54 -0
- package/docs/decisions/ADR-0006-opentui-only-terminal.md +47 -0
- package/docs/decisions/ADR-0007-reporting-section.md +33 -0
- package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +38 -0
- package/docs/decisions/ADR-0009-xharness-command.md +39 -0
- package/docs/decisions/ADR-0010-lessons-in-the-wiki.md +31 -0
- package/docs/decisions/ADR-0011-foundation-and-variants.md +28 -0
- package/docs/decisions/ADR-0012-native-session-names.md +34 -0
- package/docs/decisions/ADR-0013-doctor.md +27 -0
- package/docs/decisions/ADR-0014-public-release-baseline.md +22 -0
- package/docs/decisions/ADR-0015-resource-browser.md +28 -0
- package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
- package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
- package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
- package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
- package/docs/decisions/ADR-0020-page-annotations.md +37 -0
- package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
- package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
- package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
- package/docs/decisions/ADR-0024-xagent-command.md +40 -0
- package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
- package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
- package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
- package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
- package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
- package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
- package/docs/doctor.md +80 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/plan.md +81 -0
- package/docs/experiments/EXP-0001-terminal-input-latency/results.md +46 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/plan.md +70 -0
- package/docs/experiments/EXP-0002-opentui-typing-latency/results.md +92 -0
- package/docs/features/README.md +46 -0
- package/docs/features/automations/README.md +41 -0
- package/docs/features/cores/README.md +39 -0
- package/docs/features/google-harness2/README.md +52 -0
- package/docs/features/integrations/README.md +40 -0
- package/docs/features/interfaces/README.md +29 -0
- package/docs/features/memory/README.md +28 -0
- package/docs/future-work.md +48 -0
- package/docs/parity.md +78 -0
- package/docs/releases/1.0.0.md +40 -0
- package/docs/releases/4.0.0.md +46 -0
- package/docs/releases/README.md +41 -0
- package/docs/study/00-index.md +83 -0
- package/docs/study/arms/00-matrix.md +20 -0
- package/docs/study/arms/01-system-prompt.md +211 -0
- package/docs/study/arms/02-tools.md +79 -0
- package/docs/study/arms/03-subagents.md +90 -0
- package/docs/study/arms/04-skills.md +75 -0
- package/docs/study/arms/05-context-management.md +90 -0
- package/docs/study/arms/06-memory.md +90 -0
- package/docs/study/arms/07-governance.md +71 -0
- package/docs/study/arms/08-execution.md +69 -0
- package/docs/study/arms/09-observability.md +60 -0
- package/docs/study/arms/10-refinement.md +110 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/btw-comparison.md +37 -0
- package/docs/study/feature-gap-review.md +42 -0
- package/docs/study/insights.md +123 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/roadmap.md +72 -0
- package/docs/study/sources/claude-code.md +44 -0
- package/docs/study/sources/deepseek-harness.md +102 -0
- package/docs/study/sources/harness2.md +50 -0
- package/docs/study/sources/kimi-cli.md +42 -0
- package/docs/study/sources/omp.md +93 -0
- package/docs/study/sources/opencode.md +43 -0
- package/docs/study/sources/openhands.md +46 -0
- package/docs/study/sources/smolagents.md +40 -0
- package/docs/study/sources/swe-agent.md +34 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/system-prompt-design.md +196 -0
- package/docs/terminal-tui.md +707 -58
- package/package.json +43 -13
- package/docs/evidence/terminal-2026-10-17/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17/verification.json +0 -130
- package/docs/evidence/terminal-2026-10-17-followup/120x40-completed-transcript.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-interaction.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-settings-keyboard.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/40x20-welcome.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/app-bundle/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-approval-0.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-plugin-light.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question-batch.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/80x24-question.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/README.md +0 -27
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/global-install/shell-command.json +0 -18
- package/docs/evidence/terminal-2026-10-17-followup/global-install/verification.json +0 -95
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-final-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/harness-reference-scenario.json +0 -60
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/README.md +0 -24
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/complete-80x24.ansi.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/live-codex/verification.json +0 -57
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24-early.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-80x24.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/omp-reference-scenario.json +0 -58
- package/docs/evidence/terminal-2026-10-17-followup/omp-supplemental-static.json.gz +0 -0
- package/docs/evidence/terminal-2026-10-17-followup/rea-artifacts.json +0 -320
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.json +0 -30
- package/docs/evidence/terminal-2026-10-17-followup/reference-comparison.md +0 -133
- package/docs/evidence/terminal-2026-10-17-followup/reference-source-observations.json +0 -20
- package/docs/evidence/terminal-2026-10-17-followup/verification.json +0 -207
- package/src/accounts/accounts.test.ts +0 -333
- package/src/accounts/accounts.ts +0 -658
- package/src/accounts/env.test.ts +0 -95
- package/src/accounts/env.ts +0 -338
- package/src/accounts/secrets.ts +0 -50
- package/src/accounts/store.ts +0 -338
- package/src/capabilities/api.ts +0 -75
- package/src/capabilities/runSnapshot.ts +0 -59
- package/src/capabilities/types.ts +0 -34
- package/src/cli.ts +0 -118
- package/src/core/bus.ts +0 -34
- package/src/core/changes.ts +0 -33
- package/src/core/env.ts +0 -27
- package/src/core/errors.ts +0 -32
- package/src/core/frontmatter.ts +0 -47
- package/src/core/fs.test.ts +0 -38
- package/src/core/fs.ts +0 -92
- package/src/core/index.ts +0 -193
- package/src/core/path.test.ts +0 -14
- package/src/core/path.ts +0 -79
- package/src/core/projects.ts +0 -90
- package/src/core/text.ts +0 -39
- package/src/harness.ts +0 -312
- package/src/hub/catalog.json +0 -123
- package/src/hub/cli.ts +0 -59
- package/src/hub/hub.test.ts +0 -205
- package/src/hub/hub.ts +0 -256
- package/src/hub/schema.ts +0 -81
- package/src/index.ts +0 -75
- package/src/mcp/connections.ts +0 -614
- package/src/mcp/manager.ts +0 -130
- package/src/mcp/oauth.ts +0 -195
- package/src/mcp/scopeMigration.test.ts +0 -79
- package/src/mcp/scopeMigration.ts +0 -204
- package/src/mcp/service.test.ts +0 -376
- package/src/mcp/service.ts +0 -549
- package/src/mcp/types.ts +0 -108
- package/src/memory/api.test.ts +0 -82
- package/src/memory/api.ts +0 -303
- package/src/memory/blocks.test.ts +0 -78
- package/src/memory/blocks.ts +0 -408
- package/src/memory/caller.ts +0 -69
- package/src/memory/curation.test.ts +0 -163
- package/src/memory/curation.ts +0 -64
- package/src/memory/dispatch.ts +0 -59
- package/src/memory/facts.test.ts +0 -41
- package/src/memory/facts.ts +0 -248
- package/src/memory/graph.ts +0 -103
- package/src/memory/health.test.ts +0 -130
- package/src/memory/health.ts +0 -74
- package/src/memory/maintainer.ts +0 -272
- package/src/memory/maintenance.test.ts +0 -176
- package/src/memory/maintenance.ts +0 -518
- package/src/memory/migrate.ts +0 -79
- package/src/memory/operations.ts +0 -150
- package/src/memory/proposals.test.ts +0 -168
- package/src/memory/proposals.ts +0 -584
- package/src/memory/recall.test.ts +0 -115
- package/src/memory/recall.ts +0 -278
- package/src/memory/references.ts +0 -77
- package/src/memory/runContext.ts +0 -93
- package/src/memory/secrets.ts +0 -6
- package/src/memory/store.test.ts +0 -66
- package/src/memory/store.ts +0 -385
- package/src/memory/wiki.test.ts +0 -47
- package/src/memory/wiki.ts +0 -422
- package/src/memory/wikiIndex.test.ts +0 -88
- package/src/memory/wikiIndex.ts +0 -116
- package/src/plugins/api.ts +0 -181
- package/src/plugins/attachments.test.ts +0 -84
- package/src/plugins/attachments.ts +0 -141
- package/src/plugins/builtin.ts +0 -82
- package/src/plugins/catalogue.test.ts +0 -254
- package/src/plugins/catalogue.ts +0 -251
- package/src/plugins/cli.ts +0 -184
- package/src/plugins/discover.ts +0 -151
- package/src/plugins/firstParty.test.ts +0 -143
- package/src/plugins/firstParty.ts +0 -129
- package/src/plugins/hooks.test.ts +0 -250
- package/src/plugins/hooks.ts +0 -239
- package/src/plugins/identity.ts +0 -37
- package/src/plugins/install.test.ts +0 -107
- package/src/plugins/install.ts +0 -106
- package/src/plugins/inventory.ts +0 -123
- package/src/plugins/manifest.ts +0 -76
- package/src/plugins/plugins.test.ts +0 -205
- package/src/plugins/plugins.ts +0 -189
- package/src/plugins/proposal.ts +0 -121
- package/src/plugins/runtime.test.ts +0 -40
- package/src/plugins/runtime.ts +0 -82
- package/src/plugins/trust.ts +0 -142
- package/src/plugins/types.ts +0 -149
- package/src/prompt/compile.test.ts +0 -261
- package/src/prompt/compile.ts +0 -732
- package/src/server/methods.ts +0 -349
- package/src/server/remote.ts +0 -77
- package/src/server/rpc.ts +0 -116
- package/src/server/serve.ts +0 -28
- package/src/server/server.test.ts +0 -174
- package/src/sessions/approvals.ts +0 -118
- package/src/sessions/contracts.ts +0 -336
- package/src/sessions/conversation.test.ts +0 -177
- package/src/sessions/extensionSession.test.ts +0 -74
- package/src/sessions/extensionUi.test.ts +0 -65
- package/src/sessions/extensionUi.ts +0 -102
- package/src/sessions/history.ts +0 -51
- package/src/sessions/lineageJoin.ts +0 -75
- package/src/sessions/policy.ts +0 -36
- package/src/sessions/pricing.ts +0 -60
- package/src/sessions/questionBatches.test.ts +0 -110
- package/src/sessions/reasoningSummaries.ts +0 -59
- package/src/sessions/registry.ts +0 -381
- package/src/sessions/runIndex.ts +0 -99
- package/src/sessions/runStore.test.ts +0 -152
- package/src/sessions/runStore.ts +0 -332
- package/src/sessions/runner.ts +0 -604
- package/src/sessions/runtimeResources.test.ts +0 -61
- package/src/sessions/sessions.test.ts +0 -549
- package/src/sessions/sessions.ts +0 -339
- package/src/skills/registry.test.ts +0 -104
- package/src/skills/registry.ts +0 -413
- package/src/terminal/accountsMenu.test.ts +0 -225
- package/src/terminal/accountsMenu.ts +0 -382
- package/src/terminal/cli.ts +0 -149
- package/src/terminal/client.ts +0 -74
- package/src/terminal/control.test.ts +0 -23
- package/src/terminal/control.ts +0 -23
- package/src/terminal/controller.ts +0 -291
- package/src/terminal/hubMenu.ts +0 -24
- package/src/terminal/launch.test.ts +0 -25
- package/src/terminal/launch.ts +0 -48
- package/src/terminal/piExtension.lifecycle.test.ts +0 -55
- package/src/terminal/piExtension.ts +0 -259
- package/src/terminal/pluginControl.test.ts +0 -146
- package/src/terminal/pluginMenu.ts +0 -112
- package/src/terminal/socket.test.ts +0 -71
- package/src/terminal/socket.ts +0 -232
- package/src/terminal/standalone.test.ts +0 -167
- package/src/terminal/standalone.ts +0 -153
- package/src/terminal/terminal.test.ts +0 -399
- package/src/terminal/terminals.ts +0 -553
- package/src/terminal/tui/application.test.ts +0 -114
- package/src/terminal/tui/application.ts +0 -491
- package/src/terminal/tui/commands.test.ts +0 -218
- package/src/terminal/tui/commands.ts +0 -361
- package/src/terminal/tui/dialogs.ts +0 -301
- package/src/terminal/tui/editor.test.ts +0 -84
- package/src/terminal/tui/editor.ts +0 -79
- package/src/terminal/tui/keybindings.test.ts +0 -22
- package/src/terminal/tui/keybindings.ts +0 -50
- package/src/terminal/tui/piAdapter.ts +0 -62
- package/src/terminal/tui/presentation.test.ts +0 -79
- package/src/terminal/tui/questions.test.ts +0 -41
- package/src/terminal/tui/questions.ts +0 -47
- package/src/terminal/tui/runtime.socket.test.ts +0 -104
- package/src/terminal/tui/runtime.test.ts +0 -215
- package/src/terminal/tui/runtime.ts +0 -388
- package/src/terminal/tui/sessionUi.ts +0 -24
- package/src/terminal/tui/settings.test.ts +0 -150
- package/src/terminal/tui/settings.ts +0 -188
- package/src/terminal/tui/status.test.ts +0 -70
- package/src/terminal/tui/status.ts +0 -109
- package/src/terminal/tui/theme.ts +0 -174
- package/src/terminal/tui/tools.test.ts +0 -145
- package/src/terminal/tui/tools.ts +0 -142
- package/src/terminal/tui/transcript.test.ts +0 -25
- package/src/terminal/tui/transcript.ts +0 -124
- package/src/terminal/tui/ui.test.ts +0 -313
- package/src/terminal/tui/ui.ts +0 -166
- package/src/terminal/tui/welcome.ts +0 -102
- package/src/tools/askUser.test.ts +0 -69
- package/src/tools/askUser.ts +0 -59
- package/src/tools/definitions.ts +0 -187
- package/src/tools/maintenance.ts +0 -24
- package/src/tools/memoryTools.ts +0 -22
- package/src/tools/pluginPropose.ts +0 -41
- package/src/tools/registryWrite.test.ts +0 -101
- package/src/tools/registryWrite.ts +0 -182
- package/src/tools/spawnSubagent.ts +0 -92
- package/src/tools/types.ts +0 -39
- package/src/web/safeRequest.ts +0 -171
- package/src/web/text.ts +0 -53
- package/src/web/tools.ts +0 -134
- package/src/web/web.test.ts +0 -141
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdtempSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
|
-
import { tmpdir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
-
|
|
6
|
-
import { memorySecretStore } from '../accounts/secrets'
|
|
7
|
-
import { Bus } from '../core/bus'
|
|
8
|
-
import { createHarness, type Harness } from '../harness'
|
|
9
|
-
import { listInbox } from '../memory/operations'
|
|
10
|
-
import { wikiDir } from '../memory/store'
|
|
11
|
-
import { methods } from '../server/methods'
|
|
12
|
-
import { pluginProposeTool } from '../tools/pluginPropose'
|
|
13
|
-
import { rootWithProject, writeFile } from '../../tests/helpers'
|
|
14
|
-
import { entryEvent, Hooks, type HookEvent } from './hooks'
|
|
15
|
-
import { checkPluginProposal } from './proposal'
|
|
16
|
-
import type { Plugin } from './types'
|
|
17
|
-
|
|
18
|
-
const open: Harness[] = []
|
|
19
|
-
afterEach(async () => {
|
|
20
|
-
for (const harness of open.splice(0)) await harness.close()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const temp = (): string => mkdtempSync(join(tmpdir(), 'hooks-'))
|
|
24
|
-
// The test runner owns `import()` here; in a real process the harness uses Node's own.
|
|
25
|
-
const importFile = (address: string): Promise<unknown> => import(/* @vite-ignore */ address)
|
|
26
|
-
|
|
27
|
-
/** A hooks file that appends what it was told to a file, so a test can read it back. */
|
|
28
|
-
const recorder = (out: string, events: string[], extra = ''): string =>
|
|
29
|
-
`import { appendFileSync } from 'node:fs'\nexport default function (harness) {\n${events.map((event) => ` harness.on('${event}', async (event, ctx) => { appendFileSync(${JSON.stringify(out)}, JSON.stringify({ type: event.type, id: event.id ?? event.runId, title: event.title, body: event.body, actor: event.actor }) + '\\n')${extra} })`).join('\n')}\n}\n`
|
|
30
|
-
|
|
31
|
-
const told = (out: string): Record<string, unknown>[] => (existsSync(out) ? readFileSync(out, 'utf8').trim().split('\n').filter(Boolean).map((line) => JSON.parse(line)) : [])
|
|
32
|
-
|
|
33
|
-
function kit(folder: string, name: string, hooks: string): void {
|
|
34
|
-
writeFile(join(folder, 'package.json'), JSON.stringify({ name, version: '1.0.0', description: `${name} mirrors memory.`, harness: { extends: ['memory', 'nonsense'], hooks: './hooks.mjs' } }))
|
|
35
|
-
writeFile(join(folder, 'hooks.mjs'), hooks)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function setup() {
|
|
39
|
-
const root = rootWithProject()
|
|
40
|
-
const base = temp()
|
|
41
|
-
const harness = createHarness({ dataRoot: root, stateDir: join(base, 'state'), secrets: memorySecretStore(), hookImporter: importFile })
|
|
42
|
-
open.push(harness)
|
|
43
|
-
const table = methods(harness)
|
|
44
|
-
const call = <T>(method: string, params: Record<string, unknown> = {}) => table[method](params) as Promise<T>
|
|
45
|
-
const saveFact = (title: string, body: string) => call('facts.save', { input: { title, kind: 'fact', body, namespace: '/' } })
|
|
46
|
-
return { harness, root, base, call, saveFact }
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
describe('a written file as an event', () => {
|
|
50
|
-
it('is a fact, a memory block, or a page by where it is in the Wiki', () => {
|
|
51
|
-
const wiki = '/data/wiki'
|
|
52
|
-
const change = (path: string, text: string) => entryEvent({ wiki, id: 'x', path: join(wiki, path), text, actor: 'user', reason: 'Edited' })
|
|
53
|
-
expect(change('facts/pnpm.md', '---\ntitle: Use pnpm\nkind: preference\n---\n\nAlways pnpm.\n')).toMatchObject({ type: 'memory.saved', title: 'Use pnpm', body: 'Always pnpm.', actor: 'user' })
|
|
54
|
-
expect(change('blocks/agents/a1/notes.md', '---\nlabel: notes\n---\n\nLikes tabs.\n')).toMatchObject({ type: 'memory.block.changed', title: 'notes', body: 'Likes tabs.' })
|
|
55
|
-
expect(change('library/arch.md', '---\ntitle: Architecture\n---\n\nLayers.\n')).toMatchObject({ type: 'wiki.page.saved', title: 'Architecture' })
|
|
56
|
-
expect(change('home.md', 'No front matter.')).toMatchObject({ type: 'wiki.page.saved', title: 'x', body: 'No front matter.' })
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('hooks a plugin gives the harness', () => {
|
|
61
|
-
it('mirrors saved facts through a real disposable MCP subprocess and stops on detach, changed code or revoked permission', async () => {
|
|
62
|
-
const { harness, base, call, saveFact } = setup()
|
|
63
|
-
const output = join(base, 'mirrored.jsonl')
|
|
64
|
-
await harness.apps.save({ id: 'mirror', name: 'Disposable mirror', kind: 'local', command: process.execPath, args: [join(process.cwd(), 'tests/mcp-hook-fixture.mjs'), output], enabled: true, global: true, readTools: ['mirror_fact'] })
|
|
65
|
-
const paths = ['one', 'two'].map((name) => join(base, name))
|
|
66
|
-
for (const [index, path] of paths.entries()) {
|
|
67
|
-
const name = ['mirror-one', 'mirror-two'][index]
|
|
68
|
-
kit(path, name, `export default function(harness) { harness.on('memory.saved', async (event, ctx) => { await ctx.call('Disposable mirror', 'mirror_fact', { plugin: '${name}', id: event.id, title: event.title, body: event.body }) }) }\n`)
|
|
69
|
-
const plugins = await call<Plugin[]>('plugins.install', { source: path })
|
|
70
|
-
const plugin = plugins.find((entry) => entry.id === `package:${name}`)!
|
|
71
|
-
await call('plugins.attach', { id: plugin.id })
|
|
72
|
-
await call('plugins.trust', { hash: plugin.code!.hash })
|
|
73
|
-
}
|
|
74
|
-
await harness.hooks.reload()
|
|
75
|
-
await saveFact('Synthetic one', 'Generated probe body.')
|
|
76
|
-
await harness.hooks.settled()
|
|
77
|
-
expect(told(output).map((row) => row.plugin)).toEqual(['mirror-one', 'mirror-two'])
|
|
78
|
-
expect(told(output)[0]).toMatchObject({ title: 'Synthetic one', body: 'Generated probe body.' })
|
|
79
|
-
await call('plugins.detach', { id: 'package:mirror-one' })
|
|
80
|
-
await saveFact('Synthetic two', 'Second body.')
|
|
81
|
-
await harness.hooks.settled()
|
|
82
|
-
expect(told(output).map((row) => row.plugin)).toEqual(['mirror-one', 'mirror-two', 'mirror-two'])
|
|
83
|
-
writeFile(join(paths[1], 'hooks.mjs'), '// Changed since trust\nexport default function() {}\n')
|
|
84
|
-
await saveFact('Synthetic three', 'Untrusted code hears nothing.')
|
|
85
|
-
await harness.hooks.settled()
|
|
86
|
-
expect(told(output)).toHaveLength(3)
|
|
87
|
-
await call('plugins.attach', { id: 'package:mirror-one' })
|
|
88
|
-
await harness.apps.save({ id: 'mirror', name: 'Disposable mirror', kind: 'local', command: process.execPath, args: [join(process.cwd(), 'tests/mcp-hook-fixture.mjs'), output], enabled: true, global: true, readTools: [] })
|
|
89
|
-
await harness.hooks.reload()
|
|
90
|
-
await saveFact('Synthetic four', 'Write succeeds with permission revoked.')
|
|
91
|
-
await harness.hooks.settled()
|
|
92
|
-
expect(told(output)).toHaveLength(3)
|
|
93
|
-
expect(harness.hooks.status()[join(paths[0], 'hooks.mjs')].lastError).toContain('asks before running')
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('run for every plugin that listens, only once the plugin is switched on for everyone and trusted', async () => {
|
|
97
|
-
const { harness, base, call, saveFact } = setup()
|
|
98
|
-
const one = join(base, 'one.log')
|
|
99
|
-
const two = join(base, 'two.log')
|
|
100
|
-
kit(join(base, 'docs'), 'memory-to-docs', recorder(one, ['memory.saved']))
|
|
101
|
-
kit(join(base, 'sheet'), 'memory-to-sheet', recorder(two, ['memory.saved', 'history.run.ended']))
|
|
102
|
-
await call('plugins.install', { source: join(base, 'docs') })
|
|
103
|
-
const listed = await call<Plugin[]>('plugins.install', { source: join(base, 'sheet') })
|
|
104
|
-
const docs = listed.find((plugin) => plugin.id === 'package:memory-to-docs')!
|
|
105
|
-
const sheet = listed.find((plugin) => plugin.id === 'package:memory-to-sheet')!
|
|
106
|
-
// What it says it extends is kept only for parts that exist.
|
|
107
|
-
expect(docs).toMatchObject({ extends: ['memory'], runsCode: true, trust: 'untrusted', attachment: { global: false } })
|
|
108
|
-
expect(docs.contributions.map((entry) => [entry.kind, entry.name, entry.state])).toEqual([['code', 'hooks.mjs', 'off']])
|
|
109
|
-
|
|
110
|
-
// Installed, switched off, untrusted: a saved fact reaches nobody.
|
|
111
|
-
await saveFact('Use pnpm', 'Always pnpm.')
|
|
112
|
-
await harness.hooks.settled()
|
|
113
|
-
expect([told(one), told(two)]).toEqual([[], []])
|
|
114
|
-
|
|
115
|
-
// Switched on but not trusted is still nothing.
|
|
116
|
-
await call('plugins.attach', { id: docs.id })
|
|
117
|
-
await call('plugins.attach', { id: sheet.id })
|
|
118
|
-
await harness.hooks.reload()
|
|
119
|
-
await saveFact('Tabs', 'Use tabs.')
|
|
120
|
-
await harness.hooks.settled()
|
|
121
|
-
expect([told(one), told(two)]).toEqual([[], []])
|
|
122
|
-
|
|
123
|
-
// Trusted: both hear the next fact, each in its own way.
|
|
124
|
-
await call('plugins.trust', { hash: docs.code!.hash })
|
|
125
|
-
const trusted = await call<Plugin[]>('plugins.trust', { hash: sheet.code!.hash })
|
|
126
|
-
await harness.hooks.reload()
|
|
127
|
-
await saveFact('Line length', 'Wrap at 100.')
|
|
128
|
-
await harness.hooks.settled()
|
|
129
|
-
expect(told(one)).toEqual([{ type: 'memory.saved', id: expect.any(String), title: 'Line length', body: 'Wrap at 100.', actor: 'user' }])
|
|
130
|
-
expect(told(two)).toEqual(told(one))
|
|
131
|
-
expect(trusted.find((plugin) => plugin.id === docs.id)!.contributions[0]).toMatchObject({ state: 'on', problem: null })
|
|
132
|
-
const described = (await call<Plugin[]>('plugins.list')).find((plugin) => plugin.id === sheet.id)!.contributions[0]
|
|
133
|
-
expect(described.description).toBe('Runs in the background after: memory.saved, history.run.ended.')
|
|
134
|
-
|
|
135
|
-
// Switched off for everyone again, it stops hearing; the other keeps going.
|
|
136
|
-
await call('plugins.detach', { id: docs.id })
|
|
137
|
-
await harness.hooks.reload()
|
|
138
|
-
await saveFact('Indent', 'Two spaces.')
|
|
139
|
-
await harness.hooks.settled()
|
|
140
|
-
expect([told(one).length, told(two).length]).toEqual([1, 2])
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('never lets a hook that throws or hangs touch the write, and shows what went wrong on its plugin', async () => {
|
|
144
|
-
const bus = new Bus()
|
|
145
|
-
const base = temp()
|
|
146
|
-
const out = join(base, 'ok.log')
|
|
147
|
-
writeFile(join(base, 'bad.mjs'), `export default function (harness) { harness.on('memory.saved', async () => { throw new Error('the document is gone') }) }\n`)
|
|
148
|
-
writeFile(join(base, 'slow.mjs'), `export default function (harness) { harness.on('memory.saved', () => new Promise(() => {})) }\n`)
|
|
149
|
-
writeFile(join(base, 'good.mjs'), recorder(out, ['memory.saved']))
|
|
150
|
-
writeFile(join(base, 'broken.mjs'), `export const nothing = 1\n`)
|
|
151
|
-
writeFile(join(base, 'unknown.mjs'), `export default function (harness) { harness.on('memory.deleted', () => {}) }\n`)
|
|
152
|
-
const hooks = new Hooks({ stateDir: base, bus, timeoutMs: 40, importFile, call: async () => undefined, wanted: async () => ['bad', 'slow', 'good', 'broken', 'unknown'].map((name) => ({ path: join(base, `${name}.mjs`), hash: 'h1' })) })
|
|
153
|
-
await hooks.reload()
|
|
154
|
-
const event: HookEvent = { type: 'memory.saved', id: 'f1', path: '/w/facts/f1.md', title: 'Fact', body: 'Body', text: 'Body', actor: 'user', reason: 'Created', at: '2026-10-13T00:00:00.000Z' }
|
|
155
|
-
hooks.emit(event)
|
|
156
|
-
await hooks.settled()
|
|
157
|
-
// The good hook ran although two before it failed.
|
|
158
|
-
expect(told(out)).toHaveLength(1)
|
|
159
|
-
const status = hooks.status()
|
|
160
|
-
expect(status[join(base, 'bad.mjs')]).toMatchObject({ events: ['memory.saved'], lastError: 'memory.saved: the document is gone' })
|
|
161
|
-
expect(status[join(base, 'slow.mjs')].lastError).toMatch(/took longer than/)
|
|
162
|
-
expect(status[join(base, 'good.mjs')]).toMatchObject({ lastError: null, lastRunAt: expect.any(String) })
|
|
163
|
-
expect(status[join(base, 'broken.mjs')].lastError).toBe('It could not be loaded: It must export a default function.')
|
|
164
|
-
expect(status[join(base, 'unknown.mjs')].lastError).toMatch(/There is no event called "memory.deleted"/)
|
|
165
|
-
hooks.close()
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it('reads a hooks file again when its code changed, and keeps the one it has when it did not', async () => {
|
|
169
|
-
const base = temp()
|
|
170
|
-
const path = join(base, 'hooks.mjs')
|
|
171
|
-
let hash = 'h1'
|
|
172
|
-
const read: string[] = []
|
|
173
|
-
const heard: string[] = []
|
|
174
|
-
// Stands in for the file on disk: each read gives the code as it is at that moment.
|
|
175
|
-
const importer = async (address: string) => {
|
|
176
|
-
read.push(address.slice(address.indexOf('?')))
|
|
177
|
-
const version = read.length
|
|
178
|
-
return { default: (harness: { on(event: string, handler: () => void): void }) => harness.on('wiki.page.saved', () => void heard.push(`version ${version}`)) }
|
|
179
|
-
}
|
|
180
|
-
const hooks = new Hooks({ stateDir: base, bus: new Bus(), call: async () => undefined, importFile: importer, wanted: async () => [{ path, hash }] })
|
|
181
|
-
const event: HookEvent = { type: 'wiki.page.saved', id: 'p1', path: '/w/library/p1.md', title: 'Page', body: 'One', text: 'One', actor: 'user', reason: 'Edited', at: '2026-10-13T00:00:00.000Z' }
|
|
182
|
-
await hooks.reload()
|
|
183
|
-
await hooks.reload()
|
|
184
|
-
hooks.emit(event)
|
|
185
|
-
await hooks.settled()
|
|
186
|
-
hash = 'h2'
|
|
187
|
-
await hooks.reload()
|
|
188
|
-
hooks.emit(event)
|
|
189
|
-
await hooks.settled()
|
|
190
|
-
// The fingerprint is part of the address, so Node does not hand back the module it already has.
|
|
191
|
-
expect(read).toEqual(['?v=h1', '?v=h2'])
|
|
192
|
-
expect(heard).toEqual(['version 1', 'version 2'])
|
|
193
|
-
hooks.close()
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('lets a hook reach a connected app only through a tool marked to run without asking', async () => {
|
|
197
|
-
const { harness } = setup()
|
|
198
|
-
await harness.apps.save({ id: 'docs', name: 'Docs', kind: 'local', command: process.execPath, args: [join(process.cwd(), 'tests/mcp-fixture.mjs')], enabled: true, global: true, readTools: ['read_record'] })
|
|
199
|
-
await harness.apps.save({ id: 'private', name: 'Private', kind: 'local', command: process.execPath, args: [join(process.cwd(), 'tests/mcp-fixture.mjs')], enabled: true, global: false, agentIds: ['a1'], readTools: ['read_record'] })
|
|
200
|
-
expect(await harness.apps.callUnattended('Docs', 'read_record', {})).toMatchObject({ content: [{ type: 'text', text: 'fixture' }] })
|
|
201
|
-
await expect(harness.apps.callUnattended('Docs', 'change_record', {})).rejects.toThrow('asks before running "change_record"')
|
|
202
|
-
// A hook runs for everyone, so an app that is on for one agent only is out of its reach.
|
|
203
|
-
await expect(harness.apps.callUnattended('Private', 'read_record', {})).rejects.toThrow('on for everyone')
|
|
204
|
-
await expect(harness.apps.callUnattended('Nope', 'read_record', {})).rejects.toThrow('no connected app called "Nope"')
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
describe('a plugin an agent proposes', () => {
|
|
209
|
-
const proposal = { name: 'Memory to Docs', description: 'Adds every saved memory to the team document.', extends: ['memory'], reason: 'You asked for a copy in Docs.', files: [{ path: 'hooks.mjs', content: "export default function (harness) { harness.on('memory.saved', async () => {}) }\n" }, { path: 'skills/docs/SKILL.md', content: '---\nname: docs\ndescription: How the copy works.\n---\nSteps.\n' }] }
|
|
210
|
-
|
|
211
|
-
it('waits in the Inbox, and is saved switched off and untrusted only when the person accepts it', async () => {
|
|
212
|
-
const { harness, root, call } = setup()
|
|
213
|
-
const tool = pluginProposeTool(harness.env, { agentId: 'a1', agentName: 'Helper', actor: 'Chat' })
|
|
214
|
-
expect(await tool.execute(proposal)).toBe('The plugin "memory-to-docs" is waiting in the Inbox. It is saved only if the user accepts it, and it does nothing until they switch it on and trust its code.')
|
|
215
|
-
expect(existsSync(join(root, 'plugins'))).toBe(false)
|
|
216
|
-
const [item] = listInbox(wikiDir(root))
|
|
217
|
-
expect(item).toMatchObject({ kind: 'proposed', title: 'Proposed plugin "Memory to Docs"' })
|
|
218
|
-
expect(item.detail).toContain('- hooks.mjs (')
|
|
219
|
-
expect(item.detail).toContain('It brings code.')
|
|
220
|
-
expect(item.detail).toContain('From Helper (Chat).')
|
|
221
|
-
|
|
222
|
-
expect(await call<string>('wiki.inboxAccept', { itemId: item.id })).toBe('Saved the plugin "memory-to-docs", switched off. Its code does not run until you switch it on and trust it.')
|
|
223
|
-
expect(readdirSync(join(root, 'plugins'))).toEqual(['memory-to-docs'])
|
|
224
|
-
expect(JSON.parse(readFileSync(join(root, 'plugins/memory-to-docs/package.json'), 'utf8'))).toMatchObject({ name: 'memory-to-docs', description: proposal.description, harness: { extends: ['memory'], hooks: './hooks.mjs' } })
|
|
225
|
-
const plugin = (await call<Plugin[]>('plugins.list')).find((candidate) => candidate.id === 'package:memory-to-docs')!
|
|
226
|
-
expect(plugin).toMatchObject({ extends: ['memory'], trust: 'untrusted', install: { kind: 'folder' }, attachment: { global: false } })
|
|
227
|
-
expect(plugin.contributions.map((entry) => [entry.kind, entry.name, entry.state])).toEqual([['skill', 'docs', 'off'], ['code', 'hooks.mjs', 'off']])
|
|
228
|
-
await harness.hooks.reload()
|
|
229
|
-
expect(Object.keys(harness.hooks.status())).toEqual([])
|
|
230
|
-
// Accepting the same proposal again would overwrite a folder, so it is refused.
|
|
231
|
-
await tool.execute(proposal)
|
|
232
|
-
await expect(call('wiki.inboxAccept', { itemId: listInbox(wikiDir(root))[0].id })).rejects.toThrow('already a plugin folder')
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('refuses what could not be a safe plugin, and says why', () => {
|
|
236
|
-
const bad = (change: Partial<typeof proposal>) => () => checkPluginProposal({ ...proposal, ...change })
|
|
237
|
-
expect(bad({ name: ' ' })).toThrow('short name')
|
|
238
|
-
expect(bad({ description: '' })).toThrow('one line')
|
|
239
|
-
expect(bad({ extends: ['approvals'] })).toThrow('"approvals" is none of them')
|
|
240
|
-
expect(bad({ files: [] })).toThrow('between 1 and 40 files')
|
|
241
|
-
expect(bad({ files: [{ path: '../outside.mjs', content: 'x' }] })).toThrow('not a path inside the plugin')
|
|
242
|
-
expect(bad({ files: [{ path: '/etc/hosts', content: 'x' }] })).toThrow('not a path inside the plugin')
|
|
243
|
-
expect(bad({ files: [{ path: '.env', content: 'x' }] })).toThrow('cannot be part of a plugin')
|
|
244
|
-
expect(bad({ files: [{ path: 'node_modules/x/index.js', content: 'x' }] })).toThrow('cannot be part of a plugin')
|
|
245
|
-
expect(bad({ files: [{ path: 'package.json', content: '{}' }] })).toThrow('Leave package.json out')
|
|
246
|
-
expect(bad({ files: [{ path: 'a.md', content: 'x' }, { path: 'A.md', content: 'y' }] })).toThrow('listed twice')
|
|
247
|
-
expect(bad({ files: [{ path: 'big.md', content: 'x'.repeat(300 * 1024) }] })).toThrow('larger than 256 KiB')
|
|
248
|
-
expect(checkPluginProposal({ ...proposal, files: [{ path: './skills/a/SKILL.md', content: 'x' }] })).toMatchObject({ slug: 'memory-to-docs', hooks: null, runsCode: false })
|
|
249
|
-
})
|
|
250
|
-
})
|
package/src/plugins/hooks.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
// Hooks: code a plugin gives the harness to run after something changed. A Pi extension lives
|
|
2
|
-
// only while a session is open, and most memory is written outside one (a person accepts an
|
|
3
|
-
// Inbox item, edits a fact, the background upkeep runs), so these are the harness's own events,
|
|
4
|
-
// delivered in its own process.
|
|
5
|
-
//
|
|
6
|
-
// A hook is told after the fact. It cannot stop or change a write, it runs with a time limit,
|
|
7
|
-
// and what it throws is recorded and shown on its plugin. It reaches outside only through
|
|
8
|
-
// `ctx.call`, which the harness limits to connected-app tools a person marked to run without asking.
|
|
9
|
-
import { chmodSync } from 'node:fs'
|
|
10
|
-
import { join, sep } from 'node:path'
|
|
11
|
-
import { pathToFileURL } from 'node:url'
|
|
12
|
-
|
|
13
|
-
import type { Bus } from '../core/bus'
|
|
14
|
-
import { log } from '../core/bus'
|
|
15
|
-
import { onChange, type Change } from '../core/changes'
|
|
16
|
-
import * as frontmatter from '../core/frontmatter'
|
|
17
|
-
import { now, readJsonOr, writeJson } from '../core/fs'
|
|
18
|
-
|
|
19
|
-
export const HOOK_EVENTS = ['memory.saved', 'memory.block.changed', 'wiki.page.saved', 'history.run.ended'] as const
|
|
20
|
-
export type HookEventType = (typeof HOOK_EVENTS)[number]
|
|
21
|
-
|
|
22
|
-
/** A fact, a memory block, or a page, as it is after the change. */
|
|
23
|
-
export interface EntryEvent {
|
|
24
|
-
type: 'memory.saved' | 'memory.block.changed' | 'wiki.page.saved'
|
|
25
|
-
id: string
|
|
26
|
-
path: string
|
|
27
|
-
title: string
|
|
28
|
-
/** The entry without its front matter. */
|
|
29
|
-
body: string
|
|
30
|
-
/** The whole file. */
|
|
31
|
-
text: string
|
|
32
|
-
/** Who made the change: `user`, an agent's name, or the background upkeep. */
|
|
33
|
-
actor: string
|
|
34
|
-
reason: string
|
|
35
|
-
at: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface RunEndedEvent {
|
|
39
|
-
type: 'history.run.ended'
|
|
40
|
-
runId: string
|
|
41
|
-
sessionId: string
|
|
42
|
-
status: string
|
|
43
|
-
goal: string | null
|
|
44
|
-
agentId: string | null
|
|
45
|
-
projectId: string | null
|
|
46
|
-
startedAt: string
|
|
47
|
-
endedAt: string | null
|
|
48
|
-
at: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type HookEvent = EntryEvent | RunEndedEvent
|
|
52
|
-
|
|
53
|
-
export interface HookContext {
|
|
54
|
-
/** Calls a tool of a connected app that is on for everyone, if a person marked that tool to run without asking. */
|
|
55
|
-
call(connection: string, tool: string, args?: Record<string, unknown>): Promise<unknown>
|
|
56
|
-
log(message: string): void
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
type Handler = (event: HookEvent, context: HookContext) => unknown
|
|
60
|
-
|
|
61
|
-
/** What a hooks file is given: `export default function (harness) { harness.on('memory.saved', …) }`. */
|
|
62
|
-
export interface HookApi {
|
|
63
|
-
on(event: HookEventType, handler: Handler): void
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface HookTarget {
|
|
67
|
-
/** The hooks file. */
|
|
68
|
-
path: string
|
|
69
|
-
/** A hash of the package's code, so a changed file is loaded again. */
|
|
70
|
-
hash: string
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface HookStatus {
|
|
74
|
-
/** The events it listens for. */
|
|
75
|
-
events: string[]
|
|
76
|
-
lastRunAt: string | null
|
|
77
|
-
/** Why it could not be loaded, or what its last failing run threw. */
|
|
78
|
-
lastError: string | null
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface HooksOptions {
|
|
82
|
-
stateDir: string
|
|
83
|
-
bus: Bus
|
|
84
|
-
/** The hooks files that should run now: attached for everyone, and trusted. */
|
|
85
|
-
wanted: () => Promise<HookTarget[]>
|
|
86
|
-
call: HookContext['call']
|
|
87
|
-
/** How long one handler may take. */
|
|
88
|
-
timeoutMs?: number
|
|
89
|
-
/** Reads a hooks file as a module. Replaced in tests, where the runner owns `import()`. */
|
|
90
|
-
importFile?: (address: string) => Promise<unknown>
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Node's own `import()`, out of reach of a bundler or a test runner that would rewrite it: a hooks
|
|
94
|
-
// file is a person's file on disk, loaded as it is, and its address must reach Node unchanged.
|
|
95
|
-
const importFile = new Function('address', 'return import(address)') as (address: string) => Promise<unknown>
|
|
96
|
-
|
|
97
|
-
interface Loaded extends HookTarget {
|
|
98
|
-
handlers: Map<string, Handler[]>
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Which event a written file is, from where it is in the Wiki. */
|
|
102
|
-
export function entryEvent(change: Change): EntryEvent {
|
|
103
|
-
const within = change.path.startsWith(`${change.wiki}${sep}`) ? change.path.slice(change.wiki.length + 1).split(sep) : []
|
|
104
|
-
const type = within[0] === 'facts' ? 'memory.saved' : within[0] === 'blocks' ? 'memory.block.changed' : 'wiki.page.saved'
|
|
105
|
-
const parsed = frontmatter.parse(change.text)
|
|
106
|
-
const front = (parsed?.front ?? {}) as Record<string, unknown>
|
|
107
|
-
const title = typeof front.title === 'string' ? front.title : typeof front.label === 'string' ? front.label : change.id
|
|
108
|
-
return { type, id: change.id, path: change.path, title, body: (parsed?.body ?? change.text).trim(), text: change.text, actor: change.actor, reason: change.reason, at: now() }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export class Hooks {
|
|
112
|
-
private loaded: Loaded[] = []
|
|
113
|
-
private readonly queue: HookEvent[] = []
|
|
114
|
-
private working: Promise<void> = Promise.resolve()
|
|
115
|
-
private reloading: Promise<void> = Promise.resolve()
|
|
116
|
-
private readonly statusPath: string
|
|
117
|
-
private wiki: string | null = null
|
|
118
|
-
private readonly stop: () => void
|
|
119
|
-
|
|
120
|
-
constructor(private readonly options: HooksOptions) {
|
|
121
|
-
this.statusPath = join(options.stateDir, 'plugin-hooks.json')
|
|
122
|
-
this.stop = onChange((change) => {
|
|
123
|
-
// Several harnesses can share a process; each hears only about its own Wiki.
|
|
124
|
-
if (change.wiki === this.wiki) this.emit(entryEvent(change))
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** The Wiki whose changes these hooks hear about. */
|
|
129
|
-
watch(wiki: string | null): void {
|
|
130
|
-
this.wiki = wiki
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** What each hooks file listens for, and how its last run went. */
|
|
134
|
-
status(): Record<string, HookStatus> {
|
|
135
|
-
return readJsonOr<Record<string, HookStatus>>(this.statusPath, {})
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private record(path: string, change: Partial<HookStatus>): void {
|
|
139
|
-
const all = this.status()
|
|
140
|
-
const before = all[path] ?? { events: [], lastRunAt: null, lastError: null }
|
|
141
|
-
const next = { ...before, ...change }
|
|
142
|
-
if (JSON.stringify(before) === JSON.stringify(next) && all[path]) return
|
|
143
|
-
all[path] = next
|
|
144
|
-
writeJson(this.statusPath, all)
|
|
145
|
-
chmodSync(this.statusPath, 0o600)
|
|
146
|
-
// A new failure, or a failure that cleared, is something a person looking at the plugin should see.
|
|
147
|
-
if (before.lastError !== next.lastError || before.events.join() !== next.events.join()) this.options.bus.emit({ type: 'plugins.changed' })
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/** Loads the hooks files that should run and drops the ones that should not. Safe to call often. */
|
|
151
|
-
reload(): Promise<void> {
|
|
152
|
-
this.reloading = this.reloading.then(async () => {
|
|
153
|
-
let wanted: HookTarget[]
|
|
154
|
-
try {
|
|
155
|
-
wanted = await this.options.wanted()
|
|
156
|
-
} catch (error) {
|
|
157
|
-
log('plugin hooks', error instanceof Error ? error.message : String(error))
|
|
158
|
-
return
|
|
159
|
-
}
|
|
160
|
-
const next: Loaded[] = []
|
|
161
|
-
for (const target of wanted) {
|
|
162
|
-
const kept = this.loaded.find((hook) => hook.path === target.path && hook.hash === target.hash)
|
|
163
|
-
if (kept) {
|
|
164
|
-
next.push(kept)
|
|
165
|
-
continue
|
|
166
|
-
}
|
|
167
|
-
const handlers = new Map<string, Handler[]>()
|
|
168
|
-
try {
|
|
169
|
-
// The hash is part of the address, so a changed file is read again and not served from the module cache.
|
|
170
|
-
const module = (await (this.options.importFile ?? importFile)(`${pathToFileURL(target.path).href}?v=${target.hash}`)) as { default?: unknown }
|
|
171
|
-
if (typeof module.default !== 'function') throw new Error('It must export a default function.')
|
|
172
|
-
const api: HookApi = {
|
|
173
|
-
on: (event, handler) => {
|
|
174
|
-
if (!(HOOK_EVENTS as readonly string[]).includes(event)) throw new Error(`There is no event called "${String(event)}". The events are ${HOOK_EVENTS.join(', ')}.`)
|
|
175
|
-
if (typeof handler !== 'function') throw new Error('A handler must be a function.')
|
|
176
|
-
handlers.set(event, [...(handlers.get(event) ?? []), handler])
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
await (module.default as (api: HookApi) => unknown)(api)
|
|
180
|
-
next.push({ ...target, handlers })
|
|
181
|
-
this.record(target.path, { events: [...handlers.keys()], lastError: null })
|
|
182
|
-
} catch (error) {
|
|
183
|
-
this.record(target.path, { events: [], lastError: `It could not be loaded: ${error instanceof Error ? error.message : String(error)}` })
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
this.loaded = next
|
|
187
|
-
})
|
|
188
|
-
return this.reloading
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** Takes an event down. It is delivered afterwards, in the order events arrived. */
|
|
192
|
-
emit(event: HookEvent): void {
|
|
193
|
-
this.queue.push(event)
|
|
194
|
-
this.working = this.working.then(() => this.drain())
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private async drain(): Promise<void> {
|
|
198
|
-
// A file can change on disk without anyone telling the harness. What is loaded is checked
|
|
199
|
-
// against what should run before it is told anything, so code that changed since it was
|
|
200
|
-
// trusted stops here. With nothing loaded there is nothing to check: a new hook arrives
|
|
201
|
-
// with a `plugins.changed` notice.
|
|
202
|
-
if (this.loaded.length) await this.reload()
|
|
203
|
-
else await this.reloading
|
|
204
|
-
for (let event = this.queue.shift(); event; event = this.queue.shift()) {
|
|
205
|
-
for (const hook of this.loaded) {
|
|
206
|
-
for (const handler of hook.handlers.get(event.type) ?? []) await this.run(hook, handler, event)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
private async run(hook: Loaded, handler: Handler, event: HookEvent): Promise<void> {
|
|
212
|
-
const context: HookContext = { call: this.options.call, log: (message) => log(`plugin hook ${hook.path}`, String(message)) }
|
|
213
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
214
|
-
try {
|
|
215
|
-
await Promise.race([
|
|
216
|
-
Promise.resolve().then(() => handler(event, context)),
|
|
217
|
-
new Promise((_, reject) => {
|
|
218
|
-
timer = setTimeout(() => reject(new Error(`It took longer than ${Math.round((this.options.timeoutMs ?? 30_000) / 1000)} seconds and was left behind.`)), this.options.timeoutMs ?? 30_000)
|
|
219
|
-
})
|
|
220
|
-
])
|
|
221
|
-
this.record(hook.path, { lastRunAt: now(), lastError: null })
|
|
222
|
-
} catch (error) {
|
|
223
|
-
this.record(hook.path, { lastRunAt: now(), lastError: `${event.type}: ${error instanceof Error ? error.message : String(error)}`.slice(0, 1000) })
|
|
224
|
-
} finally {
|
|
225
|
-
if (timer) clearTimeout(timer)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** Resolves when every event taken down so far was delivered. */
|
|
230
|
-
async settled(): Promise<void> {
|
|
231
|
-
await this.reloading
|
|
232
|
-
await this.working
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
close(): void {
|
|
236
|
-
this.stop()
|
|
237
|
-
this.loaded = []
|
|
238
|
-
}
|
|
239
|
-
}
|
package/src/plugins/identity.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Which plugin a found file belongs to, and what it is called there. The list a person sees and
|
|
2
|
-
// the choice of what loads both use this, so a switch on the page names exactly what stops loading.
|
|
3
|
-
import { basename, dirname } from 'node:path'
|
|
4
|
-
|
|
5
|
-
import * as registry from '../skills/registry'
|
|
6
|
-
import { BUILTIN_PREFIX, type Found } from './discover'
|
|
7
|
-
import { packageManifest } from './manifest'
|
|
8
|
-
import type { ContributionKind } from './types'
|
|
9
|
-
|
|
10
|
-
export { packageManifest } from './manifest'
|
|
11
|
-
|
|
12
|
-
export const FOUND_KINDS: Record<Found['kind'], ContributionKind> = { extension: 'code', hook: 'code', skill: 'skill', prompt: 'command', theme: 'theme', context: 'instruction' }
|
|
13
|
-
|
|
14
|
-
/** The id of the plugin a found item is listed under. */
|
|
15
|
-
export function pluginIdOf(item: Found): string {
|
|
16
|
-
if (item.origin === 'builtin') return 'pi'
|
|
17
|
-
if (item.packageRoot) return `package:${packageManifest(item.packageRoot).name}`
|
|
18
|
-
return item.origin === 'home' ? 'home' : item.origin === 'project' ? 'project' : 'shared'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** A found item's name and description as a person reads them. */
|
|
22
|
-
export function describeFound(item: Found): { name: string; description: string | null } {
|
|
23
|
-
if (item.path.startsWith(BUILTIN_PREFIX)) return { name: item.path.slice(BUILTIN_PREFIX.length), description: null }
|
|
24
|
-
const file = basename(item.path)
|
|
25
|
-
if (item.kind === 'extension') return { name: file.startsWith('index.') ? basename(dirname(item.path)) : file, description: null }
|
|
26
|
-
if (item.kind === 'context' || item.kind === 'theme' || item.kind === 'hook') return { name: file, description: null }
|
|
27
|
-
const described = registry.describeFile(item.path)
|
|
28
|
-
return { name: described?.name ?? file, description: described?.description ?? '' }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const contributionId = (pluginId: string, kind: ContributionKind, name: string): string => `${pluginId}/${kind}/${name}`
|
|
32
|
-
|
|
33
|
-
/** The ids a choice about a found item is stored under: the item itself, then its plugin. */
|
|
34
|
-
export function foundIds(item: Found): [string, string] {
|
|
35
|
-
const pluginId = pluginIdOf(item)
|
|
36
|
-
return [contributionId(pluginId, FOUND_KINDS[item.kind], describeFound(item).name), pluginId]
|
|
37
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import { tmpdir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
4
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
-
|
|
6
|
-
import { sharedDir } from '../accounts/env'
|
|
7
|
-
import { memorySecretStore } from '../accounts/secrets'
|
|
8
|
-
import { createHarness, type Harness } from '../harness'
|
|
9
|
-
import { methods } from '../server/methods'
|
|
10
|
-
import { rootWithProject, writeFile } from '../../tests/helpers'
|
|
11
|
-
import { normalizeSource } from './install'
|
|
12
|
-
import type { Plugin } from './types'
|
|
13
|
-
|
|
14
|
-
const open: Harness[] = []
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
for (const harness of open.splice(0)) await harness.close()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
function setup() {
|
|
20
|
-
const root = rootWithProject()
|
|
21
|
-
const base = mkdtempSync(join(tmpdir(), 'install-'))
|
|
22
|
-
const harness = createHarness({ dataRoot: root, stateDir: join(base, 'state'), secrets: memorySecretStore() })
|
|
23
|
-
open.push(harness)
|
|
24
|
-
harness.accounts.create('Main', 'apiKey')
|
|
25
|
-
const agentDir = harness.accounts.context(harness.accounts.resolve(null, null)).configDir
|
|
26
|
-
const table = methods(harness)
|
|
27
|
-
const call = <T>(method: string, params: Record<string, unknown> = {}) => table[method](params) as Promise<T>
|
|
28
|
-
const loaded = async () => {
|
|
29
|
-
const loading = await harness.plugins.loading({ cwd: base, agentDir, caller: {} })
|
|
30
|
-
return { extensions: loading.extensionPaths.map((path) => path.replace(`${base}/`, '')), skills: loading.skillPaths.map((path) => path.replace(`${base}/`, '')) }
|
|
31
|
-
}
|
|
32
|
-
return { harness, root, base, call, loaded }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function kit(folder: string): void {
|
|
36
|
-
writeFile(join(folder, 'package.json'), JSON.stringify({ name: 'review-kit', description: 'Review helpers.', version: '1.0.0' }))
|
|
37
|
-
writeFile(join(folder, 'extensions/guard.ts'), 'export default function () {}\n')
|
|
38
|
-
writeFile(join(folder, 'skills/audit/SKILL.md'), '---\nname: audit\ndescription: Audit a change.\n---\nSteps.\n')
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe('installing a package', () => {
|
|
42
|
-
it('reads a name as an npm package, keeps a folder or an address as it is, and refuses a relative path', () => {
|
|
43
|
-
expect(normalizeSource(' pi-tools ')).toBe('npm:pi-tools')
|
|
44
|
-
expect(normalizeSource('npm:@scope/pi-tools@1.0.0')).toBe('npm:@scope/pi-tools@1.0.0')
|
|
45
|
-
expect(normalizeSource('git:github.com/example/pi-tools@v1')).toBe('git:github.com/example/pi-tools@v1')
|
|
46
|
-
expect(normalizeSource('https://github.com/example/pi-tools')).toBe('https://github.com/example/pi-tools')
|
|
47
|
-
expect(normalizeSource('/tmp/kit/')).toBe('/tmp/kit')
|
|
48
|
-
expect(() => normalizeSource('./kit')).toThrow('whole path')
|
|
49
|
-
expect(() => normalizeSource(' ')).toThrow()
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('arrives switched off and untrusted, and loads only once it is both switched on and trusted', async () => {
|
|
53
|
-
const { harness, base, call, loaded } = setup()
|
|
54
|
-
kit(join(base, 'kit'))
|
|
55
|
-
const after = await call<Plugin[]>('plugins.install', { source: join(base, 'kit') })
|
|
56
|
-
const plugin = after.find((candidate) => candidate.id === 'package:review-kit')!
|
|
57
|
-
expect(plugin).toMatchObject({ source: 'package', version: '1.0.0', runsCode: true, trust: 'untrusted', install: { kind: 'settings', source: join(base, 'kit') }, attachment: { global: false } })
|
|
58
|
-
expect(plugin.contributions.map((entry) => `${entry.kind}:${entry.name}:${entry.state}`)).toEqual(['code:guard.ts:off', 'skill:audit:off'])
|
|
59
|
-
// It is named in the settings every account shares, as `pi install` would have done.
|
|
60
|
-
expect(JSON.parse(readFileSync(join(sharedDir(harness.accounts.dir), 'settings.json'), 'utf8')).packages).toEqual([join(base, 'kit')])
|
|
61
|
-
expect(await loaded()).toEqual({ extensions: [], skills: [] })
|
|
62
|
-
|
|
63
|
-
// Switched on, its text loads; its code still waits for trust.
|
|
64
|
-
await call('plugins.attach', { id: plugin.id })
|
|
65
|
-
expect(await loaded()).toEqual({ extensions: [], skills: ['kit/skills/audit/SKILL.md'] })
|
|
66
|
-
const trusted = await call<Plugin[]>('plugins.trust', { hash: plugin.code!.hash })
|
|
67
|
-
expect(trusted.find((candidate) => candidate.id === plugin.id)).toMatchObject({ trust: 'trusted' })
|
|
68
|
-
expect(await loaded()).toEqual({ extensions: ['kit/extensions/guard.ts'], skills: ['kit/skills/audit/SKILL.md'] })
|
|
69
|
-
|
|
70
|
-
// A stale fingerprint is refused, and changed code waits again.
|
|
71
|
-
writeFileSync(join(base, 'kit/extensions/guard.ts'), 'export default function () { /* changed */ }\n')
|
|
72
|
-
await expect(call('plugins.trust', { hash: plugin.code!.hash })).rejects.toThrow('changed since it was shown')
|
|
73
|
-
expect((await loaded()).extensions).toEqual([])
|
|
74
|
-
|
|
75
|
-
const removed = await call<Plugin[]>('plugins.remove', { pluginId: plugin.id })
|
|
76
|
-
expect(removed.some((candidate) => candidate.id === plugin.id)).toBe(false)
|
|
77
|
-
expect(JSON.parse(readFileSync(join(sharedDir(harness.accounts.dir), 'settings.json'), 'utf8')).packages ?? []).toEqual([])
|
|
78
|
-
// A folder that was installed in place is not deleted with its entry.
|
|
79
|
-
expect(existsSync(join(base, 'kit/package.json'))).toBe(true)
|
|
80
|
-
expect(await loaded()).toEqual({ extensions: [], skills: [] })
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('refuses a folder that is not there, and a plugin that was not installed', async () => {
|
|
84
|
-
const { base, call } = setup()
|
|
85
|
-
await expect(call('plugins.install', { source: join(base, 'missing') })).rejects.toThrow('does not exist')
|
|
86
|
-
await expect(call('plugins.remove', { pluginId: 'memory' })).rejects.toThrow('was not installed as a package')
|
|
87
|
-
await expect(call('plugins.update', { pluginId: 'nope' })).rejects.toThrow()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('finds a package kept as a folder beside the data, and moves it to the trash when removed', async () => {
|
|
91
|
-
const { root, call, loaded, harness } = setup()
|
|
92
|
-
// Trust has been asked per package before this folder appears, so its code waits.
|
|
93
|
-
harness.plugins.trust.adopt([])
|
|
94
|
-
kit(join(root, 'plugins/review-kit'))
|
|
95
|
-
mkdirSync(join(root, 'plugins/.hidden'), { recursive: true })
|
|
96
|
-
const plugins = await call<Plugin[]>('plugins.list')
|
|
97
|
-
const plugin = plugins.find((candidate) => candidate.id === 'package:review-kit')!
|
|
98
|
-
expect(plugin).toMatchObject({ trust: 'untrusted', install: { kind: 'folder', source: join(root, 'plugins/review-kit') }, attachment: { global: true } })
|
|
99
|
-
expect((await loaded()).skills.some((path) => path.endsWith('plugins/review-kit/skills/audit/SKILL.md'))).toBe(true)
|
|
100
|
-
expect((await loaded()).extensions).toEqual([])
|
|
101
|
-
await expect(call('plugins.update', { pluginId: plugin.id })).rejects.toThrow('is a folder on this machine')
|
|
102
|
-
|
|
103
|
-
await call('plugins.remove', { pluginId: plugin.id })
|
|
104
|
-
expect(existsSync(join(root, 'plugins/review-kit'))).toBe(false)
|
|
105
|
-
expect(readdirSync(join(root, 'trash/plugins')).some((name) => name.startsWith('review-kit-'))).toBe(true)
|
|
106
|
-
})
|
|
107
|
-
})
|