@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,6 +1,7 @@
1
+ import { brokerClockObservation } from "./grokBrokerTaskClock.js";
1
2
  import { chmod, lstat, unlink } from "node:fs/promises";
2
3
  import { createServer } from "node:net";
3
- import { encodeEngineBrokerFrame, EngineBrokerFrameDecoder, parseEngineBrokerRequest } from "./engineBrokerProtocol.js";
4
+ import { encodeEngineBrokerFrame, EngineBrokerFrameDecoder, EngineBrokerProtocolMismatch, parseEngineBrokerRequest } from "./engineBrokerProtocol.js";
4
5
  import { EngineBrokerTurnFailure } from "./grokEngineBroker.js";
5
6
  import { GROK_INFERENCE_PROXY_BASE_URL } from "./engineBrokerInferenceProtocol.js";
6
7
  import { GrokInferenceGrantRefused } from "./grokInferenceGrants.js";
@@ -12,7 +13,11 @@ export async function startEngineBrokerServiceWithIdentity(broker, socketPath, e
12
13
  throw new Error("engine broker service requires broker identity");
13
14
  await removeOwnedSocket(socketPath, expectedUid);
14
15
  const active = new Map(), connections = new Set();
15
- const server = createServer((socket) => { connections.add(socket); socket.once("close", () => connections.delete(socket)); handle(socket, broker, active); });
16
+ const server = createServer((socket) => {
17
+ connections.add(socket);
18
+ socket.once("close", () => connections.delete(socket));
19
+ serveEngineBrokerConnection(socket, broker, active);
20
+ });
16
21
  await listen(server, socketPath);
17
22
  try {
18
23
  await chmod(socketPath, 0o600);
@@ -48,43 +53,88 @@ export async function startEngineBrokerServiceWithIdentity(broker, socketPath, e
48
53
  * reader has disconnected.
49
54
  */
50
55
  function handleSocketError(socket, owned) { socket.on("error", () => { owned()?.controller.abort(); socket.destroy(); }); }
51
- function handle(socket, broker, active) { const decoder = new EngineBrokerFrameDecoder(); let started = false, owned; handleSocketError(socket, () => owned); socket.once("close", () => owned?.controller.abort()); socket.on("data", (chunk) => { try {
52
- for (const value of decoder.push(chunk)) {
53
- const request = parseEngineBrokerRequest(value);
54
- if (request.kind === "health") {
55
- if (started)
56
- throw new Error();
57
- started = true;
58
- const ready = broker.readiness();
59
- if (ready.providerProxyPort !== 43123 || ready.mcpFacadePort !== 43124 || ready.registrations < 1 || ready.credentialStale || !ready.realmLease || !ready.workerIsolation)
60
- throw new Error();
61
- return send(socket, { version: request.version, kind: "ready", requestId: request.requestId, brokerUid: 2100, providerProxyPort: 43123, mcpFacadePort: 43124, registrations: ready.registrations, credentialStale: false, realmLease: true, workerIsolation: true });
56
+ /** @internal The production connection handler, also exercised over an in-memory transport. */
57
+ export function serveEngineBrokerConnection(socket, broker, active = new Map()) {
58
+ const decoder = new EngineBrokerFrameDecoder();
59
+ let started = false;
60
+ let owned;
61
+ handleSocketError(socket, () => owned);
62
+ socket.once("close", () => owned?.controller.abort());
63
+ socket.on("data", (chunk) => {
64
+ try {
65
+ for (const value of decoder.push(chunk)) {
66
+ const request = parseEngineBrokerRequest(value);
67
+ if (request.kind === "health") {
68
+ if (started)
69
+ throw new Error();
70
+ started = true;
71
+ const ready = broker.readiness();
72
+ if (ready.providerProxyPort !== 43123 || ready.mcpFacadePort !== 43124 ||
73
+ ready.registrations < 1 || ready.credentialStale || !ready.realmLease || !ready.workerIsolation) {
74
+ throw new Error();
75
+ }
76
+ return send(socket, {
77
+ version: request.version, kind: "ready", requestId: request.requestId, brokerUid: 2100,
78
+ providerProxyPort: 43123, mcpFacadePort: 43124, registrations: ready.registrations,
79
+ credentialStale: false, realmLease: true, workerIsolation: true
80
+ });
81
+ }
82
+ if (request.kind === "cancel_turn") {
83
+ if (owned?.turnId !== request.turnId)
84
+ throw new Error("turn not owned by connection");
85
+ owned.controller.abort();
86
+ continue;
87
+ }
88
+ if (request.kind === "request_inference_grant" || request.kind === "release_inference_grant") {
89
+ if (started)
90
+ throw new Error();
91
+ started = true;
92
+ serveInferenceGrant(socket, broker, request);
93
+ continue;
94
+ }
95
+ if (request.kind === "reset_slot") {
96
+ if (started || broker.resetSlot === undefined)
97
+ throw new Error();
98
+ started = true;
99
+ void broker.resetSlot(request.slot).then(() => send(socket, {
100
+ version: request.version, kind: "slot_reset", requestId: request.requestId, slot: request.slot
101
+ }), () => socket.destroy());
102
+ continue;
103
+ }
104
+ const key = `${request.slot ?? request.agentId}:${request.turnId}`;
105
+ if (started || active.has(key))
106
+ throw new Error();
107
+ started = true;
108
+ const controller = new AbortController();
109
+ owned = { turnId: request.turnId, controller };
110
+ active.set(key, controller);
111
+ socket.write(encodeEngineBrokerFrame({
112
+ version: request.version, kind: "accepted", requestId: request.requestId, turnId: request.turnId
113
+ }));
114
+ void broker.turn(request.agentId, request.wakeId, request.prompt, request.mcpEndpoint, controller.signal, request.limits, request.processClock, request.slot).then((result) => send(socket, {
115
+ version: request.version, kind: "completed", requestId: request.requestId, turnId: request.turnId,
116
+ text: result.text, ...brokerClockObservation(result.shellClock, result.providerClock),
117
+ ...(result.cleanupDiagnostic === undefined ? {} : { cleanupDiagnostic: result.cleanupDiagnostic }),
118
+ workerPid: result.workerPid, workerUid: result.workerUid, workerStartTime: result.workerStartTime,
119
+ outcome: "completed", usage: result.usage, model: result.model, requests: result.requests, limitReason: "none"
120
+ }), (error) => failed(socket, request, error, controller.signal.aborted)).finally(() => {
121
+ if (active.get(key) === controller)
122
+ active.delete(key);
123
+ owned = undefined;
124
+ });
125
+ }
62
126
  }
63
- if (request.kind === "cancel_turn") {
64
- active.get(request.turnId)?.abort();
65
- continue;
127
+ catch (error) {
128
+ if (error instanceof EngineBrokerProtocolMismatch && !socket.writableEnded) {
129
+ // Flush one small, closed diagnostic before FIN; destroy would lose it.
130
+ socket.pause();
131
+ socket.end(encodeEngineBrokerFrame(error.diagnostic));
132
+ }
133
+ else
134
+ socket.destroy();
66
135
  }
67
- if (request.kind === "request_inference_grant" || request.kind === "release_inference_grant") {
68
- if (started)
69
- throw new Error();
70
- started = true;
71
- serveInferenceGrant(socket, broker, request);
72
- continue;
73
- }
74
- if (started || active.has(request.turnId))
75
- throw new Error();
76
- started = true;
77
- const controller = new AbortController();
78
- owned = { turnId: request.turnId, controller };
79
- active.set(request.turnId, controller);
80
- socket.write(encodeEngineBrokerFrame({ version: request.version, kind: "accepted", requestId: request.requestId, turnId: request.turnId }));
81
- void broker.turn(request.agentId, request.wakeId, request.prompt, request.mcpEndpoint, controller.signal, request.limits).then((result) => send(socket, { version: request.version, kind: "completed", requestId: request.requestId, turnId: request.turnId, text: result.text, workerPid: result.workerPid, workerUid: result.workerUid, workerStartTime: result.workerStartTime, outcome: "completed", usage: result.usage, model: result.model, requests: result.requests, limitReason: "none" }), (error) => failed(socket, request, error, controller.signal.aborted)).finally(() => { if (active.get(request.turnId) === controller)
82
- active.delete(request.turnId); owned = undefined; });
83
- }
136
+ });
84
137
  }
85
- catch {
86
- socket.destroy();
87
- } }); }
88
138
  /**
89
139
  * Every failure the broker raises for a known registration carries its
90
140
  * accounting (a wake that tried to raise a limit: `usage: null`, zero
@@ -99,7 +149,15 @@ function failed(socket, request, error, aborted) {
99
149
  return;
100
150
  }
101
151
  const code = error instanceof EngineBrokerTurnFailure ? error.code : aborted ? "cancelled" : "engine_failed";
102
- send(socket, { version: request.version, kind: "failed", requestId: request.requestId, turnId: request.turnId, code, ...(error instanceof EngineBrokerTurnFailure && error.diagnostic ? { diagnostic: error.diagnostic } : {}), ...(error instanceof EngineBrokerTurnFailure && error.mcpCalls ? { mcpCalls: error.mcpCalls } : {}), outcome: "failed", usage: accounting.usage, model: accounting.model, requests: accounting.requests, limitReason: accounting.limitReason });
152
+ send(socket, {
153
+ version: request.version, kind: "failed", requestId: request.requestId, turnId: request.turnId, code,
154
+ ...(error instanceof EngineBrokerTurnFailure ? brokerClockObservation(error.shellClock, error.providerClock) : {}),
155
+ ...(error instanceof EngineBrokerTurnFailure && error.diagnostic ? { diagnostic: error.diagnostic } : {}),
156
+ ...(error instanceof EngineBrokerTurnFailure && error.mcpCalls ? { mcpCalls: error.mcpCalls } : {}),
157
+ ...(error instanceof EngineBrokerTurnFailure && error.cleanupDiagnostic ? { cleanupDiagnostic: error.cleanupDiagnostic } : {}),
158
+ outcome: "failed", usage: accounting.usage, model: accounting.model,
159
+ requests: accounting.requests, limitReason: accounting.limitReason
160
+ });
103
161
  }
104
162
  /** One grant verb per connection, answered and closed; any failure becomes a closed-code refusal and never touches turn state. */
105
163
  function serveInferenceGrant(socket, broker, request) {
@@ -10,7 +10,7 @@ export async function runEngineBrokerServiceCli() {
10
10
  if (process.getuid?.() !== 2100)
11
11
  throw new Error("engine broker service requires broker identity");
12
12
  const config = parseEngineBrokerServiceConfig(await readRootConfig(ENGINE_BROKER_SERVICE_CONFIG));
13
- const broker = await startGrokEngineBroker({ grokCommand: "/usr/local/bin/grok", nativeClient: "/opt/daimon/bin/daimon-engine-broker", credentialHome: config.credentialHome, turnStore: config.turnStore, registrations: config.registrations, ...(config.inferenceLedgerPath === undefined ? {} : { inferenceLedgerPath: config.inferenceLedgerPath }) });
13
+ const broker = await startGrokEngineBroker({ grokCommand: "/usr/local/bin/grok", nativeClient: "/opt/daimon/bin/daimon-engine-broker", credentialHome: config.credentialHome, turnStore: config.turnStore, registrations: config.registrations, maxRunRequests: config.maxRunRequests, runRequestStore: config.runRequestStore, ...(config.inferenceLedgerPath === undefined ? {} : { inferenceLedgerPath: config.inferenceLedgerPath }) });
14
14
  const service = await startEngineBrokerService(broker);
15
15
  let stopping;
16
16
  const stop = () => { stopping ??= service.close(); return stopping; };
@@ -1,6 +1,7 @@
1
1
  import { type EngineBrokerTurnLimits } from "./engineBrokerTurnAccounting.js";
2
2
  import { type GrokBrokerModelPolicy } from "./grokBrokerModelPolicy.js";
3
3
  export declare const ENGINE_BROKER_SERVICE_V1: "noopolis.daimon.engine-broker-service.v1";
4
+ export declare const ENGINE_BROKER_SERVICE_V3: "noopolis.daimon.engine-broker-service.v3";
4
5
  export declare const ENGINE_BROKER_SERVICE_V2: "noopolis.daimon.engine-broker-service.v2";
5
6
  /** One root-provisioned broker slot. Every field is fixed at provisioning time; a wake can only lower `limits`. */
6
7
  export type EngineBrokerServiceRegistration = Readonly<{
@@ -15,6 +16,13 @@ export type EngineBrokerServiceRegistration = Readonly<{
15
16
  usageLedgerPath: string;
16
17
  limits: EngineBrokerTurnLimits;
17
18
  model: GrokBrokerModelPolicy;
19
+ /**
20
+ * Digest of the skills root provisioning rendered into `$GROK_HOME/skills`
21
+ * (`grokWorkerSkillsSha256`), attested before every turn. Optional in v2
22
+ * registrations and v3 slot policies; absent means the slot has no skills and
23
+ * the directory must not exist.
24
+ */
25
+ skillsSha256?: string;
18
26
  }>;
19
27
  /**
20
28
  * `inferenceLedgerPath` (v2, optional) is where evaluator inference grants
@@ -28,6 +36,8 @@ export type EngineBrokerServiceConfig = Readonly<{
28
36
  turnStore: string;
29
37
  registrations: readonly EngineBrokerServiceRegistration[];
30
38
  inferenceLedgerPath?: string;
39
+ maxRunRequests?: number;
40
+ runRequestStore?: string;
31
41
  }>;
32
42
  /** The per-request stream written beside a registration's usage ledger. */
33
43
  export declare const engineBrokerRequestLedgerPathFor: (usageLedgerPath: string) => string;
@@ -40,9 +50,12 @@ export declare const engineBrokerSealLedgerPathFor: (usageLedgerPath: string) =>
40
50
  /**
41
51
  * Strict `service.json` parser.
42
52
  *
53
+ * v3 retains seven-field registrations and binds slotPolicies by slot; agent IDs
54
+ * may repeat, while worker UIDs, slots and ledger streams must be distinct.
43
55
  * v2 requires every registration to declare its usage ledger, limits and model
44
- * (`model.id`/`model.reasoningEffort` from the closed lists); unknown keys at
45
- * any level are refused. v1 is still accepted and receives today's defaults:
56
+ * (`model.id`/`model.reasoningEffort` from the closed lists) and may declare
57
+ * `skillsSha256` (v3: on its slot policy); unknown keys at any level are
58
+ * refused. v1 is still accepted and receives today's defaults:
46
59
  * the container ledger, {@link DEFAULT_GROK_BROKER_TURN_LIMITS}, and
47
60
  * `grok-4.6`/`low`.
48
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"engineBrokerServiceConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerServiceConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC5I,OAAO,EAAuF,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAK7J,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAE5F,mHAAmH;AACnH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IACpI,oJAAoJ;IACpJ,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC,CAAC;AACH;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAczL,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,GAAI,iBAAiB,MAAM,KAAG,MAAgF,CAAC;AAE5J;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,iBAAiB,MAAM,KAAG,MAA6E,CAAC;AAEtJ;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CA6BxF"}
1
+ {"version":3,"file":"engineBrokerServiceConfig.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerServiceConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC5I,OAAO,EAAuF,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAK7J,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAC5F,eAAO,MAAM,wBAAwB,EAAG,0CAAmD,CAAC;AAE5F,mHAAmH;AACnH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IACpI,oJAAoJ;IACpJ,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,qBAAqB,CAAC;IAC7B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CAAC;AACH;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAc5O,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,GAAI,iBAAiB,MAAM,KAAG,MAAgF,CAAC;AAE5J;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,iBAAiB,MAAM,KAAG,MAA6E,CAAC;AAEtJ;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAyDxF"}
@@ -5,6 +5,7 @@ import { grokWorkerEventsPathFor } from "./grokWorkerSandboxProfile.js";
5
5
  import { TURN_REQUEST_LEDGER } from "./turnRequestLedger.js";
6
6
  import { TURN_USAGE_LEDGER } from "./turnUsageLedger.js";
7
7
  export const ENGINE_BROKER_SERVICE_V1 = "noopolis.daimon.engine-broker-service.v1";
8
+ export const ENGINE_BROKER_SERVICE_V3 = "noopolis.daimon.engine-broker-service.v3";
8
9
  export const ENGINE_BROKER_SERVICE_V2 = "noopolis.daimon.engine-broker-service.v2";
9
10
  const V1_REGISTRATION = ["agentId", "slot", "workerUid", "workspace", "profilePath", "eventsPath", "profileSha256"];
10
11
  const V2_REGISTRATION = [...V1_REGISTRATION, "usageLedgerPath", "limits", "model"];
@@ -29,48 +30,88 @@ export const engineBrokerSealLedgerPathFor = (usageLedgerPath) => path.posix.joi
29
30
  /**
30
31
  * Strict `service.json` parser.
31
32
  *
33
+ * v3 retains seven-field registrations and binds slotPolicies by slot; agent IDs
34
+ * may repeat, while worker UIDs, slots and ledger streams must be distinct.
32
35
  * v2 requires every registration to declare its usage ledger, limits and model
33
- * (`model.id`/`model.reasoningEffort` from the closed lists); unknown keys at
34
- * any level are refused. v1 is still accepted and receives today's defaults:
36
+ * (`model.id`/`model.reasoningEffort` from the closed lists) and may declare
37
+ * `skillsSha256` (v3: on its slot policy); unknown keys at any level are
38
+ * refused. v1 is still accepted and receives today's defaults:
35
39
  * the container ledger, {@link DEFAULT_GROK_BROKER_TURN_LIMITS}, and
36
40
  * `grok-4.6`/`low`.
37
41
  */
38
42
  export function parseEngineBrokerServiceConfig(value) {
39
43
  if (!plain(value))
40
44
  throw invalid();
41
- const v2 = value.version === ENGINE_BROKER_SERVICE_V2;
45
+ const v3 = value.version === ENGINE_BROKER_SERVICE_V3;
46
+ const v2 = v3 || value.version === ENGINE_BROKER_SERVICE_V2;
42
47
  if (!v2 && value.version !== ENGINE_BROKER_SERVICE_V1)
43
48
  throw invalid();
44
- const top = ["version", "credentialHome", "turnStore", "registrations"];
49
+ const top = ["version", "credentialHome", "turnStore", "registrations", ...(v3 ? ["slotPolicies"] : [])];
50
+ if (v3 && Object.hasOwn(value, "maxRunRequests")) {
51
+ if (!Number.isSafeInteger(value.maxRunRequests) || value.maxRunRequests < 1)
52
+ throw invalid();
53
+ top.push("maxRunRequests");
54
+ }
55
+ if (v3 && Object.hasOwn(value, "runRequestStore")) {
56
+ if (!absolute(value.runRequestStore) || value.maxRunRequests === undefined)
57
+ throw invalid();
58
+ top.push("runRequestStore");
59
+ }
45
60
  exact(value, v2 && Object.hasOwn(value, "inferenceLedgerPath") ? [...top, "inferenceLedgerPath"] : top);
46
61
  if (!absolute(value.credentialHome) || !absolute(value.turnStore) || !Array.isArray(value.registrations) || value.registrations.length === 0)
47
62
  throw invalid();
48
- const seen = new Set(), slots = new Set();
63
+ const policies = new Map();
64
+ if (v3) {
65
+ if (!Array.isArray(value.slotPolicies) || value.slotPolicies.length !== value.registrations.length)
66
+ throw invalid();
67
+ for (const policy of value.slotPolicies) {
68
+ if (!plain(policy))
69
+ throw invalid();
70
+ exact(policy, ["slot", "usageLedgerPath", "limits", "model", ...withSkills(policy)]);
71
+ if (!Number.isSafeInteger(policy.slot) || policy.slot < 0 || policies.has(policy.slot))
72
+ throw invalid();
73
+ policies.set(policy.slot, policy);
74
+ }
75
+ }
76
+ const seen = new Set(), slots = new Set(), workers = new Set();
77
+ const ledgers = new Set();
49
78
  const registrations = value.registrations.map((entry) => {
50
79
  if (!plain(entry))
51
80
  throw invalid();
52
- exact(entry, v2 ? V2_REGISTRATION : V1_REGISTRATION);
81
+ exact(entry, v2 && !v3 ? [...V2_REGISTRATION, ...withSkills(entry)] : V1_REGISTRATION);
53
82
  const { agentId, slot, workerUid, workspace, profilePath, eventsPath, profileSha256 } = entry;
54
- if (typeof agentId !== "string" || !agentId.trim() || seen.has(agentId) || !Number.isSafeInteger(slot) || slot < 0 || slots.has(slot) || !Number.isSafeInteger(workerUid) || workerUid < 2200 || !absolute(workspace) || !absolute(profilePath) || !absolute(eventsPath) || typeof profileSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(profileSha256) || eventsPath !== grokWorkerEventsPathFor(profilePath) || !profilePath.endsWith("/sandbox.toml"))
83
+ if (typeof agentId !== "string" || !agentId.trim() || (!v3 && seen.has(agentId)) || !Number.isSafeInteger(slot) || slot < 0 || slots.has(slot) || !Number.isSafeInteger(workerUid) || workerUid < 2200 || workers.has(workerUid) || !absolute(workspace) || !absolute(profilePath) || !absolute(eventsPath) || typeof profileSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(profileSha256) || eventsPath !== grokWorkerEventsPathFor(profilePath) || !profilePath.endsWith("/sandbox.toml"))
55
84
  throw invalid();
56
85
  seen.add(agentId);
57
86
  slots.add(slot);
87
+ workers.add(workerUid);
58
88
  const base = { agentId, slot: slot, workerUid: workerUid, workspace, profilePath, eventsPath, profileSha256 };
59
89
  if (!v2)
60
90
  return { ...base, usageLedgerPath: TURN_USAGE_LEDGER.filePath, limits: DEFAULT_GROK_BROKER_TURN_LIMITS, model: DEFAULT_GROK_BROKER_MODEL_POLICY };
61
- const usageLedgerPath = entry.usageLedgerPath;
91
+ const policy = v3 ? policies.get(slot) : entry;
92
+ if (policy === undefined)
93
+ throw invalid();
94
+ const usageLedgerPath = policy.usageLedgerPath;
62
95
  if (!ledgerPath(usageLedgerPath) || usageLedgerPath === engineBrokerRequestLedgerPathFor(usageLedgerPath) || usageLedgerPath === engineBrokerSealLedgerPathFor(usageLedgerPath))
63
96
  throw invalid();
97
+ const streams = [usageLedgerPath, engineBrokerRequestLedgerPathFor(usageLedgerPath), engineBrokerSealLedgerPathFor(usageLedgerPath)];
98
+ if (v3 && streams.some((file) => ledgers.has(file)))
99
+ throw invalid();
100
+ streams.forEach((file) => ledgers.add(file));
64
101
  let limits;
65
102
  try {
66
- limits = parseEngineBrokerTurnLimits(entry.limits);
103
+ limits = parseEngineBrokerTurnLimits(policy.limits);
67
104
  }
68
105
  catch {
69
106
  throw invalid();
70
107
  }
71
- return { ...base, usageLedgerPath, limits, model: parseServiceModel(entry.model) };
108
+ if (Object.hasOwn(policy, "skillsSha256") && (typeof policy.skillsSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(policy.skillsSha256)))
109
+ throw invalid();
110
+ return { ...base, usageLedgerPath, limits, model: parseServiceModel(policy.model), ...(Object.hasOwn(policy, "skillsSha256") ? { skillsSha256: policy.skillsSha256 } : {}) };
72
111
  });
73
- const base = { credentialHome: value.credentialHome, turnStore: value.turnStore, registrations };
112
+ const base = { credentialHome: value.credentialHome, turnStore: value.turnStore, registrations,
113
+ ...(value.runRequestStore === undefined ? {} : { runRequestStore: value.runRequestStore }),
114
+ ...(value.maxRunRequests === undefined ? {} : { maxRunRequests: value.maxRunRequests }) };
74
115
  if (!Object.hasOwn(value, "inferenceLedgerPath"))
75
116
  return base;
76
117
  const inferenceLedgerPath = value.inferenceLedgerPath;
@@ -81,6 +122,7 @@ export function parseEngineBrokerServiceConfig(value) {
81
122
  throw invalid();
82
123
  return { ...base, inferenceLedgerPath };
83
124
  }
125
+ const withSkills = (value) => Object.hasOwn(value, "skillsSha256") ? ["skillsSha256"] : [];
84
126
  const ledgerPath = (item) => absolute(item) && item.endsWith(".jsonl") && path.posix.normalize(item) === item;
85
127
  function parseServiceModel(value) {
86
128
  if (!plain(value))
@@ -48,7 +48,7 @@ export declare class EngineBrokerTurnRegistry {
48
48
  }
49
49
  /**
50
50
  * Strict record parser. v2 accepts exactly `{version,digest,state,bootId}`
51
- * plus `response` and its sealed `ledger` bytes when terminal, and the response must be a v2 terminal frame.
51
+ * plus `response` and its sealed `ledger` bytes when terminal, and the response may be a v2, v3 or v4 terminal frame (older frames upgrade on read).
52
52
  * v1 records keep their historical looser shape and are upgraded on read: no
53
53
  * usage (`null`), zero requests, `limitReason: "none"`, the declared model.
54
54
  */
@@ -1 +1 @@
1
- {"version":3,"file":"engineBrokerTurnRegistry.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerTurnRegistry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkE,KAAK,mBAAmB,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACxK,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAwD,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE/H,KAAK,KAAK,GAAG,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;AAClE,KAAK,QAAQ,GAAG,4BAA4B,CAAC;AAC7C,eAAO,MAAM,4BAA4B,EAAG,uCAAgD,CAAC;AAC7F,eAAO,MAAM,4BAA4B,EAAG,uCAAgD,CAAC;AAK7F,KAAK,QAAQ,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,OAAO,4BAA4B,GAAG,OAAO,4BAA4B,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAAC;AAEpO;;;;;GAKG;AACH,qBAAa,wBAAwB;IAEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAS,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAqB,OAAO,CAAC,QAAQ,CAAC,eAAe;IADtH,yGAAyG;gBAC5E,IAAI,EAAE,MAAM,EAAkB,MAAM,GAAC,MAAmB,EAAkB,eAAe,GAAC,CAAC,SAAS,EAAC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAe;IACtK,gGAAgG;IAC1F,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,GAAG;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,qBAAqB,CAAA;KAAE,CAAC;IAW3H;;;;;;;;;OASG;IACG,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAE,qBAAgD,GAAG,OAAO,CAAC,QAAQ,CAAC;QAAE,eAAe,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAO5J;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,QAAQ,CAyB1F"}
1
+ {"version":3,"file":"engineBrokerTurnRegistry.d.ts","sourceRoot":"","sources":["../../src/runtime/engineBrokerTurnRegistry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmK,KAAK,mBAAmB,EAAE,KAAK,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzQ,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAwD,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE/H,KAAK,KAAK,GAAG,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;AAClE,KAAK,QAAQ,GAAG,4BAA4B,CAAC;AAC7C,eAAO,MAAM,4BAA4B,EAAG,uCAAgD,CAAC;AAC7F,eAAO,MAAM,4BAA4B,EAAG,uCAAgD,CAAC;AAM7F,KAAK,QAAQ,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,OAAO,4BAA4B,GAAG,OAAO,4BAA4B,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAAC;AAEpO;;;;;GAKG;AACH,qBAAa,wBAAwB;IAEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAS,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAqB,OAAO,CAAC,QAAQ,CAAC,eAAe;IADtH,yGAAyG;gBAC5E,IAAI,EAAE,MAAM,EAAkB,MAAM,GAAC,MAAmB,EAAkB,eAAe,GAAC,CAAC,SAAS,EAAC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAe;IACtK,gGAAgG;IAC1F,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,GAAG;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,qBAAqB,CAAA;KAAE,CAAC;IAW3H;;;;;;;;;OASG;IACG,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAE,qBAAgD,GAAG,OAAO,CAAC,QAAQ,CAAC;QAAE,eAAe,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAO5J;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,QAAQ,CAyB1F"}
@@ -2,13 +2,14 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { constants } from "node:fs";
3
3
  import { mkdir, open, readFile, rename, unlink } from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { parseEngineBrokerResponse, parseEngineBrokerV1TerminalResponse } from "./engineBrokerProtocol.js";
5
+ import { ENGINE_BROKER_VERSION, parseEngineBrokerResponse, parseEngineBrokerV1TerminalResponse, parseEngineBrokerV2TerminalResponse, parseEngineBrokerV3TerminalResponse } from "./engineBrokerProtocol.js";
6
6
  import { EMPTY_BROKER_TURN_LEDGER, parseBrokerTurnLedgerLines } from "./grokEngineBrokerLedger.js";
7
7
  export const ENGINE_BROKER_TURN_RECORD_V1 = "noopolis.daimon.engine-broker-turn.v1";
8
8
  export const ENGINE_BROKER_TURN_RECORD_V2 = "noopolis.daimon.engine-broker-turn.v2";
9
9
  // The digest deliberately excludes `limits` and the protocol version, so a v1
10
- // record written before the upgrade still identifies the same turn.
11
- const digest = (request) => createHash("sha256").update(JSON.stringify([request.turnId, request.agentId, request.wakeId, request.prompt, request.mcpEndpoint])).digest("hex");
10
+ // record written before the upgrade still identifies the same unclocked turn.
11
+ // A supplied process clock changes execution and must conflict with any other value.
12
+ const digest = (request) => createHash("sha256").update(JSON.stringify([request.turnId, request.agentId, request.wakeId, request.prompt, request.mcpEndpoint, ...(request.processClock === undefined ? [] : [request.processClock])])).digest("hex");
12
13
  const safe = (turnId) => `${createHash("sha256").update(turnId).digest("hex")}.json`;
13
14
  /**
14
15
  * Durable per-turn state. Record v2 stores the terminal response *with* its
@@ -97,7 +98,7 @@ export class EngineBrokerTurnRegistry {
97
98
  }
98
99
  /**
99
100
  * Strict record parser. v2 accepts exactly `{version,digest,state,bootId}`
100
- * plus `response` and its sealed `ledger` bytes when terminal, and the response must be a v2 terminal frame.
101
+ * plus `response` and its sealed `ledger` bytes when terminal, and the response may be a v2, v3 or v4 terminal frame (older frames upgrade on read).
101
102
  * v1 records keep their historical looser shape and are upgraded on read: no
102
103
  * usage (`null`), zero requests, `limitReason: "none"`, the declared model.
103
104
  */
@@ -126,7 +127,7 @@ export function parseEngineBrokerTurnRecord(text, model) {
126
127
  throw new Error("broker turn registry unavailable");
127
128
  }
128
129
  const accounting = { outcome: legacy.kind, usage: null, model, requests: 0, limitReason: "none" };
129
- const response = { ...legacy, ...accounting, version: "noopolis.daimon.engine-broker.v2" };
130
+ const response = { ...legacy, ...accounting, version: ENGINE_BROKER_VERSION };
130
131
  return { version: ENGINE_BROKER_TURN_RECORD_V1, ...base, response };
131
132
  }
132
133
  if (input.version !== ENGINE_BROKER_TURN_RECORD_V2)
@@ -138,7 +139,7 @@ export function parseEngineBrokerTurnRecord(text, model) {
138
139
  return { version: ENGINE_BROKER_TURN_RECORD_V2, ...base };
139
140
  let response;
140
141
  try {
141
- response = parseEngineBrokerResponse(input.response);
142
+ response = input.response?.version === "noopolis.daimon.engine-broker.v2" ? parseEngineBrokerV2TerminalResponse(input.response) : input.response?.version === "noopolis.daimon.engine-broker.v3" ? parseEngineBrokerV3TerminalResponse(input.response) : parseEngineBrokerResponse(input.response);
142
143
  }
143
144
  catch {
144
145
  throw new Error("broker turn registry unavailable");
@@ -11,6 +11,8 @@ export declare function startOrganizationRuntimeEngine(agent: OrganizationRuntim
11
11
  export declare function grokSandboxProtectedPaths(currentAgentId: string, organizationAgents: readonly OrganizationRuntimeAgentConfig[], sharedProtectedPaths?: readonly string[]): readonly string[];
12
12
  export declare function codexSandboxProtectedPaths(currentAgentId: string, currentAgent: OrganizationRuntimeAgentConfig, currentEngineHomePath: string, organizationAgents: readonly OrganizationRuntimeAgentConfig[], sharedProtectedPaths?: readonly string[]): readonly string[];
13
13
  export declare function codexSandboxReadablePaths(currentAgent: OrganizationRuntimeAgentConfig): readonly string[];
14
+ /** @internal The same callback used by the production session; no native process clock enters the engine environment. */
15
+ export declare function grokBrokerTurnFor(agent: OrganizationRuntimeAgentConfig, grokBroker: EngineBrokerTurnClient, wakeEnvironmentContext: import("../pi/piAgentWakeSupport.js").PiWakeEnvironmentContextRef): (prompt: string, endpoint: string, signal: AbortSignal) => Promise<string>;
14
16
  /**
15
17
  * CLI engines do not consume Pi's resource loader. Frame the same immutable
16
18
  * identity in JSON so arbitrary names/instructions cannot change its shape.
@@ -1 +1 @@
1
- {"version":3,"file":"engineDispatcher.d.ts","sourceRoot":"","sources":["../../src/runtime/engineDispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQpD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAE/E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAM7E;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,8BAA8B,EACrC,eAAe,EAAE,MAAM,EACvB,KAAK,CAAC,EAAE,UAAU,CAAC,gCAAgC,CAAC,UAAU,CAAC,CAAC,EAChE,aAAa,CAAC,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,sBAAsB,EACnC,kBAAkB,CAAC,EAAE,SAAS,8BAA8B,EAAE,EAC9D,oBAAoB,GAAE,SAAS,MAAM,EAAO,EAC5C,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA8CtB;AAED,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,SAAS,8BAA8B,EAAE,EAC7D,oBAAoB,GAAE,SAAS,MAAM,EAAO,GAC3C,SAAS,MAAM,EAAE,CAYnB;AAED,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,8BAA8B,EAC5C,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,SAAS,8BAA8B,EAAE,EAC7D,oBAAoB,GAAE,SAAS,MAAM,EAAO,GAC3C,SAAS,MAAM,EAAE,CAQnB;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,8BAA8B,GAC3C,SAAS,MAAM,EAAE,CAEnB;AAwDD;;;GAGG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,8BAA8B,EAAE,gBAAgB,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAkBxH"}
1
+ {"version":3,"file":"engineDispatcher.d.ts","sourceRoot":"","sources":["../../src/runtime/engineDispatcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAQpD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAE/E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAM7E;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,8BAA8B,EACrC,eAAe,EAAE,MAAM,EACvB,KAAK,CAAC,EAAE,UAAU,CAAC,gCAAgC,CAAC,UAAU,CAAC,CAAC,EAChE,aAAa,CAAC,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,sBAAsB,EACnC,kBAAkB,CAAC,EAAE,SAAS,8BAA8B,EAAE,EAC9D,oBAAoB,GAAE,SAAS,MAAM,EAAO,EAC5C,SAAS,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAiDtB;AAED,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,SAAS,8BAA8B,EAAE,EAC7D,oBAAoB,GAAE,SAAS,MAAM,EAAO,GAC3C,SAAS,MAAM,EAAE,CAYnB;AAED,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,8BAA8B,EAC5C,qBAAqB,EAAE,MAAM,EAC7B,kBAAkB,EAAE,SAAS,8BAA8B,EAAE,EAC7D,oBAAoB,GAAE,SAAS,MAAM,EAAO,GAC3C,SAAS,MAAM,EAAE,CAQnB;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,8BAA8B,GAC3C,SAAS,MAAM,EAAE,CAEnB;AAkDD,yHAAyH;AACzH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,8BAA8B,EAAE,UAAU,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,OAAO,6BAA6B,EAAE,2BAA2B,IAG5L,QAAQ,MAAM,EAAE,UAAU,MAAM,EAAE,QAAQ,WAAW,qBAI9D;AAED;;;GAGG;AACH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,8BAA8B,EAAE,gBAAgB,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAkBxH"}
@@ -1,4 +1,8 @@
1
+ import { brokerProcessClock } from "./grokBrokerTaskClock.js";
2
+ import { reportEngineTaskClockGap } from "../pi/engineTaskClock.js";
1
3
  import { attentionTools } from "./attention.js";
4
+ import { readTaskClock } from "./taskClock.js";
5
+ import { memoryClockOptions } from "../pi/memoryClock.js";
2
6
  import { grokDaimonToolName, grokMountedToolNamingRule } from "../contracts/grokWorkerContract.js";
3
7
  import path from "node:path";
4
8
  import { AGY_MAX_TOOL_TURNS, createCliSessionFactory, resolveCodexWakeTimeoutMs, resolveCodexWakeTokenCeiling, resolveEngineWakeLimitOverrides } from "../pi/cliSession.js";
@@ -14,6 +18,10 @@ import { recordCodexTurnRequests } from "./turnRequestLedger.js";
14
18
  * existing one-agent harness. Configuration never carries a command or env.
15
19
  */
16
20
  export async function startOrganizationRuntimeEngine(agent, controlTokenEnv, paths, agyBusAddress, grokBroker, organizationAgents, sharedProtectedPaths = [], attention) {
21
+ const clock = readTaskClock();
22
+ reportEngineTaskClockGap(agent.engine.kind);
23
+ if (agent.memory !== undefined)
24
+ memoryClockOptions(clock);
17
25
  // A declared Grok model is enforced by the broker proxy and worker config;
18
26
  // the direct path has neither, so it refuses rather than silently ignoring it.
19
27
  if (agent.engine.kind === "grok" && agent.engine.model !== undefined && grokBroker === undefined)
@@ -137,10 +145,14 @@ function adapterFor(agent, controlTokenEnv, verifyExecutable, executablePath, en
137
145
  } : {}) });
138
146
  return cliHarness(agent, sessionFactory, [controlTokenEnv], productionTools, wakeEnvironmentContext);
139
147
  }
140
- function grokBrokerTurnFor(agent, grokBroker, wakeEnvironmentContext) {
148
+ /** @internal The same callback used by the production session; no native process clock enters the engine environment. */
149
+ export function grokBrokerTurnFor(agent, grokBroker, wakeEnvironmentContext) {
141
150
  const limits = resolveEngineWakeLimitOverrides();
142
151
  const options = { ...(limits === undefined ? {} : { limits }), ...(agent.engine.model === undefined ? {} : { model: agent.engine.model }) };
143
- return (prompt, endpoint, signal) => grokBroker.turn(agent.id, wakeEnvironmentContext.current ?? "wake", prompt, endpoint, signal, options);
152
+ return (prompt, endpoint, signal) => {
153
+ const processClock = brokerProcessClock();
154
+ return grokBroker.turn(agent.id, wakeEnvironmentContext.current ?? "wake", prompt, endpoint, signal, { ...options, ...(processClock === undefined ? {} : { processClock }) });
155
+ };
144
156
  }
145
157
  /**
146
158
  * CLI engines do not consume Pi's resource loader. Frame the same immutable
@@ -1,7 +1,7 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, open, realpath, stat } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { spawn } from "node:child_process";
4
+ import { spawnRealTime as spawn } from "./taskClockProcess.js";
5
5
  import { createHash } from "node:crypto";
6
6
  import { cliChildEnvironment } from "../pi/cliEnvironment.js";
7
7
  import { hasRefreshablePortableCredential, portableCredentialSecretValues } from "./portableCredentialAuth.js";
@@ -9,6 +9,7 @@ export declare class DurableGrokBrokerCredentialAuthority {
9
9
  private staleWrite;
10
10
  private lastDigest;
11
11
  private generation;
12
+ private ancestorDigests;
12
13
  constructor(command: string, home: string, authFile?: string);
13
14
  initialize(): Promise<void>;
14
15
  accessToken(forceRefresh: boolean): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerCredentialAuthority.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerCredentialAuthority.ts"],"names":[],"mappings":"AAUA,qBAAa,oCAAoC;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAU,OAAO,CAAC,QAAQ,CAAC,QAAQ;IADtG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAAC,OAAO,CAAC,KAAK,CAAS;IAAA,OAAO,CAAC,WAAW,CAAO;IAAA,OAAO,CAAC,UAAU,CAAyB;IAAA,OAAO,CAAC,UAAU,CAAkB;IAAA,OAAO,CAAC,UAAU,CAAG;gBACrP,OAAO,EAAE,MAAM,EAAmB,IAAI,EAAE,MAAM,EAAmB,QAAQ,SAA+B;IAC/H,UAAU,IAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAInD,qBAAqB,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC;IACvE,OAAO,CAAC,OAAO;IAOT,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9D,OAAO,IAAG,OAAO;IACjB,OAAO,CAAC,SAAS;YACH,KAAK;CAKpB"}
1
+ {"version":3,"file":"grokBrokerCredentialAuthority.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerCredentialAuthority.ts"],"names":[],"mappings":"AAUA,qBAAa,oCAAoC;IAGnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAU,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFtG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAAC,OAAO,CAAC,KAAK,CAAS;IAAA,OAAO,CAAC,WAAW,CAAO;IAAA,OAAO,CAAC,UAAU,CAAyB;IAAA,OAAO,CAAC,UAAU,CAAkB;IAAA,OAAO,CAAC,UAAU,CAAG;IAClR,OAAO,CAAC,eAAe,CAAyB;gBACnB,OAAO,EAAE,MAAM,EAAmB,IAAI,EAAE,MAAM,EAAmB,QAAQ,SAA+B;IAC/H,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAInD,qBAAqB,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC;IACvE,OAAO,CAAC,OAAO;IAYT,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9D,OAAO,IAAG,OAAO;IACjB,OAAO,CAAC,SAAS;YACH,KAAK;CAKpB"}
@@ -6,7 +6,7 @@ import path from "node:path";
6
6
  import { readGrokBrokerCredential } from "./grokBrokerCredentialReader.js";
7
7
  import { refreshGrokBrokerCredential } from "./grokBrokerRefresh.js";
8
8
  import { EngineBrokerGenerationFence, EngineBrokerSingleflight } from "./engineBrokerSingleflight.js";
9
- import { parseBrokerCredentialJournal, recoverBrokerCredentialJournal } from "./engineBrokerCredentialJournal.js";
9
+ import { BROKER_CREDENTIAL_MAX_ANCESTORS, parseBrokerCredentialJournal, recoverBrokerCredentialJournal } from "./engineBrokerCredentialJournal.js";
10
10
  export class DurableGrokBrokerCredentialAuthority {
11
11
  command;
12
12
  home;
@@ -18,33 +18,52 @@ export class DurableGrokBrokerCredentialAuthority {
18
18
  staleWrite;
19
19
  lastDigest;
20
20
  generation = 0;
21
+ ancestorDigests = [];
21
22
  constructor(command, home, authFile = path.join(home, "auth.json")) {
22
23
  this.command = command;
23
24
  this.home = home;
24
25
  this.authFile = authFile;
25
26
  }
26
- async initialize() { if (this.initialized)
27
- return; const credential = await readGrokBrokerCredential(this.authFile); this.lastDigest = credential.digest; const file = path.join(this.home, ".daimon-broker", "credential-journal.json"); let handle; try {
28
- handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
29
- const stat = await handle.stat();
30
- if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 0o777) !== 0o600 || stat.size > 4096)
31
- throw unavailable();
32
- const journal = parseBrokerCredentialJournal(JSON.parse(await handle.readFile("utf8")));
33
- if (recoverBrokerCredentialJournal(journal, credential.digest) === "stale") {
27
+ async initialize() {
28
+ if (this.initialized)
29
+ return;
30
+ const credential = await readGrokBrokerCredential(this.authFile);
31
+ this.lastDigest = credential.digest;
32
+ const file = path.join(this.home, ".daimon-broker", "credential-journal.json");
33
+ let handle;
34
+ try {
35
+ handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
36
+ const stat = await handle.stat();
37
+ if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 0o777) !== 0o600 || stat.size > 65536)
38
+ throw unavailable();
39
+ const journal = parseBrokerCredentialJournal(JSON.parse(await handle.readFile("utf8")));
34
40
  this.generation = journal.generation;
35
- await this.markStale();
36
- throw unavailable();
41
+ if (recoverBrokerCredentialJournal(journal, credential.digest) === "stale") {
42
+ await this.markStale();
43
+ throw unavailable();
44
+ }
45
+ this.ancestorDigests = [...new Set([...(journal.ancestorDigests ?? []), journal.sourceDigest])]
46
+ .filter((digest) => digest !== credential.digest);
47
+ if (journal.state === "refreshing") {
48
+ // The durable credential still matches the pre-exchange source. Settle
49
+ // before serving it so writeback and drain need no subsequent refresh.
50
+ // No exchange completed: preserve the generation and existing lineage.
51
+ await this.write({ state: "promoted", generation: journal.generation,
52
+ sourceDigest: credential.digest, promotedDigest: credential.digest });
53
+ }
54
+ this.fence.restore(journal.generation);
37
55
  }
38
- this.generation = journal.generation;
39
- this.fence.restore(journal.generation);
40
- }
41
- catch (error) {
42
- if (error.code !== "ENOENT")
43
- throw unavailable();
56
+ catch (error) {
57
+ // Only a missing journal on open is an unjournaled bootstrap. A failed
58
+ // recovery write (including ENOENT) must never publish readiness.
59
+ if (handle !== undefined || error.code !== "ENOENT")
60
+ throw unavailable();
61
+ }
62
+ finally {
63
+ await handle?.close().catch(() => undefined);
64
+ }
65
+ this.initialized = true;
44
66
  }
45
- finally {
46
- await handle?.close().catch(() => undefined);
47
- } this.initialized = true; }
48
67
  async accessToken(forceRefresh) {
49
68
  if (!this.initialized)
50
69
  await this.initialize();
@@ -64,6 +83,11 @@ export class DurableGrokBrokerCredentialAuthority {
64
83
  const before = await readGrokBrokerCredential(this.authFile);
65
84
  if (rejectedTokenDigest !== undefined && createHash("sha256").update(before.accessToken).digest("hex") !== rejectedTokenDigest)
66
85
  return before.accessToken;
86
+ // Never spend a refresh whose lineage cannot be persisted. Advancing expiry
87
+ // normally clears the history, so this bound applies to one expiry epoch.
88
+ const ancestors = [...new Set([...this.ancestorDigests, before.digest])];
89
+ if (ancestors.length > BROKER_CREDENTIAL_MAX_ANCESTORS)
90
+ throw unavailable();
67
91
  try {
68
92
  await this.write({ state: "refreshing", generation, sourceDigest: before.digest });
69
93
  await refreshGrokBrokerCredential(this.command, this.home);
@@ -72,7 +96,9 @@ export class DurableGrokBrokerCredentialAuthority {
72
96
  throw unavailable();
73
97
  const next = this.fence.promote(generation);
74
98
  this.generation = next;
75
- await this.write({ state: "promoted", generation: next, sourceDigest: before.digest, promotedDigest: after.digest });
99
+ const ancestorDigests = before.expiresAt !== undefined && after.expiresAt !== undefined && after.expiresAt > before.expiresAt ? [] : ancestors;
100
+ await this.write({ state: "promoted", generation: next, sourceDigest: before.digest, promotedDigest: after.digest, ancestorDigests });
101
+ this.ancestorDigests = ancestorDigests;
76
102
  this.lastDigest = after.digest;
77
103
  return after.accessToken;
78
104
  }
@@ -100,7 +126,7 @@ export class DurableGrokBrokerCredentialAuthority {
100
126
  const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
101
127
  const handle = await open(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);
102
128
  try {
103
- await handle.writeFile(`${JSON.stringify({ version: "noopolis.daimon.broker-credential-journal.v1", ...value })}\n`);
129
+ await handle.writeFile(`${JSON.stringify({ version: "noopolis.daimon.broker-credential-journal.v1", ancestorDigests: this.ancestorDigests, ...value })}\n`);
104
130
  await handle.sync();
105
131
  }
106
132
  finally {
@@ -1,5 +1,6 @@
1
1
  export declare function readGrokBrokerCredential(file: string): Promise<Readonly<{
2
2
  accessToken: string;
3
3
  digest: string;
4
+ expiresAt?: number;
4
5
  }>>;
5
6
  //# sourceMappingURL=grokBrokerCredentialReader.d.ts.map