@noopolis/daimon 0.2.0 → 0.2.1

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 (255) 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/agySubscriptionBootstrap.js +1 -1
  67. package/dist/runtime/agySubscriptionRealm.js +1 -1
  68. package/dist/runtime/attention.d.ts +5 -0
  69. package/dist/runtime/attention.d.ts.map +1 -1
  70. package/dist/runtime/attention.js +14 -1
  71. package/dist/runtime/attentionDispatcher.d.ts +14 -2
  72. package/dist/runtime/attentionDispatcher.d.ts.map +1 -1
  73. package/dist/runtime/attentionDispatcher.js +127 -33
  74. package/dist/runtime/cli.js +5 -0
  75. package/dist/runtime/contract-manifest.json +1 -1
  76. package/dist/runtime/contract-manifest.sha256 +1 -1
  77. package/dist/runtime/engineBrokerCapabilities.d.ts +10 -0
  78. package/dist/runtime/engineBrokerCapabilities.d.ts.map +1 -1
  79. package/dist/runtime/engineBrokerCapabilities.js +10 -0
  80. package/dist/runtime/engineBrokerControlClient.d.ts +12 -1
  81. package/dist/runtime/engineBrokerControlClient.d.ts.map +1 -1
  82. package/dist/runtime/engineBrokerControlClient.js +139 -43
  83. package/dist/runtime/engineBrokerCredentialJournal.d.ts +2 -0
  84. package/dist/runtime/engineBrokerCredentialJournal.d.ts.map +1 -1
  85. package/dist/runtime/engineBrokerCredentialJournal.js +9 -0
  86. package/dist/runtime/engineBrokerInferenceProtocol.d.ts +3 -2
  87. package/dist/runtime/engineBrokerInferenceProtocol.d.ts.map +1 -1
  88. package/dist/runtime/engineBrokerInferenceProtocol.js +4 -4
  89. package/dist/runtime/engineBrokerMcpCallLog.d.ts +11 -0
  90. package/dist/runtime/engineBrokerMcpCallLog.d.ts.map +1 -1
  91. package/dist/runtime/engineBrokerMcpCallLog.js +13 -1
  92. package/dist/runtime/engineBrokerMcpFacade.d.ts +5 -2
  93. package/dist/runtime/engineBrokerMcpFacade.d.ts.map +1 -1
  94. package/dist/runtime/engineBrokerMcpFacade.js +5 -2
  95. package/dist/runtime/engineBrokerNativeClient.js +2 -2
  96. package/dist/runtime/engineBrokerProtocol.d.ts +42 -10
  97. package/dist/runtime/engineBrokerProtocol.d.ts.map +1 -1
  98. package/dist/runtime/engineBrokerProtocol.js +97 -14
  99. package/dist/runtime/engineBrokerSealLedger.d.ts.map +1 -1
  100. package/dist/runtime/engineBrokerSealLedger.js +9 -0
  101. package/dist/runtime/engineBrokerService.d.ts +8 -2
  102. package/dist/runtime/engineBrokerService.d.ts.map +1 -1
  103. package/dist/runtime/engineBrokerService.js +95 -37
  104. package/dist/runtime/engineBrokerServiceCli.js +1 -1
  105. package/dist/runtime/engineBrokerServiceConfig.d.ts +5 -0
  106. package/dist/runtime/engineBrokerServiceConfig.d.ts.map +1 -1
  107. package/dist/runtime/engineBrokerServiceConfig.js +47 -9
  108. package/dist/runtime/engineBrokerTurnRegistry.d.ts +1 -1
  109. package/dist/runtime/engineBrokerTurnRegistry.d.ts.map +1 -1
  110. package/dist/runtime/engineBrokerTurnRegistry.js +7 -6
  111. package/dist/runtime/engineDispatcher.d.ts +2 -0
  112. package/dist/runtime/engineDispatcher.d.ts.map +1 -1
  113. package/dist/runtime/engineDispatcher.js +14 -2
  114. package/dist/runtime/engineReadiness.js +1 -1
  115. package/dist/runtime/grokBrokerCredentialAuthority.d.ts +1 -0
  116. package/dist/runtime/grokBrokerCredentialAuthority.d.ts.map +1 -1
  117. package/dist/runtime/grokBrokerCredentialAuthority.js +48 -22
  118. package/dist/runtime/grokBrokerCredentialReader.d.ts +1 -0
  119. package/dist/runtime/grokBrokerCredentialReader.d.ts.map +1 -1
  120. package/dist/runtime/grokBrokerCredentialReader.js +2 -1
  121. package/dist/runtime/grokBrokerProjection.d.ts +9 -1
  122. package/dist/runtime/grokBrokerProjection.d.ts.map +1 -1
  123. package/dist/runtime/grokBrokerProjection.js +11 -4
  124. package/dist/runtime/grokBrokerProviderClock.d.ts +26 -0
  125. package/dist/runtime/grokBrokerProviderClock.d.ts.map +1 -0
  126. package/dist/runtime/grokBrokerProviderClock.js +133 -0
  127. package/dist/runtime/grokBrokerProxy.d.ts +17 -3
  128. package/dist/runtime/grokBrokerProxy.d.ts.map +1 -1
  129. package/dist/runtime/grokBrokerProxy.js +73 -9
  130. package/dist/runtime/grokBrokerProxyJson.d.ts +10 -0
  131. package/dist/runtime/grokBrokerProxyJson.d.ts.map +1 -0
  132. package/dist/runtime/grokBrokerProxyJson.js +31 -0
  133. package/dist/runtime/grokBrokerProxyRequest.d.ts.map +1 -1
  134. package/dist/runtime/grokBrokerProxyRequest.js +17 -6
  135. package/dist/runtime/grokBrokerRealmLease.js +1 -1
  136. package/dist/runtime/grokBrokerRefresh.js +1 -1
  137. package/dist/runtime/grokBrokerRunBudget.d.ts +20 -0
  138. package/dist/runtime/grokBrokerRunBudget.d.ts.map +1 -0
  139. package/dist/runtime/grokBrokerRunBudget.js +85 -0
  140. package/dist/runtime/grokBrokerSlot.d.ts +6 -0
  141. package/dist/runtime/grokBrokerSlot.d.ts.map +1 -0
  142. package/dist/runtime/grokBrokerSlot.js +21 -0
  143. package/dist/runtime/grokBrokerTaskClock.d.ts +32 -0
  144. package/dist/runtime/grokBrokerTaskClock.d.ts.map +1 -0
  145. package/dist/runtime/grokBrokerTaskClock.js +120 -0
  146. package/dist/runtime/grokBrokerTurnMeter.d.ts +55 -3
  147. package/dist/runtime/grokBrokerTurnMeter.d.ts.map +1 -1
  148. package/dist/runtime/grokBrokerTurnMeter.js +90 -4
  149. package/dist/runtime/grokBrokerWorkerConfig.d.ts +5 -2
  150. package/dist/runtime/grokBrokerWorkerConfig.d.ts.map +1 -1
  151. package/dist/runtime/grokBrokerWorkerConfig.js +22 -3
  152. package/dist/runtime/grokEngineBroker.d.ts +43 -3
  153. package/dist/runtime/grokEngineBroker.d.ts.map +1 -1
  154. package/dist/runtime/grokEngineBroker.js +159 -71
  155. package/dist/runtime/grokEngineBrokerTurn.d.ts +32 -8
  156. package/dist/runtime/grokEngineBrokerTurn.d.ts.map +1 -1
  157. package/dist/runtime/grokEngineBrokerTurn.js +204 -21
  158. package/dist/runtime/grokEngineBrokerTurnEnd.d.ts +88 -0
  159. package/dist/runtime/grokEngineBrokerTurnEnd.d.ts.map +1 -0
  160. package/dist/runtime/grokEngineBrokerTurnEnd.js +148 -0
  161. package/dist/runtime/grokInferenceProxy.d.ts.map +1 -1
  162. package/dist/runtime/grokInferenceProxy.js +3 -0
  163. package/dist/runtime/grokSubscriptionRealm.js +1 -1
  164. package/dist/runtime/grokWorkerAttestation.d.ts +9 -4
  165. package/dist/runtime/grokWorkerAttestation.d.ts.map +1 -1
  166. package/dist/runtime/grokWorkerAttestation.js +2 -2
  167. package/dist/runtime/grokWorkerHomeAttestation.d.ts +4 -2
  168. package/dist/runtime/grokWorkerHomeAttestation.d.ts.map +1 -1
  169. package/dist/runtime/grokWorkerHomeAttestation.js +10 -4
  170. package/dist/runtime/index.d.ts +7 -2
  171. package/dist/runtime/index.d.ts.map +1 -1
  172. package/dist/runtime/index.js +5 -1
  173. package/dist/runtime/native/daimon-engine-broker +0 -0
  174. package/dist/runtime/organizationRuntime.d.ts +3 -0
  175. package/dist/runtime/organizationRuntime.d.ts.map +1 -1
  176. package/dist/runtime/organizationRuntimeAdmission.d.ts +6 -0
  177. package/dist/runtime/organizationRuntimeAdmission.d.ts.map +1 -0
  178. package/dist/runtime/organizationRuntimeAdmission.js +22 -0
  179. package/dist/runtime/organizationRuntimeControl.d.ts +9 -0
  180. package/dist/runtime/organizationRuntimeControl.d.ts.map +1 -1
  181. package/dist/runtime/organizationRuntimeControl.js +72 -24
  182. package/dist/runtime/organizationRuntimeHost.d.ts.map +1 -1
  183. package/dist/runtime/organizationRuntimeHost.js +13 -4
  184. package/dist/runtime/organizationRuntimeParsing.d.ts.map +1 -1
  185. package/dist/runtime/organizationRuntimeParsing.js +4 -1
  186. package/dist/runtime/organizationRuntimeReadiness.d.ts.map +1 -1
  187. package/dist/runtime/organizationRuntimeReadiness.js +8 -0
  188. package/dist/runtime/organizationRuntimeRecording.d.ts +7 -0
  189. package/dist/runtime/organizationRuntimeRecording.d.ts.map +1 -0
  190. package/dist/runtime/organizationRuntimeRecording.js +58 -0
  191. package/dist/runtime/physicalReadiness.d.ts +12 -0
  192. package/dist/runtime/physicalReadiness.d.ts.map +1 -1
  193. package/dist/runtime/physicalReadiness.js +43 -9
  194. package/dist/runtime/productionAgentTools.d.ts.map +1 -1
  195. package/dist/runtime/productionAgentTools.js +13 -5
  196. package/dist/runtime/relocateRuntimeConfig.d.ts +14 -0
  197. package/dist/runtime/relocateRuntimeConfig.d.ts.map +1 -0
  198. package/dist/runtime/relocateRuntimeConfig.js +38 -0
  199. package/dist/runtime/schedule.d.ts.map +1 -1
  200. package/dist/runtime/schedule.js +12 -7
  201. package/dist/runtime/storeCoordination.d.ts +10 -0
  202. package/dist/runtime/storeCoordination.d.ts.map +1 -1
  203. package/dist/runtime/storeCoordination.js +23 -0
  204. package/dist/runtime/taskClock.d.ts +42 -0
  205. package/dist/runtime/taskClock.d.ts.map +1 -0
  206. package/dist/runtime/taskClock.js +102 -0
  207. package/dist/runtime/taskClockProcess.d.ts +17 -0
  208. package/dist/runtime/taskClockProcess.d.ts.map +1 -0
  209. package/dist/runtime/taskClockProcess.js +58 -0
  210. package/dist/runtime/turnRequestLedger.d.ts +0 -5
  211. package/dist/runtime/turnRequestLedger.d.ts.map +1 -1
  212. package/dist/runtime/turnRequestLedger.js +23 -21
  213. package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts +34 -0
  214. package/dist/runtime/wakeAcceptanceClaimDiagnostics.d.ts.map +1 -0
  215. package/dist/runtime/wakeAcceptanceClaimDiagnostics.js +79 -0
  216. package/dist/runtime/wakeAcceptanceIndex.d.ts +55 -0
  217. package/dist/runtime/wakeAcceptanceIndex.d.ts.map +1 -0
  218. package/dist/runtime/wakeAcceptanceIndex.js +158 -0
  219. package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts +9 -1
  220. package/dist/runtime/wakeAcceptanceProcessIdentity.d.ts.map +1 -1
  221. package/dist/runtime/wakeAcceptanceProcessIdentity.js +5 -1
  222. package/dist/runtime/wakeAcceptanceRecord.d.ts +2 -0
  223. package/dist/runtime/wakeAcceptanceRecord.d.ts.map +1 -1
  224. package/dist/runtime/wakeAcceptanceRecord.js +4 -2
  225. package/dist/runtime/wakeAcceptanceRetention.d.ts +25 -1
  226. package/dist/runtime/wakeAcceptanceRetention.d.ts.map +1 -1
  227. package/dist/runtime/wakeAcceptanceRetention.js +28 -2
  228. package/dist/runtime/wakeAcceptanceStore.d.ts +8 -8
  229. package/dist/runtime/wakeAcceptanceStore.d.ts.map +1 -1
  230. package/dist/runtime/wakeAcceptanceStore.js +66 -63
  231. package/dist/runtime/wakeFuse.d.ts +58 -3
  232. package/dist/runtime/wakeFuse.d.ts.map +1 -1
  233. package/dist/runtime/wakeFuse.js +106 -7
  234. package/dist/runtime/wakeMomentFs.d.ts +27 -0
  235. package/dist/runtime/wakeMomentFs.d.ts.map +1 -0
  236. package/dist/runtime/wakeMomentFs.js +223 -0
  237. package/dist/runtime/wakeMomentHistory.d.ts +26 -0
  238. package/dist/runtime/wakeMomentHistory.d.ts.map +1 -0
  239. package/dist/runtime/wakeMomentHistory.js +109 -0
  240. package/dist/runtime/wakeMomentIo.d.ts +22 -0
  241. package/dist/runtime/wakeMomentIo.d.ts.map +1 -0
  242. package/dist/runtime/wakeMomentIo.js +114 -0
  243. package/dist/runtime/wakeMomentRecorder.d.ts +33 -0
  244. package/dist/runtime/wakeMomentRecorder.d.ts.map +1 -0
  245. package/dist/runtime/wakeMomentRecorder.js +81 -0
  246. package/dist/runtime/wakeMomentRetention.d.ts +7 -0
  247. package/dist/runtime/wakeMomentRetention.d.ts.map +1 -0
  248. package/dist/runtime/wakeMomentRetention.js +143 -0
  249. package/dist/runtime/wakeMomentRows.d.ts +8 -0
  250. package/dist/runtime/wakeMomentRows.d.ts.map +1 -0
  251. package/dist/runtime/wakeMomentRows.js +66 -0
  252. package/dist/runtime/wakeMomentSnapshot.d.ts +56 -0
  253. package/dist/runtime/wakeMomentSnapshot.d.ts.map +1 -0
  254. package/dist/runtime/wakeMomentSnapshot.js +248 -0
  255. package/package.json +4 -4
@@ -0,0 +1,133 @@
1
+ import { parseBrokerProcessClock } from "./grokBrokerTaskClock.js";
2
+ import { parseGrokBrokerProxyJson } from "./grokBrokerProxyJson.js";
3
+ export class GrokBrokerProviderClockRefusal extends Error {
4
+ reason;
5
+ constructor(reason) {
6
+ super(`grok broker proxy refused: ${reason}`);
7
+ this.reason = reason;
8
+ }
9
+ }
10
+ const DATE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/u;
11
+ const DATE_LINE = /^Today's date: ([0-9]{4}-[0-9]{2}-[0-9]{2})$/u;
12
+ const DATE_LIKE = /today['’]?s\s+date\b/iu;
13
+ const MAX_TASK_MS = 253_402_300_799_999;
14
+ /** One processClock, owned by one turn; never reads the broker's ambient TZ. */
15
+ export class GrokBrokerProviderClock {
16
+ now;
17
+ offsetMs;
18
+ observation = {
19
+ requests: 0, linesRewritten: 0, requestsWithoutDateLine: 0, linesIgnored: 0,
20
+ injectedDate: null, taskDate: null
21
+ };
22
+ constructor(processClock, now = () => Date.now()) {
23
+ this.now = now;
24
+ this.offsetMs = Math.round(Number(parseBrokerProcessClock(processClock)) * 1000);
25
+ }
26
+ rewrite(body) {
27
+ this.observation = { ...this.observation, requests: this.observation.requests + 1 };
28
+ let parsed;
29
+ try {
30
+ parsed = parseGrokBrokerProxyJson(body);
31
+ }
32
+ catch {
33
+ throw new GrokBrokerProviderClockRefusal("request_body_rejected");
34
+ }
35
+ const taskMs = this.now() + this.offsetMs;
36
+ if (!Number.isSafeInteger(taskMs) || taskMs < 0 || taskMs > MAX_TASK_MS) {
37
+ throw new GrokBrokerProviderClockRefusal("task_clock_date_range");
38
+ }
39
+ // The native launcher pins TZ=UTC. Compute once per request, including
40
+ // history resends; a long turn may cross midnight between requests.
41
+ const taskDate = new Date(taskMs).toISOString().slice(0, 10);
42
+ let lines = 0;
43
+ let ignored = 0;
44
+ let injectedDate = null;
45
+ const countIgnored = (text) => text.split(/\r\n|\r|\n/u).filter((line) => DATE_LIKE.test(line)).length;
46
+ const rewriteText = (text, injected) => {
47
+ const block = injected ? /<user_info>([\s\S]*?)<\/user_info>/u.exec(text) : null;
48
+ if (block === null) {
49
+ ignored += countIgnored(text);
50
+ return text;
51
+ }
52
+ const before = text.slice(0, block.index), after = text.slice(block.index + block[0].length);
53
+ ignored += countIgnored(before) + countIgnored(after);
54
+ const rewritten = block[1].split(/(\r\n|\r|\n)/u).map((line) => {
55
+ if (!DATE_LIKE.test(line))
56
+ return line;
57
+ const match = DATE_LINE.exec(line);
58
+ if (match === null)
59
+ throw new GrokBrokerProviderClockRefusal("task_clock_date_format");
60
+ lines++;
61
+ injectedDate = match[1];
62
+ return `Today's date: ${taskDate}`;
63
+ }).join("");
64
+ return `${before}<user_info>${rewritten}</user_info>${after}`;
65
+ };
66
+ if (Array.isArray(parsed.value.messages)) {
67
+ let firstUser = true;
68
+ for (const message of parsed.value.messages) {
69
+ if (!isRecord(message))
70
+ continue;
71
+ const injected = message.role === "user" && firstUser;
72
+ if (message.role === "user")
73
+ firstUser = false;
74
+ if (typeof message.content === "string") {
75
+ message.content = rewriteText(message.content, injected);
76
+ }
77
+ else if (Array.isArray(message.content)) {
78
+ const parts = message.content.filter((part) => isRecord(part) && part.type === "text" && typeof part.text === "string");
79
+ // A block or date can straddle text parts. Replacements have exactly
80
+ // the same width, so slicing restores every original part boundary.
81
+ const rewritten = rewriteText(parts.map((part) => part.text).join(""), injected);
82
+ let start = 0;
83
+ for (const part of parts) {
84
+ const end = start + part.text.length;
85
+ part.text = rewritten.slice(start, end);
86
+ start = end;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ // Commit only after the entire body passes: a refused request rewrites no
92
+ // lines upstream, even when an earlier line had the recognized format.
93
+ this.observation = {
94
+ requests: this.observation.requests,
95
+ linesRewritten: this.observation.linesRewritten + lines,
96
+ requestsWithoutDateLine: this.observation.requestsWithoutDateLine + (lines === 0 ? 1 : 0),
97
+ linesIgnored: (this.observation.linesIgnored ?? 0) + ignored,
98
+ injectedDate: lines === 0 ? this.observation.injectedDate : injectedDate,
99
+ taskDate: lines === 0 ? this.observation.taskDate : taskDate
100
+ };
101
+ return lines === 0 ? parsed.body : Buffer.from(JSON.stringify(parsed.value));
102
+ }
103
+ snapshot() {
104
+ return { ...this.observation };
105
+ }
106
+ }
107
+ const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
108
+ /** Closed, dates-only observation on the v3 terminal frame and durable seal. */
109
+ export function parseGrokBrokerProviderClockObservation(value) {
110
+ if (!isRecord(value))
111
+ throw new TypeError("invalid broker frame");
112
+ const fields = ["requests", "linesRewritten", "requestsWithoutDateLine", "injectedDate", "taskDate"];
113
+ if (Object.keys(value).some((key) => !fields.includes(key) && key !== "linesIgnored") ||
114
+ fields.some((key) => !Object.hasOwn(value, key))) {
115
+ throw new TypeError("invalid broker frame");
116
+ }
117
+ for (const key of [...fields.slice(0, 3), ...(Object.hasOwn(value, "linesIgnored") ? ["linesIgnored"] : [])]) {
118
+ if (!Number.isSafeInteger(value[key]) || value[key] < 0) {
119
+ throw new TypeError("invalid broker frame");
120
+ }
121
+ }
122
+ const result = value;
123
+ if (result.requestsWithoutDateLine > result.requests ||
124
+ (result.linesRewritten > 0 && result.requestsWithoutDateLine === result.requests)) {
125
+ throw new TypeError("invalid broker frame");
126
+ }
127
+ for (const date of [result.injectedDate, result.taskDate]) {
128
+ if (result.linesRewritten === 0 ? date !== null : typeof date !== "string" || !DATE.test(date)) {
129
+ throw new TypeError("invalid broker frame");
130
+ }
131
+ }
132
+ return { ...result };
133
+ }
@@ -1,12 +1,16 @@
1
+ import { type GrokBrokerRunBudget } from "./grokBrokerRunBudget.js";
2
+ import { type IncomingMessage, type ServerResponse } from "node:http";
1
3
  import { EngineBrokerCapabilities } from "./engineBrokerCapabilities.js";
2
4
  import { type GrokBrokerModelPolicy } from "./grokBrokerModelPolicy.js";
3
5
  import { authorizeGrokBrokerProxyRequest } from "./grokBrokerProxyRequest.js";
4
6
  import { type GrokBrokerTurnMeter } from "./grokBrokerTurnMeter.js";
5
7
  import type { GrokInferenceGrants } from "./grokInferenceGrants.js";
8
+ import { type GrokBrokerProviderClock } from "./grokBrokerProviderClock.js";
6
9
  /** One running turn as the proxy sees it: its declared model/effort and its spend gate. */
7
10
  export type GrokBrokerProxyTurn = Readonly<{
8
11
  policy: GrokBrokerModelPolicy;
9
12
  meter: GrokBrokerTurnMeter;
13
+ clock?: GrokBrokerProviderClock;
10
14
  }>;
11
15
  export type GrokBrokerCredentialAuthority = Readonly<{
12
16
  accessToken(forceRefresh: boolean): Promise<string>;
@@ -30,15 +34,25 @@ export type GrokBrokerUpstream = (request: ReturnType<typeof authorizeGrokBroker
30
34
  * `grokInferenceProxy.ts`; every other bearer is looked up only among turn
31
35
  * capabilities. Without `grants` a prefixed bearer is simply refused.
32
36
  */
33
- export declare function startGrokBrokerProxy(authority: GrokBrokerCredentialAuthority, upstream?: GrokBrokerUpstream, policy?: GrokBrokerModelPolicy, listenPort?: number, grants?: GrokInferenceGrants): Promise<Readonly<{
37
+ export declare function startGrokBrokerProxy(authority: GrokBrokerCredentialAuthority, upstream?: GrokBrokerUpstream, policy?: GrokBrokerModelPolicy, listenPort?: number, grants?: GrokInferenceGrants, runBudget?: GrokBrokerRunBudget): Promise<{
34
38
  port: number;
39
+ close: () => Promise<void>;
35
40
  capabilities: EngineBrokerCapabilities;
36
41
  registerIsolationGuard(turnId: string, guard: () => Promise<void>): void;
37
42
  revokeIsolationGuard(turnId: string): void;
38
43
  registerTurn(turnId: string, turn: GrokBrokerProxyTurn): void;
39
44
  revokeTurn(turnId: string): void;
40
- close(): Promise<void>;
41
- }>>;
45
+ serve: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
46
+ }>;
47
+ /** @internal The production request handler, usable without opening a socket. */
48
+ export declare function createGrokBrokerProxyHandler(authority: GrokBrokerCredentialAuthority, upstream?: GrokBrokerUpstream, policy?: GrokBrokerModelPolicy, grants?: GrokInferenceGrants, runBudget?: GrokBrokerRunBudget): {
49
+ capabilities: EngineBrokerCapabilities;
50
+ registerIsolationGuard(turnId: string, guard: () => Promise<void>): void;
51
+ revokeIsolationGuard(turnId: string): void;
52
+ registerTurn(turnId: string, turn: GrokBrokerProxyTurn): void;
53
+ revokeTurn(turnId: string): void;
54
+ serve: (request: IncomingMessage, response: ServerResponse) => Promise<void>;
55
+ };
42
56
  /**
43
57
  * A refusal the caller must not retry.
44
58
  *
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerProxy.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxy.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtI,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAE9E,OAAO,EAAsD,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGxH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,qBAAqB,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC;AAE1G,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IAAE,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,qBAAqB,CAAC,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AACjQ,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,+BAA+B,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAAC;AAE3N;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EAAE,MAAM,GAAE,qBAAwD,EAAE,UAAU,SAAS,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,wBAAwB,CAAC;IAAC,sBAAsB,CAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAC,MAAI,OAAO,CAAC,IAAI,CAAC,GAAE,IAAI,CAAC;IAAC,oBAAoB,CAAC,MAAM,EAAC,MAAM,GAAE,IAAI,CAAC;IAAC,YAAY,CAAC,MAAM,EAAC,MAAM,EAAC,IAAI,EAAC,mBAAmB,GAAE,IAAI,CAAC;IAAC,UAAU,CAAC,MAAM,EAAC,MAAM,GAAE,IAAI,CAAC;IAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAAC,CAMtiB;AAED;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CACpC"}
1
+ {"version":3,"file":"grokBrokerProxy.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAExF,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAIpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtI,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAE9E,OAAO,EAA2E,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG7I,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAkC,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5G,2FAA2F;AAC3F,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IAAE,WAAW,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,qBAAqB,CAAC,CAAC,mBAAmB,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,mBAAmB,CAAC,EAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AACjQ,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,+BAA+B,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,CAAC;AAE3N;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,UAAU,SAAS,EACrF,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;;;mCAoC5B,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;GA5B7D;AAED,iFAAiF;AACjF,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,6BAA6B,EAAE,QAAQ,GAAE,kBAAoC,EACxF,MAAM,GAAE,qBAAwD,EAAE,MAAM,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,EAAE,mBAAmB;;mCAc9F,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;iCACpC,MAAM;yBACd,MAAM,QAAQ,mBAAmB;uBAMnC,MAAM;qBACR,eAAe,YAAY,cAAc;EAI7D;AAED;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CACpC"}
@@ -1,3 +1,4 @@
1
+ import { GrokBrokerRunLimit } from "./grokBrokerRunBudget.js";
1
2
  import { createHash } from "node:crypto";
2
3
  import { createServer } from "node:http";
3
4
  import { redactCredentialText } from "../core/credentialRedaction.js";
@@ -7,9 +8,10 @@ import { ENGINE_BROKER_AUTH_STALE } from "./engineBrokerProtocol.js";
7
8
  import { DEFAULT_GROK_BROKER_MODEL_POLICY, parseGrokBrokerModelPolicy } from "./grokBrokerModelPolicy.js";
8
9
  import { authorizeGrokBrokerProxyRequest } from "./grokBrokerProxyRequest.js";
9
10
  import { GROK_SESSION_TITLE_SINK_KEY } from "./grokBrokerWorkerConfig.js";
10
- import { parseGrokResponseToolNames, parseGrokUpstreamUsage } from "./grokBrokerTurnMeter.js";
11
+ import { parseGrokFinalReply, parseGrokResponseToolNames, parseGrokUpstreamUsage } from "./grokBrokerTurnMeter.js";
11
12
  import { GROK_ENGINE_BROKER } from "../contracts/runtimeContractManifest.js";
12
13
  import { serveGrokInferenceGrant } from "./grokInferenceProxy.js";
14
+ import { GrokBrokerProviderClockRefusal } from "./grokBrokerProviderClock.js";
13
15
  /**
14
16
  * `policy` is the fallback declared model/effort (closed list); a registered
15
17
  * turn's own policy wins. A request whose turn has no registered meter is
@@ -21,15 +23,48 @@ import { serveGrokInferenceGrant } from "./grokInferenceProxy.js";
21
23
  * `grokInferenceProxy.ts`; every other bearer is looked up only among turn
22
24
  * capabilities. Without `grants` a prefixed bearer is simply refused.
23
25
  */
24
- export async function startGrokBrokerProxy(authority, upstream = defaultUpstream, policy = DEFAULT_GROK_BROKER_MODEL_POLICY, listenPort = 43_123, grants) {
26
+ export async function startGrokBrokerProxy(authority, upstream = defaultUpstream, policy = DEFAULT_GROK_BROKER_MODEL_POLICY, listenPort = 43_123, grants, runBudget) {
27
+ const handler = createGrokBrokerProxyHandler(authority, upstream, policy, grants, runBudget);
28
+ const server = createServer((request, response) => { void handler.serve(request, response); });
29
+ await new Promise((resolve, reject) => {
30
+ server.once("error", reject);
31
+ server.listen(listenPort, "127.0.0.1", () => { server.off("error", reject); resolve(); });
32
+ });
33
+ const address = server.address();
34
+ return {
35
+ ...handler, port: address.port,
36
+ close: () => new Promise((resolve, reject) => {
37
+ server.close((error) => error === undefined ? resolve() : reject(error));
38
+ // A worker's idle pooled socket otherwise leaves close() without a bound.
39
+ server.closeAllConnections();
40
+ })
41
+ };
42
+ }
43
+ /** @internal The production request handler, usable without opening a socket. */
44
+ export function createGrokBrokerProxyHandler(authority, upstream = defaultUpstream, policy = DEFAULT_GROK_BROKER_MODEL_POLICY, grants, runBudget) {
45
+ const admittedUpstream = async (request, signal) => {
46
+ signal?.throwIfAborted();
47
+ await runBudget?.reserve();
48
+ signal?.throwIfAborted();
49
+ return upstream(request, signal);
50
+ };
25
51
  const declared = parseGrokBrokerModelPolicy(policy);
26
52
  const capabilities = new EngineBrokerCapabilities();
27
53
  const guards = new Map();
28
54
  const turns = new Map();
29
- const server = createServer((request, response) => { void serve(request, response, authority, upstream, capabilities, guards, turns, declared, grants); });
30
- await new Promise((resolve, reject) => { server.once("error", reject); server.listen(listenPort, "127.0.0.1", () => { server.off("error", reject); resolve(); }); });
31
- const address = server.address();
32
- return { port: address.port, capabilities, registerIsolationGuard(turnId, guard) { guards.set(turnId, guard); }, revokeIsolationGuard(turnId) { guards.delete(turnId); }, registerTurn(turnId, turn) { turns.set(turnId, { policy: parseGrokBrokerModelPolicy(turn.policy), meter: turn.meter }); }, revokeTurn(turnId) { turns.delete(turnId); }, close: () => new Promise((resolve, reject) => { server.close((error) => error === undefined ? resolve() : reject(error)); /* `close` waits for every open connection, and a worker keeps its client pool's socket to this proxy open with nothing here accounting for it — so that wait has no bound. Ending them is this listener's to do, exactly as the MCP facade does. */ /* `close` waits for every open connection, and a worker keeps its client pool's socket to this proxy open with nothing here accounting for it — so that wait has no bound. Ending them is this listener's to do, exactly as the MCP facade does. */ server.closeAllConnections(); }) };
55
+ return {
56
+ capabilities,
57
+ registerIsolationGuard(turnId, guard) { guards.set(turnId, guard); },
58
+ revokeIsolationGuard(turnId) { guards.delete(turnId); },
59
+ registerTurn(turnId, turn) {
60
+ turns.set(turnId, {
61
+ policy: parseGrokBrokerModelPolicy(turn.policy), meter: turn.meter,
62
+ ...(turn.clock === undefined ? {} : { clock: turn.clock })
63
+ });
64
+ },
65
+ revokeTurn(turnId) { turns.delete(turnId); },
66
+ serve: (request, response) => serve(request, response, authority, admittedUpstream, capabilities, guards, turns, declared, grants)
67
+ };
33
68
  }
34
69
  /**
35
70
  * A refusal the caller must not retry.
@@ -55,7 +90,7 @@ async function serve(request, response, authority, upstream, capabilities, guard
55
90
  // child and launcher diagnostics are. Nothing reads them but {@link brokerFaultCause}.
56
91
  const secrets = [];
57
92
  try {
58
- const body = await readBody(request);
93
+ let body = await readBody(request);
59
94
  const headers = Object.fromEntries(Object.entries(request.headers).map(([key, value]) => [key, Array.isArray(value) ? value[0] : value]));
60
95
  titleSink = headers.authorization === `Bearer ${GROK_SESSION_TITLE_SINK_KEY}`;
61
96
  const match = headers.authorization?.match(/^Bearer ([A-Za-z0-9_-]{40,})$/u);
@@ -78,6 +113,17 @@ async function serve(request, response, authority, upstream, capabilities, guard
78
113
  catch (error) {
79
114
  throw new GrokBrokerProxyRefusal("worker_isolation_unverified");
80
115
  }
116
+ // Neutralize engine context before the existing body gate or bearer read.
117
+ // The clock belongs to this turn, never to the slot's installed variant.
118
+ try {
119
+ if (turn.clock !== undefined)
120
+ body = Buffer.from(turn.clock.rewrite(body));
121
+ }
122
+ catch (error) {
123
+ if (error instanceof GrokBrokerProviderClockRefusal)
124
+ throw new GrokBrokerProxyRefusal(error.reason);
125
+ throw error;
126
+ }
81
127
  // A fenced realm is not a transient fault: the credential is gone until an
82
128
  // operator re-logs in, and 503 made Grok blind-retry it (observed: fifteen
83
129
  // retries over five minutes, $0 spent, nothing learned). Named, 400, and
@@ -102,7 +148,7 @@ async function serve(request, response, authority, upstream, capabilities, guard
102
148
  response.end('{"error":"turn request in flight"}');
103
149
  return;
104
150
  }
105
- settle = (usage, toolCalls) => { turn.meter.settle(admission.index, usage, body.byteLength, toolCalls); settle = undefined; };
151
+ settle = (usage, toolCalls, finalReply) => { turn.meter.settle(admission.index, usage, body.byteLength, toolCalls, finalReply); settle = undefined; };
106
152
  let result = await upstream(prepared, admission.signal);
107
153
  if (result.status === 401) {
108
154
  token = authority.refreshAfterRejection ? await authority.refreshAfterRejection(rejectedDigest) : await authority.accessToken(true);
@@ -118,12 +164,21 @@ async function serve(request, response, authority, upstream, capabilities, guard
118
164
  // is already buffered here for its usage block, so what the model tried to
119
165
  // call is in hand. A decoder fault records no attempt rather than a false
120
166
  // empty one, and never disturbs the turn.
121
- settle?.(usageOrEstimate(result.body, result.headers["content-type"]), toolCallsOrNothing(result.body, result.headers["content-type"]));
167
+ // A successful answer that called nothing and stopped is the turn's final
168
+ // reply: the broker ends a worker that finished but did not exit on it
169
+ // (`grokEngineBrokerTurnEnd.ts`), and reports it when the launcher refused
170
+ // the worker's stdout. Observation only, exactly like the tool names.
171
+ settle?.(usageOrEstimate(result.body, result.headers["content-type"]), toolCallsOrNothing(result.body, result.headers["content-type"]), result.status === 200 ? finalReplyOrNothing(result.body, result.headers["content-type"]) : undefined);
122
172
  response.writeHead(result.status, { "content-type": result.headers["content-type"] ?? "application/json", "cache-control": "no-store" });
123
173
  response.end(result.body);
124
174
  }
125
175
  catch (error) {
126
176
  settle?.(undefined);
177
+ if (error instanceof GrokBrokerRunLimit) {
178
+ response.writeHead(429, { "content-type": "application/json", "cache-control": "no-store" });
179
+ response.end('{"error":"run_request_limit"}');
180
+ return;
181
+ }
127
182
  // Name the refusal on the broker's own stderr (reason code only, never a body
128
183
  // or a token) so a failing turn is diagnosable without a stub harness —
129
184
  // except for the two requests every healthy turn makes anyway.
@@ -243,6 +298,15 @@ const toolCallsOrNothing = (body, contentType) => {
243
298
  return undefined;
244
299
  }
245
300
  };
301
+ /** Instrumentation must never fail a turn: a throwing decoder reports no final reply. */
302
+ const finalReplyOrNothing = (body, contentType) => {
303
+ try {
304
+ return parseGrokFinalReply(body, contentType);
305
+ }
306
+ catch {
307
+ return undefined;
308
+ }
309
+ };
246
310
  /** The body gate, refused non-retryably: a rejected body is a policy miss, never a transient fault. */
247
311
  function authorizeRequestOrRefuse(...args) {
248
312
  try {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Preserve ordinary JSON bytes, including whitespace and escaping. Ambiguous
3
+ * duplicate members still normalize to the validated values, as the spend gate
4
+ * requires: an upstream parser must never select a different model or tool set.
5
+ */
6
+ export declare function parseGrokBrokerProxyJson(body: Uint8Array): {
7
+ value: Record<string, unknown>;
8
+ body: Uint8Array;
9
+ };
10
+ //# sourceMappingURL=grokBrokerProxyJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokBrokerProxyJson.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyJson.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG;IAC1D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,UAAU,CAAC;CAClB,CAuBA"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Preserve ordinary JSON bytes, including whitespace and escaping. Ambiguous
3
+ * duplicate members still normalize to the validated values, as the spend gate
4
+ * requires: an upstream parser must never select a different model or tool set.
5
+ */
6
+ export function parseGrokBrokerProxyJson(body) {
7
+ const source = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(body);
8
+ let properties = 0;
9
+ const value = JSON.parse(source, (key, entry) => {
10
+ if (key === "__proto__")
11
+ throw new Error("broker proxy request rejected");
12
+ if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
13
+ properties += Object.keys(entry).length;
14
+ }
15
+ return entry;
16
+ });
17
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
18
+ throw new Error("broker proxy request rejected");
19
+ }
20
+ // JSON.parse established the grammar. Match whole string tokens (including
21
+ // escaped quotes), so a colon inside message content cannot count as a key.
22
+ let sourceProperties = 0;
23
+ for (const token of source.matchAll(/"(?:[^"\\]|\\[\s\S])*"\s*(:)?/gu)) {
24
+ if (token[1] !== undefined)
25
+ sourceProperties++;
26
+ }
27
+ return {
28
+ value: value,
29
+ body: sourceProperties === properties ? body : Buffer.from(JSON.stringify(value))
30
+ };
31
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"grokBrokerProxyRequest.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyRequest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGtI,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9L,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,qDAAqD,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAE9K;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,qBAAwD,GAAG,yBAAyB,CAsBhO;AASD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAStD"}
1
+ {"version":3,"file":"grokBrokerProxyRequest.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerProxyRequest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAgE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGtI,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9L,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,qDAAqD,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAE9K;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,qBAAwD,GAAG,yBAAyB,CAmChO;AASD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAStD"}
@@ -34,8 +34,11 @@ export function authorizeGrokBrokerProxyRequest(input, capabilities, bearer, pol
34
34
  // `__proto__` is refused at any depth: JSON.parse makes it an ordinary own
35
35
  // member, but an upstream JavaScript parser may treat it as a prototype.
36
36
  try {
37
- parsed = JSON.parse(Buffer.from(input.body).toString("utf8"), (key, value) => { if (key === "__proto__")
38
- throw new Error(); return value; });
37
+ parsed = JSON.parse(Buffer.from(input.body).toString("utf8"), (key, value) => {
38
+ if (key === "__proto__")
39
+ throw new Error("broker proxy request rejected");
40
+ return value;
41
+ });
39
42
  }
40
43
  catch {
41
44
  throw new Error("broker proxy request rejected");
@@ -48,10 +51,18 @@ export function authorizeGrokBrokerProxyRequest(input, capabilities, bearer, pol
48
51
  throw new Error("broker proxy request rejected");
49
52
  if (Object.keys(parsed).some((key) => !LEAN_BODY_MEMBERS.has(key)) || !validStreamOptions(parsed.stream_options))
50
53
  throw new Error("broker proxy request rejected");
51
- // Forward what was validated, never the worker's bytes: JSON.parse keeps the
52
- // last of duplicate keys, and an upstream that keeps the first would
53
- // otherwise see a different `tools`/`model`/`reasoning_effort` than the gate.
54
- return { url: "https://cli-chat-proxy.grok.com/v1/chat/completions", headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json", "x-xai-token-auth": "xai-grok-cli", "x-grok-model-override": declared.model, "x-grok-client-version": clientVersion, "x-grok-client-identifier": "grok-shell" }, body: Buffer.from(JSON.stringify(parsed)) };
54
+ // Keep origin/main's production handling: UTF-8 replacement decoding, then
55
+ // serialize the validated object (including last-member-wins duplicates).
56
+ // Only a clocked turn takes the stricter date rewrite before this gate.
57
+ return {
58
+ url: "https://cli-chat-proxy.grok.com/v1/chat/completions",
59
+ headers: {
60
+ authorization: `Bearer ${bearer}`, "content-type": "application/json",
61
+ "x-xai-token-auth": "xai-grok-cli", "x-grok-model-override": declared.model,
62
+ "x-grok-client-version": clientVersion, "x-grok-client-identifier": "grok-shell"
63
+ },
64
+ body: Buffer.from(JSON.stringify(parsed))
65
+ };
55
66
  }
56
67
  /** Top-level members of a Grok 1.0.34 lean worker chat-completions body (live stub capture). */
57
68
  const LEAN_BODY_MEMBERS = new Set(["messages", "model", "reasoning_effort", "stream", "stream_options", "tools"]);
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawnRealTime as spawn } from "./taskClockProcess.js";
2
2
  import { constants } from "node:fs";
3
3
  import { open } from "node:fs/promises";
4
4
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawnRealTime as spawn } from "./taskClockProcess.js";
2
2
  import { readChild } from "../pi/cliChildOutput.js";
3
3
  import { terminateChild, trackCliChild } from "../pi/cliProcess.js";
4
4
  export async function refreshGrokBrokerCredential(command, grokHome, timeoutMs = 15_000) {
@@ -0,0 +1,20 @@
1
+ export declare class GrokBrokerRunLimit extends Error {
2
+ constructor();
3
+ }
4
+ /** One broker realm lease owns this journal. Reserve durably BEFORE forwarding;
5
+ * failed calls and credential retries consume capacity, never refund it.
6
+ * Slot recycle must never remove this run-level file.
7
+ */
8
+ export declare class GrokBrokerRunBudget {
9
+ readonly maximum: number;
10
+ private readonly file;
11
+ private used;
12
+ private tail;
13
+ private failed;
14
+ private closed;
15
+ private constructor();
16
+ static open(maximum: number, file: string): Promise<GrokBrokerRunBudget>;
17
+ reserve(): Promise<void>;
18
+ close(): Promise<void>;
19
+ }
20
+ //# sourceMappingURL=grokBrokerRunBudget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokBrokerRunBudget.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerRunBudget.ts"],"names":[],"mappings":"AAIA,qBAAa,kBAAmB,SAAQ,KAAK;;CAE5C;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAIV,QAAQ,CAAC,OAAO,EAAE,MAAM;IAAE,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAU,OAAO,CAAC,IAAI;IAHzF,OAAO,CAAC,IAAI,CAAoC;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO;WAEM,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAc9E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBlB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
@@ -0,0 +1,85 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export class GrokBrokerRunLimit extends Error {
5
+ constructor() { super("run_request_limit"); }
6
+ }
7
+ /** One broker realm lease owns this journal. Reserve durably BEFORE forwarding;
8
+ * failed calls and credential retries consume capacity, never refund it.
9
+ * Slot recycle must never remove this run-level file.
10
+ */
11
+ export class GrokBrokerRunBudget {
12
+ maximum;
13
+ file;
14
+ used;
15
+ tail = Promise.resolve();
16
+ failed = false;
17
+ closed = false;
18
+ constructor(maximum, file, used) {
19
+ this.maximum = maximum;
20
+ this.file = file;
21
+ this.used = used;
22
+ }
23
+ static async open(maximum, file) {
24
+ if (!Number.isSafeInteger(maximum) || maximum < 1)
25
+ throw new TypeError("invalid run request cap");
26
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
27
+ let used = 0;
28
+ try {
29
+ const state = JSON.parse(await readFile(file, "utf8"));
30
+ if (state.schema !== "daimon.run-requests.v1" || state.maximum !== maximum ||
31
+ !Number.isSafeInteger(state.used) || state.used < 0 || state.used > maximum ||
32
+ Object.keys(state).length !== 3)
33
+ throw new Error("invalid run request checkpoint");
34
+ used = state.used;
35
+ }
36
+ catch (error) {
37
+ if (error.code !== "ENOENT")
38
+ throw error;
39
+ }
40
+ return new GrokBrokerRunBudget(maximum, file, used);
41
+ }
42
+ reserve() {
43
+ if (this.closed)
44
+ return Promise.reject(new Error("run request budget closed"));
45
+ const reservation = this.tail.then(async () => {
46
+ if (this.failed)
47
+ throw new Error("run request checkpoint unavailable");
48
+ if (this.used >= this.maximum)
49
+ throw new GrokBrokerRunLimit();
50
+ this.used += 1;
51
+ const temporary = `${this.file}.${randomUUID()}.tmp`;
52
+ try {
53
+ const handle = await open(temporary, "wx", 0o600);
54
+ try {
55
+ await handle.writeFile(JSON.stringify({ schema: "daimon.run-requests.v1", maximum: this.maximum, used: this.used }));
56
+ await handle.sync();
57
+ }
58
+ finally {
59
+ await handle.close();
60
+ }
61
+ await rename(temporary, this.file);
62
+ const directory = await open(path.dirname(this.file), "r");
63
+ try {
64
+ await directory.sync();
65
+ }
66
+ finally {
67
+ await directory.close();
68
+ }
69
+ }
70
+ catch (error) {
71
+ this.failed = true;
72
+ throw error;
73
+ }
74
+ finally {
75
+ await rm(temporary, { force: true });
76
+ }
77
+ });
78
+ this.tail = reservation.catch(() => undefined);
79
+ return reservation;
80
+ }
81
+ async close() {
82
+ this.closed = true;
83
+ await this.tail;
84
+ }
85
+ }
@@ -0,0 +1,6 @@
1
+ import { type GrokWorkerConfigVariant } from "../contracts/grokBrokerTaskClock.js";
2
+ /** Canonical path in both broker and worker views; never a caller-selected file. */
3
+ export declare function brokerTaskClockFileFor(slot: number): string;
4
+ /** The pinned strict base exposes /run; its deny mounts must hide every peer clock. */
5
+ export declare function assertBrokerTaskClockIsolation(variant: GrokWorkerConfigVariant, slot: number, slots: readonly number[], denyPaths: readonly string[]): void;
6
+ //# sourceMappingURL=grokBrokerSlot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokBrokerSlot.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerSlot.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAE3G,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,uFAAuF;AACvF,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GACrG,IAAI,CAUN"}
@@ -0,0 +1,21 @@
1
+ import { GROK_BROKER_TASK_CLOCK } from "../contracts/grokBrokerTaskClock.js";
2
+ /** Canonical path in both broker and worker views; never a caller-selected file. */
3
+ export function brokerTaskClockFileFor(slot) {
4
+ if (!Number.isSafeInteger(slot) || slot < 0)
5
+ throw new TypeError("invalid broker slot");
6
+ return `${GROK_BROKER_TASK_CLOCK.rootDirectoryPath}/slot-${slot}/faketime`;
7
+ }
8
+ /** The pinned strict base exposes /run; its deny mounts must hide every peer clock. */
9
+ export function assertBrokerTaskClockIsolation(variant, slot, slots, denyPaths) {
10
+ if (variant !== "clocked")
11
+ return;
12
+ const own = brokerTaskClockFileFor(slot);
13
+ const covers = (denied, file) => file === denied || file.startsWith(`${denied}/`);
14
+ if (denyPaths.some((denied) => covers(denied, own)))
15
+ throw new Error("own broker task clock is denied");
16
+ for (const peer of slots) {
17
+ if (peer !== slot && !denyPaths.some((denied) => covers(denied, brokerTaskClockFileFor(peer).slice(0, -"/faketime".length)))) {
18
+ throw new Error("sibling broker task clock is not denied");
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,32 @@
1
+ import { type Stats } from "node:fs";
2
+ import { type GrokWorkerConfigVariant } from "../contracts/grokBrokerTaskClock.js";
3
+ import type { GrokBrokerProviderClockObservation } from "./grokBrokerProviderClock.js";
4
+ import { type GrokWorkerConfigPins } from "./grokWorkerHomeAttestation.js";
5
+ /** Internal filesystem seam; tests wrap real handles to observe both fsyncs. */
6
+ export type BrokerTaskClockFs = Pick<typeof import("node:fs/promises"), "lstat" | "open" | "rename" | "unlink">;
7
+ export type BrokerShellClock = "task" | "real" | "gap";
8
+ export type BrokerShellClockObservation = Readonly<{
9
+ shellClock?: BrokerShellClock;
10
+ }>;
11
+ export type BrokerClockObservation = BrokerShellClockObservation & Readonly<{
12
+ providerClock?: GrokBrokerProviderClockObservation;
13
+ }>;
14
+ export declare function brokerClockObservation(shellClock?: BrokerShellClock, providerClock?: GrokBrokerProviderClockObservation): BrokerClockObservation;
15
+ /** Relative seconds only: never a date, speed, loader path or arbitrary environment. */
16
+ export declare function parseBrokerProcessClock(value: unknown): string;
17
+ /** Daimon remains the offset owner; the broker uses its pinned library, not ambient preload. */
18
+ export declare function brokerProcessClock(): string | undefined;
19
+ /** Discovery only; selected slots still require full attestation before reconciliation. */
20
+ export declare function needsBrokerTaskClockReconciliation(profilePath: string, pins: GrokWorkerConfigPins, file?: string): Promise<boolean>;
21
+ /** Pure provisioning seam: only the broker can replace entries; every worker can read. */
22
+ export declare function assertBrokerTaskClockDirectory(entry: Pick<Stats, "uid" | "mode" | "isDirectory">, brokerUid?: number): void;
23
+ /**
24
+ * Root provisions a non-symlink broker-owned directory. Rename publishes a
25
+ * complete value before launch; binding the DIRECTORY (not the old file inode)
26
+ * into the worker view keeps subsequent turns visible. No native ABI changes.
27
+ * Paths/uid below are internal test seams, never control or service inputs.
28
+ */
29
+ export declare function writeBrokerTaskClock(value: string | undefined, file?: string, brokerUid?: number, fs?: BrokerTaskClockFs): Promise<void>;
30
+ /** Unknown config is never called clocked: this runs only after byte attestation. */
31
+ export declare function prepareBrokerTaskClock(variant: GrokWorkerConfigVariant, value: string | undefined, write?: typeof writeBrokerTaskClock): Promise<BrokerShellClock>;
32
+ //# sourceMappingURL=grokBrokerTaskClock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grokBrokerTaskClock.d.ts","sourceRoot":"","sources":["../../src/runtime/grokBrokerTaskClock.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAIhD,OAAO,EAA0B,KAAK,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAE3G,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,8BAA8B,CAAC;AACvF,OAAO,EAA+B,KAAK,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAExG,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAGhH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AACvD,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAAC;AACtF,MAAM,MAAM,sBAAsB,GAAG,2BAA2B,GAAG,QAAQ,CAAC;IAC1E,aAAa,CAAC,EAAE,kCAAkC,CAAC;CACpD,CAAC,CAAC;AAEH,wBAAgB,sBAAsB,CACpC,UAAU,CAAC,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,kCAAkC,GAChF,sBAAsB,CAKxB;AAED,wFAAwF;AACxF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG9D;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAIvD;AAED,2FAA2F;AAC3F,wBAAsB,kCAAkC,CACtD,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAC/C,IAAI,GAAE,MAAkC,GACvC,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAED,0FAA0F;AAC1F,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,aAAa,CAAC,EAAE,SAAS,GAAE,MAAwC,GAAG,IAAI,CAI5J;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,GAAE,MAAkC,EACxC,SAAS,GAAE,MAAwC,EACnD,EAAE,GAAE,iBAA2B,GAC9B,OAAO,CAAC,IAAI,CAAC,CAoCf;AAED,qFAAqF;AACrF,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,GAAE,OAAO,oBAA2C,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAS9L"}