harnery 0.0.1 → 0.2.1

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 (445) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/bin/agent-coord +42 -0
  4. package/bin/agent-hook +44 -0
  5. package/bin/harn +40 -0
  6. package/dist/cli.d.ts +9 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +18 -0
  9. package/dist/commander.d.ts +128 -0
  10. package/dist/commander.d.ts.map +1 -0
  11. package/dist/commander.js +126 -0
  12. package/dist/commands/agents.d.ts +18 -0
  13. package/dist/commands/agents.d.ts.map +1 -0
  14. package/dist/commands/agents.js +3946 -0
  15. package/dist/commands/backup.d.ts +22 -0
  16. package/dist/commands/backup.d.ts.map +1 -0
  17. package/dist/commands/backup.js +262 -0
  18. package/dist/commands/browse-ai.d.ts +4 -0
  19. package/dist/commands/browse-ai.d.ts.map +1 -0
  20. package/dist/commands/browse-ai.js +156 -0
  21. package/dist/commands/browse.d.ts +4 -0
  22. package/dist/commands/browse.d.ts.map +1 -0
  23. package/dist/commands/browse.js +590 -0
  24. package/dist/commands/callers.d.ts +4 -0
  25. package/dist/commands/callers.d.ts.map +1 -0
  26. package/dist/commands/callers.js +276 -0
  27. package/dist/commands/completion.d.ts +17 -0
  28. package/dist/commands/completion.d.ts.map +1 -0
  29. package/dist/commands/completion.js +158 -0
  30. package/dist/commands/config-get.d.ts +4 -0
  31. package/dist/commands/config-get.d.ts.map +1 -0
  32. package/dist/commands/config-get.js +131 -0
  33. package/dist/commands/context.d.ts +11 -0
  34. package/dist/commands/context.d.ts.map +1 -0
  35. package/dist/commands/context.js +185 -0
  36. package/dist/commands/cookies.d.ts +4 -0
  37. package/dist/commands/cookies.d.ts.map +1 -0
  38. package/dist/commands/cookies.js +140 -0
  39. package/dist/commands/docs.d.ts +4 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +137 -0
  42. package/dist/commands/doctor.d.ts +25 -0
  43. package/dist/commands/doctor.d.ts.map +1 -0
  44. package/dist/commands/doctor.js +200 -0
  45. package/dist/commands/edit-batch.d.ts +18 -0
  46. package/dist/commands/edit-batch.d.ts.map +1 -0
  47. package/dist/commands/edit-batch.js +172 -0
  48. package/dist/commands/eml.d.ts +4 -0
  49. package/dist/commands/eml.d.ts.map +1 -0
  50. package/dist/commands/eml.js +428 -0
  51. package/dist/commands/env.d.ts +4 -0
  52. package/dist/commands/env.d.ts.map +1 -0
  53. package/dist/commands/env.js +201 -0
  54. package/dist/commands/fetch.d.ts +4 -0
  55. package/dist/commands/fetch.d.ts.map +1 -0
  56. package/dist/commands/fetch.js +99 -0
  57. package/dist/commands/file-history.d.ts +4 -0
  58. package/dist/commands/file-history.d.ts.map +1 -0
  59. package/dist/commands/file-history.js +152 -0
  60. package/dist/commands/grep.d.ts +4 -0
  61. package/dist/commands/grep.d.ts.map +1 -0
  62. package/dist/commands/grep.js +317 -0
  63. package/dist/commands/init.d.ts +82 -0
  64. package/dist/commands/init.d.ts.map +1 -0
  65. package/dist/commands/init.js +288 -0
  66. package/dist/commands/outline.d.ts +4 -0
  67. package/dist/commands/outline.d.ts.map +1 -0
  68. package/dist/commands/outline.js +509 -0
  69. package/dist/commands/presence.d.ts +12 -0
  70. package/dist/commands/presence.d.ts.map +1 -0
  71. package/dist/commands/presence.js +123 -0
  72. package/dist/commands/read.d.ts +7 -0
  73. package/dist/commands/read.d.ts.map +1 -0
  74. package/dist/commands/read.js +46 -0
  75. package/dist/commands/scratch.d.ts +4 -0
  76. package/dist/commands/scratch.d.ts.map +1 -0
  77. package/dist/commands/scratch.js +426 -0
  78. package/dist/commands/session.d.ts +4 -0
  79. package/dist/commands/session.d.ts.map +1 -0
  80. package/dist/commands/session.js +162 -0
  81. package/dist/commands/sync.d.ts +24 -0
  82. package/dist/commands/sync.d.ts.map +1 -0
  83. package/dist/commands/sync.js +275 -0
  84. package/dist/commands/toc.d.ts +5 -0
  85. package/dist/commands/toc.d.ts.map +1 -0
  86. package/dist/commands/toc.js +153 -0
  87. package/dist/commands/tokens.d.ts +4 -0
  88. package/dist/commands/tokens.d.ts.map +1 -0
  89. package/dist/commands/tokens.js +48 -0
  90. package/dist/commands/tunnel.d.ts +4 -0
  91. package/dist/commands/tunnel.d.ts.map +1 -0
  92. package/dist/commands/tunnel.js +513 -0
  93. package/dist/commands/uninstall.d.ts +22 -0
  94. package/dist/commands/uninstall.d.ts.map +1 -0
  95. package/dist/commands/uninstall.js +126 -0
  96. package/dist/commands/web.d.ts +4 -0
  97. package/dist/commands/web.d.ts.map +1 -0
  98. package/dist/commands/web.js +165 -0
  99. package/dist/core/agents/canonical-emit.d.ts +27 -0
  100. package/dist/core/agents/canonical-emit.d.ts.map +1 -0
  101. package/dist/core/agents/canonical-emit.js +72 -0
  102. package/dist/core/agents/cli-emit.d.ts +27 -0
  103. package/dist/core/agents/cli-emit.d.ts.map +1 -0
  104. package/dist/core/agents/cli-emit.js +57 -0
  105. package/dist/core/agents/cli.d.ts +10 -0
  106. package/dist/core/agents/cli.d.ts.map +1 -0
  107. package/dist/core/agents/cli.js +757 -0
  108. package/dist/core/agents/codex-replay.d.ts +29 -0
  109. package/dist/core/agents/codex-replay.d.ts.map +1 -0
  110. package/dist/core/agents/codex-replay.js +138 -0
  111. package/dist/core/agents/coord-client.d.ts +98 -0
  112. package/dist/core/agents/coord-client.d.ts.map +1 -0
  113. package/dist/core/agents/coord-client.js +212 -0
  114. package/dist/core/agents/events/consume.d.ts +59 -0
  115. package/dist/core/agents/events/consume.d.ts.map +1 -0
  116. package/dist/core/agents/events/consume.js +147 -0
  117. package/dist/core/agents/events/emit.d.ts +42 -0
  118. package/dist/core/agents/events/emit.d.ts.map +1 -0
  119. package/dist/core/agents/events/emit.js +70 -0
  120. package/dist/core/agents/events/ulid.d.ts +11 -0
  121. package/dist/core/agents/events/ulid.d.ts.map +1 -0
  122. package/dist/core/agents/events/ulid.js +47 -0
  123. package/dist/core/agents/index.d.ts +14 -0
  124. package/dist/core/agents/index.d.ts.map +1 -0
  125. package/dist/core/agents/index.js +13 -0
  126. package/dist/core/agents/paths.d.ts +6 -0
  127. package/dist/core/agents/paths.d.ts.map +1 -0
  128. package/dist/core/agents/paths.js +17 -0
  129. package/dist/core/agents/render/prompt-context.d.ts +43 -0
  130. package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
  131. package/dist/core/agents/render/prompt-context.js +335 -0
  132. package/dist/core/agents/render/session-context.d.ts +39 -0
  133. package/dist/core/agents/render/session-context.d.ts.map +1 -0
  134. package/dist/core/agents/render/session-context.js +283 -0
  135. package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
  136. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
  137. package/dist/core/agents/rules/claim-conflict.js +244 -0
  138. package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
  139. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
  140. package/dist/core/agents/rules/commit-conflict.js +244 -0
  141. package/dist/core/agents/rules/stop-hook.d.ts +44 -0
  142. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
  143. package/dist/core/agents/rules/stop-hook.js +161 -0
  144. package/dist/core/agents/session-events.d.ts +41 -0
  145. package/dist/core/agents/session-events.d.ts.map +1 -0
  146. package/dist/core/agents/session-events.js +205 -0
  147. package/dist/core/agents/state/activity-log.d.ts +18 -0
  148. package/dist/core/agents/state/activity-log.d.ts.map +1 -0
  149. package/dist/core/agents/state/activity-log.js +34 -0
  150. package/dist/core/agents/state/council.d.ts +39 -0
  151. package/dist/core/agents/state/council.d.ts.map +1 -0
  152. package/dist/core/agents/state/council.js +216 -0
  153. package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
  154. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
  155. package/dist/core/agents/state/heartbeat-projector.js +436 -0
  156. package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
  157. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
  158. package/dist/core/agents/state/heartbeat-writer.js +271 -0
  159. package/dist/core/agents/state/names.d.ts +35 -0
  160. package/dist/core/agents/state/names.d.ts.map +1 -0
  161. package/dist/core/agents/state/names.js +376 -0
  162. package/dist/core/agents/state/pidmap.d.ts +11 -0
  163. package/dist/core/agents/state/pidmap.d.ts.map +1 -0
  164. package/dist/core/agents/state/pidmap.js +32 -0
  165. package/dist/core/agents/state/scratch.d.ts +27 -0
  166. package/dist/core/agents/state/scratch.d.ts.map +1 -0
  167. package/dist/core/agents/state/scratch.js +90 -0
  168. package/dist/core/agents/state/shell-mutation.d.ts +17 -0
  169. package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
  170. package/dist/core/agents/state/shell-mutation.js +41 -0
  171. package/dist/core/agents/state/stale-sweep.d.ts +16 -0
  172. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
  173. package/dist/core/agents/state/stale-sweep.js +166 -0
  174. package/dist/core/config.d.ts +29 -0
  175. package/dist/core/config.d.ts.map +1 -0
  176. package/dist/core/config.js +108 -0
  177. package/dist/core/hooks/cli.d.ts +21 -0
  178. package/dist/core/hooks/cli.d.ts.map +1 -0
  179. package/dist/core/hooks/cli.js +1123 -0
  180. package/dist/core/hooks/effects/image-capture.d.ts +43 -0
  181. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
  182. package/dist/core/hooks/effects/image-capture.js +288 -0
  183. package/dist/core/hooks/effects/index.d.ts +64 -0
  184. package/dist/core/hooks/effects/index.d.ts.map +1 -0
  185. package/dist/core/hooks/effects/index.js +197 -0
  186. package/dist/core/hooks/events/emit.d.ts +31 -0
  187. package/dist/core/hooks/events/emit.d.ts.map +1 -0
  188. package/dist/core/hooks/events/emit.js +89 -0
  189. package/dist/core/hooks/events/schema.d.ts +235 -0
  190. package/dist/core/hooks/events/schema.d.ts.map +1 -0
  191. package/dist/core/hooks/events/schema.js +12 -0
  192. package/dist/core/hooks/events/ulid.d.ts +10 -0
  193. package/dist/core/hooks/events/ulid.d.ts.map +1 -0
  194. package/dist/core/hooks/events/ulid.js +47 -0
  195. package/dist/core/hooks/harness/detect.d.ts +9 -0
  196. package/dist/core/hooks/harness/detect.d.ts.map +1 -0
  197. package/dist/core/hooks/harness/detect.js +29 -0
  198. package/dist/core/hooks/harness/events.d.ts +45 -0
  199. package/dist/core/hooks/harness/events.d.ts.map +1 -0
  200. package/dist/core/hooks/harness/events.js +71 -0
  201. package/dist/core/hooks/harness/output.d.ts +46 -0
  202. package/dist/core/hooks/harness/output.d.ts.map +1 -0
  203. package/dist/core/hooks/harness/output.js +87 -0
  204. package/dist/core/hooks/harness/parse.d.ts +67 -0
  205. package/dist/core/hooks/harness/parse.d.ts.map +1 -0
  206. package/dist/core/hooks/harness/parse.js +132 -0
  207. package/dist/core/hooks/index.d.ts +8 -0
  208. package/dist/core/hooks/index.d.ts.map +1 -0
  209. package/dist/core/hooks/index.js +7 -0
  210. package/dist/core/hooks/resolve/anchor.d.ts +37 -0
  211. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
  212. package/dist/core/hooks/resolve/anchor.js +48 -0
  213. package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
  214. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
  215. package/dist/core/hooks/resolve/coord-root.js +27 -0
  216. package/dist/core/hooks/resolve/intent.d.ts +33 -0
  217. package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
  218. package/dist/core/hooks/resolve/intent.js +79 -0
  219. package/dist/core/hooks/resolve/owner.d.ts +42 -0
  220. package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
  221. package/dist/core/hooks/resolve/owner.js +140 -0
  222. package/dist/core/hooks/resolve/transcript.d.ts +26 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
  224. package/dist/core/hooks/resolve/transcript.js +73 -0
  225. package/dist/index.d.ts +15 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +13 -0
  228. package/dist/lib/agent-browser/client.d.ts +99 -0
  229. package/dist/lib/agent-browser/client.d.ts.map +1 -0
  230. package/dist/lib/agent-browser/client.js +177 -0
  231. package/dist/lib/agent-browser/index.d.ts +2 -0
  232. package/dist/lib/agent-browser/index.d.ts.map +1 -0
  233. package/dist/lib/agent-browser/index.js +1 -0
  234. package/dist/lib/browser/client.d.ts +193 -0
  235. package/dist/lib/browser/client.d.ts.map +1 -0
  236. package/dist/lib/browser/client.js +325 -0
  237. package/dist/lib/browser/dev-overlay.d.ts +23 -0
  238. package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
  239. package/dist/lib/browser/dev-overlay.js +153 -0
  240. package/dist/lib/browser/index.d.ts +5 -0
  241. package/dist/lib/browser/index.d.ts.map +1 -0
  242. package/dist/lib/browser/index.js +2 -0
  243. package/dist/lib/browser/layout.d.ts +79 -0
  244. package/dist/lib/browser/layout.d.ts.map +1 -0
  245. package/dist/lib/browser/layout.js +220 -0
  246. package/dist/lib/browser/visibility.d.ts +86 -0
  247. package/dist/lib/browser/visibility.d.ts.map +1 -0
  248. package/dist/lib/browser/visibility.js +333 -0
  249. package/dist/lib/browser/visual-diff.d.ts +38 -0
  250. package/dist/lib/browser/visual-diff.d.ts.map +1 -0
  251. package/dist/lib/browser/visual-diff.js +107 -0
  252. package/dist/lib/completion/bash.d.ts +25 -0
  253. package/dist/lib/completion/bash.d.ts.map +1 -0
  254. package/dist/lib/completion/bash.js +284 -0
  255. package/dist/lib/completion/fish.d.ts +16 -0
  256. package/dist/lib/completion/fish.d.ts.map +1 -0
  257. package/dist/lib/completion/fish.js +118 -0
  258. package/dist/lib/completion/index.d.ts +5 -0
  259. package/dist/lib/completion/index.d.ts.map +1 -0
  260. package/dist/lib/completion/index.js +4 -0
  261. package/dist/lib/completion/walk.d.ts +68 -0
  262. package/dist/lib/completion/walk.d.ts.map +1 -0
  263. package/dist/lib/completion/walk.js +102 -0
  264. package/dist/lib/completion/zsh.d.ts +13 -0
  265. package/dist/lib/completion/zsh.d.ts.map +1 -0
  266. package/dist/lib/completion/zsh.js +249 -0
  267. package/dist/lib/context/index.d.ts +107 -0
  268. package/dist/lib/context/index.d.ts.map +1 -0
  269. package/dist/lib/context/index.js +275 -0
  270. package/dist/lib/cookies/client.d.ts +131 -0
  271. package/dist/lib/cookies/client.d.ts.map +1 -0
  272. package/dist/lib/cookies/client.js +239 -0
  273. package/dist/lib/cookies/index.d.ts +2 -0
  274. package/dist/lib/cookies/index.d.ts.map +1 -0
  275. package/dist/lib/cookies/index.js +1 -0
  276. package/dist/lib/council/index.d.ts +266 -0
  277. package/dist/lib/council/index.d.ts.map +1 -0
  278. package/dist/lib/council/index.js +674 -0
  279. package/dist/lib/docs-index.d.ts +28 -0
  280. package/dist/lib/docs-index.d.ts.map +1 -0
  281. package/dist/lib/docs-index.js +169 -0
  282. package/dist/lib/docs-lint.d.ts +26 -0
  283. package/dist/lib/docs-lint.d.ts.map +1 -0
  284. package/dist/lib/docs-lint.js +378 -0
  285. package/dist/lib/docs-sweep.d.ts +34 -0
  286. package/dist/lib/docs-sweep.d.ts.map +1 -0
  287. package/dist/lib/docs-sweep.js +304 -0
  288. package/dist/lib/docs.d.ts +27 -0
  289. package/dist/lib/docs.d.ts.map +1 -0
  290. package/dist/lib/docs.js +142 -0
  291. package/dist/lib/env.d.ts +11 -0
  292. package/dist/lib/env.d.ts.map +1 -0
  293. package/dist/lib/env.js +12 -0
  294. package/dist/lib/exec.d.ts +32 -0
  295. package/dist/lib/exec.d.ts.map +1 -0
  296. package/dist/lib/exec.js +54 -0
  297. package/dist/lib/format.d.ts +29 -0
  298. package/dist/lib/format.d.ts.map +1 -0
  299. package/dist/lib/format.js +139 -0
  300. package/dist/lib/http/client.d.ts +56 -0
  301. package/dist/lib/http/client.d.ts.map +1 -0
  302. package/dist/lib/http/client.js +160 -0
  303. package/dist/lib/http/index.d.ts +2 -0
  304. package/dist/lib/http/index.d.ts.map +1 -0
  305. package/dist/lib/http/index.js +1 -0
  306. package/dist/lib/identities/index.d.ts +77 -0
  307. package/dist/lib/identities/index.d.ts.map +1 -0
  308. package/dist/lib/identities/index.js +190 -0
  309. package/dist/lib/machine.d.ts +19 -0
  310. package/dist/lib/machine.d.ts.map +1 -0
  311. package/dist/lib/machine.js +61 -0
  312. package/dist/lib/presence.d.ts +48 -0
  313. package/dist/lib/presence.d.ts.map +1 -0
  314. package/dist/lib/presence.js +123 -0
  315. package/dist/lib/readability/client.d.ts +39 -0
  316. package/dist/lib/readability/client.d.ts.map +1 -0
  317. package/dist/lib/readability/client.js +121 -0
  318. package/dist/lib/readability/index.d.ts +2 -0
  319. package/dist/lib/readability/index.d.ts.map +1 -0
  320. package/dist/lib/readability/index.js +1 -0
  321. package/dist/lib/scratch/index.d.ts +74 -0
  322. package/dist/lib/scratch/index.d.ts.map +1 -0
  323. package/dist/lib/scratch/index.js +393 -0
  324. package/dist/lib/tunnel/gate.d.ts +12 -0
  325. package/dist/lib/tunnel/gate.d.ts.map +1 -0
  326. package/dist/lib/tunnel/gate.js +101 -0
  327. package/dist/lib/tunnel/state.d.ts +34 -0
  328. package/dist/lib/tunnel/state.d.ts.map +1 -0
  329. package/dist/lib/tunnel/state.js +132 -0
  330. package/package.json +160 -8
  331. package/schemas/.gitkeep +0 -0
  332. package/schemas/config.schema.json +109 -0
  333. package/src/cli.ts +22 -0
  334. package/src/commander.ts +242 -0
  335. package/src/commands/.gitkeep +0 -0
  336. package/src/commands/agents.ts +4567 -0
  337. package/src/commands/backup.ts +305 -0
  338. package/src/commands/browse-ai.ts +198 -0
  339. package/src/commands/browse.ts +849 -0
  340. package/src/commands/callers.ts +363 -0
  341. package/src/commands/completion.ts +193 -0
  342. package/src/commands/config-get.ts +161 -0
  343. package/src/commands/context.ts +209 -0
  344. package/src/commands/cookies.ts +198 -0
  345. package/src/commands/docs.ts +174 -0
  346. package/src/commands/doctor.ts +231 -0
  347. package/src/commands/edit-batch.ts +233 -0
  348. package/src/commands/eml.ts +519 -0
  349. package/src/commands/env.ts +254 -0
  350. package/src/commands/fetch.ts +136 -0
  351. package/src/commands/file-history.ts +202 -0
  352. package/src/commands/grep.ts +371 -0
  353. package/src/commands/init.ts +335 -0
  354. package/src/commands/outline.ts +583 -0
  355. package/src/commands/presence.ts +152 -0
  356. package/src/commands/read.ts +64 -0
  357. package/src/commands/scratch.ts +445 -0
  358. package/src/commands/session.ts +187 -0
  359. package/src/commands/sync.ts +306 -0
  360. package/src/commands/toc.ts +218 -0
  361. package/src/commands/tokens.ts +79 -0
  362. package/src/commands/tunnel.ts +633 -0
  363. package/src/commands/uninstall.ts +144 -0
  364. package/src/commands/web.ts +193 -0
  365. package/src/core/agents/canonical-emit.ts +77 -0
  366. package/src/core/agents/cli-emit.ts +64 -0
  367. package/src/core/agents/cli.ts +838 -0
  368. package/src/core/agents/codex-replay.ts +163 -0
  369. package/src/core/agents/coord-client.ts +249 -0
  370. package/src/core/agents/events/consume.ts +196 -0
  371. package/src/core/agents/events/emit.ts +108 -0
  372. package/src/core/agents/events/ulid.ts +51 -0
  373. package/src/core/agents/index.ts +14 -0
  374. package/src/core/agents/paths.ts +16 -0
  375. package/src/core/agents/render/prompt-context.ts +401 -0
  376. package/src/core/agents/render/session-context.ts +341 -0
  377. package/src/core/agents/rules/claim-conflict.ts +282 -0
  378. package/src/core/agents/rules/commit-conflict.ts +303 -0
  379. package/src/core/agents/rules/stop-hook.ts +229 -0
  380. package/src/core/agents/session-events.ts +228 -0
  381. package/src/core/agents/state/activity-log.ts +33 -0
  382. package/src/core/agents/state/council.ts +265 -0
  383. package/src/core/agents/state/heartbeat-projector.ts +488 -0
  384. package/src/core/agents/state/heartbeat-writer.ts +333 -0
  385. package/src/core/agents/state/names.ts +399 -0
  386. package/src/core/agents/state/pidmap.ts +38 -0
  387. package/src/core/agents/state/scratch.ts +121 -0
  388. package/src/core/agents/state/shell-mutation.ts +44 -0
  389. package/src/core/agents/state/stale-sweep.ts +190 -0
  390. package/src/core/config.ts +111 -0
  391. package/src/core/hooks/cli.ts +1247 -0
  392. package/src/core/hooks/effects/image-capture.ts +330 -0
  393. package/src/core/hooks/effects/index.ts +210 -0
  394. package/src/core/hooks/events/emit.ts +120 -0
  395. package/src/core/hooks/events/schema.ts +430 -0
  396. package/src/core/hooks/events/ulid.ts +51 -0
  397. package/src/core/hooks/harness/detect.ts +30 -0
  398. package/src/core/hooks/harness/events.ts +102 -0
  399. package/src/core/hooks/harness/output.ts +100 -0
  400. package/src/core/hooks/harness/parse.ts +180 -0
  401. package/src/core/hooks/index.ts +16 -0
  402. package/src/core/hooks/resolve/anchor.ts +51 -0
  403. package/src/core/hooks/resolve/coord-root.ts +25 -0
  404. package/src/core/hooks/resolve/intent.ts +89 -0
  405. package/src/core/hooks/resolve/owner.ts +140 -0
  406. package/src/core/hooks/resolve/transcript.ts +72 -0
  407. package/src/hooks/.gitkeep +0 -0
  408. package/src/index.ts +15 -0
  409. package/src/lib/agent-browser/client.ts +239 -0
  410. package/src/lib/agent-browser/index.ts +1 -0
  411. package/src/lib/browser/client.ts +449 -0
  412. package/src/lib/browser/dev-overlay.ts +207 -0
  413. package/src/lib/browser/index.ts +24 -0
  414. package/src/lib/browser/layout.ts +288 -0
  415. package/src/lib/browser/visibility.ts +419 -0
  416. package/src/lib/browser/visual-diff.ts +150 -0
  417. package/src/lib/completion/bash.ts +291 -0
  418. package/src/lib/completion/fish.ts +134 -0
  419. package/src/lib/completion/index.ts +10 -0
  420. package/src/lib/completion/walk.ts +184 -0
  421. package/src/lib/completion/zsh.ts +262 -0
  422. package/src/lib/context/index.ts +386 -0
  423. package/src/lib/cookies/client.ts +301 -0
  424. package/src/lib/cookies/index.ts +13 -0
  425. package/src/lib/council/index.ts +803 -0
  426. package/src/lib/docs-index.ts +216 -0
  427. package/src/lib/docs-lint.ts +413 -0
  428. package/src/lib/docs-sweep.ts +348 -0
  429. package/src/lib/docs.ts +199 -0
  430. package/src/lib/env.ts +12 -0
  431. package/src/lib/exec.ts +74 -0
  432. package/src/lib/format.ts +147 -0
  433. package/src/lib/http/client.ts +211 -0
  434. package/src/lib/http/index.ts +1 -0
  435. package/src/lib/identities/index.ts +210 -0
  436. package/src/lib/machine.ts +61 -0
  437. package/src/lib/presence.ts +154 -0
  438. package/src/lib/readability/client.ts +169 -0
  439. package/src/lib/readability/index.ts +5 -0
  440. package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
  441. package/src/lib/scratch/index.ts +470 -0
  442. package/src/lib/tunnel/gate.ts +113 -0
  443. package/src/lib/tunnel/state.ts +167 -0
  444. package/src/web/.gitkeep +0 -0
  445. package/index.js +0 -1
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Image-feed capture: a non-coordination side effect fired from the normalized
3
+ * `agent-hook` tool handlers (see effects/index.ts for the family rationale).
4
+ *
5
+ * When an agent VIEWS an image (Read tool on a `.png`/`.jpg`/…) or PRODUCES one
6
+ * (a Bash command writes an image file via `harn browse`, `harn image`, `--diff`, …),
7
+ * we content-address the bytes into `.harnery/images/<sha256>.<ext>` (dedup:
8
+ * identical bytes collapse to one blob) and emit an `image.captured` event into
9
+ * the canonical stream. The web image feed (`/images`) groups those events by
10
+ * hash and streams them live over the existing SSE infra.
11
+ *
12
+ * Why this lives at the hook layer: it's the single harness-agnostic chokepoint
13
+ * that sees every tool call across Claude Code / Cursor / Codex with the full
14
+ * `tool_input` (file paths) and `tool_response`. No per-command code needed.
15
+ *
16
+ * Everything here is best-effort and MUST NOT throw; callers wrap it in
17
+ * try/catch + logError, matching every other effect.
18
+ */
19
+ import type { Harness } from "../events/schema.js";
20
+ import type { ParsedPayload } from "../harness/parse.js";
21
+ export interface CaptureContext {
22
+ eventType: "tool.pre_use" | "tool.post_use";
23
+ /** The built event data (tool_name, tool_input string, intent, tool_use_id). */
24
+ data: Record<string, unknown>;
25
+ /** The full parsed payload, used for the un-clamped command + tool_response. */
26
+ payload: ParsedPayload | null;
27
+ instanceId: string;
28
+ sessionId: string;
29
+ harness: Harness;
30
+ }
31
+ /**
32
+ * Inspect one tool event for image references and capture any that resolve to
33
+ * a real image on disk. Emits zero or more `image.captured` events.
34
+ */
35
+ export declare function captureImages(coordRoot: string, ctx: CaptureContext): void;
36
+ /**
37
+ * Prune `.harnery/images/` past a size cap (default 2 GB) and an age cap
38
+ * (default 30 days), oldest-mtime-first. Fired on session.start next to
39
+ * scratchJanitor. Pure-fs, fail-soft. Orphaned `image.captured` events whose
40
+ * blob was pruned render as an "expired" placeholder in the gallery.
41
+ */
42
+ export declare function imageJanitor(coordRoot: string): void;
43
+ //# sourceMappingURL=image-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-capture.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/effects/image-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAoBzD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,cAAc,GAAG,eAAe,CAAC;IAC5C,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,gFAAgF;IAChF,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAoC1E;AA2FD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAmDpD"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Image-feed capture: a non-coordination side effect fired from the normalized
3
+ * `agent-hook` tool handlers (see effects/index.ts for the family rationale).
4
+ *
5
+ * When an agent VIEWS an image (Read tool on a `.png`/`.jpg`/…) or PRODUCES one
6
+ * (a Bash command writes an image file via `harn browse`, `harn image`, `--diff`, …),
7
+ * we content-address the bytes into `.harnery/images/<sha256>.<ext>` (dedup:
8
+ * identical bytes collapse to one blob) and emit an `image.captured` event into
9
+ * the canonical stream. The web image feed (`/images`) groups those events by
10
+ * hash and streams them live over the existing SSE infra.
11
+ *
12
+ * Why this lives at the hook layer: it's the single harness-agnostic chokepoint
13
+ * that sees every tool call across Claude Code / Cursor / Codex with the full
14
+ * `tool_input` (file paths) and `tool_response`. No per-command code needed.
15
+ *
16
+ * Everything here is best-effort and MUST NOT throw; callers wrap it in
17
+ * try/catch + logError, matching every other effect.
18
+ */
19
+ import { createHash } from "node:crypto";
20
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
21
+ import { isAbsolute, join, resolve } from "node:path";
22
+ import { emit } from "../events/emit.js";
23
+ /** Raster + vector image extensions the feed accepts. PDF is intentionally
24
+ * excluded: book renders aren't an "image feed" and don't thumbnail inline. */
25
+ const IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp", "svg"]);
26
+ /** Skip hashing files larger than this; screenshots are tens of KB; a huge
27
+ * file mentioned in passing isn't worth the read. */
28
+ const MAX_CAPTURE_BYTES = 25 * 1024 * 1024;
29
+ /** A produced image must have been written within this window of the command
30
+ * finishing, otherwise a path merely *mentioned* in output (an old baseline,
31
+ * a doc reference) would be captured. Viewed images skip this gate. */
32
+ const PRODUCED_MTIME_WINDOW_MS = 120_000;
33
+ /** Matches image-ish paths in a command string or its output. Deliberately
34
+ * permissive on the path body (`~`, `@`, `.`, `-`, `/`); existence + ext + the
35
+ * mtime gate do the real filtering. */
36
+ const IMAGE_PATH_RE = /[\w./~@+-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi;
37
+ /**
38
+ * Inspect one tool event for image references and capture any that resolve to
39
+ * a real image on disk. Emits zero or more `image.captured` events.
40
+ */
41
+ export function captureImages(coordRoot, ctx) {
42
+ const imagesDir = join(coordRoot, ".harnery", "images");
43
+ const cwd = resolveCwd(ctx.payload);
44
+ const toolName = String(ctx.data.tool_name ?? "");
45
+ const candidates = ctx.eventType === "tool.pre_use"
46
+ ? collectViewed(toolName, ctx.data, cwd)
47
+ : collectProduced(toolName, ctx.payload, cwd);
48
+ if (candidates.length === 0)
49
+ return;
50
+ for (const cand of candidates) {
51
+ // Never re-capture our own blob store (would loop on Reads of the gallery).
52
+ if (cand.path.startsWith(`${imagesDir}/`))
53
+ continue;
54
+ const captured = captureOne(imagesDir, cand);
55
+ if (!captured)
56
+ continue;
57
+ emit(coordRoot, {
58
+ event_type: "image.captured",
59
+ instance_id: ctx.instanceId,
60
+ session_id: ctx.sessionId,
61
+ harness: ctx.harness,
62
+ data: {
63
+ hash: captured.hash,
64
+ ext: captured.ext,
65
+ bytes: captured.bytes,
66
+ role: cand.role,
67
+ source_path: canonicalize(coordRoot, cand.path),
68
+ tool_name: toolName,
69
+ tool_use_id: ctx.data.tool_use_id,
70
+ ...(cand.intent ? { intent: cand.intent } : {}),
71
+ ...(cand.commandHead ? { command_head: cand.commandHead } : {}),
72
+ },
73
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
74
+ });
75
+ }
76
+ }
77
+ /** Read tool → the file_path it's about to show (the "agent saw this" signal). */
78
+ function collectViewed(toolName, data, cwd) {
79
+ if (toolName !== "Read")
80
+ return [];
81
+ const input = parseToolInput(data.tool_input);
82
+ const filePath = input?.file_path;
83
+ if (typeof filePath !== "string" || !filePath)
84
+ return [];
85
+ if (!hasImageExt(filePath))
86
+ return [];
87
+ return [
88
+ {
89
+ path: toAbsolute(filePath, cwd),
90
+ role: "viewed",
91
+ intent: typeof data.intent === "string" ? data.intent : undefined,
92
+ requireRecentMtime: false,
93
+ },
94
+ ];
95
+ }
96
+ /** Bash tool → scan the command + its output for freshly-written image files. */
97
+ function collectProduced(toolName, payload, cwd) {
98
+ if (toolName !== "Bash")
99
+ return [];
100
+ const command = bashCommand(payload);
101
+ const responseText = stringifyResponse(payload?.tool_response);
102
+ const commandHead = command ? command.slice(0, 120) : undefined;
103
+ const seen = new Set();
104
+ const out = [];
105
+ for (const text of [command, responseText]) {
106
+ if (!text)
107
+ continue;
108
+ for (const m of text.matchAll(IMAGE_PATH_RE)) {
109
+ const raw = m[0];
110
+ const abs = toAbsolute(raw, cwd);
111
+ if (seen.has(abs))
112
+ continue;
113
+ seen.add(abs);
114
+ out.push({ path: abs, role: "produced", commandHead, requireRecentMtime: true });
115
+ }
116
+ }
117
+ return out;
118
+ }
119
+ /**
120
+ * Validate the candidate on disk, hash it, and copy into the content-addressed
121
+ * store (skipping the copy when the blob already exists). Returns null when the
122
+ * candidate doesn't qualify.
123
+ */
124
+ function captureOne(imagesDir, cand) {
125
+ let st;
126
+ try {
127
+ st = statSync(cand.path);
128
+ }
129
+ catch {
130
+ return null; // path doesn't exist (common for "produced" false-positives)
131
+ }
132
+ if (!st.isFile() || st.size === 0 || st.size > MAX_CAPTURE_BYTES)
133
+ return null;
134
+ if (cand.requireRecentMtime && Date.now() - st.mtimeMs > PRODUCED_MTIME_WINDOW_MS) {
135
+ return null; // mentioned but not freshly produced by this command
136
+ }
137
+ const ext = extOf(cand.path);
138
+ if (!ext || !IMAGE_EXTS.has(ext))
139
+ return null;
140
+ let bytes;
141
+ try {
142
+ bytes = readFileSync(cand.path);
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ const hash = createHash("sha256").update(bytes).digest("hex");
148
+ const dest = join(imagesDir, `${hash}.${ext}`);
149
+ if (!existsSync(dest)) {
150
+ try {
151
+ mkdirSync(imagesDir, { recursive: true });
152
+ const tmp = `${dest}.tmp.${process.pid}`;
153
+ writeFileSync(tmp, bytes);
154
+ renameSync(tmp, dest);
155
+ }
156
+ catch {
157
+ return null; // couldn't store, don't emit a dangling event
158
+ }
159
+ }
160
+ return { hash, ext, bytes: st.size };
161
+ }
162
+ /**
163
+ * Prune `.harnery/images/` past a size cap (default 2 GB) and an age cap
164
+ * (default 30 days), oldest-mtime-first. Fired on session.start next to
165
+ * scratchJanitor. Pure-fs, fail-soft. Orphaned `image.captured` events whose
166
+ * blob was pruned render as an "expired" placeholder in the gallery.
167
+ */
168
+ export function imageJanitor(coordRoot) {
169
+ try {
170
+ const dir = join(coordRoot, ".harnery", "images");
171
+ if (!existsSync(dir))
172
+ return;
173
+ const maxBytes = envInt("HARNERY_IMAGES_MAX_BYTES", 2 * 1024 * 1024 * 1024);
174
+ const maxAgeMs = envInt("HARNERY_IMAGES_MAX_AGE_DAYS", 30) * 24 * 60 * 60 * 1000;
175
+ const now = Date.now();
176
+ const entries = [];
177
+ for (const name of readdirSync(dir)) {
178
+ if (name.endsWith(".tmp") || name.includes(".tmp.")) {
179
+ // Orphaned temp from a crashed copy; sweep it.
180
+ rmSync(join(dir, name), { force: true });
181
+ continue;
182
+ }
183
+ const full = join(dir, name);
184
+ let st;
185
+ try {
186
+ st = statSync(full);
187
+ }
188
+ catch {
189
+ continue;
190
+ }
191
+ if (!st.isFile())
192
+ continue;
193
+ entries.push({ path: full, size: st.size, mtimeMs: st.mtimeMs });
194
+ }
195
+ // Age cap.
196
+ let total = 0;
197
+ const survivors = [];
198
+ for (const e of entries) {
199
+ if (now - e.mtimeMs > maxAgeMs) {
200
+ rmSync(e.path, { force: true });
201
+ continue;
202
+ }
203
+ total += e.size;
204
+ survivors.push(e);
205
+ }
206
+ // Size cap: drop oldest first until under.
207
+ if (total > maxBytes) {
208
+ survivors.sort((a, b) => a.mtimeMs - b.mtimeMs);
209
+ for (const e of survivors) {
210
+ if (total <= maxBytes)
211
+ break;
212
+ rmSync(e.path, { force: true });
213
+ total -= e.size;
214
+ }
215
+ }
216
+ }
217
+ catch {
218
+ // best-effort
219
+ }
220
+ }
221
+ /* ── helpers ─────────────────────────────────────────────────────────────── */
222
+ function envInt(name, fallback) {
223
+ const v = process.env[name];
224
+ if (!v)
225
+ return fallback;
226
+ const n = Number(v);
227
+ return Number.isFinite(n) && n > 0 ? n : fallback;
228
+ }
229
+ function parseToolInput(raw) {
230
+ if (typeof raw !== "string")
231
+ return null;
232
+ try {
233
+ const parsed = JSON.parse(raw);
234
+ return parsed && typeof parsed === "object" ? parsed : null;
235
+ }
236
+ catch {
237
+ return null;
238
+ }
239
+ }
240
+ /** The un-clamped Bash command from the raw payload (falls back to clamped). */
241
+ function bashCommand(payload) {
242
+ const ti = payload?.raw?.tool_input;
243
+ const cmd = ti?.command;
244
+ return typeof cmd === "string" ? cmd : "";
245
+ }
246
+ function stringifyResponse(value) {
247
+ if (value == null)
248
+ return "";
249
+ if (typeof value === "string")
250
+ return value;
251
+ try {
252
+ return JSON.stringify(value);
253
+ }
254
+ catch {
255
+ return "";
256
+ }
257
+ }
258
+ function resolveCwd(payload) {
259
+ const raw = payload?.raw;
260
+ const cwd = raw?.cwd;
261
+ return typeof cwd === "string" && cwd ? cwd : process.cwd();
262
+ }
263
+ function hasImageExt(p) {
264
+ const ext = extOf(p);
265
+ return !!ext && IMAGE_EXTS.has(ext);
266
+ }
267
+ function extOf(p) {
268
+ const clean = p.split(/[?#]/)[0] ?? p; // drop any query/fragment
269
+ const dot = clean.lastIndexOf(".");
270
+ if (dot < 0)
271
+ return "";
272
+ return clean.slice(dot + 1).toLowerCase();
273
+ }
274
+ function toAbsolute(p, cwd) {
275
+ let path = p;
276
+ if (path.startsWith("~/")) {
277
+ const home = process.env.HOME;
278
+ if (home)
279
+ path = join(home, path.slice(2));
280
+ }
281
+ return isAbsolute(path) ? path : resolve(cwd, path);
282
+ }
283
+ /** Strip the coordRoot prefix so the feed shows repo-relative paths. */
284
+ function canonicalize(coordRoot, p) {
285
+ if (p.startsWith(`${coordRoot}/`))
286
+ return p.slice(coordRoot.length + 1);
287
+ return p;
288
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Non-coordination side effects fired from the normalized `agent-hook` handlers.
3
+ *
4
+ * These are DELIBERATELY outside the agent-coord path: sounds, scratch
5
+ * lifecycle, session telemetry, presence detection. They used to live in
6
+ * per-harness bash adapters. Per the directive ("use the normalized hooks; if
7
+ * they aren't coordination, implement them outside of coordination") they move
8
+ * here so the harness configs reference only `agent-hook`, while staying a
9
+ * distinct concern from the coordination logic in cli.ts / agent-coord.
10
+ *
11
+ * Everything here is best-effort: it never throws and never blocks the hook on a
12
+ * slow dependency (telemetry runs detached).
13
+ */
14
+ export type { CaptureContext } from "./image-capture.js";
15
+ export { captureImages, imageJanitor } from "./image-capture.js";
16
+ /**
17
+ * Play a notification sound via the cross-platform utility
18
+ * (afplay on macOS, powershell.exe on WSL). The
19
+ * utility backgrounds the actual player, so this returns fast. Rate-limiting
20
+ * lives in the utility, keyed on CLAUDE_SOUND_SESSION_ID + the per-event count.
21
+ * Claude-Code-only today (Cursor has no sounds; Codex's never worked).
22
+ */
23
+ export declare function playSound(repoRoot: string, soundEvent: string, sessionId: string, maxPlays?: number): void;
24
+ /** Map an agent-hook CLI event name → sound, or null if the event has no sound. */
25
+ export declare function soundForEvent(eventName: string): {
26
+ sound: string;
27
+ maxPlays: number;
28
+ } | null;
29
+ /** Prune stale scratch archives + sweep orphans (global, fast). Fire-and-forget. */
30
+ export declare function scratchJanitor(repoRoot: string): void;
31
+ /**
32
+ * Return the one-line scratch recovery cue for SessionStart, or "" if none.
33
+ * The caller merges it into the session-start additionalContext (it used to be
34
+ * a standalone additionalContext emission from the previous scratch-on-start adapter).
35
+ */
36
+ export declare function scratchRecoveryCue(repoRoot: string): string;
37
+ /** Archive the ending agent's scratchpad. Fire-and-forget. */
38
+ export declare function scratchArchive(repoRoot: string, owner: string): void;
39
+ /**
40
+ * Sync Claude Code session JSONL → BigQuery (`harn claude-sessions sync`). Lives
41
+ * in the host CLI (parses Claude Code's own ~/.claude/projects/**\/*.jsonl), so harnery
42
+ * shells out to the harn binary rather than importing it. Detached + unref'd so a
43
+ * slow BigQuery round-trip never blocks the hook. Stop-path syncs are rate-
44
+ * limited inside `harn claude-sessions sync`; SessionEnd forces via env. Caller
45
+ * gates to the claude-code harness.
46
+ */
47
+ export declare function syncClaudeSessions(repoRoot: string, force: boolean): void;
48
+ /**
49
+ * Fire the turn-summary extension (Haiku auto-summary of the turn → heartbeat
50
+ * `turn_summary`). Detached + unref'd; it makes an Anthropic API call and must
51
+ * never block the Stop hook. Claude-Code-only. The
52
+ * script self-guards on ANTHROPIC_API_KEY / curl / jq / matching session.
53
+ */
54
+ export declare function runTurnSummary(repoRoot: string, owner: string, sessionId: string, transcriptPath: string | undefined): void;
55
+ /** Reset per-turn sound rate-limit counters at the start of a new turn. */
56
+ export declare function resetSoundCounters(sessionId: string): void;
57
+ /**
58
+ * Update the mobile-vs-office presence file from the user's prompt shape, using
59
+ * harnery's own presence library in-process: no external script, no host-path
60
+ * dependency. Claude-Code-only (the heuristic is tuned to CC's user-prompt
61
+ * payload; Cursor/Codex don't surface comparable prompt text). Fire-and-forget.
62
+ */
63
+ export declare function detectPresence(prompt: string): void;
64
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/effects/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,SAAI,GACX,IAAI,CAYN;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW3F;AAOD,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYrD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa3D;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAYpE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAkBzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,IAAI,CAqBN;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAa1D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOnD"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Non-coordination side effects fired from the normalized `agent-hook` handlers.
3
+ *
4
+ * These are DELIBERATELY outside the agent-coord path: sounds, scratch
5
+ * lifecycle, session telemetry, presence detection. They used to live in
6
+ * per-harness bash adapters. Per the directive ("use the normalized hooks; if
7
+ * they aren't coordination, implement them outside of coordination") they move
8
+ * here so the harness configs reference only `agent-hook`, while staying a
9
+ * distinct concern from the coordination logic in cli.ts / agent-coord.
10
+ *
11
+ * Everything here is best-effort: it never throws and never blocks the hook on a
12
+ * slow dependency (telemetry runs detached).
13
+ */
14
+ import { spawn, spawnSync } from "node:child_process";
15
+ import { existsSync, readdirSync, rmSync } from "node:fs";
16
+ import os from "node:os";
17
+ import { join } from "node:path";
18
+ import { applyDetection } from "../../../lib/presence.js";
19
+ export { captureImages, imageJanitor } from "./image-capture.js";
20
+ /**
21
+ * Play a notification sound via the cross-platform utility
22
+ * (afplay on macOS, powershell.exe on WSL). The
23
+ * utility backgrounds the actual player, so this returns fast. Rate-limiting
24
+ * lives in the utility, keyed on CLAUDE_SOUND_SESSION_ID + the per-event count.
25
+ * Claude-Code-only today (Cursor has no sounds; Codex's never worked).
26
+ */
27
+ export function playSound(repoRoot, soundEvent, sessionId, maxPlays = 0) {
28
+ try {
29
+ const player = join(repoRoot, "scripts", "hooks", "play-sound.sh");
30
+ if (!existsSync(player))
31
+ return;
32
+ spawnSync("bash", [player, soundEvent, String(maxPlays)], {
33
+ env: { ...process.env, CLAUDE_SOUND_SESSION_ID: sessionId },
34
+ timeout: 4000,
35
+ stdio: "ignore",
36
+ });
37
+ }
38
+ catch {
39
+ // best-effort
40
+ }
41
+ }
42
+ /** Map an agent-hook CLI event name → sound, or null if the event has no sound. */
43
+ export function soundForEvent(eventName) {
44
+ switch (eventName) {
45
+ case "stop":
46
+ return { sound: "stop", maxPlays: 0 };
47
+ case "stop-failure":
48
+ return { sound: "error", maxPlays: 0 };
49
+ case "sub-agent-start":
50
+ return { sound: "subagent-start", maxPlays: 3 };
51
+ default:
52
+ return null;
53
+ }
54
+ }
55
+ function harnBin(repoRoot) {
56
+ const bin = join(repoRoot, "harnery", "bin", "harn");
57
+ return existsSync(bin) ? bin : null;
58
+ }
59
+ /** Prune stale scratch archives + sweep orphans (global, fast). Fire-and-forget. */
60
+ export function scratchJanitor(repoRoot) {
61
+ try {
62
+ const bin = harnBin(repoRoot);
63
+ if (!bin)
64
+ return;
65
+ spawnSync("bash", [bin, "scratch", "janitor", "--quiet"], {
66
+ env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
67
+ timeout: 5000,
68
+ stdio: "ignore",
69
+ });
70
+ }
71
+ catch {
72
+ // best-effort
73
+ }
74
+ }
75
+ /**
76
+ * Return the one-line scratch recovery cue for SessionStart, or "" if none.
77
+ * The caller merges it into the session-start additionalContext (it used to be
78
+ * a standalone additionalContext emission from the previous scratch-on-start adapter).
79
+ */
80
+ export function scratchRecoveryCue(repoRoot) {
81
+ try {
82
+ const bin = harnBin(repoRoot);
83
+ if (!bin)
84
+ return "";
85
+ const r = spawnSync("bash", [bin, "scratch", "recovery-cue"], {
86
+ env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
87
+ timeout: 5000,
88
+ encoding: "utf8",
89
+ });
90
+ return (r.stdout ?? "").trim();
91
+ }
92
+ catch {
93
+ return "";
94
+ }
95
+ }
96
+ /** Archive the ending agent's scratchpad. Fire-and-forget. */
97
+ export function scratchArchive(repoRoot, owner) {
98
+ try {
99
+ const bin = harnBin(repoRoot);
100
+ if (!bin || !owner)
101
+ return;
102
+ spawnSync("bash", [bin, "scratch", "archive", "--owner", owner], {
103
+ env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
104
+ timeout: 5000,
105
+ stdio: "ignore",
106
+ });
107
+ }
108
+ catch {
109
+ // best-effort
110
+ }
111
+ }
112
+ /**
113
+ * Sync Claude Code session JSONL → BigQuery (`harn claude-sessions sync`). Lives
114
+ * in the host CLI (parses Claude Code's own ~/.claude/projects/**\/*.jsonl), so harnery
115
+ * shells out to the harn binary rather than importing it. Detached + unref'd so a
116
+ * slow BigQuery round-trip never blocks the hook. Stop-path syncs are rate-
117
+ * limited inside `harn claude-sessions sync`; SessionEnd forces via env. Caller
118
+ * gates to the claude-code harness.
119
+ */
120
+ export function syncClaudeSessions(repoRoot, force) {
121
+ try {
122
+ const bin = join(repoRoot, "bin", "bp");
123
+ if (!existsSync(bin))
124
+ return;
125
+ const env = {
126
+ ...process.env,
127
+ HARNERY_OUTPUT_SESSION_TEE: "0",
128
+ };
129
+ if (force)
130
+ env.HARNERY_CLAUDE_SESSIONS_FORCE = "1";
131
+ const child = spawn("bash", [bin, "claude-sessions", "sync", "--quiet"], {
132
+ env,
133
+ detached: true,
134
+ stdio: "ignore",
135
+ });
136
+ child.unref();
137
+ }
138
+ catch {
139
+ // best-effort
140
+ }
141
+ }
142
+ /**
143
+ * Fire the turn-summary extension (Haiku auto-summary of the turn → heartbeat
144
+ * `turn_summary`). Detached + unref'd; it makes an Anthropic API call and must
145
+ * never block the Stop hook. Claude-Code-only. The
146
+ * script self-guards on ANTHROPIC_API_KEY / curl / jq / matching session.
147
+ */
148
+ export function runTurnSummary(repoRoot, owner, sessionId, transcriptPath) {
149
+ try {
150
+ if (!transcriptPath || !existsSync(transcriptPath))
151
+ return;
152
+ const script = join(repoRoot, "scripts", "hooks", "harness", "claude_code", "extensions", "turn-summary.sh");
153
+ if (!existsSync(script))
154
+ return;
155
+ const child = spawn("bash", [script, owner, sessionId, transcriptPath], {
156
+ detached: true,
157
+ stdio: "ignore",
158
+ });
159
+ child.unref();
160
+ }
161
+ catch {
162
+ // best-effort
163
+ }
164
+ }
165
+ /** Reset per-turn sound rate-limit counters at the start of a new turn. */
166
+ export function resetSoundCounters(sessionId) {
167
+ try {
168
+ if (!sessionId)
169
+ return;
170
+ const dir = os.tmpdir();
171
+ const prefix = `claude-sounds-${sessionId}-`;
172
+ for (const f of readdirSync(dir)) {
173
+ if (f.startsWith(prefix) && f.endsWith(".count")) {
174
+ rmSync(join(dir, f), { force: true });
175
+ }
176
+ }
177
+ }
178
+ catch {
179
+ // best-effort
180
+ }
181
+ }
182
+ /**
183
+ * Update the mobile-vs-office presence file from the user's prompt shape, using
184
+ * harnery's own presence library in-process: no external script, no host-path
185
+ * dependency. Claude-Code-only (the heuristic is tuned to CC's user-prompt
186
+ * payload; Cursor/Codex don't surface comparable prompt text). Fire-and-forget.
187
+ */
188
+ export function detectPresence(prompt) {
189
+ try {
190
+ if (!prompt)
191
+ return;
192
+ applyDetection(prompt);
193
+ }
194
+ catch {
195
+ // best-effort
196
+ }
197
+ }
@@ -0,0 +1,31 @@
1
+ import { type EventEnvelope, type EventType, type Harness, type Source } from "./schema.js";
2
+ export interface EmitInput<TType extends EventType, TData> {
3
+ event_type: TType;
4
+ instance_id: string;
5
+ session_id: string;
6
+ harness: Harness;
7
+ source?: Source;
8
+ parent_session_id?: string;
9
+ turn_id?: string;
10
+ parent_turn_id?: string;
11
+ ts?: string;
12
+ data: TData;
13
+ }
14
+ /**
15
+ * Build a canonical envelope around the caller's `data` payload. Pure;
16
+ * doesn't touch the filesystem. Useful for tests + replay fixtures.
17
+ */
18
+ export declare function buildEnvelope<TType extends EventType, TData>(input: EmitInput<TType, TData>): EventEnvelope<TType, TData>;
19
+ /**
20
+ * Append one canonical event to `.harnery/events.ndjson` under a file lock.
21
+ * Append-only, one JSON object per line, lock-serialized.
22
+ *
23
+ * Synchronous because hooks are short-lived processes: we want the append to
24
+ * land before the binary exits, and async/await across a tiny write is just
25
+ * latency for no benefit.
26
+ *
27
+ * Returns the envelope so callers can chain off it (debug logs, post-emit
28
+ * verdict requests).
29
+ */
30
+ export declare function emit<TType extends EventType, TData>(coordRoot: string, input: EmitInput<TType, TData>): EventEnvelope<TType, TData>;
31
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/events/emit.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,OAAO,EAEZ,KAAK,MAAM,EACZ,MAAM,aAAa,CAAC;AAQrB,MAAM,WAAW,SAAS,CAAC,KAAK,SAAS,SAAS,EAAE,KAAK;IACvD,UAAU,EAAE,KAAK,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,SAAS,SAAS,EAAE,KAAK,EAC1D,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAC7B,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAe7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,KAAK,SAAS,SAAS,EAAE,KAAK,EACjD,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,GAC7B,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAqC7B"}