harnery 0.45.0 → 0.45.2

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 (235) hide show
  1. package/dist/commander.js +0 -1
  2. package/dist/commands/files.d.ts +2 -2
  3. package/dist/commands/files.d.ts.map +1 -1
  4. package/dist/commands/files.js +4 -7
  5. package/dist/commands/servers.d.ts.map +1 -1
  6. package/dist/commands/servers.js +17 -11
  7. package/dist/commands/tunnel.d.ts +0 -15
  8. package/dist/commands/tunnel.d.ts.map +1 -1
  9. package/dist/commands/tunnel.js +153 -453
  10. package/dist/commands/workflow.d.ts.map +1 -1
  11. package/dist/commands/workflow.js +6 -45
  12. package/dist/core/agents/coord-client.d.ts.map +1 -1
  13. package/dist/core/agents/coord-client.js +0 -18
  14. package/dist/core/agents/live-authority-v3.d.ts +0 -1
  15. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  16. package/dist/core/agents/live-authority-v3.js +0 -11
  17. package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
  18. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  19. package/dist/core/agents/live-lifecycle-v3.js +7 -28
  20. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  21. package/dist/core/agents/state/heartbeat-writer.js +14 -25
  22. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  23. package/dist/core/agents/state/live-coordination-writer.js +0 -5
  24. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  25. package/dist/core/agents/state/pidmap.js +2 -7
  26. package/dist/core/agents/state/proc-start.d.ts +20 -31
  27. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  28. package/dist/core/agents/state/proc-start.js +37 -70
  29. package/dist/core/agents/state/service-process.d.ts +61 -0
  30. package/dist/core/agents/state/service-process.d.ts.map +1 -0
  31. package/dist/core/agents/state/service-process.js +143 -0
  32. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  33. package/dist/core/agents/state/stale-sweep.js +70 -180
  34. package/dist/core/artifacts/activity.d.ts +4 -1
  35. package/dist/core/artifacts/activity.d.ts.map +1 -1
  36. package/dist/core/artifacts/activity.js +8 -2
  37. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  38. package/dist/core/artifacts/delivery-card.js +1 -2
  39. package/dist/core/artifacts/index.d.ts.map +1 -1
  40. package/dist/core/artifacts/index.js +38 -12
  41. package/dist/core/context/index.d.ts +6 -4
  42. package/dist/core/context/index.d.ts.map +1 -1
  43. package/dist/core/context/index.js +194 -256
  44. package/dist/core/context/observation.d.ts +9 -9
  45. package/dist/core/context/observation.d.ts.map +1 -1
  46. package/dist/core/context/observation.js +37 -6
  47. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  48. package/dist/core/events/v3/authority-outbox.js +0 -29
  49. package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
  50. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  51. package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
  52. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  53. package/dist/core/events/v3/producers/recorder.js +64 -77
  54. package/dist/core/events/v3/runtime-build.d.ts +14 -0
  55. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  56. package/dist/core/events/v3/runtime-build.js +44 -0
  57. package/dist/core/governor/index.d.ts +2 -1
  58. package/dist/core/governor/index.d.ts.map +1 -1
  59. package/dist/core/governor/index.js +2 -1
  60. package/dist/core/governor/service-read.d.ts +3 -10
  61. package/dist/core/governor/service-read.d.ts.map +1 -1
  62. package/dist/core/governor/service-read.js +10 -94
  63. package/dist/core/governor/service.d.ts +3 -7
  64. package/dist/core/governor/service.d.ts.map +1 -1
  65. package/dist/core/governor/service.js +17 -182
  66. package/dist/core/hooks/cli.js +78 -48
  67. package/dist/core/hooks/resolve/owner.js +2 -2
  68. package/dist/core/resources/sampler.js +2 -2
  69. package/dist/core/resources/service-status.d.ts +2 -14
  70. package/dist/core/resources/service-status.d.ts.map +1 -1
  71. package/dist/core/resources/service-status.js +3 -23
  72. package/dist/core/resources/service.d.ts +3 -7
  73. package/dist/core/resources/service.d.ts.map +1 -1
  74. package/dist/core/resources/service.js +12 -101
  75. package/dist/core/resources/status.d.ts.map +1 -1
  76. package/dist/core/resources/status.js +3 -2
  77. package/dist/core/semantic/service.d.ts.map +1 -1
  78. package/dist/core/semantic/service.js +9 -2
  79. package/dist/core/servers/index.d.ts +11 -10
  80. package/dist/core/servers/index.d.ts.map +1 -1
  81. package/dist/core/servers/index.js +82 -192
  82. package/dist/core/servers/tunnels.d.ts +3 -5
  83. package/dist/core/servers/tunnels.d.ts.map +1 -1
  84. package/dist/core/servers/tunnels.js +2 -45
  85. package/dist/core/storage/durable-history.js +56 -22
  86. package/dist/core/storage/segments.d.ts.map +1 -1
  87. package/dist/core/storage/segments.js +73 -17
  88. package/dist/core/supervisor/service.d.ts +4 -7
  89. package/dist/core/supervisor/service.d.ts.map +1 -1
  90. package/dist/core/supervisor/service.js +23 -122
  91. package/dist/core/supervisor/services.js +2 -2
  92. package/dist/core/supervisor/status.d.ts +3 -17
  93. package/dist/core/supervisor/status.d.ts.map +1 -1
  94. package/dist/core/supervisor/status.js +7 -70
  95. package/dist/core/work/state.js +7 -7
  96. package/dist/core/workflow/engine.d.ts.map +1 -1
  97. package/dist/core/workflow/engine.js +23 -66
  98. package/dist/core/workflow/index.d.ts +3 -4
  99. package/dist/core/workflow/index.d.ts.map +1 -1
  100. package/dist/core/workflow/index.js +2 -2
  101. package/dist/core/workflow/live-session-v3.d.ts +0 -6
  102. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  103. package/dist/core/workflow/live-session-v3.js +5 -126
  104. package/dist/core/workflow/proof.d.ts +4 -4
  105. package/dist/core/workflow/proof.d.ts.map +1 -1
  106. package/dist/core/workflow/proof.js +34 -86
  107. package/dist/core/workflow/resume-lock.d.ts +17 -0
  108. package/dist/core/workflow/resume-lock.d.ts.map +1 -0
  109. package/dist/core/workflow/resume-lock.js +158 -0
  110. package/dist/core/workflow/run-state.d.ts +0 -21
  111. package/dist/core/workflow/run-state.d.ts.map +1 -1
  112. package/dist/core/workflow/run-state.js +3 -174
  113. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  114. package/dist/core/workflow/spawn-claude.js +0 -19
  115. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  116. package/dist/core/workflow/spawn-codex.js +0 -13
  117. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  118. package/dist/core/workflow/spawn-cursor.js +0 -19
  119. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  120. package/dist/core/workflow/spawn-opencode.js +0 -14
  121. package/dist/core/workflow/types.d.ts +11 -54
  122. package/dist/core/workflow/types.d.ts.map +1 -1
  123. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  124. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  125. package/dist/core/workflow/workspaces/leases.js +1 -1
  126. package/dist/core/workflow/workspaces/local-git.js +91 -210
  127. package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
  128. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
  129. package/dist/core/workflow/workspaces/ownership.js +3 -130
  130. package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
  131. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  132. package/dist/core/workflow/workspaces/reclaim.js +0 -4
  133. package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
  134. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  135. package/dist/core/workflow/workspaces/recovery.js +1 -1
  136. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  137. package/dist/core/workflow/workspaces/state.js +1 -4
  138. package/dist/lib/identities/assume.d.ts.map +1 -1
  139. package/dist/lib/identities/assume.js +0 -4
  140. package/dist/lib/tunnel/state.d.ts +6 -13
  141. package/dist/lib/tunnel/state.d.ts.map +1 -1
  142. package/dist/lib/tunnel/state.js +12 -58
  143. package/package.json +1 -6
  144. package/src/commander.ts +0 -10
  145. package/src/commands/files.ts +5 -13
  146. package/src/commands/servers.ts +23 -12
  147. package/src/commands/tunnel.ts +166 -533
  148. package/src/commands/workflow.ts +8 -58
  149. package/src/core/agents/coord-client.ts +0 -20
  150. package/src/core/agents/live-authority-v3.ts +0 -17
  151. package/src/core/agents/live-lifecycle-v3.ts +14 -52
  152. package/src/core/agents/state/heartbeat-writer.ts +12 -29
  153. package/src/core/agents/state/live-coordination-writer.ts +0 -29
  154. package/src/core/agents/state/pidmap.ts +2 -15
  155. package/src/core/agents/state/proc-start.ts +41 -69
  156. package/src/core/agents/state/service-process.ts +217 -0
  157. package/src/core/agents/state/stale-sweep.ts +82 -247
  158. package/src/core/artifacts/activity.ts +9 -2
  159. package/src/core/artifacts/delivery-card.ts +1 -7
  160. package/src/core/artifacts/index.ts +45 -12
  161. package/src/core/context/index.ts +207 -272
  162. package/src/core/context/observation.ts +40 -9
  163. package/src/core/events/v3/authority-outbox.ts +0 -56
  164. package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
  165. package/src/core/events/v3/producers/recorder.ts +68 -94
  166. package/src/core/events/v3/runtime-build.ts +62 -0
  167. package/src/core/governor/index.ts +1 -2
  168. package/src/core/governor/service-read.ts +15 -114
  169. package/src/core/governor/service.ts +36 -220
  170. package/src/core/hooks/cli.ts +85 -47
  171. package/src/core/hooks/resolve/owner.ts +2 -2
  172. package/src/core/resources/sampler.ts +3 -2
  173. package/src/core/resources/service-status.ts +6 -35
  174. package/src/core/resources/service.ts +23 -133
  175. package/src/core/resources/status.ts +3 -2
  176. package/src/core/semantic/service.ts +8 -2
  177. package/src/core/servers/index.ts +86 -228
  178. package/src/core/servers/tunnels.ts +3 -55
  179. package/src/core/storage/durable-history.ts +52 -20
  180. package/src/core/storage/segments.ts +79 -17
  181. package/src/core/supervisor/service.ts +33 -155
  182. package/src/core/supervisor/services.ts +2 -2
  183. package/src/core/supervisor/status.ts +10 -85
  184. package/src/core/work/state.ts +7 -7
  185. package/src/core/workflow/engine.ts +22 -74
  186. package/src/core/workflow/index.ts +2 -13
  187. package/src/core/workflow/live-session-v3.ts +5 -146
  188. package/src/core/workflow/proof.ts +40 -104
  189. package/src/core/workflow/resume-lock.ts +179 -0
  190. package/src/core/workflow/run-state.ts +3 -207
  191. package/src/core/workflow/spawn-claude.ts +0 -18
  192. package/src/core/workflow/spawn-codex.ts +0 -13
  193. package/src/core/workflow/spawn-cursor.ts +0 -18
  194. package/src/core/workflow/spawn-opencode.ts +0 -14
  195. package/src/core/workflow/types.ts +11 -41
  196. package/src/core/workflow/workspaces/leases.ts +1 -1
  197. package/src/core/workflow/workspaces/local-git.ts +120 -239
  198. package/src/core/workflow/workspaces/ownership.ts +3 -158
  199. package/src/core/workflow/workspaces/reclaim.ts +3 -9
  200. package/src/core/workflow/workspaces/recovery.ts +1 -1
  201. package/src/core/workflow/workspaces/state.ts +1 -5
  202. package/src/lib/identities/assume.ts +0 -4
  203. package/src/lib/tunnel/state.ts +15 -70
  204. package/dist/commands/turn-rollback.d.ts +0 -5
  205. package/dist/commands/turn-rollback.d.ts.map +0 -1
  206. package/dist/commands/turn-rollback.js +0 -76
  207. package/dist/core/agents/state/process-handle.d.ts +0 -26
  208. package/dist/core/agents/state/process-handle.d.ts.map +0 -1
  209. package/dist/core/agents/state/process-handle.js +0 -121
  210. package/dist/core/agents/state/unchanged-file.d.ts +0 -44
  211. package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
  212. package/dist/core/agents/state/unchanged-file.js +0 -390
  213. package/dist/core/events/v3/generation-custody.d.ts +0 -5
  214. package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
  215. package/dist/core/events/v3/generation-custody.js +0 -50
  216. package/dist/core/turn-rollback/content.d.ts +0 -53
  217. package/dist/core/turn-rollback/content.d.ts.map +0 -1
  218. package/dist/core/turn-rollback/content.js +0 -396
  219. package/dist/core/turn-rollback/index.d.ts +0 -72
  220. package/dist/core/turn-rollback/index.d.ts.map +0 -1
  221. package/dist/core/turn-rollback/index.js +0 -443
  222. package/dist/core/workflow/failure-evidence.d.ts +0 -16
  223. package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
  224. package/dist/core/workflow/failure-evidence.js +0 -114
  225. package/dist/core/workflow/recovery-audit.d.ts +0 -30
  226. package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
  227. package/dist/core/workflow/recovery-audit.js +0 -116
  228. package/src/commands/turn-rollback.ts +0 -111
  229. package/src/core/agents/state/process-handle.ts +0 -165
  230. package/src/core/agents/state/unchanged-file.ts +0 -456
  231. package/src/core/events/v3/generation-custody.ts +0 -59
  232. package/src/core/turn-rollback/content.ts +0 -499
  233. package/src/core/turn-rollback/index.ts +0 -580
  234. package/src/core/workflow/failure-evidence.ts +0 -141
  235. package/src/core/workflow/recovery-audit.ts +0 -181
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Repository observation contract shared by context recovery and workflow proof.
3
3
  *
4
- * This module has no runtime imports so read-only consumers (the Codec
5
- * dashboard, proof readers) can use it without reaching the Git-running
6
- * snapshot code in ./index.ts.
4
+ * This module runs no Git and touches no files, so read-only consumers (the
5
+ * Codec dashboard, proof readers) can use it without reaching the
6
+ * Git-running snapshot code in ./index.ts.
7
7
  */
8
8
 
9
+ import { isAbsolute } from "node:path";
10
+
9
11
  export type RepoObservation = "ok" | "unavailable" | "partial";
10
12
 
11
13
  export interface RepoSnapshot {
@@ -20,10 +22,39 @@ export interface RepoSnapshot {
20
22
  dirty_paths_truncated?: boolean;
21
23
  }
22
24
 
23
- /** Only explicitly successful observations can establish repository drift. */
24
- export function observationsComparable(
25
- a: { observation?: string },
26
- b: { observation?: string },
27
- ): boolean {
28
- return a.observation === "ok" && b.observation === "ok";
25
+ /**
26
+ * Every reason two snapshots cannot establish repository drift. Only an empty
27
+ * list lets a caller compare branch, HEAD and dirty paths; anything else
28
+ * leaves the whole comparison unknown.
29
+ */
30
+ export function repoComparisonProblems(
31
+ a: RepoSnapshot,
32
+ b: RepoSnapshot,
33
+ labels: readonly [string, string],
34
+ ): string[] {
35
+ const sides = [
36
+ [labels[0], a],
37
+ [labels[1], b],
38
+ ] as const;
39
+ const failed = sides.flatMap(([label, repo]) =>
40
+ repo.observation === "ok"
41
+ ? []
42
+ : [
43
+ // Snapshots recorded before observations existed carry none.
44
+ `${label} snapshot is ${repo.observation ?? "unavailable"}${repo.observation_detail ? ` (${repo.observation_detail})` : ""}`,
45
+ ],
46
+ );
47
+ if (failed.length > 0) return failed;
48
+ const problems: string[] = [];
49
+ if (a.dirty_paths_truncated || b.dirty_paths_truncated) {
50
+ problems.push("dirty path list is truncated");
51
+ }
52
+ if (!a.root || !b.root || !isAbsolute(a.root) || !isAbsolute(b.root)) {
53
+ problems.push("repository root is unknown");
54
+ } else if (a.root !== b.root) {
55
+ problems.push("repository roots differ");
56
+ }
57
+ if (!a.branch?.trim() || !b.branch?.trim()) problems.push("branch is unknown");
58
+ if (!a.head?.trim() || !b.head?.trim()) problems.push("HEAD is unknown");
59
+ return problems;
29
60
  }
@@ -16,7 +16,6 @@ import { fsyncParentDirectory } from "../../workflow/durable-record.ts";
16
16
  import { canonicalJsonV3, sha256V3 } from "./canonical.ts";
17
17
  import type { EventV3 } from "./contract.ts";
18
18
  import { liveGenesisIdV3 } from "./control.ts";
19
- import { assertCurrentGenerationV3, withGenerationCustodyV3 } from "./generation-custody.ts";
20
19
  import { assertEventV3 } from "./validate.ts";
21
20
  import { ensureEventV3Layout, eventV3Paths, writeEventV3 } from "./writer.ts";
22
21
 
@@ -155,32 +154,9 @@ export function publishAuthorityTransactionV3(
155
154
  coordRoot: string,
156
155
  transaction: AuthorityTransactionV3,
157
156
  fence: AuthorityEpochFenceV3 = {},
158
- ): AuthorityTransactionV3 {
159
- return withGenerationCustodyV3(
160
- coordRoot,
161
- [transaction.actor_instance_id, transaction.subject_instance_id],
162
- () => publishAuthorityTransactionWithCustodyV3(coordRoot, transaction, fence),
163
- );
164
- }
165
-
166
- function publishAuthorityTransactionWithCustodyV3(
167
- coordRoot: string,
168
- transaction: AuthorityTransactionV3,
169
- fence: AuthorityEpochFenceV3,
170
157
  ): AuthorityTransactionV3 {
171
158
  validateTransaction(transaction);
172
159
  const paths = ensureEventV3Layout(coordRoot);
173
- const committedPath = authorityCommittedPath(paths.authorityOutbox, transaction.transaction_id);
174
- if (existsSync(committedPath)) {
175
- if (
176
- readAuthorityReceiptV3(committedPath).transaction_digest !==
177
- sha256V3(`${canonicalJsonV3(transaction)}\n`)
178
- ) {
179
- throw new Error("authority committed transaction conflicts with this transaction");
180
- }
181
- return transaction;
182
- }
183
- assertTransactionGeneration(coordRoot, transaction);
184
160
  if (fence.expectedGenesisId && liveGenesisIdV3(coordRoot) !== fence.expectedGenesisId) {
185
161
  throw new Error("authority transaction epoch was replaced before publication");
186
162
  }
@@ -209,26 +185,6 @@ export function reconcileAuthorityTransactionV3(
209
185
  transactionId: string,
210
186
  reconciler: AuthorityReconcilerV3,
211
187
  fence: AuthorityEpochFenceV3 = {},
212
- ): AuthorityReceiptV3 {
213
- assertTransactionId(transactionId);
214
- const paths = ensureEventV3Layout(coordRoot);
215
- const committedPath = authorityCommittedPath(paths.authorityOutbox, transactionId);
216
- if (existsSync(committedPath)) return readAuthorityReceiptV3(committedPath);
217
- const transaction = readAuthorityTransactionV3(
218
- authorityReadyPath(paths.authorityOutbox, transactionId),
219
- );
220
- return withGenerationCustodyV3(
221
- coordRoot,
222
- [transaction.actor_instance_id, transaction.subject_instance_id],
223
- () => reconcileAuthorityTransactionWithCustodyV3(coordRoot, transactionId, reconciler, fence),
224
- );
225
- }
226
-
227
- function reconcileAuthorityTransactionWithCustodyV3(
228
- coordRoot: string,
229
- transactionId: string,
230
- reconciler: AuthorityReconcilerV3,
231
- fence: AuthorityEpochFenceV3,
232
188
  ): AuthorityReceiptV3 {
233
189
  assertTransactionId(transactionId);
234
190
  const paths = ensureEventV3Layout(coordRoot);
@@ -236,10 +192,8 @@ function reconcileAuthorityTransactionWithCustodyV3(
236
192
  if (existsSync(committedPath)) return readAuthorityReceiptV3(committedPath);
237
193
  const readyPath = authorityReadyPath(paths.authorityOutbox, transactionId);
238
194
  const transaction = readAuthorityTransactionV3(readyPath);
239
- assertTransactionGeneration(coordRoot, transaction);
240
195
  const current = reconciler.readStateDigest();
241
196
  if (current === transaction.expected_prior_state_digest) {
242
- assertTransactionGeneration(coordRoot, transaction);
243
197
  reconciler.apply(transaction.mutation, transaction.transaction_id);
244
198
  } else if (current !== transaction.desired_state_digest) {
245
199
  throw new Error("authority transaction prior state conflicts with current state");
@@ -247,7 +201,6 @@ function reconcileAuthorityTransactionWithCustodyV3(
247
201
  if (reconciler.readStateDigest() !== transaction.desired_state_digest) {
248
202
  throw new Error("authority transaction did not reach its declared desired state");
249
203
  }
250
- assertTransactionGeneration(coordRoot, transaction);
251
204
  const event = JSON.parse(transaction.event_row) as unknown;
252
205
  assertEventV3(event);
253
206
  const durability = writeEventV3(coordRoot, event, {
@@ -277,15 +230,6 @@ function reconcileAuthorityTransactionWithCustodyV3(
277
230
  return receipt;
278
231
  }
279
232
 
280
- function assertTransactionGeneration(coordRoot: string, transaction: AuthorityTransactionV3): void {
281
- const event = JSON.parse(transaction.event_row) as EventV3;
282
- assertCurrentGenerationV3(
283
- coordRoot,
284
- transaction.actor_instance_id,
285
- "generation_id" in event.scope ? event.scope.generation_id : undefined,
286
- );
287
- }
288
-
289
233
  export function listPendingAuthorityTransactionsV3(coordRoot: string): AuthorityTransactionV3[] {
290
234
  const outbox = eventV3Paths(coordRoot).authorityOutbox;
291
235
  if (!existsSync(outbox)) return [];
@@ -33,7 +33,6 @@ import { markObservedClockRegressionV3 } from "../clock-order.ts";
33
33
  import type { EventV3 } from "../contract.ts";
34
34
  import { type EventV3WriteMode, readEventV3ControlState } from "../control.ts";
35
35
  import { fingerprintContextV3 } from "../fingerprint-keys.ts";
36
- import { assertCurrentGenerationV3, withGenerationCustodyV3 } from "../generation-custody.ts";
37
36
  import { clockIdV3, spanIdV3 } from "../ids.ts";
38
37
  import { closeSpanStateV3, type OpenSpanStateV3, openSpanStateV3 } from "../span-state.ts";
39
38
  import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../writer.ts";
@@ -104,7 +103,6 @@ export interface RecordCoordinationAuthorityV3Input<
104
103
  bridge?: "codex-wsl";
105
104
  expected_prior_state_digest: `sha256:${string}`;
106
105
  desired_state_digest: `sha256:${string}`;
107
- expected_generation_id?: `gen_${string}`;
108
106
  monotonic_ns?: string;
109
107
  observed_at?: string;
110
108
  reconciler: AuthorityReconcilerV3;
@@ -131,16 +129,6 @@ export type RecordCoordinationAuthorityV3Result =
131
129
  */
132
130
  export function recordCoordinationAuthorityV3<S extends CoordinationAuthoritySignalV3>(
133
131
  input: RecordCoordinationAuthorityV3Input<S>,
134
- ): RecordCoordinationAuthorityV3Result {
135
- return withGenerationCustodyV3(
136
- input.coordRoot,
137
- [input.actor_instance_id, input.subject_instance_id],
138
- () => recordCoordinationAuthorityWithCustodyV3(input),
139
- );
140
- }
141
-
142
- function recordCoordinationAuthorityWithCustodyV3<S extends CoordinationAuthoritySignalV3>(
143
- input: RecordCoordinationAuthorityV3Input<S>,
144
132
  ): RecordCoordinationAuthorityV3Result {
145
133
  const control = readEventV3ControlState(input.coordRoot);
146
134
  if (control.state !== input.mode) return { state: "gate_closed", reason: control.state };
@@ -153,8 +141,6 @@ function recordCoordinationAuthorityWithCustodyV3<S extends CoordinationAuthorit
153
141
  if (
154
142
  !hook ||
155
143
  hook.terminal ||
156
- (input.expected_generation_id !== undefined &&
157
- hook.generation_id !== input.expected_generation_id) ||
158
144
  hook.instance_id !== input.actor_instance_id ||
159
145
  !hook.last_event_id
160
146
  ) {
@@ -342,7 +328,6 @@ function recordCoordinationAuthorityWithCustodyV3<S extends CoordinationAuthorit
342
328
  mutation: normalized.mutation,
343
329
  event: normalized.event,
344
330
  });
345
- assertCurrentGenerationV3(input.coordRoot, state.actor_instance_id, state.generation_id);
346
331
  if (input.signal === "wait-ended" && waitId) delete state.open_waits[waitId];
347
332
  state.pending = { source_id: sourceId, transaction };
348
333
  publishCoordinationState(path, state);
@@ -43,7 +43,6 @@ import { markObservedClockRegressionV3 } from "../clock-order.ts";
43
43
  import type { EventV3 } from "../contract.ts";
44
44
  import { type EventV3WriteMode, readEventV3ControlState } from "../control.ts";
45
45
  import { fingerprintContextV3 } from "../fingerprint-keys.ts";
46
- import { withGenerationCustodyV3 } from "../generation-custody.ts";
47
46
  import {
48
47
  attestationIdV3,
49
48
  clockIdV3,
@@ -659,17 +658,6 @@ function processHookSignalLocked(
659
658
  input: RecordHookSignalV3Input,
660
659
  sessionHash: `hid_${string}`,
661
660
  path: string,
662
- ): RecordHookSignalV3Result {
663
- return withGenerationCustodyV3(input.coordRoot, [input.instance_id], () =>
664
- processHookSignalWithCustodyLocked(control, input, sessionHash, path),
665
- );
666
- }
667
-
668
- function processHookSignalWithCustodyLocked(
669
- control: OpenHookControlStateV3,
670
- input: RecordHookSignalV3Input,
671
- sessionHash: `hid_${string}`,
672
- path: string,
673
661
  ): RecordHookSignalV3Result {
674
662
  const behavior = adapterBehavior(input.adapter);
675
663
  const rootId = control.genesis.event.scope.root_id as `root_${string}`;
@@ -3221,14 +3209,6 @@ function suppressClosedSpanSignal(
3221
3209
  */
3222
3210
  export function recordApprovedSessionEndV3(
3223
3211
  input: RecordApprovedSessionEndV3Input,
3224
- ): RecordApprovedSessionEndV3Result {
3225
- return withGenerationCustodyV3(input.coordRoot, [input.instance_id], () =>
3226
- recordApprovedSessionEndWithCustodyV3(input),
3227
- );
3228
- }
3229
-
3230
- function recordApprovedSessionEndWithCustodyV3(
3231
- input: RecordApprovedSessionEndV3Input,
3232
3212
  ): RecordApprovedSessionEndV3Result {
3233
3213
  const control = readEventV3ControlState(input.coordRoot);
3234
3214
  if (control.state !== input.mode) {
@@ -3576,81 +3556,77 @@ export function reanchorArchivedHookProducersV3(
3576
3556
  for (const archived of listArchivedHookProducerStatesV3(input.archivedEpoch)) {
3577
3557
  const previous = archived.state;
3578
3558
  if (previous.terminal || !previous.started_event_id) continue;
3579
- withGenerationCustodyV3(input.coordRoot, [previous.instance_id], () => {
3580
- const path = producerStatePath(input.coordRoot, previous.adapter, archived.sessionHash);
3581
- const lease = acquireStateLeaseWithRetry(input.coordRoot, path, 40);
3582
- if (!lease) {
3583
- throw new Error(`event_v3_rotation_reanchor_busy:${previous.instance_id}`);
3559
+ const path = producerStatePath(input.coordRoot, previous.adapter, archived.sessionHash);
3560
+ const lease = acquireStateLeaseWithRetry(input.coordRoot, path, 40);
3561
+ if (!lease) {
3562
+ throw new Error(`event_v3_rotation_reanchor_busy:${previous.instance_id}`);
3563
+ }
3564
+ try {
3565
+ if (existsSync(path)) {
3566
+ const current = readProducerState(path);
3567
+ if (current.epoch_genesis_id === genesisId) continue;
3584
3568
  }
3585
- try {
3586
- if (existsSync(path)) {
3587
- const current = readProducerState(path);
3588
- if (current.epoch_genesis_id === genesisId) return;
3589
- }
3590
- const sourceEvent = `${previous.adapter}.epoch-rotation-reanchor`;
3591
- const rotationInput: RecordHookSignalV3Input = {
3592
- coordRoot: input.coordRoot,
3593
- mode: input.mode,
3594
- signal: "session-start",
3595
- payload: {
3596
- raw: {},
3597
- ...(previous.current_native_turn_id
3598
- ? { turn_id: previous.current_native_turn_id }
3599
- : {}),
3600
- ...(adapterBehavior(previous.adapter).tracksExecutionMode && previous.cursor_mode
3601
- ? { cursor_mode: previous.cursor_mode }
3602
- : {}),
3603
- },
3604
- adapter: previous.adapter,
3605
- instance_id: previous.instance_id,
3606
- producer_id: "prd_agent-hook",
3607
- build_id: input.build_id,
3608
- platform: input.platform,
3609
- observed_at: observedAt,
3610
- writerOptions: { expectedGenesisId: genesisId },
3611
- };
3612
- const state = newProducerState(
3613
- rotationInput,
3614
- previous.session_id,
3615
- epochId,
3616
- boundaryEventId as `evt_${string}`,
3617
- genesisId,
3618
- );
3619
- const sessionStart = buildMidFlightSessionStart(rotationInput, state, rootId);
3620
- if (sessionStart.event_type !== "session.started") {
3621
- throw new Error("rotation re-anchor session start could not be normalized");
3622
- }
3623
- sessionStart.provenance = {
3624
- ...sessionStart.provenance,
3569
+ const sourceEvent = `${previous.adapter}.epoch-rotation-reanchor`;
3570
+ const rotationInput: RecordHookSignalV3Input = {
3571
+ coordRoot: input.coordRoot,
3572
+ mode: input.mode,
3573
+ signal: "session-start",
3574
+ payload: {
3575
+ raw: {},
3576
+ ...(previous.current_native_turn_id ? { turn_id: previous.current_native_turn_id } : {}),
3577
+ ...(adapterBehavior(previous.adapter).tracksExecutionMode && previous.cursor_mode
3578
+ ? { cursor_mode: previous.cursor_mode }
3579
+ : {}),
3580
+ },
3581
+ adapter: previous.adapter,
3582
+ instance_id: previous.instance_id,
3583
+ producer_id: "prd_agent-hook",
3584
+ build_id: input.build_id,
3585
+ platform: input.platform,
3586
+ observed_at: observedAt,
3587
+ writerOptions: { expectedGenesisId: genesisId },
3588
+ };
3589
+ const state = newProducerState(
3590
+ rotationInput,
3591
+ previous.session_id,
3592
+ epochId,
3593
+ boundaryEventId as `evt_${string}`,
3594
+ genesisId,
3595
+ );
3596
+ const sessionStart = buildMidFlightSessionStart(rotationInput, state, rootId);
3597
+ if (sessionStart.event_type !== "session.started") {
3598
+ throw new Error("rotation re-anchor session start could not be normalized");
3599
+ }
3600
+ sessionStart.provenance = {
3601
+ ...sessionStart.provenance,
3602
+ source_event: sourceEvent,
3603
+ attestation: "derived",
3604
+ confidence: "high",
3605
+ attribution: {
3606
+ method: "session_env",
3607
+ state: "verified",
3608
+ subject_instance_id: state.instance_id,
3609
+ },
3610
+ };
3611
+ sessionStart.payload.resume = {
3612
+ state: "unknown",
3613
+ reason: "epoch_rotation_reanchor",
3614
+ };
3615
+ assertEventV3(sessionStart);
3616
+ commitEventLocked(rotationInput, state, path, sessionStart);
3617
+ sessions += 1;
3618
+
3619
+ if (previous.current_turn_id && previous.current_turn_span) {
3620
+ commitMidFlightTurnStart(rotationInput, state, path, rootId, {
3625
3621
  source_event: sourceEvent,
3626
- attestation: "derived",
3627
3622
  confidence: "high",
3628
- attribution: {
3629
- method: "session_env",
3630
- state: "verified",
3631
- subject_instance_id: state.instance_id,
3632
- },
3633
- };
3634
- sessionStart.payload.resume = {
3635
- state: "unknown",
3636
- reason: "epoch_rotation_reanchor",
3637
- };
3638
- assertEventV3(sessionStart);
3639
- commitEventLocked(rotationInput, state, path, sessionStart);
3640
- sessions += 1;
3641
-
3642
- if (previous.current_turn_id && previous.current_turn_span) {
3643
- commitMidFlightTurnStart(rotationInput, state, path, rootId, {
3644
- source_event: sourceEvent,
3645
- confidence: "high",
3646
- attribution_method: "session_env",
3647
- });
3648
- turns += 1;
3649
- }
3650
- } finally {
3651
- lease.release();
3623
+ attribution_method: "session_env",
3624
+ });
3625
+ turns += 1;
3652
3626
  }
3653
- });
3627
+ } finally {
3628
+ lease.release();
3629
+ }
3654
3630
  }
3655
3631
  return { sessions, turns };
3656
3632
  }
@@ -3888,9 +3864,7 @@ export function readJoinableHookProducerStateV3(
3888
3864
  instanceId: `inst_${string}`,
3889
3865
  ): HookProducerStateV3 | undefined {
3890
3866
  const direct = readHookProducerStateV3(coordRoot, adapter, nativeSessionId);
3891
- // A terminal native session belonging to this actor is stale authority,
3892
- // not permission to adopt a replacement published under the same instance.
3893
- if (direct?.instance_id === instanceId) return direct.terminal ? undefined : direct;
3867
+ if (direct?.instance_id === instanceId && !direct.terminal) return direct;
3894
3868
  const byInstance = readHookProducerStateByInstanceV3(coordRoot, instanceId);
3895
3869
  return byInstance?.adapter === adapter && !byInstance.terminal ? byInstance : undefined;
3896
3870
  }
@@ -40,3 +40,65 @@ export function currentHarneryRuntimeBuild(): string {
40
40
  export function harneryPackageRoot(): string {
41
41
  return resolve(dirname(fileURLToPath(import.meta.url)), "../../../..");
42
42
  }
43
+
44
+ export interface HarneryBuild {
45
+ version: string;
46
+ revision: string;
47
+ }
48
+
49
+ /** The installed package version and runtime revision; "unknown" when unreadable. */
50
+ export function installedHarneryBuild(): HarneryBuild {
51
+ let version = "unknown";
52
+ let revision = "unknown";
53
+ try {
54
+ const manifest = JSON.parse(
55
+ readFileSync(resolve(harneryPackageRoot(), "package.json"), "utf8"),
56
+ ) as { version?: unknown };
57
+ if (typeof manifest.version === "string" && manifest.version.length <= 100)
58
+ version = manifest.version;
59
+ } catch {
60
+ // Missing package metadata cannot establish that a running build is current.
61
+ }
62
+ try {
63
+ revision = currentHarneryRuntimeBuild();
64
+ } catch {
65
+ // Status still reports the unavailable build instead of guessing a match.
66
+ }
67
+ return { version, revision };
68
+ }
69
+
70
+ export interface ServiceBuildStatus {
71
+ running_build: HarneryBuild | null;
72
+ installed_build: HarneryBuild;
73
+ restart_needed: boolean;
74
+ build_status: string;
75
+ }
76
+
77
+ /** A missing or unknown running build, or any difference, means restart. */
78
+ export function serviceBuildStatus(recorded: unknown): ServiceBuildStatus {
79
+ const installed_build = installedHarneryBuild();
80
+ const candidate = recorded as Partial<HarneryBuild> | undefined;
81
+ const known = (value: unknown): value is string =>
82
+ typeof value === "string" && value.length > 0 && value.length <= 100 && value !== "unknown";
83
+ const running_build =
84
+ candidate && known(candidate.version) && known(candidate.revision)
85
+ ? { version: candidate.version, revision: candidate.revision }
86
+ : null;
87
+ const restart_needed =
88
+ running_build === null ||
89
+ installed_build.version === "unknown" ||
90
+ installed_build.revision === "unknown" ||
91
+ running_build.version !== installed_build.version ||
92
+ running_build.revision !== installed_build.revision;
93
+ return {
94
+ running_build,
95
+ installed_build,
96
+ restart_needed,
97
+ build_status:
98
+ running_build === null
99
+ ? "running build unknown; restart needed"
100
+ : restart_needed
101
+ ? "restart needed"
102
+ : "current",
103
+ };
104
+ }
@@ -63,13 +63,12 @@ export {
63
63
  type RunGovernorServiceDaemonInput,
64
64
  type RunGovernorServiceSweepInput,
65
65
  readGovernorServiceConfig,
66
- readGovernorServiceRuntime,
67
- readGovernorServiceStatus,
68
66
  requestGovernorServiceStop,
69
67
  runGovernorServiceDaemon,
70
68
  runGovernorServiceSweep,
71
69
  spawnGovernorService,
72
70
  } from "./service.ts";
71
+ export { readGovernorServiceRuntime, readGovernorServiceStatus } from "./service-read.ts";
73
72
  export {
74
73
  assertGovernorId,
75
74
  type CreateGovernorInput,
@@ -1,21 +1,20 @@
1
1
  import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from "node:fs";
2
- import { hostname } from "node:os";
3
2
  import { join, resolve } from "node:path";
4
3
  import { gunzipSync } from "node:zlib";
5
4
  import { coordEnv } from "../../lib/env.ts";
5
+ import type { ProcessGenerationVerdict } from "../agents/state/proc-start.ts";
6
6
  import {
7
- type ProcessGenerationVerdict,
8
- verifyProcessGeneration,
9
- } from "../agents/state/proc-start.ts";
10
- import { currentHarneryRuntimeBuild } from "../events/v3/runtime-build.ts";
7
+ observeServiceProcess,
8
+ type ServiceLiveness,
9
+ type ServiceProcessDependencies,
10
+ } from "../agents/state/service-process.ts";
11
+ import { serviceBuildStatus } from "../events/v3/runtime-build.ts";
11
12
  import { createStorageCatalog } from "../storage/catalog.ts";
12
13
  import type { HarneryLogRecordV1 } from "../storage/jsonl.ts";
13
14
  import { parseLogRecord } from "../storage/jsonl.ts";
14
15
  import { familyLogDirectory, readSegmentManifest } from "../storage/segments.ts";
15
16
  import type {
16
- GovernorServiceBuild,
17
17
  GovernorServiceConfig,
18
- GovernorServiceProcessDependencies,
19
18
  GovernorServiceRuntime,
20
19
  GovernorServiceStatus,
21
20
  GovernorServiceStatusRecord,
@@ -25,106 +24,6 @@ const MAX_FILE_BYTES = 512 * 1024;
25
24
  const MAX_LOG_RECORDS = 10_000;
26
25
  const MAX_GOALS = 100;
27
26
 
28
- export function currentGovernorServiceBuild(): GovernorServiceBuild {
29
- let version = "unknown";
30
- let revision = "unknown";
31
- try {
32
- const manifest = JSON.parse(
33
- readFileSync(new URL("../../../package.json", import.meta.url), "utf8"),
34
- ) as {
35
- version?: unknown;
36
- };
37
- if (typeof manifest.version === "string" && manifest.version.length <= 100) {
38
- version = manifest.version;
39
- }
40
- } catch {
41
- // Missing package metadata cannot establish that the running build is current.
42
- }
43
- try {
44
- revision = currentHarneryRuntimeBuild();
45
- } catch {
46
- // Status still reports the unavailable build instead of guessing a match.
47
- }
48
- return { version, revision };
49
- }
50
-
51
- export function governorServiceBuildStatus(
52
- record: GovernorServiceStatusRecord | undefined,
53
- ): Pick<
54
- GovernorServiceStatus,
55
- "running_build" | "installed_build" | "restart_needed" | "build_status"
56
- > {
57
- const installed_build = currentGovernorServiceBuild();
58
- const candidate = record?.running_build;
59
- const running_build =
60
- candidate &&
61
- typeof candidate.version === "string" &&
62
- candidate.version.length > 0 &&
63
- candidate.version.length <= 100 &&
64
- typeof candidate.revision === "string" &&
65
- candidate.revision.length > 0 &&
66
- candidate.revision.length <= 100 &&
67
- candidate.version !== "unknown" &&
68
- candidate.revision !== "unknown"
69
- ? candidate
70
- : null;
71
- const restart_needed =
72
- running_build === null ||
73
- installed_build.version === "unknown" ||
74
- installed_build.revision === "unknown" ||
75
- running_build.version !== installed_build.version ||
76
- running_build.revision !== installed_build.revision;
77
- return {
78
- running_build,
79
- installed_build,
80
- restart_needed,
81
- build_status:
82
- running_build === null
83
- ? "running build unknown; restart needed"
84
- : restart_needed
85
- ? "restart needed"
86
- : "current",
87
- };
88
- }
89
-
90
- /** An unknown token or process probe cannot establish a running or stopped owner. */
91
- export function observeGovernorServiceProcess(
92
- record: Pick<GovernorServiceStatusRecord, "pid" | "host" | "state" | "start_token"> | undefined,
93
- dependencies: GovernorServiceProcessDependencies = {},
94
- ): { generation: ProcessGenerationVerdict; liveness: GovernorServiceStatus["liveness"] } {
95
- if (!record) return { generation: "unknown", liveness: "stopped" };
96
- const generation =
97
- record.host === hostname()
98
- ? verifyProcessGeneration(record.pid, record.start_token, dependencies.readToken)
99
- : "unknown";
100
- if (record.host !== hostname()) return { generation, liveness: "unknown" };
101
- try {
102
- const present = (dependencies.probePid ?? probePid)(record.pid);
103
- if (!present) return { generation, liveness: "stopped" };
104
- } catch (error) {
105
- return {
106
- generation,
107
- liveness: (error as NodeJS.ErrnoException).code === "ESRCH" ? "stopped" : "unknown",
108
- };
109
- }
110
- return {
111
- generation,
112
- liveness:
113
- generation === "same"
114
- ? record.state === "stopped"
115
- ? "stopped"
116
- : "running"
117
- : generation === "different"
118
- ? "stopped"
119
- : "unknown",
120
- };
121
- }
122
-
123
- function probePid(pid: number): boolean {
124
- process.kill(pid, 0);
125
- return true;
126
- }
127
-
128
27
  export function readGovernorServiceConfig(coordRootRaw: string): GovernorServiceConfig {
129
28
  const coordRoot = resolve(coordRootRaw);
130
29
  const config = readBoundedJson<GovernorServiceConfig>(
@@ -175,7 +74,7 @@ export function readGovernorServiceRuntime(
175
74
 
176
75
  export function readGovernorServiceStatus(
177
76
  coordRootRaw: string,
178
- dependencies: GovernorServiceProcessDependencies = {},
77
+ dependencies: ServiceProcessDependencies = {},
179
78
  ): GovernorServiceStatus {
180
79
  const coordRoot = resolve(coordRootRaw);
181
80
  const record = readStatusRecord(coordRoot);
@@ -191,15 +90,17 @@ export function readGovernorServiceStatus(
191
90
  } catch {
192
91
  // Runtime is recoverable and must not make the dashboard unreadable.
193
92
  }
194
- const observation = observeGovernorServiceProcess(record, dependencies);
195
- if (!record && existsSync(join(serviceDir(coordRoot), "status.json"))) {
196
- observation.liveness = "unknown";
197
- }
93
+ const observation: { generation: ProcessGenerationVerdict; liveness: ServiceLiveness } = record
94
+ ? observeServiceProcess(record, dependencies)
95
+ : {
96
+ generation: "unknown",
97
+ liveness: existsSync(join(serviceDir(coordRoot), "status.json")) ? "unknown" : "stopped",
98
+ };
198
99
  return {
199
- running: observation.liveness === "running" && record?.state !== "stopped",
100
+ running: observation.liveness === "running",
200
101
  stale: observation.liveness === "stopped" && record !== undefined && record.state !== "stopped",
201
102
  ...observation,
202
- ...governorServiceBuildStatus(record),
103
+ ...serviceBuildStatus(record?.running_build),
203
104
  record,
204
105
  config,
205
106
  runtime,