harnery 0.34.1 → 0.35.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 (212) hide show
  1. package/dist/commander.d.ts.map +1 -1
  2. package/dist/commander.js +2 -0
  3. package/dist/commands/agents.d.ts +15 -4
  4. package/dist/commands/agents.d.ts.map +1 -1
  5. package/dist/commands/agents.js +89 -175
  6. package/dist/commands/browse.d.ts.map +1 -1
  7. package/dist/commands/browse.js +26 -2
  8. package/dist/commands/decision.d.ts.map +1 -1
  9. package/dist/commands/decision.js +2 -2
  10. package/dist/commands/events.d.ts.map +1 -1
  11. package/dist/commands/events.js +18 -1
  12. package/dist/commands/presence.d.ts.map +1 -1
  13. package/dist/commands/presence.js +2 -2
  14. package/dist/commands/semantic.d.ts +4 -0
  15. package/dist/commands/semantic.d.ts.map +1 -0
  16. package/dist/commands/semantic.js +147 -0
  17. package/dist/commands/web.d.ts.map +1 -1
  18. package/dist/commands/web.js +17 -0
  19. package/dist/core/agents/canonical-emit.d.ts +3 -0
  20. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  21. package/dist/core/agents/canonical-emit.js +12 -0
  22. package/dist/core/agents/claim-path.d.ts +11 -0
  23. package/dist/core/agents/claim-path.d.ts.map +1 -0
  24. package/dist/core/agents/claim-path.js +22 -0
  25. package/dist/core/agents/coord-client.d.ts +29 -3
  26. package/dist/core/agents/coord-client.d.ts.map +1 -1
  27. package/dist/core/agents/coord-client.js +88 -9
  28. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  29. package/dist/core/agents/live-authority-v3.js +1 -3
  30. package/dist/core/agents/live-lifecycle-v3.js +3 -3
  31. package/dist/core/agents/live-observation-v3.d.ts.map +1 -1
  32. package/dist/core/agents/live-observation-v3.js +2 -2
  33. package/dist/core/agents/render/prompt-context.js +12 -10
  34. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  35. package/dist/core/agents/rules/stop-hook.js +23 -11
  36. package/dist/core/agents/session-name-display.d.ts +1 -0
  37. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  38. package/dist/core/agents/session-name-display.js +8 -1
  39. package/dist/core/agents/state/heartbeat-reader.d.ts +46 -0
  40. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -0
  41. package/dist/core/agents/state/heartbeat-reader.js +25 -0
  42. package/dist/core/agents/state/heartbeat-writer.d.ts +3 -48
  43. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  44. package/dist/core/agents/state/heartbeat-writer.js +16 -34
  45. package/dist/core/agents/state/live-coordination-view.d.ts +1 -1
  46. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  47. package/dist/core/agents/state/live-coordination-view.js +25 -1
  48. package/dist/core/agents/state/names.d.ts +9 -1
  49. package/dist/core/agents/state/names.d.ts.map +1 -1
  50. package/dist/core/agents/state/names.js +190 -182
  51. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  52. package/dist/core/agents/state/stale-sweep.js +23 -5
  53. package/dist/core/artifacts/constants.d.ts +5 -0
  54. package/dist/core/artifacts/constants.d.ts.map +1 -0
  55. package/dist/core/artifacts/constants.js +4 -0
  56. package/dist/core/artifacts/index.d.ts +3 -3
  57. package/dist/core/artifacts/index.d.ts.map +1 -1
  58. package/dist/core/artifacts/index.js +50 -7
  59. package/dist/core/events/v3/base-contract.d.ts +795 -0
  60. package/dist/core/events/v3/base-contract.d.ts.map +1 -1
  61. package/dist/core/events/v3/base-contract.js +33 -0
  62. package/dist/core/events/v3/capabilities.d.ts +11 -0
  63. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  64. package/dist/core/events/v3/capabilities.js +23 -0
  65. package/dist/core/events/v3/generated.d.ts +1 -1
  66. package/dist/core/events/v3/generated.js +1 -1
  67. package/dist/core/events/v3/index.d.ts +4 -3
  68. package/dist/core/events/v3/index.d.ts.map +1 -1
  69. package/dist/core/events/v3/index.js +4 -3
  70. package/dist/core/events/v3/latency.d.ts +31 -1
  71. package/dist/core/events/v3/latency.d.ts.map +1 -1
  72. package/dist/core/events/v3/latency.js +127 -3
  73. package/dist/core/events/v3/live-routing.d.ts +30 -0
  74. package/dist/core/events/v3/live-routing.d.ts.map +1 -1
  75. package/dist/core/events/v3/live-routing.js +109 -1
  76. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  77. package/dist/core/events/v3/producers/command-recorder.js +10 -1
  78. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  79. package/dist/core/events/v3/producers/coordination-recorder.js +14 -3
  80. package/dist/core/events/v3/producers/hook-base.d.ts +17 -1
  81. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  82. package/dist/core/events/v3/producers/hook-base.js +82 -1
  83. package/dist/core/events/v3/producers/intake.d.ts +6 -0
  84. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  85. package/dist/core/events/v3/producers/intake.js +11 -0
  86. package/dist/core/events/v3/producers/recorder.d.ts +62 -0
  87. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  88. package/dist/core/events/v3/producers/recorder.js +654 -18
  89. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +51 -0
  90. package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -0
  91. package/dist/core/events/v3/runtime-telemetry-capabilities.js +54 -0
  92. package/dist/core/events/v3/turn-telemetry.d.ts +9 -0
  93. package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
  94. package/dist/core/hooks/adapter/parse.d.ts +3 -0
  95. package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
  96. package/dist/core/hooks/adapter/parse.js +14 -0
  97. package/dist/core/hooks/adapter/runtime-telemetry.d.ts +119 -0
  98. package/dist/core/hooks/adapter/runtime-telemetry.d.ts.map +1 -0
  99. package/dist/core/hooks/adapter/runtime-telemetry.js +568 -0
  100. package/dist/core/hooks/cli.js +111 -36
  101. package/dist/core/hooks/remediation-cap.d.ts +34 -0
  102. package/dist/core/hooks/remediation-cap.d.ts.map +1 -0
  103. package/dist/core/hooks/remediation-cap.js +71 -0
  104. package/dist/core/hooks/resolve/transcript.d.ts +25 -9
  105. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  106. package/dist/core/hooks/resolve/transcript.js +22 -10
  107. package/dist/core/semantic/adapters.d.ts +34 -0
  108. package/dist/core/semantic/adapters.d.ts.map +1 -0
  109. package/dist/core/semantic/adapters.js +222 -0
  110. package/dist/core/semantic/contract.d.ts +588 -0
  111. package/dist/core/semantic/contract.d.ts.map +1 -0
  112. package/dist/core/semantic/contract.js +230 -0
  113. package/dist/core/semantic/evidence.d.ts +11 -0
  114. package/dist/core/semantic/evidence.d.ts.map +1 -0
  115. package/dist/core/semantic/evidence.js +262 -0
  116. package/dist/core/semantic/index.d.ts +10 -0
  117. package/dist/core/semantic/index.d.ts.map +1 -0
  118. package/dist/core/semantic/index.js +9 -0
  119. package/dist/core/semantic/once.d.ts +33 -0
  120. package/dist/core/semantic/once.d.ts.map +1 -0
  121. package/dist/core/semantic/once.js +293 -0
  122. package/dist/core/semantic/prompt.d.ts +12 -0
  123. package/dist/core/semantic/prompt.d.ts.map +1 -0
  124. package/dist/core/semantic/prompt.js +70 -0
  125. package/dist/core/semantic/scheduler.d.ts +16 -0
  126. package/dist/core/semantic/scheduler.d.ts.map +1 -0
  127. package/dist/core/semantic/scheduler.js +67 -0
  128. package/dist/core/semantic/service-status.d.ts +31 -0
  129. package/dist/core/semantic/service-status.d.ts.map +1 -0
  130. package/dist/core/semantic/service-status.js +84 -0
  131. package/dist/core/semantic/service.d.ts +51 -0
  132. package/dist/core/semantic/service.d.ts.map +1 -0
  133. package/dist/core/semantic/service.js +470 -0
  134. package/dist/core/semantic/storage.d.ts +71 -0
  135. package/dist/core/semantic/storage.d.ts.map +1 -0
  136. package/dist/core/semantic/storage.js +179 -0
  137. package/dist/core/semantic/validate.d.ts +16 -0
  138. package/dist/core/semantic/validate.d.ts.map +1 -0
  139. package/dist/core/semantic/validate.js +174 -0
  140. package/dist/lib/browser/client.d.ts +11 -0
  141. package/dist/lib/browser/client.d.ts.map +1 -1
  142. package/dist/lib/browser/client.js +13 -0
  143. package/dist/lib/browser/index.d.ts +1 -0
  144. package/dist/lib/browser/index.d.ts.map +1 -1
  145. package/dist/lib/browser/standalone-html.d.ts +25 -0
  146. package/dist/lib/browser/standalone-html.d.ts.map +1 -0
  147. package/dist/lib/browser/standalone-html.js +284 -0
  148. package/dist/lib/instructions/apply.d.ts.map +1 -1
  149. package/dist/lib/instructions/apply.js +1 -0
  150. package/dist/lib/instructions/templates.d.ts +2 -0
  151. package/dist/lib/instructions/templates.d.ts.map +1 -1
  152. package/dist/lib/instructions/templates.js +144 -4
  153. package/package.json +6 -1
  154. package/schemas/event-v3.schema.json +2758 -478
  155. package/src/commander.ts +2 -0
  156. package/src/commands/agents.ts +102 -167
  157. package/src/commands/browse.ts +25 -2
  158. package/src/commands/decision.ts +8 -2
  159. package/src/commands/events.ts +21 -1
  160. package/src/commands/presence.ts +7 -2
  161. package/src/commands/semantic.ts +179 -0
  162. package/src/commands/web.ts +15 -0
  163. package/src/core/agents/canonical-emit.ts +15 -0
  164. package/src/core/agents/claim-path.ts +23 -0
  165. package/src/core/agents/coord-client.ts +92 -8
  166. package/src/core/agents/live-authority-v3.ts +1 -4
  167. package/src/core/agents/live-lifecycle-v3.ts +8 -3
  168. package/src/core/agents/live-observation-v3.ts +7 -2
  169. package/src/core/agents/render/prompt-context.ts +13 -10
  170. package/src/core/agents/rules/stop-hook.ts +24 -11
  171. package/src/core/agents/session-name-display.ts +9 -1
  172. package/src/core/agents/state/heartbeat-reader.ts +69 -0
  173. package/src/core/agents/state/heartbeat-writer.ts +20 -84
  174. package/src/core/agents/state/live-coordination-view.ts +26 -1
  175. package/src/core/agents/state/names.ts +190 -182
  176. package/src/core/agents/state/stale-sweep.ts +25 -5
  177. package/src/core/artifacts/constants.ts +4 -0
  178. package/src/core/artifacts/index.ts +49 -7
  179. package/src/core/events/v3/base-contract.ts +39 -0
  180. package/src/core/events/v3/capabilities.ts +34 -0
  181. package/src/core/events/v3/generated.ts +1 -1
  182. package/src/core/events/v3/index.ts +21 -0
  183. package/src/core/events/v3/latency.ts +213 -2
  184. package/src/core/events/v3/live-routing.ts +120 -1
  185. package/src/core/events/v3/producers/command-recorder.ts +10 -1
  186. package/src/core/events/v3/producers/coordination-recorder.ts +17 -5
  187. package/src/core/events/v3/producers/hook-base.ts +115 -1
  188. package/src/core/events/v3/producers/intake.ts +18 -0
  189. package/src/core/events/v3/producers/recorder.ts +856 -21
  190. package/src/core/events/v3/runtime-telemetry-capabilities.ts +137 -0
  191. package/src/core/events/v3/turn-telemetry.ts +10 -0
  192. package/src/core/hooks/adapter/parse.ts +16 -0
  193. package/src/core/hooks/adapter/runtime-telemetry.ts +805 -0
  194. package/src/core/hooks/cli.ts +123 -39
  195. package/src/core/hooks/remediation-cap.ts +77 -0
  196. package/src/core/hooks/resolve/transcript.ts +37 -11
  197. package/src/core/semantic/adapters.ts +293 -0
  198. package/src/core/semantic/contract.ts +298 -0
  199. package/src/core/semantic/evidence.ts +336 -0
  200. package/src/core/semantic/index.ts +127 -0
  201. package/src/core/semantic/once.ts +410 -0
  202. package/src/core/semantic/prompt.ts +84 -0
  203. package/src/core/semantic/scheduler.ts +107 -0
  204. package/src/core/semantic/service-status.ts +118 -0
  205. package/src/core/semantic/service.ts +580 -0
  206. package/src/core/semantic/storage.ts +286 -0
  207. package/src/core/semantic/validate.ts +212 -0
  208. package/src/lib/browser/client.ts +21 -0
  209. package/src/lib/browser/index.ts +4 -0
  210. package/src/lib/browser/standalone-html.ts +312 -0
  211. package/src/lib/instructions/apply.ts +1 -0
  212. package/src/lib/instructions/templates.ts +149 -4
@@ -78,12 +78,14 @@ export function evaluateStopHookV3Events(coordRoot, req, events) {
78
78
  const taskSet = inTurn.some((event) => event.event_type === "coord.task_changed" && event.payload.subject_instance_id === owner);
79
79
  const turnTerminals = inTurn.filter((event) => event.event_type === "turn.completed");
80
80
  const latestTerminal = turnTerminals.at(-1);
81
- const latestBox = observedBoolean(latestTerminal?.payload.ritual?.status_box_present);
81
+ const latestBox = observedBoolean(latestTerminal?.payload.ritual?.status_box_present_strict);
82
82
  if (req.adapter !== "cursor" && latestBox === undefined) {
83
- return evidenceUnavailable("turn.completed has no observed V3 status-box result; Stop remains fail-open");
83
+ return evidenceUnavailable("turn.completed has no observed V3 assistant-text status-box result; Stop remains fail-open");
84
84
  }
85
85
  const ackPresent = req.adapter === "cursor" ? requiredStatusChecked : latestBox === true;
86
- const ackBlock = req.adapter === "cursor" ? () => rule13Block(coordRoot) : () => rule23Block(coordRoot);
86
+ const ackBlock = req.adapter === "cursor"
87
+ ? () => rule13Block(coordRoot, req.session_id)
88
+ : () => rule23Block(coordRoot, req.session_id);
87
89
  // Pure prose turns keep the human-visible acknowledgement but do not require
88
90
  // task or final-status mutations. This preserves the pre-V3 policy exactly.
89
91
  if (!usedTool) {
@@ -97,11 +99,11 @@ export function evaluateStopHookV3Events(coordRoot, req, events) {
97
99
  };
98
100
  }
99
101
  if (!requiredStatusChecked)
100
- return rule13Block(coordRoot);
102
+ return rule13Block(coordRoot, req.session_id);
101
103
  if (!ackPresent)
102
104
  return ackBlock();
103
105
  if (!taskSet)
104
- return rule33Block(coordRoot);
106
+ return rule33Block(coordRoot, req.session_id);
105
107
  // Claude Code can prove session-name presentation from assistant-only text.
106
108
  // Cursor cannot expose that text and Codex returned observe-only above.
107
109
  if (req.adapter !== "cursor") {
@@ -192,7 +194,17 @@ function evidenceUnavailable(reason) {
192
194
  reason,
193
195
  };
194
196
  }
195
- function rule13Block(coordRoot) {
197
+ /**
198
+ * Remediation commands carry `--session-id` when the session id is known: the
199
+ * remediating tool call may not descend from a pid-map-registered anchor
200
+ * (headless children, Cursor shells), and a ppid-walk that resolves to a
201
+ * DIFFERENT owner records evidence this verdict will never see — the block
202
+ * then repeats forever while the agent complies every time.
203
+ */
204
+ function sessionIdSuffix(sessionId) {
205
+ return sessionId ? ` --session-id ${sessionId}` : "";
206
+ }
207
+ function rule13Block(coordRoot, sessionId) {
196
208
  const missingEvidence = agentsRequireGitFinalization(coordRoot)
197
209
  ? "no end_turn_checked coord.status_observed event found in this turn"
198
210
  : "no coord.status_observed event found in this turn";
@@ -200,15 +212,15 @@ function rule13Block(coordRoot) {
200
212
  allow: false,
201
213
  exit_code: 2,
202
214
  rule: "stop-hook.rule_1_3",
203
- reason: `End-of-turn rule (1/3): ${missingEvidence}; run \`${endOfTurnStatusCommand(coordRoot)}\` as your last tool call.`,
215
+ reason: `End-of-turn rule (1/3): ${missingEvidence}; run \`${endOfTurnStatusCommand(coordRoot)}${sessionIdSuffix(sessionId)}\` as your last tool call.`,
204
216
  };
205
217
  }
206
- function rule23Block(coordRoot) {
218
+ function rule23Block(coordRoot, sessionId) {
207
219
  return {
208
220
  allow: false,
209
221
  exit_code: 2,
210
222
  rule: "stop-hook.rule_2_3",
211
- reason: `End-of-turn rule (2/3): turn.completed did not observe the agent-status box in your reply text. Paste the \`${endOfTurnStatusCommand(coordRoot)}\` output verbatim as a fenced code block (the \`┌─ agent-\` prefix is the detection signal).`,
223
+ reason: `End-of-turn rule (2/3): turn.completed did not observe the agent-status box in your reply text. Paste the \`${endOfTurnStatusCommand(coordRoot)}${sessionIdSuffix(sessionId)}\` output verbatim as a fenced code block (the \`┌─ agent-\` prefix is the detection signal).`,
212
224
  };
213
225
  }
214
226
  /**
@@ -252,11 +264,11 @@ function sessionNameBlock(coordRoot, instanceId) {
252
264
  reason: `Session-naming rule: V3 observed that the current session name was required but absent. ${instruction}`,
253
265
  };
254
266
  }
255
- function rule33Block(coordRoot) {
267
+ function rule33Block(coordRoot, sessionId) {
256
268
  return {
257
269
  allow: false,
258
270
  exit_code: 2,
259
271
  rule: "stop-hook.rule_3_3",
260
- reason: `End-of-turn rule (3/3): no coord.task_changed event found in this turn; run \`${resolveBinName(coordRoot)} agents set-task "<short focus>"\` to declare what you're working on. Pass an empty string if the turn was purely conversational.`,
272
+ reason: `End-of-turn rule (3/3): no coord.task_changed event found in this turn; run \`${resolveBinName(coordRoot)} agents set-task${sessionIdSuffix(sessionId)} "<short focus>"\` to declare what you're working on. Pass an empty string if the turn was purely conversational.`,
261
273
  };
262
274
  }
@@ -12,6 +12,7 @@ export declare const SESSION_NAME_DISPLAY_NOTE = "Next, before any prose or anot
12
12
  export declare function sessionNameDisplayPending(row: SessionNameDisplayState | null | undefined): string | null;
13
13
  export declare function sessionNameDisplayBlock(name: string): string;
14
14
  export declare function sessionNameDisplayInstruction(name: string): string;
15
+ export declare function sessionNameDisplayRecoveryInstruction(name: string, binName: string): string;
15
16
  /**
16
17
  * A PostToolUse result should announce the display protocol only when that
17
18
  * exact tool call minted or retried the current suggestion. The coordination row keeps
@@ -1 +1 @@
1
- {"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,yBAAyB,wHACiF,CAAC;AAExH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtF;AAgDD;;;;GAIG;AACH,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAK3F;AAUD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAmBT"}
1
+ {"version":3,"file":"session-name-display.d.ts","sourceRoot":"","sources":["../../../src/core/agents/session-name-display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,uBAAuB;IACtC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,yBAAyB,wHACiF,CAAC;AAExH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlE;AAED,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtF;AAgDD;;;;GAIG;AACH,wBAAgB,uCAAuC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAK3F;AAUD;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAmBT"}
@@ -24,6 +24,13 @@ export function sessionNameDisplayInstruction(name) {
24
24
  "Do not put commentary before the block. Harnery will reject tool calls until it is shown.",
25
25
  ].join("\n");
26
26
  }
27
+ export function sessionNameDisplayRecoveryInstruction(name, binName) {
28
+ return [
29
+ sessionNameDisplayInstruction(name),
30
+ "",
31
+ `If that exact block was already shown but the gate still rejects tools, run \`${binName} agents suggest-name --json\` as the only tool call. That read-only command is exempt and creates a fresh verification boundary.`,
32
+ ].join("\n");
33
+ }
27
34
  /**
28
35
  * A PostToolUse result should announce the display protocol only when that
29
36
  * exact tool call minted or retried the current suggestion. The coordination row keeps
@@ -114,6 +121,6 @@ export function isSessionNameRemediationCommand(command, binName) {
114
121
  if (!body || /[\n;&|<>`$]/.test(body))
115
122
  return false;
116
123
  const escapedBin = binName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
117
- const pattern = new RegExp(`^(?:codex-wsl\\s+--\\s+)?${escapedBin}\\s+agents\\s+(?:set-task|status)(?:\\s+.*)?$`);
124
+ const pattern = new RegExp(`^(?:codex-wsl\\s+--\\s+)?${escapedBin}\\s+agents\\s+(?:set-task|status|suggest-name)(?:\\s+.*)?$`);
118
125
  return pattern.test(body);
119
126
  }
@@ -0,0 +1,46 @@
1
+ /** Read-only access to the generation-bound heartbeat cache. */
2
+ import type { AgentActivity, TaskState } from "./session-state.js";
3
+ export interface Heartbeat {
4
+ schema_version?: number;
5
+ instance_id: string;
6
+ name?: string;
7
+ kind?: string;
8
+ agent_id?: string;
9
+ session_id: string;
10
+ /** Native adapter session ID retained only in a generation-bound local projection. */
11
+ native_session_id?: string;
12
+ subagent_call_id?: string;
13
+ model?: string;
14
+ platform?: string;
15
+ started_at?: string;
16
+ last_heartbeat: string;
17
+ files_touched: string[];
18
+ task?: string;
19
+ task_updated_at?: string | null;
20
+ activity?: AgentActivity;
21
+ activity_updated_at?: string;
22
+ activity_source?: string;
23
+ task_state?: TaskState;
24
+ task_state_updated_at?: string;
25
+ task_state_reason?: string;
26
+ /** Session name built on the first non-empty set-task (never rebuilt). */
27
+ suggested_session_name?: string;
28
+ /** Stamped once the suggested name is seen in assistant reply text. */
29
+ session_name_seen_at?: string;
30
+ /** The specific suggested name proven present by the sighting stamp. */
31
+ session_name_seen_for?: string;
32
+ last_status_at?: string;
33
+ current_turn_id?: string;
34
+ parent_instance_id?: string;
35
+ workflow_run_id?: string;
36
+ workflow_agent_id?: string;
37
+ /** Disposable-cache bindings that prove a row belongs to the current V3 generation. */
38
+ v3_instance_id?: `inst_${string}`;
39
+ v3_generation_id?: `gen_${string}`;
40
+ v3_projection_event_id?: string;
41
+ v3_task_state?: "set" | "cleared";
42
+ [extra: string]: unknown;
43
+ }
44
+ export declare function heartbeatPath(coordRoot: string, instanceId: string): string;
45
+ export declare function readHeartbeat(coordRoot: string, instanceId: string): Heartbeat | null;
46
+ //# sourceMappingURL=heartbeat-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat-reader.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-reader.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAIhE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEnE,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uFAAuF;IACvF,cAAc,CAAC,EAAE,QAAQ,MAAM,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,MAAM,EAAE,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,aAAa,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAClC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAU3E;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQrF"}
@@ -0,0 +1,25 @@
1
+ /** Read-only access to the generation-bound heartbeat cache. */
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { dirname, resolve, sep } from "node:path";
4
+ export function heartbeatPath(coordRoot, instanceId) {
5
+ if (!/^[A-Za-z0-9_-]{1,128}$/.test(instanceId)) {
6
+ throw new Error("instance_id must be 1-128 ASCII letters, digits, hyphens, or underscores");
7
+ }
8
+ const root = resolve(coordRoot, ".harnery", "active");
9
+ const candidate = resolve(root, `${instanceId}.json`);
10
+ if (!candidate.startsWith(`${root}${sep}`) || dirname(candidate) !== root) {
11
+ throw new Error("coordination filename must resolve directly beneath its root");
12
+ }
13
+ return candidate;
14
+ }
15
+ export function readHeartbeat(coordRoot, instanceId) {
16
+ const path = heartbeatPath(coordRoot, instanceId);
17
+ if (!existsSync(path))
18
+ return null;
19
+ try {
20
+ return JSON.parse(readFileSync(path, "utf8"));
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
@@ -13,54 +13,9 @@
13
13
  * `.harnery/active/<instance_id>.json`; the canonical V3 ledger remains authoritative.
14
14
  * (the file IS the heartbeat). No mutations happen elsewhere.
15
15
  */
16
- import type { AgentActivity, TaskState } from "./session-state.js";
17
- export interface Heartbeat {
18
- schema_version?: number;
19
- instance_id: string;
20
- name?: string;
21
- kind?: string;
22
- agent_id?: string;
23
- session_id: string;
24
- /** Native adapter session ID retained only in a generation-bound local projection. */
25
- native_session_id?: string;
26
- subagent_call_id?: string;
27
- model?: string;
28
- platform?: string;
29
- started_at?: string;
30
- last_heartbeat: string;
31
- files_touched: string[];
32
- task?: string;
33
- task_updated_at?: string | null;
34
- activity?: AgentActivity;
35
- activity_updated_at?: string;
36
- activity_source?: string;
37
- task_state?: TaskState;
38
- task_state_updated_at?: string;
39
- task_state_reason?: string;
40
- /** Session name built on the first non-empty set-task (never rebuilt). Its
41
- * presence is the "this session has been named" signal the prompt-context
42
- * nudge and the Stop-hook naming rule key on. */
43
- suggested_session_name?: string;
44
- /** Stamped by turn.completed once the suggested name is seen in assistant reply
45
- * text, ending the per-turn transcript scan. */
46
- session_name_seen_at?: string;
47
- /** WHICH name that sighting was for. The scan is skipped only while this
48
- * matches the current suggested name, so a re-minted name is detectable
49
- * again rather than being suppressed by the earlier sighting. */
50
- session_name_seen_for?: string;
51
- last_status_at?: string;
52
- current_turn_id?: string;
53
- parent_instance_id?: string;
54
- workflow_run_id?: string;
55
- workflow_agent_id?: string;
56
- /** Disposable-cache bindings that prove a row belongs to the current V3 generation. */
57
- v3_instance_id?: `inst_${string}`;
58
- v3_generation_id?: `gen_${string}`;
59
- v3_projection_event_id?: string;
60
- v3_task_state?: "set" | "cleared";
61
- [extra: string]: unknown;
62
- }
63
- export declare function readHeartbeat(coordRoot: string, instanceId: string): Heartbeat | null;
16
+ import { type Heartbeat } from "./heartbeat-reader.js";
17
+ import type { TaskState } from "./session-state.js";
18
+ export { type Heartbeat, readHeartbeat } from "./heartbeat-reader.js";
64
19
  /** Persist operator-facing lifecycle prose in the generation-bound cache only. */
65
20
  export declare function setLifecycleCache(coordRoot: string, instanceId: string, state: TaskState, reason?: string, suggestedSessionName?: string): Heartbeat | null;
66
21
  export declare function setTask(coordRoot: string, instanceId: string, task: string): Heartbeat | null;
@@ -1 +1 @@
1
- {"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/heartbeat-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,OAAO,EAAE,KAAK,SAAS,EAAgC,MAAM,uBAAuB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,KAAK,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAatE,kFAAkF;AAClF,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,oBAAoB,CAAC,EAAE,MAAM,GAC5B,SAAS,GAAG,IAAI,CAQlB;AAeD,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAoB7F;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,SAAS,GAAG,IAAI,CAOlB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EAAE,GACzB;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKvD;AAED,2EAA2E;AAC3E,wBAAgB,2BAA2B,CACzC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,KAAK,EAAE,SAAS,GACf,MAAM,GAAG,IAAI,CAOf;AAED;;;;4EAI4E;AAC5E,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS,GAAG,IAAI,CAMlB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,IAAI,CAQlB;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,SAAS,GAAG,IAAI,CAWlB;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,eAAe,EAAE,CA6BnB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CASrF;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAiBnF"}
@@ -14,16 +14,10 @@
14
14
  * (the file IS the heartbeat). No mutations happen elsewhere.
15
15
  */
16
16
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
17
- import { basename, dirname, join } from "node:path";
18
- import { assertSafeInstanceId, resolveContainedFile } from "../coord-client.js";
19
- function heartbeatPath(coordRoot, instanceId) {
20
- assertSafeInstanceId(instanceId);
21
- const safeInstanceId = basename(instanceId);
22
- if (safeInstanceId !== instanceId) {
23
- throw new Error("instance_id must be a direct-child coordination basename");
24
- }
25
- return resolveContainedFile(join(coordRoot, ".harnery", "active"), `${safeInstanceId}.json`);
26
- }
17
+ import { dirname, join } from "node:path";
18
+ import { canonicalClaimPath } from "../claim-path.js";
19
+ import { heartbeatPath, readHeartbeat } from "./heartbeat-reader.js";
20
+ export { readHeartbeat } from "./heartbeat-reader.js";
27
21
  function nowIsoSeconds() {
28
22
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
29
23
  }
@@ -33,17 +27,6 @@ function atomicWrite(path, content) {
33
27
  writeFileSync(tmp, content, "utf8");
34
28
  renameSync(tmp, path);
35
29
  }
36
- export function readHeartbeat(coordRoot, instanceId) {
37
- const path = heartbeatPath(coordRoot, instanceId);
38
- if (!existsSync(path))
39
- return null;
40
- try {
41
- return JSON.parse(readFileSync(path, "utf8"));
42
- }
43
- catch {
44
- return null;
45
- }
46
- }
47
30
  /** Persist operator-facing lifecycle prose in the generation-bound cache only. */
48
31
  export function setLifecycleCache(coordRoot, instanceId, state, reason, suggestedSessionName) {
49
32
  return mutate(coordRoot, instanceId, (heartbeat) => ({
@@ -128,24 +111,23 @@ export function stampSessionNameSeen(coordRoot, instanceId, name) {
128
111
  }));
129
112
  }
130
113
  export function releaseClaim(coordRoot, instanceId, path) {
131
- // files_touched can hold either absolute-under-coordRoot or canonical
132
- // monorepo-relative entries; normalize both sides so release matches
133
- // regardless of the form the caller passes (the old exact-string filter
134
- // silently no-op'd on a form mismatch).
135
- const norm = (p) => p.startsWith(`${coordRoot}/`) ? p.slice(coordRoot.length + 1) : p;
136
- const target = norm(path);
114
+ const target = canonicalClaimPath(coordRoot, path);
137
115
  return mutate(coordRoot, instanceId, (hb) => ({
138
116
  ...hb,
139
- files_touched: (hb.files_touched ?? []).filter((p) => norm(p) !== target),
117
+ files_touched: (hb.files_touched ?? []).filter((candidate) => canonicalClaimPath(coordRoot, candidate) !== target),
140
118
  }));
141
119
  }
142
120
  /** Materialize one V3 write-claim authority transition into the heartbeat cache. */
143
121
  export function acquireClaim(coordRoot, instanceId, path) {
144
- const norm = (p) => p.startsWith(`${coordRoot}/`) ? p.slice(coordRoot.length + 1) : p;
145
- const target = norm(path);
122
+ const target = canonicalClaimPath(coordRoot, path);
146
123
  return mutate(coordRoot, instanceId, (hb) => ({
147
124
  ...hb,
148
- files_touched: [...new Set([...(hb.files_touched ?? []).map(norm), target])].sort(),
125
+ files_touched: [
126
+ ...new Set([
127
+ ...(hb.files_touched ?? []).map((candidate) => canonicalClaimPath(coordRoot, candidate)),
128
+ target,
129
+ ]),
130
+ ].sort(),
149
131
  }));
150
132
  }
151
133
  /**
@@ -172,8 +154,7 @@ export function findGroupClaims(coordRoot, groupId, path) {
172
154
  const activeDir = join(coordRoot, ".harnery", "active");
173
155
  if (!existsSync(activeDir))
174
156
  return hits;
175
- const norm = (p) => p.startsWith(`${coordRoot}/`) ? p.slice(coordRoot.length + 1) : p;
176
- const target = norm(path);
157
+ const target = canonicalClaimPath(coordRoot, path);
177
158
  for (const f of readdirSync(activeDir)) {
178
159
  if (!f.endsWith(".json"))
179
160
  continue;
@@ -189,8 +170,9 @@ export function findGroupClaims(coordRoot, groupId, path) {
189
170
  if (peerSession !== groupId)
190
171
  continue;
191
172
  const files = body.files_touched ?? [];
192
- if (!files.some((candidate) => norm(candidate) === target))
173
+ if (!files.some((candidate) => canonicalClaimPath(coordRoot, candidate) === target)) {
193
174
  continue;
175
+ }
194
176
  hits.push({
195
177
  instance_id: body.instance_id ?? f.replace(/\.json$/, ""),
196
178
  session_id: body.session_id,
@@ -1,5 +1,5 @@
1
1
  import type { Adapter } from "../../adapter.js";
2
- import { type Heartbeat } from "./heartbeat-writer.js";
2
+ import { type Heartbeat } from "./heartbeat-reader.js";
3
3
  /**
4
4
  * Heartbeat files are disposable V3 caches. These bindings prevent a stale
5
5
  * row from a prior generation from being mistaken for current authority.
@@ -1 +1 @@
1
- {"version":3,"file":"live-coordination-view.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/live-coordination-view.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAYhD,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAGtE;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,SAAS;IAC3D,cAAc,EAAE,CAAC,CAAC;IAClB,cAAc,EAAE,QAAQ,MAAM,EAAE,CAAC;IACjC,gBAAgB,EAAE,OAAO,MAAM,EAAE,CAAC;IAClC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,KAAK,GAAG,SAAS,CAAC;CAClC;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,0BAA0B,EAAE,CA2BxF;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,GACvB,0BAA0B,GAAG,IAAI,CAuBnC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,GACvB,OAAO,GAAG,IAAI,CAWhB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CAgClB"}
1
+ {"version":3,"file":"live-coordination-view.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/live-coordination-view.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAYhD,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,uBAAuB,CAAC;AAGtE;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,SAAS;IAC3D,cAAc,EAAE,CAAC,CAAC;IAClB,cAAc,EAAE,QAAQ,MAAM,EAAE,CAAC;IACjC,gBAAgB,EAAE,OAAO,MAAM,EAAE,CAAC;IAClC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,KAAK,GAAG,SAAS,CAAC;CAClC;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,0BAA0B,EAAE,CA2BxF;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,GACvB,0BAA0B,GAAG,IAAI,CAuBnC;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,GACvB,OAAO,GAAG,IAAI,CAWhB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,OAAO,EACjB,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,GAAG,IAAI,CAyDlB"}
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFile
2
2
  import { dirname, join } from "node:path";
3
3
  import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../../events/v3/coordination-view.js";
4
4
  import { liveInstanceIdV3, nativeInstanceIdV3, resolveLiveEventLedgerRouteV3, } from "../../events/v3/live-routing.js";
5
- import { readHeartbeat } from "./heartbeat-writer.js";
5
+ import { readHeartbeat } from "./heartbeat-reader.js";
6
6
  import { resolveName } from "./names.js";
7
7
  /** Read the live coordination rows through the hard-cut ledger route. */
8
8
  export function readLiveCoordinationRows(coordRoot) {
@@ -89,8 +89,32 @@ export function ensureLiveCoordinationHeartbeat(coordRoot, nativeInstanceId, nat
89
89
  return current;
90
90
  const resolved = resolveName(coordRoot, nativeInstanceId, nativeSessionId);
91
91
  const projected = projectHeartbeatV3(coordRoot, generation, undefined, nativeInstanceId);
92
+ // A generation reopen (resume, mid-flight onboarding) discards the stale
93
+ // cache, but the adapter tab it names is unchanged: same native session,
94
+ // same agent identity. Rebuilding without the naming evidence re-mints a
95
+ // fresh suggested name on the next set-task, so a multi-day session
96
+ // accumulates titles it is asked to display over and over. Carry the
97
+ // naming evidence across generations; everything else re-projects from V3.
98
+ // Same native session id means the same adapter tab; drop the carry only
99
+ // when the durable identity observably changed (a reassumed name would make
100
+ // the old "Agent <name> - …" block wrong for the new owner).
101
+ const identityUnchanged = !resolved?.name || !current?.name || resolved.name === current.name;
102
+ const carriedNaming = current && current.session_id === nativeSessionId && identityUnchanged
103
+ ? {
104
+ ...(current.suggested_session_name
105
+ ? { suggested_session_name: current.suggested_session_name }
106
+ : {}),
107
+ ...(current.session_name_seen_for
108
+ ? { session_name_seen_for: current.session_name_seen_for }
109
+ : {}),
110
+ ...(current.session_name_seen_at
111
+ ? { session_name_seen_at: current.session_name_seen_at }
112
+ : {}),
113
+ }
114
+ : {};
92
115
  const materialized = {
93
116
  ...projected,
117
+ ...carriedNaming,
94
118
  schema_version: 2,
95
119
  instance_id: nativeInstanceId,
96
120
  session_id: nativeSessionId,
@@ -5,6 +5,14 @@
5
5
  * Layout invariant: 260 entries (10 per starting letter A..Z). Counter N
6
6
  * picks COORD_NAMES[N % 260]; wraps to A at N=260.
7
7
  *
8
+ * Gender invariant: within each 26-name pass the names alternate female/male
9
+ * letter by letter, so every pass is 13 female and 13 male. Five passes start
10
+ * female at A (1, 3, 5, 7, 9) and five start male (2, 4, 6, 8, 10), which gives
11
+ * each letter exactly five names of each gender and the pool 130/130 overall.
12
+ * This exists so a fixed set of 13 female and 13 male portraits can dress one
13
+ * whole pass. Adding or reordering a name must preserve it; the expected
14
+ * pattern is pinned in tests/unit/names-pool.test.ts.
15
+ *
8
16
  * Durable persistence: `.harnery/.name-history` (JSONL, one row per assignment)
9
17
  * + `.harnery/.name-counter` (current counter, atomic temp+rename).
10
18
  *
@@ -19,7 +27,7 @@
19
27
  * 3. Else: new assignment, consume a counter slot.
20
28
  */
21
29
  /** All 260 names. */
22
- export declare const COORD_NAMES: readonly ["Anna", "Bob", "Carlos", "Dalia", "Erika", "Fred", "Gemma", "Hank", "Imelda", "Jerry", "Kirk", "Lee", "Milton", "Nate", "Oscar", "Patty", "Quentin", "Rafael", "Sara", "Tony", "Ulrik", "Valerie", "Whitney", "Xander", "Yara", "Zara", "Adelaide", "Bertha", "Carmen", "Dorian", "Edna", "Francine", "Greta", "Helene", "Ines", "Joyce", "Karen", "Lorenzo", "Mindy", "Nadine", "Odette", "Paulette", "Quinn", "Rebekah", "Sean", "Tammy", "Ursula", "Vinson", "Wallace", "Xanthe", "Yusuf", "Zoe", "Alex", "Beatrice", "Cyrus", "Delia", "Ernesto", "Florence", "Gordon", "Hester", "Isaias", "Jenna", "Klaus", "Larry", "Maxine", "Nicholas", "Otto", "Peter", "Quill", "Renee", "Stella", "Theresa", "Umberto", "Virgil", "Willow", "Xavier", "Yolanda", "Zane", "Anita", "Bruno", "Cora", "Damon", "Edith", "Felix", "Gloria", "Holly", "Iris", "Jasper", "Kaia", "Linda", "Marco", "Nora", "Olive", "Petra", "Querida", "Roman", "Sebastian", "Tobias", "Una", "Vera", "Walter", "Xena", "Yusra", "Zelda", "Aaron", "Bonnie", "Caleb", "Daphne", "Elias", "Fiona", "Galileo", "Hugo", "Ian", "Jude", "Knox", "Leo", "Maya", "Nash", "Owen", "Paxton", "Quetzal", "Rosa", "Sage", "Talia", "Uri", "Vincent", "Wesley", "Ximena", "Yvette", "Zephyr", "Astrid", "Beau", "Celeste", "Dexter", "Ezra", "Fitz", "Genevieve", "Hadley", "Imogen", "Joaquin", "Kira", "Lila", "Mason", "Nila", "Olga", "Pearl", "Quincy", "Royce", "Sterling", "Trent", "Ulysses", "Violet", "Wynne", "Xerxes", "Yael", "Zinnia", "Amelia", "Bianca", "Cody", "Dahlia", "Esme", "Fern", "Greer", "Harriet", "Ivan", "Juno", "Kendrick", "Luther", "Margot", "Nigel", "Orion", "Phillip", "Quenby", "Reagan", "Sienna", "Tessa", "Uma", "Vesper", "Watson", "Xiomara", "Yuri", "Zoltan", "Andre", "Boris", "Cassidy", "Davis", "Evander", "Forrest", "Gibson", "Henry", "Irene", "Justus", "Kasper", "Lainey", "Miles", "Naomi", "Ophelia", "Phoebe", "Quark", "Rylie", "Saul", "Truman", "Ulrich", "Voss", "Winifred", "Xuxa", "Yvonne", "Zia", "Aria", "Brenda", "Crispin", "Dolores", "Estelle", "Felicity", "Goldie", "Hazel", "Ira", "Jovi", "Kestrel", "Lyric", "Mavis", "Nico", "Otis", "Percy", "Querubin", "Rhett", "Scout", "Tatum", "Unity", "Vivian", "Wyatt", "Xan", "Yancy", "Zev", "Atticus", "Barnaby", "Calliope", "Drake", "Eustace", "Foster", "Granger", "Hollis", "Indira", "Jericho", "Kaleb", "Logan", "Magnus", "Noor", "Oakley", "Posy", "Quito", "Rosalind", "Sloane", "Theron", "Upton", "Vance", "Wendell", "Xola", "Yann", "Zora"];
30
+ export declare const COORD_NAMES: readonly ["Anna", "Bob", "Carmen", "Dorian", "Erika", "Fred", "Gemma", "Hank", "Imelda", "Jerry", "Karen", "Lorenzo", "Mindy", "Nate", "Odette", "Peter", "Quinn", "Rafael", "Sara", "Tony", "Ursula", "Vinson", "Whitney", "Xander", "Yara", "Zane", "Alex", "Bertha", "Carlos", "Dalia", "Ernesto", "Francine", "Gordon", "Helene", "Isaias", "Joyce", "Kirk", "Lucia", "Milton", "Nadine", "Oscar", "Patty", "Quentin", "Rebekah", "Sean", "Tammy", "Ulrik", "Valerie", "Wallace", "Xanthe", "Yusuf", "Zara", "Adelaide", "Bruno", "Cora", "Damon", "Edna", "Felix", "Greta", "Hugo", "Ines", "Jasper", "Kaia", "Larry", "Maxine", "Nicholas", "Olive", "Paxton", "Querida", "Roman", "Stella", "Tobias", "Una", "Virgil", "Willow", "Xavier", "Yolanda", "Zephyr", "Aaron", "Beatrice", "Cyrus", "Delia", "Elias", "Florence", "Galileo", "Hester", "Ian", "Jenna", "Klaus", "Linda", "Marco", "Nora", "Otto", "Paulette", "Quill", "Renee", "Sebastian", "Theresa", "Umberto", "Vera", "Walter", "Xena", "Yves", "Zoe", "Anita", "Beau", "Celeste", "Dexter", "Edith", "Fitz", "Gloria", "Hadley", "Iris", "Jude", "Kira", "Leo", "Maya", "Nash", "Olga", "Phillip", "Quetzal", "Royce", "Sage", "Trent", "Uma", "Vincent", "Wynne", "Xerxes", "Yvette", "Zoltan", "Andre", "Bonnie", "Caleb", "Daphne", "Ezra", "Fiona", "Gibson", "Holly", "Ivan", "Juno", "Knox", "Lila", "Mason", "Nila", "Owen", "Petra", "Quincy", "Rosa", "Sterling", "Talia", "Uri", "Violet", "Wesley", "Ximena", "Yuri", "Zelda", "Astrid", "Boris", "Cassidy", "Davis", "Esme", "Forrest", "Genevieve", "Henry", "Imogen", "Joaquin", "Kestrel", "Luther", "Margot", "Nigel", "Ophelia", "Percy", "Quenby", "Reagan", "Sienna", "Truman", "Undine", "Voss", "Wren", "Xan", "Yael", "Zia", "Arthur", "Bianca", "Cody", "Dahlia", "Evander", "Fern", "Gustavo", "Harriet", "Ira", "Josephine", "Kendrick", "Lainey", "Miles", "Naomi", "Orion", "Pearl", "Quark", "Rylie", "Saul", "Tessa", "Ulysses", "Vesper", "Wyatt", "Xiomara", "Yancy", "Zinnia", "Amelia", "Barnaby", "Calliope", "Drake", "Estelle", "Foster", "Greer", "Hollis", "Irene", "Jericho", "Klara", "Logan", "Mavis", "Nico", "Oakley", "Pascal", "Quito", "Rhett", "Scout", "Theron", "Unity", "Vance", "Winifred", "Xola", "Yvonne", "Zev", "Atticus", "Brenda", "Crispin", "Dolores", "Eustace", "Felicity", "Granger", "Hazel", "Idris", "Jovi", "Kasper", "Lyric", "Magnus", "Noor", "Otis", "Phoebe", "Querubin", "Rosalind", "Silas", "Tatum", "Upton", "Vivian", "Wendell", "Xuxa", "Yann", "Zora"];
23
31
  export type NameKind = "session" | "subagent" | "transient";
24
32
  export interface NameHistoryRow {
25
33
  instance_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,qBAAqB;AACrB,eAAO,MAAM,WAAW,y5EA+Qd,CAAC;AAMX,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;2DAEuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsCD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4B5D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,QAAoB,GACzB,oBAAoB,CAoBtB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,MAAM,CA0BR;AAED;oEACoE;AACpE,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAUrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAYrD"}
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,qBAAqB;AACrB,eAAO,MAAM,WAAW,85EA+Qd,CAAC;AAMX,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;2DAEuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsCD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4B5D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,QAAoB,GACzB,oBAAoB,CAoBtB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B,MAAM,CA0BR;AAED;oEACoE;AACpE,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAUrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAYrD"}