@noopolis/daimon 0.2.0 → 0.2.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 (261) hide show
  1. package/README.md +6 -0
  2. package/dist/contracts/attentionContract.d.ts +25 -0
  3. package/dist/contracts/attentionContract.d.ts.map +1 -1
  4. package/dist/contracts/attentionContract.js +8 -1
  5. package/dist/contracts/grokBrokerTaskClock.d.ts +18 -0
  6. package/dist/contracts/grokBrokerTaskClock.d.ts.map +1 -0
  7. package/dist/contracts/grokBrokerTaskClock.js +16 -0
  8. package/dist/contracts/organizationRuntimeContract.d.ts +76 -0
  9. package/dist/contracts/organizationRuntimeContract.d.ts.map +1 -1
  10. package/dist/contracts/organizationRuntimeContract.js +2 -0
  11. package/dist/contracts/runtimeContractManifest.d.ts +222 -15
  12. package/dist/contracts/runtimeContractManifest.d.ts.map +1 -1
  13. package/dist/contracts/runtimeContractManifest.js +35 -10
  14. package/dist/contracts/shellEnvironmentPolicy.d.ts +3 -0
  15. package/dist/contracts/shellEnvironmentPolicy.d.ts.map +1 -0
  16. package/dist/contracts/shellEnvironmentPolicy.js +5 -0
  17. package/dist/contracts/wakeMomentContract.d.ts +61 -0
  18. package/dist/contracts/wakeMomentContract.d.ts.map +1 -0
  19. package/dist/contracts/wakeMomentContract.js +27 -0
  20. package/dist/core/types.d.ts +2 -0
  21. package/dist/core/types.d.ts.map +1 -1
  22. package/dist/examples/jungianPlayAgent.d.ts +1 -0
  23. package/dist/examples/jungianPlayAgent.d.ts.map +1 -1
  24. package/dist/examples/jungianPlayAgent.js +6 -2
  25. package/dist/observability/causalEvents.d.ts +3 -3
  26. package/dist/observability/causalEvents.d.ts.map +1 -1
  27. package/dist/observability/causalEvents.js +4 -4
  28. package/dist/pi/cliEngineSpawn.d.ts +2 -2
  29. package/dist/pi/cliEngineSpawn.d.ts.map +1 -1
  30. package/dist/pi/cliEngineSpawn.js +7 -2
  31. package/dist/pi/cliEnvironment.d.ts.map +1 -1
  32. package/dist/pi/cliEnvironment.js +2 -0
  33. package/dist/pi/cliMcpRegistration.d.ts +1 -1
  34. package/dist/pi/cliMcpRegistration.d.ts.map +1 -1
  35. package/dist/pi/cliMcpRegistration.js +1 -1
  36. package/dist/pi/cliSession.d.ts +2 -0
  37. package/dist/pi/cliSession.d.ts.map +1 -1
  38. package/dist/pi/cliSession.js +3 -80
  39. package/dist/pi/cliSessionMcp.d.ts +9 -0
  40. package/dist/pi/cliSessionMcp.d.ts.map +1 -0
  41. package/dist/pi/cliSessionMcp.js +80 -0
  42. package/dist/pi/engineTaskClock.d.ts +9 -0
  43. package/dist/pi/engineTaskClock.d.ts.map +1 -0
  44. package/dist/pi/engineTaskClock.js +28 -0
  45. package/dist/pi/grokHomeMcpRegistration.d.ts.map +1 -1
  46. package/dist/pi/grokHomeMcpRegistration.js +2 -1
  47. package/dist/pi/grokSandbox.js +1 -1
  48. package/dist/pi/memoryClock.d.ts +25 -0
  49. package/dist/pi/memoryClock.d.ts.map +1 -0
  50. package/dist/pi/memoryClock.js +18 -0
  51. package/dist/pi/piAgentHandle.d.ts +3 -1
  52. package/dist/pi/piAgentHandle.d.ts.map +1 -1
  53. package/dist/pi/piAgentHandle.js +5 -2
  54. package/dist/pi/piHarness.d.ts +8 -2
  55. package/dist/pi/piHarness.d.ts.map +1 -1
  56. package/dist/pi/piHarness.js +28 -12
  57. package/dist/pi/taskClockProcess.d.ts +6 -0
  58. package/dist/pi/taskClockProcess.d.ts.map +1 -0
  59. package/dist/pi/taskClockProcess.js +8 -0
  60. package/dist/pi/turnCausal.d.ts +4 -2
  61. package/dist/pi/turnCausal.d.ts.map +1 -1
  62. package/dist/pi/turnCausal.js +4 -4
  63. package/dist/pi/wakeAcceptance.d.ts +1 -1
  64. package/dist/pi/wakeAcceptance.d.ts.map +1 -1
  65. package/dist/pi/wakeAcceptance.js +2 -2
  66. package/dist/runtime/agentSkillsDirectory.d.ts +25 -0
  67. package/dist/runtime/agentSkillsDirectory.d.ts.map +1 -0
  68. package/dist/runtime/agentSkillsDirectory.js +101 -0
  69. package/dist/runtime/agySubscriptionBootstrap.js +1 -1
  70. package/dist/runtime/agySubscriptionRealm.js +1 -1
  71. package/dist/runtime/attention.d.ts +5 -0
  72. package/dist/runtime/attention.d.ts.map +1 -1
  73. package/dist/runtime/attention.js +14 -1
  74. package/dist/runtime/attentionDispatcher.d.ts +14 -2
  75. package/dist/runtime/attentionDispatcher.d.ts.map +1 -1
  76. package/dist/runtime/attentionDispatcher.js +127 -33
  77. package/dist/runtime/cli.js +5 -0
  78. package/dist/runtime/contract-manifest.json +1 -1
  79. package/dist/runtime/contract-manifest.sha256 +1 -1
  80. package/dist/runtime/engineBrokerCapabilities.d.ts +10 -0
  81. package/dist/runtime/engineBrokerCapabilities.d.ts.map +1 -1
  82. package/dist/runtime/engineBrokerCapabilities.js +10 -0
  83. package/dist/runtime/engineBrokerControlClient.d.ts +12 -1
  84. package/dist/runtime/engineBrokerControlClient.d.ts.map +1 -1
  85. package/dist/runtime/engineBrokerControlClient.js +139 -43
  86. package/dist/runtime/engineBrokerCredentialJournal.d.ts +2 -0
  87. package/dist/runtime/engineBrokerCredentialJournal.d.ts.map +1 -1
  88. package/dist/runtime/engineBrokerCredentialJournal.js +9 -0
  89. package/dist/runtime/engineBrokerInferenceProtocol.d.ts +3 -2
  90. package/dist/runtime/engineBrokerInferenceProtocol.d.ts.map +1 -1
  91. package/dist/runtime/engineBrokerInferenceProtocol.js +4 -4
  92. package/dist/runtime/engineBrokerMcpCallLog.d.ts +11 -0
  93. package/dist/runtime/engineBrokerMcpCallLog.d.ts.map +1 -1
  94. package/dist/runtime/engineBrokerMcpCallLog.js +13 -1
  95. package/dist/runtime/engineBrokerMcpFacade.d.ts +5 -2
  96. package/dist/runtime/engineBrokerMcpFacade.d.ts.map +1 -1
  97. package/dist/runtime/engineBrokerMcpFacade.js +5 -2
  98. package/dist/runtime/engineBrokerNativeClient.js +2 -2
  99. package/dist/runtime/engineBrokerProtocol.d.ts +42 -10
  100. package/dist/runtime/engineBrokerProtocol.d.ts.map +1 -1
  101. package/dist/runtime/engineBrokerProtocol.js +97 -14
  102. package/dist/runtime/engineBrokerSealLedger.d.ts.map +1 -1
  103. package/dist/runtime/engineBrokerSealLedger.js +9 -0
  104. package/dist/runtime/engineBrokerService.d.ts +8 -2
  105. package/dist/runtime/engineBrokerService.d.ts.map +1 -1
  106. package/dist/runtime/engineBrokerService.js +95 -37
  107. package/dist/runtime/engineBrokerServiceCli.js +1 -1
  108. package/dist/runtime/engineBrokerServiceConfig.d.ts +15 -2
  109. package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -1
  110. package/dist/runtime/engineBrokerServiceConfig.js +53 -11
  111. package/dist/runtime/engineBrokerTurnRegistry.d.ts +1 -1
  112. package/dist/runtime/engineBrokerTurnRegistry.d.ts.map +1 -1
  113. package/dist/runtime/engineBrokerTurnRegistry.js +7 -6
  114. package/dist/runtime/engineDispatcher.d.ts +2 -0
  115. package/dist/runtime/engineDispatcher.d.ts.map +1 -1
  116. package/dist/runtime/engineDispatcher.js +14 -2
  117. package/dist/runtime/engineReadiness.js +1 -1
  118. package/dist/runtime/grokBrokerCredentialAuthority.d.ts +1 -0
  119. package/dist/runtime/grokBrokerCredentialAuthority.d.ts.map +1 -1
  120. package/dist/runtime/grokBrokerCredentialAuthority.js +48 -22
  121. package/dist/runtime/grokBrokerCredentialReader.d.ts +1 -0
  122. package/dist/runtime/grokBrokerCredentialReader.d.ts.map +1 -1
  123. package/dist/runtime/grokBrokerCredentialReader.js +2 -1
  124. package/dist/runtime/grokBrokerProjection.d.ts +26 -2
  125. package/dist/runtime/grokBrokerProjection.d.ts.map +1 -1
  126. package/dist/runtime/grokBrokerProjection.js +18 -7
  127. package/dist/runtime/grokBrokerProviderClock.d.ts +26 -0
  128. package/dist/runtime/grokBrokerProviderClock.d.ts.map +1 -0
  129. package/dist/runtime/grokBrokerProviderClock.js +133 -0
  130. package/dist/runtime/grokBrokerProxy.d.ts +17 -3
  131. package/dist/runtime/grokBrokerProxy.d.ts.map +1 -1
  132. package/dist/runtime/grokBrokerProxy.js +73 -9
  133. package/dist/runtime/grokBrokerProxyJson.d.ts +10 -0
  134. package/dist/runtime/grokBrokerProxyJson.d.ts.map +1 -0
  135. package/dist/runtime/grokBrokerProxyJson.js +31 -0
  136. package/dist/runtime/grokBrokerProxyRequest.d.ts.map +1 -1
  137. package/dist/runtime/grokBrokerProxyRequest.js +17 -6
  138. package/dist/runtime/grokBrokerRealmLease.js +1 -1
  139. package/dist/runtime/grokBrokerRefresh.js +1 -1
  140. package/dist/runtime/grokBrokerRunBudget.d.ts +20 -0
  141. package/dist/runtime/grokBrokerRunBudget.d.ts.map +1 -0
  142. package/dist/runtime/grokBrokerRunBudget.js +85 -0
  143. package/dist/runtime/grokBrokerSlot.d.ts +6 -0
  144. package/dist/runtime/grokBrokerSlot.d.ts.map +1 -0
  145. package/dist/runtime/grokBrokerSlot.js +21 -0
  146. package/dist/runtime/grokBrokerTaskClock.d.ts +32 -0
  147. package/dist/runtime/grokBrokerTaskClock.d.ts.map +1 -0
  148. package/dist/runtime/grokBrokerTaskClock.js +120 -0
  149. package/dist/runtime/grokBrokerTurnMeter.d.ts +55 -3
  150. package/dist/runtime/grokBrokerTurnMeter.d.ts.map +1 -1
  151. package/dist/runtime/grokBrokerTurnMeter.js +90 -4
  152. package/dist/runtime/grokBrokerWorkerConfig.d.ts +5 -2
  153. package/dist/runtime/grokBrokerWorkerConfig.d.ts.map +1 -1
  154. package/dist/runtime/grokBrokerWorkerConfig.js +22 -3
  155. package/dist/runtime/grokEngineBroker.d.ts +43 -3
  156. package/dist/runtime/grokEngineBroker.d.ts.map +1 -1
  157. package/dist/runtime/grokEngineBroker.js +159 -71
  158. package/dist/runtime/grokEngineBrokerTurn.d.ts +32 -8
  159. package/dist/runtime/grokEngineBrokerTurn.d.ts.map +1 -1
  160. package/dist/runtime/grokEngineBrokerTurn.js +204 -21
  161. package/dist/runtime/grokEngineBrokerTurnEnd.d.ts +88 -0
  162. package/dist/runtime/grokEngineBrokerTurnEnd.d.ts.map +1 -0
  163. package/dist/runtime/grokEngineBrokerTurnEnd.js +148 -0
  164. package/dist/runtime/grokInferenceProxy.d.ts.map +1 -1
  165. package/dist/runtime/grokInferenceProxy.js +3 -0
  166. package/dist/runtime/grokSubscriptionRealm.js +1 -1
  167. package/dist/runtime/grokWorkerAttestation.d.ts +12 -5
  168. package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -1
  169. package/dist/runtime/grokWorkerAttestation.js +5 -3
  170. package/dist/runtime/grokWorkerHomeAttestation.d.ts +51 -5
  171. package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -1
  172. package/dist/runtime/grokWorkerHomeAttestation.js +103 -8
  173. package/dist/runtime/grokWorkerSkills.d.ts +86 -0
  174. package/dist/runtime/grokWorkerSkills.d.ts.map +1 -0
  175. package/dist/runtime/grokWorkerSkills.js +135 -0
  176. package/dist/runtime/index.d.ts +10 -2
  177. package/dist/runtime/index.d.ts.map +1 -1
  178. package/dist/runtime/index.js +8 -1
  179. package/dist/runtime/native/daimon-engine-broker +0 -0
  180. package/dist/runtime/organizationRuntime.d.ts +3 -0
  181. package/dist/runtime/organizationRuntime.d.ts.map +1 -1
  182. package/dist/runtime/organizationRuntimeAdmission.d.ts +6 -0
  183. package/dist/runtime/organizationRuntimeAdmission.d.ts.map +1 -0
  184. package/dist/runtime/organizationRuntimeAdmission.js +22 -0
  185. package/dist/runtime/organizationRuntimeControl.d.ts +9 -0
  186. package/dist/runtime/organizationRuntimeControl.d.ts.map +1 -1
  187. package/dist/runtime/organizationRuntimeControl.js +72 -24
  188. package/dist/runtime/organizationRuntimeHost.d.ts.map +1 -1
  189. package/dist/runtime/organizationRuntimeHost.js +13 -4
  190. package/dist/runtime/organizationRuntimeParsing.d.ts.map +1 -1
  191. package/dist/runtime/organizationRuntimeParsing.js +4 -1
  192. package/dist/runtime/organizationRuntimeReadiness.d.ts.map +1 -1
  193. package/dist/runtime/organizationRuntimeReadiness.js +8 -0
  194. package/dist/runtime/organizationRuntimeRecording.d.ts +7 -0
  195. package/dist/runtime/organizationRuntimeRecording.d.ts.map +1 -0
  196. package/dist/runtime/organizationRuntimeRecording.js +58 -0
  197. package/dist/runtime/physicalReadiness.d.ts +12 -0
  198. package/dist/runtime/physicalReadiness.d.ts.map +1 -1
  199. package/dist/runtime/physicalReadiness.js +43 -9
  200. package/dist/runtime/productionAgentTools.d.ts.map +1 -1
  201. package/dist/runtime/productionAgentTools.js +13 -5
  202. package/dist/runtime/relocateRuntimeConfig.d.ts +14 -0
  203. package/dist/runtime/relocateRuntimeConfig.d.ts.map +1 -0
  204. package/dist/runtime/relocateRuntimeConfig.js +38 -0
  205. package/dist/runtime/schedule.d.ts.map +1 -1
  206. package/dist/runtime/schedule.js +12 -7
  207. package/dist/runtime/storeCoordination.d.ts +10 -0
  208. package/dist/runtime/storeCoordination.d.ts.map +1 -1
  209. package/dist/runtime/storeCoordination.js +23 -0
  210. package/dist/runtime/taskClock.d.ts +42 -0
  211. package/dist/runtime/taskClock.d.ts.map +1 -0
  212. package/dist/runtime/taskClock.js +102 -0
  213. package/dist/runtime/taskClockProcess.d.ts +17 -0
  214. package/dist/runtime/taskClockProcess.d.ts.map +1 -0
  215. package/dist/runtime/taskClockProcess.js +58 -0
  216. package/dist/runtime/turnRequestLedger.d.ts +0 -5
  217. package/dist/runtime/turnRequestLedger.d.ts.map +1 -1
  218. package/dist/runtime/turnRequestLedger.js +23 -21
  219. package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts +34 -0
  220. package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts.map +1 -0
  221. package/dist/runtime/wakeAcceptanceClaimDiagnostics.js +79 -0
  222. package/dist/runtime/wakeAcceptanceIndex.d.ts +55 -0
  223. package/dist/runtime/wakeAcceptanceIndex.d.ts.map +1 -0
  224. package/dist/runtime/wakeAcceptanceIndex.js +158 -0
  225. package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts +9 -1
  226. package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts.map +1 -1
  227. package/dist/runtime/wakeAcceptanceProcessIdentity.js +5 -1
  228. package/dist/runtime/wakeAcceptanceRecord.d.ts +2 -0
  229. package/dist/runtime/wakeAcceptanceRecord.d.ts.map +1 -1
  230. package/dist/runtime/wakeAcceptanceRecord.js +4 -2
  231. package/dist/runtime/wakeAcceptanceRetention.d.ts +25 -1
  232. package/dist/runtime/wakeAcceptanceRetention.d.ts.map +1 -1
  233. package/dist/runtime/wakeAcceptanceRetention.js +28 -2
  234. package/dist/runtime/wakeAcceptanceStore.d.ts +8 -8
  235. package/dist/runtime/wakeAcceptanceStore.d.ts.map +1 -1
  236. package/dist/runtime/wakeAcceptanceStore.js +66 -63
  237. package/dist/runtime/wakeFuse.d.ts +58 -3
  238. package/dist/runtime/wakeFuse.d.ts.map +1 -1
  239. package/dist/runtime/wakeFuse.js +106 -7
  240. package/dist/runtime/wakeMomentFs.d.ts +27 -0
  241. package/dist/runtime/wakeMomentFs.d.ts.map +1 -0
  242. package/dist/runtime/wakeMomentFs.js +223 -0
  243. package/dist/runtime/wakeMomentHistory.d.ts +26 -0
  244. package/dist/runtime/wakeMomentHistory.d.ts.map +1 -0
  245. package/dist/runtime/wakeMomentHistory.js +109 -0
  246. package/dist/runtime/wakeMomentIo.d.ts +22 -0
  247. package/dist/runtime/wakeMomentIo.d.ts.map +1 -0
  248. package/dist/runtime/wakeMomentIo.js +114 -0
  249. package/dist/runtime/wakeMomentRecorder.d.ts +33 -0
  250. package/dist/runtime/wakeMomentRecorder.d.ts.map +1 -0
  251. package/dist/runtime/wakeMomentRecorder.js +81 -0
  252. package/dist/runtime/wakeMomentRetention.d.ts +7 -0
  253. package/dist/runtime/wakeMomentRetention.d.ts.map +1 -0
  254. package/dist/runtime/wakeMomentRetention.js +143 -0
  255. package/dist/runtime/wakeMomentRows.d.ts +8 -0
  256. package/dist/runtime/wakeMomentRows.d.ts.map +1 -0
  257. package/dist/runtime/wakeMomentRows.js +66 -0
  258. package/dist/runtime/wakeMomentSnapshot.d.ts +56 -0
  259. package/dist/runtime/wakeMomentSnapshot.d.ts.map +1 -0
  260. package/dist/runtime/wakeMomentSnapshot.js +248 -0
  261. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawn, readyTaskClockEnvironment } from "./taskClockProcess.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { constants } from "node:fs";
4
4
  import { lstat, open, readdir, rename, unlink } from "node:fs/promises";
@@ -12,6 +12,7 @@ import { McpToolCallError, MCP_TOOL_RESULT_MAX_BYTES, renderMcpToolResult, repla
12
12
  import { ensureRuntimeHomeDirectory } from "./runtimeHomeLayout.js";
13
13
  import { capToolResult, resolveExemptToolNames, resolveToolResultMaxBytes, TOOL_OUTPUT_DIRECTORY_NAME } from "./toolResultSpill.js";
14
14
  import { cliChildEnvironment } from "../pi/cliEnvironment.js";
15
+ import { taskClockChildEnvironment, taskClockProcessEnvironment, taskClockTimestamp } from "./taskClock.js";
15
16
  const MAX_RESULT = 65_536;
16
17
  const TIMEOUT = 10_000;
17
18
  /**
@@ -25,6 +26,10 @@ const TIMEOUT = 10_000;
25
26
  */
26
27
  const DAIMON_ACTION_ID_PREFIX = "daimon-";
27
28
  export async function createProductionAgentTools(agent, wakeContext = {}) {
29
+ taskClockChildEnvironment();
30
+ for (const server of agent.mcp ?? [])
31
+ taskClockChildEnvironment(server.env);
32
+ readyTaskClockEnvironment(cliChildEnvironment([], agent.runtimeHomePath));
28
33
  await ensureRuntimeHomeDirectory(agent.runtimeHomePath, "tool-state");
29
34
  // Resolved once, at agent start: a malformed bound is a configuration error
30
35
  // that should refuse the agent, not a surprise thrown from the middle of a
@@ -202,7 +207,8 @@ function moltnetTool(agent, wakeContext) {
202
207
  const prior = await priorReceipt(agent, deliveryId);
203
208
  if (prior !== undefined) {
204
209
  wakeContext.spokeFor = wakeContext.current;
205
- return { content: [{ type: "text", text: JSON.stringify(prior) }], details: prior };
210
+ const visible = typeof prior.at === "string" ? { ...prior, at: taskClockTimestamp(prior.at) } : prior;
211
+ return { content: [{ type: "text", text: JSON.stringify(visible) }], details: visible };
206
212
  }
207
213
  const response = await machine(agent.moltnet.cliPath, agent.moltnet.configPath, input.network, { version: "moltnet.machine.v1", correlation_id: deliveryId, operation: "send_nudge", send_nudge: { delivery_id: deliveryId, target: { kind, id: target }, body: input.text } });
208
214
  const result = moltnetOperationResult(response, "send_nudge", "send");
@@ -219,10 +225,11 @@ function moltnetTool(agent, wakeContext) {
219
225
  };
220
226
  }
221
227
  async function connect(agent, server) {
222
- const client = new Client({ name: "daimon-production", version: "0.2.0" });
228
+ taskClockChildEnvironment(server.env);
229
+ const client = new Client({ name: "daimon-production", version: "0.2.2" });
223
230
  const headers = server.authSecretEnv === undefined ? {} : { authorization: `Bearer ${requiredSecret(server.authSecretEnv)}` };
224
231
  const transport = server.transport === "stdio"
225
- ? new StdioClientTransport({ command: server.command, args: [...server.args], env: stringEnvironment({ ...server.env, ...cliChildEnvironment([], agent.runtimeHomePath, { executablePath: server.command }) }) })
232
+ ? new StdioClientTransport({ command: server.command, args: [...server.args], env: stringEnvironment(readyTaskClockEnvironment({ ...server.env, ...cliChildEnvironment([], agent.runtimeHomePath, { executablePath: server.command }) })) })
226
233
  : server.transport === "sse" ? new SSEClientTransport(new URL(server.url), { requestInit: { headers } })
227
234
  : new StreamableHTTPClientTransport(new URL(server.url), { requestInit: { headers } });
228
235
  await client.connect(transport);
@@ -286,8 +293,9 @@ finally {
286
293
  * no longer cancel it.
287
294
  */
288
295
  async function machine(cli, config, network, request) {
296
+ const clockEnvironment = taskClockChildEnvironment();
289
297
  return await new Promise((resolve, reject) => {
290
- const child = spawn(cli, ["machine", "--config", config, "--network", network], { stdio: ["pipe", "pipe", "pipe"] });
298
+ const child = spawn(cli, ["machine", "--config", config, "--network", network], { stdio: ["pipe", "pipe", "pipe"], env: taskClockProcessEnvironment(process.env, clockEnvironment) });
291
299
  let output = "", error = "", settled = false;
292
300
  const settle = (action) => { if (settled)
293
301
  return; settled = true; clearTimeout(timer); action(); };
@@ -0,0 +1,14 @@
1
+ import { type OrganizationRuntimeConfig } from "./organizationRuntime.js";
2
+ export type RuntimeConfigLocation = Readonly<{
3
+ workspace: string;
4
+ home: string;
5
+ memory: string;
6
+ /** Both paths are explicit: runtime config declares no control-token file. */
7
+ controlToken: Readonly<{
8
+ from: string;
9
+ to: string;
10
+ }>;
11
+ }>;
12
+ /** Rebase one memory-backed agent's path prefixes, preserving instructions verbatim. */
13
+ export declare function relocateRuntimeConfig(config: OrganizationRuntimeConfig, location: RuntimeConfigLocation): OrganizationRuntimeConfig;
14
+ //# sourceMappingURL=relocateRuntimeConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relocateRuntimeConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/relocateRuntimeConfig.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAE1G,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,yBAAyB,EAAE,QAAQ,EAAE,qBAAqB,GAAG,yBAAyB,CA8BnI"}
@@ -0,0 +1,38 @@
1
+ import { posix } from "node:path";
2
+ import { parseOrganizationRuntimeConfig } from "./organizationRuntime.js";
3
+ /** Rebase one memory-backed agent's path prefixes, preserving instructions verbatim. */
4
+ export function relocateRuntimeConfig(config, location) {
5
+ const parsed = parseOrganizationRuntimeConfig(config);
6
+ const agent = parsed.agents[0];
7
+ if (parsed.agents.length !== 1 || !agent?.memory)
8
+ throw new TypeError("Relocation requires one memory-backed agent");
9
+ const mappings = [
10
+ [agent.workspacePath, location.workspace],
11
+ [agent.runtimeHomePath, location.home],
12
+ [agent.memory.runtimeHomePath, location.memory],
13
+ [location.controlToken.from, location.controlToken.to]
14
+ ];
15
+ for (const pair of mappings)
16
+ for (const value of pair) {
17
+ if (typeof value !== "string" || !posix.isAbsolute(value) || posix.normalize(value) !== value || value === "/" || value.endsWith("/") || value.includes("\\") || value.includes("\0")) {
18
+ throw new TypeError("Relocation paths must be canonical absolute POSIX paths other than root");
19
+ }
20
+ }
21
+ // Memory may live below home; the most specific source must own that subtree.
22
+ const ordered = [...mappings].sort(([a], [b]) => b.length - a.length);
23
+ if (new Set(ordered.map(([source]) => source)).size !== ordered.length)
24
+ throw new TypeError("Relocation source paths must be distinct");
25
+ const relocate = (value) => {
26
+ if (typeof value === "string") {
27
+ const mapping = ordered.find(([source]) => value === source || value.startsWith(`${source}/`));
28
+ return mapping ? mapping[1] + value.slice(mapping[0].length) : value;
29
+ }
30
+ if (Array.isArray(value))
31
+ return value.map(relocate);
32
+ if (value !== null && typeof value === "object") {
33
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, key === "instructions" ? item : relocate(item)]));
34
+ }
35
+ return value;
36
+ };
37
+ return parseOrganizationRuntimeConfig(relocate(parsed));
38
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/runtime/schedule.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAK5G,eAAO,MAAM,kBAAkB,aAAgB,CAAC;AAKhD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACxH,KAAK,cAAc,GAAG,OAAO,CAAC,2BAA2B,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAGjF,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAAC;AACrI,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAClD,8EAA8E;IAC9E,MAAM,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACjE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;IAC5D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,KAAK,IAAI,CAAC;CAC9F,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CA8F/F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,mBAAmB,CAGxG;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAU1H;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAA8E;AAEnI,iHAAiH;AACjH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAKjG"}
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/runtime/schedule.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAK5G,eAAO,MAAM,kBAAkB,aAAgB,CAAC;AAKhD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACxH,KAAK,cAAc,GAAG,OAAO,CAAC,2BAA2B,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAGjF,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAAC;AACrI,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,SAAS,8BAA8B,EAAE,CAAC;IAClD,8EAA8E;IAC9E,MAAM,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACjE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;IAC5D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,KAAK,IAAI,CAAC;CAC9F,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CA8F/F;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,mBAAmB,CAIxG;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAY1H;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAA8E;AAEnI,iHAAiH;AACjH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAKjG"}
@@ -1,3 +1,4 @@
1
+ import { assertTaskInstant, validTaskInstant } from "./taskClock.js";
1
2
  import { createHash, randomUUID } from "node:crypto";
2
3
  import { constants } from "node:fs";
3
4
  import { lstat, open, readFile, rename, unlink } from "node:fs/promises";
@@ -11,7 +12,7 @@ const MAX_SEARCH_CHECKS = 1_000_000;
11
12
  const CRON_FIELD_BOUNDS = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 7]];
12
13
  /** Schedules create durable pending occurrences; engine execution remains elsewhere. */
13
14
  export function createScheduleController(options) {
14
- const now = options.now ?? Date.now;
15
+ const now = () => assertTaskInstant((options.now ?? Date.now)());
15
16
  const random = options.random ?? Math.random;
16
17
  const setTimer = options.setTimer ?? ((callback, delay) => setTimeout(callback, delay));
17
18
  const clearTimer = options.clearTimer ?? clearTimeout;
@@ -131,6 +132,7 @@ export function createScheduleController(options) {
131
132
  };
132
133
  }
133
134
  export function occurrenceFor(agentId, schedule, at) {
135
+ assertTaskInstant(at);
134
136
  const local = schedule.kind === "cron" ? occurrenceForOffset(at, schedule.timezone) : new Date(at).toISOString();
135
137
  return { agentId, occurredAt: new Date(at).toISOString(), prompt: schedule.prompt, deliveryId: `schedule:${identity(agentId, schedule)}:${local}` };
136
138
  }
@@ -142,16 +144,19 @@ export function isCanonicalScheduleDeliveryId(value) {
142
144
  || /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}@(?:GMT(?:[+-]\d{2}:\d{2})?|UTC)$/u.test(occurrence);
143
145
  }
144
146
  export function nextOccurrence(agentId, schedule, anchor, from) {
147
+ assertTaskInstant(from);
148
+ if (anchor !== undefined)
149
+ assertTaskInstant(anchor);
145
150
  if (schedule.kind === "every") {
146
151
  const base = anchor ?? from;
147
- return base > from ? base : base + (Math.floor((from - base) / schedule.interval_ms) + 1) * schedule.interval_ms;
152
+ return assertTaskInstant(base > from ? base : base + (Math.floor((from - base) / schedule.interval_ms) + 1) * schedule.interval_ms);
148
153
  }
149
154
  const fields = parseCron(schedule.cron);
150
155
  if (!calendarPossible(fields))
151
156
  throw new Error(`schedule ${agentId} is impossible`);
152
157
  const found = searchCron(fields, schedule.timezone, from, from + SEARCH_HORIZON_MS, 1);
153
158
  if (found !== undefined)
154
- return found;
159
+ return assertTaskInstant(found);
155
160
  throw new Error(`schedule ${agentId} has no occurrence within the deterministic search bound`);
156
161
  }
157
162
  export function cronIsPossible(cron) { try {
@@ -170,7 +175,7 @@ export function jitterOffsetMs(schedule, random) {
170
175
  }
171
176
  function withFireTarget(entry, schedule, random) {
172
177
  const offset = jitterOffsetMs(schedule, random);
173
- return offset === 0 ? entry : { ...entry, fire_at_ms: entry.next_due_ms + offset };
178
+ return offset === 0 ? entry : { ...entry, fire_at_ms: assertTaskInstant(entry.next_due_ms + offset) };
174
179
  }
175
180
  function withoutPending(entry) {
176
181
  return { next_due_ms: entry.next_due_ms, ...(entry.fire_at_ms === undefined ? {} : { fire_at_ms: entry.fire_at_ms }) };
@@ -306,13 +311,13 @@ async function restore(file, allowed) {
306
311
  const keys = Object.keys(entry).sort().join();
307
312
  if (!["fire_at_ms,next_due_ms", "fire_at_ms,latest_pending,next_due_ms", "latest_pending,next_due_ms", "next_due_ms"].includes(keys))
308
313
  throw new Error("schedule state is invalid");
309
- if (!Number.isSafeInteger(entry.next_due_ms) || entry.next_due_ms < 0)
314
+ if (!validTaskInstant(entry.next_due_ms))
310
315
  throw new Error("schedule state is invalid");
311
316
  const nextDue = entry.next_due_ms;
312
317
  const fireAt = entry.fire_at_ms;
313
318
  if (fireAt !== undefined) {
314
319
  const maximumFireAt = nextDue + jitterOffsetMs(allowed.get(key).schedule, () => 0.999_999_999);
315
- if (!Number.isSafeInteger(fireAt) || fireAt < nextDue || fireAt > maximumFireAt)
320
+ if (!validTaskInstant(fireAt) || fireAt < nextDue || fireAt > maximumFireAt)
316
321
  throw new Error("schedule state is invalid");
317
322
  }
318
323
  const pending = entry.latest_pending === undefined ? undefined : parseOccurrence(entry.latest_pending);
@@ -338,7 +343,7 @@ function parseOccurrence(value) {
338
343
  if (Object.keys(item).sort().join() !== "agentId,deliveryId,occurredAt,prompt" || Object.values(item).some((entry) => typeof entry !== "string" || Buffer.byteLength(entry, "utf8") > 16_384))
339
344
  throw new Error("schedule state is invalid");
340
345
  const occurredAt = item.occurredAt;
341
- if (Number.isNaN(Date.parse(occurredAt)) || new Date(occurredAt).toISOString() !== occurredAt || !/^schedule:[a-f0-9]{64}:/u.test(item.deliveryId))
346
+ if (!validTaskInstant(Date.parse(occurredAt)) || new Date(occurredAt).toISOString() !== occurredAt || !/^schedule:[a-f0-9]{64}:/u.test(item.deliveryId))
342
347
  throw new Error("schedule state is invalid");
343
348
  return item;
344
349
  }
@@ -15,6 +15,16 @@ export type StoreHostRegistrationIdentity = StoreHostRegistration & Readonly<{
15
15
  }>;
16
16
  export declare function acquireHostRegistration(root: string, directory: Awaited<ReturnType<typeof open>>, identity: StoreProcessIdentity): Promise<StoreHostRegistration>;
17
17
  export declare function releaseHostRegistration(root: string, directory: Awaited<ReturnType<typeof open>>, registration: StoreHostRegistration): Promise<void>;
18
+ /**
19
+ * Remove `.host-online-*` markers left by hosts that are provably dead in this
20
+ * PID namespace. A host only ever removes its own marker on a clean close, so
21
+ * every crash, kill or container recreate left one behind (148 by 2026-10-05),
22
+ * and the markers count against the store's directory bound. The opening host,
23
+ * live owners, other namespaces and anything unreadable are left untouched.
24
+ */
25
+ export declare function pruneDeadHostRegistrations(root: string, directory: Awaited<ReturnType<typeof open>>, self: StoreHostRegistration, liveness: (owner: StoreHostRegistration & Readonly<{
26
+ generation: string;
27
+ }>) => Promise<boolean>): Promise<number>;
18
28
  /** Admin callers hold their durable offline lease before calling this. */
19
29
  export declare function listHostRegistrations(root: string): Promise<readonly StoreHostRegistrationIdentity[]>;
20
30
  /** Returns undefined when any registration is live, unknown, or changed. */
@@ -1 +1 @@
1
- {"version":3,"file":"storeCoordination.d.ts","sourceRoot":"","sources":["../../src/runtime/storeCoordination.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,IAAI,EAA6B,MAAM,kBAAkB,CAAC;AAG1E,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3J,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1F,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3G,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWvK;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3J;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,6BAA6B,EAAE,CAAC,CAA0O;AACtV,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAgB/R;AAkBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAA6E"}
1
+ {"version":3,"file":"storeCoordination.d.ts","sourceRoot":"","sources":["../../src/runtime/storeCoordination.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,IAAI,EAA6B,MAAM,kBAAkB,CAAC;AAG1E,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3J,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC1F,MAAM,MAAM,6BAA6B,GAAG,qBAAqB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3G,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAWvK;AAED,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3J;AAED;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,GAAG,QAAQ,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzP;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,6BAA6B,EAAE,CAAC,CAA0O;AACtV,4EAA4E;AAC5E,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAgB/R;AAkBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,6BAA6B,GAAG,MAAM,CAA6E"}
@@ -22,6 +22,29 @@ export async function releaseHostRegistration(root, directory, registration) {
22
22
  await unlink(target);
23
23
  await directory.sync();
24
24
  }
25
+ /**
26
+ * Remove `.host-online-*` markers left by hosts that are provably dead in this
27
+ * PID namespace. A host only ever removes its own marker on a clean close, so
28
+ * every crash, kill or container recreate left one behind (148 by 2026-10-05),
29
+ * and the markers count against the store's directory bound. The opening host,
30
+ * live owners, other namespaces and anything unreadable are left untouched.
31
+ */
32
+ export async function pruneDeadHostRegistrations(root, directory, self, liveness) {
33
+ let pruned = 0;
34
+ for (const entry of await readdir(root)) {
35
+ if (!/^\.host-online-[0-9a-f-]{36}\.json$/iu.test(entry) || entry === `.host-online-${self.owner_id}.json`)
36
+ continue;
37
+ const owner = await readRegistration(path.join(root, entry)).catch(() => undefined);
38
+ // This very process is alive by definition, whichever store in it registered the marker.
39
+ if (owner === undefined || !sameNamespace(owner, self) || (owner.pid === self.pid && owner.process_start === self.process_start && owner.boot_id === self.boot_id) || await liveness({ ...owner, generation: owner.owner_id }).catch(() => true))
40
+ continue;
41
+ await unlink(path.join(root, entry)).catch(() => undefined);
42
+ pruned += 1;
43
+ }
44
+ if (pruned > 0)
45
+ await directory.sync();
46
+ return pruned;
47
+ }
25
48
  /** Admin callers hold their durable offline lease before calling this. */
26
49
  export async function listHostRegistrations(root) { const result = []; for (const entry of await readdir(root)) {
27
50
  if (/^\.host-online-[0-9a-f-]{36}\.json$/iu.test(entry))
@@ -0,0 +1,42 @@
1
+ export declare const TASK_CLOCK_ENV = "NOOPOLIS_TASK_CLOCK";
2
+ export declare const TASK_CLOCK_VERSION = "noopolis.task-clock.v1";
3
+ export declare const TASK_CLOCK_MIN_MS = 0;
4
+ export declare const TASK_CLOCK_MAX_MS = 253402300799999;
5
+ export declare function validTaskInstant(value: unknown): value is number;
6
+ export declare function assertTaskInstant(value: number): number;
7
+ export type TaskClock = Readonly<{
8
+ raw: string;
9
+ origin: string;
10
+ anchorEpochMs: number;
11
+ /** Convert a real epoch timestamp without resetting the shared anchor. */
12
+ at(realEpochMs: number): number;
13
+ now(): number;
14
+ }>;
15
+ /** Environment-only contract: no clock callbacks or compiler config in the public API. */
16
+ export declare function readTaskClock(environment?: NodeJS.ProcessEnv): TaskClock | undefined;
17
+ export declare function parseTaskClock(raw: string | undefined): TaskClock | undefined;
18
+ /** The caller installs libfaketime; Daimon derives one process-independent offset. */
19
+ export declare function taskClockChildEnvironment(declared?: NodeJS.ProcessEnv, clock?: Readonly<{
20
+ raw: string;
21
+ origin: string;
22
+ anchorEpochMs: number;
23
+ /** Convert a real epoch timestamp without resetting the shared anchor. */
24
+ at(realEpochMs: number): number;
25
+ now(): number;
26
+ }> | undefined, environment?: NodeJS.ProcessEnv): Record<string, string>;
27
+ /** Inherited environments must not reintroduce another offset owner or loader controls. */
28
+ export declare function taskClockProcessEnvironment(inherited: NodeJS.ProcessEnv, derived?: Record<string, string>): NodeJS.ProcessEnv;
29
+ /** Provider/auth/control processes must never inherit the agent's process clock. */
30
+ export declare function realTimeProcessEnvironment(inherited: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
31
+ /** Reject mixed loader lists: unrelated libraries must never ride this exception. */
32
+ export declare function taskClockPreload(value: string | undefined): string | undefined;
33
+ /** Project real delivery/receipt timestamps, never historical dates inside tool payloads. */
34
+ export declare function taskClockTimestamp(realTimestamp: string, clock?: Readonly<{
35
+ raw: string;
36
+ origin: string;
37
+ anchorEpochMs: number;
38
+ /** Convert a real epoch timestamp without resetting the shared anchor. */
39
+ at(realEpochMs: number): number;
40
+ now(): number;
41
+ }> | undefined): string;
42
+ //# sourceMappingURL=taskClock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taskClock.d.ts","sourceRoot":"","sources":["../../src/runtime/taskClock.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,wBAAwB,CAAC;AACpD,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,iBAAiB,kBAAsB,CAAC;AAErD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,GAAG,IAAI,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,GAAG,SAAS,CAEjG;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAc7E;AAED,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,QAAQ,GAAE,MAAM,CAAC,UAAe,EAAE,KAAK;SA/BlC,MAAM;YACH,MAAM;mBACC,MAAM;IACrB,0EAA0E;oBAC1D,MAAM,GAAG,MAAM;WACxB,MAAM;cA0B4C,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GACtG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBxB;AAED,2FAA2F;AAC3F,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,yBAA8B,GAClE,MAAM,CAAC,UAAU,CAGnB;AAMD,oFAAoF;AACpF,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAG1F;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9E;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK;SAhFxD,MAAM;YACH,MAAM;mBACC,MAAM;IACrB,0EAA0E;oBAC1D,MAAM,GAAG,MAAM;WACxB,MAAM;cA2EkE,GAAG,MAAM,CAEzF"}
@@ -0,0 +1,102 @@
1
+ export const TASK_CLOCK_ENV = "NOOPOLIS_TASK_CLOCK";
2
+ export const TASK_CLOCK_VERSION = "noopolis.task-clock.v1";
3
+ export const TASK_CLOCK_MIN_MS = 0;
4
+ export const TASK_CLOCK_MAX_MS = 253_402_300_799_999;
5
+ export function validTaskInstant(value) {
6
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= TASK_CLOCK_MIN_MS && value <= TASK_CLOCK_MAX_MS;
7
+ }
8
+ export function assertTaskInstant(value) {
9
+ if (!validTaskInstant(value))
10
+ throw invalid("task instant must be within 1970-01-01T00:00:00.000Z .. 9999-12-31T23:59:59.999Z");
11
+ return value;
12
+ }
13
+ /** Environment-only contract: no clock callbacks or compiler config in the public API. */
14
+ export function readTaskClock(environment = process.env) {
15
+ return parseTaskClock(environment[TASK_CLOCK_ENV]);
16
+ }
17
+ export function parseTaskClock(raw) {
18
+ if (raw === undefined)
19
+ return undefined;
20
+ let value;
21
+ try {
22
+ value = JSON.parse(raw);
23
+ }
24
+ catch {
25
+ throw invalid("must be valid JSON");
26
+ }
27
+ if (value === null || typeof value !== "object" || Array.isArray(value))
28
+ throw invalid("must be an object");
29
+ const fields = value;
30
+ if (Object.keys(fields).sort().join() !== "anchorEpochMs,origin,version")
31
+ throw invalid("requires exactly version, origin, anchorEpochMs (no unknown fields)");
32
+ if (fields.version !== TASK_CLOCK_VERSION)
33
+ throw invalid(`version must be ${TASK_CLOCK_VERSION}`);
34
+ if (typeof fields.origin !== "string" || !validInstant(fields.origin))
35
+ throw invalid("origin must be a valid ISO-8601 instant with Z or an explicit offset");
36
+ if (typeof fields.anchorEpochMs !== "number" || !Number.isSafeInteger(fields.anchorEpochMs))
37
+ throw invalid("anchorEpochMs must be a safe integer");
38
+ const origin = fields.origin, anchorEpochMs = fields.anchorEpochMs, originMs = Date.parse(origin);
39
+ assertTaskInstant(originMs);
40
+ const at = (realEpochMs) => assertTaskInstant(originMs + (realEpochMs - anchorEpochMs));
41
+ return Object.freeze({ raw, origin, anchorEpochMs, at, now: () => at(Date.now()) });
42
+ }
43
+ /** The caller installs libfaketime; Daimon derives one process-independent offset. */
44
+ export function taskClockChildEnvironment(declared = {}, clock = readTaskClock(), environment = process.env) {
45
+ if (clock === undefined)
46
+ return {};
47
+ for (const name of Object.keys(declared)) {
48
+ if (declared[name] !== undefined && clockVariable(name))
49
+ throw invalid(`declared child environment conflicts with ${name}`);
50
+ }
51
+ // Integer arithmetic keeps every millisecond even at the safe-integer anchor limits.
52
+ const offset = BigInt(Date.parse(clock.origin)) - BigInt(clock.anchorEpochMs);
53
+ const magnitude = offset < 0n ? -offset : offset;
54
+ const fraction = magnitude % 1000n;
55
+ const seconds = `${offset < 0n ? "-" : "+"}${magnitude / 1000n}${fraction === 0n ? "" : `.${String(fraction).padStart(3, "0")}`}`;
56
+ const preload = taskClockPreload(environment.LD_PRELOAD);
57
+ if (preload === undefined)
58
+ throw invalid("process clock startup probe requires caller-provided LD_PRELOAD naming libfaketime*.so*; refusing clocked execution");
59
+ return {
60
+ // The child process clock owns the offset; Mneme and nested consumers use Date.now().
61
+ // libfaketime's relative format defaults to seconds; an `s` suffix is not supported.
62
+ FAKETIME: seconds,
63
+ FAKETIME_DONT_FAKE_MONOTONIC: "1",
64
+ // libfaketime parses decimal fractions using the process locale.
65
+ LC_ALL: "C",
66
+ LD_PRELOAD: preload
67
+ };
68
+ }
69
+ /** Inherited environments must not reintroduce another offset owner or loader controls. */
70
+ export function taskClockProcessEnvironment(inherited, derived = taskClockChildEnvironment()) {
71
+ if (Object.keys(derived).length === 0)
72
+ return inherited;
73
+ return { ...Object.fromEntries(Object.entries(inherited).filter(([name]) => !clockVariable(name))), ...derived };
74
+ }
75
+ function clockVariable(name) {
76
+ return /^(?:LD_PRELOAD$|DYLD_|FAKETIME|NOOPOLIS_TASK_CLOCK$|MNEME_CLOCK_)/u.test(name);
77
+ }
78
+ /** Provider/auth/control processes must never inherit the agent's process clock. */
79
+ export function realTimeProcessEnvironment(inherited) {
80
+ if (readTaskClock() === undefined)
81
+ return inherited;
82
+ return Object.fromEntries(Object.entries(inherited).filter(([name]) => !clockVariable(name)));
83
+ }
84
+ /** Reject mixed loader lists: unrelated libraries must never ride this exception. */
85
+ export function taskClockPreload(value) {
86
+ const libraries = value?.split(/[\s:]+/u).filter(Boolean);
87
+ return libraries?.length && libraries.every((library) => /^libfaketime[^/]*\.so[^/]*$/u.test(library.split("/").at(-1))) ? value : undefined;
88
+ }
89
+ /** Project real delivery/receipt timestamps, never historical dates inside tool payloads. */
90
+ export function taskClockTimestamp(realTimestamp, clock = readTaskClock()) {
91
+ return clock === undefined ? realTimestamp : new Date(clock.at(Date.parse(realTimestamp))).toISOString();
92
+ }
93
+ function invalid(reason) { return new Error(`${TASK_CLOCK_ENV}: ${reason}`); }
94
+ function validInstant(value) {
95
+ const match = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):?[0-5]\d)$/u.exec(value);
96
+ if (match === null || !Number.isFinite(Date.parse(value)))
97
+ return false;
98
+ const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
99
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
100
+ const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
101
+ return month >= 1 && month <= 12 && day >= 1 && day <= days[month - 1];
102
+ }
@@ -0,0 +1,17 @@
1
+ import childProcess from "node:child_process";
2
+ import { type TaskClock } from "./taskClock.js";
3
+ export type { ChildProcess } from "node:child_process";
4
+ /** @internal Synchronous injection seam; never runtime configuration. */
5
+ export type TaskClockProcessProbe = (command: string, args: readonly string[], environment: NodeJS.ProcessEnv) => string;
6
+ /** @internal Isolate simulated probes between tests. */
7
+ export declare function resetTaskClockProcessForTest(): void;
8
+ /** Shared child-launch boundary, including SDK transports and Pi's spawn hook.
9
+ * Keep the synchronous spawn API: only the first launch blocks for the bounded probes.
10
+ * Every launch derives a fresh environment; only successful interposition is memoized.
11
+ */
12
+ export declare function readyTaskClockEnvironment(environment: NodeJS.ProcessEnv, clock?: TaskClock | undefined, probe?: TaskClockProcessProbe): NodeJS.ProcessEnv;
13
+ /** Only agent-facing tool/server children cross the shifted readiness gate. */
14
+ export declare const spawn: typeof childProcess.spawn;
15
+ /** Engine CLIs and operational helpers stay real; no libfaketime probe runs here. */
16
+ export declare const spawnRealTime: typeof childProcess.spawn;
17
+ //# sourceMappingURL=taskClockProcess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taskClockProcess.d.ts","sourceRoot":"","sources":["../../src/runtime/taskClockProcess.ts"],"names":[],"mappings":"AAAA,OAAO,YAAmC,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAqG,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEnJ,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC;AAGzH,wDAAwD;AACxD,wBAAgB,4BAA4B,IAAI,IAAI,CAAsB;AAS1E;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,GAAE,SAAS,GAAG,SAA2B,EAAE,KAAK,GAAE,qBAAiC,GACvH,MAAM,CAAC,UAAU,CAyBnB;AAED,+EAA+E;AAC/E,eAAO,MAAM,KAAK,EAAE,OAAO,YAAY,CAAC,KAIT,CAAC;AAEhC,qFAAqF;AACrF,eAAO,MAAM,aAAa,EAAE,OAAO,YAAY,CAAC,KAIjB,CAAC"}
@@ -0,0 +1,58 @@
1
+ import childProcess from "node:child_process";
2
+ import { readTaskClock, realTimeProcessEnvironment, taskClockChildEnvironment, taskClockProcessEnvironment } from "./taskClock.js";
3
+ const verified = new Set();
4
+ /** @internal Isolate simulated probes between tests. */
5
+ export function resetTaskClockProcessForTest() { verified.clear(); }
6
+ const dateProbe = (command, args, env) => {
7
+ const result = childProcess.spawnSync(command, [...args], { env, encoding: "utf8", timeout: 5000, killSignal: "SIGKILL", maxBuffer: 1024 });
8
+ // Loaders can ignore a missing library and still exit zero, including at zero offset.
9
+ if (result.error || result.status !== 0 || result.stderr?.trim())
10
+ throw result.error ?? new Error("date/libfaketime emitted a startup diagnostic");
11
+ return result.stdout;
12
+ };
13
+ /** Shared child-launch boundary, including SDK transports and Pi's spawn hook.
14
+ * Keep the synchronous spawn API: only the first launch blocks for the bounded probes.
15
+ * Every launch derives a fresh environment; only successful interposition is memoized.
16
+ */
17
+ export function readyTaskClockEnvironment(environment, clock = readTaskClock(), probe = dateProbe) {
18
+ if (clock === undefined)
19
+ return environment;
20
+ const refuse = (reason) => new Error(`NOOPOLIS_TASK_CLOCK: process clock startup probe ${reason}; refusing clocked execution`);
21
+ if (process.platform !== "linux")
22
+ throw refuse("requires Linux libfaketime interposition");
23
+ const derived = taskClockChildEnvironment({}, clock);
24
+ const env = taskClockProcessEnvironment(environment, derived);
25
+ clock.now(); // A cached process clock never permits reads outside the supported calendar.
26
+ const key = JSON.stringify([process.pid, process.platform, derived.LD_PRELOAD, derived.FAKETIME]);
27
+ if (verified.has(key))
28
+ return env;
29
+ for (const sentinel of [true, false]) {
30
+ let output;
31
+ try {
32
+ // Absolute path: engine-specific PATHs need not contain system utilities.
33
+ output = probe("/bin/date", ["-u", "+%s"], sentinel ? { ...env, FAKETIME: "-31536000" } : env);
34
+ }
35
+ catch (cause) {
36
+ throw new Error(refuse("could not run date -u +%s (date/libfaketime missing or unusable)").message, { cause });
37
+ }
38
+ const seconds = Number(output.trim());
39
+ const expected = sentinel ? Date.now() - 31_536_000_000 : clock.now();
40
+ if (!/^-?\d+$/u.test(output.trim()) || !Number.isSafeInteger(seconds) || Math.abs(seconds * 1000 - expected) > 5000) {
41
+ throw refuse(`did not observe ${sentinel ? "sentinel offset" : "task time"} within ±5 seconds (check libfaketime and its offset)`);
42
+ }
43
+ }
44
+ verified.add(key);
45
+ return env;
46
+ }
47
+ /** Only agent-facing tool/server children cross the shifted readiness gate. */
48
+ export const spawn = ((command, args, options) => {
49
+ const argv = Array.isArray(args) ? args : [];
50
+ const settings = (Array.isArray(args) ? options : args ?? options);
51
+ return childProcess.spawn(command, argv, { ...settings, env: readyTaskClockEnvironment(settings?.env ?? process.env) });
52
+ });
53
+ /** Engine CLIs and operational helpers stay real; no libfaketime probe runs here. */
54
+ export const spawnRealTime = ((command, args, options) => {
55
+ const argv = Array.isArray(args) ? args : [];
56
+ const settings = (Array.isArray(args) ? options : args ?? options);
57
+ return childProcess.spawn(command, argv, { ...settings, env: realTimeProcessEnvironment(settings?.env ?? process.env) });
58
+ });
@@ -123,11 +123,6 @@ export type GrokTurnRequestEntry = Readonly<{
123
123
  * field is not.
124
124
  */
125
125
  export declare const renderGrokTurnRequestLines: (entry: GrokTurnRequestEntry) => string;
126
- /**
127
- * Append already-rendered, newline-terminated ledger lines in one write, with the
128
- * same rotation and file mode as both ledgers. Advisory: never rejects. The
129
- * broker uses it to append the exact bytes it sealed into a turn record.
130
- */
131
126
  export declare const recordLedgerLines: (file: string, lines: string) => Promise<boolean>;
132
127
  /** Advisory and never rejects, like {@link recordTurnRequests}; an empty turn writes nothing. */
133
128
  export declare const recordGrokTurnRequests: (file: string, entry: GrokTurnRequestEntry) => Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"turnRequestLedger.d.ts","sourceRoot":"","sources":["../../src/runtime/turnRequestLedger.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,2BAA2B,EAAG,kCAA2C,CAAC;AAEvF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAG,kCAA2C,CAAC;AAExF,eAAO,MAAM,4BAA4B,GAAI,cAAa,MAAM,CAAC,UAAwB,KAAG,MAG3F,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAIH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,gBAAgB,KAAG,MAqBhE,CAAC;AASF,0FAA0F;AAC1F;;;;GAIG;AACH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AACvQ;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/M;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,oBAAoB,KAAG,MAuBxE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,OAAO,CAGpF,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,GAAU,MAAM,MAAM,EAAE,OAAO,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAGvG,CAAC;AA4BF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,KAAG,OAAO,CAAC,OAAO,CAU/F,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAU,OAAO,QAAQ,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,KAAG,OAAO,CAAC,OAAO,CAiBnB,CAAC"}
1
+ {"version":3,"file":"turnRequestLedger.d.ts","sourceRoot":"","sources":["../../src/runtime/turnRequestLedger.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,2BAA2B,EAAG,kCAA2C,CAAC;AAEvF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,EAAG,kCAA2C,CAAC;AAExF,eAAO,MAAM,4BAA4B,GAAI,cAAa,MAAM,CAAC,UAAwB,KAAG,MAG3F,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAIH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,gBAAgB,KAAG,MAqBhE,CAAC;AASF,0FAA0F;AAC1F;;;;GAIG;AACH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AACvQ;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/M;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,oBAAoB,KAAG,MAuBxE,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,OAAO,CAWpF,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,GAAU,MAAM,MAAM,EAAE,OAAO,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAGvG,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,OAAO,gBAAgB,KAAG,OAAO,CAAC,OAAO,CAQ/F,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAU,OAAO,QAAQ,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,KAAG,OAAO,CAAC,OAAO,CAiBnB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import path from "node:path";
1
2
  import { constants } from "node:fs";
2
3
  import { open, rename, stat } from "node:fs/promises";
3
4
  import { readCodexRolloutRequests } from "../pi/codexRolloutUsage.js";
@@ -138,16 +139,29 @@ export const renderGrokTurnRequestLines = (entry) => {
138
139
  * same rotation and file mode as both ledgers. Advisory: never rejects. The
139
140
  * broker uses it to append the exact bytes it sealed into a turn record.
140
141
  */
142
+ const ledgerWrites = new Map();
141
143
  export const recordLedgerLines = async (file, lines) => {
142
144
  if (lines.length === 0)
143
145
  return false;
146
+ const key = path.resolve(file);
147
+ const previous = ledgerWrites.get(key) ?? Promise.resolve(true);
148
+ const pending = previous.then(async () => {
149
+ try {
150
+ await rotate(key);
151
+ await appendLines(key, lines);
152
+ return true;
153
+ }
154
+ catch {
155
+ return false;
156
+ }
157
+ });
158
+ ledgerWrites.set(key, pending);
144
159
  try {
145
- await rotate(file);
146
- await appendLines(file, lines);
147
- return true;
160
+ return await pending;
148
161
  }
149
- catch {
150
- return false;
162
+ finally {
163
+ if (ledgerWrites.get(key) === pending)
164
+ ledgerWrites.delete(key);
151
165
  }
152
166
  };
153
167
  /** Advisory and never rejects, like {@link recordTurnRequests}; an empty turn writes nothing. */
@@ -155,9 +169,7 @@ export const recordGrokTurnRequests = async (file, entry) => {
155
169
  if (entry.requests.length === 0)
156
170
  return false;
157
171
  try {
158
- await rotate(file);
159
- await appendLines(file, renderGrokTurnRequestLines(entry));
160
- return true;
172
+ return await recordLedgerLines(file, renderGrokTurnRequestLines(entry));
161
173
  }
162
174
  catch {
163
175
  return false;
@@ -174,16 +186,8 @@ const rotate = async (file) => {
174
186
  if (size >= TURN_USAGE_ROTATE_BYTES)
175
187
  await rename(file, `${file}.1`);
176
188
  };
177
- /**
178
- * One `write(2)` for the whole wake's rows.
179
- *
180
- * Turns for different agents run concurrently inside the single broker process.
181
- * `O_APPEND` makes a single sub-page write atomic against them, so a wake's rows
182
- * are contiguous and never interleaved with another wake's — which matters more
183
- * here than in the per-wake ledger, because a row only means something beside
184
- * the other rows of its own thread. A wake with enough requests to exceed a page
185
- * is still append-ordered per line; interleaving would cost grouping, not
186
- * correctness, since every row carries its own thread id.
189
+ /** Rotation and all writes are serialized per canonical path in this broker process.
190
+ * The realm lease excludes another broker. Independent paths remain concurrent.
187
191
  */
188
192
  const appendLines = async (file, lines) => {
189
193
  const bytes = Buffer.from(lines, "utf8");
@@ -209,9 +213,7 @@ export const recordTurnRequests = async (file, entry) => {
209
213
  if (entry.requests.length === 0)
210
214
  return false;
211
215
  try {
212
- await rotate(file);
213
- await appendLines(file, renderTurnRequestLines(entry));
214
- return true;
216
+ return await recordLedgerLines(file, renderTurnRequestLines(entry));
215
217
  }
216
218
  catch {
217
219
  /* advisory: instrumentation never fails a wake that published */