harnery 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (441) hide show
  1. package/README.md +1 -1
  2. package/dist/commander.js +1 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +25 -19
  5. package/dist/commands/browse-ai.d.ts.map +1 -1
  6. package/dist/commands/browse-ai.js +4 -0
  7. package/dist/commands/browse.d.ts.map +1 -1
  8. package/dist/commands/browse.js +141 -10
  9. package/dist/commands/checkpoint.js +8 -12
  10. package/dist/commands/deinit.d.ts +1 -1
  11. package/dist/commands/deinit.d.ts.map +1 -1
  12. package/dist/commands/deinit.js +8 -4
  13. package/dist/commands/docs.d.ts.map +1 -1
  14. package/dist/commands/docs.js +2 -0
  15. package/dist/commands/doctor.js +10 -2
  16. package/dist/commands/fetch.d.ts.map +1 -1
  17. package/dist/commands/fetch.js +10 -0
  18. package/dist/commands/init.d.ts +19 -3
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +232 -104
  21. package/dist/commands/instructions.js +5 -4
  22. package/dist/commands/ledger-v3.d.ts.map +1 -1
  23. package/dist/commands/ledger-v3.js +2 -1
  24. package/dist/commands/webview.d.ts +4 -0
  25. package/dist/commands/webview.d.ts.map +1 -0
  26. package/dist/commands/webview.js +94 -0
  27. package/dist/core/adapter.d.ts +50 -1
  28. package/dist/core/adapter.d.ts.map +1 -1
  29. package/dist/core/adapter.js +59 -1
  30. package/dist/core/adapters/attestation.d.ts.map +1 -1
  31. package/dist/core/adapters/attestation.js +3 -2
  32. package/dist/core/adapters/profiles.d.ts +20 -3
  33. package/dist/core/adapters/profiles.d.ts.map +1 -1
  34. package/dist/core/adapters/profiles.js +32 -1
  35. package/dist/core/adapters/registry.d.ts.map +1 -1
  36. package/dist/core/adapters/registry.js +33 -0
  37. package/dist/core/agents/canonical-emit.d.ts +1 -1
  38. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  39. package/dist/core/agents/canonical-emit.js +2 -7
  40. package/dist/core/agents/cli.js +18 -16
  41. package/dist/core/agents/coord-client.d.ts.map +1 -1
  42. package/dist/core/agents/coord-client.js +7 -2
  43. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  44. package/dist/core/agents/rules/claim-conflict.js +3 -9
  45. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  46. package/dist/core/agents/rules/commit-conflict.js +2 -8
  47. package/dist/core/agents/rules/stop-hook.d.ts +2 -0
  48. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  49. package/dist/core/agents/rules/stop-hook.js +1 -1
  50. package/dist/core/agents/session-events.d.ts +6 -2
  51. package/dist/core/agents/session-events.d.ts.map +1 -1
  52. package/dist/core/agents/session-events.js +105 -26
  53. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
  54. package/dist/core/agents/session-finalization-state-v3.js +2 -1
  55. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
  56. package/dist/core/agents/session-finalizer-v3.js +5 -4
  57. package/dist/core/agents/session-name-display.d.ts +28 -0
  58. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  59. package/dist/core/agents/session-name-display.js +43 -0
  60. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  61. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  62. package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
  63. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  64. package/dist/core/agents/state/heartbeat-writer.js +21 -2
  65. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  66. package/dist/core/agents/state/live-coordination-view.js +1 -0
  67. package/dist/core/agents/state/live-coordination-writer.js +3 -0
  68. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  69. package/dist/core/agents/state/stale-sweep.js +2 -10
  70. package/dist/core/agents/turn-changes.d.ts +17 -0
  71. package/dist/core/agents/turn-changes.d.ts.map +1 -0
  72. package/dist/core/agents/turn-changes.js +76 -0
  73. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  74. package/dist/core/artifacts/delivery-card.js +2 -1
  75. package/dist/core/artifacts/index.d.ts.map +1 -1
  76. package/dist/core/artifacts/index.js +4 -3
  77. package/dist/core/config.d.ts +5 -0
  78. package/dist/core/config.d.ts.map +1 -1
  79. package/dist/core/config.js +14 -0
  80. package/dist/core/context/index.d.ts.map +1 -1
  81. package/dist/core/context/index.js +2 -1
  82. package/dist/core/conversations/archive.d.ts.map +1 -1
  83. package/dist/core/conversations/archive.js +4 -3
  84. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  85. package/dist/core/diagnostics/bundle.js +3 -2
  86. package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
  87. package/dist/core/events/legacy-storage/canary.js +5 -4
  88. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
  89. package/dist/core/events/legacy-storage/compression.js +2 -1
  90. package/dist/core/events/v3/adapter-id.d.ts +10 -2
  91. package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
  92. package/dist/core/events/v3/adapter-id.js +16 -0
  93. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  94. package/dist/core/events/v3/authority-outbox.js +2 -1
  95. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  96. package/dist/core/events/v3/bootstrap.js +5 -4
  97. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  98. package/dist/core/events/v3/capabilities.js +17 -0
  99. package/dist/core/events/v3/contract.d.ts +5 -0
  100. package/dist/core/events/v3/contract.d.ts.map +1 -1
  101. package/dist/core/events/v3/contract.js +7 -1
  102. package/dist/core/events/v3/control-witness.d.ts.map +1 -1
  103. package/dist/core/events/v3/control-witness.js +3 -2
  104. package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
  105. package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
  106. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
  107. package/dist/core/events/v3/fingerprint-keys.js +7 -6
  108. package/dist/core/events/v3/generated.d.ts +1 -1
  109. package/dist/core/events/v3/generated.js +1 -1
  110. package/dist/core/events/v3/live-feed.d.ts.map +1 -1
  111. package/dist/core/events/v3/live-feed.js +5 -4
  112. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  113. package/dist/core/events/v3/producers/command-recorder.js +8 -7
  114. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  115. package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
  116. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
  117. package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
  118. package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
  119. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  120. package/dist/core/events/v3/producers/hook-base.js +10 -4
  121. package/dist/core/events/v3/producers/hook.d.ts +2 -0
  122. package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
  123. package/dist/core/events/v3/producers/hook.js +16 -1
  124. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  125. package/dist/core/events/v3/producers/intake.js +16 -6
  126. package/dist/core/events/v3/producers/recorder.d.ts +2 -4
  127. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  128. package/dist/core/events/v3/producers/recorder.js +109 -101
  129. package/dist/core/events/v3/recovery.d.ts.map +1 -1
  130. package/dist/core/events/v3/recovery.js +4 -3
  131. package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
  132. package/dist/core/events/v3/support-storage/inventory.js +2 -1
  133. package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
  134. package/dist/core/events/v3/support-storage/maintenance.js +4 -3
  135. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
  136. package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
  137. package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
  138. package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
  139. package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
  140. package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
  141. package/dist/core/events/v3/tool-line-changes.js +135 -0
  142. package/dist/core/events/v3/writer.d.ts.map +1 -1
  143. package/dist/core/events/v3/writer.js +8 -7
  144. package/dist/core/governor/planning.d.ts.map +1 -1
  145. package/dist/core/governor/planning.js +6 -5
  146. package/dist/core/governor/service.d.ts.map +1 -1
  147. package/dist/core/governor/service.js +12 -11
  148. package/dist/core/governor/state.d.ts.map +1 -1
  149. package/dist/core/governor/state.js +7 -6
  150. package/dist/core/guard/evaluator.js +1 -0
  151. package/dist/core/guard/storage.d.ts.map +1 -1
  152. package/dist/core/guard/storage.js +3 -2
  153. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
  154. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
  155. package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
  156. package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
  157. package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
  158. package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
  159. package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
  160. package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
  161. package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
  162. package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
  163. package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
  164. package/dist/core/hooks/adapter/behaviors/index.js +20 -0
  165. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
  166. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
  167. package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
  168. package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
  169. package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
  170. package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
  171. package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
  172. package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
  173. package/dist/core/hooks/adapter/behaviors/types.js +1 -0
  174. package/dist/core/hooks/adapter/detect.d.ts +1 -1
  175. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  176. package/dist/core/hooks/adapter/detect.js +2 -3
  177. package/dist/core/hooks/adapter/events.d.ts +29 -1
  178. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  179. package/dist/core/hooks/adapter/events.js +31 -0
  180. package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
  181. package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
  182. package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
  183. package/dist/core/hooks/adapter/output.d.ts +7 -0
  184. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  185. package/dist/core/hooks/adapter/output.js +8 -1
  186. package/dist/core/hooks/adapter/wiring.d.ts +15 -1
  187. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  188. package/dist/core/hooks/adapter/wiring.js +74 -11
  189. package/dist/core/hooks/cli.js +635 -529
  190. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  191. package/dist/core/hooks/effects/image-capture.js +2 -1
  192. package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
  193. package/dist/core/hooks/prompt-context/contract.js +1 -0
  194. package/dist/core/hooks/resolve/transcript.d.ts +1 -1
  195. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  196. package/dist/core/hooks/resolve/transcript.js +35 -4
  197. package/dist/core/hooks/session-name-presence.d.ts +3 -1
  198. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  199. package/dist/core/hooks/session-name-presence.js +7 -2
  200. package/dist/core/hooks/shell-waiter.d.ts +2 -0
  201. package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
  202. package/dist/core/hooks/shell-waiter.js +173 -0
  203. package/dist/core/resources/service.d.ts.map +1 -1
  204. package/dist/core/resources/service.js +5 -4
  205. package/dist/core/semantic/review.d.ts.map +1 -1
  206. package/dist/core/semantic/review.js +5 -4
  207. package/dist/core/semantic/service.d.ts.map +1 -1
  208. package/dist/core/semantic/service.js +15 -14
  209. package/dist/core/semantic/storage.d.ts.map +1 -1
  210. package/dist/core/semantic/storage.js +5 -4
  211. package/dist/core/storage/atomic-json.d.ts.map +1 -1
  212. package/dist/core/storage/atomic-json.js +7 -3
  213. package/dist/core/storage/durable-history.d.ts.map +1 -1
  214. package/dist/core/storage/durable-history.js +6 -5
  215. package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
  216. package/dist/core/storage/maintenance-providers.js +2 -1
  217. package/dist/core/storage/maintenance.d.ts.map +1 -1
  218. package/dist/core/storage/maintenance.js +7 -6
  219. package/dist/core/storage/metrics.d.ts.map +1 -1
  220. package/dist/core/storage/metrics.js +5 -1
  221. package/dist/core/storage/modes.d.ts +17 -0
  222. package/dist/core/storage/modes.d.ts.map +1 -0
  223. package/dist/core/storage/modes.js +47 -0
  224. package/dist/core/storage/process-log.d.ts.map +1 -1
  225. package/dist/core/storage/process-log.js +3 -2
  226. package/dist/core/storage/receipts.d.ts.map +1 -1
  227. package/dist/core/storage/receipts.js +4 -3
  228. package/dist/core/storage/segments.d.ts.map +1 -1
  229. package/dist/core/storage/segments.js +7 -6
  230. package/dist/core/supervisor/hook-health.d.ts +2 -1
  231. package/dist/core/supervisor/hook-health.d.ts.map +1 -1
  232. package/dist/core/supervisor/hook-health.js +4 -3
  233. package/dist/core/supervisor/service.d.ts.map +1 -1
  234. package/dist/core/supervisor/service.js +7 -6
  235. package/dist/core/work/state.d.ts.map +1 -1
  236. package/dist/core/work/state.js +8 -7
  237. package/dist/core/workflow/adapters.d.ts +7 -0
  238. package/dist/core/workflow/adapters.d.ts.map +1 -1
  239. package/dist/core/workflow/adapters.js +9 -0
  240. package/dist/core/workflow/approvals.d.ts.map +1 -1
  241. package/dist/core/workflow/approvals.js +6 -5
  242. package/dist/core/workflow/billing.d.ts.map +1 -1
  243. package/dist/core/workflow/billing.js +33 -4
  244. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  245. package/dist/core/workflow/durable-record.js +4 -3
  246. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  247. package/dist/core/workflow/live-session-v3.js +2 -3
  248. package/dist/core/workflow/run-state.d.ts.map +1 -1
  249. package/dist/core/workflow/run-state.js +3 -2
  250. package/dist/core/workflow/spawn-opencode.d.ts +36 -0
  251. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
  252. package/dist/core/workflow/spawn-opencode.js +132 -0
  253. package/dist/core/workflow/transcript.d.ts.map +1 -1
  254. package/dist/core/workflow/transcript.js +3 -2
  255. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  256. package/dist/core/workflow/workspaces/leases.js +5 -4
  257. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  258. package/dist/core/workflow/workspaces/local-git.js +3 -2
  259. package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
  260. package/dist/core/workflow/workspaces/paths.js +2 -1
  261. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  262. package/dist/core/workflow/workspaces/state.js +10 -9
  263. package/dist/lazy-commands.js +8 -1
  264. package/dist/lib/agent-browser/client.d.ts +9 -0
  265. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  266. package/dist/lib/agent-browser/client.js +13 -0
  267. package/dist/lib/browser/client.d.ts +35 -1
  268. package/dist/lib/browser/client.d.ts.map +1 -1
  269. package/dist/lib/browser/client.js +117 -8
  270. package/dist/lib/browser/identity.d.ts +25 -0
  271. package/dist/lib/browser/identity.d.ts.map +1 -0
  272. package/dist/lib/browser/identity.js +66 -0
  273. package/dist/lib/browser/index.d.ts +2 -1
  274. package/dist/lib/browser/index.d.ts.map +1 -1
  275. package/dist/lib/browser/index.js +2 -1
  276. package/dist/lib/browser/launch-args.d.ts +26 -0
  277. package/dist/lib/browser/launch-args.d.ts.map +1 -1
  278. package/dist/lib/browser/launch-args.js +41 -1
  279. package/dist/lib/browser/user-agent.d.ts +53 -0
  280. package/dist/lib/browser/user-agent.d.ts.map +1 -0
  281. package/dist/lib/browser/user-agent.js +79 -0
  282. package/dist/lib/docs-metadata-managed.d.ts +1 -0
  283. package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
  284. package/dist/lib/docs-metadata-managed.js +17 -1
  285. package/dist/lib/identities/assume.d.ts.map +1 -1
  286. package/dist/lib/identities/assume.js +4 -9
  287. package/dist/lib/instructions/apply.d.ts.map +1 -1
  288. package/dist/lib/instructions/apply.js +2 -0
  289. package/dist/lib/pace/index.d.ts +2 -0
  290. package/dist/lib/pace/index.d.ts.map +1 -0
  291. package/dist/lib/pace/index.js +1 -0
  292. package/dist/lib/pace/pace.d.ts +95 -0
  293. package/dist/lib/pace/pace.d.ts.map +1 -0
  294. package/dist/lib/pace/pace.js +313 -0
  295. package/dist/lib/tunnel/forward-headers.d.ts +11 -0
  296. package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
  297. package/dist/lib/tunnel/forward-headers.js +17 -0
  298. package/dist/lib/tunnel/gate.js +15 -10
  299. package/dist/lib/webview/index.d.ts +2 -0
  300. package/dist/lib/webview/index.d.ts.map +1 -0
  301. package/dist/lib/webview/index.js +1 -0
  302. package/dist/lib/webview/webview.d.ts +116 -0
  303. package/dist/lib/webview/webview.d.ts.map +1 -0
  304. package/dist/lib/webview/webview.js +289 -0
  305. package/opencode-plugin/package.json +19 -0
  306. package/opencode-plugin/src/index.ts +791 -0
  307. package/package.json +11 -4
  308. package/schemas/config.schema.json +13 -0
  309. package/schemas/event-v3.schema.json +164 -0
  310. package/src/commander.ts +7 -0
  311. package/src/commands/agents.ts +33 -18
  312. package/src/commands/browse-ai.ts +8 -0
  313. package/src/commands/browse.ts +189 -10
  314. package/src/commands/checkpoint.ts +7 -9
  315. package/src/commands/deinit.ts +9 -4
  316. package/src/commands/docs.ts +2 -0
  317. package/src/commands/doctor.ts +11 -2
  318. package/src/commands/fetch.ts +14 -0
  319. package/src/commands/init.ts +281 -119
  320. package/src/commands/instructions.ts +8 -4
  321. package/src/commands/ledger-v3.ts +2 -1
  322. package/src/commands/webview.ts +159 -0
  323. package/src/core/adapter.ts +102 -1
  324. package/src/core/adapters/attestation.ts +3 -2
  325. package/src/core/adapters/profiles.ts +42 -1
  326. package/src/core/adapters/registry.ts +37 -0
  327. package/src/core/agents/canonical-emit.ts +2 -5
  328. package/src/core/agents/cli.ts +18 -17
  329. package/src/core/agents/coord-client.ts +9 -3
  330. package/src/core/agents/rules/claim-conflict.ts +3 -8
  331. package/src/core/agents/rules/commit-conflict.ts +2 -7
  332. package/src/core/agents/rules/stop-hook.ts +3 -1
  333. package/src/core/agents/session-events.ts +120 -26
  334. package/src/core/agents/session-finalization-state-v3.ts +2 -1
  335. package/src/core/agents/session-finalizer-v3.ts +5 -4
  336. package/src/core/agents/session-name-display.ts +48 -0
  337. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  338. package/src/core/agents/state/heartbeat-writer.ts +24 -2
  339. package/src/core/agents/state/live-coordination-view.ts +1 -0
  340. package/src/core/agents/state/live-coordination-writer.ts +3 -0
  341. package/src/core/agents/state/stale-sweep.ts +2 -9
  342. package/src/core/agents/turn-changes.ts +97 -0
  343. package/src/core/artifacts/delivery-card.ts +2 -1
  344. package/src/core/artifacts/index.ts +4 -3
  345. package/src/core/config.ts +23 -0
  346. package/src/core/context/index.ts +2 -1
  347. package/src/core/conversations/archive.ts +4 -3
  348. package/src/core/diagnostics/bundle.ts +3 -2
  349. package/src/core/events/legacy-storage/canary.ts +5 -4
  350. package/src/core/events/legacy-storage/compression.ts +2 -1
  351. package/src/core/events/v3/adapter-id.ts +29 -1
  352. package/src/core/events/v3/authority-outbox.ts +2 -1
  353. package/src/core/events/v3/bootstrap.ts +5 -4
  354. package/src/core/events/v3/capabilities.ts +17 -0
  355. package/src/core/events/v3/contract.ts +9 -1
  356. package/src/core/events/v3/control-witness.ts +3 -2
  357. package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
  358. package/src/core/events/v3/fingerprint-keys.ts +7 -6
  359. package/src/core/events/v3/generated.ts +1 -1
  360. package/src/core/events/v3/live-feed.ts +5 -4
  361. package/src/core/events/v3/producers/command-recorder.ts +8 -6
  362. package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
  363. package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
  364. package/src/core/events/v3/producers/hook-base.ts +11 -4
  365. package/src/core/events/v3/producers/hook.ts +19 -1
  366. package/src/core/events/v3/producers/intake.ts +18 -6
  367. package/src/core/events/v3/producers/recorder.ts +123 -114
  368. package/src/core/events/v3/recovery.ts +4 -3
  369. package/src/core/events/v3/support-storage/inventory.ts +2 -1
  370. package/src/core/events/v3/support-storage/maintenance.ts +8 -3
  371. package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
  372. package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
  373. package/src/core/events/v3/tool-line-changes.ts +139 -0
  374. package/src/core/events/v3/writer.ts +8 -7
  375. package/src/core/governor/planning.ts +6 -5
  376. package/src/core/governor/service.ts +12 -11
  377. package/src/core/governor/state.ts +7 -6
  378. package/src/core/guard/evaluator.ts +1 -0
  379. package/src/core/guard/storage.ts +3 -2
  380. package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
  381. package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
  382. package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
  383. package/src/core/hooks/adapter/behaviors/index.ts +31 -0
  384. package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
  385. package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
  386. package/src/core/hooks/adapter/behaviors/types.ts +110 -0
  387. package/src/core/hooks/adapter/detect.ts +2 -3
  388. package/src/core/hooks/adapter/events.ts +53 -1
  389. package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
  390. package/src/core/hooks/adapter/output.ts +8 -1
  391. package/src/core/hooks/adapter/wiring.ts +85 -11
  392. package/src/core/hooks/cli.ts +733 -537
  393. package/src/core/hooks/effects/image-capture.ts +2 -1
  394. package/src/core/hooks/prompt-context/contract.ts +1 -0
  395. package/src/core/hooks/resolve/transcript.ts +42 -5
  396. package/src/core/hooks/session-name-presence.ts +10 -3
  397. package/src/core/hooks/shell-waiter.ts +166 -0
  398. package/src/core/resources/service.ts +5 -4
  399. package/src/core/semantic/review.ts +5 -4
  400. package/src/core/semantic/service.ts +15 -14
  401. package/src/core/semantic/storage.ts +5 -4
  402. package/src/core/storage/atomic-json.ts +7 -3
  403. package/src/core/storage/durable-history.ts +6 -5
  404. package/src/core/storage/maintenance-providers.ts +2 -1
  405. package/src/core/storage/maintenance.ts +7 -6
  406. package/src/core/storage/metrics.ts +5 -1
  407. package/src/core/storage/modes.ts +53 -0
  408. package/src/core/storage/process-log.ts +3 -2
  409. package/src/core/storage/receipts.ts +4 -3
  410. package/src/core/storage/segments.ts +7 -6
  411. package/src/core/supervisor/hook-health.ts +4 -4
  412. package/src/core/supervisor/service.ts +7 -6
  413. package/src/core/work/state.ts +8 -7
  414. package/src/core/workflow/adapters.ts +15 -0
  415. package/src/core/workflow/approvals.ts +6 -5
  416. package/src/core/workflow/billing.ts +34 -4
  417. package/src/core/workflow/durable-record.ts +4 -3
  418. package/src/core/workflow/live-session-v3.ts +2 -3
  419. package/src/core/workflow/run-state.ts +3 -2
  420. package/src/core/workflow/spawn-opencode.ts +150 -0
  421. package/src/core/workflow/transcript.ts +3 -2
  422. package/src/core/workflow/workspaces/leases.ts +5 -4
  423. package/src/core/workflow/workspaces/local-git.ts +3 -2
  424. package/src/core/workflow/workspaces/paths.ts +2 -1
  425. package/src/core/workflow/workspaces/state.ts +10 -9
  426. package/src/lazy-commands.ts +11 -1
  427. package/src/lib/agent-browser/client.ts +18 -0
  428. package/src/lib/browser/client.ts +147 -9
  429. package/src/lib/browser/identity.ts +64 -0
  430. package/src/lib/browser/index.ts +15 -1
  431. package/src/lib/browser/launch-args.ts +53 -1
  432. package/src/lib/browser/user-agent.ts +130 -0
  433. package/src/lib/docs-metadata-managed.ts +21 -0
  434. package/src/lib/identities/assume.ts +7 -7
  435. package/src/lib/instructions/apply.ts +2 -0
  436. package/src/lib/pace/index.ts +14 -0
  437. package/src/lib/pace/pace.ts +370 -0
  438. package/src/lib/tunnel/forward-headers.ts +16 -0
  439. package/src/lib/tunnel/gate.ts +18 -10
  440. package/src/lib/webview/index.ts +13 -0
  441. package/src/lib/webview/webview.ts +457 -0
@@ -0,0 +1,159 @@
1
+ import { resolve } from "node:path";
2
+ import type { Command } from "commander";
3
+ import type { EmitContext } from "../commander.ts";
4
+ import { commandPaceGate } from "../lib/pace/index.ts";
5
+ import {
6
+ parseWebViewTypeStep,
7
+ parseWebViewViewport,
8
+ runWebView,
9
+ type WebViewBackend,
10
+ WebViewUnavailableError,
11
+ } from "../lib/webview/index.ts";
12
+
13
+ /**
14
+ * `harn webview`: a lightweight headless page probe through Bun.WebView.
15
+ *
16
+ * Experimental and Bun-only (Bun 1.4+). It always launches a fresh browser with
17
+ * ephemeral storage unless `--profile` is given, and never attaches to a
18
+ * running user browser. Use `browse` for headed login, the shared cookie jar,
19
+ * HAR capture, full-page screenshots, and page QA; use `browse-ai` for
20
+ * accessibility-tree element references. On Node the command fails with a
21
+ * clear message, matching how `tunnel` handles its Bun-only worker.
22
+ */
23
+
24
+ interface WebviewOpts {
25
+ userAgent?: string;
26
+ backend: string;
27
+ viewport: string;
28
+ profile?: string;
29
+ chromePath?: string;
30
+ browserArg: string[];
31
+ timeout: string;
32
+ waitFor?: string;
33
+ type: string[];
34
+ click: string[];
35
+ press: string[];
36
+ evaluate?: string;
37
+ settle: string;
38
+ screenshot?: string;
39
+ html?: boolean;
40
+ json?: boolean;
41
+ pace?: boolean;
42
+ }
43
+
44
+ export function registerWebviewCommand(program: Command, emit: EmitContext, binName: string): void {
45
+ program
46
+ .command("webview <url>")
47
+ .description(
48
+ "Lightweight headless page probe through Bun.WebView (experimental, Bun 1.4+). " +
49
+ `Fresh ephemeral browser per call; use ${binName} browse for login, cookies, HAR, and QA.`,
50
+ )
51
+ .option("--backend <engine>", "Browser engine: auto | chrome | webkit", "auto")
52
+ .option(
53
+ "--user-agent <ua>",
54
+ "Chrome user agent: shared store by default; auto refreshes, native disables",
55
+ )
56
+ .option("--viewport <WxH>", "Viewport in CSS pixels", "1280x800")
57
+ .option(
58
+ "--profile <dir>",
59
+ "Persist cookies and local storage in this directory (default is ephemeral)",
60
+ )
61
+ .option("--chrome-path <path>", "Chrome-family executable; implies the Chrome backend")
62
+ .option("--browser-arg <flag>", "Extra Chrome launch flag (repeatable)", collect, [])
63
+ .option("--timeout <ms>", "Navigation and selector timeout in milliseconds", "30000")
64
+ .option("--wait-for <css>", "Wait for a CSS selector after navigation")
65
+ .option(
66
+ "--type <css=>text>",
67
+ "Focus a selector and insert text (repeatable; runs before clicks)",
68
+ collect,
69
+ [],
70
+ )
71
+ .option("--click <css>", "Scroll to and click a CSS selector (repeatable)", collect, [])
72
+ .option(
73
+ "--press <key>",
74
+ "Press a key in the currently focused element (repeatable)",
75
+ collect,
76
+ [],
77
+ )
78
+ .option("--evaluate <expression>", "Evaluate a JavaScript expression after actions")
79
+ .option("--settle <ms>", "Extra delay after actions before capture", "0")
80
+ .option(
81
+ "--screenshot <path>",
82
+ "Save a viewport PNG (Bun.WebView does not provide full-page capture)",
83
+ )
84
+ .option("--html", "Print the final document HTML instead of body text")
85
+ .option("--json", "Print the full result envelope")
86
+ .option(
87
+ "--no-pace",
88
+ "Skip the human-pace gap (3 to 9 s) between page loads of the same site for this run. " +
89
+ "HARNERY_PACE=off disables it machine-wide; local and private hosts never wait.",
90
+ )
91
+ .action(async (url: string, opts: WebviewOpts) => {
92
+ try {
93
+ await runWebview(url, opts, emit);
94
+ } catch (err: unknown) {
95
+ const message = err instanceof Error ? err.message : String(err);
96
+ emit.error({
97
+ code: err instanceof WebViewUnavailableError ? err.code : "webview_error",
98
+ message,
99
+ });
100
+ process.exit(1);
101
+ }
102
+ });
103
+ }
104
+
105
+ async function runWebview(url: string, opts: WebviewOpts, emit: EmitContext): Promise<void> {
106
+ if (!isBackend(opts.backend)) {
107
+ throw new Error(`Invalid backend "${opts.backend}". Expected auto, chrome, or webkit.`);
108
+ }
109
+ const backend = opts.backend === "auto" && opts.chromePath ? "chrome" : opts.backend;
110
+ const result = await runWebView(url, {
111
+ backend,
112
+ viewport: parseWebViewViewport(opts.viewport),
113
+ profileDir: opts.profile ? resolve(opts.profile) : undefined,
114
+ chromePath: opts.chromePath ? resolve(opts.chromePath) : undefined,
115
+ browserArgs: opts.browserArg,
116
+ timeoutMs: parseNonNegativeInteger(opts.timeout, "--timeout", false),
117
+ waitFor: opts.waitFor,
118
+ typeSteps: opts.type.map(parseWebViewTypeStep),
119
+ clicks: opts.click,
120
+ presses: opts.press,
121
+ evaluate: opts.evaluate,
122
+ settleMs: parseNonNegativeInteger(opts.settle, "--settle", true),
123
+ screenshotPath: opts.screenshot ? resolve(opts.screenshot) : undefined,
124
+ captureHtml: opts.html === true,
125
+ pace: commandPaceGate(opts.pace !== false, (message) => emit.log(message, "info")),
126
+ userAgent: opts.userAgent,
127
+ });
128
+
129
+ if (opts.json) {
130
+ emit.data(result);
131
+ } else if (opts.html) {
132
+ emit.text(result.html?.endsWith("\n") ? result.html : `${result.html ?? ""}\n`);
133
+ } else if (opts.evaluate !== undefined) {
134
+ const value =
135
+ typeof result.evaluation === "string"
136
+ ? result.evaluation
137
+ : JSON.stringify(result.evaluation, null, 2);
138
+ emit.text(`${value ?? "undefined"}\n`);
139
+ } else {
140
+ emit.text(result.text.endsWith("\n") ? result.text : `${result.text}\n`);
141
+ }
142
+ }
143
+
144
+ function collect(value: string, previous: string[]): string[] {
145
+ return [...previous, value];
146
+ }
147
+
148
+ function isBackend(value: string): value is WebViewBackend {
149
+ return value === "auto" || value === "chrome" || value === "webkit";
150
+ }
151
+
152
+ function parseNonNegativeInteger(value: string, flag: string, allowZero: boolean): number {
153
+ if (!/^\d+$/.test(value)) throw new Error(`${flag} expects an integer, got "${value}".`);
154
+ const parsed = Number.parseInt(value, 10);
155
+ if ((!allowZero && parsed < 1) || (allowZero && parsed < 0)) {
156
+ throw new Error(`${flag} must be ${allowZero ? "zero or greater" : "greater than zero"}.`);
157
+ }
158
+ return parsed;
159
+ }
@@ -1,5 +1,106 @@
1
1
  /** First-party harness adapters supported by Harnery's canonical V3 ledger. */
2
- export type Adapter = "claude-code" | "cursor" | "codex";
2
+ export type Adapter = "claude-code" | "cursor" | "codex" | "opencode";
3
3
 
4
4
  /** Canonical producer classes used by V3 provenance. */
5
5
  export type EventSource = "agent-hooks" | "agent-coord" | "user" | "system";
6
+
7
+ /**
8
+ * Runtime census of the workflow adapters. Adding a member to `Adapter`
9
+ * without listing it here is a compile error (see `adaptersComplete`), so a
10
+ * new adapter can never slip past the shared normalizer.
11
+ */
12
+ export const ADAPTERS = [
13
+ "claude-code",
14
+ "cursor",
15
+ "codex",
16
+ "opencode",
17
+ ] as const satisfies readonly Adapter[];
18
+
19
+ type MissingAdapter = Exclude<Adapter, (typeof ADAPTERS)[number]>;
20
+ const adaptersComplete: [MissingAdapter] extends [never] ? true : never = true;
21
+ void adaptersComplete;
22
+
23
+ /** Every adapter falls back to this identity when a platform value carries no usable evidence. */
24
+ export const DEFAULT_ADAPTER: Adapter = "claude-code";
25
+
26
+ /** Why a platform value could not be normalized to a known adapter. */
27
+ export type AdapterFallbackReason = "missing" | "unknown";
28
+
29
+ export interface AdapterFallback {
30
+ /** The raw value that was normalized. */
31
+ value: unknown;
32
+ /** `missing` for null, undefined, or an empty string; `unknown` for anything else. */
33
+ reason: AdapterFallbackReason;
34
+ /** The adapter that was substituted. */
35
+ fallback: string;
36
+ /** Caller-supplied label naming the code path, for the diagnostic line. */
37
+ context?: string;
38
+ }
39
+
40
+ export interface AdapterFallbackOptions {
41
+ /** Names the caller in the diagnostic line, e.g. `"claim-conflict"`. */
42
+ context?: string;
43
+ /**
44
+ * Receives every fallback. The default writes one stderr line per unknown
45
+ * value; a missing value is a pre-heartbeat state rather than a
46
+ * misattribution, so it is passed here but not printed.
47
+ */
48
+ onFallback?: (fallback: AdapterFallback) => void;
49
+ }
50
+
51
+ /**
52
+ * Generic membership normalizer shared by every adapter census. Returns the
53
+ * matching id or `null`; it never substitutes a default.
54
+ */
55
+ export function normalizeAdapterId<T extends string>(
56
+ value: unknown,
57
+ census: readonly T[],
58
+ ): T | null {
59
+ if (typeof value !== "string") return null;
60
+ return (census as readonly string[]).includes(value) ? (value as T) : null;
61
+ }
62
+
63
+ /**
64
+ * Normalize with an explicit, reported fallback. Every caller that must end up
65
+ * with *some* adapter routes through here so the substitution is visible.
66
+ */
67
+ export function resolveAdapterId<T extends string>(
68
+ value: unknown,
69
+ census: readonly T[],
70
+ fallback: T,
71
+ options: AdapterFallbackOptions = {},
72
+ ): T {
73
+ const normalized = normalizeAdapterId(value, census);
74
+ if (normalized !== null) return normalized;
75
+ const reason: AdapterFallbackReason =
76
+ value === undefined || value === null || value === "" ? "missing" : "unknown";
77
+ (options.onFallback ?? reportAdapterFallback)({
78
+ value,
79
+ reason,
80
+ fallback,
81
+ context: options.context,
82
+ });
83
+ return fallback;
84
+ }
85
+
86
+ /** Default fallback sink: one stderr line for an unrecognized platform value. */
87
+ export function reportAdapterFallback(fallback: AdapterFallback): void {
88
+ if (fallback.reason !== "unknown") return;
89
+ const where = fallback.context ? ` (${fallback.context})` : "";
90
+ process.stderr.write(
91
+ `harnery: unknown adapter platform ${JSON.stringify(fallback.value)}${where}; treating it as ${fallback.fallback}\n`,
92
+ );
93
+ }
94
+
95
+ /** Strict workflow-adapter normalizer: a known id or `null`. */
96
+ export function normalizeAdapter(value: unknown): Adapter | null {
97
+ return normalizeAdapterId(value, ADAPTERS);
98
+ }
99
+
100
+ /**
101
+ * Heartbeat `platform` (or any adapter-shaped string) to a workflow adapter,
102
+ * substituting `DEFAULT_ADAPTER` and reporting the substitution.
103
+ */
104
+ export function adapterFromPlatform(value: unknown, options?: AdapterFallbackOptions): Adapter {
105
+ return resolveAdapterId(value, ADAPTERS, DEFAULT_ADAPTER, options);
106
+ }
@@ -22,6 +22,7 @@ import {
22
22
  } from "node:fs";
23
23
  import { resolve } from "node:path";
24
24
  import { monorepoRoot } from "../agents/coord-client.ts";
25
+ import { stateDirMode, stateFileMode } from "../storage/modes.ts";
25
26
  import { stableDigest } from "../workflow/durable-record.ts";
26
27
  import type { AdapterId, AdapterProfile, CapabilitySupport } from "./types.ts";
27
28
 
@@ -107,12 +108,12 @@ export function writeAttestation(
107
108
  opts: AttestationStoreOptions = {},
108
109
  ): string {
109
110
  const path = attestationPath(record.adapter, opts);
110
- mkdirSync(attestationsDir(opts), { recursive: true, mode: 0o700 });
111
+ mkdirSync(attestationsDir(opts), { recursive: true, mode: stateDirMode() });
111
112
  const temporary = `${path}.tmp-${process.pid}`;
112
113
  try {
113
114
  writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\n`, {
114
115
  encoding: "utf8",
115
- mode: 0o600,
116
+ mode: stateFileMode(),
116
117
  });
117
118
  renameSync(temporary, path);
118
119
  } catch (error) {
@@ -106,7 +106,7 @@ export const BUILTIN_ADAPTER_PROFILES = {
106
106
  authModel: "own-auth",
107
107
  modelFamily: "multi",
108
108
  effortValues: [],
109
- verified: { date: "2026-08-18", version: "2026.08.11-e8db854" },
109
+ verified: { date: "2026-09-20", version: "2026.09.18-9a7762b" },
110
110
  capabilities: capabilities({
111
111
  effortSelection: unsupported(
112
112
  "Cursor embeds effort in some parameterized model ids; Harnery does not rewrite model ids.",
@@ -114,6 +114,10 @@ export const BUILTIN_ADAPTER_PROFILES = {
114
114
  maxTurns: unsupported("cursor-agent exposes no turn-ceiling flag."),
115
115
  sessionId: supported("Read from the JSON result envelope."),
116
116
  cost: unsupported("The JSON result envelope carries no cost."),
117
+ // Still unknown on 2026-09-20 against cursor-agent 2026.09.18: the live
118
+ // `cursor_composer_context_percent` reader serves IDE hook sessions from
119
+ // the composer `state.vscdb`, and headless `cursor-agent` children (this
120
+ // workflow adapter) leave no composer row and no usage in the JSON result.
117
121
  contextTelemetry: unknown("No stable Cursor context-usage payload has been certified."),
118
122
  preCompactionSignal: supported("Cursor preCompact is wired to a durable checkpoint."),
119
123
  postCompactionSignal: unsupported(
@@ -121,6 +125,43 @@ export const BUILTIN_ADAPTER_PROFILES = {
121
125
  ),
122
126
  }),
123
127
  },
128
+ opencode: {
129
+ id: "opencode",
130
+ displayName: "OpenCode",
131
+ binary: "opencode",
132
+ installHint: "curl -fsSL https://opencode.ai/install | bash",
133
+ loginHint: "opencode auth login",
134
+ apiKeyEnv: "OPENCODE_API_KEY",
135
+ integrationMode: "cli-subprocess",
136
+ authModel: "own-auth",
137
+ modelFamily: "multi",
138
+ // Model reasoning is selected through the `provider/model#variant` id;
139
+ // OpenCode exposes no separate effort dial for Harnery to map.
140
+ effortValues: [],
141
+ verified: { date: "2026-09-20", version: "opencode v2.0.11" },
142
+ capabilities: capabilities({
143
+ effortSelection: unsupported(
144
+ "OpenCode selects reasoning through the model id variant; there is no separate effort flag.",
145
+ ),
146
+ maxTurns: unsupported("`opencode run` exposes no turn-ceiling flag."),
147
+ sessionId: supported("Read from the `sessionID` on every `--format json` NDJSON line."),
148
+ cost: unsupported(
149
+ "`opencode run --format json` streams parts without a final cost; cost lives in `session export`.",
150
+ ),
151
+ // Tool evidence is delivered on the event axis by the Harnery OpenCode
152
+ // plugin, not by the final-result workflow spawn, which retains no tool
153
+ // events (as with Codex and Cursor).
154
+ contextTelemetry: unknown(
155
+ "OpenCode stores context in SQLite; no context-window bridge is certified yet.",
156
+ ),
157
+ preCompactionSignal: supported(
158
+ 'OpenCode session.hook("compaction") is bridged to a durable checkpoint.',
159
+ ),
160
+ postCompactionSignal: unsupported(
161
+ "No post-compact hook is wired; OpenCode post-compaction recovery is not yet certified.",
162
+ ),
163
+ }),
164
+ },
124
165
  } as const satisfies Record<string, AdapterProfile>;
125
166
 
126
167
  export type BuiltinAdapterId = keyof typeof BUILTIN_ADAPTER_PROFILES;
@@ -13,6 +13,11 @@ import {
13
13
  cursorSpawner,
14
14
  normalizeCursorResult,
15
15
  } from "../workflow/spawn-cursor.ts";
16
+ import {
17
+ buildOpenCodeInvocation,
18
+ normalizeOpenCodeResult,
19
+ openCodeSpawner,
20
+ } from "../workflow/spawn-opencode.ts";
16
21
  import {
17
22
  BUILTIN_ADAPTER_IDS,
18
23
  BUILTIN_ADAPTER_PROFILES,
@@ -85,6 +90,38 @@ const BUILTIN_ADAPTERS: Record<BuiltinAdapterId, Adapter> = {
85
90
  },
86
91
  },
87
92
  },
93
+ opencode: {
94
+ profile: BUILTIN_ADAPTER_PROFILES.opencode,
95
+ spawn: openCodeSpawner,
96
+ buildInvocation: buildOpenCodeInvocation,
97
+ normalizeResult: normalizeOpenCodeResult,
98
+ fixture: {
99
+ // `opencode run --format json` streams NDJSON: a step-start line then one
100
+ // or more text parts, each carrying the same sessionID.
101
+ raw: {
102
+ stdout: [
103
+ JSON.stringify({
104
+ type: "step_start",
105
+ sessionID: "opencode-bench-session",
106
+ part: { type: "step-start" },
107
+ }),
108
+ JSON.stringify({
109
+ type: "text",
110
+ sessionID: "opencode-bench-session",
111
+ part: { type: "text", text: "HARNERY_BENCH_OK" },
112
+ }),
113
+ ].join("\n"),
114
+ stderr: "",
115
+ exitCode: 0,
116
+ durationMs: 11,
117
+ },
118
+ expected: {
119
+ ok: true,
120
+ text: "HARNERY_BENCH_OK",
121
+ sessionId: "opencode-bench-session",
122
+ },
123
+ },
124
+ },
88
125
  };
89
126
 
90
127
  /** Mutable registry for embedders, initialized empty unless adapters are
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import { spawnSync } from "node:child_process";
14
- import type { EventAdapterIdV3 } from "../events/v3/adapter-id.ts";
14
+ import { type EventAdapterIdV3, eventAdapterIdV3FromPlatform } from "../events/v3/adapter-id.ts";
15
15
  import type { LiveCoordinationObservationV3 } from "../events/v3/live-observation.ts";
16
16
  import { coordBinPath } from "./coord-bin.ts";
17
17
  import { resolveCoordRoot } from "./coord-client.ts";
@@ -94,10 +94,7 @@ export function emitEventV3(input: EventV3EmitInput): boolean {
94
94
  * Normalize the heartbeat's `platform` field to the canonical Adapter type.
95
95
  */
96
96
  export function normalizeAdapter(platform: string | undefined): EventAdapterIdV3 {
97
- if (platform === "cursor") return "cursor";
98
- if (platform === "codex") return "codex";
99
- if (platform === "openclaw") return "openclaw";
100
- return "claude-code";
97
+ return eventAdapterIdV3FromPlatform(platform, { context: "canonical-emit" });
101
98
  }
102
99
 
103
100
  /** Producer joins use the private native session ID, never its V3 fingerprint. */
@@ -13,6 +13,8 @@ import { appendFile, mkdir } from "node:fs/promises";
13
13
  import { hostname } from "node:os";
14
14
  import { dirname, join, resolve } from "node:path";
15
15
  import { coordEnv } from "../../lib/env.ts";
16
+ import { normalizeAdapter, adapterFromPlatform as sharedAdapterFromPlatform } from "../adapter.ts";
17
+ import { resolveBinName } from "../config.ts";
16
18
  import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
17
19
  import { acquireNoClobberLease } from "../workflow/workspaces/leases.ts";
18
20
 
@@ -166,12 +168,6 @@ async function handleProject(root: string, rest: string[]): Promise<number> {
166
168
  return view.authority_safe ? 0 : 1;
167
169
  }
168
170
 
169
- function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
170
- if (platform === "cursor") return "cursor";
171
- if (platform === "codex") return "codex";
172
- return "claude-code";
173
- }
174
-
175
171
  const coordinationRetryCell = new Int32Array(new SharedArrayBuffer(4));
176
172
  const COORDINATION_RETRY_ATTEMPTS = 240;
177
173
  const COORDINATION_RETRY_DELAY_MS = 25;
@@ -281,7 +277,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
281
277
  coordRoot: root,
282
278
  owner,
283
279
  nativeSessionId: before?.session_id ?? owner,
284
- adapter: adapterFromPlatform(before?.platform),
280
+ adapter: sharedAdapterFromPlatform(before?.platform, { context: "agent-coord" }),
285
281
  task,
286
282
  });
287
283
  break;
@@ -312,9 +308,14 @@ async function handleStateAction(root: string, action: string, rest: string[]):
312
308
  hb = writer.setAssignedNameCache(root, owner, name, "session");
313
309
  }
314
310
  } catch (error) {
315
- process.stderr.write(
316
- `agent-coord set-task: V3 authority refused (${error instanceof Error ? error.message : String(error)})\n`,
317
- );
311
+ const detail = error instanceof Error ? error.message : String(error);
312
+ // A missing heartbeat here means the session's generation ended
313
+ // while its process lived on. Name the reopen command (ADR 0088);
314
+ // set-task is usually the first thing such a session runs.
315
+ const hint = detail.includes("heartbeat_missing:")
316
+ ? `; run \`${resolveBinName(root)} agents lifecycle active\` to open a fresh generation for this continuing session`
317
+ : "";
318
+ process.stderr.write(`agent-coord set-task: V3 authority refused (${detail})${hint}\n`);
318
319
  return 1;
319
320
  }
320
321
  if (!hb) {
@@ -347,7 +348,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
347
348
  coordRoot: root,
348
349
  owner,
349
350
  nativeSessionId: before?.session_id ?? owner,
350
- adapter: adapterFromPlatform(before?.platform),
351
+ adapter: sharedAdapterFromPlatform(before?.platform, { context: "agent-coord" }),
351
352
  operation: "released",
352
353
  path,
353
354
  access: "write",
@@ -408,7 +409,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
408
409
  coordRoot: root,
409
410
  owner,
410
411
  nativeSessionId: sessionId ?? owner,
411
- adapter: adapterFromPlatform(adapter),
412
+ adapter: sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
412
413
  });
413
414
  }
414
415
  const { repairLiveCoordinationHeartbeat } = await import(
@@ -418,7 +419,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
418
419
  root,
419
420
  owner,
420
421
  sessionId ?? owner,
421
- adapterFromPlatform(adapter),
422
+ sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
422
423
  model,
423
424
  );
424
425
  } catch (error) {
@@ -453,7 +454,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
453
454
  root,
454
455
  owner,
455
456
  sessionId ?? owner,
456
- adapterFromPlatform(adapter),
457
+ sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
457
458
  );
458
459
  if (!heartbeat) {
459
460
  throw new Error("authoritative coordination cache could not be materialized");
@@ -471,7 +472,7 @@ async function handleStateAction(root: string, action: string, rest: string[]):
471
472
  root,
472
473
  owner,
473
474
  sessionId ?? owner,
474
- adapterFromPlatform(adapter),
475
+ sharedAdapterFromPlatform(adapter, { context: "agent-coord" }),
475
476
  );
476
477
  process.stdout.write(
477
478
  `${JSON.stringify({
@@ -875,7 +876,7 @@ async function handleEmitEvent(root: string, rest: string[]): Promise<number> {
875
876
  const eventType = args.type;
876
877
  const instanceId = args.owner;
877
878
  const sessionId = args.session;
878
- const adapter = args.adapter as "claude-code" | "cursor" | "codex" | undefined;
879
+ const adapter = normalizeAdapter(args.adapter) ?? undefined;
879
880
  const dataJson = args["data-stdin"] === "true" ? await readStdin() : "";
880
881
 
881
882
  if (!eventType || !instanceId || !sessionId || !adapter || !dataJson) {
@@ -1063,7 +1064,7 @@ async function handleGitHook(fallbackRoot: string, rest: string[]): Promise<numb
1063
1064
  owner,
1064
1065
  subject: hit.instance_id,
1065
1066
  nativeSessionId: hit.session_id ?? owner,
1066
- adapter: adapterFromPlatform(hit.platform),
1067
+ adapter: sharedAdapterFromPlatform(hit.platform, { context: "agent-coord" }),
1067
1068
  operation: "released",
1068
1069
  path,
1069
1070
  access: "write",
@@ -527,6 +527,10 @@ const SESSION_ID_ENV_VARS = [
527
527
  "CURSOR_CONVERSATION_ID",
528
528
  "CODEX_SESSION_ID",
529
529
  "CODEX_THREAD_ID",
530
+ // OpenCode exports no session id into the tool shell; the Harnery OpenCode
531
+ // plugin stamps this via shell.hook("create.before") so a coord CLI run as a
532
+ // Bash tool recovers its identity.
533
+ "OPENCODE_SESSION_ID",
530
534
  ] as const;
531
535
 
532
536
  /** Read normalized candidates from the first non-empty adapter session-id env var. */
@@ -637,10 +641,12 @@ export function resolveSingleActiveOwner(root: string): string | null {
637
641
  return live.size === 1 ? [...live][0]! : null;
638
642
  }
639
643
 
640
- function adapterCandidatesFromEnv(): Array<"claude-code" | "codex" | "cursor"> {
644
+ function adapterCandidatesFromEnv(): Array<"claude-code" | "codex" | "cursor" | "opencode"> {
641
645
  const value = process.env.HARNERY_AGENT_COORD_PLATFORM?.trim();
642
- if (value === "claude-code" || value === "codex" || value === "cursor") return [value];
643
- return ["claude-code", "codex", "cursor"];
646
+ if (value === "claude-code" || value === "codex" || value === "cursor" || value === "opencode") {
647
+ return [value];
648
+ }
649
+ return ["claude-code", "codex", "cursor", "opencode"];
644
650
  }
645
651
 
646
652
  function nativeOwnerForV3Instance(root: string, instanceId: string): string {
@@ -17,6 +17,7 @@
17
17
  import { spawnSync } from "node:child_process";
18
18
  import { existsSync } from "node:fs";
19
19
  import { join } from "node:path";
20
+ import { adapterFromPlatform as sharedAdapterFromPlatform } from "../../adapter.ts";
20
21
  import { recordLiveClaimChangeV3 } from "../live-authority-v3.ts";
21
22
  import { readLiveCoordinationRows } from "../state/live-coordination-view.ts";
22
23
 
@@ -248,7 +249,7 @@ function acquireClaimThroughLedger(coordRoot: string, req: ClaimRequest): boolea
248
249
  coordRoot,
249
250
  owner: req.instance_id,
250
251
  nativeSessionId: req.session_id ?? owner?.session_id ?? req.instance_id,
251
- adapter: adapterFromPlatform(owner?.platform),
252
+ adapter: sharedAdapterFromPlatform(owner?.platform, { context: "claim-conflict" }),
252
253
  operation: "acquired",
253
254
  path: req.path,
254
255
  access: "write",
@@ -272,7 +273,7 @@ function releaseClaimThroughLedger(
272
273
  owner: actor.instance_id,
273
274
  subject: subjectInstanceId,
274
275
  nativeSessionId: actor.session_id ?? owner?.session_id ?? actor.instance_id,
275
- adapter: adapterFromPlatform(owner?.platform),
276
+ adapter: sharedAdapterFromPlatform(owner?.platform, { context: "claim-conflict" }),
276
277
  operation: "released",
277
278
  path: relPath,
278
279
  access: "write",
@@ -283,12 +284,6 @@ function releaseClaimThroughLedger(
283
284
  }
284
285
  }
285
286
 
286
- function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
287
- if (platform === "cursor") return "cursor";
288
- if (platform === "codex") return "codex";
289
- return "claude-code";
290
- }
291
-
292
287
  function authorityUnavailable(path: string, operation: string): VerdictResult {
293
288
  return {
294
289
  allow: false,
@@ -23,6 +23,7 @@
23
23
  import { spawnSync } from "node:child_process";
24
24
  import { appendFileSync, existsSync } from "node:fs";
25
25
  import { basename, dirname, join } from "node:path";
26
+ import { adapterFromPlatform as sharedAdapterFromPlatform } from "../../adapter.ts";
26
27
  import { coordFreshnessSeconds } from "../../config.ts";
27
28
  import { resolveOwner } from "../../hooks/resolve/owner.ts";
28
29
  import { recordLiveClaimChangeV3 } from "../live-authority-v3.ts";
@@ -232,7 +233,7 @@ function retainBlockedStagedPaths(
232
233
  coordRoot,
233
234
  owner: instanceId,
234
235
  nativeSessionId: self.session_id ?? sessionId,
235
- adapter: adapterFromPlatform(self.platform),
236
+ adapter: sharedAdapterFromPlatform(self.platform, { context: "commit-conflict" }),
236
237
  operation: "acquired",
237
238
  path,
238
239
  access: "write",
@@ -244,12 +245,6 @@ function retainBlockedStagedPaths(
244
245
  }
245
246
  }
246
247
 
247
- function adapterFromPlatform(platform: unknown): "claude-code" | "cursor" | "codex" {
248
- if (platform === "cursor") return "cursor";
249
- if (platform === "codex") return "codex";
250
- return "claude-code";
251
- }
252
-
253
248
  function findOverlap(
254
249
  staged: string,
255
250
  files: readonly string[],
@@ -32,6 +32,8 @@ export interface StopHookRequest {
32
32
  turn_window?: { start_ms: number; end_ms: number };
33
33
  stop_hook_active?: boolean;
34
34
  status_box_present_strict?: boolean;
35
+ /** Current observation overrides an older failed terminal during remediation. */
36
+ session_name_observation_unavailable?: boolean;
35
37
  bypass?: boolean;
36
38
  workflow_child?: boolean;
37
39
  }
@@ -188,7 +190,7 @@ export function evaluateStopHookV3Events(
188
190
 
189
191
  // Claude Code can prove session-name presentation from assistant-only text.
190
192
  // Cursor cannot expose that text and Codex returned observe-only above.
191
- if (req.adapter !== "cursor") {
193
+ if (req.adapter !== "cursor" && !req.session_name_observation_unavailable) {
192
194
  const naming = turnTerminals
193
195
  .map((event) => observedNaming(event.payload.ritual?.session_name))
194
196
  .filter((value): value is { required: boolean; present: boolean } => value !== undefined);