@xingular/harness 1.0.0 → 1.2.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 +46 -5
- package/README.md +75 -21
- package/dist/cli.js +17421 -8711
- package/dist/sdk/accounts/accounts.d.ts +27 -3
- package/dist/sdk/accounts/accounts.js +78 -24
- package/dist/sdk/accounts/secrets.d.ts +12 -0
- package/dist/sdk/accounts/secrets.js +114 -7
- package/dist/sdk/accounts/store.d.ts +26 -1
- package/dist/sdk/accounts/store.js +1 -1
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +48 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/planResearch.d.ts +5 -0
- package/dist/sdk/agents/planResearch.js +19 -0
- package/dist/sdk/agents/profiles.d.ts +81 -0
- package/dist/sdk/agents/profiles.js +226 -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 +871 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +155 -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 +167 -0
- package/dist/sdk/annotations/store.js +394 -0
- package/dist/sdk/browser/backend.d.ts +16 -0
- package/dist/sdk/browser/backend.js +17 -0
- package/dist/sdk/browser/contracts.d.ts +19 -0
- package/dist/sdk/browser/contracts.js +116 -0
- package/dist/sdk/browser/index.d.ts +14 -3
- package/dist/sdk/browser/index.js +11 -4
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +1 -0
- package/dist/sdk/browser/session.js +45 -9
- package/dist/sdk/browser/tools.d.ts +1 -4
- package/dist/sdk/browser/tools.js +14 -91
- package/dist/sdk/cli.js +24 -15
- package/dist/sdk/commands/list.d.ts +3 -0
- package/dist/sdk/commands/list.js +75 -0
- package/dist/sdk/commands/registry.d.ts +250 -0
- package/dist/sdk/commands/registry.js +47 -0
- package/dist/sdk/commands/routes.d.ts +16 -0
- package/dist/sdk/commands/routes.js +33 -0
- package/dist/sdk/core/bus.js +1 -1
- package/dist/sdk/core/commandBudget.d.ts +23 -0
- package/dist/sdk/core/commandBudget.js +57 -0
- package/dist/sdk/core/env.d.ts +1 -1
- package/dist/sdk/core/env.js +1 -1
- package/dist/sdk/core/index.js +17 -1
- package/dist/sdk/core/interactiveProfile.d.ts +46 -0
- package/dist/sdk/core/interactiveProfile.js +143 -0
- package/dist/sdk/core/workspace.d.ts +13 -0
- package/dist/sdk/core/workspace.js +88 -0
- package/dist/sdk/core/workspaceBashTool.d.ts +4 -0
- package/dist/sdk/core/workspaceBashTool.js +36 -0
- package/dist/sdk/core/workspaceSandbox.d.ts +31 -0
- package/dist/sdk/core/workspaceSandbox.js +320 -0
- package/dist/sdk/core/workspaceWriteOperations.d.ts +22 -0
- package/dist/sdk/core/workspaceWriteOperations.js +206 -0
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/doctor/types.d.ts +2 -2
- package/dist/sdk/harness.d.ts +22 -0
- package/dist/sdk/harness.js +84 -5
- package/dist/sdk/history/recall.js +8 -8
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/hub/cli.d.ts +1 -1
- package/dist/sdk/hub/cli.js +1 -1
- package/dist/sdk/hub/schema.d.ts +1 -1
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +16 -1
- package/dist/sdk/index.js +10 -0
- package/dist/sdk/mcp/connections.js +14 -7
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/pi/runtime.d.ts +2 -1
- package/dist/sdk/pi/runtime.js +2 -2
- package/dist/sdk/plugins/builtin.d.ts +1 -0
- package/dist/sdk/plugins/builtin.js +145 -33
- package/dist/sdk/plugins/builtinRegistry.d.ts +60 -0
- package/dist/sdk/plugins/builtinRegistry.js +71 -0
- package/dist/sdk/plugins/catalogue.js +2 -1
- package/dist/sdk/plugins/cli.d.ts +1 -1
- package/dist/sdk/plugins/cli.js +4 -3
- package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
- package/dist/sdk/plugins/commandPrefix.js +21 -0
- package/dist/sdk/plugins/firstParty.d.ts +1 -1
- package/dist/sdk/plugins/firstParty.js +8 -2
- package/dist/sdk/plugins/inventory.js +4 -3
- package/dist/sdk/plugins/manifest.d.ts +2 -0
- package/dist/sdk/plugins/manifest.js +2 -1
- package/dist/sdk/plugins/surfaces.d.ts +5 -0
- package/dist/sdk/plugins/surfaces.js +11 -0
- package/dist/sdk/plugins/types.d.ts +15 -0
- package/dist/sdk/prompt/compile.d.ts +19 -2
- package/dist/sdk/prompt/compile.js +79 -26
- package/dist/sdk/prompt/standard.d.ts +17 -0
- package/dist/sdk/prompt/standard.js +21 -0
- package/dist/sdk/refine/cli.d.ts +1 -1
- package/dist/sdk/refine/cli.js +2 -2
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/server/methods.js +27 -86
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/server/remote.js +36 -11
- package/dist/sdk/sessions/compactionCheckpoint.d.ts +11 -0
- package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
- package/dist/sdk/sessions/continuation.d.ts +3 -0
- package/dist/sdk/sessions/continuation.js +5 -1
- package/dist/sdk/sessions/contracts.d.ts +185 -0
- package/dist/sdk/sessions/contracts.js +101 -4
- package/dist/sdk/sessions/control.d.ts +72 -0
- package/dist/sdk/sessions/control.js +158 -0
- package/dist/sdk/sessions/initialBash.d.ts +7 -0
- package/dist/sdk/sessions/initialBash.js +7 -0
- package/dist/sdk/sessions/planMode.d.ts +18 -4
- package/dist/sdk/sessions/planMode.js +82 -16
- package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
- package/dist/sdk/sessions/reasoningSummaries.js +3 -3
- package/dist/sdk/sessions/registry.d.ts +2 -1
- package/dist/sdk/sessions/registry.js +6 -3
- package/dist/sdk/sessions/runIndex.js +7 -7
- package/dist/sdk/sessions/runStore.d.ts +14 -0
- package/dist/sdk/sessions/runStore.js +38 -1
- package/dist/sdk/sessions/runner.d.ts +40 -2
- package/dist/sdk/sessions/runner.js +163 -15
- package/dist/sdk/sessions/sessions.d.ts +40 -8
- package/dist/sdk/sessions/sessions.js +279 -27
- 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 +22 -2
- package/dist/sdk/sessions/subagentService.js +93 -16
- package/dist/sdk/sessions/subagents.d.ts +9 -1
- package/dist/sdk/sessions/subagents.js +19 -0
- package/dist/sdk/sessions/workSlice.d.ts +103 -0
- package/dist/sdk/sessions/workSlice.js +297 -0
- package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
- package/dist/sdk/sessions/workspaceBoundary.js +46 -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 +187 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/controller.d.ts +15 -1
- package/dist/sdk/terminal/controller.js +67 -8
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
- package/dist/sdk/terminal/opentui/theme.js +87 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -1
- package/dist/sdk/terminal/piExtension.js +169 -84
- package/dist/sdk/terminal/socket.d.ts +6 -3
- package/dist/sdk/terminal/socket.js +40 -9
- package/dist/sdk/terminal/standalone.d.ts +9 -0
- package/dist/sdk/terminal/standalone.js +118 -19
- package/dist/sdk/terminal/subagentMonitor.d.ts +47 -0
- package/dist/sdk/terminal/subagentMonitor.js +152 -0
- package/dist/sdk/terminal/terminals.d.ts +60 -7
- package/dist/sdk/terminal/terminals.js +598 -47
- package/dist/sdk/terminal/trajectory.js +2 -2
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +7 -1
- package/dist/sdk/terminal/tui/commandPages.js +57 -6
- package/dist/sdk/terminal/tui/commands.d.ts +10 -4
- package/dist/sdk/terminal/tui/commands.js +269 -65
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
- package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
- package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
- package/dist/sdk/terminal/tui/dialogs.js +9 -2
- package/dist/sdk/terminal/tui/list.d.ts +14 -1
- package/dist/sdk/terminal/tui/list.js +96 -14
- package/dist/sdk/terminal/tui/modelPicker.js +1 -6
- package/dist/sdk/terminal/tui/page.d.ts +3 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +27 -7
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
- package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
- 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/doctorPage.d.ts +2 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
- 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/pluginsPage.d.ts +0 -5
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +9 -3
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +24 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +152 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
- package/dist/sdk/terminal/tui/pages/usagePage.d.ts +43 -0
- package/dist/sdk/terminal/tui/pages/usagePage.js +370 -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 +1 -2
- package/dist/sdk/terminal/tui/piAdapter.js +3 -5
- package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
- package/dist/sdk/terminal/tui/planMode.js +1 -1
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +16 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +31 -0
- package/dist/sdk/terminal/tui/questions.d.ts +2 -0
- package/dist/sdk/terminal/tui/runtime.js +27 -5
- package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
- package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
- package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
- package/dist/sdk/terminal/tui/settingsModel.js +32 -2
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
- package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
- package/dist/sdk/terminal/tui/ui.d.ts +2 -0
- package/dist/sdk/terminal/tui/ui.js +1 -0
- package/dist/sdk/tools/askUser.js +26 -8
- package/dist/sdk/tools/checklist.js +3 -2
- package/dist/sdk/tools/checkpoint.d.ts +27 -0
- package/dist/sdk/tools/checkpoint.js +154 -0
- package/dist/sdk/tools/getCapabilities.d.ts +31 -0
- package/dist/sdk/tools/getCapabilities.js +34 -0
- package/dist/sdk/tools/harnessTools.d.ts +2 -0
- package/dist/sdk/tools/harnessTools.js +1 -1
- package/dist/sdk/tools/spawnSubagent.js +14 -4
- package/dist/sdk/usage/analytics.d.ts +20 -0
- package/dist/sdk/usage/analytics.js +151 -0
- package/dist/sdk/usage/ledger.d.ts +38 -0
- package/dist/sdk/usage/ledger.js +142 -0
- package/dist/sdk/usage/limits/adapters.d.ts +11 -0
- package/dist/sdk/usage/limits/adapters.js +349 -0
- package/dist/sdk/usage/limits/capture.d.ts +8 -0
- package/dist/sdk/usage/limits/capture.js +47 -0
- package/dist/sdk/usage/limits/http.d.ts +12 -0
- package/dist/sdk/usage/limits/http.js +51 -0
- package/dist/sdk/usage/limits/normalize.d.ts +15 -0
- package/dist/sdk/usage/limits/normalize.js +211 -0
- package/dist/sdk/usage/limits/service.d.ts +30 -0
- package/dist/sdk/usage/limits/service.js +176 -0
- package/dist/sdk/usage/limits/types.d.ts +65 -0
- package/dist/sdk/usage/limits/types.js +1 -0
- package/dist/sdk/usage/serverMethods.d.ts +3 -0
- package/dist/sdk/usage/serverMethods.js +22 -0
- package/dist/sdk/usage/service.d.ts +32 -0
- package/dist/sdk/usage/service.js +66 -0
- package/dist/sdk/usage/settings.d.ts +7 -0
- package/dist/sdk/usage/settings.js +28 -0
- package/dist/sdk/usage/types.d.ts +60 -0
- package/dist/sdk/usage/types.js +4 -0
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -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/cli.d.ts +2 -2
- package/dist/sdk/world/cli.js +6 -6
- package/dist/sdk/world/world.d.ts +3 -1
- package/dist/sdk/world/world.js +8 -9
- package/dist/terminal-opentui.js +16456 -5966
- package/dist/terminal-pi.js +1579 -573
- package/docs/README.md +22 -2
- package/docs/architecture/00-overview.md +28 -16
- package/docs/architecture/02-sessions-and-runs.md +184 -25
- package/docs/architecture/03-variants.md +10 -2
- package/docs/architecture/04-prompt.md +65 -7
- package/docs/architecture/06-plugins-and-capabilities.md +57 -4
- package/docs/architecture/07-tools-and-gate.md +112 -17
- package/docs/architecture/08-memory-and-history.md +4 -2
- package/docs/architecture/09-accounts-and-connectors.md +39 -1
- package/docs/architecture/10-server-protocol.md +168 -4
- package/docs/architecture/11-terminal.md +251 -8
- package/docs/architecture/12-storage.md +45 -3
- package/docs/architecture/13-world-tools.md +49 -6
- package/docs/architecture/14-surfaces-and-parity.md +136 -0
- package/docs/architecture/README.md +17 -4
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
- 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 +131 -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/PROP-0031-plugin-command-owner-namespaces.md +54 -0
- package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
- package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
- package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
- package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
- package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
- package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
- package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
- package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
- package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
- package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +247 -0
- package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
- package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
- package/docs/architecture/proposals/README.md +39 -0
- package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
- package/docs/decisions/ADR-0009-xharness-command.md +6 -5
- package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
- 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/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
- package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +33 -0
- package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
- package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
- package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
- package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
- package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
- package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
- package/docs/doctor.md +10 -4
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
- package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
- package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
- package/docs/features/README.md +26 -0
- package/docs/features/interfaces/README.md +1 -1
- package/docs/future-work.md +48 -0
- package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
- package/docs/parity.md +134 -0
- package/docs/releases/1.0.0.md +2 -2
- package/docs/releases/1.1.1.md +28 -0
- package/docs/releases/1.2.0.md +49 -0
- package/docs/releases/4.0.0.md +47 -0
- package/docs/releases/README.md +37 -27
- package/docs/study/00-index.md +8 -0
- package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/terminal-tui.md +386 -113
- package/package.json +6 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CapabilitySet } from "../capabilities/types.js";
|
|
2
|
+
import type { ToolSpec } from "./types.js";
|
|
3
|
+
export declare const GET_CAPABILITIES = "get_capabilities";
|
|
4
|
+
export declare const CAPABILITY_DISCOVERY: {
|
|
5
|
+
readonly name: "get_capabilities";
|
|
6
|
+
readonly label: "Inspect current capabilities";
|
|
7
|
+
readonly description: "List the tools callable in this run, or inspect one tool and its input schema. Availability is not permission: calls still follow the gate. Disabled, unavailable and unregistered tools are not advertised as callable.";
|
|
8
|
+
readonly parameters: {
|
|
9
|
+
readonly type: "object";
|
|
10
|
+
readonly additionalProperties: false;
|
|
11
|
+
readonly properties: {
|
|
12
|
+
readonly tool: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly minLength: 1;
|
|
15
|
+
readonly maxLength: 200;
|
|
16
|
+
readonly description: "An exact tool name to inspect. Omit to list callable tools.";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export interface RegisteredCapabilityTool {
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
parameters?: unknown;
|
|
25
|
+
}
|
|
26
|
+
export declare function describeCapabilities(input: {
|
|
27
|
+
capabilities: CapabilitySet;
|
|
28
|
+
active: readonly string[];
|
|
29
|
+
registered: readonly RegisteredCapabilityTool[];
|
|
30
|
+
}, name?: string): Record<string, unknown>;
|
|
31
|
+
export declare function getCapabilitiesTool(snapshot: () => Parameters<typeof describeCapabilities>[0]): ToolSpec;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const GET_CAPABILITIES = 'get_capabilities';
|
|
2
|
+
export const CAPABILITY_DISCOVERY = {
|
|
3
|
+
name: GET_CAPABILITIES,
|
|
4
|
+
label: 'Inspect current capabilities',
|
|
5
|
+
description: 'List the tools callable in this run, or inspect one tool and its input schema. Availability is not permission: calls still follow the gate. Disabled, unavailable and unregistered tools are not advertised as callable.',
|
|
6
|
+
parameters: {
|
|
7
|
+
type: 'object', additionalProperties: false,
|
|
8
|
+
properties: { tool: { type: 'string', minLength: 1, maxLength: 200, description: 'An exact tool name to inspect. Omit to list callable tools.' } }
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export function describeCapabilities(input, name) {
|
|
12
|
+
const allowed = new Set(input.capabilities.tools);
|
|
13
|
+
const active = new Set(input.active);
|
|
14
|
+
const tools = input.registered.filter(tool => allowed.has(tool.name) && active.has(tool.name));
|
|
15
|
+
if (name) {
|
|
16
|
+
const tool = tools.find(tool => tool.name === name);
|
|
17
|
+
return tool
|
|
18
|
+
? { tool: { name: tool.name, description: tool.description ?? '', ...(tool.parameters !== undefined ? { inputSchema: tool.parameters } : {}), state: 'callable', permission: 'checked per call' } }
|
|
19
|
+
: { tool: { name, state: 'unavailable', reason: 'This tool is not callable in the current run.' } };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
tools: tools.map(tool => ({ name: tool.name, description: tool.description ?? '', state: 'callable' })),
|
|
23
|
+
connectors: [...input.capabilities.connectors],
|
|
24
|
+
permission: 'Availability does not grant permission. Every call still passes the gate.'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function getCapabilitiesTool(snapshot) {
|
|
28
|
+
return {
|
|
29
|
+
...CAPABILITY_DISCOVERY,
|
|
30
|
+
readOnly: true,
|
|
31
|
+
parameters: Type => Type.Unsafe(CAPABILITY_DISCOVERY.parameters),
|
|
32
|
+
execute: async (input) => JSON.stringify(describeCapabilities(snapshot(), typeof input.tool === 'string' ? input.tool : undefined))
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CallerContext } from "../core/caller.js";
|
|
2
|
+
import type { HarnessToolDefinition } from "./definitions.js";
|
|
2
3
|
import { type DispatchEnv } from "./dispatch.js";
|
|
3
4
|
import type { ToolSpec } from "./types.js";
|
|
4
5
|
export declare function harnessTools(env: DispatchEnv, context: CallerContext, options?: {
|
|
5
6
|
variantDir?: string | null;
|
|
7
|
+
definitions?: readonly HarnessToolDefinition[];
|
|
6
8
|
}): ToolSpec[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describeTool, HARNESS_TOOLS } from "./definitions.js";
|
|
2
2
|
import { dispatch } from "./dispatch.js";
|
|
3
3
|
export function harnessTools(env, context, options = {}) {
|
|
4
|
-
return HARNESS_TOOLS.map((tool) => ({
|
|
4
|
+
return (options.definitions ?? HARNESS_TOOLS).map((tool) => ({
|
|
5
5
|
name: tool.name,
|
|
6
6
|
label: tool.label,
|
|
7
7
|
description: describeTool(tool, env.productName),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HarnessError, invalid } from "../core/errors.js";
|
|
2
|
-
import { SPAWN_SUBAGENT, WAIT_SUBAGENTS, SUBAGENT_RECEIPT } from "../sessions/subagents.js";
|
|
2
|
+
import { SPAWN_SUBAGENT, WAIT_SUBAGENTS, SUBAGENT_RECEIPT, MAX_SUBAGENT_TITLE_WORDS, subagentTitle } from "../sessions/subagents.js";
|
|
3
3
|
export { SPAWN_SUBAGENT, SUBAGENT_RELATION, DEFAULT_SUBAGENT_DEPTH, MAX_SUBAGENT_DEPTH, subagentDepth, subagentReport } from "../sessions/subagents.js";
|
|
4
4
|
function reported(parentRunId, results, spawned) {
|
|
5
5
|
const included = [];
|
|
@@ -27,10 +27,20 @@ export function subagentTools(service, parentRunId) {
|
|
|
27
27
|
return [{
|
|
28
28
|
name: SPAWN_SUBAGENT,
|
|
29
29
|
label: 'Start a subagent',
|
|
30
|
-
description: 'Start a fresh child run for independent work
|
|
31
|
-
|
|
30
|
+
description: 'Start a fresh child run for independent work. Delegate substantial, bounded tasks that benefit from a separate context, not simple lookups, tiny edits or tightly coupled steps. Prefer a named specialist profile; omit agent only when no profile fits. Plan mode permits only eligible read-only research profiles. Give a short title and a self-contained brief: the child has no conversation history and no more capabilities than you. Returns its run ID immediately by default so you can continue other useful work. Results arrive automatically before you finish. Use wait_subagents when you need results before proceeding, or wait: true for an inline result. Avoid overlapping write assignments. Stopping you stops your children.',
|
|
31
|
+
planModeSafe: input => service.planSpawnRefusal(parentRunId, input) === null,
|
|
32
|
+
parameters: (Type) => Type.Object({
|
|
33
|
+
title: Type.String({ description: `A short imperative title for the roster, at most ${MAX_SUBAGENT_TITLE_WORDS} words, such as "Map the auth flow"` }),
|
|
34
|
+
brief: Type.String({ description: 'A self-contained task: goal, relevant context or paths, non-goals, expected result, and acceptance or verification. The child cannot see this conversation.' }),
|
|
35
|
+
agent: Type.Optional(Type.String({ description: 'The name of a specialist profile from your Subagents list; omit only when no listed profile fits (omitting starts an unprofiled copy)' })),
|
|
36
|
+
tools: Type.Optional(Type.Array(Type.String())),
|
|
37
|
+
wait: Type.Optional(Type.Boolean({ description: 'Wait for the child and return its result; default false' }))
|
|
38
|
+
}),
|
|
32
39
|
execute: execute(async (input) => {
|
|
33
|
-
const
|
|
40
|
+
const title = subagentTitle(input.title);
|
|
41
|
+
if (!title)
|
|
42
|
+
throw invalid(`Give the subagent a title of at most ${MAX_SUBAGENT_TITLE_WORDS} words, with no control characters.`);
|
|
43
|
+
const child = await service.spawn({ parentRunId, brief: input.brief, title, agent: input.agent, tools: input.tools, wait: input.wait });
|
|
34
44
|
if (input.wait === true)
|
|
35
45
|
return reported(parentRunId, service.results({ parentRunId, runIds: [child.runId] }), [child.runId]);
|
|
36
46
|
return { text: JSON.stringify(child), details: { [SUBAGENT_RECEIPT]: { version: 1, ownerRunId: parentRunId, spawned: [child.runId] } } };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type PriceRates } from "../sessions/pricing.js";
|
|
2
|
+
import type { UsageFilter, UsagePeriod, UsageSummary } from "./types.js";
|
|
3
|
+
/** A price lookup, injected so tests never load Pi's catalogue. */
|
|
4
|
+
export type RatesLookup = (provider: string, model: string) => Promise<PriceRates | null>;
|
|
5
|
+
/** The local-day boundaries `period` covers, expressed as UTC instants. */
|
|
6
|
+
export declare function periodRange(period: UsagePeriod, offsetMinutes: number, now?: Date): {
|
|
7
|
+
from: string | null;
|
|
8
|
+
to: string;
|
|
9
|
+
};
|
|
10
|
+
export interface SummaryInput {
|
|
11
|
+
root: string;
|
|
12
|
+
filter: UsageFilter;
|
|
13
|
+
period: UsagePeriod;
|
|
14
|
+
timezoneOffsetMinutes: number;
|
|
15
|
+
/** Model labels are `provider/model`; only accounts need a lookup. */
|
|
16
|
+
accountLabel?: (accountId: string) => string;
|
|
17
|
+
rates?: RatesLookup;
|
|
18
|
+
now?: Date;
|
|
19
|
+
}
|
|
20
|
+
export declare function summarize(input: SummaryInput): Promise<UsageSummary>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { all, withIndex } from "../core/index.js";
|
|
2
|
+
import { costAt } from "../sessions/pricing.js";
|
|
3
|
+
import { lastIndexedAt } from "./ledger.js";
|
|
4
|
+
const ROW_LIMIT = 10;
|
|
5
|
+
const DAY_LIMIT = 400;
|
|
6
|
+
const UNPRICED_LIMIT = 40;
|
|
7
|
+
const zero = (value) => value ?? 0;
|
|
8
|
+
const round = (value, places = 6) => Math.round(value * 10 ** places) / 10 ** places;
|
|
9
|
+
function totals(row, estimated = 0) {
|
|
10
|
+
const input = zero(row?.input);
|
|
11
|
+
const cacheRead = zero(row?.cacheRead);
|
|
12
|
+
const cacheWrite = zero(row?.cacheWrite);
|
|
13
|
+
const cost = row?.cost == null && !estimated ? null : round(zero(row?.cost) + estimated);
|
|
14
|
+
const cached = cacheRead + input + cacheWrite;
|
|
15
|
+
return {
|
|
16
|
+
requests: zero(row?.requests),
|
|
17
|
+
inputTokens: input,
|
|
18
|
+
cacheReadTokens: cacheRead,
|
|
19
|
+
cacheWriteTokens: cacheWrite,
|
|
20
|
+
outputTokens: zero(row?.output),
|
|
21
|
+
reasoningTokens: zero(row?.reasoning),
|
|
22
|
+
costUsd: cost,
|
|
23
|
+
estimatedCostUsd: estimated ? round(estimated) : 0,
|
|
24
|
+
unpricedRequests: zero(row?.unpriced),
|
|
25
|
+
avgTtftMs: row?.ttft == null ? null : Math.round(row.ttft),
|
|
26
|
+
avgDurationMs: row?.duration == null ? null : Math.round(row.duration),
|
|
27
|
+
cacheHitPercent: cached ? Math.round((cacheRead / cached) * 1000) / 10 : null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** The local-day boundaries `period` covers, expressed as UTC instants. */
|
|
31
|
+
export function periodRange(period, offsetMinutes, now = new Date()) {
|
|
32
|
+
const to = now.toISOString();
|
|
33
|
+
if (period === 'all')
|
|
34
|
+
return { from: null, to };
|
|
35
|
+
const shifted = new Date(now.getTime() + offsetMinutes * 60_000);
|
|
36
|
+
const midnight = Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth(), shifted.getUTCDate());
|
|
37
|
+
const days = period === 'today' ? 0 : period === '7d' ? 6 : 29;
|
|
38
|
+
return { from: new Date(midnight - days * 86_400_000 - offsetMinutes * 60_000).toISOString(), to };
|
|
39
|
+
}
|
|
40
|
+
function where(filter, from, to) {
|
|
41
|
+
const clauses = ['at < ?'];
|
|
42
|
+
const params = [to];
|
|
43
|
+
if (from) {
|
|
44
|
+
clauses.push('at >= ?');
|
|
45
|
+
params.push(from);
|
|
46
|
+
}
|
|
47
|
+
if (filter.session) {
|
|
48
|
+
clauses.push('session_id = ?');
|
|
49
|
+
params.push(filter.session);
|
|
50
|
+
}
|
|
51
|
+
if (filter.account) {
|
|
52
|
+
clauses.push('account_id = ?');
|
|
53
|
+
params.push(filter.account);
|
|
54
|
+
}
|
|
55
|
+
if (filter.project) {
|
|
56
|
+
clauses.push('project_id = ?');
|
|
57
|
+
params.push(filter.project);
|
|
58
|
+
}
|
|
59
|
+
return { sql: clauses.join(' AND '), params };
|
|
60
|
+
}
|
|
61
|
+
const SUM = `COUNT(*) AS requests, SUM(input_tokens) AS input, SUM(cache_read_tokens) AS cacheRead,
|
|
62
|
+
SUM(cache_write_tokens) AS cacheWrite, SUM(output_tokens) AS output, SUM(reasoning_tokens) AS reasoning,
|
|
63
|
+
SUM(cost_usd) AS cost, SUM(CASE WHEN cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced,
|
|
64
|
+
AVG(ttft_ms) AS ttft, AVG(duration_ms) AS duration`;
|
|
65
|
+
function grouped(database, column, clause, params, limit) {
|
|
66
|
+
return all(database, `SELECT ${column} AS key, ${SUM} FROM usage_requests WHERE ${clause}
|
|
67
|
+
GROUP BY ${column} ORDER BY SUM(COALESCE(input_tokens,0)+COALESCE(cache_read_tokens,0)+COALESCE(cache_write_tokens,0)+COALESCE(output_tokens,0)) DESC
|
|
68
|
+
LIMIT ${limit}`, ...params).map((row) => ({ key: row.key ?? '', values: row }));
|
|
69
|
+
}
|
|
70
|
+
function modelKey(provider, model) {
|
|
71
|
+
return `${provider ?? 'unknown'}/${model ?? 'unknown'}`;
|
|
72
|
+
}
|
|
73
|
+
/** Estimates the cost of the requests a provider did not price, grouped by model. */
|
|
74
|
+
async function estimate(database, clause, params, rates) {
|
|
75
|
+
const rows = all(database, `SELECT provider, model, ${SUM} FROM usage_requests
|
|
76
|
+
WHERE ${clause} AND cost_usd IS NULL GROUP BY provider, model LIMIT ${UNPRICED_LIMIT}`, ...params);
|
|
77
|
+
const estimates = new Map();
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
if (!row.model)
|
|
80
|
+
continue;
|
|
81
|
+
const table = await rates(row.provider ?? '', row.model).catch(() => null);
|
|
82
|
+
if (!table)
|
|
83
|
+
continue;
|
|
84
|
+
const cost = costAt(table, {
|
|
85
|
+
inputTokens: zero(row.input),
|
|
86
|
+
outputTokens: zero(row.output),
|
|
87
|
+
cacheReadTokens: zero(row.cacheRead),
|
|
88
|
+
cacheWriteTokens: zero(row.cacheWrite)
|
|
89
|
+
});
|
|
90
|
+
const key = modelKey(row.provider, row.model);
|
|
91
|
+
const previous = estimates.get(key) ?? { cost: 0, requests: 0 };
|
|
92
|
+
estimates.set(key, { cost: previous.cost + cost, requests: previous.requests + zero(row.requests) });
|
|
93
|
+
}
|
|
94
|
+
return estimates;
|
|
95
|
+
}
|
|
96
|
+
export async function summarize(input) {
|
|
97
|
+
const offset = Number.isFinite(input.timezoneOffsetMinutes) ? Math.max(-840, Math.min(840, Math.trunc(input.timezoneOffsetMinutes))) : 0;
|
|
98
|
+
const { from, to } = periodRange(input.period, offset, input.now ?? new Date());
|
|
99
|
+
const { sql: clause, params } = where(input.filter, from, to);
|
|
100
|
+
const shift = `${offset >= 0 ? '+' : ''}${offset} minutes`;
|
|
101
|
+
const label = input.accountLabel ?? ((id) => id.slice(0, 8));
|
|
102
|
+
const summary = withIndex(input.root, (database) => {
|
|
103
|
+
const total = all(database, `SELECT ${SUM} FROM usage_requests WHERE ${clause}`, ...params)[0];
|
|
104
|
+
const days = all(database, `SELECT date(at, ?) AS day, ${SUM} FROM usage_requests WHERE ${clause}
|
|
105
|
+
GROUP BY day ORDER BY day DESC LIMIT ${DAY_LIMIT}`, shift, ...params);
|
|
106
|
+
return {
|
|
107
|
+
total,
|
|
108
|
+
days: days.reverse(),
|
|
109
|
+
byModel: grouped(database, "COALESCE(provider,'unknown') || '/' || COALESCE(model,'unknown')", clause, params, ROW_LIMIT + 1),
|
|
110
|
+
byProvider: grouped(database, "COALESCE(provider,'unknown')", clause, params, ROW_LIMIT + 1),
|
|
111
|
+
byAccount: grouped(database, "COALESCE(account_id,'')", clause, params, ROW_LIMIT + 1),
|
|
112
|
+
bySession: grouped(database, 'session_id', clause, params, ROW_LIMIT + 1)
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
const estimates = input.rates && zero(summary.total?.unpriced)
|
|
116
|
+
? await withIndex(input.root, (database) => estimate(database, clause, params, input.rates))
|
|
117
|
+
: new Map();
|
|
118
|
+
const estimated = [...estimates.values()].reduce((sum, entry) => sum + entry.cost, 0);
|
|
119
|
+
const row = (key, values, labelText, detail) => {
|
|
120
|
+
const estimateEntry = estimates.get(key);
|
|
121
|
+
return { key, label: labelText, detail, ...totals(values, estimateEntry?.cost ?? 0) };
|
|
122
|
+
};
|
|
123
|
+
const modelRows = summary.byModel.map((entry) => row(entry.key, entry.values, entry.key, null));
|
|
124
|
+
const truncate = (entries) => entries.length > ROW_LIMIT;
|
|
125
|
+
const accountRows = summary.byAccount.slice(0, ROW_LIMIT).map((entry) => row(entry.key, entry.values, entry.key ? label(entry.key) : 'No account', null));
|
|
126
|
+
const sessionRows = summary.bySession.slice(0, ROW_LIMIT).map((entry) => row(entry.key, entry.values, entry.key.slice(0, 12), null));
|
|
127
|
+
const days = summary.days.map((entry) => ({
|
|
128
|
+
day: entry.day,
|
|
129
|
+
requests: zero(entry.requests),
|
|
130
|
+
inputTokens: zero(entry.input),
|
|
131
|
+
cacheReadTokens: zero(entry.cacheRead),
|
|
132
|
+
outputTokens: zero(entry.output),
|
|
133
|
+
costUsd: entry.cost == null ? null : round(entry.cost)
|
|
134
|
+
}));
|
|
135
|
+
return {
|
|
136
|
+
scope: input.filter.session ? 'session' : input.filter.project ? 'project' : input.filter.account ? 'account' : 'all',
|
|
137
|
+
period: input.period,
|
|
138
|
+
from,
|
|
139
|
+
to,
|
|
140
|
+
timezoneOffsetMinutes: offset,
|
|
141
|
+
generatedAt: new Date().toISOString(),
|
|
142
|
+
totals: totals(summary.total, estimated),
|
|
143
|
+
byDay: days,
|
|
144
|
+
byModel: modelRows.slice(0, ROW_LIMIT),
|
|
145
|
+
byProvider: summary.byProvider.slice(0, ROW_LIMIT).map((entry) => row(entry.key, entry.values, entry.key, null)),
|
|
146
|
+
byAccount: accountRows,
|
|
147
|
+
bySession: sessionRows,
|
|
148
|
+
truncated: truncate(summary.byModel) || truncate(summary.byProvider) || truncate(summary.byAccount) || truncate(summary.bySession),
|
|
149
|
+
lastIndexedAt: lastIndexedAt(input.root)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type RunEvent, type RunRecord } from "../sessions/contracts.js";
|
|
2
|
+
export interface UsageRow {
|
|
3
|
+
runId: string;
|
|
4
|
+
sequence: number;
|
|
5
|
+
requestIndex: number;
|
|
6
|
+
at: string;
|
|
7
|
+
day: string;
|
|
8
|
+
accountId: string | null;
|
|
9
|
+
projectId: string | null;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
agentId: string | null;
|
|
12
|
+
mode: string;
|
|
13
|
+
trigger: string | null;
|
|
14
|
+
provider: string | null;
|
|
15
|
+
model: string | null;
|
|
16
|
+
inputTokens: number;
|
|
17
|
+
cacheReadTokens: number;
|
|
18
|
+
cacheWriteTokens: number;
|
|
19
|
+
outputTokens: number;
|
|
20
|
+
reasoningTokens: number;
|
|
21
|
+
costUsd: number | null;
|
|
22
|
+
costSource: string | null;
|
|
23
|
+
contextTokens: number | null;
|
|
24
|
+
contextWindow: number | null;
|
|
25
|
+
durationMs: number | null;
|
|
26
|
+
ttftMs: number | null;
|
|
27
|
+
requests: number;
|
|
28
|
+
}
|
|
29
|
+
/** One row per request: the event's own `request`, or the run's totals when a provider reported only those. */
|
|
30
|
+
export declare function usageRowsFor(run: RunRecord, events: readonly RunEvent[]): UsageRow[];
|
|
31
|
+
/** Indexes one run's usage. Idempotent, and safe to call when the index is unavailable. */
|
|
32
|
+
export declare function indexRunUsage(root: string, runId: string): boolean;
|
|
33
|
+
/** Indexes every run the ledger has not seen yet. Returns how many runs it added. */
|
|
34
|
+
export declare function syncUsage(root: string): number;
|
|
35
|
+
/** The ledger's last write, or null when it holds nothing. */
|
|
36
|
+
export declare function lastIndexedAt(root: string): string | null;
|
|
37
|
+
/** Run ids the ledger knows, for diagnostics and tests. */
|
|
38
|
+
export declare function indexedRuns(root: string): string[];
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { all, run as execute, transaction, withIndex } from "../core/index.js";
|
|
2
|
+
import { now } from "../core/fs.js";
|
|
3
|
+
import { readAllRuns } from "../sessions/runIndex.js";
|
|
4
|
+
import { findRun, readEvents } from "../sessions/runStore.js";
|
|
5
|
+
import { usagePayloadSchema } from "../sessions/contracts.js";
|
|
6
|
+
const number = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : 0);
|
|
7
|
+
const optionalNumber = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : null);
|
|
8
|
+
const text = (value) => (typeof value === 'string' && value ? value : null);
|
|
9
|
+
/** One row per request: the event's own `request`, or the run's totals when a provider reported only those. */
|
|
10
|
+
export function usageRowsFor(run, events) {
|
|
11
|
+
const rows = [];
|
|
12
|
+
let lastTotals = null;
|
|
13
|
+
let lastTotalsAt = null;
|
|
14
|
+
for (const event of events) {
|
|
15
|
+
if (event.type !== 'usage.updated')
|
|
16
|
+
continue;
|
|
17
|
+
const parsed = usagePayloadSchema.safeParse(event.payload);
|
|
18
|
+
if (!parsed.success)
|
|
19
|
+
continue;
|
|
20
|
+
const payload = parsed.data;
|
|
21
|
+
lastTotals = payload;
|
|
22
|
+
lastTotalsAt = event.timestamp;
|
|
23
|
+
const request = payload.request;
|
|
24
|
+
// Without a per-request update the event's fields are cumulative; only the last one may count.
|
|
25
|
+
if (!request)
|
|
26
|
+
continue;
|
|
27
|
+
rows.push({
|
|
28
|
+
runId: run.id,
|
|
29
|
+
sequence: event.sequence,
|
|
30
|
+
requestIndex: request?.index ?? payload.requests ?? rows.length + 1,
|
|
31
|
+
at: event.timestamp,
|
|
32
|
+
day: event.timestamp.slice(0, 10),
|
|
33
|
+
accountId: run.accountId ?? null,
|
|
34
|
+
projectId: run.projectId ?? null,
|
|
35
|
+
sessionId: run.sessionId,
|
|
36
|
+
agentId: run.agentId ?? null,
|
|
37
|
+
mode: run.mode ?? 'chat',
|
|
38
|
+
trigger: run.trigger ?? null,
|
|
39
|
+
provider: request?.provider ?? payload.provider ?? null,
|
|
40
|
+
model: request?.model ?? payload.model ?? null,
|
|
41
|
+
inputTokens: number(request?.inputTokens ?? payload.inputTokens),
|
|
42
|
+
cacheReadTokens: number(request?.cacheReadTokens ?? payload.cacheReadTokens),
|
|
43
|
+
cacheWriteTokens: number(request?.cacheWriteTokens ?? payload.cacheWriteTokens),
|
|
44
|
+
outputTokens: number(request?.outputTokens ?? payload.outputTokens),
|
|
45
|
+
reasoningTokens: number(request?.reasoningTokens ?? payload.reasoningTokens),
|
|
46
|
+
costUsd: optionalNumber(request?.costUsd ?? payload.costUsd),
|
|
47
|
+
costSource: text(request?.costSource ?? payload.costSource),
|
|
48
|
+
contextTokens: optionalNumber(payload.contextTokens),
|
|
49
|
+
contextWindow: optionalNumber(payload.contextWindow),
|
|
50
|
+
durationMs: optionalNumber(request?.durationMs),
|
|
51
|
+
ttftMs: optionalNumber(request?.ttftMs),
|
|
52
|
+
requests: number(payload.requests)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// A run whose provider reported totals but no per-request update (an older record) still counts once.
|
|
56
|
+
if (!rows.length && lastTotals) {
|
|
57
|
+
rows.push({
|
|
58
|
+
runId: run.id, sequence: 0, requestIndex: lastTotals.requests ?? 1,
|
|
59
|
+
at: lastTotalsAt ?? run.endedAt ?? run.startedAt, day: (lastTotalsAt ?? run.endedAt ?? run.startedAt).slice(0, 10),
|
|
60
|
+
accountId: run.accountId ?? null, projectId: run.projectId ?? null, sessionId: run.sessionId,
|
|
61
|
+
agentId: run.agentId ?? null, mode: run.mode ?? 'chat', trigger: run.trigger ?? null,
|
|
62
|
+
provider: lastTotals.provider ?? null, model: lastTotals.model ?? null,
|
|
63
|
+
inputTokens: number(lastTotals.inputTokens), cacheReadTokens: number(lastTotals.cacheReadTokens),
|
|
64
|
+
cacheWriteTokens: number(lastTotals.cacheWriteTokens), outputTokens: number(lastTotals.outputTokens),
|
|
65
|
+
reasoningTokens: number(lastTotals.reasoningTokens), costUsd: optionalNumber(lastTotals.costUsd),
|
|
66
|
+
costSource: text(lastTotals.costSource), contextTokens: optionalNumber(lastTotals.contextTokens),
|
|
67
|
+
contextWindow: optionalNumber(lastTotals.contextWindow), durationMs: null, ttftMs: null,
|
|
68
|
+
requests: number(lastTotals.requests)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return rows;
|
|
72
|
+
}
|
|
73
|
+
function write(database, run, rows) {
|
|
74
|
+
execute(database, 'DELETE FROM usage_requests WHERE run_id = ?', run.id);
|
|
75
|
+
for (const row of rows) {
|
|
76
|
+
execute(database, `INSERT INTO usage_requests (run_id, sequence, request_index, at, day, account_id, project_id, session_id, agent_id, mode, trigger,
|
|
77
|
+
provider, model, input_tokens, cache_read_tokens, cache_write_tokens, output_tokens, reasoning_tokens,
|
|
78
|
+
cost_usd, cost_source, context_tokens, context_window, duration_ms, ttft_ms, requests)
|
|
79
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, row.runId, row.sequence, row.requestIndex, row.at, row.day, row.accountId, row.projectId, row.sessionId, row.agentId, row.mode, row.trigger, row.provider, row.model, row.inputTokens, row.cacheReadTokens, row.cacheWriteTokens, row.outputTokens, row.reasoningTokens, row.costUsd, row.costSource, row.contextTokens, row.contextWindow, row.durationMs, row.ttftMs, row.requests);
|
|
80
|
+
}
|
|
81
|
+
execute(database, 'INSERT OR REPLACE INTO usage_indexed (run_id, indexed_at, rows) VALUES (?, ?, ?)', run.id, now(), rows.length);
|
|
82
|
+
}
|
|
83
|
+
/** Indexes one run's usage. Idempotent, and safe to call when the index is unavailable. */
|
|
84
|
+
export function indexRunUsage(root, runId) {
|
|
85
|
+
const run = findRun(root, runId);
|
|
86
|
+
if (!run)
|
|
87
|
+
return false;
|
|
88
|
+
let events;
|
|
89
|
+
try {
|
|
90
|
+
events = readEvents(root, runId);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const rows = usageRowsFor(run, events);
|
|
96
|
+
try {
|
|
97
|
+
withIndex(root, (database) => transaction(database, () => write(database, run, rows)));
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Indexes every run the ledger has not seen yet. Returns how many runs it added. */
|
|
105
|
+
export function syncUsage(root) {
|
|
106
|
+
const indexed = new Set();
|
|
107
|
+
try {
|
|
108
|
+
withIndex(root, (database) => {
|
|
109
|
+
for (const row of all(database, 'SELECT run_id FROM usage_indexed'))
|
|
110
|
+
indexed.add(row.run_id);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
let added = 0;
|
|
117
|
+
for (const record of readAllRuns(root)) {
|
|
118
|
+
if (indexed.has(record.id))
|
|
119
|
+
continue;
|
|
120
|
+
if (indexRunUsage(root, record.id))
|
|
121
|
+
added += 1;
|
|
122
|
+
}
|
|
123
|
+
return added;
|
|
124
|
+
}
|
|
125
|
+
/** The ledger's last write, or null when it holds nothing. */
|
|
126
|
+
export function lastIndexedAt(root) {
|
|
127
|
+
try {
|
|
128
|
+
return withIndex(root, (database) => all(database, 'SELECT MAX(indexed_at) AS at FROM usage_indexed')[0]?.at ?? null);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Run ids the ledger knows, for diagnostics and tests. */
|
|
135
|
+
export function indexedRuns(root) {
|
|
136
|
+
try {
|
|
137
|
+
return withIndex(root, (database) => all(database, 'SELECT run_id FROM usage_indexed').map((row) => row.run_id));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProviderLimitAdapter } from "./types.js";
|
|
2
|
+
export declare const OPENROUTER_ADAPTER: ProviderLimitAdapter;
|
|
3
|
+
export declare const DEEPSEEK_ADAPTER: ProviderLimitAdapter;
|
|
4
|
+
export declare const ANTHROPIC_ADMIN_ADAPTER: ProviderLimitAdapter;
|
|
5
|
+
/** ChatGPT/Codex plan windows and credits, from the account's own usage endpoint. */
|
|
6
|
+
export declare const CODEX_ADAPTER: ProviderLimitAdapter;
|
|
7
|
+
/** OpenCode Go's 5-hour, weekly and monthly subscription windows. */
|
|
8
|
+
export declare const OPENCODE_GO_ADAPTER: ProviderLimitAdapter;
|
|
9
|
+
/** The documented adapter for a provider, when one exists. Headers remain the other source. */
|
|
10
|
+
export declare function adapterFor(provider: string): ProviderLimitAdapter | null;
|
|
11
|
+
export declare const DOCUMENTED_ADAPTERS: readonly ProviderLimitAdapter[];
|