harnery 0.36.0 → 0.37.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 (311) hide show
  1. package/dist/commander.js +4 -0
  2. package/dist/commands/agents.d.ts.map +1 -1
  3. package/dist/commands/agents.js +87 -46
  4. package/dist/commands/artifacts.d.ts.map +1 -1
  5. package/dist/commands/artifacts.js +26 -1
  6. package/dist/commands/browse.d.ts.map +1 -1
  7. package/dist/commands/browse.js +11 -0
  8. package/dist/commands/diagnostics.d.ts +4 -0
  9. package/dist/commands/diagnostics.d.ts.map +1 -0
  10. package/dist/commands/diagnostics.js +229 -0
  11. package/dist/commands/files.d.ts +12 -0
  12. package/dist/commands/files.d.ts.map +1 -1
  13. package/dist/commands/files.js +54 -1
  14. package/dist/commands/governor.d.ts.map +1 -1
  15. package/dist/commands/governor.js +4 -0
  16. package/dist/commands/ledger-v3.d.ts.map +1 -1
  17. package/dist/commands/ledger-v3.js +71 -2
  18. package/dist/commands/qa-run.d.ts +4 -0
  19. package/dist/commands/qa-run.d.ts.map +1 -0
  20. package/dist/commands/qa-run.js +141 -0
  21. package/dist/commands/resources.d.ts +4 -0
  22. package/dist/commands/resources.d.ts.map +1 -0
  23. package/dist/commands/resources.js +29 -0
  24. package/dist/commands/supervisor.d.ts +4 -0
  25. package/dist/commands/supervisor.d.ts.map +1 -0
  26. package/dist/commands/supervisor.js +101 -0
  27. package/dist/commands/web.d.ts.map +1 -1
  28. package/dist/commands/web.js +17 -0
  29. package/dist/commands/workflow.d.ts.map +1 -1
  30. package/dist/commands/workflow.js +4 -0
  31. package/dist/core/agents/cli.js +7 -3
  32. package/dist/core/agents/health.d.ts +17 -0
  33. package/dist/core/agents/health.d.ts.map +1 -0
  34. package/dist/core/agents/health.js +109 -0
  35. package/dist/core/agents/mailbox.d.ts +92 -0
  36. package/dist/core/agents/mailbox.d.ts.map +1 -0
  37. package/dist/core/agents/mailbox.js +325 -0
  38. package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
  39. package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
  40. package/dist/core/agents/reconcile-coordination-v3.js +58 -0
  41. package/dist/core/agents/render/prompt-context.d.ts +5 -0
  42. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  43. package/dist/core/agents/render/prompt-context.js +34 -13
  44. package/dist/core/agents/render/session-context.d.ts +8 -0
  45. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  46. package/dist/core/agents/render/session-context.js +34 -3
  47. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  48. package/dist/core/agents/rules/commit-conflict.js +50 -1
  49. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  50. package/dist/core/agents/rules/stop-hook.js +6 -10
  51. package/dist/core/agents/session-finalizer-v3.js +4 -1
  52. package/dist/core/artifacts/index.d.ts +26 -1
  53. package/dist/core/artifacts/index.d.ts.map +1 -1
  54. package/dist/core/artifacts/index.js +146 -6
  55. package/dist/core/config.d.ts +12 -0
  56. package/dist/core/config.d.ts.map +1 -1
  57. package/dist/core/config.js +43 -0
  58. package/dist/core/diagnostics/advice.d.ts +9 -0
  59. package/dist/core/diagnostics/advice.d.ts.map +1 -0
  60. package/dist/core/diagnostics/advice.js +112 -0
  61. package/dist/core/diagnostics/bundle.d.ts +35 -0
  62. package/dist/core/diagnostics/bundle.d.ts.map +1 -0
  63. package/dist/core/diagnostics/bundle.js +874 -0
  64. package/dist/core/diagnostics/comparison.d.ts +5 -0
  65. package/dist/core/diagnostics/comparison.d.ts.map +1 -0
  66. package/dist/core/diagnostics/comparison.js +444 -0
  67. package/dist/core/diagnostics/contract.d.ts +245 -0
  68. package/dist/core/diagnostics/contract.d.ts.map +1 -0
  69. package/dist/core/diagnostics/contract.js +18 -0
  70. package/dist/core/diagnostics/identity.d.ts +3 -0
  71. package/dist/core/diagnostics/identity.d.ts.map +1 -0
  72. package/dist/core/diagnostics/identity.js +10 -0
  73. package/dist/core/diagnostics/index.d.ts +8 -0
  74. package/dist/core/diagnostics/index.d.ts.map +1 -0
  75. package/dist/core/diagnostics/index.js +7 -0
  76. package/dist/core/diagnostics/replay.d.ts +8 -0
  77. package/dist/core/diagnostics/replay.d.ts.map +1 -0
  78. package/dist/core/diagnostics/replay.js +213 -0
  79. package/dist/core/diagnostics/sanitize.d.ts +9 -0
  80. package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
  81. package/dist/core/diagnostics/sanitize.js +84 -0
  82. package/dist/core/events/legacy-storage/compression.d.ts +13 -0
  83. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
  84. package/dist/core/events/legacy-storage/compression.js +107 -0
  85. package/dist/core/events/legacy-storage/index.d.ts +1 -0
  86. package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
  87. package/dist/core/events/legacy-storage/index.js +1 -0
  88. package/dist/core/events/v3/archive-retention.d.ts +33 -0
  89. package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
  90. package/dist/core/events/v3/archive-retention.js +195 -0
  91. package/dist/core/events/v3/capabilities.d.ts +1 -1
  92. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  93. package/dist/core/events/v3/capabilities.js +1 -0
  94. package/dist/core/events/v3/index.d.ts +1 -0
  95. package/dist/core/events/v3/index.d.ts.map +1 -1
  96. package/dist/core/events/v3/index.js +1 -0
  97. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  98. package/dist/core/events/v3/live-routing.js +1 -0
  99. package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
  100. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  101. package/dist/core/events/v3/producers/hook-base.js +13 -2
  102. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  103. package/dist/core/events/v3/producers/intake.js +1 -0
  104. package/dist/core/events/v3/producers/recorder.d.ts +16 -0
  105. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  106. package/dist/core/events/v3/producers/recorder.js +88 -1
  107. package/dist/core/hooks/adapter/output.d.ts +8 -0
  108. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  109. package/dist/core/hooks/adapter/output.js +11 -1
  110. package/dist/core/hooks/cli.js +183 -192
  111. package/dist/core/hooks/health.d.ts +59 -0
  112. package/dist/core/hooks/health.d.ts.map +1 -0
  113. package/dist/core/hooks/health.js +84 -0
  114. package/dist/core/resources/contract.d.ts +90 -0
  115. package/dist/core/resources/contract.d.ts.map +1 -0
  116. package/dist/core/resources/contract.js +2 -0
  117. package/dist/core/resources/index.d.ts +6 -0
  118. package/dist/core/resources/index.d.ts.map +1 -0
  119. package/dist/core/resources/index.js +5 -0
  120. package/dist/core/resources/sampler.d.ts +27 -0
  121. package/dist/core/resources/sampler.d.ts.map +1 -0
  122. package/dist/core/resources/sampler.js +357 -0
  123. package/dist/core/resources/service-status.d.ts +3 -0
  124. package/dist/core/resources/service-status.d.ts.map +1 -0
  125. package/dist/core/resources/service-status.js +48 -0
  126. package/dist/core/resources/service.d.ts +26 -0
  127. package/dist/core/resources/service.d.ts.map +1 -0
  128. package/dist/core/resources/service.js +292 -0
  129. package/dist/core/resources/storage.d.ts +13 -0
  130. package/dist/core/resources/storage.d.ts.map +1 -0
  131. package/dist/core/resources/storage.js +30 -0
  132. package/dist/core/storage/atomic-json.d.ts +3 -0
  133. package/dist/core/storage/atomic-json.d.ts.map +1 -0
  134. package/dist/core/storage/atomic-json.js +15 -0
  135. package/dist/core/storage/builtins.d.ts.map +1 -1
  136. package/dist/core/storage/builtins.js +45 -6
  137. package/dist/core/storage/logger.d.ts +2 -0
  138. package/dist/core/storage/logger.d.ts.map +1 -1
  139. package/dist/core/storage/logger.js +2 -0
  140. package/dist/core/storage/query.d.ts +14 -0
  141. package/dist/core/storage/query.d.ts.map +1 -1
  142. package/dist/core/storage/query.js +51 -0
  143. package/dist/core/supervisor/activity.d.ts +4 -0
  144. package/dist/core/supervisor/activity.d.ts.map +1 -0
  145. package/dist/core/supervisor/activity.js +141 -0
  146. package/dist/core/supervisor/contract.d.ts +268 -0
  147. package/dist/core/supervisor/contract.d.ts.map +1 -0
  148. package/dist/core/supervisor/contract.js +35 -0
  149. package/dist/core/supervisor/explanations.d.ts +4 -0
  150. package/dist/core/supervisor/explanations.d.ts.map +1 -0
  151. package/dist/core/supervisor/explanations.js +71 -0
  152. package/dist/core/supervisor/findings.d.ts +20 -0
  153. package/dist/core/supervisor/findings.d.ts.map +1 -0
  154. package/dist/core/supervisor/findings.js +399 -0
  155. package/dist/core/supervisor/history.d.ts +9 -0
  156. package/dist/core/supervisor/history.d.ts.map +1 -0
  157. package/dist/core/supervisor/history.js +46 -0
  158. package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
  159. package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
  160. package/dist/core/supervisor/hook-health-alerts.js +70 -0
  161. package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
  162. package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
  163. package/dist/core/supervisor/hook-health-storage.js +24 -0
  164. package/dist/core/supervisor/hook-health.d.ts +72 -0
  165. package/dist/core/supervisor/hook-health.d.ts.map +1 -0
  166. package/dist/core/supervisor/hook-health.js +203 -0
  167. package/dist/core/supervisor/hooks.d.ts +10 -0
  168. package/dist/core/supervisor/hooks.d.ts.map +1 -0
  169. package/dist/core/supervisor/hooks.js +28 -0
  170. package/dist/core/supervisor/index.d.ts +15 -0
  171. package/dist/core/supervisor/index.d.ts.map +1 -0
  172. package/dist/core/supervisor/index.js +14 -0
  173. package/dist/core/supervisor/log-feed.d.ts +9 -0
  174. package/dist/core/supervisor/log-feed.d.ts.map +1 -0
  175. package/dist/core/supervisor/log-feed.js +103 -0
  176. package/dist/core/supervisor/service.d.ts +29 -0
  177. package/dist/core/supervisor/service.d.ts.map +1 -0
  178. package/dist/core/supervisor/service.js +450 -0
  179. package/dist/core/supervisor/services.d.ts +11 -0
  180. package/dist/core/supervisor/services.d.ts.map +1 -0
  181. package/dist/core/supervisor/services.js +155 -0
  182. package/dist/core/supervisor/status.d.ts +3 -0
  183. package/dist/core/supervisor/status.d.ts.map +1 -0
  184. package/dist/core/supervisor/status.js +48 -0
  185. package/dist/core/supervisor/storage.d.ts +30 -0
  186. package/dist/core/supervisor/storage.d.ts.map +1 -0
  187. package/dist/core/supervisor/storage.js +73 -0
  188. package/dist/core/supervisor/timeline.d.ts +3 -0
  189. package/dist/core/supervisor/timeline.d.ts.map +1 -0
  190. package/dist/core/supervisor/timeline.js +87 -0
  191. package/dist/core/workflow/admission.d.ts +23 -0
  192. package/dist/core/workflow/admission.d.ts.map +1 -0
  193. package/dist/core/workflow/admission.js +136 -0
  194. package/dist/core/workflow/engine.d.ts.map +1 -1
  195. package/dist/core/workflow/engine.js +77 -2
  196. package/dist/core/workflow/index.d.ts +3 -2
  197. package/dist/core/workflow/index.d.ts.map +1 -1
  198. package/dist/core/workflow/index.js +2 -1
  199. package/dist/core/workflow/proof.d.ts +2 -1
  200. package/dist/core/workflow/proof.d.ts.map +1 -1
  201. package/dist/core/workflow/proof.js +39 -1
  202. package/dist/core/workflow/run-state.d.ts +3 -2
  203. package/dist/core/workflow/run-state.d.ts.map +1 -1
  204. package/dist/core/workflow/run-state.js +8 -1
  205. package/dist/core/workflow/types.d.ts +33 -1
  206. package/dist/core/workflow/types.d.ts.map +1 -1
  207. package/dist/core/workflow/types.js +2 -1
  208. package/dist/lib/browser/client.d.ts +5 -0
  209. package/dist/lib/browser/client.d.ts.map +1 -1
  210. package/dist/lib/browser/client.js +1 -0
  211. package/dist/lib/browser/index.d.ts +2 -0
  212. package/dist/lib/browser/index.d.ts.map +1 -1
  213. package/dist/lib/browser/index.js +2 -0
  214. package/dist/lib/browser/qa-run-contracts.d.ts +173 -0
  215. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
  216. package/dist/lib/browser/qa-run-contracts.js +226 -0
  217. package/dist/lib/browser/qa-run.d.ts +51 -0
  218. package/dist/lib/browser/qa-run.d.ts.map +1 -0
  219. package/dist/lib/browser/qa-run.js +684 -0
  220. package/dist/lib/coord-root-id.d.ts +9 -0
  221. package/dist/lib/coord-root-id.d.ts.map +1 -0
  222. package/dist/lib/coord-root-id.js +17 -0
  223. package/dist/lib/instructions/apply.d.ts +3 -1
  224. package/dist/lib/instructions/apply.d.ts.map +1 -1
  225. package/dist/lib/instructions/apply.js +74 -3
  226. package/dist/lib/instructions/templates.d.ts.map +1 -1
  227. package/dist/lib/instructions/templates.js +6 -0
  228. package/package.json +12 -1
  229. package/src/commander.ts +26 -0
  230. package/src/commands/agents.ts +102 -49
  231. package/src/commands/artifacts.ts +40 -2
  232. package/src/commands/browse.ts +15 -0
  233. package/src/commands/diagnostics.ts +271 -0
  234. package/src/commands/files.ts +102 -2
  235. package/src/commands/governor.ts +8 -0
  236. package/src/commands/ledger-v3.ts +82 -0
  237. package/src/commands/qa-run.ts +184 -0
  238. package/src/commands/resources.ts +38 -0
  239. package/src/commands/supervisor.ts +141 -0
  240. package/src/commands/web.ts +19 -0
  241. package/src/commands/workflow.ts +8 -0
  242. package/src/core/agents/cli.ts +7 -3
  243. package/src/core/agents/health.ts +142 -0
  244. package/src/core/agents/mailbox.ts +373 -0
  245. package/src/core/agents/reconcile-coordination-v3.ts +77 -0
  246. package/src/core/agents/render/prompt-context.ts +42 -12
  247. package/src/core/agents/render/session-context.ts +37 -3
  248. package/src/core/agents/rules/commit-conflict.ts +58 -1
  249. package/src/core/agents/rules/stop-hook.ts +6 -12
  250. package/src/core/agents/session-finalizer-v3.ts +4 -1
  251. package/src/core/artifacts/index.ts +196 -10
  252. package/src/core/config.ts +111 -2
  253. package/src/core/diagnostics/advice.ts +147 -0
  254. package/src/core/diagnostics/bundle.ts +1142 -0
  255. package/src/core/diagnostics/comparison.ts +565 -0
  256. package/src/core/diagnostics/contract.ts +311 -0
  257. package/src/core/diagnostics/identity.ts +11 -0
  258. package/src/core/diagnostics/index.ts +7 -0
  259. package/src/core/diagnostics/replay.ts +314 -0
  260. package/src/core/diagnostics/sanitize.ts +97 -0
  261. package/src/core/events/legacy-storage/compression.ts +130 -0
  262. package/src/core/events/legacy-storage/index.ts +1 -0
  263. package/src/core/events/v3/archive-retention.ts +251 -0
  264. package/src/core/events/v3/capabilities.ts +2 -0
  265. package/src/core/events/v3/index.ts +9 -0
  266. package/src/core/events/v3/live-routing.ts +1 -0
  267. package/src/core/events/v3/producers/hook-base.ts +14 -2
  268. package/src/core/events/v3/producers/intake.ts +1 -0
  269. package/src/core/events/v3/producers/recorder.ts +110 -1
  270. package/src/core/hooks/adapter/output.ts +12 -1
  271. package/src/core/hooks/cli.ts +192 -215
  272. package/src/core/hooks/health.ts +146 -0
  273. package/src/core/resources/contract.ts +98 -0
  274. package/src/core/resources/index.ts +5 -0
  275. package/src/core/resources/sampler.ts +458 -0
  276. package/src/core/resources/service-status.ts +62 -0
  277. package/src/core/resources/service.ts +337 -0
  278. package/src/core/resources/storage.ts +44 -0
  279. package/src/core/storage/atomic-json.ts +15 -0
  280. package/src/core/storage/builtins.ts +62 -6
  281. package/src/core/storage/logger.ts +2 -0
  282. package/src/core/storage/query.ts +58 -0
  283. package/src/core/supervisor/activity.ts +193 -0
  284. package/src/core/supervisor/contract.ts +312 -0
  285. package/src/core/supervisor/explanations.ts +104 -0
  286. package/src/core/supervisor/findings.ts +788 -0
  287. package/src/core/supervisor/history.ts +61 -0
  288. package/src/core/supervisor/hook-health-alerts.ts +108 -0
  289. package/src/core/supervisor/hook-health-storage.ts +25 -0
  290. package/src/core/supervisor/hook-health.ts +301 -0
  291. package/src/core/supervisor/hooks.ts +35 -0
  292. package/src/core/supervisor/index.ts +14 -0
  293. package/src/core/supervisor/log-feed.ts +131 -0
  294. package/src/core/supervisor/service.ts +511 -0
  295. package/src/core/supervisor/services.ts +203 -0
  296. package/src/core/supervisor/status.ts +59 -0
  297. package/src/core/supervisor/storage.ts +126 -0
  298. package/src/core/supervisor/timeline.ts +133 -0
  299. package/src/core/workflow/admission.ts +215 -0
  300. package/src/core/workflow/engine.ts +93 -1
  301. package/src/core/workflow/index.ts +10 -0
  302. package/src/core/workflow/proof.ts +54 -1
  303. package/src/core/workflow/run-state.ts +13 -1
  304. package/src/core/workflow/types.ts +39 -1
  305. package/src/lib/browser/client.ts +6 -0
  306. package/src/lib/browser/index.ts +29 -0
  307. package/src/lib/browser/qa-run-contracts.ts +380 -0
  308. package/src/lib/browser/qa-run.ts +784 -0
  309. package/src/lib/coord-root-id.ts +23 -0
  310. package/src/lib/instructions/apply.ts +81 -2
  311. package/src/lib/instructions/templates.ts +6 -0
@@ -21,11 +21,21 @@
21
21
  */
22
22
  import { STOP_REMEDIATION_MARKER } from "../../agents/rules/stop-hook.js";
23
23
  import { endOfTurnStatusCommand, resolveBinName } from "../../config.js";
24
+ /**
25
+ * Whether this adapter can actually receive injected context for an event.
26
+ * Cursor's beforeSubmitPrompt hook can allow or block a turn but cannot inject
27
+ * model context, so anything rendered for that event is discarded. A caller
28
+ * that consumes state to build the text (draining a mailbox, for instance)
29
+ * must check this first, or the state is spent on output nobody reads.
30
+ */
31
+ export function canReceiveContext(adapter, event) {
32
+ return !(adapter === "cursor" && event === "UserPromptSubmit");
33
+ }
24
34
  /** Emit a context-injection (peer table, wiring check, council pending, …). */
25
35
  export function emitContext(adapter, event, text) {
26
36
  if (!text || text.length === 0)
27
37
  return;
28
- if (adapter === "cursor" && event === "UserPromptSubmit")
38
+ if (!canReceiveContext(adapter, event))
29
39
  return;
30
40
  const json = buildContextJson(adapter, event, text);
31
41
  process.stdout.write(`${JSON.stringify(json)}\n`);
@@ -21,15 +21,15 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
21
21
  import { dirname, join } from "node:path";
22
22
  import { coordEnv } from "../../lib/env.js";
23
23
  import { buildInstructionBundle } from "../../lib/instructions/bundle.js";
24
- import { coordBinPath } from "../agents/coord-bin.js";
25
24
  import { resolveCoordRoot } from "../agents/coord-client.js";
26
25
  import { classifyWriteClaimFinalization, formatWriteClaimFinalizationDenial, } from "../agents/finalization.js";
27
- import { restoreLiveCoordinationStateAfterEpochV3 } from "../agents/live-authority-v3.js";
26
+ import { recordLiveClaimChangeV3, restoreLiveCoordinationStateAfterEpochV3, } from "../agents/live-authority-v3.js";
28
27
  import { evaluateStopHook, STOP_REMEDIATION_MARKER } from "../agents/rules/stop-hook.js";
29
28
  import { assistantTextStartsWithSessionNameBlock, isSessionNameRemediationCommand, sessionNameDisplayInstruction, sessionNameDisplayPending, sessionNameDisplayRecoveryInstruction, toolResponseMintedSessionName, } from "../agents/session-name-display.js";
30
29
  import { readHeartbeat, stampSessionNameSeen } from "../agents/state/heartbeat-writer.js";
31
30
  import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
32
31
  import { ensureLiveCoordinationHeartbeat } from "../agents/state/live-coordination-writer.js";
32
+ import { assignName } from "../agents/state/names.js";
33
33
  import { writePidmapRow } from "../agents/state/pidmap.js";
34
34
  import { agentsRequireGitFinalization, resolveBinName } from "../config.js";
35
35
  import { checkpointContext, completeContextRecovery, extractContextSample, markContextCompactionCompleted, prepareContextRecovery, readContextState, recordContextSample, } from "../context/index.js";
@@ -46,12 +46,14 @@ import { codexWslFileLinkTelemetry, inspectCodexWslBridge, isWslUncPath, renderC
46
46
  import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
47
47
  import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
48
48
  import { canonicalize } from "./guard-path.js";
49
+ import { beginHookHealth, observeHookDebug, observeHookError, writeHookHealthCompletion, } from "./health.js";
49
50
  import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/anchor.js";
50
51
  import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
51
52
  import { resolveOwner } from "./resolve/owner.js";
52
53
  import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanSessionNameDisplayedImmediately, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
53
54
  import { sessionNamePresence } from "./session-name-presence.js";
54
55
  import { unsafeCrossShellReason } from "./unsafe-cross-shell.js";
56
+ let hookHealthState;
55
57
  function parseArgv(argv) {
56
58
  const out = { eventName: null, extra: [] };
57
59
  for (let i = 0; i < argv.length; i++) {
@@ -90,16 +92,15 @@ async function readStdin() {
90
92
  }
91
93
  }
92
94
  /**
93
- * Env for child agent-coord spawns, with the coord root pinned. The hook
94
- * process may be running with the session shell's cwd (inside a submodule or
95
- * journal dir), and agent-coord resolves its root from cwd unless overridden —
96
- * without the pin a child could write state to a different .harnery than the
97
- * one this hook resolved.
95
+ * Env for a detached hook retry worker, with the coord root pinned. The hook
96
+ * may run with the session shell's cwd (inside a submodule or journal dir), so
97
+ * the worker must retain the root this process already resolved.
98
98
  */
99
99
  function childEnv(coordRoot) {
100
100
  return { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot };
101
101
  }
102
102
  function appendDebug(coordRoot, entry) {
103
+ observeHookDebug(hookHealthState, entry);
103
104
  if (coordEnv("SHARED_LOGS") !== "0") {
104
105
  try {
105
106
  processLogger(coordRoot, "agent-hook").debug("agent_hook.diagnostic", legacyLogFields(entry));
@@ -119,6 +120,7 @@ function appendDebug(coordRoot, entry) {
119
120
  }
120
121
  }
121
122
  function logError(coordRoot, err, context) {
123
+ observeHookError(hookHealthState, context.phase);
122
124
  if (!coordRoot)
123
125
  return;
124
126
  if (coordEnv("SHARED_LOGS") !== "0") {
@@ -145,33 +147,13 @@ function logError(coordRoot, err, context) {
145
147
  }
146
148
  }
147
149
  /**
148
- * Spawn `agent-coord assign-name <owner> <kind>` to mint or recover the
149
- * hurricane-style name for this owner. Returns null on any failure so
150
- * session.started emission never breaks the adapter flow.
151
- *
152
- * Lives at agent-hooks side (not agent-coord) to keep emitter/consumer
153
- * separation: we spawn rather than import.
150
+ * Mint or recover the hurricane-style name for this owner in-process. Returns
151
+ * null on any failure so session.started emission never breaks adapter flow.
154
152
  */
155
- function assignNameViaAgentCoord(coordRoot, instanceId, kind, forkedFrom) {
156
- const binary = coordBinPath("agent-coord", coordRoot) ?? "";
157
- if (!existsSync(binary))
158
- return null;
153
+ function assignNameInProcess(coordRoot, instanceId, kind, forkedFrom) {
159
154
  try {
160
- const args = ["assign-name", instanceId, kind];
161
- if (forkedFrom)
162
- args.push("--forked-from", forkedFrom);
163
- const result = spawnSync(binary, args, {
164
- encoding: "utf8",
165
- timeout: 2000,
166
- env: childEnv(coordRoot),
167
- });
168
- if (result.status !== 0 || !result.stdout)
169
- return null;
170
- const parsed = JSON.parse(result.stdout.trim());
171
- if (parsed.name && parsed.kind) {
172
- return { name: parsed.name, kind: parsed.kind };
173
- }
174
- return null;
155
+ const name = assignName(coordRoot, instanceId, kind, forkedFrom ? { forkedFrom } : undefined);
156
+ return { name, kind };
175
157
  }
176
158
  catch {
177
159
  return null;
@@ -227,9 +209,9 @@ function buildEventData(eventType, ctx) {
227
209
  // tool.requested heal path instead. The forkedFrom plumbing below stays
228
210
  // for adapters that DO report a parent at session start.
229
211
  const forkedFrom = undefined;
230
- // Assign (or recover) name + kind via agent-coord. Idempotent: resume
212
+ // Assign (or recover) name + kind in-process. Idempotent: resume
231
213
  // returns the original name; new owner consumes a counter slot.
232
- const assigned = assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
214
+ const assigned = assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
233
215
  // Write the adapter pid-map row so `harn agents whoami` ppid-walks find
234
216
  // this owner. Prefer the payload pid (the actual claude binary), then the
235
217
  // anchor walk (the `node` ancestor for Cursor, which has no payload pid),
@@ -293,7 +275,7 @@ function buildEventData(eventType, ctx) {
293
275
  // a later SessionStart remains idempotent.
294
276
  if (ctx.adapter === "cursor" &&
295
277
  !readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
296
- assignNameViaAgentCoord(ctx.coordRoot, ctx.instanceId, "session");
278
+ assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
297
279
  }
298
280
  const prompt = p?.prompt ?? "";
299
281
  const { value, truncated } = clampString(prompt, 4000);
@@ -351,7 +333,7 @@ function buildEventData(eventType, ctx) {
351
333
  // (agent-coord/state/names.ts → kind=transient). Use the call ID as the
352
334
  // instance_id input; assignName falls through to transient.
353
335
  const childInstanceId = subagentCallId ?? ctx.instanceId;
354
- const assigned = assignNameViaAgentCoord(ctx.coordRoot, childInstanceId, "subagent");
336
+ const assigned = assignNameInProcess(ctx.coordRoot, childInstanceId, "subagent");
355
337
  return {
356
338
  agent_type: p?.raw.agent_type ??
357
339
  p?.raw.subagent_type ??
@@ -466,10 +448,18 @@ function numberField(value) {
466
448
  }
467
449
  async function main() {
468
450
  const hookStartedAt = performance.now();
451
+ const hookStartedRss = process.memoryUsage().rss;
469
452
  const hookClock = captureSpanClockV3();
470
453
  const { eventName, extra } = parseArgv(process.argv.slice(2));
471
454
  const adapter = detectAdapter(process.argv.slice(2));
472
455
  const raw = await readStdin();
456
+ hookHealthState = beginHookHealth({
457
+ started_at_ms: hookStartedAt,
458
+ started_rss_bytes: hookStartedRss,
459
+ event_name: eventName,
460
+ adapter,
461
+ payload_bytes: raw.length,
462
+ });
473
463
  // Cursor executes the Claude Code project hooks too on hosts wired for both
474
464
  // adapters, piping them its own payload (identifiable by the top-level
475
465
  // cursor_version envelope field). That stray `--adapter claude-code`
@@ -480,6 +470,7 @@ async function main() {
480
470
  if (coordEnv("AGENT_COORD_OFF") !== "1") {
481
471
  const coordRoot = resolveCoordRoot(process.cwd());
482
472
  if (coordRoot) {
473
+ hookHealthState.coord_root = coordRoot;
483
474
  appendDebug(coordRoot, {
484
475
  ts: new Date().toISOString(),
485
476
  event_name: eventName,
@@ -532,6 +523,7 @@ async function main() {
532
523
  const coordRoot = resolveCoordRoot(process.cwd());
533
524
  if (!coordRoot)
534
525
  return 0;
526
+ hookHealthState.coord_root = coordRoot;
535
527
  if (eventName === "runtime-context-retry") {
536
528
  return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
537
529
  }
@@ -557,8 +549,9 @@ async function main() {
557
549
  // that tool. It is not a transcript and therefore cannot prove that an
558
550
  // earlier assistant response omitted the required session-name block.
559
551
  // afterAgentResponse is the one native boundary that carries the completed
560
- // assistant text, so stamp a positive sighting here without retaining or
561
- // emitting the response body into V3.
552
+ // assistant text. Preserve only privacy-safe ritual booleans in the V3
553
+ // producer's open-turn state; Stop consumes them into the authoritative
554
+ // turn.completed event. The response body never enters V3 or heartbeat.
562
555
  if (adapter === "cursor" && eventName === "after-agent-response") {
563
556
  const payload = parsePayload(raw, adapter);
564
557
  const owner = resolveOwner({ payload: payload?.raw ?? null, coordRoot });
@@ -575,6 +568,53 @@ async function main() {
575
568
  else {
576
569
  appendDebug(coordRoot, { ...debugBase, skipped: "no-session-name-sighting" });
577
570
  }
571
+ if (ledgerRoute.state === "blocked") {
572
+ appendDebug(coordRoot, {
573
+ ...debugBase,
574
+ skipped: "v3-control-blocked",
575
+ reason: ledgerRoute.reason,
576
+ owner_source: owner.source,
577
+ });
578
+ return 0;
579
+ }
580
+ const statusBoxPresentStrict = scanAssistantStatusBoxPresent(undefined, text);
581
+ // The hook parser needs the completed reply long enough to scan it, but
582
+ // durable hook intake must never retain that body. Keep every other native
583
+ // field so turn correlation remains available to the producer.
584
+ const { text: _completedReply, ...privacySafeRaw } = payload?.raw ?? {};
585
+ const privacySafePayload = payload ? { ...payload, raw: privacySafeRaw } : null;
586
+ const v3Result = recordLiveHookSignalV3({
587
+ coordRoot,
588
+ route: ledgerRoute,
589
+ eventName,
590
+ payload: privacySafePayload,
591
+ adapter,
592
+ instanceId: owner.instance_id,
593
+ hook_name: eventName,
594
+ hook_duration_ms: Math.max(0, Math.floor(performance.now() - hookStartedAt)),
595
+ monotonic_ns: hookClock.monotonic_ns,
596
+ turn_ritual: {
597
+ status_box_present: statusBoxPresentStrict,
598
+ status_box_present_strict: statusBoxPresentStrict,
599
+ session_name_required: name !== undefined,
600
+ session_name_present: name ? assistantTextStartsWithSessionNameBlock(text, name) : false,
601
+ },
602
+ });
603
+ observeHookDebug(hookHealthState, { event_v3_state: v3Result.state });
604
+ appendDebug(coordRoot, {
605
+ ...debugBase,
606
+ effect: "cursor-response-ritual-observed",
607
+ owner_source: owner.source,
608
+ event_v3_state: v3Result.state,
609
+ ...(v3Result.state === "observed"
610
+ ? {
611
+ generation_id: v3Result.generation_id,
612
+ turn_id: v3Result.turn_id,
613
+ response_observed_at: v3Result.observed_at,
614
+ status_box_present_strict: statusBoxPresentStrict,
615
+ }
616
+ : {}),
617
+ });
578
618
  return 0;
579
619
  }
580
620
  const norm = normalizeEventName(eventName);
@@ -595,6 +635,7 @@ async function main() {
595
635
  });
596
636
  return 0;
597
637
  }
638
+ hookHealthState.owner_id = owner.instance_id;
598
639
  const sessionId = payload?.session_id ?? payload?.conversation_id ?? owner.instance_id;
599
640
  const priorCoordination = readHeartbeat(coordRoot, owner.instance_id);
600
641
  const data = buildEventData(norm.event_type, {
@@ -661,6 +702,9 @@ async function main() {
661
702
  ...(turnRitual ? { turn_ritual: turnRitual } : {}),
662
703
  ...(deferV3Drain ? { defer_drain: true } : {}),
663
704
  });
705
+ observeHookDebug(hookHealthState, {
706
+ ...(v3Result ? { event_v3_state: v3Result.state } : {}),
707
+ });
664
708
  const recordedGenerationId = v3Result && "event" in v3Result && "generation_id" in v3Result.event.scope
665
709
  ? v3Result.event.scope.generation_id
666
710
  : undefined;
@@ -806,6 +850,7 @@ async function main() {
806
850
  // Session start is the only caller of the artifact janitor and the storage
807
851
  // maintenance pass, so both load here instead of on every tool call.
808
852
  const { autoCleanArtifacts } = await import("../artifacts/index.js");
853
+ const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
809
854
  const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
810
855
  // Effect: throttled daily sweep of expired artifact workspaces (guarded,
811
856
  // managed-expired only; see autoCleanArtifacts). Best-effort like every
@@ -816,6 +861,14 @@ async function main() {
816
861
  catch (err) {
817
862
  logError(coordRoot, err, { phase: "artifact-auto-clean" });
818
863
  }
864
+ // Effect: apply the same daily throttle to closed V3 epochs. The newest
865
+ // recovery epochs remain protected by the archive retention policy.
866
+ try {
867
+ autoCleanEventV3Archives(coordRoot);
868
+ }
869
+ catch (err) {
870
+ logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
871
+ }
819
872
  // Plan one bounded, claim-first global slice from cached pressure only.
820
873
  // Existing owner janitors above remain the only active executors until
821
874
  // each provider receives a separate production activation.
@@ -874,7 +927,7 @@ async function main() {
874
927
  // agnostic since v0.5.0.
875
928
  if (norm.event_type === "session.ended") {
876
929
  try {
877
- cleanupSessionEnd(coordRoot, owner.instance_id, data.reason ?? "unknown");
930
+ cleanupSessionEnd(coordRoot, owner.instance_id);
878
931
  }
879
932
  catch (err) {
880
933
  logError(coordRoot, err, { phase: "session-end-cleanup" });
@@ -900,40 +953,17 @@ async function main() {
900
953
  if (norm.event_type === "agent.started") {
901
954
  try {
902
955
  const childInstanceId = data.agent_id ?? owner.instance_id;
903
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
904
- if (existsSync(agentCoordBin)) {
905
- spawnSync(agentCoordBin, ["project"], {
906
- encoding: "utf8",
907
- timeout: 3000,
908
- env: childEnv(coordRoot),
909
- });
910
- spawnSync(agentCoordBin, [
911
- "log",
912
- `SUBAGENT_START agent_type=${data.agent_type ?? "unknown"} agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
913
- "--instance",
914
- childInstanceId,
915
- ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
916
- }
917
- emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
956
+ await emitSubagentStartContext(coordRoot, childInstanceId, sessionId, data, adapter);
918
957
  }
919
958
  catch (err) {
920
- logError(coordRoot, err, { phase: "subagent-start-project" });
959
+ logError(coordRoot, err, { phase: "subagent-start-context" });
921
960
  }
922
961
  }
923
962
  // Phase 8: SubagentStop: delete subagent heartbeat + log.
924
963
  if (norm.event_type === "agent.completed") {
925
964
  try {
926
965
  const childInstanceId = payload?.subagent_id ?? payload?.agent_id ?? owner.instance_id;
927
- cleanupSessionEnd(coordRoot, childInstanceId, data.reason ?? "unknown");
928
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
929
- if (existsSync(agentCoordBin)) {
930
- spawnSync(agentCoordBin, [
931
- "log",
932
- `SUBAGENT_STOP agent_id=${childInstanceId.slice(0, 8)} platform=${adapterPlatform(adapter)}`,
933
- "--instance",
934
- childInstanceId,
935
- ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
936
- }
966
+ cleanupSessionEnd(coordRoot, childInstanceId);
937
967
  const { reconcileSessionFinalizationV3 } = await import("../agents/session-finalizer-v3.js");
938
968
  reconcileSessionFinalizationV3(coordRoot, { archive_observations: [] });
939
969
  }
@@ -1273,40 +1303,19 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
1273
1303
  }
1274
1304
  if (targets.length === 0)
1275
1305
  return true;
1276
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1277
- if (!existsSync(agentCoordBin)) {
1278
- if (agentsRequireGitFinalization(coordRoot)) {
1279
- const { emitDeny } = await import("./adapter/output.js");
1280
- emitDeny(adapter, "Harnery denied the write before mutation because the claim coordinator is unavailable; repair the project's Harnery installation and retry.");
1281
- return false;
1282
- }
1283
- return true;
1284
- }
1306
+ const { evaluateClaim } = await import("../agents/rules/claim-conflict.js");
1285
1307
  // For apply_patch (multi-file), collect siblings so the deny reason names
1286
1308
  // them. For single-file tools the array has one entry.
1287
1309
  for (const target of targets) {
1288
- const verdictReq = JSON.stringify({
1289
- rule: "claim",
1290
- instance_id: instanceId,
1291
- session_id: sessionId,
1292
- path: target.path,
1293
- });
1294
- const result = spawnSync(agentCoordBin, ["verdict"], {
1295
- input: verdictReq,
1296
- encoding: "utf8",
1297
- timeout: 3000,
1298
- env: childEnv(coordRoot),
1299
- });
1300
- if (result.status !== 0 || !result.stdout)
1301
- continue;
1302
- let parsed = {};
1303
1310
  try {
1304
- parsed = JSON.parse(result.stdout.trim());
1305
- }
1306
- catch {
1307
- continue;
1308
- }
1309
- if (parsed.allow === false) {
1311
+ const parsed = evaluateClaim(coordRoot, {
1312
+ rule: "claim",
1313
+ instance_id: instanceId,
1314
+ session_id: sessionId,
1315
+ path: target.path,
1316
+ });
1317
+ if (parsed.allow !== false)
1318
+ continue;
1310
1319
  let reason = parsed.reason ?? `Path ${target.path} is currently being edited by another agent.`;
1311
1320
  if (targets.length > 1) {
1312
1321
  const siblings = targets
@@ -1322,6 +1331,7 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, adapte
1322
1331
  emitDeny(adapter, reason);
1323
1332
  return false;
1324
1333
  }
1334
+ catch { }
1325
1335
  }
1326
1336
  return true;
1327
1337
  }
@@ -1520,16 +1530,23 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
1520
1530
  const row = readLiveCoordinationRow(coordRoot, instanceId);
1521
1531
  if (!row?.files_touched?.includes(canonical))
1522
1532
  return;
1523
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1524
- if (!existsSync(agentCoordBin))
1525
- return;
1526
- spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
1527
- encoding: "utf8",
1528
- timeout: 2000,
1529
- env: childEnv(coordRoot),
1530
- });
1533
+ const before = readHeartbeat(coordRoot, instanceId);
1534
+ try {
1535
+ recordLiveClaimChangeV3({
1536
+ coordRoot,
1537
+ owner: instanceId,
1538
+ nativeSessionId: before?.session_id ?? instanceId,
1539
+ adapter: adapterFromPlatform(before?.platform),
1540
+ operation: "released",
1541
+ path: canonical,
1542
+ access: "write",
1543
+ });
1544
+ }
1545
+ catch {
1546
+ /* best-effort release must not break adapter flow */
1547
+ }
1531
1548
  }
1532
- function cleanupSessionEnd(coordRoot, instanceId, reason) {
1549
+ function cleanupSessionEnd(coordRoot, instanceId) {
1533
1550
  // Sweep pid-map entries pointing to this instance
1534
1551
  const pidmapDir = join(coordRoot, ".harnery", "pid-map");
1535
1552
  if (existsSync(pidmapDir)) {
@@ -1553,44 +1570,20 @@ function cleanupSessionEnd(coordRoot, instanceId, reason) {
1553
1570
  /* swallow */
1554
1571
  }
1555
1572
  }
1556
- // Activity log
1557
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1558
- if (existsSync(agentCoordBin)) {
1559
- spawnSync(agentCoordBin, ["log", `SESSION_END reason=${reason}`, "--instance", instanceId], { encoding: "utf8", timeout: 2000 });
1560
- }
1561
1573
  }
1562
1574
  async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "") {
1563
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1564
- let additionalContext = "";
1565
- if (existsSync(agentCoordBin)) {
1566
- const args = ["prompt-context", "--instance", instanceId, "--session", sessionId];
1567
- // Every human-facing adapter gets the first-session naming reminder from
1568
- // the shared hook path. Cursor + Codex additionally get ongoing set-task
1569
- // staleness nudges; Claude Code enforces those via its Stop transcript.
1570
- // Codex also gets the optional project-owned prompt reminder. Claude Code
1571
- // already has native output-style reminders, so duplicating it would add
1572
- // noise. Cursor's current beforeSubmitPrompt output can allow or block but
1573
- // cannot inject model context; an Always Apply rule is its durable carrier.
1574
- // Only the boolean route travels through argv, never the host's text.
1575
- if (adapter === "codex")
1576
- args.push("--host-prompt-reminder");
1577
- // Codex gets a fresh status-footer reminder on every prompt. Its Stop hook
1578
- // stays observe-only, so missing the footer never replaces the answer.
1579
- // Claude Code gets a fresh per-turn ritual reminder: satisfying the
1580
- // contract up front is far cheaper than the bounce-and-retry the Stop hook
1581
- // otherwise forces. Cursor's beforeSubmitPrompt cannot inject context, so
1582
- // its Stop follow-up remains the supported enforcement channel.
1583
- args.push("--session-name-nudge");
1584
- if (adapter === "cursor" || adapter === "codex")
1585
- args.push("--task-nudge");
1586
- if (adapter === "codex")
1587
- args.push("--status-footer-nudge");
1588
- if (adapter === "claude-code")
1589
- args.push("--turn-ritual-nudge", adapter);
1590
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1591
- if (result.status === 0 && result.stdout)
1592
- additionalContext = result.stdout.trim();
1593
- }
1575
+ const { renderPromptContext } = await import("../agents/render/prompt-context.js");
1576
+ let additionalContext = renderPromptContext({
1577
+ coordRoot,
1578
+ instanceId,
1579
+ sessionId,
1580
+ adapter,
1581
+ sessionNameNudge: true,
1582
+ taskNudge: adapter === "cursor" || adapter === "codex",
1583
+ hostPromptReminder: adapter === "codex",
1584
+ statusFooterNudge: adapter === "codex",
1585
+ turnRitualNudge: adapter === "claude-code" ? adapter : undefined,
1586
+ }).trim();
1594
1587
  if (adapter === "codex") {
1595
1588
  const fileLinkContext = renderCodexWslFileLinkContext(coordRoot, workspaceCwd);
1596
1589
  if (fileLinkContext) {
@@ -1606,8 +1599,8 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
1606
1599
  emitContext(adapter, "UserPromptSubmit", additionalContext);
1607
1600
  return true;
1608
1601
  }
1609
- function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
1610
- // Look up the subagent's assigned name (just-written by agent-coord assignName
1602
+ async function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapter) {
1603
+ // Look up the subagent's assigned name (just-written by the in-process name
1611
1604
  // in session.started data) + the parent's short id for the "you are a subagent
1612
1605
  // of X" framing.
1613
1606
  const subagentName = data.name ?? "";
@@ -1619,69 +1612,55 @@ function emitSubagentStartContext(coordRoot, instanceId, sessionId, data, adapte
1619
1612
  // Render peer table inline since the subagent might want to know who else
1620
1613
  // is around. Reuse prompt-context (which dedups against the per-owner hash);
1621
1614
  // first call will always emit.
1622
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1623
1615
  let combined = message;
1624
- if (existsSync(agentCoordBin)) {
1625
- const result = spawnSync(agentCoordBin, ["prompt-context", "--instance", instanceId, "--session", sessionId, "--name", subagentName], { encoding: "utf8", timeout: 3000 });
1626
- const ctx = (result.stdout ?? "").trim();
1627
- if (ctx)
1628
- combined = `${message}\n\n${ctx}`;
1629
- }
1616
+ const { renderPromptContext } = await import("../agents/render/prompt-context.js");
1617
+ const context = renderPromptContext({
1618
+ coordRoot,
1619
+ instanceId,
1620
+ sessionId,
1621
+ agentName: subagentName,
1622
+ }).trim();
1623
+ if (context)
1624
+ combined = `${message}\n\n${context}`;
1630
1625
  // Use SubagentStart event-name in CC's hookSpecificOutput shape; cursor's
1631
1626
  // flat `additional_context` works the same way.
1632
- void import("./adapter/output.js").then(({ emitContext }) => {
1633
- emitContext(adapter, "SubagentStart", combined);
1634
- });
1627
+ const { emitContext } = await import("./adapter/output.js");
1628
+ emitContext(adapter, "SubagentStart", combined);
1635
1629
  }
1636
1630
  function adapterPlatform(adapter) {
1637
1631
  if (adapter === "claude-code")
1638
1632
  return "claude-code";
1639
1633
  return adapter;
1640
1634
  }
1635
+ function adapterFromPlatform(platform) {
1636
+ if (platform === "cursor")
1637
+ return "cursor";
1638
+ if (platform === "codex")
1639
+ return "codex";
1640
+ return "claude-code";
1641
+ }
1641
1642
  async function emitSessionStartSystemMessage(coordRoot, instanceId, sessionId, emittedData, adapter, recoveryBriefing = "") {
1642
- const agentCoordBin = coordBinPath("agent-coord", coordRoot) ?? "";
1643
1643
  const workflowChild = coordEnv("WORKFLOW_CHILD") === "1";
1644
1644
  let additionalContext = "";
1645
- if (existsSync(agentCoordBin)) {
1646
- // Sync-project so the heartbeat exists for downstream readers (peer table,
1647
- // wiring check, council invites).
1648
- spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
1649
- // Opportunistic reconciliation makes normal session starts a failsafe for
1650
- // archive, idle, cascade, and host lifecycle observations.
1651
- spawnSync(agentCoordBin, ["reconcile-finalization"], {
1652
- encoding: "utf8",
1653
- timeout: 5000,
1654
- env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot },
1655
- });
1656
- // SESSION_START activity log line, fired across all adapters.
1657
- const model = emittedData.model ?? "unknown";
1658
- const source = emittedData.source ?? "startup";
1659
- const platform = adapterPlatform(adapter);
1660
- spawnSync(agentCoordBin, [
1661
- "log",
1662
- `SESSION_START model=${model} source=${source} platform=${platform}`,
1663
- "--instance",
1645
+ // Opportunistic reconciliation makes normal session starts a failsafe for
1646
+ // archive, idle, cascade, and host lifecycle observations. It also runs the
1647
+ // stale cache sweep through the shared ADR 0077 composition.
1648
+ const { reconcileCoordinationV3 } = await import("../agents/reconcile-coordination-v3.js");
1649
+ reconcileCoordinationV3(coordRoot);
1650
+ // Workflow children retain lifecycle/event capture but do not receive
1651
+ // operator-facing peer, council, or init-remediation context. Injecting
1652
+ // that context can make a bounded child follow housekeeping instructions
1653
+ // instead of its assigned prompt.
1654
+ if (!workflowChild) {
1655
+ const agentName = emittedData.name ?? "";
1656
+ const { renderSessionContext } = await import("../agents/render/session-context.js");
1657
+ additionalContext = renderSessionContext({
1658
+ coordRoot,
1664
1659
  instanceId,
1665
- ], { encoding: "utf8", timeout: 2000 });
1666
- // Workflow children retain lifecycle/event capture but do not receive
1667
- // operator-facing peer, council, or init-remediation context. Injecting
1668
- // that context can make a bounded child follow housekeeping instructions
1669
- // instead of its assigned prompt.
1670
- if (!workflowChild) {
1671
- const agentName = emittedData.name ?? "";
1672
- const args = ["session-context", "--instance", instanceId, "--session", sessionId];
1673
- if (agentName)
1674
- args.push("--name", agentName);
1675
- // The "You are agent-X." prefix in session-context renders unqualified by
1676
- // default (claude-code-style). For cursor/codex the bash dispatchers add
1677
- // a "(Cursor)" / "(Codex)" suffix; pass it through as --platform-label.
1678
- if (adapter !== "claude-code") {
1679
- args.push("--platform-label", platform === "cursor" ? "Cursor" : "Codex");
1680
- }
1681
- const result = spawnSync(agentCoordBin, args, { encoding: "utf8", timeout: 3000 });
1682
- if (result.status === 0 && result.stdout)
1683
- additionalContext = result.stdout.trim();
1684
- }
1660
+ sessionId,
1661
+ agentName: agentName || undefined,
1662
+ platformLabel: adapter === "claude-code" ? undefined : adapter === "cursor" ? "Cursor" : "Codex",
1663
+ }).trim();
1685
1664
  }
1686
1665
  // Effect (claude-code): merge the journal recovery cue into the session-start
1687
1666
  // context. Was a standalone additionalContext emission from the previous
@@ -1722,6 +1701,11 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
1722
1701
  }
1723
1702
  main()
1724
1703
  .then(async (code) => {
1704
+ writeHookHealthCompletion(hookHealthState, {
1705
+ finished_at_ms: performance.now(),
1706
+ finished_rss_bytes: process.memoryUsage().rss,
1707
+ pid: process.pid,
1708
+ });
1725
1709
  await closeProcessLoggers();
1726
1710
  process.exit(code);
1727
1711
  })
@@ -1729,6 +1713,13 @@ main()
1729
1713
  logError(resolveCoordRoot(process.cwd()), err, {
1730
1714
  argv: process.argv.slice(2),
1731
1715
  pid: process.pid,
1716
+ phase: "top-level",
1717
+ });
1718
+ writeHookHealthCompletion(hookHealthState, {
1719
+ finished_at_ms: performance.now(),
1720
+ finished_rss_bytes: process.memoryUsage().rss,
1721
+ pid: process.pid,
1722
+ faulted: true,
1732
1723
  });
1733
1724
  await closeProcessLoggers();
1734
1725
  process.exit(0);