orizu 0.5.20 → 0.5.21
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/dist/archive-artifacts-cli.d.ts +50 -0
- package/dist/archive-artifacts-cli.d.ts.map +1 -0
- package/dist/artifact-archive.d.ts +48 -0
- package/dist/artifact-archive.d.ts.map +1 -0
- package/dist/artifact-pull.d.ts +22 -0
- package/dist/artifact-pull.d.ts.map +1 -0
- package/dist/banner.d.ts +3 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/connectors-cli.d.ts +31 -0
- package/dist/connectors-cli.d.ts.map +1 -0
- package/dist/credentials.d.ts +46 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/csv.d.ts +49 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/dataset-download.d.ts +6 -0
- package/dist/dataset-download.d.ts.map +1 -0
- package/dist/dataset-edit-rows.d.ts +10 -0
- package/dist/dataset-edit-rows.d.ts.map +1 -0
- package/dist/dataset-upload-snapshot.d.ts +13 -0
- package/dist/dataset-upload-snapshot.d.ts.map +1 -0
- package/dist/daytona-slice-rehearsal.d.ts +89 -0
- package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
- package/dist/daytona-slice.d.ts +106 -0
- package/dist/daytona-slice.d.ts.map +1 -0
- package/dist/deploy-key-merge-core.d.ts +121 -0
- package/dist/deploy-key-merge-core.d.ts.map +1 -0
- package/dist/diff-comments-cli.d.ts +12 -0
- package/dist/diff-comments-cli.d.ts.map +1 -0
- package/dist/diff-comments-cli.js +3 -0
- package/dist/egress-allowlist-cli.d.ts +23 -0
- package/dist/egress-allowlist-cli.d.ts.map +1 -0
- package/dist/egress-policy.d.ts +108 -0
- package/dist/egress-policy.d.ts.map +1 -0
- package/dist/error-response.d.ts +9 -0
- package/dist/error-response.d.ts.map +1 -0
- package/dist/file-parser.d.ts +5 -0
- package/dist/file-parser.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.d.ts +9 -0
- package/dist/gepa-engine-dispatch.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.js +48 -2
- package/dist/gepa-python-command.d.ts +6 -0
- package/dist/gepa-python-command.d.ts.map +1 -0
- package/dist/gepa-python-paths.d.ts +6 -0
- package/dist/gepa-python-paths.d.ts.map +1 -0
- package/dist/gepa-python-paths.js +6 -3
- package/dist/git-credential.d.ts +69 -0
- package/dist/git-credential.d.ts.map +1 -0
- package/dist/git-recovery-environment.d.ts +31 -0
- package/dist/git-recovery-environment.d.ts.map +1 -0
- package/dist/github-setup.d.ts +122 -0
- package/dist/github-setup.d.ts.map +1 -0
- package/dist/global-flags.d.ts +12 -0
- package/dist/global-flags.d.ts.map +1 -0
- package/dist/help.d.ts +46 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +91 -23
- package/dist/hosted-boot.d.ts +273 -0
- package/dist/hosted-boot.d.ts.map +1 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
- package/dist/hosted-bootstrap.d.ts +188 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -0
- package/dist/hosted-broker-server.d.ts +50 -0
- package/dist/hosted-broker-server.d.ts.map +1 -0
- package/dist/hosted-git-http-server.d.ts +36 -0
- package/dist/hosted-git-http-server.d.ts.map +1 -0
- package/dist/hosted-harness-claude.d.ts +195 -0
- package/dist/hosted-harness-claude.d.ts.map +1 -0
- package/dist/hosted-harness-opencode.d.ts +227 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -0
- package/dist/hosted-harness.d.ts +118 -0
- package/dist/hosted-harness.d.ts.map +1 -0
- package/dist/hosted-harvest.d.ts +68 -0
- package/dist/hosted-harvest.d.ts.map +1 -0
- package/dist/hosted-headless.d.ts +72 -0
- package/dist/hosted-headless.d.ts.map +1 -0
- package/dist/hosted-loop-lifecycle.d.ts +206 -0
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
- package/dist/hosted-loop.d.ts +170 -0
- package/dist/hosted-loop.d.ts.map +1 -0
- package/dist/hosted-run-event-sink.d.ts +222 -0
- package/dist/hosted-run-event-sink.d.ts.map +1 -0
- package/dist/hosted-runtime-assets.d.ts +186 -0
- package/dist/hosted-runtime-assets.d.ts.map +1 -0
- package/dist/hosted-session-cli.d.ts +258 -0
- package/dist/hosted-session-cli.d.ts.map +1 -0
- package/dist/hosted-skill-staging.d.ts +73 -0
- package/dist/hosted-skill-staging.d.ts.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -92
- package/dist/instruction-set-gepa-launch.d.ts +3 -0
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
- package/dist/instruction-set-gepa-launch.js +15 -0
- package/dist/instruction-set-loader.d.ts +6 -0
- package/dist/instruction-set-loader.d.ts.map +1 -0
- package/dist/instruction-set-loader.js +59 -0
- package/dist/instruction-set-manifest.d.ts +14 -0
- package/dist/instruction-set-manifest.d.ts.map +1 -0
- package/dist/instruction-set-manifest.js +162 -0
- package/dist/instruction-sets-cli.d.ts +40 -0
- package/dist/instruction-sets-cli.d.ts.map +1 -0
- package/dist/instruction-sets-cli.js +361 -0
- package/dist/json-response.d.ts +4 -0
- package/dist/json-response.d.ts.map +1 -0
- package/dist/jsonl-stream.d.ts +8 -0
- package/dist/jsonl-stream.d.ts.map +1 -0
- package/dist/kill-switch-cli.d.ts +30 -0
- package/dist/kill-switch-cli.d.ts.map +1 -0
- package/dist/loopback-serve.d.ts +25 -0
- package/dist/loopback-serve.d.ts.map +1 -0
- package/dist/manifests-cli.d.ts +22 -0
- package/dist/manifests-cli.d.ts.map +1 -0
- package/dist/markdown-report-input.d.ts +7 -0
- package/dist/markdown-report-input.d.ts.map +1 -0
- package/dist/merge-job.d.ts +155 -0
- package/dist/merge-job.d.ts.map +1 -0
- package/dist/model-configs-cli.d.ts +19 -0
- package/dist/model-configs-cli.d.ts.map +1 -0
- package/dist/model-configs-cli.js +104 -0
- package/dist/optimization-export-cli.d.ts +19 -0
- package/dist/optimization-export-cli.d.ts.map +1 -0
- package/dist/optimizations-list-cli.d.ts +20 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.d.ts +8 -0
- package/dist/optimizations-promote-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.js +34 -0
- package/dist/option-validation.d.ts +15 -0
- package/dist/option-validation.d.ts.map +1 -0
- package/dist/preview-runtime.d.ts +63 -0
- package/dist/preview-runtime.d.ts.map +1 -0
- package/dist/prompt-draft-push.d.ts +60 -0
- package/dist/prompt-draft-push.d.ts.map +1 -0
- package/dist/prompt-label-cli.d.ts +11 -0
- package/dist/prompt-label-cli.d.ts.map +1 -0
- package/dist/prompt-label-cli.js +29 -0
- package/dist/prompt-length-wire.d.ts +28 -0
- package/dist/prompt-length-wire.d.ts.map +1 -0
- package/dist/prompt-report-cli.d.ts +10 -0
- package/dist/prompt-report-cli.d.ts.map +1 -0
- package/dist/prompt-summary-table.d.ts +15 -0
- package/dist/prompt-summary-table.d.ts.map +1 -0
- package/dist/prompt-summary-table.js +6 -0
- package/dist/registration-tag-warning.d.ts +32 -0
- package/dist/registration-tag-warning.d.ts.map +1 -0
- package/dist/report-comments-cli.d.ts +16 -0
- package/dist/report-comments-cli.d.ts.map +1 -0
- package/dist/report-markdown-blankness.d.ts +2 -0
- package/dist/report-markdown-blankness.d.ts.map +1 -0
- package/dist/report-replacement-warning.d.ts +17 -0
- package/dist/report-replacement-warning.d.ts.map +1 -0
- package/dist/repository-backup-core.d.ts +225 -0
- package/dist/repository-backup-core.d.ts.map +1 -0
- package/dist/repository-recovery-manifest.d.ts +83 -0
- package/dist/repository-recovery-manifest.d.ts.map +1 -0
- package/dist/run-event-digest.d.ts +74 -0
- package/dist/run-event-digest.d.ts.map +1 -0
- package/dist/runner-dir-verify.d.ts +66 -0
- package/dist/runner-dir-verify.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.d.ts +24 -0
- package/dist/runner-instruction-set-contract.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.js +50 -0
- package/dist/sandbox-hygiene.d.ts +45 -0
- package/dist/sandbox-hygiene.d.ts.map +1 -0
- package/dist/sandbox-provider.d.ts +193 -0
- package/dist/sandbox-provider.d.ts.map +1 -0
- package/dist/scorer-draft-push.d.ts +50 -0
- package/dist/scorer-draft-push.d.ts.map +1 -0
- package/dist/scorers-cli.d.ts +20 -0
- package/dist/scorers-cli.d.ts.map +1 -0
- package/dist/scorers-cli.js +22 -0
- package/dist/scorers-verify-parity-bridge.d.ts +16 -0
- package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
- package/dist/scorers-verify-parity-bridge.js +90 -0
- package/dist/scorers-verify-parity.d.ts +81 -0
- package/dist/scorers-verify-parity.d.ts.map +1 -0
- package/dist/scorers-verify-parity.js +502 -0
- package/dist/scores-accept-cli.d.ts +21 -0
- package/dist/scores-accept-cli.d.ts.map +1 -0
- package/dist/secret-redaction.d.ts +47 -0
- package/dist/secret-redaction.d.ts.map +1 -0
- package/dist/session-timeline.d.ts +54 -0
- package/dist/session-timeline.d.ts.map +1 -0
- package/dist/setup-prompt.d.ts +5 -0
- package/dist/setup-prompt.d.ts.map +1 -0
- package/dist/skill-installer.d.ts +68 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.d.ts +7 -0
- package/dist/skilled-proposer-launch.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.js +60 -0
- package/dist/smoke.d.ts +102 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/start-intent.d.ts +12 -0
- package/dist/start-intent.d.ts.map +1 -0
- package/dist/task-assignment-manifest.d.ts +15 -0
- package/dist/task-assignment-manifest.d.ts.map +1 -0
- package/dist/task-create-error.d.ts +18 -0
- package/dist/task-create-error.d.ts.map +1 -0
- package/dist/team-connectors-cli.d.ts +29 -0
- package/dist/team-connectors-cli.d.ts.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/vercel-sandbox-provider.d.ts +156 -0
- package/dist/vercel-sandbox-provider.d.ts.map +1 -0
- package/dist/workbench-cli.d.ts +233 -0
- package/dist/workbench-cli.d.ts.map +1 -0
- package/dist/workspace-sync.d.ts +207 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace.d.ts +92 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/zip-draft-push.d.ts +55 -0
- package/dist/zip-draft-push.d.ts.map +1 -0
- package/package.json +14 -1
- package/requirements/README.md +138 -0
- package/requirements/skilled-proposer.in +66 -0
- package/requirements/skilled-proposer.lock +265 -0
- package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
- package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
- package/src/diff-comments-cli.ts +4 -0
- package/src/gepa-engine-dispatch.ts +42 -2
- package/src/gepa-python-paths.ts +6 -3
- package/src/help.ts +93 -23
- package/src/index.ts +99 -126
- package/src/instruction-set-gepa-launch.ts +28 -0
- package/src/instruction-set-loader.ts +37 -0
- package/src/instruction-set-manifest.ts +120 -0
- package/src/instruction-sets-cli.ts +306 -0
- package/src/model-configs-cli.ts +119 -0
- package/src/optimizations-promote-cli.ts +41 -0
- package/src/prompt-label-cli.ts +55 -0
- package/src/prompt-summary-table.ts +12 -0
- package/src/runner-instruction-set-contract.ts +64 -0
- package/src/scorers-cli.ts +40 -0
- package/src/scorers-verify-parity-bridge.ts +90 -0
- package/src/scorers-verify-parity.ts +564 -0
- package/src/skilled-proposer-launch.ts +89 -0
- package/src/skilled-proposer-venv-manager.mjs +757 -0
- package/tsconfig.json +2 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +2 -1
- package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +8 -2
- package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +17 -3
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +100 -8
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +92 -12
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +100 -22
- package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +17 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +2 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +4 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
- package/vendor/skills/orizu-cli/SKILL.md +2 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
- package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +25 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +108 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-git-http-server.d.ts","sourceRoot":"","sources":["../src/hosted-git-http-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAmCH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,aAAa,CAM9E;AAED,kFAAkF;AAClF,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKxE;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,CAyE3G"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude-Agent-SDK driver for the AgentHarness seam (ALI-929 / P3.6, per ADR-005
|
|
3
|
+
* §4a — the swappability proof).
|
|
4
|
+
*
|
|
5
|
+
* This is the SECOND `AgentHarness` implementation behind the same seam as the
|
|
6
|
+
* OpenCode driver (`hosted-harness-opencode.ts`). It drives
|
|
7
|
+
* `@anthropic-ai/claude-agent-sdk`'s `query()` loop — an IN-PROCESS agent loop,
|
|
8
|
+
* with NO server process to spawn and no localhost SSE to parse — and normalizes
|
|
9
|
+
* the SDK's message/tool stream into the SAME `HarnessEvent` vocabulary the
|
|
10
|
+
* event bridge (`hosted-run-event-sink.ts`) already consumes. It carries ZERO
|
|
11
|
+
* Claude-Agent-SDK types across the seam: everything below stays behind
|
|
12
|
+
* `createClaudeAgentHarness`, exactly as the OpenCode SSE schema stays behind
|
|
13
|
+
* `createOpenCodeHarness`.
|
|
14
|
+
*
|
|
15
|
+
* The founder's locked escape-hatch ("OpenInspect, but we can move to our own
|
|
16
|
+
* harness whenever we want") is proven by this module existing and passing the
|
|
17
|
+
* SAME session-cell invariants through the SAME sink — see
|
|
18
|
+
* `test/cli/hosted-harness-swap.test.ts`.
|
|
19
|
+
*
|
|
20
|
+
* INJECTABLE LOADER: like the Daytona/Vercel SDK adapters, the SDK is imported
|
|
21
|
+
* LAZILY through a NON-LITERAL specifier so the CLI/app build resolve without the
|
|
22
|
+
* package, and a `loadModule` dep is injectable so tests exercise the
|
|
23
|
+
* normalization against a fake `query()` with no real SDK and no model calls.
|
|
24
|
+
*
|
|
25
|
+
* ── query() → runPrompt() mapping ────────────────────────────────────────────
|
|
26
|
+
* ONE `query()` call drives ONE `runPrompt()` to completion. The SDK's async
|
|
27
|
+
* iterable yields `SDKMessage`s; we translate them:
|
|
28
|
+
* • system/init → capture session_id; emit `ready` once.
|
|
29
|
+
* • assistant.message → text blocks → `token` (keyed by the SDK message id
|
|
30
|
+
* for coalescing); tool_use blocks → `tool_call`. The
|
|
31
|
+
* per-turn `message.usage` the SDK ALWAYS populates is
|
|
32
|
+
* deliberately NOT emitted as a `step_finish` (see
|
|
33
|
+
* leak #3): the driver coalesces to ONE terminal
|
|
34
|
+
* step_finish so both harnesses share one granularity.
|
|
35
|
+
* • user.message → tool_result blocks → `tool_result` (paired to the
|
|
36
|
+
* tool_use by callId, exactly once).
|
|
37
|
+
* • stream_event (partial) → text deltas are accumulated into the running token
|
|
38
|
+
* snapshot for coalescing; consumed, not 1:1 mapped.
|
|
39
|
+
* • result/success → final `step_finish` with cost+tokens, then
|
|
40
|
+
* `execution_complete` (success, or aborted on stop).
|
|
41
|
+
* • result/error_* → `error` (critical terminal).
|
|
42
|
+
* The stream ALWAYS ends with exactly one terminal event.
|
|
43
|
+
*
|
|
44
|
+
* ── WHAT LEAKS THROUGH THE BOUNDARY (map-conservatively evidence) ─────────────
|
|
45
|
+
* These SDK signals have NO clean `HarnessEvent` kind and are DROPPED here (not a
|
|
46
|
+
* reason to widen the seam — this is the audit of the boundary's fit):
|
|
47
|
+
* 1. `thinking` / `redacted_thinking` content blocks — the harness vocabulary
|
|
48
|
+
* has no reasoning kind (OpenCode's driver drops reasoning too), so extended
|
|
49
|
+
* thinking is not forwarded. Cost of surfacing it: a new `thinking` kind on
|
|
50
|
+
* the seam + a bridge mapping — a deliberate future decision, not done here.
|
|
51
|
+
* 2. `system/compact_boundary` — context-compaction markers are dropped (the
|
|
52
|
+
* OpenCode driver handles `session.compacted` internally and forwards
|
|
53
|
+
* nothing either; parity, not a leak).
|
|
54
|
+
* 3. Per-assistant-turn USAGE granularity — the real contract is that every
|
|
55
|
+
* `SDKAssistantMessage.message` is a `BetaMessage` whose `usage: BetaUsage`
|
|
56
|
+
* is ALWAYS populated (input/output token counts are non-optional), so the
|
|
57
|
+
* SDK reports usage on EACH assistant turn, not only at the terminal. The
|
|
58
|
+
* driver deliberately DOES NOT emit a `step_finish` per assistant turn: it
|
|
59
|
+
* coalesces to exactly ONE terminal `step_finish` at the `result` message,
|
|
60
|
+
* carrying `total_cost_usd` + aggregate `usage`. OpenCode emits one
|
|
61
|
+
* `step_finish` per `step-finish` part; for a one-tool task that is also a
|
|
62
|
+
* single terminal step, so the two streams are legitimately identical at the
|
|
63
|
+
* seam. This coalescing is itself a swappability win — the seam absorbs the
|
|
64
|
+
* raw per-turn/per-step granularity difference so downstream sees one
|
|
65
|
+
* normalized `agent_step_finish` from either harness. (Cost is only known at
|
|
66
|
+
* the terminal `result`; per-turn `usage` carries tokens but no cost, which
|
|
67
|
+
* is why folding to the terminal loses nothing the seam surfaces.)
|
|
68
|
+
* 4. `result.num_turns` / `duration_ms` / `permission_denials` — folded into
|
|
69
|
+
* the terminal transition's shape (success/aborted/error), not surfaced as
|
|
70
|
+
* distinct events.
|
|
71
|
+
* 5. Git author identity is plumbed via `GIT_AUTHOR_*` / `GIT_COMMITTER_*`
|
|
72
|
+
* ENV on the query options (the SDK has no first-class author param), where
|
|
73
|
+
* OpenCode takes it structurally. Same effect, different plumbing point.
|
|
74
|
+
* NOTE the `env` CONTRACT: the SDK's `env` REPLACES the subprocess
|
|
75
|
+
* environment entirely (it is NOT merged with `process.env`), so we spread
|
|
76
|
+
* `...process.env` FIRST — without it the `claude` subprocess would lose
|
|
77
|
+
* `PATH`/`HOME`/`ANTHROPIC_API_KEY` and die. G3 credential model: like the
|
|
78
|
+
* OpenCode path (which hands `opencode` the non-secret dummy key), the SDK
|
|
79
|
+
* client is given the same `anthropicDummyKey` as `ANTHROPIC_API_KEY`; the
|
|
80
|
+
* real key is injected by the sandbox firewall on egress to
|
|
81
|
+
* api.anthropic.com, so NO raw key ever enters the in-process client. See
|
|
82
|
+
* the G3/credentials axis in ali-929-harness-comparison.md.
|
|
83
|
+
* 6. The resumable agent session id is assigned LAZILY by the SDK at
|
|
84
|
+
* `system/init` — it does not exist before the first `runPrompt`. So
|
|
85
|
+
* `start()` returns the RESUME id if given, else `''`; the real id first
|
|
86
|
+
* reaches the bridge via the `ready` event. OpenCode creates its session
|
|
87
|
+
* eagerly in `start()`. This is the one place the seam's "start returns the
|
|
88
|
+
* session id" shape fits OpenCode better than the SDK — the run still
|
|
89
|
+
* completes identically (the sink records the id from `ready`); see the
|
|
90
|
+
* comparison doc's swappability verdict.
|
|
91
|
+
* 7. Provider-qualified model strings (e.g. `anthropic/claude-opus-4-8`) are
|
|
92
|
+
* reduced to the bare model id the SDK expects (leading `anthropic/` is
|
|
93
|
+
* stripped; other providers pass through unchanged and would be the SDK's to
|
|
94
|
+
* reject).
|
|
95
|
+
*/
|
|
96
|
+
import type { AgentHarness } from './hosted-harness.js';
|
|
97
|
+
/** An Anthropic content block as it appears inside an SDK assistant/user message
|
|
98
|
+
* (`message.content`). Kept structural — we read only the fields we map. */
|
|
99
|
+
interface SdkContentBlock {
|
|
100
|
+
type: string;
|
|
101
|
+
text?: string;
|
|
102
|
+
id?: string;
|
|
103
|
+
name?: string;
|
|
104
|
+
input?: unknown;
|
|
105
|
+
tool_use_id?: string;
|
|
106
|
+
content?: unknown;
|
|
107
|
+
is_error?: boolean;
|
|
108
|
+
}
|
|
109
|
+
interface SdkAnthropicMessage {
|
|
110
|
+
id?: string;
|
|
111
|
+
role?: string;
|
|
112
|
+
content?: SdkContentBlock[] | string;
|
|
113
|
+
usage?: Record<string, unknown>;
|
|
114
|
+
}
|
|
115
|
+
/** A raw Anthropic streaming event, forwarded by the SDK when partial messages
|
|
116
|
+
* are enabled (`type: 'stream_event'`). We read only the text-delta path. */
|
|
117
|
+
interface SdkStreamRawEvent {
|
|
118
|
+
type?: string;
|
|
119
|
+
index?: number;
|
|
120
|
+
delta?: {
|
|
121
|
+
type?: string;
|
|
122
|
+
text?: string;
|
|
123
|
+
};
|
|
124
|
+
content_block?: {
|
|
125
|
+
type?: string;
|
|
126
|
+
};
|
|
127
|
+
message?: {
|
|
128
|
+
id?: string;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** The SDK message union. Discriminated on `type` (+ `subtype` for system). */
|
|
132
|
+
interface SdkMessage {
|
|
133
|
+
type: string;
|
|
134
|
+
subtype?: string;
|
|
135
|
+
session_id?: string;
|
|
136
|
+
message?: SdkAnthropicMessage;
|
|
137
|
+
event?: SdkStreamRawEvent;
|
|
138
|
+
is_error?: boolean;
|
|
139
|
+
total_cost_usd?: number;
|
|
140
|
+
usage?: Record<string, unknown>;
|
|
141
|
+
result?: string;
|
|
142
|
+
}
|
|
143
|
+
interface SdkMcpServerConfig {
|
|
144
|
+
type: string;
|
|
145
|
+
command?: string;
|
|
146
|
+
args?: readonly string[];
|
|
147
|
+
url?: string;
|
|
148
|
+
env?: Record<string, string>;
|
|
149
|
+
}
|
|
150
|
+
interface SdkQueryOptions {
|
|
151
|
+
model?: string;
|
|
152
|
+
cwd?: string;
|
|
153
|
+
resume?: string;
|
|
154
|
+
abortController?: AbortController;
|
|
155
|
+
permissionMode?: string;
|
|
156
|
+
/** REQUIRED by the SDK whenever `permissionMode: 'bypassPermissions'` — a
|
|
157
|
+
* safety measure to make the bypass intentional. Verified against
|
|
158
|
+
* @anthropic-ai/claude-agent-sdk@0.3.201 Options.allowDangerouslySkipPermissions. */
|
|
159
|
+
allowDangerouslySkipPermissions?: boolean;
|
|
160
|
+
includePartialMessages?: boolean;
|
|
161
|
+
mcpServers?: Record<string, SdkMcpServerConfig>;
|
|
162
|
+
/** The SDK's `env` REPLACES the subprocess environment entirely (it is NOT
|
|
163
|
+
* merged with `process.env`). Matches the real contract's
|
|
164
|
+
* `{ [k: string]: string | undefined }` so `...process.env` spreads cleanly. */
|
|
165
|
+
env?: Record<string, string | undefined>;
|
|
166
|
+
}
|
|
167
|
+
/** The object `query()` returns: an async iterable of messages plus `interrupt`. */
|
|
168
|
+
interface SdkQueryResult extends AsyncIterable<SdkMessage> {
|
|
169
|
+
interrupt?: () => Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
export interface ClaudeAgentSdkModule {
|
|
172
|
+
query: (args: {
|
|
173
|
+
prompt: string;
|
|
174
|
+
options: SdkQueryOptions;
|
|
175
|
+
}) => SdkQueryResult;
|
|
176
|
+
}
|
|
177
|
+
export interface ClaudeAgentHarnessOptions {
|
|
178
|
+
/** Test seam: inject a fake SDK so normalization runs with no real query/model. */
|
|
179
|
+
loadModule?: () => Promise<ClaudeAgentSdkModule>;
|
|
180
|
+
/** Permission posture; default 'bypassPermissions' (the sandbox is the boundary,
|
|
181
|
+
* mirroring the OpenCode driver's allow-all config). */
|
|
182
|
+
permissionMode?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Non-secret placeholder key handed to the in-process SDK client as
|
|
185
|
+
* `ANTHROPIC_API_KEY` (G3). The sandbox firewall swaps in the real key on
|
|
186
|
+
* egress to api.anthropic.com, so no raw key enters the client — the SAME
|
|
187
|
+
* brokering the OpenCode path relies on. Threaded from the loop boot context;
|
|
188
|
+
* never a real key. When absent, `ANTHROPIC_API_KEY` is inherited from
|
|
189
|
+
* `process.env` as-is (still expected to be the dummy in the sandbox).
|
|
190
|
+
*/
|
|
191
|
+
anthropicDummyKey?: string;
|
|
192
|
+
}
|
|
193
|
+
export declare function createClaudeAgentHarness(options?: ClaudeAgentHarnessOptions): AgentHarness;
|
|
194
|
+
export {};
|
|
195
|
+
//# sourceMappingURL=hosted-harness-claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-harness-claude.d.ts","sourceRoot":"","sources":["../src/hosted-harness-claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode driver for the AgentHarness seam (ALI-926 / P3.4, per ADR-005 §4a).
|
|
3
|
+
*
|
|
4
|
+
* The `opencode` implementation of `AgentHarness`. Drives a long-running
|
|
5
|
+
* `opencode serve` process over localhost HTTP/SSE, exactly as OpenInspect's
|
|
6
|
+
* `bridge.py` does — this module is a DESIGN port of `bridge.py`'s
|
|
7
|
+
* `_stream_opencode_response_sse` + `_transform_part_to_event` into idiomatic
|
|
8
|
+
* TypeScript. The WebSocket half of `bridge.py` is NOT ported: the transport is
|
|
9
|
+
* the RunAPI event bridge (`hosted-run-event-sink.ts`), not a Durable Object.
|
|
10
|
+
*
|
|
11
|
+
* DRIVING PROTOCOL (all localhost):
|
|
12
|
+
* - POST /session → create an OpenCode session
|
|
13
|
+
* - GET /session/{id} → validate a resumed session
|
|
14
|
+
* - GET /event → SSE stream of message parts
|
|
15
|
+
* - POST /session/{id}/prompt_async → enqueue a prompt (non-blocking)
|
|
16
|
+
* - GET /session/{id}/message → final-state reconciliation
|
|
17
|
+
* - POST /session/{id}/abort → stop the in-flight prompt
|
|
18
|
+
*
|
|
19
|
+
* The stream is opened BEFORE the prompt is POSTed (mirrors bridge.py) so no
|
|
20
|
+
* early parts are missed. Termination is driven by `session.idle` /
|
|
21
|
+
* `session.status{idle}` (→ `execution_complete`) or `session.error` (→
|
|
22
|
+
* `error`). Sub-task / child-session fan-out is intentionally NOT ported —
|
|
23
|
+
* ADR-005 keeps the RunAPI single-writer-per-run, so `spawn-task` is out of P3.
|
|
24
|
+
*
|
|
25
|
+
* TESTABILITY: the driver takes a `baseUrl` (localhost:port) and an injectable
|
|
26
|
+
* `fetchImpl`, so it runs against a fake in-process OpenCode SSE server with no
|
|
27
|
+
* real `opencode` binary. `spawnOpenCode` (below) is the P3.5 in-sandbox launch
|
|
28
|
+
* helper; the driver itself never spawns a process.
|
|
29
|
+
*
|
|
30
|
+
* ── Deliberate deltas from bridge.py (kept in ONE place) ─────────────────────
|
|
31
|
+
* This driver is a faithful port EXCEPT for these consciously-chosen
|
|
32
|
+
* differences — read them before diffing behavior against `bridge.py`:
|
|
33
|
+
* • Sub-task / child-session fan-out is OUT OF SCOPE. bridge.py tracks
|
|
34
|
+
* `tracked_child_session_ids`, authorizes child assistant messages, and
|
|
35
|
+
* emits `isSubtask` events; ADR-005 keeps the RunAPI single-writer-per-run,
|
|
36
|
+
* so `task`-tool child sessions are neither tracked nor forwarded here.
|
|
37
|
+
* • Compaction IS NOW PORTED (P2-1). `session.compacted` sets a
|
|
38
|
+
* `compactionOccurred` flag; thereafter assistant messages whose parentID
|
|
39
|
+
* no longer matches the user message are accepted (except the compaction
|
|
40
|
+
* summary itself), in both the live stream and `fetchFinalState` — mirrors
|
|
41
|
+
* bridge.py lines ~1300-1307 / 1153-1160 / 1421-1425.
|
|
42
|
+
* • Max prompt duration IS NOW PORTED (P3-1). `PROMPT_MAX_DURATION`
|
|
43
|
+
* (default 5400s, injectable) runs ALONGSIDE the SSE inactivity deadline;
|
|
44
|
+
* on breach the prompt is aborted and a terminal `error` is emitted.
|
|
45
|
+
* • SSE framing assumes LF `\n\n` event separators and `data:` lines, exactly
|
|
46
|
+
* as OpenCode 1.14.41 emits them (bridge.py `_parse_sse_stream` makes the
|
|
47
|
+
* same assumption — it splits on `"\n\n"`, never CRLF). This is pinned to
|
|
48
|
+
* `OPENCODE_PINNED_VERSION`; a bump that switches OpenCode to CRLF framing
|
|
49
|
+
* would break both ports and must be re-verified.
|
|
50
|
+
*/
|
|
51
|
+
import type { AgentHarness } from './hosted-harness.js';
|
|
52
|
+
export declare const OPENCODE_PINNED_VERSION = "1.14.41";
|
|
53
|
+
export declare const DEFAULT_PROMPT_MAX_DURATION_MS = 5400000;
|
|
54
|
+
export type HarnessFetch = (url: string, init?: RequestInit) => Promise<Response>;
|
|
55
|
+
export interface OpenCodeHarnessOptions {
|
|
56
|
+
/** Base URL of the local `opencode serve`, e.g. "http://localhost:4096". */
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
fetchImpl?: HarnessFetch;
|
|
59
|
+
/** Inactivity ceiling on the SSE stream (ms); reset on every chunk. */
|
|
60
|
+
sseInactivityMs?: number;
|
|
61
|
+
/** Timeout for the discrete (non-streaming) OpenCode requests (ms). */
|
|
62
|
+
requestTimeoutMs?: number;
|
|
63
|
+
/** Wall-clock ceiling on a single prompt (ms); breach aborts + errors out. */
|
|
64
|
+
promptMaxDurationMs?: number;
|
|
65
|
+
now?: () => number;
|
|
66
|
+
}
|
|
67
|
+
/** Distinct terminal reason emitted when a prompt breaches PROMPT_MAX_DURATION,
|
|
68
|
+
* so the bridge/tests can tell a duration abort from a model error. */
|
|
69
|
+
export declare const PROMPT_MAX_DURATION_ERROR = "prompt exceeded max duration";
|
|
70
|
+
/**
|
|
71
|
+
* Split a possibly provider-qualified model string into provider + model id.
|
|
72
|
+
* SINGLE-SLASH ids ("anthropic/claude-opus-4-8") are the assumed form; a bare
|
|
73
|
+
* id defaults to provider "anthropic". This is THE shared parse — consumed by
|
|
74
|
+
* BOTH the prompt path (`buildPromptRequestBody`) and the pre-prompt
|
|
75
|
+
* validation (`awaitOpenCodeModelResolvable`), so the two can never drift:
|
|
76
|
+
* parity is exactly what makes the validation meaningful (ALI-1086).
|
|
77
|
+
* KNOWN LIMIT: `split('/', 2)` drops everything after the second slash, so a
|
|
78
|
+
* 3-part id ("openrouter/anthropic/claude-x") misparses to provider
|
|
79
|
+
* "openrouter" + model "anthropic" — identically in both places (today's
|
|
80
|
+
* pre-existing prompt-path behavior, now shared rather than duplicated).
|
|
81
|
+
*/
|
|
82
|
+
export declare function qualifyHostedModel(model: string): {
|
|
83
|
+
providerId: string;
|
|
84
|
+
modelId: string;
|
|
85
|
+
};
|
|
86
|
+
export declare function buildPromptRequestBody(content: string, model: string | undefined, opencodeMessageId: string, reasoningEffort: string | undefined): Record<string, unknown>;
|
|
87
|
+
export declare function createOpenCodeHarness(options: OpenCodeHarnessOptions): AgentHarness;
|
|
88
|
+
/** provider id → model ids the RUNNING opencode instance can resolve. */
|
|
89
|
+
export type OpenCodeModelCatalog = ReadonlyMap<string, readonly string[]>;
|
|
90
|
+
/**
|
|
91
|
+
* Read the resolvable catalog from a running `opencode serve` via
|
|
92
|
+
* `GET /config/providers` (shape: `{ providers: [{ id, models: { <id>: … } }] }`,
|
|
93
|
+
* verified against opencode-ai@1.14.41). Returns null on ANY transport/shape
|
|
94
|
+
* failure — the caller treats that as "cannot validate", never as "invalid".
|
|
95
|
+
*/
|
|
96
|
+
export declare function fetchOpenCodeModelCatalog(baseUrl: string, fetchImpl?: HarnessFetch, timeoutMs?: number): Promise<OpenCodeModelCatalog | null>;
|
|
97
|
+
/** Rank candidate model ids by similarity to the requested id (longest shared
|
|
98
|
+
* prefix first, ties broken lexicographically DESCENDING so the newest of a
|
|
99
|
+
* versioned family leads) and cap the list — the fail-fast error should lead
|
|
100
|
+
* with the closest/newest substitutes, not a full catalog dump. */
|
|
101
|
+
export declare function rankModelAlternatives(requested: string, candidates: readonly string[], limit?: number): string[];
|
|
102
|
+
export type ModelValidationOutcome = {
|
|
103
|
+
kind: 'resolvable';
|
|
104
|
+
waitedMs: number;
|
|
105
|
+
} | {
|
|
106
|
+
kind: 'unresolvable';
|
|
107
|
+
/** Provider-qualified requested model, e.g. "anthropic/claude-opus-4-8". */
|
|
108
|
+
requested: string;
|
|
109
|
+
/** Provider-qualified resolvable substitutes, closest first. */
|
|
110
|
+
alternatives: string[];
|
|
111
|
+
/** False when the PROVIDER itself is unknown to the runtime. */
|
|
112
|
+
providerKnown: boolean;
|
|
113
|
+
waitedMs: number;
|
|
114
|
+
} | {
|
|
115
|
+
kind: 'skipped';
|
|
116
|
+
reason: string;
|
|
117
|
+
};
|
|
118
|
+
export interface AwaitModelResolvableOptions {
|
|
119
|
+
/** Base URL of the running `opencode serve`. */
|
|
120
|
+
baseUrl: string;
|
|
121
|
+
/** Requested model, provider-qualified or bare (bare → "anthropic"). */
|
|
122
|
+
model: string;
|
|
123
|
+
fetchImpl?: HarnessFetch;
|
|
124
|
+
/**
|
|
125
|
+
* Total budget to wait for the model to become resolvable. OpenCode's
|
|
126
|
+
* models.dev refresh runs in the BACKGROUND at boot, so the catalog can be
|
|
127
|
+
* bundled-only for the first seconds after the server answers HTTP; polling
|
|
128
|
+
* bridges that window instead of failing a run the refresh would have fixed.
|
|
129
|
+
*/
|
|
130
|
+
timeoutMs?: number;
|
|
131
|
+
pollMs?: number;
|
|
132
|
+
sleep?: (ms: number) => Promise<void>;
|
|
133
|
+
now?: () => number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Decide whether the requested model is resolvable by the RUNNING opencode,
|
|
137
|
+
* polling briefly so the boot-time models.dev refresh has time to land.
|
|
138
|
+
* Fail-open on validator infrastructure, with a DELIBERATE asymmetry on null
|
|
139
|
+
* catalog reads:
|
|
140
|
+
* - null BEFORE any successful read → `skipped` immediately (dead/absent
|
|
141
|
+
* endpoint; opencode's own `session.error` remains the backstop, and the
|
|
142
|
+
* early exit keeps absent-endpoint runs — and tests — cheap);
|
|
143
|
+
* - null AFTER at least one successful read → a transient boot-window blip
|
|
144
|
+
* (5xx/drop): sleep and keep polling within the budget; a budget that
|
|
145
|
+
* ends in that state returns `skipped`, NEVER a false `unresolvable` off
|
|
146
|
+
* a flaky read.
|
|
147
|
+
* Only a HEALTHY catalog that never lists the model within the budget yields
|
|
148
|
+
* `unresolvable`, with ranked, provider-qualified alternatives.
|
|
149
|
+
*/
|
|
150
|
+
export declare function awaitOpenCodeModelResolvable(opts: AwaitModelResolvableOptions): Promise<ModelValidationOutcome>;
|
|
151
|
+
export interface OpenCodeConfigOptions {
|
|
152
|
+
/** Provider-qualified model string, e.g. "anthropic/claude-opus-4-8". */
|
|
153
|
+
model: string;
|
|
154
|
+
/** OpenCode permission map; defaults to allow-all EXCEPT the headless-denied
|
|
155
|
+
* tools below (sandbox is the boundary for everything else). */
|
|
156
|
+
permission?: Record<string, unknown>;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Tools DENIED in a hosted headless run (ALI-1037). OpenCode exposes a `question`
|
|
160
|
+
* permission key (docs/permissions) that gates the interactive "ask the user a
|
|
161
|
+
* question" tool. In an unattended sandbox there is no human to answer it, and
|
|
162
|
+
* OpenCode's only external reply endpoint — `POST /session/{id}/permissions/{permID}`
|
|
163
|
+
* — takes a BOOLEAN allow/deny, so it cannot select one of the tool's multiple
|
|
164
|
+
* options anyway. Denying the tool makes the model's ask fail fast (surfaced back
|
|
165
|
+
* to it as a tool error) so it proceeds autonomously per the standing preamble
|
|
166
|
+
* (HOSTED_TASK_PREAMBLE), instead of blocking the run forever. This is the
|
|
167
|
+
* documented fallback path in ALI-1037 (answer-externally is not cleanly possible).
|
|
168
|
+
*/
|
|
169
|
+
export declare const HEADLESS_DENIED_TOOLS: readonly ["question"];
|
|
170
|
+
/** The default headless permission map: allow everything, deny the interactive
|
|
171
|
+
* question tool (see HEADLESS_DENIED_TOOLS). */
|
|
172
|
+
export declare function defaultHeadlessPermission(): Record<string, unknown>;
|
|
173
|
+
/**
|
|
174
|
+
* Build the `OPENCODE_CONFIG_CONTENT` payload OpenCode reads at boot (mirrors
|
|
175
|
+
* OpenInspect's `start_opencode`). Kept pure so the launch command and the
|
|
176
|
+
* config are independently testable.
|
|
177
|
+
*/
|
|
178
|
+
export declare function buildOpenCodeConfigContent(opts: OpenCodeConfigOptions): string;
|
|
179
|
+
export interface SpawnOpenCodeOptions extends OpenCodeConfigOptions {
|
|
180
|
+
port?: number;
|
|
181
|
+
/** Directory OpenCode runs in (the cloned workspace). */
|
|
182
|
+
cwd?: string;
|
|
183
|
+
/** Extra env merged over the config-content injection. */
|
|
184
|
+
env?: Record<string, string>;
|
|
185
|
+
/** Injectable spawner (defaults to Bun.spawn) so this stays unit-testable. */
|
|
186
|
+
spawn?: (cmd: string[], opts: {
|
|
187
|
+
cwd?: string;
|
|
188
|
+
env: Record<string, string>;
|
|
189
|
+
logPath?: string;
|
|
190
|
+
}) => {
|
|
191
|
+
kill: () => void;
|
|
192
|
+
};
|
|
193
|
+
/** File the spawner should append opencode's stdout/stderr to. On readiness
|
|
194
|
+
* timeout its tail is included in the thrown error — the bare "fetch failed"
|
|
195
|
+
* this replaces was undiagnosable (ALI-1034). */
|
|
196
|
+
logPath?: string;
|
|
197
|
+
/** Overall readiness budget. OpenCode's FIRST boot runs a one-time sqlite
|
|
198
|
+
* migration ("may take a few minutes") before it listens, so this must
|
|
199
|
+
* comfortably cover a cold snapshot. */
|
|
200
|
+
readyTimeoutMs?: number;
|
|
201
|
+
/** Poll interval while waiting for the server to answer. */
|
|
202
|
+
readyPollMs?: number;
|
|
203
|
+
/** Abort (e.g. run cancelled) while still waiting for readiness. */
|
|
204
|
+
signal?: AbortSignal;
|
|
205
|
+
/** Injectable readiness probe (defaults to global fetch) for tests. */
|
|
206
|
+
fetchImpl?: typeof fetch;
|
|
207
|
+
}
|
|
208
|
+
export interface SpawnedOpenCode {
|
|
209
|
+
baseUrl: string;
|
|
210
|
+
port: number;
|
|
211
|
+
stop: () => void;
|
|
212
|
+
/** How long the server took to answer its first request (set by the real
|
|
213
|
+
* spawnOpenCode; injected test spawners may omit it). */
|
|
214
|
+
readyAfterMs?: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Launch `opencode serve` locally and WAIT until it answers HTTP. OpenCode's
|
|
218
|
+
* first boot performs a one-time sqlite migration before it listens; returning
|
|
219
|
+
* the baseUrl without waiting raced the harness's first `POST /session` into a
|
|
220
|
+
* connection-refused "fetch failed" (ALI-1034, found live in QA-3). Any HTTP
|
|
221
|
+
* status counts as ready — we only need the socket to be accepting. Process
|
|
222
|
+
* supervision is otherwise intentionally minimal for this slice; the pinned
|
|
223
|
+
* version (`OPENCODE_PINNED_VERSION`) is the one the sandbox image must install
|
|
224
|
+
* (see the SSE-fragility note on that constant).
|
|
225
|
+
*/
|
|
226
|
+
export declare function spawnOpenCode(opts: SpawnOpenCodeOptions): Promise<SpawnedOpenCode>;
|
|
227
|
+
//# sourceMappingURL=hosted-harness-opencode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-harness-opencode.d.ts","sourceRoot":"","sources":["../src/hosted-harness-opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHarness seam (ALI-926 / P3.4, interface 1 of ADR-005's swappability
|
|
3
|
+
* contract; sketch in audit §4a).
|
|
4
|
+
*
|
|
5
|
+
* `AgentHarness` abstracts "what starts and steps the agent" so the OpenCode
|
|
6
|
+
* driver (`hosted-harness-opencode.ts`) can be swapped for a Claude-Agent-SDK
|
|
7
|
+
* loop (ALI-929) WITHOUT touching the event bridge (`hosted-run-event-sink.ts`)
|
|
8
|
+
* or the RunAPI. The interface therefore carries ZERO OpenCode types: it speaks
|
|
9
|
+
* only in normalized `HarnessEvent`s whose `kind` is the audit §4b left column.
|
|
10
|
+
*
|
|
11
|
+
* Shape ported (in DESIGN, not code) from OpenInspect's `bridge.py`:
|
|
12
|
+
* create session → send prompt → stream parts → detect idle → stop.
|
|
13
|
+
* The OpenCode-specific SSE part schema, message-id correlation, and localhost
|
|
14
|
+
* HTTP driving live entirely behind this seam.
|
|
15
|
+
*
|
|
16
|
+
* Terminal semantics: `execution_complete` and `error` are emitted as ordinary
|
|
17
|
+
* stream events with `critical: true`. The bridge (§4b) routes those to the
|
|
18
|
+
* RunAPI terminal PATCH — they are NEVER appended as raw run events. The harness
|
|
19
|
+
* does not know about run status; "Orizu records win" (the disagreement rule) is
|
|
20
|
+
* enforced by the sink, not here.
|
|
21
|
+
*/
|
|
22
|
+
/** Minimal MCP server descriptor. Kept structural so the seam does not depend on
|
|
23
|
+
* any concrete MCP client type; the driver interprets it. */
|
|
24
|
+
export interface McpServerConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
/** Transport hint the driver understands (e.g. 'stdio' | 'http'). */
|
|
27
|
+
transport?: string;
|
|
28
|
+
command?: string;
|
|
29
|
+
args?: readonly string[];
|
|
30
|
+
url?: string;
|
|
31
|
+
env?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
export interface HarnessStartOptions {
|
|
34
|
+
/** Absolute or repo-relative working directory the agent operates in. */
|
|
35
|
+
workspaceDir: string;
|
|
36
|
+
/** Provider-qualified model, e.g. "anthropic/claude-opus-4-8". */
|
|
37
|
+
model: string;
|
|
38
|
+
reasoningEffort?: string;
|
|
39
|
+
mcpServers?: readonly McpServerConfig[];
|
|
40
|
+
/** OpenCode session id persisted across restarts (reconnect/resume). */
|
|
41
|
+
resumeAgentSessionId?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface HarnessPrompt {
|
|
44
|
+
runId: string;
|
|
45
|
+
/** Idempotency / correlation key handed by the caller. */
|
|
46
|
+
messageId: string;
|
|
47
|
+
content: string;
|
|
48
|
+
/** Git identity used for commit attribution during the prompt. */
|
|
49
|
+
author: {
|
|
50
|
+
name: string;
|
|
51
|
+
email: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The normalized event vocabulary — the audit §4b left column. The bridge maps
|
|
56
|
+
* each kind onto a `workbench_run_events.event_type` (or drops it). Kept as a
|
|
57
|
+
* const map (no enums, per project guidelines).
|
|
58
|
+
*
|
|
59
|
+
* `push_complete` / `push_error` are deliberately OMITTED: they belong to the
|
|
60
|
+
* finish-branch / manifest push flow deferred to P3.5. `heartbeat` and
|
|
61
|
+
* `user_message` are not modeled at all — the harness never emits liveness or
|
|
62
|
+
* prompt-echo events (§4b drops both).
|
|
63
|
+
*/
|
|
64
|
+
export declare const HARNESS_EVENT_KINDS: {
|
|
65
|
+
readonly ready: "ready";
|
|
66
|
+
readonly token: "token";
|
|
67
|
+
readonly tool_call: "tool_call";
|
|
68
|
+
readonly tool_result: "tool_result";
|
|
69
|
+
readonly step_start: "step_start";
|
|
70
|
+
readonly step_finish: "step_finish";
|
|
71
|
+
readonly git_sync: "git_sync";
|
|
72
|
+
readonly artifact: "artifact";
|
|
73
|
+
readonly session_title: "session_title";
|
|
74
|
+
readonly egress_blocked: "egress_blocked";
|
|
75
|
+
readonly egress_allowed: "egress_allowed";
|
|
76
|
+
readonly work_persisted: "work_persisted";
|
|
77
|
+
readonly work_none: "work_none";
|
|
78
|
+
readonly work_persist_failed: "work_persist_failed";
|
|
79
|
+
readonly question_auto_answered: "question_auto_answered";
|
|
80
|
+
readonly error: "error";
|
|
81
|
+
readonly execution_complete: "execution_complete";
|
|
82
|
+
};
|
|
83
|
+
export type HarnessEventKind = keyof typeof HARNESS_EVENT_KINDS;
|
|
84
|
+
/** The two kinds that must NEVER be appended as raw events — they drive the
|
|
85
|
+
* terminal PATCH instead (§4b mapping rule). */
|
|
86
|
+
export declare const HARNESS_TERMINAL_KINDS: {
|
|
87
|
+
readonly execution_complete: "execution_complete";
|
|
88
|
+
readonly error: "error";
|
|
89
|
+
};
|
|
90
|
+
export declare function isHarnessTerminalKind(kind: HarnessEventKind): boolean;
|
|
91
|
+
export interface HarnessEvent {
|
|
92
|
+
kind: HarnessEventKind;
|
|
93
|
+
/** Correlation key for coalescing (token snapshots) and attribution. */
|
|
94
|
+
messageId?: string;
|
|
95
|
+
payload: Record<string, unknown>;
|
|
96
|
+
/** Requires guaranteed delivery (execution_complete / error / push_*). */
|
|
97
|
+
critical?: boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface AgentHarness {
|
|
100
|
+
/** Stable driver name — "opencode" | "claude-agent-sdk". */
|
|
101
|
+
readonly name: string;
|
|
102
|
+
/** Boot the agent process/session; returns the resumable agent session id. */
|
|
103
|
+
start(opts: HarnessStartOptions): Promise<{
|
|
104
|
+
agentSessionId: string;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Drive one prompt to completion, yielding normalized events. Cancellable via
|
|
108
|
+
* `signal` (abort → the driver requests the agent to stop and ends the stream
|
|
109
|
+
* with a terminal event). The stream ALWAYS ends with exactly one terminal
|
|
110
|
+
* event (`execution_complete` or `error`).
|
|
111
|
+
*/
|
|
112
|
+
runPrompt(prompt: HarnessPrompt, signal: AbortSignal): AsyncIterable<HarnessEvent>;
|
|
113
|
+
/** Abort the in-flight prompt (e.g. OpenCode POST /session/{id}/abort). */
|
|
114
|
+
stop(): Promise<void>;
|
|
115
|
+
/** Release all resources; the harness is unusable afterward. */
|
|
116
|
+
shutdown(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=hosted-harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-harness.d.ts","sourceRoot":"","sources":["../src/hosted-harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;8DAC8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IACvC,wEAAwE;IACxE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACxC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;CA4BtB,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,mBAAmB,CAAA;AAE/D;iDACiD;AACjD,eAAO,MAAM,sBAAsB;;;CAGzB,CAAA;AAEV,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAErE;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAA;IACtB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,KAAK,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACrE;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC,YAAY,CAAC,CAAA;IAClF,2EAA2E;IAC3E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,gEAAgE;IAChE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-harvest (ALI-1036) — deterministic, loop-side durability.
|
|
3
|
+
*
|
|
4
|
+
* At the END of a hosted run (BOTH the success and failure paths — a failed
|
|
5
|
+
* run's partial work is still valuable), the in-sandbox loop calls
|
|
6
|
+
* `harvestWorkspace` to make sure nothing the agent produced is lost:
|
|
7
|
+
*
|
|
8
|
+
* 1. `git status --porcelain` (plumbing) decides if the clone is dirty. Ignored
|
|
9
|
+
* files never appear in porcelain output, so a checkpoint is skipped when the
|
|
10
|
+
* only changes are ignored files.
|
|
11
|
+
* 2. If dirty: `git add -A` then `git commit` attributed to the agent identity
|
|
12
|
+
* (AGENT_GIT_IDENTITY) with message `checkpoint: run <runId> auto-harvest`.
|
|
13
|
+
* 3. If there is anything unpushed (a fresh checkpoint OR pre-existing local
|
|
14
|
+
* commits the agent made but did not push), `git push` to the session branch
|
|
15
|
+
* (the repo-local credential helper configured by bootstrap authorizes it).
|
|
16
|
+
* 4. Return a typed outcome the loop records as a run event: `work_persisted`
|
|
17
|
+
* (sha + files), `work_none` (clean, nothing to push), or `work_persist_failed`
|
|
18
|
+
* (error). Harvest NEVER throws — a harvest failure must not change the run's
|
|
19
|
+
* terminal status; it is recorded and the run proceeds to terminal.
|
|
20
|
+
*
|
|
21
|
+
* The loop runs IN the sandbox, so git is driven with `child_process` (matching
|
|
22
|
+
* `defaultRunSetupHook` / `installOpenCodePinned` in `hosted-loop.ts`). `exec` is
|
|
23
|
+
* injectable so the whole thing is unit-testable with no real git.
|
|
24
|
+
*/
|
|
25
|
+
export interface HarvestExecResult {
|
|
26
|
+
exitCode: number;
|
|
27
|
+
stdout: string;
|
|
28
|
+
stderr: string;
|
|
29
|
+
}
|
|
30
|
+
/** Injectable git runner. Defaults to a `spawnSync` git in the workspace dir. */
|
|
31
|
+
export type HarvestExec = (args: readonly string[]) => HarvestExecResult;
|
|
32
|
+
export interface HarvestOptions {
|
|
33
|
+
/** Directory of the cloned session-branch workspace (the loop's cwd for git). */
|
|
34
|
+
workspaceDir: string;
|
|
35
|
+
runId: string;
|
|
36
|
+
/** Commit attribution (AGENT_GIT_IDENTITY from hosted-runtime-assets). */
|
|
37
|
+
author: {
|
|
38
|
+
name: string;
|
|
39
|
+
email: string;
|
|
40
|
+
};
|
|
41
|
+
/** Injectable git runner (default: real `git` via child_process). */
|
|
42
|
+
exec?: HarvestExec;
|
|
43
|
+
/**
|
|
44
|
+
* Safety gate (review finding #1): the default exec runs REAL `git add/commit/
|
|
45
|
+
* push` in `workspaceDir`. Real harvest must be affirmatively enabled — the loop
|
|
46
|
+
* only enables it inside a genuine hosted sandbox (prebaked marker present). When
|
|
47
|
+
* false AND no `exec` is injected, harvest is a no-op (`work_none`) so an errant
|
|
48
|
+
* workspaceDir (a test, a dev run) can never commit/push a host repo. An injected
|
|
49
|
+
* `exec` always wins (tests that DO drive harvest).
|
|
50
|
+
*/
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export type HarvestOutcome = {
|
|
54
|
+
kind: 'work_persisted';
|
|
55
|
+
sha: string;
|
|
56
|
+
files: string[];
|
|
57
|
+
} | {
|
|
58
|
+
kind: 'work_none';
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'work_persist_failed';
|
|
61
|
+
error: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Persist any uncommitted or unpushed work in the workspace. Deterministic and
|
|
65
|
+
* total: every path returns a `HarvestOutcome`, never throws.
|
|
66
|
+
*/
|
|
67
|
+
export declare function harvestWorkspace(opts: HarvestOptions): HarvestOutcome;
|
|
68
|
+
//# sourceMappingURL=hosted-harvest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosted-harvest.d.ts","sourceRoot":"","sources":["../src/hosted-harvest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,iBAAiB,CAAA;AAExE,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,qEAAqE;IACrE,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAoClD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,cAAc,CAiErE"}
|