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
@@ -5,6 +5,7 @@ import {
5
5
  type DurableHistoryFaultBoundary,
6
6
  readDurableHistorySync,
7
7
  } from "../storage/durable-history.ts";
8
+ import { stateDirMode, stateFileMode } from "../storage/modes.ts";
8
9
  import type {
9
10
  HarneryConversationAccessMode,
10
11
  HarneryConversationArchiveAuthorityMode,
@@ -69,10 +70,10 @@ export async function captureProviderConversation(
69
70
  provider.capabilities.provider_id,
70
71
  options.conversation_id,
71
72
  );
72
- mkdirSync(objectDir, { recursive: true, mode: 0o700 });
73
+ mkdirSync(objectDir, { recursive: true, mode: stateDirMode() });
73
74
  const lease = join(objectDir, ".capture.lease");
74
75
  try {
75
- mkdirSync(lease, { mode: 0o700 });
76
+ mkdirSync(lease, { mode: stateDirMode() });
76
77
  } catch {
77
78
  throw new Error(`conversation capture lease busy: ${options.conversation_id}`);
78
79
  }
@@ -223,7 +224,7 @@ function manifestFor(
223
224
  function publishManifest(objectDir: string, manifest: HarneryConversationArchiveManifest): void {
224
225
  const path = join(objectDir, "manifest.json");
225
226
  const temp = `${path}.tmp-${process.pid}`;
226
- writeFileSync(temp, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
227
+ writeFileSync(temp, `${JSON.stringify(manifest, null, 2)}\n`, { mode: stateFileMode() });
227
228
  renameSync(temp, path);
228
229
  }
229
230
 
@@ -25,6 +25,7 @@ import {
25
25
  } from "../artifacts/index.ts";
26
26
  import { currentHarneryRuntimeBuild } from "../events/v3/runtime-build.ts";
27
27
  import { writePrivateJsonAtomic } from "../storage/atomic-json.ts";
28
+ import { stateDirMode } from "../storage/modes.ts";
28
29
  import {
29
30
  SUPERVISOR_DIAGNOSTIC_LIMITS,
30
31
  SUPERVISOR_EXPLANATION_SCHEMA_VERSION,
@@ -171,8 +172,8 @@ export function captureDiagnosticBundle(
171
172
  now,
172
173
  });
173
174
  const inputsPath = join(created.path, "inputs");
174
- mkdirSync(inputsPath, { recursive: true, mode: 0o700 });
175
- tryChmod(inputsPath, 0o700);
175
+ mkdirSync(inputsPath, { recursive: true, mode: stateDirMode() });
176
+ tryChmod(inputsPath, stateDirMode());
176
177
 
177
178
  const payloads: Record<(typeof PAYLOAD_FILES)[number], unknown> = {
178
179
  "inputs/observations.json": observations,
@@ -4,6 +4,7 @@ import { chmod, lstat, mkdir, open, rename, rm } from "node:fs/promises";
4
4
  import { basename, join, resolve } from "node:path";
5
5
  import { pipeline } from "node:stream/promises";
6
6
  import { createGzip } from "node:zlib";
7
+ import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
7
8
  import { canonicalJsonV3 } from "../v3/canonical.ts";
8
9
  import { verifyLegacyV1HardFence } from "./fence.ts";
9
10
  import { type LegacyV1SegmentManifest, validateLegacyV1SegmentManifest } from "./manifest.ts";
@@ -46,8 +47,8 @@ export async function writeLegacyV1Canary(input: {
46
47
  .digest("hex")
47
48
  .slice(0, 32)}` as const;
48
49
  const outputDirectory = resolve(input.output_directory);
49
- await mkdir(outputDirectory, { recursive: true, mode: 0o700 });
50
- await chmod(outputDirectory, 0o700);
50
+ await mkdir(outputDirectory, { recursive: true, mode: stateDirMode() });
51
+ await chmod(outputDirectory, stateDirMode());
51
52
  const payloadPath = join(outputDirectory, `${segmentId}.ndjson.gz`);
52
53
  const manifestPath = join(outputDirectory, `${segmentId}.manifest.json`);
53
54
  const partialPayload = `${payloadPath}.partial`;
@@ -57,7 +58,7 @@ export async function writeLegacyV1Canary(input: {
57
58
  await pipeline(
58
59
  createReadStream(source),
59
60
  createGzip({ level: 9 }),
60
- createWriteStream(partialPayload, { flags: "wx", mode: 0o600 }),
61
+ createWriteStream(partialPayload, { flags: "wx", mode: stateFileMode() }),
61
62
  );
62
63
  await rename(partialPayload, payloadPath);
63
64
  const payloadStat = await lstat(payloadPath);
@@ -85,7 +86,7 @@ export async function writeLegacyV1Canary(input: {
85
86
  const handle = await open(
86
87
  partialManifest,
87
88
  constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL,
88
- 0o600,
89
+ stateFileMode(),
89
90
  );
90
91
  try {
91
92
  await handle.writeFile(`${canonicalJsonV3(manifest)}\n`, "utf8");
@@ -5,6 +5,7 @@ import { chmod, lstat, rename, rm } from "node:fs/promises";
5
5
  import { basename } from "node:path";
6
6
  import { pipeline } from "node:stream/promises";
7
7
  import { createGzip } from "node:zlib";
8
+ import { stateFileMode } from "../../storage/modes.ts";
8
9
  import { verifyLegacyV1HardFence } from "./fence.ts";
9
10
  import { inventoryLegacyV1Segments } from "./inventory.ts";
10
11
  import { streamLegacyV1Rows } from "./reader.ts";
@@ -84,7 +85,7 @@ export async function compressSealedLegacyV1Segments(
84
85
  await pipeline(
85
86
  createReadStream(row.path),
86
87
  createGzip({ level: 9 }),
87
- createWriteStream(temporary, { flags: "wx", mode: 0o600 }),
88
+ createWriteStream(temporary, { flags: "wx", mode: stateFileMode() }),
88
89
  );
89
90
  await proveRowParity(row.path, temporary);
90
91
  const unchanged = (await inventoryLegacyV1Segments(coordRoot)).find(
@@ -1,4 +1,10 @@
1
- import type { Adapter } from "../../adapter.ts";
1
+ import {
2
+ type Adapter,
3
+ type AdapterFallbackOptions,
4
+ DEFAULT_ADAPTER,
5
+ normalizeAdapterId,
6
+ resolveAdapterId,
7
+ } from "../../adapter.ts";
2
8
 
3
9
  /** Harness identities that may produce or describe Event Ledger V3 evidence. */
4
10
  export type EventAdapterIdV3 = Adapter | "openclaw";
@@ -8,5 +14,27 @@ export const EVENT_ADAPTER_IDS_V3 = [
8
14
  "claude-code",
9
15
  "codex",
10
16
  "cursor",
17
+ "opencode",
11
18
  "openclaw",
12
19
  ] as const satisfies readonly EventAdapterIdV3[];
20
+
21
+ type MissingEventAdapterId = Exclude<EventAdapterIdV3, (typeof EVENT_ADAPTER_IDS_V3)[number]>;
22
+ const eventAdapterIdsComplete: [MissingEventAdapterId] extends [never] ? true : never = true;
23
+ void eventAdapterIdsComplete;
24
+
25
+ /** Strict event-adapter normalizer: a known id or `null`. */
26
+ export function normalizeEventAdapterIdV3(value: unknown): EventAdapterIdV3 | null {
27
+ return normalizeAdapterId(value, EVENT_ADAPTER_IDS_V3);
28
+ }
29
+
30
+ /**
31
+ * Heartbeat `platform` to an event adapter id, substituting `DEFAULT_ADAPTER`
32
+ * and reporting the substitution. Use this wherever a row must be attributed
33
+ * to some adapter; use `normalizeEventAdapterIdV3` when `null` is acceptable.
34
+ */
35
+ export function eventAdapterIdV3FromPlatform(
36
+ value: unknown,
37
+ options?: AdapterFallbackOptions,
38
+ ): EventAdapterIdV3 {
39
+ return resolveAdapterId(value, EVENT_ADAPTER_IDS_V3, DEFAULT_ADAPTER, options);
40
+ }
@@ -11,6 +11,7 @@ import {
11
11
  writeFileSync,
12
12
  } from "node:fs";
13
13
  import { join } from "node:path";
14
+ import { stateFileMode } from "../../storage/modes.ts";
14
15
  import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
15
16
  import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
16
17
  import type { EventV3 } from "./contract.ts";
@@ -540,7 +541,7 @@ function publishExclusive(path: string, serialized: string): void {
540
541
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
541
542
  let fd: number | undefined;
542
543
  try {
543
- fd = openSync(temporary, "wx", 0o600);
544
+ fd = openSync(temporary, "wx", stateFileMode());
544
545
  writeFileSync(fd, serialized, "utf8");
545
546
  fsyncSync(fd);
546
547
  closeSync(fd);
@@ -16,6 +16,7 @@ import {
16
16
  import { hostname } from "node:os";
17
17
  import { dirname, join, resolve } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
+ import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
19
20
  import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
20
21
  import { acquireNoClobberLease } from "../../workflow/workspaces/leases.ts";
21
22
  import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
@@ -524,7 +525,7 @@ function archiveCurrentEpoch(root: string, createdAt: string): string | undefine
524
525
  const current = join(root, ".harnery", "ledgers", "v3");
525
526
  if (!existsSync(current)) return undefined;
526
527
  const archives = join(root, ".harnery", "ledgers", "v3-archives");
527
- mkdirSync(archives, { recursive: true, mode: 0o700 });
528
+ mkdirSync(archives, { recursive: true, mode: stateDirMode() });
528
529
  const stamp = createdAt.replace(/[^0-9]/g, "");
529
530
  let target = join(archives, `epoch-${stamp}`);
530
531
  let suffix = 0;
@@ -535,20 +536,20 @@ function archiveCurrentEpoch(root: string, createdAt: string): string | undefine
535
536
  }
536
537
 
537
538
  function publishControlFile(path: string, value: unknown): void {
538
- mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
539
+ mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
539
540
  const temp = `${path}.tmp.${process.pid}`;
540
541
  // A prior failed publish by this same pid would otherwise make every retry
541
542
  // fail on the exclusive create, which is exactly how one transient error
542
543
  // turns into a permanently stranded epoch.
543
544
  if (existsSync(temp)) unlinkSync(temp);
544
- const fd = openSync(temp, "wx", 0o600);
545
+ const fd = openSync(temp, "wx", stateFileMode());
545
546
  try {
546
547
  writeFileSync(fd, `${canonicalJsonV3(value)}\n`, "utf8");
547
548
  fsyncSync(fd);
548
549
  } finally {
549
550
  closeSync(fd);
550
551
  }
551
- chmodSync(temp, 0o600);
552
+ chmodSync(temp, stateFileMode());
552
553
  renameSync(temp, path);
553
554
  fsyncParentDirectory(path);
554
555
  }
@@ -115,6 +115,19 @@ const BASE_SIGNAL_SUPPORT: Record<
115
115
  post_compaction: "unsupported",
116
116
  context_usage: "conditional",
117
117
  },
118
+ opencode: {
119
+ ...SHARED_SIGNAL_SUPPORT,
120
+ // OpenCode sessions are persistent server objects; the plugin observes a
121
+ // turn boundary (session.idle) rather than a clean session close, so end is
122
+ // conditional. Turns group by messageID without a first-class turn id.
123
+ session_end: "conditional",
124
+ turn_id: "conditional",
125
+ permission: "native",
126
+ // Compaction is intercepted (session.hook("compaction")); post-compaction
127
+ // recovery and SQLite-backed context usage are not yet certified.
128
+ post_compaction: "unsupported",
129
+ context_usage: "unsupported",
130
+ },
118
131
  openclaw: {
119
132
  session_start: "conditional",
120
133
  session_end: "native",
@@ -169,6 +182,10 @@ export const ADAPTER_CAPABILITY_PROFILES_V3: Record<EventAdapterIdV3, AdapterCap
169
182
  model_usage: "unsupported",
170
183
  inference_timing: "unsupported",
171
184
  }),
185
+ opencode: profile("opencode", {
186
+ model_usage: "conditional",
187
+ inference_timing: "unsupported",
188
+ }),
172
189
  openclaw: profile(
173
190
  "openclaw",
174
191
  {
@@ -123,8 +123,15 @@ export const TurnCompletedV3Schema = eventV3("turn.completed", {
123
123
  }),
124
124
  });
125
125
 
126
+ export const ToolLineChangesV3Schema = StrictObject({
127
+ added: Type.Integer({ minimum: 0 }),
128
+ removed: Type.Integer({ minimum: 0 }),
129
+ });
130
+
126
131
  export const ToolCompletedV3Schema = eventV3("tool.completed", {
127
- payload: terminalPayloadV3("tool.completed"),
132
+ payload: terminalPayloadV3("tool.completed", {
133
+ line_changes: Type.Optional(ObservationV3Schema(ToolLineChangesV3Schema)),
134
+ }),
128
135
  });
129
136
 
130
137
  export const CommandCompletedV3Schema = eventV3("command.completed", {
@@ -260,6 +267,7 @@ type ToolCompletedEventV3 = ReplaceEventV3<
260
267
  Omit<EventOfTypeV3Base<"tool.completed">["payload"], "duration_ms"> & {
261
268
  duration_ms: SpanSummaryValueV3["duration_ms"];
262
269
  span: SpanSummaryValueV3;
270
+ line_changes?: Static<ReturnType<typeof ObservationV3Schema<typeof ToolLineChangesV3Schema>>>;
263
271
  }
264
272
  >;
265
273
  type CommandCompletedEventV3 = ReplaceEventV3<
@@ -10,6 +10,7 @@ import {
10
10
  unlinkSync,
11
11
  writeFileSync,
12
12
  } from "node:fs";
13
+ import { stateFileMode } from "../../storage/modes.ts";
13
14
  import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
14
15
  import { canonicalJsonV3, type FingerprintV3, fingerprintV3, sha256V3 } from "./canonical.ts";
15
16
  import type { ActivationManifestV3, CandidateGenesisManifestV3 } from "./control.ts";
@@ -332,12 +333,12 @@ function publishCanonicalFileV3(path: string, value: unknown): boolean {
332
333
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
333
334
  let fd: number | undefined;
334
335
  try {
335
- fd = openSync(temporary, "wx", 0o600);
336
+ fd = openSync(temporary, "wx", stateFileMode());
336
337
  writeFileSync(fd, `${canonicalJsonV3(value)}\n`, "utf8");
337
338
  fsyncSync(fd);
338
339
  closeSync(fd);
339
340
  fd = undefined;
340
- chmodSync(temporary, 0o600);
341
+ chmodSync(temporary, stateFileMode());
341
342
  renameSync(temporary, path);
342
343
  fsyncParentDirectory(path);
343
344
  return true;
@@ -13,6 +13,7 @@ import {
13
13
  writeFileSync,
14
14
  } from "node:fs";
15
15
  import { join } from "node:path";
16
+ import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
16
17
  import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
17
18
  import {
18
19
  type AuthorityTransactionV3,
@@ -306,8 +307,8 @@ function recoveryPaths(coordRoot: string, transactionId: string) {
306
307
 
307
308
  function ensureRecoveryLayout(paths: ReturnType<typeof recoveryPaths>): void {
308
309
  for (const path of [paths.root, paths.quarantineRoot]) {
309
- mkdirSync(path, { recursive: true, mode: 0o700 });
310
- chmodSync(path, 0o700);
310
+ mkdirSync(path, { recursive: true, mode: stateDirMode() });
311
+ chmodSync(path, stateDirMode());
311
312
  }
312
313
  }
313
314
 
@@ -325,7 +326,7 @@ function publishSerializedExclusive(path: string, serialized: string): void {
325
326
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
326
327
  let fd: number | undefined;
327
328
  try {
328
- fd = openSync(temporary, "wx", 0o600);
329
+ fd = openSync(temporary, "wx", stateFileMode());
329
330
  writeFileSync(fd, serialized, "utf8");
330
331
  fsyncSync(fd);
331
332
  closeSync(fd);
@@ -14,6 +14,7 @@ import {
14
14
  } from "node:fs";
15
15
  import { hostname } from "node:os";
16
16
  import { join, resolve } from "node:path";
17
+ import { stateDirMode, stateFileMode, stateModeTooOpen } from "../../storage/modes.ts";
17
18
  import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
18
19
  import { acquireNoClobberLease } from "../../workflow/workspaces/leases.ts";
19
20
  import type { FingerprintContextV3 } from "./canonical.ts";
@@ -78,8 +79,8 @@ export function readFingerprintKeyStoreV3(coordRoot: string): FingerprintKeyStor
78
79
 
79
80
  function readKeyStoreValue(path: string): unknown {
80
81
  const mode = statSync(path).mode & 0o777;
81
- if ((mode & 0o077) !== 0) {
82
- throw new Error("fingerprint key store permissions are not owner-only");
82
+ if (stateModeTooOpen(mode)) {
83
+ throw new Error("fingerprint key store is open to other users");
83
84
  }
84
85
  let parsed: unknown;
85
86
  try {
@@ -135,8 +136,8 @@ export function fingerprintContextV3(
135
136
 
136
137
  function withKeyStoreLock<T>(coordRoot: string, operation: () => T): T {
137
138
  const privateDir = join(resolve(coordRoot), ".harnery/private");
138
- mkdirSync(privateDir, { recursive: true, mode: 0o700 });
139
- chmodSync(privateDir, 0o700);
139
+ mkdirSync(privateDir, { recursive: true, mode: stateDirMode() });
140
+ chmodSync(privateDir, stateDirMode());
140
141
  const leasePath = join(privateDir, "fingerprint-key-lease");
141
142
  const authority = createHash("sha256").update(resolve(coordRoot)).digest("hex");
142
143
  let lease: ReturnType<typeof acquireNoClobberLease> | undefined;
@@ -178,12 +179,12 @@ function publishKeyStore(path: string, store: FingerprintKeyStoreV3, replacing:
178
179
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
179
180
  let fd: number | undefined;
180
181
  try {
181
- fd = openSync(temporary, "wx", 0o600);
182
+ fd = openSync(temporary, "wx", stateFileMode());
182
183
  writeFileSync(fd, `${JSON.stringify(store)}\n`, "utf8");
183
184
  fsyncSync(fd);
184
185
  closeSync(fd);
185
186
  fd = undefined;
186
- chmodSync(temporary, 0o600);
187
+ chmodSync(temporary, stateFileMode());
187
188
  if (!replacing && existsSync(path)) throw new Error("fingerprint key store already exists");
188
189
  renameSync(temporary, path);
189
190
  fsyncParentDirectory(path);
@@ -1,6 +1,6 @@
1
1
  /** Generated by scripts/generate-event-v3-contract.ts. Do not edit. */
2
2
  export const EVENT_V3_SCHEMA_DIGEST =
3
- "sha256:bdcee8d7839add0c786740ba9d5cfd0b6573f0ad4974bf53272737468734aa36" as const;
3
+ "sha256:07137e2cdf5eaed58d55a34e4036c87aa1f375d45c88274d07ad1cc69a6fe6f7" as const;
4
4
  export const EVENT_V3_GENERATED_CORE_TYPES = [
5
5
  "ledger.genesis",
6
6
  "ledger.activated",
@@ -12,6 +12,7 @@ import {
12
12
  writeSync,
13
13
  } from "node:fs";
14
14
  import { basename, join, resolve } from "node:path";
15
+ import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
15
16
 
16
17
  const DEFAULT_TTL_MS = 15 * 60 * 1_000;
17
18
  const HARD_MAX_TTL_MS = 60 * 60 * 1_000;
@@ -92,16 +93,16 @@ export function writeLiveDisplayV3(
92
93
  throw new Error("V3 live-display row exceeds its bounded size");
93
94
  }
94
95
  const root = liveRoot(coordRoot);
95
- mkdirSync(root, { recursive: true, mode: 0o700 });
96
- chmodSync(root, 0o700);
96
+ mkdirSync(root, { recursive: true, mode: stateDirMode() });
97
+ chmodSync(root, stateDirMode());
97
98
  const path = join(root, `${input.generation_id}.ndjson`);
98
- const fd = openSync(path, "a", 0o600);
99
+ const fd = openSync(path, "a", stateFileMode());
99
100
  try {
100
101
  writeSync(fd, serialized, undefined, "utf8");
101
102
  } finally {
102
103
  closeSync(fd);
103
104
  }
104
- chmodSync(path, 0o600);
105
+ chmodSync(path, stateFileMode());
105
106
  return row;
106
107
  }
107
108
 
@@ -15,6 +15,7 @@ import {
15
15
  } from "node:fs";
16
16
  import { hostname } from "node:os";
17
17
  import { join, resolve } from "node:path";
18
+ import { stateDirMode, stateFileMode, stateModeTooOpen } from "../../../storage/modes.ts";
18
19
  import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
19
20
  import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
20
21
  import { selectCommandParentSpan } from "../../span-parent.ts";
@@ -502,9 +503,9 @@ function commandStatePath(coordRoot: string, commandSource: `hid_${string}`): st
502
503
  function acquireCommandLease(coordRoot: string, statePath: string) {
503
504
  const directory = join(statePath, "..");
504
505
  const producerRoot = join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "private-producers");
505
- mkdirSync(directory, { recursive: true, mode: 0o700 });
506
- chmodSync(producerRoot, 0o700);
507
- chmodSync(directory, 0o700);
506
+ mkdirSync(directory, { recursive: true, mode: stateDirMode() });
507
+ chmodSync(producerRoot, stateDirMode());
508
+ chmodSync(directory, stateDirMode());
508
509
  return acquireNoClobberLease({
509
510
  path: `${statePath}.lease`,
510
511
  scope: "event-v3-command-producer",
@@ -522,13 +523,13 @@ function publishCommandState(path: string, state: CommandRecorderStateV3): void
522
523
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
523
524
  let fd: number | undefined;
524
525
  try {
525
- fd = openSync(temporary, "wx", 0o600);
526
+ fd = openSync(temporary, "wx", stateFileMode());
526
527
  writeFileSync(fd, `${JSON.stringify(state)}\n`, "utf8");
527
528
  fsyncSync(fd);
528
529
  closeSync(fd);
529
530
  fd = undefined;
530
531
  renameSync(temporary, path);
531
- chmodSync(path, 0o600);
532
+ chmodSync(path, stateFileMode());
532
533
  fsyncParentDirectory(path);
533
534
  } finally {
534
535
  if (fd !== undefined) closeSync(fd);
@@ -537,7 +538,8 @@ function publishCommandState(path: string, state: CommandRecorderStateV3): void
537
538
  }
538
539
 
539
540
  function readCommandState(path: string): CommandRecorderStateV3 {
540
- if ((statSync(path).mode & 0o077) !== 0) throw new Error("V3 command state is not owner-only");
541
+ if (stateModeTooOpen(statSync(path).mode))
542
+ throw new Error("V3 command state is open to other users");
541
543
  let parsed: unknown;
542
544
  try {
543
545
  parsed = JSON.parse(readFileSync(path, "utf8"));
@@ -15,6 +15,7 @@ import {
15
15
  } from "node:fs";
16
16
  import { hostname } from "node:os";
17
17
  import { basename, join, resolve } from "node:path";
18
+ import { stateDirMode, stateFileMode, stateModeTooOpen } from "../../../storage/modes.ts";
18
19
  import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
19
20
  import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
20
21
  import { EVENT_ADAPTER_IDS_V3, type EventAdapterIdV3 } from "../adapter-id.ts";
@@ -488,9 +489,9 @@ function coordinationProducerDirectory(coordRoot: string): string {
488
489
  function acquireCoordinationLease(coordRoot: string, statePath: string) {
489
490
  const directory = join(statePath, "..");
490
491
  const producerRoot = join(resolve(coordRoot), EVENT_V3_LEDGER_RELATIVE_ROOT, "private-producers");
491
- mkdirSync(directory, { recursive: true, mode: 0o700 });
492
- chmodSync(producerRoot, 0o700);
493
- chmodSync(directory, 0o700);
492
+ mkdirSync(directory, { recursive: true, mode: stateDirMode() });
493
+ chmodSync(producerRoot, stateDirMode());
494
+ chmodSync(directory, stateDirMode());
494
495
  return acquireNoClobberLease({
495
496
  path: `${statePath}.lease`,
496
497
  scope: "event-v3-coordination-producer",
@@ -508,13 +509,13 @@ function publishCoordinationState(path: string, state: CoordinationRecorderState
508
509
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
509
510
  let fd: number | undefined;
510
511
  try {
511
- fd = openSync(temporary, "wx", 0o600);
512
+ fd = openSync(temporary, "wx", stateFileMode());
512
513
  writeFileSync(fd, `${JSON.stringify(state)}\n`, "utf8");
513
514
  fsyncSync(fd);
514
515
  closeSync(fd);
515
516
  fd = undefined;
516
517
  renameSync(temporary, path);
517
- chmodSync(path, 0o600);
518
+ chmodSync(path, stateFileMode());
518
519
  fsyncParentDirectory(path);
519
520
  } finally {
520
521
  if (fd !== undefined) closeSync(fd);
@@ -523,8 +524,8 @@ function publishCoordinationState(path: string, state: CoordinationRecorderState
523
524
  }
524
525
 
525
526
  function readCoordinationState(path: string): CoordinationRecorderStateV3 {
526
- if ((statSync(path).mode & 0o077) !== 0) {
527
- throw new Error("V3 coordination state is not owner-only");
527
+ if (stateModeTooOpen(statSync(path).mode)) {
528
+ throw new Error("V3 coordination state is open to other users");
528
529
  }
529
530
  let parsed: unknown;
530
531
  try {
@@ -19,6 +19,7 @@ import {
19
19
  import { hostname } from "node:os";
20
20
  import { join, resolve } from "node:path";
21
21
  import { coordEnv } from "../../../../lib/env.ts";
22
+ import { stateDirMode, stateFileMode } from "../../../storage/modes.ts";
22
23
  import { fsyncParentDirectory } from "../../../workflow/durable-record.ts";
23
24
  import { acquireNoClobberLease } from "../../../workflow/workspaces/leases.ts";
24
25
  import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../writer.ts";
@@ -189,8 +190,8 @@ function summaryPath(root: string, category: string, window: string, digest: str
189
190
  }
190
191
 
191
192
  function ensureOwnerOnlyDirectory(path: string): void {
192
- mkdirSync(path, { recursive: true, mode: 0o700 });
193
- chmodSync(path, 0o700);
193
+ mkdirSync(path, { recursive: true, mode: stateDirMode() });
194
+ chmodSync(path, stateDirMode());
194
195
  }
195
196
 
196
197
  function publishOwnerOnlyJson(path: string, value: unknown, durable: boolean): void {
@@ -201,7 +202,7 @@ function publishOwnerOnlyJson(path: string, value: unknown, durable: boolean): v
201
202
  const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
202
203
  let fd: number | undefined;
203
204
  try {
204
- fd = openSync(temporary, "wx", 0o600);
205
+ fd = openSync(temporary, "wx", stateFileMode());
205
206
  writeFileSync(fd, contents, "utf8");
206
207
  if (durable) fsyncSync(fd);
207
208
  closeSync(fd);
@@ -401,7 +402,7 @@ function recordFailOpen(root: string, stage: string, nowMs: number): void {
401
402
  // Missing log starts fresh.
402
403
  }
403
404
  const line = JSON.stringify({ at: new Date(nowMs).toISOString(), stage, pid: process.pid });
404
- appendFileSync(path, `${line}\n`, { mode: 0o600 });
405
+ appendFileSync(path, `${line}\n`, { mode: stateFileMode() });
405
406
  } catch {
406
407
  // The emergency log may undercount when storage is unavailable; it is a
407
408
  // signal, not accounting, and must never throw into the producer path.
@@ -85,6 +85,7 @@ export interface TurnRitualEvidenceV3 {
85
85
  status_box_present_strict: boolean;
86
86
  session_name_required: boolean;
87
87
  session_name_present: boolean;
88
+ session_name_unavailable_reason?: string;
88
89
  }
89
90
 
90
91
  type TurnRitualObservationV3 = NonNullable<
@@ -478,10 +479,16 @@ function turnRitualObservation(
478
479
  return {
479
480
  status_box_present: observed(evidence.status_box_present),
480
481
  status_box_present_strict: observed(evidence.status_box_present_strict),
481
- session_name: observed({
482
- required: evidence.session_name_required,
483
- present: evidence.session_name_present,
484
- }),
482
+ session_name: evidence.session_name_unavailable_reason
483
+ ? {
484
+ state: "expected_but_missing",
485
+ capability: "assistant_reply_text",
486
+ reason: evidence.session_name_unavailable_reason,
487
+ }
488
+ : observed({
489
+ required: evidence.session_name_required,
490
+ present: evidence.session_name_present,
491
+ }),
485
492
  };
486
493
  }
487
494
 
@@ -4,6 +4,7 @@ import { adapterDurationSupportV3, adapterSignalSupportV3 } from "../capabilitie
4
4
  import type { EventOfTypeV3, EventTypeV3, SpanSummaryV3 } from "../contract.ts";
5
5
  import { EVENT_V3_CONTRACT_MAJOR, EVENT_V3_CONTRACT_NAME } from "../contract.ts";
6
6
  import { EVENT_V3_SCHEMA_DIGEST } from "../generated.ts";
7
+ import type { LineChanges } from "../tool-line-changes.ts";
7
8
  import {
8
9
  emptyHarnessTimingV3,
9
10
  extractTurnTelemetryV3,
@@ -19,6 +20,7 @@ import {
19
20
  } from "./hook-base.ts";
20
21
 
21
22
  export interface HookProducerContextV3 extends HookProducerContextV3Base {
23
+ tool_line_changes?: LineChanges;
22
24
  /** Required for terminal events; captured when the corresponding span opens. */
23
25
  terminal_span?: SpanSummaryV3;
24
26
  /** Required by V3 delegation starts so the child span is anchored in its parent tree. */
@@ -111,7 +113,23 @@ function terminalPayload(
111
113
  const span =
112
114
  duration === sourceSpan.duration_ms ? sourceSpan : { ...sourceSpan, duration_ms: duration };
113
115
  if (eventType === "tool.completed") {
114
- return { ...basePayload, duration_ms: duration, span };
116
+ const counts = context.tool_line_changes;
117
+ const response = source.tool_response as { isError?: boolean } | undefined;
118
+ return {
119
+ ...basePayload,
120
+ duration_ms: duration,
121
+ span,
122
+ line_changes:
123
+ counts &&
124
+ (basePayload as { outcome?: string }).outcome === "succeeded" &&
125
+ response?.isError !== true
126
+ ? { state: "observed", value: counts, attestation: "derived", confidence: "high" }
127
+ : {
128
+ state: "expected_but_missing",
129
+ reason: "tool_line_changes_unmeasured",
130
+ capability: "tool_line_changes",
131
+ },
132
+ };
115
133
  }
116
134
  if (eventType !== "turn.completed") return { ...basePayload, span };
117
135