harnery 0.45.1 → 0.46.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 (332) hide show
  1. package/dist/commander.d.ts +13 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commands/admission.d.ts.map +1 -1
  4. package/dist/commands/admission.js +214 -27
  5. package/dist/commands/agents.d.ts +4 -0
  6. package/dist/commands/agents.d.ts.map +1 -1
  7. package/dist/commands/agents.js +102 -25
  8. package/dist/commands/browse.d.ts +3 -0
  9. package/dist/commands/browse.d.ts.map +1 -1
  10. package/dist/commands/browse.js +172 -8
  11. package/dist/commands/fetch.js +1 -0
  12. package/dist/commands/ledger-v3.d.ts.map +1 -1
  13. package/dist/commands/ledger-v3.js +8 -2
  14. package/dist/commands/presence.d.ts.map +1 -1
  15. package/dist/commands/presence.js +40 -0
  16. package/dist/commands/qa-run.d.ts.map +1 -1
  17. package/dist/commands/qa-run.js +4 -3
  18. package/dist/commands/review-pack.d.ts.map +1 -1
  19. package/dist/commands/review-pack.js +33 -6
  20. package/dist/commands/workflow.d.ts.map +1 -1
  21. package/dist/commands/workflow.js +6 -45
  22. package/dist/core/agents/coord-client.d.ts.map +1 -1
  23. package/dist/core/agents/coord-client.js +0 -18
  24. package/dist/core/agents/index.d.ts +1 -0
  25. package/dist/core/agents/index.d.ts.map +1 -1
  26. package/dist/core/agents/index.js +1 -0
  27. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  28. package/dist/core/agents/live-authority-v3.js +11 -4
  29. package/dist/core/agents/render/session-context.d.ts +0 -6
  30. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  31. package/dist/core/agents/render/session-context.js +1 -6
  32. package/dist/core/agents/session-events.d.ts.map +1 -1
  33. package/dist/core/agents/session-events.js +12 -4
  34. package/dist/core/agents/state/heartbeat-reader.d.ts +3 -0
  35. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  36. package/dist/core/agents/state/live-coordination-view.d.ts +3 -1
  37. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  38. package/dist/core/agents/state/live-coordination-view.js +6 -2
  39. package/dist/core/agents/state/parent-reference-contract.d.ts +16 -0
  40. package/dist/core/agents/state/parent-reference-contract.d.ts.map +1 -0
  41. package/dist/core/agents/state/parent-reference-contract.js +1 -0
  42. package/dist/core/agents/state/parent-reference.d.ts +18 -0
  43. package/dist/core/agents/state/parent-reference.d.ts.map +1 -0
  44. package/dist/core/agents/state/parent-reference.js +119 -0
  45. package/dist/core/agents/state/pidmap.js +2 -2
  46. package/dist/core/agents/state/proc-start.d.ts +20 -31
  47. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  48. package/dist/core/agents/state/proc-start.js +37 -70
  49. package/dist/core/agents/state/service-process.d.ts +61 -0
  50. package/dist/core/agents/state/service-process.d.ts.map +1 -0
  51. package/dist/core/agents/state/service-process.js +143 -0
  52. package/dist/core/config.d.ts +39 -0
  53. package/dist/core/config.d.ts.map +1 -1
  54. package/dist/core/config.js +63 -0
  55. package/dist/core/context/index.d.ts +6 -4
  56. package/dist/core/context/index.d.ts.map +1 -1
  57. package/dist/core/context/index.js +194 -256
  58. package/dist/core/context/observation.d.ts +9 -9
  59. package/dist/core/context/observation.d.ts.map +1 -1
  60. package/dist/core/context/observation.js +37 -6
  61. package/dist/core/diagnostics/path-identity.d.ts +1 -1
  62. package/dist/core/diagnostics/path-identity.d.ts.map +1 -1
  63. package/dist/core/diagnostics/path-identity.js +51 -6
  64. package/dist/core/events/v3/canonical.js +43 -14
  65. package/dist/core/events/v3/index.d.ts +1 -1
  66. package/dist/core/events/v3/index.d.ts.map +1 -1
  67. package/dist/core/events/v3/index.js +1 -1
  68. package/dist/core/events/v3/producers/recorder.d.ts +34 -0
  69. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  70. package/dist/core/events/v3/producers/recorder.js +117 -13
  71. package/dist/core/events/v3/reader.d.ts.map +1 -1
  72. package/dist/core/events/v3/reader.js +53 -13
  73. package/dist/core/events/v3/runtime-build.d.ts +14 -0
  74. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  75. package/dist/core/events/v3/runtime-build.js +44 -0
  76. package/dist/core/events/v3/validate.d.ts.map +1 -1
  77. package/dist/core/events/v3/validate.js +36 -1
  78. package/dist/core/governor/index.d.ts +2 -1
  79. package/dist/core/governor/index.d.ts.map +1 -1
  80. package/dist/core/governor/index.js +2 -1
  81. package/dist/core/governor/service-read.d.ts +3 -10
  82. package/dist/core/governor/service-read.d.ts.map +1 -1
  83. package/dist/core/governor/service-read.js +10 -94
  84. package/dist/core/governor/service.d.ts +3 -7
  85. package/dist/core/governor/service.d.ts.map +1 -1
  86. package/dist/core/governor/service.js +17 -182
  87. package/dist/core/hooks/adapter/native-parent.d.ts +23 -0
  88. package/dist/core/hooks/adapter/native-parent.d.ts.map +1 -0
  89. package/dist/core/hooks/adapter/native-parent.js +91 -0
  90. package/dist/core/hooks/cli.js +79 -4
  91. package/dist/core/hooks/resolve/owner.d.ts +0 -11
  92. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  93. package/dist/core/hooks/resolve/owner.js +2 -25
  94. package/dist/core/hooks/shell-admission.d.ts +84 -0
  95. package/dist/core/hooks/shell-admission.d.ts.map +1 -0
  96. package/dist/core/hooks/shell-admission.js +622 -0
  97. package/dist/core/hooks/shell-commit-tree.d.ts +2 -0
  98. package/dist/core/hooks/shell-commit-tree.d.ts.map +1 -0
  99. package/dist/core/hooks/shell-commit-tree.js +104 -0
  100. package/dist/core/inbox/contract.d.ts +12 -0
  101. package/dist/core/inbox/contract.d.ts.map +1 -1
  102. package/dist/core/inbox/contract.js +16 -0
  103. package/dist/core/inbox/peer-messages.d.ts +41 -16
  104. package/dist/core/inbox/peer-messages.d.ts.map +1 -1
  105. package/dist/core/inbox/peer-messages.js +372 -15
  106. package/dist/core/inbox/service.d.ts +8 -1
  107. package/dist/core/inbox/service.d.ts.map +1 -1
  108. package/dist/core/inbox/service.js +83 -3
  109. package/dist/core/inbox/targets.d.ts +17 -3
  110. package/dist/core/inbox/targets.d.ts.map +1 -1
  111. package/dist/core/inbox/targets.js +77 -2
  112. package/dist/core/presence/git.d.ts +18 -12
  113. package/dist/core/presence/git.d.ts.map +1 -1
  114. package/dist/core/presence/git.js +32 -38
  115. package/dist/core/presence/index.d.ts +2 -2
  116. package/dist/core/presence/index.d.ts.map +1 -1
  117. package/dist/core/presence/index.js +8 -6
  118. package/dist/core/presence/mail.d.ts +133 -0
  119. package/dist/core/presence/mail.d.ts.map +1 -0
  120. package/dist/core/presence/mail.js +464 -0
  121. package/dist/core/resources/sampler.js +2 -2
  122. package/dist/core/resources/service-status.d.ts +2 -14
  123. package/dist/core/resources/service-status.d.ts.map +1 -1
  124. package/dist/core/resources/service-status.js +3 -23
  125. package/dist/core/resources/service.d.ts +3 -7
  126. package/dist/core/resources/service.d.ts.map +1 -1
  127. package/dist/core/resources/service.js +12 -101
  128. package/dist/core/resources/status.d.ts.map +1 -1
  129. package/dist/core/resources/status.js +3 -2
  130. package/dist/core/semantic/contract.d.ts +5 -5
  131. package/dist/core/semantic/service.d.ts.map +1 -1
  132. package/dist/core/semantic/service.js +9 -2
  133. package/dist/core/servers/index.d.ts +0 -2
  134. package/dist/core/servers/index.d.ts.map +1 -1
  135. package/dist/core/servers/index.js +3 -18
  136. package/dist/core/storage/modes.d.ts +4 -0
  137. package/dist/core/storage/modes.d.ts.map +1 -1
  138. package/dist/core/storage/modes.js +4 -0
  139. package/dist/core/storage/segments.d.ts.map +1 -1
  140. package/dist/core/storage/segments.js +5 -4
  141. package/dist/core/supervisor/service.d.ts +4 -7
  142. package/dist/core/supervisor/service.d.ts.map +1 -1
  143. package/dist/core/supervisor/service.js +23 -122
  144. package/dist/core/supervisor/services.js +2 -2
  145. package/dist/core/supervisor/status.d.ts +3 -17
  146. package/dist/core/supervisor/status.d.ts.map +1 -1
  147. package/dist/core/supervisor/status.js +7 -70
  148. package/dist/core/workflow/engine.js +2 -1
  149. package/dist/core/workflow/index.d.ts +3 -4
  150. package/dist/core/workflow/index.d.ts.map +1 -1
  151. package/dist/core/workflow/index.js +2 -2
  152. package/dist/core/workflow/proof.d.ts.map +1 -1
  153. package/dist/core/workflow/proof.js +30 -74
  154. package/dist/core/workflow/resume-lock.d.ts +17 -0
  155. package/dist/core/workflow/resume-lock.d.ts.map +1 -0
  156. package/dist/core/workflow/resume-lock.js +158 -0
  157. package/dist/core/workflow/run-state.d.ts +0 -21
  158. package/dist/core/workflow/run-state.d.ts.map +1 -1
  159. package/dist/core/workflow/run-state.js +3 -174
  160. package/dist/core/workflow/workspaces/leases.d.ts +4 -0
  161. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  162. package/dist/core/workflow/workspaces/leases.js +1 -1
  163. package/dist/core/workflow/workspaces/local-git.js +91 -210
  164. package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
  165. package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
  166. package/dist/core/workflow/workspaces/ownership.js +3 -130
  167. package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
  168. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  169. package/dist/core/workflow/workspaces/reclaim.js +0 -4
  170. package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
  171. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  172. package/dist/core/workflow/workspaces/recovery.js +1 -1
  173. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  174. package/dist/core/workflow/workspaces/state.js +1 -4
  175. package/dist/lib/admission-history.d.ts +96 -0
  176. package/dist/lib/admission-history.d.ts.map +1 -0
  177. package/dist/lib/admission-history.js +196 -0
  178. package/dist/lib/admission.d.ts +56 -7
  179. package/dist/lib/admission.d.ts.map +1 -1
  180. package/dist/lib/admission.js +161 -19
  181. package/dist/lib/browser/client.d.ts +18 -0
  182. package/dist/lib/browser/client.d.ts.map +1 -1
  183. package/dist/lib/browser/client.js +100 -12
  184. package/dist/lib/browser/content-checks.d.ts +3 -1
  185. package/dist/lib/browser/content-checks.d.ts.map +1 -1
  186. package/dist/lib/browser/content-checks.js +10 -2
  187. package/dist/lib/browser/critique.d.ts +32 -0
  188. package/dist/lib/browser/critique.d.ts.map +1 -1
  189. package/dist/lib/browser/critique.js +72 -3
  190. package/dist/lib/browser/geometry.d.ts +2 -0
  191. package/dist/lib/browser/geometry.d.ts.map +1 -1
  192. package/dist/lib/browser/geometry.js +61 -15
  193. package/dist/lib/browser/index.d.ts +1 -1
  194. package/dist/lib/browser/index.d.ts.map +1 -1
  195. package/dist/lib/browser/index.js +1 -1
  196. package/dist/lib/browser/page-review-contracts.d.ts +6 -1
  197. package/dist/lib/browser/page-review-contracts.d.ts.map +1 -1
  198. package/dist/lib/browser/page-review-contracts.js +6 -1
  199. package/dist/lib/browser/page-review-judge.d.ts +3 -1
  200. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  201. package/dist/lib/browser/page-review-judge.js +41 -11
  202. package/dist/lib/browser/page-review-pack.d.ts +3 -0
  203. package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
  204. package/dist/lib/browser/page-review-pack.js +5 -2
  205. package/dist/lib/browser/qa-run-contracts.d.ts +11 -0
  206. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  207. package/dist/lib/browser/qa-run-contracts.js +11 -4
  208. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  209. package/dist/lib/browser/qa-run.js +73 -16
  210. package/dist/lib/browser/runts.d.ts +4 -2
  211. package/dist/lib/browser/runts.d.ts.map +1 -1
  212. package/dist/lib/browser/runts.js +84 -33
  213. package/dist/lib/browser/windows-chrome.d.ts +14 -0
  214. package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
  215. package/dist/lib/browser/windows-chrome.js +42 -0
  216. package/dist/lib/browser/windows-login-sync.d.ts +110 -0
  217. package/dist/lib/browser/windows-login-sync.d.ts.map +1 -0
  218. package/dist/lib/browser/windows-login-sync.js +171 -0
  219. package/dist/lib/cookies/client.d.ts +30 -5
  220. package/dist/lib/cookies/client.d.ts.map +1 -1
  221. package/dist/lib/cookies/client.js +55 -10
  222. package/dist/lib/cookies/index.d.ts +1 -1
  223. package/dist/lib/cookies/index.d.ts.map +1 -1
  224. package/dist/lib/cookies/index.js +1 -1
  225. package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
  226. package/dist/lib/docs-metadata-sync.js +41 -2
  227. package/dist/lib/durable-job.d.ts +8 -0
  228. package/dist/lib/durable-job.d.ts.map +1 -1
  229. package/dist/lib/durable-job.js +10 -0
  230. package/dist/lib/http/client.d.ts +9 -8
  231. package/dist/lib/http/client.d.ts.map +1 -1
  232. package/dist/lib/http/client.js +44 -2
  233. package/dist/lib/instructions/templates.js +4 -4
  234. package/package.json +2 -2
  235. package/src/commander.ts +13 -0
  236. package/src/commands/admission.ts +272 -29
  237. package/src/commands/agents.ts +132 -27
  238. package/src/commands/browse.ts +208 -6
  239. package/src/commands/fetch.ts +1 -0
  240. package/src/commands/ledger-v3.ts +8 -2
  241. package/src/commands/presence.ts +49 -0
  242. package/src/commands/qa-run.ts +4 -3
  243. package/src/commands/review-pack.ts +41 -5
  244. package/src/commands/workflow.ts +8 -58
  245. package/src/core/agents/coord-client.ts +0 -20
  246. package/src/core/agents/index.ts +6 -0
  247. package/src/core/agents/live-authority-v3.ts +11 -4
  248. package/src/core/agents/render/session-context.ts +1 -7
  249. package/src/core/agents/session-events.ts +18 -3
  250. package/src/core/agents/state/heartbeat-reader.ts +3 -0
  251. package/src/core/agents/state/live-coordination-view.ts +13 -6
  252. package/src/core/agents/state/parent-reference-contract.ts +14 -0
  253. package/src/core/agents/state/parent-reference.ts +179 -0
  254. package/src/core/agents/state/pidmap.ts +2 -2
  255. package/src/core/agents/state/proc-start.ts +41 -69
  256. package/src/core/agents/state/service-process.ts +217 -0
  257. package/src/core/config.ts +85 -0
  258. package/src/core/context/index.ts +207 -272
  259. package/src/core/context/observation.ts +40 -9
  260. package/src/core/diagnostics/path-identity.ts +52 -6
  261. package/src/core/events/v3/canonical.ts +46 -13
  262. package/src/core/events/v3/index.ts +3 -0
  263. package/src/core/events/v3/producers/recorder.ts +151 -13
  264. package/src/core/events/v3/reader.ts +55 -11
  265. package/src/core/events/v3/runtime-build.ts +62 -0
  266. package/src/core/events/v3/validate.ts +42 -1
  267. package/src/core/governor/index.ts +1 -2
  268. package/src/core/governor/service-read.ts +15 -114
  269. package/src/core/governor/service.ts +36 -220
  270. package/src/core/hooks/adapter/native-parent.ts +109 -0
  271. package/src/core/hooks/cli.ts +83 -4
  272. package/src/core/hooks/resolve/owner.ts +2 -25
  273. package/src/core/hooks/shell-admission.ts +634 -0
  274. package/src/core/hooks/shell-commit-tree.ts +102 -0
  275. package/src/core/inbox/contract.ts +18 -0
  276. package/src/core/inbox/peer-messages.ts +501 -15
  277. package/src/core/inbox/service.ts +108 -3
  278. package/src/core/inbox/targets.ts +94 -3
  279. package/src/core/presence/git.ts +45 -37
  280. package/src/core/presence/index.ts +16 -8
  281. package/src/core/presence/mail.ts +620 -0
  282. package/src/core/resources/sampler.ts +3 -2
  283. package/src/core/resources/service-status.ts +6 -35
  284. package/src/core/resources/service.ts +23 -133
  285. package/src/core/resources/status.ts +3 -2
  286. package/src/core/semantic/service.ts +8 -2
  287. package/src/core/servers/index.ts +3 -23
  288. package/src/core/storage/modes.ts +6 -0
  289. package/src/core/storage/segments.ts +5 -4
  290. package/src/core/supervisor/service.ts +33 -155
  291. package/src/core/supervisor/services.ts +2 -2
  292. package/src/core/supervisor/status.ts +10 -85
  293. package/src/core/workflow/engine.ts +1 -1
  294. package/src/core/workflow/index.ts +2 -13
  295. package/src/core/workflow/proof.ts +35 -83
  296. package/src/core/workflow/resume-lock.ts +179 -0
  297. package/src/core/workflow/run-state.ts +3 -207
  298. package/src/core/workflow/workspaces/leases.ts +1 -1
  299. package/src/core/workflow/workspaces/local-git.ts +120 -239
  300. package/src/core/workflow/workspaces/ownership.ts +3 -158
  301. package/src/core/workflow/workspaces/reclaim.ts +3 -9
  302. package/src/core/workflow/workspaces/recovery.ts +1 -1
  303. package/src/core/workflow/workspaces/state.ts +1 -5
  304. package/src/lib/admission-history.ts +267 -0
  305. package/src/lib/admission.ts +217 -19
  306. package/src/lib/browser/client.ts +147 -35
  307. package/src/lib/browser/content-checks.ts +14 -3
  308. package/src/lib/browser/critique.ts +113 -3
  309. package/src/lib/browser/geometry.ts +63 -14
  310. package/src/lib/browser/index.ts +5 -0
  311. package/src/lib/browser/page-review-contracts.ts +6 -1
  312. package/src/lib/browser/page-review-judge.ts +58 -13
  313. package/src/lib/browser/page-review-pack.ts +13 -2
  314. package/src/lib/browser/qa-run-contracts.ts +30 -4
  315. package/src/lib/browser/qa-run.ts +78 -17
  316. package/src/lib/browser/runts.ts +85 -37
  317. package/src/lib/browser/windows-chrome.ts +50 -0
  318. package/src/lib/browser/windows-login-sync.ts +275 -0
  319. package/src/lib/cookies/client.ts +62 -10
  320. package/src/lib/cookies/index.ts +3 -0
  321. package/src/lib/docs-metadata-sync.ts +44 -2
  322. package/src/lib/durable-job.ts +15 -0
  323. package/src/lib/http/client.ts +63 -1
  324. package/src/lib/instructions/templates.ts +4 -4
  325. package/dist/core/agents/state/process-handle.d.ts +0 -26
  326. package/dist/core/agents/state/process-handle.d.ts.map +0 -1
  327. package/dist/core/agents/state/process-handle.js +0 -121
  328. package/dist/core/workflow/recovery-audit.d.ts +0 -30
  329. package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
  330. package/dist/core/workflow/recovery-audit.js +0 -116
  331. package/src/core/agents/state/process-handle.ts +0 -165
  332. package/src/core/workflow/recovery-audit.ts +0 -181
@@ -1,9 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
- import { hostname } from "node:os";
3
2
  import {
4
- type ProcessGenerationVerdict,
5
- verifyProcessGeneration,
6
- } from "../agents/state/proc-start.ts";
3
+ observeServiceProcess,
4
+ type ServiceProcessDependencies,
5
+ } from "../agents/state/service-process.ts";
7
6
  import {
8
7
  RESOURCE_SERVICE_STATUS_SCHEMA_VERSION,
9
8
  type ResourceServiceStatus,
@@ -13,15 +12,10 @@ import { readResourceServiceRecord, resourcePaths } from "./storage.ts";
13
12
 
14
13
  const STALE_HEARTBEAT_MS = 15_000;
15
14
 
16
- export interface ResourceProcessDependencies {
17
- readToken?: (pid: number) => string | null;
18
- probePid?: (pid: number) => unknown;
19
- }
20
-
21
15
  export function readResourceServiceStatus(
22
16
  coordRoot: string,
23
17
  nowMs = Date.now(),
24
- dependencies: ResourceProcessDependencies = {},
18
+ dependencies: ServiceProcessDependencies = {},
25
19
  ): ResourceServiceStatus {
26
20
  const paths = resourcePaths(coordRoot);
27
21
  const record = readResourceServiceRecord(coordRoot);
@@ -37,13 +31,11 @@ export function readResourceServiceStatus(
37
31
  }
38
32
  const heartbeatAt = Date.parse(record.heartbeat_at);
39
33
  const stale = !Number.isFinite(heartbeatAt) || nowMs - heartbeatAt > STALE_HEARTBEAT_MS;
40
- const owner = resourceProcessLiveness(record, dependencies);
41
- const liveness =
42
- owner.generation === "same" && record.state === "stopped" ? "stopped" : owner.liveness;
34
+ const { generation, liveness } = observeServiceProcess(record, dependencies);
43
35
  return {
44
36
  running: liveness === "running" && !stale,
45
37
  stale,
46
- generation: owner.generation,
38
+ generation,
47
39
  liveness,
48
40
  record,
49
41
  status_path: paths.service,
@@ -62,24 +54,3 @@ function validRecord(
62
54
  typeof value.heartbeat_at === "string"
63
55
  );
64
56
  }
65
-
66
- export function resourceProcessLiveness(
67
- owner: { pid: number; host: string; start_token?: string },
68
- dependencies: ResourceProcessDependencies = {},
69
- ): { generation: ProcessGenerationVerdict; liveness: ResourceServiceStatus["liveness"] } {
70
- if (owner.host !== hostname()) return { generation: "unknown", liveness: "unknown" };
71
- try {
72
- (dependencies.probePid ?? ((pid) => process.kill(pid, 0)))(owner.pid);
73
- } catch (error) {
74
- return {
75
- generation: "unknown",
76
- liveness: (error as NodeJS.ErrnoException).code === "ESRCH" ? "stopped" : "unknown",
77
- };
78
- }
79
- const generation = verifyProcessGeneration(owner.pid, owner.start_token, dependencies.readToken);
80
- return {
81
- generation,
82
- liveness:
83
- generation === "same" ? "running" : generation === "different" ? "stopped" : "unknown",
84
- };
85
- }
@@ -1,25 +1,17 @@
1
- import { randomUUID } from "node:crypto";
2
- import {
3
- closeSync,
4
- existsSync,
5
- mkdirSync,
6
- openSync,
7
- readFileSync,
8
- rmSync,
9
- unlinkSync,
10
- writeFileSync,
11
- } from "node:fs";
1
+ import { existsSync, rmSync } from "node:fs";
12
2
  import { hostname } from "node:os";
13
3
  import { resolve } from "node:path";
4
+ import { processStartToken } from "../agents/state/proc-start.ts";
14
5
  import {
15
- type ProcessGenerationSignalDependencies,
16
- type ProcessGenerationVerdict,
17
- processStartToken,
18
- signalIfSameGeneration,
19
- } from "../agents/state/proc-start.ts";
6
+ acquireServiceLease,
7
+ requestServiceStop,
8
+ type ServiceLiveness,
9
+ type ServiceProcessDependencies,
10
+ type ServiceStopFields,
11
+ serviceStartRefusal,
12
+ } from "../agents/state/service-process.ts";
20
13
  import { resolveBinName } from "../config.ts";
21
14
  import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
22
- import { stateDirMode, stateFileMode } from "../storage/modes.ts";
23
15
  import {
24
16
  RESOURCE_SERVICE_STATUS_SCHEMA_VERSION,
25
17
  type ResourceSamplerState,
@@ -27,25 +19,13 @@ import {
27
19
  type ResourceServiceStatusRecord,
28
20
  } from "./contract.ts";
29
21
  import { sampleResources } from "./sampler.ts";
30
- import {
31
- type ResourceProcessDependencies,
32
- readResourceServiceStatus,
33
- resourceProcessLiveness,
34
- } from "./service-status.ts";
22
+ import { readResourceServiceStatus } from "./service-status.ts";
35
23
  import { resourcePaths, writePrivateJsonAtomic } from "./storage.ts";
36
24
 
37
25
  export const RESOURCE_SERVICE_DEFAULT_INTERVAL_MS = 2_000;
38
26
  const RESOURCE_SERVICE_MIN_INTERVAL_MS = 500;
39
27
  const RESOURCE_SERVICE_MAX_INTERVAL_MS = 60_000;
40
28
 
41
- interface ResourceServiceLease {
42
- pid: number;
43
- start_token?: string;
44
- host: string;
45
- nonce: string;
46
- created_at: string;
47
- }
48
-
49
29
  export interface RunResourceServiceInput {
50
30
  coordRoot: string;
51
31
  intervalMs?: number;
@@ -55,37 +35,15 @@ export interface RunResourceServiceInput {
55
35
  wait?: (milliseconds: number) => Promise<void>;
56
36
  }
57
37
 
58
- export interface ResourceServiceStopResult extends ResourceServiceStatus {
59
- signal_sent: boolean;
60
- signal_verdict: ProcessGenerationVerdict;
61
- }
38
+ export type ResourceServiceStopResult = ResourceServiceStatus & ServiceStopFields;
62
39
 
63
40
  export function requestResourceServiceStop(
64
41
  coordRootRaw: string,
65
- dependencies: ResourceProcessDependencies & ProcessGenerationSignalDependencies = {},
42
+ dependencies: ServiceProcessDependencies = {},
66
43
  ): ResourceServiceStopResult {
67
44
  const coordRoot = resolve(coordRootRaw);
68
- const paths = resourcePaths(coordRoot);
69
- const status = readResourceServiceStatus(coordRoot, Date.now(), dependencies);
70
- mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
71
- writePrivateJsonAtomic(paths.stop, {
72
- requested_at: new Date().toISOString(),
73
- requested_by_pid: process.pid,
74
- });
75
- const signal =
76
- status.liveness === "running" && status.record?.host === hostname()
77
- ? signalIfSameGeneration(
78
- status.record.pid,
79
- status.record.start_token,
80
- "SIGTERM",
81
- dependencies,
82
- )
83
- : { sent: false, verdict: status.generation };
84
- return {
85
- ...readResourceServiceStatus(coordRoot, Date.now(), dependencies),
86
- signal_sent: signal.sent,
87
- signal_verdict: signal.verdict,
88
- };
45
+ const read = () => readResourceServiceStatus(coordRoot, Date.now(), dependencies);
46
+ return requestServiceStop(read(), read, resourcePaths(coordRoot).stop, dependencies);
89
47
  }
90
48
 
91
49
  export async function runResourceService(
@@ -93,15 +51,13 @@ export async function runResourceService(
93
51
  ): Promise<ResourceServiceStatusRecord> {
94
52
  const coordRoot = resolve(input.coordRoot);
95
53
  const current = readResourceServiceStatus(coordRoot);
96
- if (current.liveness === "unknown") throw unknownResourceOwnerError();
97
- if (current.liveness === "running")
98
- throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
54
+ if (current.liveness !== "stopped") throw startRefusal(current.record?.pid, current.liveness);
99
55
  const paths = resourcePaths(coordRoot);
100
56
  const intervalMs = normalizeInterval(input.intervalMs);
101
57
  const now = input.now ?? (() => new Date());
102
58
  const sample = input.sample ?? sampleResources;
103
59
  const wait = input.wait ?? delay;
104
- const lease = acquireLease(coordRoot, now());
60
+ const lease = acquireServiceLease(paths.lease, startRefusal);
105
61
  rmSync(paths.stop, { force: true });
106
62
  const startedAt = now().toISOString();
107
63
  const daemonStartToken = processStartToken(process.pid);
@@ -178,84 +134,18 @@ export async function runResourceService(
178
134
  try {
179
135
  await closeProcessLoggers();
180
136
  } finally {
181
- releaseLease(paths.lease, lease.nonce);
137
+ lease.release();
182
138
  }
183
139
  }
184
140
  return status;
185
141
  }
186
142
 
187
- function acquireLease(coordRoot: string, now: Date): ResourceServiceLease {
188
- const paths = resourcePaths(coordRoot);
189
- mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
190
- for (let attempt = 0; attempt < 2; attempt++) {
191
- const daemonStartToken = processStartToken(process.pid);
192
- const lease: ResourceServiceLease = {
193
- pid: process.pid,
194
- ...(daemonStartToken ? { start_token: daemonStartToken } : {}),
195
- host: hostname(),
196
- nonce: randomUUID(),
197
- created_at: now.toISOString(),
198
- };
199
- try {
200
- const fd = openSync(paths.lease, "wx", stateFileMode());
201
- try {
202
- writeFileSync(fd, `${JSON.stringify(lease)}\n`, "utf8");
203
- } finally {
204
- closeSync(fd);
205
- }
206
- return lease;
207
- } catch (error) {
208
- if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
209
- const existing = readLease(paths.lease);
210
- const liveness = existing ? leaseIsLive(existing) : "unknown";
211
- if (liveness === "unknown") throw unknownResourceOwnerError();
212
- if (existing && liveness === "running") {
213
- throw new Error(`resource observer lease is held by pid ${existing.pid}`);
214
- }
215
- try {
216
- unlinkSync(paths.lease);
217
- } catch {
218
- // A concurrent starter may have replaced or removed the stale lease.
219
- }
220
- }
221
- }
222
- throw new Error("resource observer could not acquire its singleton lease");
223
- }
224
-
225
- function releaseLease(path: string, nonce: string): void {
226
- const current = readLease(path);
227
- if (current?.nonce !== nonce) return;
228
- try {
229
- unlinkSync(path);
230
- } catch {
231
- // A missing lease is already released.
232
- }
233
- }
234
-
235
- function readLease(path: string): ResourceServiceLease | undefined {
236
- try {
237
- const value = JSON.parse(readFileSync(path, "utf8")) as ResourceServiceLease;
238
- return value &&
239
- Number.isSafeInteger(value.pid) &&
240
- value.pid > 0 &&
241
- typeof value.host === "string" &&
242
- value.host.length > 0 &&
243
- typeof value.nonce === "string" &&
244
- value.nonce.length > 0
245
- ? value
246
- : undefined;
247
- } catch {
248
- return undefined;
249
- }
250
- }
251
-
252
- function leaseIsLive(lease: ResourceServiceLease): ResourceServiceStatus["liveness"] {
253
- return resourceProcessLiveness(lease).liveness;
254
- }
255
-
256
- function unknownResourceOwnerError(): Error {
257
- return new Error(
258
- `resource observer process identity is unknown; run ${resolveBinName()} supervisor stop to write the durable stop request before starting again`,
143
+ function startRefusal(pid: number | undefined, liveness: ServiceLiveness): Error {
144
+ return serviceStartRefusal(
145
+ "resource observer",
146
+ `${resolveBinName()} supervisor stop`,
147
+ pid,
148
+ liveness,
259
149
  );
260
150
  }
261
151
 
@@ -6,13 +6,14 @@ import {
6
6
  type PressureAssessment,
7
7
  type PressureHysteresisState,
8
8
  } from "../diagnostics/contract.ts";
9
+ import { installedHarneryBuild } from "../events/v3/runtime-build.ts";
9
10
  import {
10
11
  SUPERVISOR_PRESSURE_SCHEMA_VERSION,
11
12
  type SupervisorCapability,
12
13
  type SupervisorPressureRecord,
13
14
  type SupervisorStatus,
14
15
  } from "../supervisor/contract.ts";
15
- import { installedSupervisorBuild, readSupervisorStatus } from "../supervisor/status.ts";
16
+ import { readSupervisorStatus } from "../supervisor/status.ts";
16
17
  import { supervisorPaths } from "../supervisor/storage.ts";
17
18
  import {
18
19
  RESOURCE_SNAPSHOT_SCHEMA_VERSION,
@@ -465,7 +466,7 @@ function unknownWriterState(): ResourceStatus["writer"] {
465
466
  generation: "unknown",
466
467
  liveness: "unknown",
467
468
  running_build: null,
468
- installed_build: installedSupervisorBuild(),
469
+ installed_build: installedHarneryBuild(),
469
470
  restart_needed: true,
470
471
  build_status: "running build unknown; restart needed",
471
472
  };
@@ -225,13 +225,19 @@ export async function ensureSemanticServiceRunning(
225
225
 
226
226
  export function requestSemanticServiceStop(coordRootRaw: string): SemanticServiceStatus {
227
227
  const coordRoot = resolve(coordRootRaw);
228
+ const status = readSemanticServiceStatus(coordRoot);
228
229
  mkdirSync(semanticPaths(coordRoot).root, { recursive: true, mode: stateDirMode() });
229
230
  writePrivateJsonAtomic(semanticPaths(coordRoot).stop, {
230
231
  requested_at: new Date().toISOString(),
231
232
  requested_by_pid: process.pid,
232
233
  });
233
- // This record has no generation witness. The durable request is the stop
234
- // authority; a numeric PID cannot authorize an additional signal.
234
+ if (status.running && status.record?.host === hostname()) {
235
+ try {
236
+ process.kill(status.record.pid, "SIGTERM");
237
+ } catch {
238
+ // The durable stop request remains for a racing or restarted daemon.
239
+ }
240
+ }
235
241
  return readSemanticServiceStatus(coordRoot);
236
242
  }
237
243
 
@@ -598,26 +598,6 @@ export interface StopResult {
598
598
 
599
599
  export interface ServerProcessDependencies extends ProcessGenerationSignalDependencies {
600
600
  probePid?: (pid: number) => boolean;
601
- /** Numeric signal, used for the process group and in tests. */
602
- kill?: (pid: number, signal: NodeJS.Signals) => void;
603
- }
604
-
605
- /** Recheck the generation, then signal. A supplied `kill` replaces the handle path. */
606
- function signalVerified(
607
- pid: number,
608
- token: string | undefined,
609
- name: NodeJS.Signals,
610
- dependencies: ServerProcessDependencies,
611
- ): { sent: boolean; verdict: ProcessGenerationVerdict } {
612
- if (!dependencies.kill) return signalIfSameGeneration(pid, token, name, dependencies);
613
- const verdict = verifyProcessGeneration(pid, token, dependencies.readToken);
614
- if (verdict !== "same") return { sent: false, verdict };
615
- try {
616
- dependencies.kill(pid, name);
617
- return { sent: true, verdict };
618
- } catch {
619
- return { sent: false, verdict };
620
- }
621
601
  }
622
602
 
623
603
  /**
@@ -680,7 +660,7 @@ export async function stopServer(
680
660
  const result =
681
661
  target.pid === record.pid
682
662
  ? signalServerLeader(target.pid, target.token, name, dependencies)
683
- : signalVerified(target.pid, target.token, name, dependencies);
663
+ : signalIfSameGeneration(target.pid, target.token, name, dependencies);
684
664
  if (result.verdict !== "same") return refuse(target.pid, result.verdict);
685
665
  }
686
666
  const observed = await waitForExit(targets, name === "SIGTERM" ? graceMs : 2_000, dependencies);
@@ -706,13 +686,13 @@ function signalServerLeader(
706
686
  // The leader's generation authorizes its group. A failed group signal must
707
687
  // not reuse that observation for the direct-PID fallback.
708
688
  const kill = dependencies.kill ?? process.kill;
709
- const group = signalVerified(pid, token, name, {
689
+ const group = signalIfSameGeneration(pid, token, name, {
710
690
  readToken: dependencies.readToken,
711
691
  kill: (leader, signal) => kill(-leader, signal),
712
692
  });
713
693
  return group.sent || group.verdict !== "same"
714
694
  ? group
715
- : signalVerified(pid, token, name, dependencies);
695
+ : signalIfSameGeneration(pid, token, name, dependencies);
716
696
  }
717
697
 
718
698
  async function waitForExit(
@@ -26,6 +26,12 @@ export function stateSharing(): "private" | "group" {
26
26
  return resolved;
27
27
  }
28
28
 
29
+ /** Directory mode for personal state in the user's home, which never follows project sharing. */
30
+ export const PERSONAL_DIR_MODE = 0o700;
31
+
32
+ /** File mode for personal state in the user's home, such as a per-install secret key. */
33
+ export const PERSONAL_FILE_MODE = 0o600;
34
+
29
35
  export function resetStorageSharing(): void {
30
36
  resolved = undefined;
31
37
  }
@@ -150,16 +150,17 @@ export class FileSegmentSink {
150
150
  records: readonly Buffer[],
151
151
  metrics: HarneryLogMetricsDelta = {},
152
152
  durable = false,
153
- now = new Date(),
153
+ now?: Date,
154
154
  ): Promise<SegmentAppendResult> {
155
+ const requestedAt = now ?? new Date();
155
156
  if (records.length === 0)
156
157
  return { appended_records: 0, appended_bytes: 0, rotated: false, synced: false };
157
158
  return withFamilyLease(this.#options.directory, this.#options, async () => {
158
159
  const directory = ensurePrivateDirectory(this.#options.directory);
159
160
  const active = join(this.#options.directory, "active.jsonl");
160
161
  let rotated = false;
161
- if (existsSync(active) && shouldRotate(active, records, this.#options, now)) {
162
- sealActive(active, this.#options, now);
162
+ if (existsSync(active) && shouldRotate(active, records, this.#options, requestedAt)) {
163
+ sealActive(active, this.#options, now ?? new Date());
163
164
  rotated = true;
164
165
  }
165
166
  rejectExistingSymlink(active);
@@ -190,7 +191,7 @@ export class FileSegmentSink {
190
191
  rotations: (metrics.rotations ?? 0) + (rotated ? 1 : 0),
191
192
  synced: (metrics.synced ?? 0) + (durable ? records.length : 0),
192
193
  },
193
- now,
194
+ requestedAt,
194
195
  );
195
196
  return { appended_records: records.length, appended_bytes: bytes, rotated, synced: durable };
196
197
  });