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.
Files changed (260) hide show
  1. package/dist/archive-artifacts-cli.d.ts +50 -0
  2. package/dist/archive-artifacts-cli.d.ts.map +1 -0
  3. package/dist/artifact-archive.d.ts +48 -0
  4. package/dist/artifact-archive.d.ts.map +1 -0
  5. package/dist/artifact-pull.d.ts +22 -0
  6. package/dist/artifact-pull.d.ts.map +1 -0
  7. package/dist/banner.d.ts +3 -0
  8. package/dist/banner.d.ts.map +1 -0
  9. package/dist/connectors-cli.d.ts +31 -0
  10. package/dist/connectors-cli.d.ts.map +1 -0
  11. package/dist/credentials.d.ts +46 -0
  12. package/dist/credentials.d.ts.map +1 -0
  13. package/dist/csv.d.ts +49 -0
  14. package/dist/csv.d.ts.map +1 -0
  15. package/dist/dataset-download.d.ts +6 -0
  16. package/dist/dataset-download.d.ts.map +1 -0
  17. package/dist/dataset-edit-rows.d.ts +10 -0
  18. package/dist/dataset-edit-rows.d.ts.map +1 -0
  19. package/dist/dataset-upload-snapshot.d.ts +13 -0
  20. package/dist/dataset-upload-snapshot.d.ts.map +1 -0
  21. package/dist/daytona-slice-rehearsal.d.ts +89 -0
  22. package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
  23. package/dist/daytona-slice.d.ts +106 -0
  24. package/dist/daytona-slice.d.ts.map +1 -0
  25. package/dist/deploy-key-merge-core.d.ts +121 -0
  26. package/dist/deploy-key-merge-core.d.ts.map +1 -0
  27. package/dist/diff-comments-cli.d.ts +12 -0
  28. package/dist/diff-comments-cli.d.ts.map +1 -0
  29. package/dist/diff-comments-cli.js +3 -0
  30. package/dist/egress-allowlist-cli.d.ts +23 -0
  31. package/dist/egress-allowlist-cli.d.ts.map +1 -0
  32. package/dist/egress-policy.d.ts +108 -0
  33. package/dist/egress-policy.d.ts.map +1 -0
  34. package/dist/error-response.d.ts +9 -0
  35. package/dist/error-response.d.ts.map +1 -0
  36. package/dist/file-parser.d.ts +5 -0
  37. package/dist/file-parser.d.ts.map +1 -0
  38. package/dist/gepa-engine-dispatch.d.ts +9 -0
  39. package/dist/gepa-engine-dispatch.d.ts.map +1 -0
  40. package/dist/gepa-engine-dispatch.js +48 -2
  41. package/dist/gepa-python-command.d.ts +6 -0
  42. package/dist/gepa-python-command.d.ts.map +1 -0
  43. package/dist/gepa-python-paths.d.ts +6 -0
  44. package/dist/gepa-python-paths.d.ts.map +1 -0
  45. package/dist/gepa-python-paths.js +6 -3
  46. package/dist/git-credential.d.ts +69 -0
  47. package/dist/git-credential.d.ts.map +1 -0
  48. package/dist/git-recovery-environment.d.ts +31 -0
  49. package/dist/git-recovery-environment.d.ts.map +1 -0
  50. package/dist/github-setup.d.ts +122 -0
  51. package/dist/github-setup.d.ts.map +1 -0
  52. package/dist/global-flags.d.ts +12 -0
  53. package/dist/global-flags.d.ts.map +1 -0
  54. package/dist/help.d.ts +46 -0
  55. package/dist/help.d.ts.map +1 -0
  56. package/dist/help.js +91 -23
  57. package/dist/hosted-boot.d.ts +273 -0
  58. package/dist/hosted-boot.d.ts.map +1 -0
  59. package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
  60. package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
  61. package/dist/hosted-bootstrap.d.ts +188 -0
  62. package/dist/hosted-bootstrap.d.ts.map +1 -0
  63. package/dist/hosted-broker-server.d.ts +50 -0
  64. package/dist/hosted-broker-server.d.ts.map +1 -0
  65. package/dist/hosted-git-http-server.d.ts +36 -0
  66. package/dist/hosted-git-http-server.d.ts.map +1 -0
  67. package/dist/hosted-harness-claude.d.ts +195 -0
  68. package/dist/hosted-harness-claude.d.ts.map +1 -0
  69. package/dist/hosted-harness-opencode.d.ts +227 -0
  70. package/dist/hosted-harness-opencode.d.ts.map +1 -0
  71. package/dist/hosted-harness.d.ts +118 -0
  72. package/dist/hosted-harness.d.ts.map +1 -0
  73. package/dist/hosted-harvest.d.ts +68 -0
  74. package/dist/hosted-harvest.d.ts.map +1 -0
  75. package/dist/hosted-headless.d.ts +72 -0
  76. package/dist/hosted-headless.d.ts.map +1 -0
  77. package/dist/hosted-loop-lifecycle.d.ts +206 -0
  78. package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
  79. package/dist/hosted-loop.d.ts +170 -0
  80. package/dist/hosted-loop.d.ts.map +1 -0
  81. package/dist/hosted-run-event-sink.d.ts +222 -0
  82. package/dist/hosted-run-event-sink.d.ts.map +1 -0
  83. package/dist/hosted-runtime-assets.d.ts +186 -0
  84. package/dist/hosted-runtime-assets.d.ts.map +1 -0
  85. package/dist/hosted-session-cli.d.ts +258 -0
  86. package/dist/hosted-session-cli.d.ts.map +1 -0
  87. package/dist/hosted-skill-staging.d.ts +73 -0
  88. package/dist/hosted-skill-staging.d.ts.map +1 -0
  89. package/dist/http.d.ts +8 -0
  90. package/dist/http.d.ts.map +1 -0
  91. package/dist/index.d.ts +22 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +88 -92
  94. package/dist/instruction-set-gepa-launch.d.ts +3 -0
  95. package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
  96. package/dist/instruction-set-gepa-launch.js +15 -0
  97. package/dist/instruction-set-loader.d.ts +6 -0
  98. package/dist/instruction-set-loader.d.ts.map +1 -0
  99. package/dist/instruction-set-loader.js +59 -0
  100. package/dist/instruction-set-manifest.d.ts +14 -0
  101. package/dist/instruction-set-manifest.d.ts.map +1 -0
  102. package/dist/instruction-set-manifest.js +162 -0
  103. package/dist/instruction-sets-cli.d.ts +40 -0
  104. package/dist/instruction-sets-cli.d.ts.map +1 -0
  105. package/dist/instruction-sets-cli.js +361 -0
  106. package/dist/json-response.d.ts +4 -0
  107. package/dist/json-response.d.ts.map +1 -0
  108. package/dist/jsonl-stream.d.ts +8 -0
  109. package/dist/jsonl-stream.d.ts.map +1 -0
  110. package/dist/kill-switch-cli.d.ts +30 -0
  111. package/dist/kill-switch-cli.d.ts.map +1 -0
  112. package/dist/loopback-serve.d.ts +25 -0
  113. package/dist/loopback-serve.d.ts.map +1 -0
  114. package/dist/manifests-cli.d.ts +22 -0
  115. package/dist/manifests-cli.d.ts.map +1 -0
  116. package/dist/markdown-report-input.d.ts +7 -0
  117. package/dist/markdown-report-input.d.ts.map +1 -0
  118. package/dist/merge-job.d.ts +155 -0
  119. package/dist/merge-job.d.ts.map +1 -0
  120. package/dist/model-configs-cli.d.ts +19 -0
  121. package/dist/model-configs-cli.d.ts.map +1 -0
  122. package/dist/model-configs-cli.js +104 -0
  123. package/dist/optimization-export-cli.d.ts +19 -0
  124. package/dist/optimization-export-cli.d.ts.map +1 -0
  125. package/dist/optimizations-list-cli.d.ts +20 -0
  126. package/dist/optimizations-list-cli.d.ts.map +1 -0
  127. package/dist/optimizations-promote-cli.d.ts +8 -0
  128. package/dist/optimizations-promote-cli.d.ts.map +1 -0
  129. package/dist/optimizations-promote-cli.js +34 -0
  130. package/dist/option-validation.d.ts +15 -0
  131. package/dist/option-validation.d.ts.map +1 -0
  132. package/dist/preview-runtime.d.ts +63 -0
  133. package/dist/preview-runtime.d.ts.map +1 -0
  134. package/dist/prompt-draft-push.d.ts +60 -0
  135. package/dist/prompt-draft-push.d.ts.map +1 -0
  136. package/dist/prompt-label-cli.d.ts +11 -0
  137. package/dist/prompt-label-cli.d.ts.map +1 -0
  138. package/dist/prompt-label-cli.js +29 -0
  139. package/dist/prompt-length-wire.d.ts +28 -0
  140. package/dist/prompt-length-wire.d.ts.map +1 -0
  141. package/dist/prompt-report-cli.d.ts +10 -0
  142. package/dist/prompt-report-cli.d.ts.map +1 -0
  143. package/dist/prompt-summary-table.d.ts +15 -0
  144. package/dist/prompt-summary-table.d.ts.map +1 -0
  145. package/dist/prompt-summary-table.js +6 -0
  146. package/dist/registration-tag-warning.d.ts +32 -0
  147. package/dist/registration-tag-warning.d.ts.map +1 -0
  148. package/dist/report-comments-cli.d.ts +16 -0
  149. package/dist/report-comments-cli.d.ts.map +1 -0
  150. package/dist/report-markdown-blankness.d.ts +2 -0
  151. package/dist/report-markdown-blankness.d.ts.map +1 -0
  152. package/dist/report-replacement-warning.d.ts +17 -0
  153. package/dist/report-replacement-warning.d.ts.map +1 -0
  154. package/dist/repository-backup-core.d.ts +225 -0
  155. package/dist/repository-backup-core.d.ts.map +1 -0
  156. package/dist/repository-recovery-manifest.d.ts +83 -0
  157. package/dist/repository-recovery-manifest.d.ts.map +1 -0
  158. package/dist/run-event-digest.d.ts +74 -0
  159. package/dist/run-event-digest.d.ts.map +1 -0
  160. package/dist/runner-dir-verify.d.ts +66 -0
  161. package/dist/runner-dir-verify.d.ts.map +1 -0
  162. package/dist/runner-instruction-set-contract.d.ts +24 -0
  163. package/dist/runner-instruction-set-contract.d.ts.map +1 -0
  164. package/dist/runner-instruction-set-contract.js +50 -0
  165. package/dist/sandbox-hygiene.d.ts +45 -0
  166. package/dist/sandbox-hygiene.d.ts.map +1 -0
  167. package/dist/sandbox-provider.d.ts +193 -0
  168. package/dist/sandbox-provider.d.ts.map +1 -0
  169. package/dist/scorer-draft-push.d.ts +50 -0
  170. package/dist/scorer-draft-push.d.ts.map +1 -0
  171. package/dist/scorers-cli.d.ts +20 -0
  172. package/dist/scorers-cli.d.ts.map +1 -0
  173. package/dist/scorers-cli.js +22 -0
  174. package/dist/scorers-verify-parity-bridge.d.ts +16 -0
  175. package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
  176. package/dist/scorers-verify-parity-bridge.js +90 -0
  177. package/dist/scorers-verify-parity.d.ts +81 -0
  178. package/dist/scorers-verify-parity.d.ts.map +1 -0
  179. package/dist/scorers-verify-parity.js +502 -0
  180. package/dist/scores-accept-cli.d.ts +21 -0
  181. package/dist/scores-accept-cli.d.ts.map +1 -0
  182. package/dist/secret-redaction.d.ts +47 -0
  183. package/dist/secret-redaction.d.ts.map +1 -0
  184. package/dist/session-timeline.d.ts +54 -0
  185. package/dist/session-timeline.d.ts.map +1 -0
  186. package/dist/setup-prompt.d.ts +5 -0
  187. package/dist/setup-prompt.d.ts.map +1 -0
  188. package/dist/skill-installer.d.ts +68 -0
  189. package/dist/skill-installer.d.ts.map +1 -0
  190. package/dist/skilled-proposer-launch.d.ts +7 -0
  191. package/dist/skilled-proposer-launch.d.ts.map +1 -0
  192. package/dist/skilled-proposer-launch.js +60 -0
  193. package/dist/smoke.d.ts +102 -0
  194. package/dist/smoke.d.ts.map +1 -0
  195. package/dist/start-intent.d.ts +12 -0
  196. package/dist/start-intent.d.ts.map +1 -0
  197. package/dist/task-assignment-manifest.d.ts +15 -0
  198. package/dist/task-assignment-manifest.d.ts.map +1 -0
  199. package/dist/task-create-error.d.ts +18 -0
  200. package/dist/task-create-error.d.ts.map +1 -0
  201. package/dist/team-connectors-cli.d.ts +29 -0
  202. package/dist/team-connectors-cli.d.ts.map +1 -0
  203. package/dist/types.d.ts +39 -0
  204. package/dist/types.d.ts.map +1 -0
  205. package/dist/vercel-sandbox-provider.d.ts +156 -0
  206. package/dist/vercel-sandbox-provider.d.ts.map +1 -0
  207. package/dist/workbench-cli.d.ts +233 -0
  208. package/dist/workbench-cli.d.ts.map +1 -0
  209. package/dist/workspace-sync.d.ts +207 -0
  210. package/dist/workspace-sync.d.ts.map +1 -0
  211. package/dist/workspace.d.ts +92 -0
  212. package/dist/workspace.d.ts.map +1 -0
  213. package/dist/zip-draft-push.d.ts +55 -0
  214. package/dist/zip-draft-push.d.ts.map +1 -0
  215. package/package.json +14 -1
  216. package/requirements/README.md +138 -0
  217. package/requirements/skilled-proposer.in +66 -0
  218. package/requirements/skilled-proposer.lock +265 -0
  219. package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
  220. package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
  221. package/src/diff-comments-cli.ts +4 -0
  222. package/src/gepa-engine-dispatch.ts +42 -2
  223. package/src/gepa-python-paths.ts +6 -3
  224. package/src/help.ts +93 -23
  225. package/src/index.ts +99 -126
  226. package/src/instruction-set-gepa-launch.ts +28 -0
  227. package/src/instruction-set-loader.ts +37 -0
  228. package/src/instruction-set-manifest.ts +120 -0
  229. package/src/instruction-sets-cli.ts +306 -0
  230. package/src/model-configs-cli.ts +119 -0
  231. package/src/optimizations-promote-cli.ts +41 -0
  232. package/src/prompt-label-cli.ts +55 -0
  233. package/src/prompt-summary-table.ts +12 -0
  234. package/src/runner-instruction-set-contract.ts +64 -0
  235. package/src/scorers-cli.ts +40 -0
  236. package/src/scorers-verify-parity-bridge.ts +90 -0
  237. package/src/scorers-verify-parity.ts +564 -0
  238. package/src/skilled-proposer-launch.ts +89 -0
  239. package/src/skilled-proposer-venv-manager.mjs +757 -0
  240. package/tsconfig.json +2 -0
  241. package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +2 -1
  242. package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +8 -2
  243. package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +17 -3
  244. package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +100 -8
  245. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
  246. package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +92 -12
  247. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +100 -22
  248. package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
  249. package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +17 -0
  250. package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +2 -1
  251. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
  252. package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +4 -1
  253. package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
  254. package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
  255. package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
  256. package/vendor/skills/orizu-cli/SKILL.md +2 -0
  257. package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
  258. package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
  259. package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +25 -0
  260. 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAIH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,qBAAqB,CAAA;AAS5B,eAAO,MAAM,uBAAuB,YAAY,CAAA;AAUhD,eAAO,MAAM,8BAA8B,UAAY,CAAA;AAQvD,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEjF,MAAM,WAAW,sBAAsB;IACrC,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;wEACwE;AACxE,eAAO,MAAM,yBAAyB,iCAAiC,CAAA;AAuDvE;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAGzF;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCzB;AA2GD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,YAAY,CA2bnF;AA8BD,yEAAyE;AACzE,MAAM,MAAM,oBAAoB,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAA;AAIzE;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,YAA+C,EAC1D,SAAS,GAAE,MAAyC,GACnD,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuBtC;AASD;;;oEAGoE;AACpE,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,SAAI,GACR,MAAM,EAAE,CAOV;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACxC;IACE,IAAI,EAAE,cAAc,CAAA;IACpB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,gEAAgE;IAChE,aAAa,EAAE,OAAO,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;CACjB,GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC,MAAM,WAAW,2BAA2B;IAC1C,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,sBAAsB,CAAC,CAqDjC;AAID,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAA;IACb;qEACiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,uBAAwB,CAAA;AAE1D;iDACiD;AACjD,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAInE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAK9E;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,CACN,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAClE;QAAE,IAAI,EAAE,MAAM,IAAI,CAAA;KAAE,CAAA;IACzB;;sDAEkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;6CAEyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB;8DAC0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAkBD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+DxF"}
@@ -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"}