harnery 0.40.0 → 0.42.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 (595) hide show
  1. package/README.md +1 -1
  2. package/dist/commander.js +4 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +34 -20
  5. package/dist/commands/artifacts.d.ts.map +1 -1
  6. package/dist/commands/artifacts.js +113 -12
  7. package/dist/commands/browse-ai.d.ts.map +1 -1
  8. package/dist/commands/browse-ai.js +4 -0
  9. package/dist/commands/browse.d.ts +164 -0
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +388 -45
  12. package/dist/commands/checkpoint.js +8 -12
  13. package/dist/commands/claude-desktop.d.ts.map +1 -1
  14. package/dist/commands/claude-desktop.js +52 -0
  15. package/dist/commands/deinit.d.ts +1 -1
  16. package/dist/commands/deinit.d.ts.map +1 -1
  17. package/dist/commands/deinit.js +8 -4
  18. package/dist/commands/disk.d.ts +6 -0
  19. package/dist/commands/disk.d.ts.map +1 -0
  20. package/dist/commands/disk.js +80 -0
  21. package/dist/commands/docs.d.ts.map +1 -1
  22. package/dist/commands/docs.js +2 -0
  23. package/dist/commands/doctor.d.ts +8 -0
  24. package/dist/commands/doctor.d.ts.map +1 -1
  25. package/dist/commands/doctor.js +58 -3
  26. package/dist/commands/fetch.d.ts.map +1 -1
  27. package/dist/commands/fetch.js +10 -0
  28. package/dist/commands/files.d.ts +16 -0
  29. package/dist/commands/files.d.ts.map +1 -1
  30. package/dist/commands/files.js +54 -2
  31. package/dist/commands/init.d.ts +21 -5
  32. package/dist/commands/init.d.ts.map +1 -1
  33. package/dist/commands/init.js +271 -109
  34. package/dist/commands/instructions.js +5 -4
  35. package/dist/commands/ledger-v3.d.ts.map +1 -1
  36. package/dist/commands/ledger-v3.js +2 -1
  37. package/dist/commands/rm.d.ts +4 -0
  38. package/dist/commands/rm.d.ts.map +1 -0
  39. package/dist/commands/rm.js +87 -0
  40. package/dist/commands/servers.d.ts +7 -0
  41. package/dist/commands/servers.d.ts.map +1 -0
  42. package/dist/commands/servers.js +394 -0
  43. package/dist/commands/tunnel.d.ts.map +1 -1
  44. package/dist/commands/tunnel.js +82 -2
  45. package/dist/commands/webview.d.ts +4 -0
  46. package/dist/commands/webview.d.ts.map +1 -0
  47. package/dist/commands/webview.js +94 -0
  48. package/dist/core/adapter.d.ts +50 -1
  49. package/dist/core/adapter.d.ts.map +1 -1
  50. package/dist/core/adapter.js +59 -1
  51. package/dist/core/adapters/attestation.d.ts.map +1 -1
  52. package/dist/core/adapters/attestation.js +3 -2
  53. package/dist/core/adapters/profiles.d.ts +29 -3
  54. package/dist/core/adapters/profiles.d.ts.map +1 -1
  55. package/dist/core/adapters/profiles.js +53 -1
  56. package/dist/core/adapters/registry.d.ts.map +1 -1
  57. package/dist/core/adapters/registry.js +33 -0
  58. package/dist/core/adapters/types.d.ts +9 -0
  59. package/dist/core/adapters/types.d.ts.map +1 -1
  60. package/dist/core/agents/canonical-emit.d.ts +1 -1
  61. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  62. package/dist/core/agents/canonical-emit.js +2 -7
  63. package/dist/core/agents/cli.js +25 -16
  64. package/dist/core/agents/coord-client.d.ts.map +1 -1
  65. package/dist/core/agents/coord-client.js +7 -2
  66. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  67. package/dist/core/agents/render/session-context.js +2 -0
  68. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  69. package/dist/core/agents/rules/claim-conflict.js +3 -9
  70. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  71. package/dist/core/agents/rules/commit-conflict.js +2 -8
  72. package/dist/core/agents/rules/stop-hook.d.ts +2 -0
  73. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  74. package/dist/core/agents/rules/stop-hook.js +1 -1
  75. package/dist/core/agents/session-events.d.ts +6 -2
  76. package/dist/core/agents/session-events.d.ts.map +1 -1
  77. package/dist/core/agents/session-events.js +105 -26
  78. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
  79. package/dist/core/agents/session-finalization-state-v3.js +2 -1
  80. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
  81. package/dist/core/agents/session-finalizer-v3.js +5 -4
  82. package/dist/core/agents/session-name-display.d.ts +28 -0
  83. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  84. package/dist/core/agents/session-name-display.js +43 -0
  85. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  86. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  87. package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
  88. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  89. package/dist/core/agents/state/heartbeat-writer.js +21 -2
  90. package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
  91. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  92. package/dist/core/agents/state/live-coordination-view.js +61 -0
  93. package/dist/core/agents/state/live-coordination-writer.js +3 -0
  94. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  95. package/dist/core/agents/state/stale-sweep.js +2 -10
  96. package/dist/core/agents/turn-changes.d.ts +17 -0
  97. package/dist/core/agents/turn-changes.d.ts.map +1 -0
  98. package/dist/core/agents/turn-changes.js +76 -0
  99. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  100. package/dist/core/artifacts/delivery-card.js +11 -3
  101. package/dist/core/artifacts/index.d.ts +140 -4
  102. package/dist/core/artifacts/index.d.ts.map +1 -1
  103. package/dist/core/artifacts/index.js +526 -52
  104. package/dist/core/config.d.ts +73 -11
  105. package/dist/core/config.d.ts.map +1 -1
  106. package/dist/core/config.js +142 -3
  107. package/dist/core/context/index.d.ts.map +1 -1
  108. package/dist/core/context/index.js +2 -1
  109. package/dist/core/conversations/archive.d.ts.map +1 -1
  110. package/dist/core/conversations/archive.js +4 -3
  111. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  112. package/dist/core/diagnostics/bundle.js +5 -4
  113. package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
  114. package/dist/core/events/legacy-storage/canary.js +5 -4
  115. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
  116. package/dist/core/events/legacy-storage/compression.js +2 -1
  117. package/dist/core/events/v3/adapter-id.d.ts +10 -2
  118. package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
  119. package/dist/core/events/v3/adapter-id.js +16 -0
  120. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  121. package/dist/core/events/v3/authority-outbox.js +2 -1
  122. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  123. package/dist/core/events/v3/bootstrap.js +5 -4
  124. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  125. package/dist/core/events/v3/capabilities.js +17 -0
  126. package/dist/core/events/v3/contract.d.ts +5 -0
  127. package/dist/core/events/v3/contract.d.ts.map +1 -1
  128. package/dist/core/events/v3/contract.js +7 -1
  129. package/dist/core/events/v3/control-witness.d.ts.map +1 -1
  130. package/dist/core/events/v3/control-witness.js +3 -2
  131. package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
  132. package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
  133. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
  134. package/dist/core/events/v3/fingerprint-keys.js +7 -6
  135. package/dist/core/events/v3/generated.d.ts +1 -1
  136. package/dist/core/events/v3/generated.js +1 -1
  137. package/dist/core/events/v3/live-feed.d.ts.map +1 -1
  138. package/dist/core/events/v3/live-feed.js +5 -4
  139. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  140. package/dist/core/events/v3/producers/command-recorder.js +8 -7
  141. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  142. package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
  143. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
  144. package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
  145. package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
  146. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  147. package/dist/core/events/v3/producers/hook-base.js +10 -4
  148. package/dist/core/events/v3/producers/hook.d.ts +2 -0
  149. package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
  150. package/dist/core/events/v3/producers/hook.js +16 -1
  151. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  152. package/dist/core/events/v3/producers/intake.js +16 -6
  153. package/dist/core/events/v3/producers/recorder.d.ts +2 -4
  154. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  155. package/dist/core/events/v3/producers/recorder.js +125 -101
  156. package/dist/core/events/v3/projection.js +9 -1
  157. package/dist/core/events/v3/recovery.d.ts.map +1 -1
  158. package/dist/core/events/v3/recovery.js +4 -3
  159. package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
  160. package/dist/core/events/v3/support-storage/inventory.js +2 -1
  161. package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
  162. package/dist/core/events/v3/support-storage/maintenance.js +4 -3
  163. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
  164. package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
  165. package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
  166. package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
  167. package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
  168. package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
  169. package/dist/core/events/v3/tool-line-changes.js +135 -0
  170. package/dist/core/events/v3/writer.d.ts.map +1 -1
  171. package/dist/core/events/v3/writer.js +8 -7
  172. package/dist/core/governor/planning.d.ts.map +1 -1
  173. package/dist/core/governor/planning.js +6 -5
  174. package/dist/core/governor/service.d.ts.map +1 -1
  175. package/dist/core/governor/service.js +12 -11
  176. package/dist/core/governor/state.d.ts.map +1 -1
  177. package/dist/core/governor/state.js +7 -6
  178. package/dist/core/guard/evaluator.js +1 -0
  179. package/dist/core/guard/storage.d.ts.map +1 -1
  180. package/dist/core/guard/storage.js +3 -2
  181. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
  182. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
  183. package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
  184. package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
  185. package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
  186. package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
  187. package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
  188. package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
  189. package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
  190. package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
  191. package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
  192. package/dist/core/hooks/adapter/behaviors/index.js +20 -0
  193. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
  194. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
  195. package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
  196. package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
  197. package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
  198. package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
  199. package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
  200. package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
  201. package/dist/core/hooks/adapter/behaviors/types.js +1 -0
  202. package/dist/core/hooks/adapter/detect.d.ts +1 -1
  203. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  204. package/dist/core/hooks/adapter/detect.js +2 -3
  205. package/dist/core/hooks/adapter/events.d.ts +46 -1
  206. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  207. package/dist/core/hooks/adapter/events.js +36 -0
  208. package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
  209. package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
  210. package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
  211. package/dist/core/hooks/adapter/output.d.ts +7 -0
  212. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  213. package/dist/core/hooks/adapter/output.js +8 -1
  214. package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
  215. package/dist/core/hooks/adapter/wiring.d.ts +52 -8
  216. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  217. package/dist/core/hooks/adapter/wiring.js +133 -12
  218. package/dist/core/hooks/cli.js +676 -531
  219. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  220. package/dist/core/hooks/effects/image-capture.js +2 -1
  221. package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
  222. package/dist/core/hooks/prompt-context/contract.js +1 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts +1 -1
  224. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  225. package/dist/core/hooks/resolve/transcript.js +35 -4
  226. package/dist/core/hooks/run-markers.d.ts +37 -0
  227. package/dist/core/hooks/run-markers.d.ts.map +1 -0
  228. package/dist/core/hooks/run-markers.js +109 -0
  229. package/dist/core/hooks/session-name-presence.d.ts +3 -1
  230. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  231. package/dist/core/hooks/session-name-presence.js +7 -2
  232. package/dist/core/hooks/shell-waiter.d.ts +2 -0
  233. package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
  234. package/dist/core/hooks/shell-waiter.js +173 -0
  235. package/dist/core/resources/service.d.ts.map +1 -1
  236. package/dist/core/resources/service.js +5 -4
  237. package/dist/core/semantic/review.d.ts.map +1 -1
  238. package/dist/core/semantic/review.js +5 -4
  239. package/dist/core/semantic/service.d.ts.map +1 -1
  240. package/dist/core/semantic/service.js +15 -14
  241. package/dist/core/semantic/storage.d.ts.map +1 -1
  242. package/dist/core/semantic/storage.js +5 -4
  243. package/dist/core/servers/index.d.ts +211 -0
  244. package/dist/core/servers/index.d.ts.map +1 -0
  245. package/dist/core/servers/index.js +560 -0
  246. package/dist/core/servers/net.d.ts +58 -0
  247. package/dist/core/servers/net.d.ts.map +1 -0
  248. package/dist/core/servers/net.js +270 -0
  249. package/dist/core/servers/tunnels.d.ts +14 -0
  250. package/dist/core/servers/tunnels.d.ts.map +1 -0
  251. package/dist/core/servers/tunnels.js +33 -0
  252. package/dist/core/storage/atomic-json.d.ts.map +1 -1
  253. package/dist/core/storage/atomic-json.js +7 -3
  254. package/dist/core/storage/builtins.js +1 -0
  255. package/dist/core/storage/durable-history.d.ts.map +1 -1
  256. package/dist/core/storage/durable-history.js +6 -5
  257. package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
  258. package/dist/core/storage/maintenance-providers.js +2 -1
  259. package/dist/core/storage/maintenance.d.ts.map +1 -1
  260. package/dist/core/storage/maintenance.js +7 -6
  261. package/dist/core/storage/metrics.d.ts.map +1 -1
  262. package/dist/core/storage/metrics.js +5 -1
  263. package/dist/core/storage/modes.d.ts +17 -0
  264. package/dist/core/storage/modes.d.ts.map +1 -0
  265. package/dist/core/storage/modes.js +47 -0
  266. package/dist/core/storage/process-log.d.ts.map +1 -1
  267. package/dist/core/storage/process-log.js +3 -2
  268. package/dist/core/storage/receipts.d.ts.map +1 -1
  269. package/dist/core/storage/receipts.js +4 -3
  270. package/dist/core/storage/segments.d.ts.map +1 -1
  271. package/dist/core/storage/segments.js +7 -6
  272. package/dist/core/supervisor/hook-health.d.ts +2 -1
  273. package/dist/core/supervisor/hook-health.d.ts.map +1 -1
  274. package/dist/core/supervisor/hook-health.js +4 -3
  275. package/dist/core/supervisor/service.d.ts.map +1 -1
  276. package/dist/core/supervisor/service.js +7 -6
  277. package/dist/core/work/state.d.ts.map +1 -1
  278. package/dist/core/work/state.js +8 -7
  279. package/dist/core/workflow/adapters.d.ts +7 -0
  280. package/dist/core/workflow/adapters.d.ts.map +1 -1
  281. package/dist/core/workflow/adapters.js +9 -0
  282. package/dist/core/workflow/approvals.d.ts.map +1 -1
  283. package/dist/core/workflow/approvals.js +6 -5
  284. package/dist/core/workflow/billing.d.ts.map +1 -1
  285. package/dist/core/workflow/billing.js +33 -4
  286. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  287. package/dist/core/workflow/durable-record.js +4 -3
  288. package/dist/core/workflow/engine.d.ts.map +1 -1
  289. package/dist/core/workflow/engine.js +30 -1
  290. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  291. package/dist/core/workflow/live-session-v3.js +2 -3
  292. package/dist/core/workflow/run-state.d.ts.map +1 -1
  293. package/dist/core/workflow/run-state.js +3 -2
  294. package/dist/core/workflow/sandbox-projection.d.ts +13 -2
  295. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  296. package/dist/core/workflow/sandbox-projection.js +23 -0
  297. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  298. package/dist/core/workflow/spawn-claude.js +6 -1
  299. package/dist/core/workflow/spawn-codex.d.ts +2 -0
  300. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  301. package/dist/core/workflow/spawn-codex.js +20 -1
  302. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  303. package/dist/core/workflow/spawn-cursor.js +6 -1
  304. package/dist/core/workflow/spawn-opencode.d.ts +36 -0
  305. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
  306. package/dist/core/workflow/spawn-opencode.js +135 -0
  307. package/dist/core/workflow/transcript.d.ts.map +1 -1
  308. package/dist/core/workflow/transcript.js +3 -2
  309. package/dist/core/workflow/types.d.ts +47 -2
  310. package/dist/core/workflow/types.d.ts.map +1 -1
  311. package/dist/core/workflow/worker-access.d.ts +92 -0
  312. package/dist/core/workflow/worker-access.d.ts.map +1 -0
  313. package/dist/core/workflow/worker-access.js +450 -0
  314. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  315. package/dist/core/workflow/workspaces/leases.js +5 -4
  316. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  317. package/dist/core/workflow/workspaces/local-git.js +3 -2
  318. package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
  319. package/dist/core/workflow/workspaces/paths.js +2 -1
  320. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  321. package/dist/core/workflow/workspaces/state.js +10 -9
  322. package/dist/lazy-commands.js +8 -1
  323. package/dist/lib/agent-browser/client.d.ts +9 -0
  324. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  325. package/dist/lib/agent-browser/client.js +13 -0
  326. package/dist/lib/browser/batch-steps.d.ts +18 -0
  327. package/dist/lib/browser/batch-steps.d.ts.map +1 -0
  328. package/dist/lib/browser/batch-steps.js +82 -0
  329. package/dist/lib/browser/client.d.ts +35 -1
  330. package/dist/lib/browser/client.d.ts.map +1 -1
  331. package/dist/lib/browser/client.js +117 -8
  332. package/dist/lib/browser/collect/collector.d.ts +99 -0
  333. package/dist/lib/browser/collect/collector.d.ts.map +1 -0
  334. package/dist/lib/browser/collect/collector.js +202 -0
  335. package/dist/lib/browser/collect/fields.d.ts +31 -0
  336. package/dist/lib/browser/collect/fields.d.ts.map +1 -0
  337. package/dist/lib/browser/collect/fields.js +75 -0
  338. package/dist/lib/browser/collect/index.d.ts +6 -0
  339. package/dist/lib/browser/collect/index.d.ts.map +1 -0
  340. package/dist/lib/browser/collect/index.js +5 -0
  341. package/dist/lib/browser/collect/page-driver.d.ts +14 -0
  342. package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
  343. package/dist/lib/browser/collect/page-driver.js +306 -0
  344. package/dist/lib/browser/collect/presets.d.ts +52 -0
  345. package/dist/lib/browser/collect/presets.d.ts.map +1 -0
  346. package/dist/lib/browser/collect/presets.js +176 -0
  347. package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
  348. package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
  349. package/dist/lib/browser/collect/scroll-plan.js +118 -0
  350. package/dist/lib/browser/identity.d.ts +25 -0
  351. package/dist/lib/browser/identity.d.ts.map +1 -0
  352. package/dist/lib/browser/identity.js +66 -0
  353. package/dist/lib/browser/index.d.ts +3 -2
  354. package/dist/lib/browser/index.d.ts.map +1 -1
  355. package/dist/lib/browser/index.js +2 -1
  356. package/dist/lib/browser/launch-args.d.ts +26 -0
  357. package/dist/lib/browser/launch-args.d.ts.map +1 -1
  358. package/dist/lib/browser/launch-args.js +41 -1
  359. package/dist/lib/browser/page-review-judge.d.ts +2 -0
  360. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  361. package/dist/lib/browser/page-review-judge.js +13 -0
  362. package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
  363. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  364. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  365. package/dist/lib/browser/qa-run.js +1 -0
  366. package/dist/lib/browser/user-agent.d.ts +53 -0
  367. package/dist/lib/browser/user-agent.d.ts.map +1 -0
  368. package/dist/lib/browser/user-agent.js +79 -0
  369. package/dist/lib/browser/windows-chrome.d.ts +59 -0
  370. package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
  371. package/dist/lib/browser/windows-chrome.js +179 -0
  372. package/dist/lib/claude-desktop-share.d.ts +136 -0
  373. package/dist/lib/claude-desktop-share.d.ts.map +1 -0
  374. package/dist/lib/claude-desktop-share.js +394 -0
  375. package/dist/lib/disk-usage.d.ts +50 -0
  376. package/dist/lib/disk-usage.d.ts.map +1 -0
  377. package/dist/lib/disk-usage.js +303 -0
  378. package/dist/lib/docs-lint.d.ts +6 -0
  379. package/dist/lib/docs-lint.d.ts.map +1 -1
  380. package/dist/lib/docs-lint.js +8 -3
  381. package/dist/lib/docs-metadata-managed.d.ts +1 -0
  382. package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
  383. package/dist/lib/docs-metadata-managed.js +17 -1
  384. package/dist/lib/guarded-remove.d.ts +34 -0
  385. package/dist/lib/guarded-remove.d.ts.map +1 -0
  386. package/dist/lib/guarded-remove.js +310 -0
  387. package/dist/lib/identities/assume.d.ts.map +1 -1
  388. package/dist/lib/identities/assume.js +4 -9
  389. package/dist/lib/instructions/apply.d.ts.map +1 -1
  390. package/dist/lib/instructions/apply.js +2 -0
  391. package/dist/lib/instructions/templates.js +8 -8
  392. package/dist/lib/pace/index.d.ts +2 -0
  393. package/dist/lib/pace/index.d.ts.map +1 -0
  394. package/dist/lib/pace/index.js +1 -0
  395. package/dist/lib/pace/pace.d.ts +95 -0
  396. package/dist/lib/pace/pace.d.ts.map +1 -0
  397. package/dist/lib/pace/pace.js +313 -0
  398. package/dist/lib/tunnel/error-page.d.ts +1 -1
  399. package/dist/lib/tunnel/error-page.d.ts.map +1 -1
  400. package/dist/lib/tunnel/error-page.js +19 -8
  401. package/dist/lib/tunnel/forward-headers.d.ts +11 -0
  402. package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
  403. package/dist/lib/tunnel/forward-headers.js +17 -0
  404. package/dist/lib/tunnel/gate.js +34 -10
  405. package/dist/lib/tunnel/path-scope.d.ts +22 -0
  406. package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
  407. package/dist/lib/tunnel/path-scope.js +62 -0
  408. package/dist/lib/tunnel/state.d.ts +5 -0
  409. package/dist/lib/tunnel/state.d.ts.map +1 -1
  410. package/dist/lib/tunnel/state.js +17 -3
  411. package/dist/lib/webview/index.d.ts +2 -0
  412. package/dist/lib/webview/index.d.ts.map +1 -0
  413. package/dist/lib/webview/index.js +1 -0
  414. package/dist/lib/webview/webview.d.ts +116 -0
  415. package/dist/lib/webview/webview.d.ts.map +1 -0
  416. package/dist/lib/webview/webview.js +289 -0
  417. package/opencode-plugin/package.json +19 -0
  418. package/opencode-plugin/src/index.ts +791 -0
  419. package/package.json +16 -4
  420. package/schemas/config.schema.json +104 -0
  421. package/schemas/event-v3.schema.json +164 -0
  422. package/src/commander.ts +26 -0
  423. package/src/commands/agents.ts +47 -18
  424. package/src/commands/artifacts.ts +171 -19
  425. package/src/commands/browse-ai.ts +8 -0
  426. package/src/commands/browse.ts +570 -48
  427. package/src/commands/checkpoint.ts +7 -9
  428. package/src/commands/claude-desktop.ts +61 -0
  429. package/src/commands/deinit.ts +9 -4
  430. package/src/commands/disk.ts +114 -0
  431. package/src/commands/docs.ts +2 -0
  432. package/src/commands/doctor.ts +63 -3
  433. package/src/commands/fetch.ts +14 -0
  434. package/src/commands/files.ts +93 -2
  435. package/src/commands/init.ts +334 -122
  436. package/src/commands/instructions.ts +8 -4
  437. package/src/commands/ledger-v3.ts +2 -1
  438. package/src/commands/rm.ts +115 -0
  439. package/src/commands/servers.ts +506 -0
  440. package/src/commands/tunnel.ts +102 -2
  441. package/src/commands/webview.ts +159 -0
  442. package/src/core/adapter.ts +102 -1
  443. package/src/core/adapters/attestation.ts +3 -2
  444. package/src/core/adapters/profiles.ts +63 -1
  445. package/src/core/adapters/registry.ts +37 -0
  446. package/src/core/adapters/types.ts +10 -0
  447. package/src/core/agents/canonical-emit.ts +2 -5
  448. package/src/core/agents/cli.ts +26 -17
  449. package/src/core/agents/coord-client.ts +9 -3
  450. package/src/core/agents/render/session-context.ts +1 -0
  451. package/src/core/agents/rules/claim-conflict.ts +3 -8
  452. package/src/core/agents/rules/commit-conflict.ts +2 -7
  453. package/src/core/agents/rules/stop-hook.ts +3 -1
  454. package/src/core/agents/session-events.ts +120 -26
  455. package/src/core/agents/session-finalization-state-v3.ts +2 -1
  456. package/src/core/agents/session-finalizer-v3.ts +5 -4
  457. package/src/core/agents/session-name-display.ts +48 -0
  458. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  459. package/src/core/agents/state/heartbeat-writer.ts +24 -2
  460. package/src/core/agents/state/live-coordination-view.ts +74 -0
  461. package/src/core/agents/state/live-coordination-writer.ts +3 -0
  462. package/src/core/agents/state/stale-sweep.ts +2 -9
  463. package/src/core/agents/turn-changes.ts +97 -0
  464. package/src/core/artifacts/delivery-card.ts +12 -3
  465. package/src/core/artifacts/index.ts +701 -47
  466. package/src/core/config.ts +206 -4
  467. package/src/core/context/index.ts +2 -1
  468. package/src/core/conversations/archive.ts +4 -3
  469. package/src/core/diagnostics/bundle.ts +5 -3
  470. package/src/core/events/legacy-storage/canary.ts +5 -4
  471. package/src/core/events/legacy-storage/compression.ts +2 -1
  472. package/src/core/events/v3/adapter-id.ts +29 -1
  473. package/src/core/events/v3/authority-outbox.ts +2 -1
  474. package/src/core/events/v3/bootstrap.ts +5 -4
  475. package/src/core/events/v3/capabilities.ts +17 -0
  476. package/src/core/events/v3/contract.ts +9 -1
  477. package/src/core/events/v3/control-witness.ts +3 -2
  478. package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
  479. package/src/core/events/v3/fingerprint-keys.ts +7 -6
  480. package/src/core/events/v3/generated.ts +1 -1
  481. package/src/core/events/v3/live-feed.ts +5 -4
  482. package/src/core/events/v3/producers/command-recorder.ts +8 -6
  483. package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
  484. package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
  485. package/src/core/events/v3/producers/hook-base.ts +11 -4
  486. package/src/core/events/v3/producers/hook.ts +19 -1
  487. package/src/core/events/v3/producers/intake.ts +18 -6
  488. package/src/core/events/v3/producers/recorder.ts +139 -114
  489. package/src/core/events/v3/projection.ts +11 -1
  490. package/src/core/events/v3/recovery.ts +4 -3
  491. package/src/core/events/v3/support-storage/inventory.ts +2 -1
  492. package/src/core/events/v3/support-storage/maintenance.ts +8 -3
  493. package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
  494. package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
  495. package/src/core/events/v3/tool-line-changes.ts +139 -0
  496. package/src/core/events/v3/writer.ts +8 -7
  497. package/src/core/governor/planning.ts +6 -5
  498. package/src/core/governor/service.ts +12 -11
  499. package/src/core/governor/state.ts +7 -6
  500. package/src/core/guard/evaluator.ts +1 -0
  501. package/src/core/guard/storage.ts +3 -2
  502. package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
  503. package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
  504. package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
  505. package/src/core/hooks/adapter/behaviors/index.ts +31 -0
  506. package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
  507. package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
  508. package/src/core/hooks/adapter/behaviors/types.ts +110 -0
  509. package/src/core/hooks/adapter/detect.ts +2 -3
  510. package/src/core/hooks/adapter/events.ts +72 -1
  511. package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
  512. package/src/core/hooks/adapter/output.ts +8 -1
  513. package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
  514. package/src/core/hooks/adapter/wiring.ts +198 -16
  515. package/src/core/hooks/cli.ts +776 -539
  516. package/src/core/hooks/effects/image-capture.ts +2 -1
  517. package/src/core/hooks/prompt-context/contract.ts +1 -0
  518. package/src/core/hooks/resolve/transcript.ts +42 -5
  519. package/src/core/hooks/run-markers.ts +132 -0
  520. package/src/core/hooks/session-name-presence.ts +10 -3
  521. package/src/core/hooks/shell-waiter.ts +166 -0
  522. package/src/core/resources/service.ts +5 -4
  523. package/src/core/semantic/review.ts +5 -4
  524. package/src/core/semantic/service.ts +15 -14
  525. package/src/core/semantic/storage.ts +5 -4
  526. package/src/core/servers/index.ts +712 -0
  527. package/src/core/servers/net.ts +283 -0
  528. package/src/core/servers/tunnels.ts +39 -0
  529. package/src/core/storage/atomic-json.ts +7 -3
  530. package/src/core/storage/builtins.ts +1 -0
  531. package/src/core/storage/durable-history.ts +6 -5
  532. package/src/core/storage/maintenance-providers.ts +2 -1
  533. package/src/core/storage/maintenance.ts +7 -6
  534. package/src/core/storage/metrics.ts +5 -1
  535. package/src/core/storage/modes.ts +53 -0
  536. package/src/core/storage/process-log.ts +3 -2
  537. package/src/core/storage/receipts.ts +4 -3
  538. package/src/core/storage/segments.ts +7 -6
  539. package/src/core/supervisor/hook-health.ts +4 -4
  540. package/src/core/supervisor/service.ts +7 -6
  541. package/src/core/work/state.ts +8 -7
  542. package/src/core/workflow/adapters.ts +15 -0
  543. package/src/core/workflow/approvals.ts +6 -5
  544. package/src/core/workflow/billing.ts +34 -4
  545. package/src/core/workflow/durable-record.ts +4 -3
  546. package/src/core/workflow/engine.ts +38 -1
  547. package/src/core/workflow/live-session-v3.ts +2 -3
  548. package/src/core/workflow/run-state.ts +3 -2
  549. package/src/core/workflow/sandbox-projection.ts +38 -2
  550. package/src/core/workflow/spawn-claude.ts +5 -1
  551. package/src/core/workflow/spawn-codex.ts +18 -1
  552. package/src/core/workflow/spawn-cursor.ts +5 -1
  553. package/src/core/workflow/spawn-opencode.ts +153 -0
  554. package/src/core/workflow/transcript.ts +3 -2
  555. package/src/core/workflow/types.ts +62 -2
  556. package/src/core/workflow/worker-access.ts +525 -0
  557. package/src/core/workflow/workspaces/leases.ts +5 -4
  558. package/src/core/workflow/workspaces/local-git.ts +3 -2
  559. package/src/core/workflow/workspaces/paths.ts +2 -1
  560. package/src/core/workflow/workspaces/state.ts +10 -9
  561. package/src/lazy-commands.ts +11 -1
  562. package/src/lib/agent-browser/client.ts +18 -0
  563. package/src/lib/browser/batch-steps.ts +79 -0
  564. package/src/lib/browser/client.ts +147 -9
  565. package/src/lib/browser/collect/collector.ts +306 -0
  566. package/src/lib/browser/collect/fields.ts +87 -0
  567. package/src/lib/browser/collect/index.ts +47 -0
  568. package/src/lib/browser/collect/page-driver.ts +327 -0
  569. package/src/lib/browser/collect/presets.ts +237 -0
  570. package/src/lib/browser/collect/scroll-plan.ts +179 -0
  571. package/src/lib/browser/identity.ts +64 -0
  572. package/src/lib/browser/index.ts +16 -1
  573. package/src/lib/browser/launch-args.ts +53 -1
  574. package/src/lib/browser/page-review-judge.ts +16 -0
  575. package/src/lib/browser/qa-run-contracts.ts +11 -0
  576. package/src/lib/browser/qa-run.ts +1 -0
  577. package/src/lib/browser/user-agent.ts +130 -0
  578. package/src/lib/browser/windows-chrome.ts +227 -0
  579. package/src/lib/claude-desktop-share.ts +511 -0
  580. package/src/lib/disk-usage.ts +347 -0
  581. package/src/lib/docs-lint.ts +8 -3
  582. package/src/lib/docs-metadata-managed.ts +21 -0
  583. package/src/lib/guarded-remove.ts +376 -0
  584. package/src/lib/identities/assume.ts +7 -7
  585. package/src/lib/instructions/apply.ts +2 -0
  586. package/src/lib/instructions/templates.ts +8 -8
  587. package/src/lib/pace/index.ts +14 -0
  588. package/src/lib/pace/pace.ts +370 -0
  589. package/src/lib/tunnel/error-page.ts +25 -9
  590. package/src/lib/tunnel/forward-headers.ts +16 -0
  591. package/src/lib/tunnel/gate.ts +39 -10
  592. package/src/lib/tunnel/path-scope.ts +73 -0
  593. package/src/lib/tunnel/state.ts +20 -3
  594. package/src/lib/webview/index.ts +13 -0
  595. package/src/lib/webview/webview.ts +457 -0
@@ -4,12 +4,20 @@ import { resolve } from "node:path";
4
4
  import type { Command } from "commander";
5
5
  import type { EmitContext, HarneryProgramContext } from "../commander.ts";
6
6
 
7
+ import { resolveCoordRoot } from "../core/agents/coord-client.ts";
7
8
  import { resolveBinName } from "../core/config.ts";
9
+ import {
10
+ tryRegisterServer,
11
+ tunnelServerId,
12
+ tunnelServerInput,
13
+ unregisterServer,
14
+ } from "../core/servers/index.ts";
8
15
  import {
9
16
  processLogDestination,
10
17
  runRotatingProcessSync,
11
18
  spawnRotatingProcess,
12
19
  } from "../core/storage/process-log.ts";
20
+ import { ALLOW_PATHS_ENV, normalizeAllowPaths } from "../lib/tunnel/path-scope.ts";
13
21
  import {
14
22
  clearState,
15
23
  DEFAULT_INSTANCE,
@@ -35,6 +43,8 @@ import {
35
43
  * upstream. Cloudflare quick tunnels add an IP allowlist at the gate via the
36
44
  * Cloudflare-set `CF-Connecting-IP` header; Tailscale Serve/Funnel exposes the
37
45
  * same gate through tailscaled, with Tailscale owning the access boundary.
46
+ * Every provider also passes through a path scope: `up` requires at least one
47
+ * `--allow-path <prefix>`, and the gate refuses any request outside it.
38
48
  *
39
49
  * State + config persisted under `.cache/tunnel/`. cloudflared auto-installs
40
50
  * to ~/.local/bin/ on first run (Linux only; macOS users `brew install`);
@@ -55,6 +65,7 @@ interface UpOpts {
55
65
  visibility?: string;
56
66
  path?: string;
57
67
  httpsPort?: string;
68
+ allowPath?: string[];
58
69
  }
59
70
 
60
71
  interface DownOpts {
@@ -165,6 +176,46 @@ function resolveHttpsPort(raw: string | undefined): number {
165
176
  return port;
166
177
  }
167
178
 
179
+ /**
180
+ * The path scope for `up`. At least one prefix is required: a tunnel forwards
181
+ * to a whole local app, and publishing all of it has to be a stated choice
182
+ * (`--allow-path /`), never the result of leaving a flag off.
183
+ */
184
+ function resolveAllowPaths(raw: string[] | undefined): string[] {
185
+ let paths: string[];
186
+ try {
187
+ paths = normalizeAllowPaths(raw ?? []);
188
+ } catch (error) {
189
+ emit.error({
190
+ code: "tunnel_path_scope_invalid",
191
+ message: error instanceof Error ? error.message : String(error),
192
+ });
193
+ process.exit(1);
194
+ }
195
+ if (paths.length === 0) {
196
+ emit.error({
197
+ code: "tunnel_path_scope_empty",
198
+ message:
199
+ "No path scope; refusing to start. Name what the tunnel shares with --allow-path <prefix> " +
200
+ "(repeatable), or pass --allow-path / to publish the whole upstream.",
201
+ });
202
+ process.exit(1);
203
+ }
204
+ return paths;
205
+ }
206
+
207
+ function samePaths(a: readonly string[], b: readonly string[]): boolean {
208
+ return a.length === b.length && [...a].sort().join("\n") === [...b].sort().join("\n");
209
+ }
210
+
211
+ function formatPaths(paths: readonly string[]): string {
212
+ return paths.length === 0 ? "(none: every path refused)" : paths.join(", ");
213
+ }
214
+
215
+ function collectPath(value: string, previous: string[]): string[] {
216
+ return [...previous, value];
217
+ }
218
+
168
219
  function gateScriptPath(): string {
169
220
  return resolve(import.meta.dirname, "..", "lib", "tunnel", "gate.ts");
170
221
  }
@@ -444,6 +495,7 @@ interface GateSpawnOpts {
444
495
  vhost: string;
445
496
  provider: TunnelProvider;
446
497
  allowedIps: string[];
498
+ allowPaths: string[];
447
499
  gateLogPath: string;
448
500
  }
449
501
 
@@ -470,6 +522,7 @@ function spawnGate(o: GateSpawnOpts): ChildProcess {
470
522
  HARNERY_TUNNEL_TARGET: o.target,
471
523
  HARNERY_TUNNEL_VHOST: o.vhost,
472
524
  HARNERY_TUNNEL_PORT: String(o.gatePort),
525
+ [ALLOW_PATHS_ENV]: o.allowPaths.join(","),
473
526
  },
474
527
  });
475
528
  gateProc.unref();
@@ -498,6 +551,7 @@ async function up(opts: UpOpts): Promise<void> {
498
551
  provider === "tailscale" ? resolveTailscalePath(opts.path, name) : undefined;
499
552
  const tailscaleHttpsPort =
500
553
  provider === "tailscale" ? resolveHttpsPort(opts.httpsPort) : undefined;
554
+ const allowPaths = resolveAllowPaths(opts.allowPath);
501
555
 
502
556
  const existing = readState(name);
503
557
  if (existing && tunnelIsAlive(existing)) {
@@ -508,9 +562,19 @@ async function up(opts: UpOpts): Promise<void> {
508
562
  });
509
563
  process.exit(1);
510
564
  }
565
+ if (!samePaths(existing.allow_paths, allowPaths)) {
566
+ emit.error({
567
+ code: "tunnel_instance_in_use",
568
+ message:
569
+ `Tunnel [${name}] is already up sharing ${formatPaths(existing.allow_paths)}. ` +
570
+ `Stop it before starting it with ${formatPaths(allowPaths)}.`,
571
+ });
572
+ process.exit(1);
573
+ }
511
574
  emit.text(`Already up [${name}]: ${existing.url}\n`);
512
575
  emit.text(` Provider: ${existing.provider}\n`);
513
576
  emit.text(` Forwarding: ${existing.target} (Host: ${existing.vhost})\n`);
577
+ emit.text(` Paths: ${formatPaths(existing.allow_paths)}\n`);
514
578
  return;
515
579
  }
516
580
  if (existing) downOne(name, new Set());
@@ -552,6 +616,7 @@ async function up(opts: UpOpts): Promise<void> {
552
616
  vhost,
553
617
  provider,
554
618
  allowedIps: cfg.allowed_ips,
619
+ allowPaths,
555
620
  gateLogPath,
556
621
  });
557
622
 
@@ -630,11 +695,13 @@ async function up(opts: UpOpts): Promise<void> {
630
695
  target,
631
696
  vhost,
632
697
  gate_port: gatePort,
698
+ allow_paths: allowPaths,
633
699
  tailscale_mode: tailscaleMode,
634
700
  tailscale_path: tailscalePath,
635
701
  tailscale_https_port: tailscaleHttpsPort,
636
702
  };
637
703
  writeState(state);
704
+ recordTunnelServer(state);
638
705
 
639
706
  const stopHint =
640
707
  name === DEFAULT_INSTANCE ? "harn tunnel down" : `harn tunnel down --name ${name}`;
@@ -642,6 +709,7 @@ async function up(opts: UpOpts): Promise<void> {
642
709
  emit.text(` Provider: ${provider}${tailscaleMode ? ` (${tailscaleMode})` : ""}\n`);
643
710
  emit.text(` URL: ${url}\n\n`);
644
711
  emit.text(` Forwarding: ${target} (Host: ${vhost})\n`);
712
+ emit.text(` Paths: ${formatPaths(allowPaths)}\n`);
645
713
  emit.text(` Gate port: ${gatePort}\n`);
646
714
  if (provider === "cloudflare") {
647
715
  emit.text(` Allowed IPs: ${cfg.allowed_ips.join(", ")}\n\n`);
@@ -662,6 +730,16 @@ async function up(opts: UpOpts): Promise<void> {
662
730
  emit.text(" Status: harn tunnel status\n");
663
731
  }
664
732
 
733
+ /** The server registry lists tunnels beside every other local server. */
734
+ function registryRoot(): string {
735
+ return resolveCoordRoot() ?? process.cwd();
736
+ }
737
+
738
+ function recordTunnelServer(state: TunnelState): void {
739
+ const coordRoot = registryRoot();
740
+ tryRegisterServer(tunnelServerInput(state, coordRoot), { coordRoot });
741
+ }
742
+
665
743
  /** Tear down a single instance by name. Returns the number of processes killed. */
666
744
  function downOne(name: string, killed: Set<number>): number {
667
745
  const before = killed.size;
@@ -684,6 +762,7 @@ function downOne(name: string, killed: Set<number>): number {
684
762
  // lost; they'd otherwise squat on the port and break the next `up`.
685
763
  sweepStrays(state?.gate_port ?? DEFAULT_GATE_PORT, killed);
686
764
  clearState(name);
765
+ unregisterServer(tunnelServerId(name), { coordRoot: registryRoot() });
687
766
  return killed.size - before;
688
767
  }
689
768
 
@@ -771,6 +850,15 @@ export async function reloadOne(state: TunnelState): Promise<{ ok: boolean; mess
771
850
  if (!bunAvailable()) {
772
851
  return { ok: false, message: `[${name}] bun is not on PATH, so the gate can't be respawned.` };
773
852
  }
853
+ if (state.allow_paths.length === 0) {
854
+ return {
855
+ ok: false,
856
+ message:
857
+ `[${name}] has no recorded path scope, so a reloaded gate would refuse every request. ` +
858
+ `Restart it with \`${bin} tunnel down --name ${name}\` and ` +
859
+ `\`${bin} tunnel up --name ${name} --allow-path <prefix>\`.`,
860
+ };
861
+ }
774
862
 
775
863
  // Kill ONLY the gate. sweepStrays() is deliberately avoided here: it also
776
864
  // matches `--url http://localhost:<port>`, which is the provider process we
@@ -803,6 +891,7 @@ export async function reloadOne(state: TunnelState): Promise<{ ok: boolean; mess
803
891
  vhost: state.vhost,
804
892
  provider: state.provider,
805
893
  allowedIps: cfg.allowed_ips,
894
+ allowPaths: state.allow_paths,
806
895
  gateLogPath: tunnelLogDestinations(name, state.provider).gate,
807
896
  });
808
897
 
@@ -822,6 +911,7 @@ export async function reloadOne(state: TunnelState): Promise<{ ok: boolean; mess
822
911
  }
823
912
 
824
913
  writeState({ ...state, gate_pid: pid });
914
+ recordTunnelServer({ ...state, gate_pid: pid });
825
915
  const allowNote =
826
916
  state.provider === "cloudflare" ? ` Allowlist: ${cfg.allowed_ips.length} IP(s).` : "";
827
917
  return { ok: true, message: `[${name}] gate reloaded, URL unchanged: ${state.url}.${allowNote}` };
@@ -898,6 +988,7 @@ function statusDetail(state: TunnelState): void {
898
988
  );
899
989
  emit.text(` URL: ${state.url}\n`);
900
990
  emit.text(` Forwarding: ${state.target} (Host: ${state.vhost})\n`);
991
+ emit.text(` Paths: ${formatPaths(state.allow_paths)}\n`);
901
992
  emit.text(` Gate port: ${state.gate_port}\n`);
902
993
  if (state.provider === "cloudflare") {
903
994
  emit.text(` Allowed IPs: ${cfg.allowed_ips.join(", ")}\n`);
@@ -941,6 +1032,7 @@ function status(opts: StatusOpts): void {
941
1032
  state: instanceState(s),
942
1033
  url: s.url,
943
1034
  fwd: `${s.target} (${s.vhost})`,
1035
+ paths: formatPaths(s.allow_paths),
944
1036
  port: String(s.gate_port),
945
1037
  up: fmtUptime(s.started_at),
946
1038
  }));
@@ -950,15 +1042,16 @@ function status(opts: StatusOpts): void {
950
1042
  state: 5,
951
1043
  url: Math.max(3, ...rows.map((r) => r.url.length)),
952
1044
  fwd: Math.max(10, ...rows.map((r) => r.fwd.length)),
1045
+ paths: Math.max(5, ...rows.map((r) => r.paths.length)),
953
1046
  port: 4,
954
1047
  };
955
1048
  const pad = (s: string, n: number) => s.padEnd(n);
956
1049
  emit.text(
957
- `${pad("NAME", w.name)} ${pad("PROVIDER", w.provider)} ${pad("STATE", w.state)} ${pad("URL", w.url)} ${pad("FORWARDING", w.fwd)} ${pad("PORT", w.port)} UPTIME\n`,
1050
+ `${pad("NAME", w.name)} ${pad("PROVIDER", w.provider)} ${pad("STATE", w.state)} ${pad("URL", w.url)} ${pad("FORWARDING", w.fwd)} ${pad("PATHS", w.paths)} ${pad("PORT", w.port)} UPTIME\n`,
958
1051
  );
959
1052
  for (const r of rows) {
960
1053
  emit.text(
961
- `${pad(r.name, w.name)} ${pad(r.provider, w.provider)} ${pad(r.state, w.state)} ${pad(r.url, w.url)} ${pad(r.fwd, w.fwd)} ${pad(r.port, w.port)} ${r.up}\n`,
1054
+ `${pad(r.name, w.name)} ${pad(r.provider, w.provider)} ${pad(r.state, w.state)} ${pad(r.url, w.url)} ${pad(r.fwd, w.fwd)} ${pad(r.paths, w.paths)} ${pad(r.port, w.port)} ${r.up}\n`,
962
1055
  );
963
1056
  }
964
1057
  }
@@ -1071,6 +1164,13 @@ export function registerTunnelCommand(
1071
1164
  "Tailscale only: URL path mount (default: / for default, /<name> for named instances)",
1072
1165
  )
1073
1166
  .option("--https-port <port>", "Tailscale only: HTTPS listen port (default: 443)", "443")
1167
+ .option(
1168
+ "--allow-path <prefix>",
1169
+ "URL path prefix the tunnel shares (repeatable, required). Every other path is refused; " +
1170
+ "pass / to publish the whole upstream.",
1171
+ collectPath,
1172
+ [],
1173
+ )
1074
1174
  .action(up);
1075
1175
 
1076
1176
  cmd
@@ -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) {
@@ -45,6 +45,18 @@ export const BUILTIN_ADAPTER_PROFILES = {
45
45
  modelFamily: "claude",
46
46
  effortValues: ["low", "medium", "high", "xhigh", "max"],
47
47
  verified: { date: "2026-08-18", version: "2.1.233 (Claude Code)" },
48
+ // Checked against 2.1.283 (ADR 0192). Claude Code has no sandbox mode flag;
49
+ // its permission mode and its optional Bash sandbox are separate, so full
50
+ // access needs both: bypass permissions, and a settings layer that turns
51
+ // the Bash sandbox off in case a user or project settings file enabled it.
52
+ fullAccess: {
53
+ argv: [
54
+ "--permission-mode",
55
+ "bypassPermissions",
56
+ "--settings",
57
+ '{"sandbox":{"enabled":false}}',
58
+ ],
59
+ },
48
60
  capabilities: capabilities({
49
61
  effortSelection: supported("Mapped to `--effort <level>`."),
50
62
  maxTurns: supported("Mapped to `--max-turns <n>`."),
@@ -78,6 +90,9 @@ export const BUILTIN_ADAPTER_PROFILES = {
78
90
  modes: { "read-only": "read-only", "workspace-write": "workspace-write" },
79
91
  writableRoots: true,
80
92
  },
93
+ // Checked against codex-cli 0.155.1 (ADR 0192): `--sandbox` accepts
94
+ // danger-full-access, and `codex exec` never prompts for approval.
95
+ fullAccess: { argv: ["--sandbox", "danger-full-access"] },
81
96
  capabilities: capabilities({
82
97
  effortSelection: supported('Mapped to `-c model_reasoning_effort="<level>"`.'),
83
98
  filesystemPolicyProjection: supported(
@@ -106,7 +121,11 @@ export const BUILTIN_ADAPTER_PROFILES = {
106
121
  authModel: "own-auth",
107
122
  modelFamily: "multi",
108
123
  effortValues: [],
109
- verified: { date: "2026-08-18", version: "2026.08.11-e8db854" },
124
+ verified: { date: "2026-09-20", version: "2026.09.18-9a7762b" },
125
+ // Checked against 2026.09.26-dd393fe (ADR 0192): `--sandbox disabled`
126
+ // overrides the configured sandbox. Cursor carries effort inside the model
127
+ // id, so a full-access floor for it names exact model ids.
128
+ fullAccess: { argv: ["--sandbox", "disabled"] },
110
129
  capabilities: capabilities({
111
130
  effortSelection: unsupported(
112
131
  "Cursor embeds effort in some parameterized model ids; Harnery does not rewrite model ids.",
@@ -114,6 +133,10 @@ export const BUILTIN_ADAPTER_PROFILES = {
114
133
  maxTurns: unsupported("cursor-agent exposes no turn-ceiling flag."),
115
134
  sessionId: supported("Read from the JSON result envelope."),
116
135
  cost: unsupported("The JSON result envelope carries no cost."),
136
+ // Still unknown on 2026-09-20 against cursor-agent 2026.09.18: the live
137
+ // `cursor_composer_context_percent` reader serves IDE hook sessions from
138
+ // the composer `state.vscdb`, and headless `cursor-agent` children (this
139
+ // workflow adapter) leave no composer row and no usage in the JSON result.
117
140
  contextTelemetry: unknown("No stable Cursor context-usage payload has been certified."),
118
141
  preCompactionSignal: supported("Cursor preCompact is wired to a durable checkpoint."),
119
142
  postCompactionSignal: unsupported(
@@ -121,6 +144,45 @@ export const BUILTIN_ADAPTER_PROFILES = {
121
144
  ),
122
145
  }),
123
146
  },
147
+ opencode: {
148
+ id: "opencode",
149
+ displayName: "OpenCode",
150
+ binary: "opencode",
151
+ installHint: "curl -fsSL https://opencode.ai/install | bash",
152
+ loginHint: "opencode auth login",
153
+ apiKeyEnv: "OPENCODE_API_KEY",
154
+ integrationMode: "cli-subprocess",
155
+ authModel: "own-auth",
156
+ modelFamily: "multi",
157
+ // Model reasoning is selected through the `provider/model#variant` id;
158
+ // OpenCode exposes no separate effort dial for Harnery to map. It also has
159
+ // no vendor sandbox to remove, so it declares no full-access rendering and
160
+ // a worker on it is never granted one (ADR 0192).
161
+ effortValues: [],
162
+ verified: { date: "2026-09-20", version: "opencode v2.0.11" },
163
+ capabilities: capabilities({
164
+ effortSelection: unsupported(
165
+ "OpenCode selects reasoning through the model id variant; there is no separate effort flag.",
166
+ ),
167
+ maxTurns: unsupported("`opencode run` exposes no turn-ceiling flag."),
168
+ sessionId: supported("Read from the `sessionID` on every `--format json` NDJSON line."),
169
+ cost: unsupported(
170
+ "`opencode run --format json` streams parts without a final cost; cost lives in `session export`.",
171
+ ),
172
+ // Tool evidence is delivered on the event axis by the Harnery OpenCode
173
+ // plugin, not by the final-result workflow spawn, which retains no tool
174
+ // events (as with Codex and Cursor).
175
+ contextTelemetry: unknown(
176
+ "OpenCode stores context in SQLite; no context-window bridge is certified yet.",
177
+ ),
178
+ preCompactionSignal: supported(
179
+ 'OpenCode session.hook("compaction") is bridged to a durable checkpoint.',
180
+ ),
181
+ postCompactionSignal: unsupported(
182
+ "No post-compact hook is wired; OpenCode post-compaction recovery is not yet certified.",
183
+ ),
184
+ }),
185
+ },
124
186
  } as const satisfies Record<string, AdapterProfile>;
125
187
 
126
188
  export type BuiltinAdapterId = keyof typeof BUILTIN_ADAPTER_PROFILES;