@xingular/harness 1.1.1 → 1.3.1-unstable.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 +48 -0
- package/README.md +36 -28
- package/dist/cli.js +10837 -6159
- 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.js +18 -4
- package/dist/sdk/agents/planResearch.d.ts +27 -0
- package/dist/sdk/agents/planResearch.js +82 -0
- package/dist/sdk/agents/profiles.d.ts +5 -1
- package/dist/sdk/agents/profiles.js +21 -5
- package/dist/sdk/annotations/pageScript.js +349 -48
- package/dist/sdk/annotations/part.js +34 -23
- package/dist/sdk/annotations/store.d.ts +7 -0
- package/dist/sdk/annotations/store.js +13 -5
- 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/tools.d.ts +1 -4
- package/dist/sdk/browser/tools.js +14 -91
- package/dist/sdk/capabilities/runSnapshot.js +10 -4
- package/dist/sdk/cli.js +5 -4
- package/dist/sdk/commands/list.d.ts +3 -0
- package/dist/sdk/commands/list.js +75 -0
- package/dist/sdk/commands/registry.d.ts +258 -0
- package/dist/sdk/commands/registry.js +48 -0
- package/dist/sdk/commands/routes.d.ts +16 -0
- package/dist/sdk/commands/routes.js +33 -0
- package/dist/sdk/core/commandBudget.d.ts +23 -0
- package/dist/sdk/core/commandBudget.js +57 -0
- 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/shell.d.ts +20 -0
- package/dist/sdk/core/shell.js +73 -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 +20 -0
- package/dist/sdk/core/workspaceBashTool.js +50 -0
- package/dist/sdk/core/workspaceSandbox.d.ts +41 -0
- package/dist/sdk/core/workspaceSandbox.js +360 -0
- package/dist/sdk/core/workspaceWriteOperations.d.ts +33 -0
- package/dist/sdk/core/workspaceWriteOperations.js +224 -0
- package/dist/sdk/doctor/checks.js +71 -12
- package/dist/sdk/doctor/types.d.ts +2 -2
- package/dist/sdk/harness.d.ts +7 -0
- package/dist/sdk/harness.js +42 -2
- package/dist/sdk/hub/hub.js +5 -3
- package/dist/sdk/index.d.ts +8 -3
- package/dist/sdk/index.js +4 -1
- package/dist/sdk/pi/runtime.d.ts +2 -1
- package/dist/sdk/pi/runtime.js +2 -2
- package/dist/sdk/plugins/builtin.js +32 -15
- package/dist/sdk/plugins/builtinRegistry.d.ts +10 -3
- package/dist/sdk/plugins/builtinRegistry.js +30 -8
- package/dist/sdk/plugins/catalogue.js +2 -1
- 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/install.d.ts +6 -2
- package/dist/sdk/plugins/install.js +29 -5
- package/dist/sdk/plugins/inventory.js +6 -5
- 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 +14 -0
- package/dist/sdk/prompt/compile.js +65 -31
- package/dist/sdk/prompt/standard.d.ts +17 -0
- package/dist/sdk/prompt/standard.js +21 -0
- package/dist/sdk/server/methods.js +22 -2
- 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 +7 -2
- package/dist/sdk/sessions/continuation.js +11 -6
- package/dist/sdk/sessions/contracts.d.ts +190 -4
- package/dist/sdk/sessions/contracts.js +71 -8
- 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 +14 -3
- package/dist/sdk/sessions/planMode.js +74 -17
- package/dist/sdk/sessions/registry.d.ts +1 -1
- package/dist/sdk/sessions/registry.js +2 -2
- 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 +161 -13
- package/dist/sdk/sessions/sessions.d.ts +23 -3
- package/dist/sdk/sessions/sessions.js +225 -22
- package/dist/sdk/sessions/subagentService.d.ts +24 -0
- package/dist/sdk/sessions/subagentService.js +225 -26
- package/dist/sdk/sessions/subagents.d.ts +10 -1
- package/dist/sdk/sessions/subagents.js +4 -0
- package/dist/sdk/sessions/workSlice.d.ts +106 -0
- package/dist/sdk/sessions/workSlice.js +329 -0
- package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
- package/dist/sdk/sessions/workspaceBoundary.js +46 -0
- package/dist/sdk/terminal/browserControl.js +2 -1
- package/dist/sdk/terminal/controller.d.ts +31 -3
- package/dist/sdk/terminal/controller.js +81 -29
- package/dist/sdk/terminal/opentui/ansi.d.ts +25 -0
- package/dist/sdk/terminal/opentui/ansi.js +192 -0
- package/dist/sdk/terminal/opentui/model.d.ts +163 -0
- package/dist/sdk/terminal/opentui/model.js +482 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +3 -3
- package/dist/sdk/terminal/opentui/subagentsWidget.js +7 -5
- package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
- package/dist/sdk/terminal/opentui/theme.js +88 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -1
- package/dist/sdk/terminal/piExtension.js +177 -63
- package/dist/sdk/terminal/standalone.d.ts +7 -0
- package/dist/sdk/terminal/standalone.js +72 -13
- package/dist/sdk/terminal/subagentMonitor.d.ts +28 -6
- package/dist/sdk/terminal/subagentMonitor.js +61 -9
- package/dist/sdk/terminal/terminals.d.ts +26 -2
- package/dist/sdk/terminal/terminals.js +325 -43
- package/dist/sdk/terminal/trajectory.d.ts +6 -1
- package/dist/sdk/terminal/trajectory.js +8 -0
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +1 -1
- package/dist/sdk/terminal/tui/annotateCommand.js +3 -3
- package/dist/sdk/terminal/tui/chrome.d.ts +4 -0
- package/dist/sdk/terminal/tui/chrome.js +13 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +3 -1
- package/dist/sdk/terminal/tui/commandPages.js +17 -4
- package/dist/sdk/terminal/tui/commands.d.ts +6 -0
- package/dist/sdk/terminal/tui/commands.js +213 -124
- package/dist/sdk/terminal/tui/list.d.ts +14 -1
- package/dist/sdk/terminal/tui/list.js +96 -14
- package/dist/sdk/terminal/tui/pageOverlay.js +21 -5
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +1 -1
- package/dist/sdk/terminal/tui/pages/browserPage.js +5 -4
- package/dist/sdk/terminal/tui/pages/hubPage.d.ts +14 -1
- package/dist/sdk/terminal/tui/pages/hubPage.js +192 -28
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +5 -5
- package/dist/sdk/terminal/tui/pages/notesPage.js +18 -18
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +8 -6
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +206 -28
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +1 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +104 -19
- 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/piAdapter.d.ts +1 -2
- package/dist/sdk/terminal/tui/piAdapter.js +3 -5
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +13 -9
- package/dist/sdk/terminal/tui/runtime.js +23 -3
- package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
- package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
- 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/sessionName.d.ts +2 -0
- package/dist/sdk/tools/sessionName.js +5 -3
- package/dist/sdk/tools/spawnSubagent.js +11 -8
- package/dist/sdk/tools/todo.d.ts +15 -0
- package/dist/sdk/tools/todo.js +64 -0
- 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/terminal-opentui.js +9204 -4812
- package/dist/terminal-pi.js +1848 -272
- package/docs/architecture/00-overview.md +1 -1
- package/docs/architecture/01-extension-points.md +1 -1
- package/docs/architecture/02-sessions-and-runs.md +169 -36
- package/docs/architecture/04-prompt.md +55 -15
- package/docs/architecture/06-plugins-and-capabilities.md +36 -15
- package/docs/architecture/07-tools-and-gate.md +131 -25
- package/docs/architecture/08-memory-and-history.md +4 -2
- package/docs/architecture/09-accounts-and-connectors.md +39 -3
- package/docs/architecture/10-server-protocol.md +71 -9
- package/docs/architecture/11-terminal.md +148 -60
- package/docs/architecture/12-storage.md +30 -2
- package/docs/architecture/13-world-tools.md +22 -1
- package/docs/architecture/14-surfaces-and-parity.md +58 -8
- package/docs/architecture/README.md +20 -17
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +2 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +6 -2
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +1 -1
- 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 +258 -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/PROP-0044-work-slice-defaults.md +93 -0
- package/docs/architecture/proposals/PROP-0045-rename-checklist-to-todo.md +132 -0
- package/docs/architecture/proposals/PROP-0046-progress-aware-stall-guards.md +82 -0
- package/docs/architecture/proposals/PROP-0047-plugins-hub-separation.md +122 -0
- package/docs/architecture/proposals/README.md +24 -6
- package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
- package/docs/decisions/ADR-0021-required-command-namespaces.md +1 -1
- 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/decisions/ADR-0039-progress-aware-stall-guards.md +39 -0
- package/docs/decisions/ADR-0040-architecture-change-workflow.md +31 -0
- package/docs/decisions/ADR-0041-plugins-hub-separation.md +41 -0
- package/docs/decisions/ADR-0042-windows-shell-without-sandbox.md +40 -0
- package/docs/decisions/ADR-0043-parents-continue-stopped-subagents.md +49 -0
- package/docs/doctor.md +14 -1
- 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/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
- package/docs/parity.md +83 -27
- package/docs/releases/1.1.1.md +28 -0
- package/docs/releases/1.2.0.md +49 -0
- package/docs/releases/1.3.0.md +21 -0
- package/docs/releases/4.0.0.md +10 -9
- package/docs/releases/README.md +35 -30
- package/docs/study/00-index.md +3 -0
- package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
- package/docs/terminal-tui.md +162 -82
- package/package.json +4 -3
- package/dist/sdk/tools/checklist.d.ts +0 -9
- package/dist/sdk/tools/checklist.js +0 -54
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { accessSync, constants, existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
-
import { delimiter, isAbsolute, join } from 'node:path';
|
|
3
|
+
import { basename, delimiter, isAbsolute, join } from 'node:path';
|
|
4
|
+
import { hostShell, shellLabel } from "../core/shell.js";
|
|
5
|
+
import { createWorkspaceBashOperations, workspaceSandboxBackend } from "../core/workspaceSandbox.js";
|
|
6
|
+
import { resolveWorkspaceRoot } from "../core/workspace.js";
|
|
3
7
|
import { defaultAssets } from "../terminal/terminals.js";
|
|
4
8
|
import { loadPi, PINNED_PI_VERSION } from "../pi/runtime.js";
|
|
5
9
|
import { list as listPlugins } from "../plugins/api.js";
|
|
@@ -12,18 +16,23 @@ import { BRAVE, TAVILY, EXA } from "../web/search.js";
|
|
|
12
16
|
import { KEYLESS_SEARCH, WEB_PLUGIN } from "../web/tools.js";
|
|
13
17
|
const pass = (evidence) => ({ status: 'pass', evidence });
|
|
14
18
|
const fail = (evidence, remedy) => ({ status: 'fail', evidence, remedy });
|
|
15
|
-
function
|
|
19
|
+
function resolvedExecutable(name) {
|
|
16
20
|
const paths = isAbsolute(name) ? [name] : (process.env.PATH ?? '').split(delimiter).filter(Boolean).map(dir => join(dir, name));
|
|
17
|
-
|
|
21
|
+
for (const path of paths) {
|
|
18
22
|
try {
|
|
19
23
|
accessSync(path, constants.X_OK);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
catch {
|
|
23
|
-
return false;
|
|
24
|
+
if (statSync(path).isFile())
|
|
25
|
+
return path;
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
catch { /* try the next PATH entry */ }
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
26
30
|
}
|
|
31
|
+
function executable(name) {
|
|
32
|
+
return resolvedExecutable(name) !== null;
|
|
33
|
+
}
|
|
34
|
+
/** One command written by the harness, not the model: it prints a fixed word and touches nothing. */
|
|
35
|
+
const BASH_PROBE_COMMAND = `printf '%s\\n' harness-doctor`;
|
|
27
36
|
export async function builtinChecks(harness, context, secrets, assets = defaultAssets()) {
|
|
28
37
|
const check = (id, category, title, local) => ({ id, category, title, local });
|
|
29
38
|
const account = () => context.accountId ? harness.accounts.find(context.accountId) : null;
|
|
@@ -96,10 +105,60 @@ export async function builtinChecks(harness, context, secrets, assets = defaultA
|
|
|
96
105
|
}), tools: ['read', 'grep', 'find', 'ls', 'write', 'edit']
|
|
97
106
|
},
|
|
98
107
|
{
|
|
99
|
-
...check('native.bash', 'tools', 'Native shell tool', async () =>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
...check('native.bash', 'tools', 'Native shell tool', async () => {
|
|
109
|
+
if (process.platform === 'win32') {
|
|
110
|
+
const shell = hostShell();
|
|
111
|
+
if (!shell)
|
|
112
|
+
return fail('Neither Git Bash nor PowerShell was found.', 'Install Git for Windows, or restore Windows PowerShell.');
|
|
113
|
+
return { status: 'warning', evidence: `Commands run in ${shellLabel(shell)} at ${shell.executable}. Windows has no workspace write-boundary sandbox, so each command asks for approval unless Full Permission is on, and plan mode cannot run commands. No shell command was run.`, remedy: 'Approve each command when asked, or turn on Full Permission for this session.' };
|
|
114
|
+
}
|
|
115
|
+
const shell = resolvedExecutable('bash');
|
|
116
|
+
if (!shell)
|
|
117
|
+
return fail('Bash was not found on the inherited PATH.', 'Verify the shell executable used by Pi.');
|
|
118
|
+
// Bash runs inside the platform's write-boundary sandbox; without that backend every call
|
|
119
|
+
// fails before the command starts, so presence of bash alone is not readiness.
|
|
120
|
+
const backend = workspaceSandboxBackend(process.platform);
|
|
121
|
+
return backend
|
|
122
|
+
? pass(`Bash is executable at ${shell}, and the ${basename(backend)} workspace write-boundary sandbox it runs inside is present. No shell command was run.`)
|
|
123
|
+
: fail(`Bash is executable at ${shell}, but the workspace write-boundary sandbox this platform needs is missing, so every Bash call fails before it runs.`, process.platform === 'linux' ? 'Install bubblewrap (bwrap) so Bash can run inside the workspace boundary.' : 'Restore the platform sandbox executable so Bash can run inside the workspace boundary.');
|
|
124
|
+
}),
|
|
125
|
+
tools: ['bash'],
|
|
126
|
+
live: {
|
|
127
|
+
notice: 'Run one fixed command written by the harness (printf) in the working folder, inside the same workspace write-boundary sandbox as a normal Bash call. No model input, no network request, no file written; only the exit status is reported.',
|
|
128
|
+
run: async (probe) => {
|
|
129
|
+
if (process.platform === 'win32')
|
|
130
|
+
return { status: 'unverified', evidence: 'On Windows every command needs a person to approve it, so the diagnostic does not run one.' };
|
|
131
|
+
const refusal = await harness.sessions.diagnosticGate(probe.run, context.caller).check('bash', { command: BASH_PROBE_COMMAND });
|
|
132
|
+
if (refusal)
|
|
133
|
+
return { status: 'blocked', evidence: `The tool policy refused the diagnostic command: ${refusal}`, remedy: 'Review what the policy allows Bash to do in this session.' };
|
|
134
|
+
const toolCallId = randomUUID();
|
|
135
|
+
probe.run.emit('tool.started', { toolCallId, name: 'bash', summary: 'Synthetic diagnostic command' });
|
|
136
|
+
let output = '';
|
|
137
|
+
try {
|
|
138
|
+
// The same root, boundary, hard-link scan and sandbox a real Bash call takes. A
|
|
139
|
+
// diagnostic never widens that boundary, so Full Permission cannot change this result.
|
|
140
|
+
const workspaceRoot = resolveWorkspaceRoot(context.cwd);
|
|
141
|
+
const { exitCode } = await createWorkspaceBashOperations(workspaceRoot).exec(BASH_PROBE_COMMAND, workspaceRoot, {
|
|
142
|
+
onData: chunk => { if (output.length < 400)
|
|
143
|
+
output += chunk.toString('utf8'); },
|
|
144
|
+
signal: probe.signal,
|
|
145
|
+
timeout: 20
|
|
146
|
+
});
|
|
147
|
+
probe.run.emit('tool.completed', { toolCallId, name: 'bash', ok: exitCode === 0 });
|
|
148
|
+
return exitCode === 0 && output.includes('harness-doctor')
|
|
149
|
+
? pass('A real command ran in the working folder through the workspace sandbox and returned exit status 0 with the expected output.')
|
|
150
|
+
: fail(`The shell returned exit status ${exitCode ?? 'unknown'} for the diagnostic command.`, 'Review the shell executable, the workspace write-boundary sandbox and the working folder permissions.');
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
probe.run.emit('tool.completed', { toolCallId, name: 'bash', ok: false });
|
|
154
|
+
return {
|
|
155
|
+
status: 'blocked',
|
|
156
|
+
evidence: `The command could not start in this working folder, so the shell itself was not exercised: ${error instanceof Error ? error.message : String(error)}`,
|
|
157
|
+
remedy: 'Resolve what stops Bash before it runs (a hard-link alias in the workspace, a missing sandbox backend, or a redirected workspace root), or choose another working folder.'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
103
162
|
},
|
|
104
163
|
check('runtime.storage', 'runtime', 'State and data storage', async () => {
|
|
105
164
|
accessSync(harness.stateDir, constants.R_OK | constants.W_OK);
|
|
@@ -11,9 +11,9 @@ export declare const findingSchema: z.ZodObject<{
|
|
|
11
11
|
status: z.ZodEnum<{
|
|
12
12
|
disabled: "disabled";
|
|
13
13
|
warning: "warning";
|
|
14
|
+
blocked: "blocked";
|
|
14
15
|
pass: "pass";
|
|
15
16
|
fail: "fail";
|
|
16
|
-
blocked: "blocked";
|
|
17
17
|
unverified: "unverified";
|
|
18
18
|
}>;
|
|
19
19
|
verification: z.ZodEnum<{
|
|
@@ -119,9 +119,9 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
119
119
|
status: z.ZodEnum<{
|
|
120
120
|
disabled: "disabled";
|
|
121
121
|
warning: "warning";
|
|
122
|
+
blocked: "blocked";
|
|
122
123
|
pass: "pass";
|
|
123
124
|
fail: "fail";
|
|
124
|
-
blocked: "blocked";
|
|
125
125
|
unverified: "unverified";
|
|
126
126
|
}>;
|
|
127
127
|
verification: z.ZodEnum<{
|
package/dist/sdk/harness.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ import { Sessions } from "./sessions/sessions.js";
|
|
|
24
24
|
import { SideQuestions } from "./sessions/sideQuestions.js";
|
|
25
25
|
import { Terminals, type TerminalAssets } from "./terminal/terminals.js";
|
|
26
26
|
import { Worktrees } from "./worktrees/service.js";
|
|
27
|
+
import { Usage } from "./usage/service.js";
|
|
28
|
+
import { UsageLimits } from "./usage/limits/service.js";
|
|
27
29
|
import { AnnotationCapture } from "./annotations/capture.js";
|
|
28
30
|
import { NoteDeliveries } from "./annotations/delivery.js";
|
|
29
31
|
import type { WorktreesOptions } from "./worktrees/types.js";
|
|
@@ -89,6 +91,8 @@ declare class Env implements HarnessEnv {
|
|
|
89
91
|
get root(): string;
|
|
90
92
|
}
|
|
91
93
|
export declare class Harness {
|
|
94
|
+
/** The periodic liveness check, started once with recovery (PROP-0041). */
|
|
95
|
+
private staleRuns?;
|
|
92
96
|
private readonly browserEnabled;
|
|
93
97
|
readonly hub: Hub;
|
|
94
98
|
readonly bus: Bus;
|
|
@@ -120,6 +124,9 @@ export declare class Harness {
|
|
|
120
124
|
refineHost: RefineHost | null;
|
|
121
125
|
readonly terminals: Terminals;
|
|
122
126
|
readonly doctor: Doctor;
|
|
127
|
+
/** Usage analytics over the derived ledger, and the provider limits last reported. */
|
|
128
|
+
readonly usage: Usage;
|
|
129
|
+
readonly usageLimits: UsageLimits;
|
|
123
130
|
/** Managed git worktrees sessions can work in (PROP-0016). */
|
|
124
131
|
readonly worktrees: Worktrees;
|
|
125
132
|
/** The terminal's visible browser for capturing page notes (PROP-0017). */
|
package/dist/sdk/harness.js
CHANGED
|
@@ -36,11 +36,14 @@ import { Hooks } from "./plugins/hooks.js";
|
|
|
36
36
|
import { packageFolders, Plugins } from "./plugins/plugins.js";
|
|
37
37
|
import { approvalKey, Approvals } from "./sessions/approvals.js";
|
|
38
38
|
import { RunRegistry } from "./sessions/registry.js";
|
|
39
|
-
import { loadRun, markInterruptedRuns } from "./sessions/runStore.js";
|
|
39
|
+
import { loadRun, markInterruptedRuns, reconcileStaleRuns } from "./sessions/runStore.js";
|
|
40
40
|
import { Sessions } from "./sessions/sessions.js";
|
|
41
41
|
import { SideQuestions } from "./sessions/sideQuestions.js";
|
|
42
42
|
import { Terminals } from "./terminal/terminals.js";
|
|
43
43
|
import { Worktrees } from "./worktrees/service.js";
|
|
44
|
+
import { Usage } from "./usage/service.js";
|
|
45
|
+
import { UsageLimits } from "./usage/limits/service.js";
|
|
46
|
+
import { indexRunUsage, syncUsage } from "./usage/ledger.js";
|
|
44
47
|
import { AnnotationCapture } from "./annotations/capture.js";
|
|
45
48
|
import { NoteDeliveries } from "./annotations/delivery.js";
|
|
46
49
|
class Env {
|
|
@@ -61,6 +64,8 @@ class Env {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
export class Harness {
|
|
67
|
+
/** The periodic liveness check, started once with recovery (PROP-0041). */
|
|
68
|
+
staleRuns;
|
|
64
69
|
browserEnabled;
|
|
65
70
|
hub;
|
|
66
71
|
bus = new Bus();
|
|
@@ -92,6 +97,9 @@ export class Harness {
|
|
|
92
97
|
refineHost;
|
|
93
98
|
terminals;
|
|
94
99
|
doctor;
|
|
100
|
+
/** Usage analytics over the derived ledger, and the provider limits last reported. */
|
|
101
|
+
usage;
|
|
102
|
+
usageLimits;
|
|
95
103
|
/** Managed git worktrees sessions can work in (PROP-0016). */
|
|
96
104
|
worktrees;
|
|
97
105
|
/** The terminal's visible browser for capturing page notes (PROP-0017). */
|
|
@@ -116,6 +124,10 @@ export class Harness {
|
|
|
116
124
|
const secrets = options.secrets ?? fileSecretStore(join(this.stateDir, 'secrets.json'));
|
|
117
125
|
this.accounts = new Accounts({ dir: join(this.stateDir, 'accounts'), secrets, bus: this.bus });
|
|
118
126
|
this.approvals = new Approvals(this.stateDir, this.bus);
|
|
127
|
+
this.usageLimits = new UsageLimits({ accounts: this.accounts, root: () => this.env.dataRoot });
|
|
128
|
+
// Response headers are the free, always-fresh limit signal; the account runtime reports them here.
|
|
129
|
+
this.accounts.observeModels = (models, accountId) => this.usageLimits.observeModels(models, accountId);
|
|
130
|
+
this.usage = new Usage({ root: () => this.env.root, accounts: this.accounts });
|
|
119
131
|
this.runs = new RunRegistry({
|
|
120
132
|
root: () => this.env.root,
|
|
121
133
|
automatic: async (run, target, operation, standing) => {
|
|
@@ -171,6 +183,7 @@ export class Harness {
|
|
|
171
183
|
broker: this.apps,
|
|
172
184
|
plugins: this.plugins,
|
|
173
185
|
resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }),
|
|
186
|
+
fullPermission: (run) => this.approvals.isAuto(approvalKey(run)),
|
|
174
187
|
factory: options.sessionFactory,
|
|
175
188
|
trust: this.trust,
|
|
176
189
|
worktrees: this.worktrees
|
|
@@ -187,7 +200,7 @@ export class Harness {
|
|
|
187
200
|
this.refine = new Refinements({ root: () => this.env.root, bus: this.bus, trust: this.trust, host: () => this.refineHost, runtime: refineRuntime });
|
|
188
201
|
this.lessons = new Lessons({ root: () => this.env.root, runtime: refineRuntime });
|
|
189
202
|
this.pageNotes = new NoteDeliveries({ root: () => this.env.root, bus: this.bus, terminals: () => this.terminals ?? null });
|
|
190
|
-
this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, sideQuestions: this.sideQuestions, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params), install: params => pluginInstall.install(this, params), remove: params => pluginInstall.remove(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor, worktrees: this.worktrees, noteCapture: this.noteCapture, pageNotes: this.pageNotes });
|
|
203
|
+
this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, sideQuestions: this.sideQuestions, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, usage: this.usage, usageLimits: this.usageLimits, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params), install: params => pluginInstall.install(this, params), remove: params => pluginInstall.remove(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor, worktrees: this.worktrees, noteCapture: this.noteCapture, pageNotes: this.pageNotes });
|
|
191
204
|
this.ready = this.apps.start().catch((error) => log('connected apps', error instanceof Error ? error.message : String(error)));
|
|
192
205
|
this.runs.listen((event, run) => {
|
|
193
206
|
this.accounts.onRunEvent(event, run);
|
|
@@ -197,6 +210,10 @@ export class Harness {
|
|
|
197
210
|
if (run?.trigger === 'doctor')
|
|
198
211
|
return;
|
|
199
212
|
const runEnded = event.type === 'completed' || event.type === 'failed' || (event.type === 'status.changed' && event.payload.status === 'interrupted');
|
|
213
|
+
// The usage ledger is derived from the same recorded events; a failure never blocks the run.
|
|
214
|
+
// An interrupted run is indexed too, so its spent tokens are not lost until the next start.
|
|
215
|
+
if (runEnded)
|
|
216
|
+
indexRunUsage(this.env.root, event.runId);
|
|
200
217
|
if (runEnded && run?.sessionId) {
|
|
201
218
|
// Notes sent with "Proceed now" while this session was working go out now; the terminal
|
|
202
219
|
// needs a moment to see its run as ended.
|
|
@@ -294,18 +311,41 @@ export class Harness {
|
|
|
294
311
|
catch (error) {
|
|
295
312
|
log('recall', error instanceof Error ? error.message : String(error));
|
|
296
313
|
}
|
|
314
|
+
try {
|
|
315
|
+
// Usage rows come from the same run events; the ledger can always be rebuilt from them.
|
|
316
|
+
syncUsage(root);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
log('usage ledger', error instanceof Error ? error.message : String(error));
|
|
320
|
+
}
|
|
297
321
|
this.recovery = { interruptedRuns: interruptedRunIds.length, interruptedRunIds, reindexedPages: safeCount(root), migrationError, legacyWiki };
|
|
322
|
+
if (!this.staleRuns) {
|
|
323
|
+
// A reply that stops emitting heartbeats while its process lives is settled here, not left
|
|
324
|
+
// `running` for the next restart (PROP-0041).
|
|
325
|
+
this.staleRuns = setInterval(() => {
|
|
326
|
+
try {
|
|
327
|
+
reconcileStaleRuns(root);
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
log('stale run check', error instanceof Error ? error.message : String(error));
|
|
331
|
+
}
|
|
332
|
+
}, 60_000);
|
|
333
|
+
this.staleRuns.unref?.();
|
|
334
|
+
}
|
|
298
335
|
return this.recovery;
|
|
299
336
|
}
|
|
300
337
|
/** Rebuilds the history index from the run records. Returns the notes in the Wiki. */
|
|
301
338
|
rebuildIndex() {
|
|
302
339
|
recall.sync(this.env.root);
|
|
340
|
+
syncUsage(this.env.root);
|
|
303
341
|
const count = safeCount(this.env.root);
|
|
304
342
|
this.recovery = { ...this.recovery, reindexedPages: count };
|
|
305
343
|
return count;
|
|
306
344
|
}
|
|
307
345
|
/** Stops live runs and refinements and releases the index. */
|
|
308
346
|
async close() {
|
|
347
|
+
if (this.staleRuns)
|
|
348
|
+
clearInterval(this.staleRuns);
|
|
309
349
|
await this.doctor.close();
|
|
310
350
|
this.hub.close();
|
|
311
351
|
await this.refine.close();
|
package/dist/sdk/hub/hub.js
CHANGED
|
@@ -316,7 +316,7 @@ export class Hub {
|
|
|
316
316
|
attachments.save(root, choicesBefore);
|
|
317
317
|
if (entry.delivery.kind === 'pi-package')
|
|
318
318
|
for (const pluginId of receipt.resourceIds)
|
|
319
|
-
await packages.
|
|
319
|
+
await packages.removeDirect(this.harness, { pluginId });
|
|
320
320
|
throw error;
|
|
321
321
|
}
|
|
322
322
|
this.harness.bus.emit({ type: 'hub.changed' });
|
|
@@ -404,8 +404,10 @@ export class Hub {
|
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
406
|
if (receipt.entry.delivery.kind === 'pi-package')
|
|
407
|
-
for (const pluginId of receipt.resourceIds)
|
|
408
|
-
await
|
|
407
|
+
for (const pluginId of receipt.resourceIds) {
|
|
408
|
+
if ((await pluginApi.list(this.harness, {})).some((plugin) => plugin.id === pluginId && plugin.install))
|
|
409
|
+
await packages.removeDirect(this.harness, { pluginId });
|
|
410
|
+
}
|
|
409
411
|
writeJson(join(root, 'hub', 'installed.json'), records.filter((item) => item !== receipt));
|
|
410
412
|
committed = true;
|
|
411
413
|
try {
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -16,17 +16,21 @@ export type { RunCapabilitySnapshot } from "./capabilities/runSnapshot.js";
|
|
|
16
16
|
export type { CallerContext } from "./core/caller.js";
|
|
17
17
|
export { dispatch as harnessCall } from "./tools/dispatch.js";
|
|
18
18
|
export { compile, render, SECTION_ORDER, CONTEXT_MESSAGE_TYPE, CONTEXT_SNAPSHOT_KEY, type ContextPack, type HostSection, type HostToolGroup, type Launch, type Reference, type Section } from "./prompt/compile.js";
|
|
19
|
-
export { runEventSchema, runRecordSchema, isActiveStatus, type
|
|
19
|
+
export { runEventSchema, runRecordSchema, isActiveStatus, type TodoItem, type RunEvent, type RunEventType, type RunRecord } from "./sessions/contracts.js";
|
|
20
20
|
export type { ApprovalFacts, ApprovalPolicy, AutomaticReason } from "./sessions/approvals.js";
|
|
21
21
|
export type { ScopeDecision, ToolCall, ToolPolicy } from "./sessions/policy.js";
|
|
22
22
|
export type { ActiveRun, ApprovalDecision, ApprovalInput, QuestionAnswer, QuestionInput } from "./sessions/registry.js";
|
|
23
23
|
export type { RunFields, SessionRunInput } from "./sessions/sessions.js";
|
|
24
|
+
export type { SessionCommand } from "./sessions/runner.js";
|
|
25
|
+
export type { SessionRef, SessionTree, SessionTreeEntry } from "./sessions/control.js";
|
|
26
|
+
export { CORE_COMMANDS, coreCommand, type CoreCommand, type CoreCommandGroup, type CoreCommandKind, type CoreCommandName, type ListedCommand } from "./commands/registry.js";
|
|
27
|
+
export { STANDARD_CONNECTIONS, type StandardSession } from "./prompt/standard.js";
|
|
24
28
|
export { SideQuestions, SIDE_QUESTION_INSTRUCTION, buildSideQuestionText, type SideQuestionContext, type SideQuestionRead, type SideQuestionStartInput } from "./sessions/sideQuestions.js";
|
|
25
29
|
export type { Effort } from "./sessions/runner.js";
|
|
26
30
|
export { describeTool, HARNESS_TOOLS, DEFAULT_TOOL_NAMES, WIKI_WRITE, type HarnessCall, type HarnessToolDefinition } from "./tools/definitions.js";
|
|
27
31
|
export type { ToolProvider, ToolResult, ToolSession, ToolSpec } from "./tools/types.js";
|
|
28
32
|
export { SPAWN_SUBAGENT, SUBAGENT_RELATION } from "./tools/spawnSubagent.js";
|
|
29
|
-
export { CHECKLIST_WRITE, checklistOf } from "./tools/
|
|
33
|
+
export { TODO_WRITE, todoOf, CHECKLIST_WRITE, checklistOf } from "./tools/todo.js";
|
|
30
34
|
export { BUILTIN_PROFILES } from "./agents/builtin.js";
|
|
31
35
|
export { PROFILE_COLORS, createProfile, listProfiles, parseProfile, profileDirs, removeProfile, resolveProfile, type AgentProfile, type ProfileColor, type ProfileListing, type ProfileProblem, type ProfileScope, type ProfileSource, type ProfileVariant } from "./agents/profiles.js";
|
|
32
36
|
export { continuationOf, type Continuation, type ContinuationBlock } from "./sessions/continuation.js";
|
|
@@ -56,7 +60,8 @@ export { HARNESS_PLUGINS } from "./plugins/builtin.js";
|
|
|
56
60
|
export { FIRST_PARTY_PLUGINS } from "./plugins/firstParty.js";
|
|
57
61
|
export type { Found, Resolver } from "./plugins/discover.js";
|
|
58
62
|
export type { Loading, Registered } from "./plugins/plugins.js";
|
|
59
|
-
export { CONTRIBUTION_KINDS, PLUGIN_SOURCES, type ConnectorTemplate, type Contribution, type ContributionKind, type ContributionState, type Delivery, type Inventory, type InventoryItem, type Plugin, type PluginDescriptor, type PluginManage, type PluginSetting, type PluginSource } from "./plugins/types.js";
|
|
63
|
+
export { CONTRIBUTION_KINDS, PLUGIN_SOURCES, type ConnectorTemplate, type Contribution, type ContributionKind, type ContributionState, type Delivery, type Inventory, type InventoryItem, type Plugin, type PluginDescriptor, type PluginManage, type PluginSetting, type PluginSource, type PluginSurfaces } from "./plugins/types.js";
|
|
64
|
+
export { describePlugin, surfaceBadge } from "./plugins/surfaces.js";
|
|
60
65
|
export { methods } from "./server/methods.js";
|
|
61
66
|
export { remoteDirectory, remotePolicy, remoteSecrets, remoteTools } from "./server/remote.js";
|
|
62
67
|
export { PROTOCOL_VERSION, RpcPeer, stdioTransport, type Handler, type Transport } from "./server/rpc.js";
|
package/dist/sdk/index.js
CHANGED
|
@@ -20,10 +20,12 @@ export { fileSecretStore, memorySecretStore, secretKey } from "./accounts/secret
|
|
|
20
20
|
export { dispatch as harnessCall } from "./tools/dispatch.js";
|
|
21
21
|
export { compile, render, SECTION_ORDER, CONTEXT_MESSAGE_TYPE, CONTEXT_SNAPSHOT_KEY } from "./prompt/compile.js";
|
|
22
22
|
export { runEventSchema, runRecordSchema, isActiveStatus } from "./sessions/contracts.js";
|
|
23
|
+
export { CORE_COMMANDS, coreCommand } from "./commands/registry.js";
|
|
24
|
+
export { STANDARD_CONNECTIONS } from "./prompt/standard.js";
|
|
23
25
|
export { SideQuestions, SIDE_QUESTION_INSTRUCTION, buildSideQuestionText } from "./sessions/sideQuestions.js";
|
|
24
26
|
export { describeTool, HARNESS_TOOLS, DEFAULT_TOOL_NAMES, WIKI_WRITE } from "./tools/definitions.js";
|
|
25
27
|
export { SPAWN_SUBAGENT, SUBAGENT_RELATION } from "./tools/spawnSubagent.js";
|
|
26
|
-
export { CHECKLIST_WRITE, checklistOf } from "./tools/
|
|
28
|
+
export { TODO_WRITE, todoOf, CHECKLIST_WRITE, checklistOf } from "./tools/todo.js";
|
|
27
29
|
export { BUILTIN_PROFILES } from "./agents/builtin.js";
|
|
28
30
|
export { PROFILE_COLORS, createProfile, listProfiles, parseProfile, profileDirs, removeProfile, resolveProfile } from "./agents/profiles.js";
|
|
29
31
|
export { continuationOf } from "./sessions/continuation.js";
|
|
@@ -42,6 +44,7 @@ export { attach as attachPlugin, inventoryFor as pluginInventory, list as listPl
|
|
|
42
44
|
export { HARNESS_PLUGINS } from "./plugins/builtin.js";
|
|
43
45
|
export { FIRST_PARTY_PLUGINS } from "./plugins/firstParty.js";
|
|
44
46
|
export { CONTRIBUTION_KINDS, PLUGIN_SOURCES } from "./plugins/types.js";
|
|
47
|
+
export { describePlugin, surfaceBadge } from "./plugins/surfaces.js";
|
|
45
48
|
export { methods } from "./server/methods.js";
|
|
46
49
|
export { remoteDirectory, remotePolicy, remoteSecrets, remoteTools } from "./server/remote.js";
|
|
47
50
|
export { PROTOCOL_VERSION, RpcPeer, stdioTransport } from "./server/rpc.js";
|
package/dist/sdk/pi/runtime.d.ts
CHANGED
package/dist/sdk/pi/runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
|
-
export const PINNED_PI_VERSION = '1.
|
|
4
|
-
const HTTP_DISPATCHER_CHUNK = 'dist/bundle/chunks/chunk-
|
|
3
|
+
export const PINNED_PI_VERSION = '1.4.0';
|
|
4
|
+
export const HTTP_DISPATCHER_CHUNK = 'dist/bundle/chunks/chunk-MM3LKT73.js';
|
|
5
5
|
export function assertPiRuntimeVersion(version) {
|
|
6
6
|
if (version !== PINNED_PI_VERSION)
|
|
7
7
|
throw new Error(`The Harness Pi runtime requires Pi ${PINNED_PI_VERSION}; found ${version}. Install the Harness package's pinned dependencies.`);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Built-in feature modules register their contributions here; this registry never loads Pi code.
|
|
2
2
|
import { CONNECTION_TOOLS, NATIVE_GROUP } from "../prompt/compile.js";
|
|
3
3
|
import { ASK_USER, askUserTool } from "../tools/askUser.js";
|
|
4
|
-
import {
|
|
4
|
+
import { TODO_WRITE, todoTool } from "../tools/todo.js";
|
|
5
|
+
import { CHECKPOINT_WRITE, checkpointTool } from "../tools/checkpoint.js";
|
|
6
|
+
import { sliceOf } from "../sessions/workSlice.js";
|
|
5
7
|
import { SPAWN_SUBAGENT, WAIT_SUBAGENTS } from "../sessions/subagents.js";
|
|
6
8
|
import { subagentTools } from "../tools/spawnSubagent.js";
|
|
7
9
|
import { SESSION_NAME } from "../tools/sessionName.js";
|
|
@@ -25,6 +27,9 @@ import { subagentServerMethods } from "../sessions/subagentServerMethods.js";
|
|
|
25
27
|
import { sideQuestionServerMethods } from "../sessions/sideQuestionServerMethods.js";
|
|
26
28
|
import { connectedAppServerMethods } from "../mcp/serverMethods.js";
|
|
27
29
|
import { harnessTools } from "../tools/harnessTools.js";
|
|
30
|
+
import { GET_CAPABILITIES } from "../tools/getCapabilities.js";
|
|
31
|
+
import { USAGE_SETTINGS } from "../usage/settings.js";
|
|
32
|
+
import { usageServerMethods } from "../usage/serverMethods.js";
|
|
28
33
|
import { createBuiltinPluginRegistry } from "./builtinRegistry.js";
|
|
29
34
|
const tools = (definitions) => definitions.map((tool) => ({ name: tool.name, description: tool.label }));
|
|
30
35
|
const toolContribution = (definitions, toolOrder) => ({
|
|
@@ -74,7 +79,8 @@ const WIKI_PLUGIN = {
|
|
|
74
79
|
};
|
|
75
80
|
const HISTORY_PLUGIN = { id: 'history', title: 'History', description: 'Search earlier conversations and inspect recorded run trajectories.', required: 'Earlier runs are how an agent picks up where work stopped.', tools: tools([...RECALL_TOOLS, ...TRAJECTORY_TOOLS]), toolGroups: ['History'] };
|
|
76
81
|
const ASK_USER_PLUGIN = { id: 'ask-user', title: 'Ask the user', description: 'Lets the agent stop and ask a question with choices.', required: 'An agent must be able to stop and ask you.', tools: [{ name: ASK_USER }] };
|
|
77
|
-
const
|
|
82
|
+
const TODO_PLUGIN = { id: 'todo', title: 'Todo', description: 'Lets the agent keep a todo list of the steps of its work, recorded with the run so progress can be shown and picked up again.', required: 'Progress of longer work is kept through it.', tools: [{ name: TODO_WRITE, description: 'Write the todo list' }] };
|
|
83
|
+
const CHECKPOINT_PLUGIN = { id: 'checkpoint', title: 'Checkpoint', description: 'Lets a bounded reply record a handoff at a work-slice boundary so the next slice starts from it.', required: 'A reply that stops at its budget leaves the state the next one resumes from.', tools: [{ name: CHECKPOINT_WRITE, description: 'Write the handoff checkpoint' }] };
|
|
78
84
|
const CONNECTED_APPS_PLUGIN = {
|
|
79
85
|
id: 'connected-apps',
|
|
80
86
|
title: 'Connected apps',
|
|
@@ -88,6 +94,13 @@ const DOCTOR_PLUGIN = { id: 'doctor', title: 'Doctor', description: 'Local readi
|
|
|
88
94
|
const WORKTREES_PLUGIN = { id: 'worktrees', title: 'Worktrees', description: 'Create and manage isolated git worktrees, move changes between them, and restore removed worktrees.', required: 'Managed worktrees are available to every local terminal.' };
|
|
89
95
|
const HUB_PLUGIN = { id: 'hub', title: 'Hub', description: 'Browse and install reviewed skills, plugins and documents from configured registries.', required: 'Reviewed Harness resources are available to every host.' };
|
|
90
96
|
const REFINEMENT_PLUGIN = { id: 'refinement', title: 'Refinement', description: 'Improve variants from recorded evidence through isolated refinement runs and reviewed candidates.', required: 'Variant refinement is available to every host.' };
|
|
97
|
+
const CAPABILITIES_PLUGIN = { id: 'capabilities', title: 'Capability discovery', description: 'Inspect the callable tools of the current run, including their input schemas. Discovery never grants access.', required: 'Every interactive session can inspect its own registered capabilities.', tools: [{ name: GET_CAPABILITIES, description: 'Inspect current capabilities' }] };
|
|
98
|
+
const USAGE_PLUGIN = {
|
|
99
|
+
id: 'usage',
|
|
100
|
+
title: 'Usage',
|
|
101
|
+
description: 'Token, cost and latency totals from recorded runs, and the plan windows, balances or rate limits each provider reports.',
|
|
102
|
+
settings: [...USAGE_SETTINGS]
|
|
103
|
+
};
|
|
91
104
|
const SIDE_QUESTIONS_PLUGIN = {
|
|
92
105
|
id: 'side-questions',
|
|
93
106
|
title: 'Side questions',
|
|
@@ -107,7 +120,6 @@ export const BUILTIN_PLUGIN_REGISTRY = createBuiltinPluginRegistry([
|
|
|
107
120
|
tools: context => context.allowedTools.includes(SPAWN_SUBAGENT)
|
|
108
121
|
? subagentTools(context.subagents, context.runId).filter(tool => context.allowedTools.includes(tool.name))
|
|
109
122
|
: [],
|
|
110
|
-
commands: [{ name: 'subagents', description: 'Watch current subagent tasks and activity' }, { name: 'agents', description: 'Browse and edit agent profiles' }],
|
|
111
123
|
serverMethods: subagentServerMethods
|
|
112
124
|
},
|
|
113
125
|
{ descriptor: VARIANT_PLUGIN, ...toolContribution(SKILL_TOOLS, 30) },
|
|
@@ -115,11 +127,11 @@ export const BUILTIN_PLUGIN_REGISTRY = createBuiltinPluginRegistry([
|
|
|
115
127
|
{
|
|
116
128
|
descriptor: HISTORY_PLUGIN,
|
|
117
129
|
...toolContribution([...RECALL_TOOLS, ...TRAJECTORY_TOOLS], 10),
|
|
118
|
-
commands: [{ name: 'trajectory', description: 'Inspect the recorded runs of this session' }],
|
|
119
130
|
serverMethods: historyServerMethods
|
|
120
131
|
},
|
|
121
132
|
{ descriptor: ASK_USER_PLUGIN, toolOrder: 40, tools: context => context.harnessTools && context.allowedTools.includes(ASK_USER) ? [askUserTool(context.run)] : [] },
|
|
122
|
-
{ descriptor:
|
|
133
|
+
{ descriptor: TODO_PLUGIN, toolOrder: 50, tools: context => context.harnessTools ? [todoTool(context.run)] : [] },
|
|
134
|
+
{ descriptor: CHECKPOINT_PLUGIN, toolOrder: 51, tools: context => context.harnessTools && context.allowedTools.includes(CHECKPOINT_WRITE) ? [checkpointTool(context.run, () => sliceOf(context.env.root, context.runId))] : [] },
|
|
123
135
|
{
|
|
124
136
|
descriptor: CONNECTED_APPS_PLUGIN,
|
|
125
137
|
toolOrder: 0,
|
|
@@ -141,23 +153,28 @@ export const BUILTIN_PLUGIN_REGISTRY = createBuiltinPluginRegistry([
|
|
|
141
153
|
},
|
|
142
154
|
{
|
|
143
155
|
descriptor: ANNOTATIONS_PLUGIN,
|
|
156
|
+
commandPrefix: 'annotate',
|
|
157
|
+
defaultCommand: 'annotate',
|
|
144
158
|
commands: [
|
|
145
|
-
{ name: 'notes', description: '
|
|
146
|
-
{ name: 'annotate', description: 'Inspect a page or app, comment on components, send fixes
|
|
159
|
+
{ name: 'notes', description: 'Review web annotations saved from pages and apps' },
|
|
160
|
+
{ name: 'annotate', description: 'Inspect a page or app, comment on components, and send fixes: /annotate:annotate [url | --app <devtools address> | --bridge]' }
|
|
147
161
|
],
|
|
148
162
|
createWorldPart: ({ options }) => annotationsPart({ root: options.root, changed: options.notesChanged }),
|
|
149
163
|
serverMethods: annotationServerMethods
|
|
150
164
|
},
|
|
165
|
+
{ descriptor: CAPABILITIES_PLUGIN },
|
|
151
166
|
{
|
|
152
|
-
descriptor:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
serverMethods:
|
|
167
|
+
descriptor: USAGE_PLUGIN,
|
|
168
|
+
commandPrefix: 'usage',
|
|
169
|
+
defaultCommand: 'analysis',
|
|
170
|
+
commands: [
|
|
171
|
+
{ name: 'analysis', description: 'Token, cost and latency totals: /usage:analysis [today | 7d | 30d | all]' },
|
|
172
|
+
{ name: 'limit', description: 'Provider plan windows, balances and rate limits: /usage:limit' }
|
|
173
|
+
],
|
|
174
|
+
serverMethods: usageServerMethods
|
|
160
175
|
},
|
|
176
|
+
{ descriptor: DOCTOR_PLUGIN, serverMethods: doctorServerMethods },
|
|
177
|
+
{ descriptor: WORKTREES_PLUGIN, serverMethods: worktreeServerMethods },
|
|
161
178
|
...FIRST_PARTY_PLUGINS.map(descriptor => ({ descriptor }))
|
|
162
179
|
]);
|
|
163
180
|
export const HARNESS_PLUGINS = BUILTIN_PLUGIN_REGISTRY.descriptors();
|
|
@@ -10,9 +10,10 @@ import type { ActiveRun } from "../sessions/registry.js";
|
|
|
10
10
|
import type { PluginDescriptor } from "./types.js";
|
|
11
11
|
export interface BuiltinPluginCommand {
|
|
12
12
|
pluginId: string;
|
|
13
|
+
prefix: string;
|
|
13
14
|
name: string;
|
|
14
15
|
description: string;
|
|
15
|
-
|
|
16
|
+
legacyBare: boolean;
|
|
16
17
|
}
|
|
17
18
|
export interface BuiltinWorldContext {
|
|
18
19
|
options: WorldOptions;
|
|
@@ -33,10 +34,14 @@ export interface BuiltinToolContext {
|
|
|
33
34
|
}
|
|
34
35
|
export interface BuiltinPluginModule {
|
|
35
36
|
descriptor: PluginDescriptor;
|
|
37
|
+
/** Stable terminal command namespace; defaults to the plugin id. */
|
|
38
|
+
commandPrefix?: string;
|
|
39
|
+
/** Command invoked by the namespace root, such as `/annotate:`. */
|
|
40
|
+
defaultCommand?: string;
|
|
36
41
|
/** Stable order for model tools, independent of descriptor display order. */
|
|
37
42
|
toolOrder?: number;
|
|
38
43
|
tools?: (context: BuiltinToolContext) => ToolSpec[];
|
|
39
|
-
commands?: readonly Omit<BuiltinPluginCommand, 'pluginId' | '
|
|
44
|
+
commands?: readonly Omit<BuiltinPluginCommand, 'pluginId' | 'prefix' | 'legacyBare'>[];
|
|
40
45
|
createWorldPart?: (context: BuiltinWorldContext) => WorldPart | null;
|
|
41
46
|
serverMethods?: (harness: Harness) => Record<string, Handler>;
|
|
42
47
|
}
|
|
@@ -44,7 +49,9 @@ export interface BuiltinPluginRegistry {
|
|
|
44
49
|
readonly modules: readonly BuiltinPluginModule[];
|
|
45
50
|
descriptors(): PluginDescriptor[];
|
|
46
51
|
commands(): BuiltinPluginCommand[];
|
|
47
|
-
command(name: string): BuiltinPluginCommand | undefined;
|
|
52
|
+
command(prefix: string, name: string): BuiltinPluginCommand | undefined;
|
|
53
|
+
commandsNamed(name: string): BuiltinPluginCommand[];
|
|
54
|
+
defaultCommand(prefix: string): BuiltinPluginCommand | undefined;
|
|
48
55
|
tools(context: BuiltinToolContext): ToolSpec[];
|
|
49
56
|
worldParts(context: BuiltinWorldContext): WorldPart[];
|
|
50
57
|
serverMethods(harness: Harness): Record<string, Handler>;
|
|
@@ -1,33 +1,55 @@
|
|
|
1
1
|
/** One checked registry for the Harness-owned plugin modules and their surface contributions. */
|
|
2
2
|
export function createBuiltinPluginRegistry(modules) {
|
|
3
3
|
const pluginIds = new Set();
|
|
4
|
-
const
|
|
4
|
+
const prefixOwners = new Map();
|
|
5
|
+
const routeKeys = new Set();
|
|
5
6
|
for (const module of modules) {
|
|
6
7
|
const id = module.descriptor.id;
|
|
7
8
|
if (pluginIds.has(id))
|
|
8
9
|
throw new Error(`Duplicate built-in plugin id: ${id}`);
|
|
9
10
|
pluginIds.add(id);
|
|
11
|
+
const prefix = module.commandPrefix ?? id;
|
|
12
|
+
if (!/^[a-z][a-z0-9-]*$/.test(prefix) || prefix === 'plugin' || prefix === 'skill')
|
|
13
|
+
throw new Error(`Invalid built-in plugin command prefix: ${prefix}`);
|
|
14
|
+
if ((module.commands?.length || module.defaultCommand) && prefixOwners.has(prefix)) {
|
|
15
|
+
throw new Error(`Duplicate built-in plugin command prefix: ${prefix}`);
|
|
16
|
+
}
|
|
17
|
+
if (module.commands?.length || module.defaultCommand)
|
|
18
|
+
prefixOwners.set(prefix, id);
|
|
10
19
|
for (const command of module.commands ?? []) {
|
|
11
|
-
if (!/^[a-z][a-z0-9-]
|
|
20
|
+
if (!/^[a-z][a-z0-9-]*$/.test(command.name))
|
|
12
21
|
throw new Error(`Invalid built-in plugin command: ${command.name}`);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
const key = `${prefix}:${command.name}`;
|
|
23
|
+
if (routeKeys.has(key))
|
|
24
|
+
throw new Error(`Duplicate built-in plugin command: ${key}`);
|
|
25
|
+
routeKeys.add(key);
|
|
26
|
+
}
|
|
27
|
+
if (module.defaultCommand && !module.commands?.some(command => command.name === module.defaultCommand)) {
|
|
28
|
+
throw new Error(`Unknown default command for built-in plugin ${id}: ${module.defaultCommand}`);
|
|
16
29
|
}
|
|
17
30
|
}
|
|
18
31
|
const commands = modules.flatMap(module => (module.commands ?? []).map(command => ({
|
|
19
32
|
pluginId: module.descriptor.id,
|
|
33
|
+
prefix: module.commandPrefix ?? module.descriptor.id,
|
|
20
34
|
...command,
|
|
21
|
-
|
|
35
|
+
legacyBare: !!module.descriptor.required
|
|
22
36
|
})));
|
|
23
|
-
const
|
|
37
|
+
const byRoute = new Map(commands.map(command => [`${command.prefix}:${command.name}`, command]));
|
|
38
|
+
const byName = new Map();
|
|
39
|
+
for (const command of commands)
|
|
40
|
+
byName.set(command.name, [...(byName.get(command.name) ?? []), command]);
|
|
41
|
+
const defaults = new Map(modules.flatMap(module => module.defaultCommand
|
|
42
|
+
? [[module.commandPrefix ?? module.descriptor.id, byRoute.get(`${module.commandPrefix ?? module.descriptor.id}:${module.defaultCommand}`)]]
|
|
43
|
+
: []));
|
|
24
44
|
const toolModules = modules.map((module, index) => ({ module, index })).filter(({ module }) => module.tools)
|
|
25
45
|
.sort((left, right) => (left.module.toolOrder ?? left.index) - (right.module.toolOrder ?? right.index));
|
|
26
46
|
return {
|
|
27
47
|
modules,
|
|
28
48
|
descriptors: () => modules.map(module => module.descriptor),
|
|
29
49
|
commands: () => [...commands],
|
|
30
|
-
command: name =>
|
|
50
|
+
command: (prefix, name) => byRoute.get(`${prefix}:${name}`),
|
|
51
|
+
commandsNamed: name => [...(byName.get(name) ?? [])],
|
|
52
|
+
defaultCommand: prefix => defaults.get(prefix),
|
|
31
53
|
tools: context => toolModules.flatMap(({ module }) => module.tools(context)),
|
|
32
54
|
worldParts: context => modules.flatMap(module => {
|
|
33
55
|
const part = module.createWorldPart?.(context);
|
|
@@ -43,6 +43,7 @@ function fromDescriptor(descriptor, source, attachments = null, caller = {}) {
|
|
|
43
43
|
settings: (descriptor.settings ?? []).map((setting) => ({ ...setting, value: settingValue(setting, attachments?.settings[descriptor.id]) })),
|
|
44
44
|
connectorTemplates: descriptor.connectorTemplates ?? [],
|
|
45
45
|
attachment: switchable ? describe(attachments, descriptor.id, switchable.on) : null,
|
|
46
|
+
surfaces: descriptor.surfaces ?? null,
|
|
46
47
|
contributions: [
|
|
47
48
|
...(descriptor.code ?? []).map((code) => contribution(descriptor.id, 'code', code.name, { description: code.description, state: on ? 'on' : 'off' })),
|
|
48
49
|
...(descriptor.sections ?? []).map((section) => contribution(descriptor.id, 'instruction', section.name, { description: section.description ?? '' })),
|
|
@@ -102,7 +103,7 @@ function foundContribution(plugin, item, trusted, input, projects) {
|
|
|
102
103
|
const state = !item.enabled || !on ? 'off' : loads(item, trusted) ? 'on' : 'untrusted';
|
|
103
104
|
const rest = { state, attachment, scope: attachment ? scopeWords(attachment, projects) : null };
|
|
104
105
|
if (item.path.startsWith(BUILTIN_PREFIX))
|
|
105
|
-
return contribution(plugin, kind, name, { ...rest, description: 'Built into the pi command.
|
|
106
|
+
return contribution(plugin, kind, name, { ...rest, description: 'Built into the pi command. Xingular sessions do not load it, in the terminal or in a host, so both offer the same tools.' });
|
|
106
107
|
if (item.kind === 'extension') {
|
|
107
108
|
const observed = input.registered[item.path];
|
|
108
109
|
const hash = input.loading ? registrationHash(input.loading, item.path) : null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface CommandSourceInfo {
|
|
2
|
+
path: string;
|
|
3
|
+
source?: string;
|
|
4
|
+
origin?: 'package' | 'top-level';
|
|
5
|
+
baseDir?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Stable command namespace for a Pi extension's owning package or loose extension file. */
|
|
8
|
+
export declare function pluginCommandPrefix(source: CommandSourceInfo): string;
|