@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
@@ -0,0 +1,22 @@
1
+ // Internal capabilities are object identities, never caller-provided wire fields.
2
+ const durableRequests = new WeakSet();
3
+ const recordingVerifiers = new WeakMap();
4
+ export async function wakeFromDurableInbox(host, request) {
5
+ durableRequests.add(request);
6
+ try {
7
+ return await host.wake(request);
8
+ }
9
+ finally {
10
+ durableRequests.delete(request);
11
+ }
12
+ }
13
+ export function consumeDurableAdmission(request) {
14
+ return durableRequests.delete(request);
15
+ }
16
+ export function registerRecordingVerifier(host, verify) {
17
+ recordingVerifiers.set(host, verify);
18
+ return host;
19
+ }
20
+ export async function verifyHostRecordingStore(host, agentId) {
21
+ await recordingVerifiers.get(host)?.(agentId);
22
+ }
@@ -4,6 +4,10 @@ import type { AttentionRegistry } from "./attention.js";
4
4
  import { type ScheduleControllerOptions } from "./schedule.js";
5
5
  import { type WakeAcceptanceStoreTestOptions } from "./wakeAcceptanceStore.js";
6
6
  import { type OrganizationRuntimeActivityV2, type OrganizationRuntimeWakeAcceptanceResult, type OrganizationRuntimeWakeReceiptStatus } from "./wakeAcceptanceTypes.js";
7
+ export type WorkDrain = Readonly<{
8
+ state: "draining" | "drained";
9
+ since: string;
10
+ }>;
7
11
  export type WorkAvailability = Readonly<{
8
12
  version: "noopolis.daimon.work-availability.v1";
9
13
  state: "running" | "paused" | "stopped";
@@ -15,12 +19,17 @@ export type WorkAvailability = Readonly<{
15
19
  budget: WakeBudgetSnapshot;
16
20
  error?: string;
17
21
  }>[];
22
+ drain?: WorkDrain;
18
23
  }>;
19
24
  export type OrganizationRuntimeControlHost = OrganizationRuntimeHost & Readonly<{
20
25
  accept(request: unknown): Promise<OrganizationRuntimeWakeAcceptanceResult>;
21
26
  wakeReceipt(token: string | undefined, acceptanceId: string): Promise<OrganizationRuntimeWakeReceiptStatus | undefined>;
22
27
  activityV2(token: string | undefined): Promise<OrganizationRuntimeActivityV2 | undefined>;
23
28
  availability(token: string | undefined): Promise<WorkAvailability | undefined>;
29
+ /** Reversible operator drain: new wakes answer work-blocked, queued ones stay durable, running turns finish. */
30
+ drain(token: string | undefined): Promise<WorkAvailability | undefined>;
31
+ /** Ends a drain and dispatches the queue. Never clears the latched `fuse.stop`. */
32
+ resume(token: string | undefined): Promise<WorkAvailability | undefined>;
24
33
  }>;
25
34
  export type OrganizationRuntimeControlOptions = Readonly<{
26
35
  acceptanceStorePath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"organizationRuntimeControl.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeControl.ts"],"names":[],"mappings":"AACA,OAAO,EAAuG,KAAK,uBAAuB,EAA8C,MAAM,0BAA0B,CAAC;AAEzN,OAAO,EAAY,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAqD,KAAK,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAA0F,KAAK,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AACvK,OAAO,EAAmD,KAAK,6BAA6B,EAAE,KAAK,uCAAuC,EAAE,KAAK,oCAAoC,EAAE,MAAM,0BAA0B,CAAC;AAGxN,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,sCAAsC,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,QAAQ,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAA;CAAE,CAAC,CAAC;AAC1R,MAAM,MAAM,8BAA8B,GAAG,uBAAuB,GAAG,QAAQ,CAAC;IAC9E,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;IAC3E,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oCAAoC,GAAG,SAAS,CAAC,CAAC;IACxH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,6BAA6B,GAAG,SAAS,CAAC,CAAC;IAC1F,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;CAChF,CAAC,CAAC;AACH,MAAM,MAAM,iCAAiC,GAAG,QAAQ,CAAC;IAAE,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACjH,KAAK,kBAAkB,GAAG,iCAAiC,GAAG,QAAQ,CAAC;IACrE,eAAe,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAC,CAAC;IACrF,YAAY,CAAC,EAAE,8BAA8B,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACpC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,wBAAwB,CAAC,EAAE,iBAAiB,CAAC;CAC9C,CAAC,CAAC;AAEH,sFAAsF;AACtF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,iCAAiC,GAAG,8BAA8B,CAIhJ;AACD,gFAAgF;AAChF,wBAAgB,mDAAmD,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,kBAAkB,GAAG,8BAA8B,CAE/K;AA4JD,eAAO,MAAM,0BAA0B,OAAO,CAAC"}
1
+ {"version":3,"file":"organizationRuntimeControl.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeControl.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuG,KAAK,uBAAuB,EAA8C,MAAM,0BAA0B,CAAC;AAEzN,OAAO,EAAY,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAqD,KAAK,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAClH,OAAO,EAA0F,KAAK,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AACvK,OAAO,EAAmD,KAAK,6BAA6B,EAAE,KAAK,uCAAuC,EAAE,KAAK,oCAAoC,EAAE,MAAM,0BAA0B,CAAC;AAGxN,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACnF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,sCAAsC,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,QAAQ,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAC7S,MAAM,MAAM,8BAA8B,GAAG,uBAAuB,GAAG,QAAQ,CAAC;IAC9E,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;IAC3E,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oCAAoC,GAAG,SAAS,CAAC,CAAC;IACxH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,6BAA6B,GAAG,SAAS,CAAC,CAAC;IAC1F,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IAC/E,gHAAgH;IAChH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IACxE,mFAAmF;IACnF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;CAC1E,CAAC,CAAC;AACH,MAAM,MAAM,iCAAiC,GAAG,QAAQ,CAAC;IAAE,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AACjH,KAAK,kBAAkB,GAAG,iCAAiC,GAAG,QAAQ,CAAC;IACrE,eAAe,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAC,CAAC;IACrF,YAAY,CAAC,EAAE,8BAA8B,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACpC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,wBAAwB,CAAC,EAAE,iBAAiB,CAAC;CAC9C,CAAC,CAAC;AAEH,sFAAsF;AACtF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,iCAAiC,GAAG,8BAA8B,CAIhJ;AACD,gFAAgF;AAChF,wBAAgB,mDAAmD,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,kBAAkB,GAAG,8BAA8B,CAE/K;AAoMD,eAAO,MAAM,0BAA0B,OAAO,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { createHash, timingSafeEqual } from "node:crypto";
2
+ import { readTaskClock } from "./taskClock.js";
2
3
  import { parseOrganizationRuntimeConfig, parseOrganizationRuntimeWakeRequest } from "./organizationRuntime.js";
3
4
  import { createOrganizationRuntimeHostWithAttention } from "./organizationRuntimeHost.js";
4
5
  import { WakeFuse } from "./wakeFuse.js";
@@ -28,6 +29,10 @@ function createControl(config, host, options, registry) {
28
29
  let started = false;
29
30
  let stopping = false;
30
31
  let sealedActivity;
32
+ /** In-process only: a restarted host admits, so a drained release ends drained-free. */
33
+ let drainedSince;
34
+ /** Synchronous v1 turns bypass the dispatcher; a drain is not drained while one is admitted or running. */
35
+ let v1Turns = 0;
31
36
  /**
32
37
  * One projection, read the same way live and at shutdown. `active` is decided by
33
38
  * the dispatcher's own execution authority rather than the record's flag alone,
@@ -47,7 +52,7 @@ function createControl(config, host, options, registry) {
47
52
  const reason = fuse?.tripped();
48
53
  return reason === "operator_stop" || reason === "ledger_unavailable" ? reason : undefined;
49
54
  };
50
- const accept = async (value) => {
55
+ const accept = async (value, nativeSchedule = false) => {
51
56
  let request;
52
57
  try {
53
58
  request = parseWakeAcceptanceRequest(value);
@@ -64,9 +69,12 @@ function createControl(config, host, options, registry) {
64
69
  const reason = hardReason();
65
70
  if (reason)
66
71
  return blocked(reason);
72
+ // A drain is an operator stop that can be undone; bridges already defer on this reason.
73
+ if (drainedSince !== undefined)
74
+ return blocked("operator_stop");
67
75
  const operation = (async () => {
68
76
  try {
69
- const accepted = await store.accept(request);
77
+ const accepted = await store.accept(request, nativeSchedule);
70
78
  // A stop racing this fsync cannot revoke already durable ownership.
71
79
  // It remains accepted for restart instead of being terminalized.
72
80
  dispatcher?.notify(request.agent_id, accepted.created);
@@ -88,6 +96,25 @@ function createControl(config, host, options, registry) {
88
96
  persistence.delete(operation);
89
97
  }
90
98
  };
99
+ const availability = async (token) => {
100
+ if (!tokensEqual(expectedToken, token) || !store || !fuse)
101
+ return undefined;
102
+ await fuse.pollOperatorStop();
103
+ const items = await store.activityWithExecutionErrors();
104
+ const executionErrors = new Map(items.filter((record) => (record.state === "accepted" || record.state === "running")
105
+ && record.execution_error !== undefined).map((record) => [record.agent_id, record.execution_error]));
106
+ const agents = await Promise.all(config.agents.map(async (agent) => ({
107
+ agent_id: agent.id, pending: items.filter((item) => item.agent_id === agent.id && (item.state === "accepted" || item.state === "running" && !dispatcher?.activeExecutions().some((execution) => execution.agent_id === agent.id))).length,
108
+ running: dispatcher?.activeExecutions().some((execution) => execution.agent_id === agent.id) ?? false,
109
+ deferred: items.filter((item) => item.agent_id === agent.id && item.state === "accepted" && item.deferred).length,
110
+ budget: await fuse.snapshot(agent.id, agent.attention),
111
+ ...((dispatcher?.failure(agent.id) ?? executionErrors.get(agent.id))
112
+ ? { error: dispatcher?.failure(agent.id) ?? executionErrors.get(agent.id) } : {})
113
+ })));
114
+ // "drained" only once no inbox loop is alive: nothing runs and nothing can start until resume.
115
+ const drain = drainedSince === undefined ? undefined : { state: dispatcher?.quiescent() === false || v1Turns > 0 ? "draining" : "drained", since: drainedSince };
116
+ return { version: "noopolis.daimon.work-availability.v1", state: hardReason() ? "stopped" : drain !== undefined || agents.some((agent) => agent.budget.state !== "available" || agent.error) ? "paused" : "running", agents, ...(drain === undefined ? {} : { drain }) };
117
+ };
91
118
  return {
92
119
  wake: async (request) => {
93
120
  try {
@@ -96,23 +123,33 @@ function createControl(config, host, options, registry) {
96
123
  catch {
97
124
  return { version: "noopolis.daimon.wake-result.v1", status: "rejected", agentId: "", wakeId: "", code: "invalid_request" };
98
125
  }
99
- // Inbox attention requires durable ownership; synchronous v1 cannot
100
- // provide a receipt or leave deferred messages pending.
126
+ // Attention and recording require durable ownership and dispatcher admission.
101
127
  if (!tokensEqual(expectedToken, request.token))
102
128
  return { version: "noopolis.daimon.wake-result.v1", status: "rejected", agentId: request.agentId, wakeId: request.event.id, code: "unauthorized" };
103
129
  if (!knownAgents.has(request.agentId))
104
130
  return { version: "noopolis.daimon.wake-result.v1", status: "rejected", agentId: request.agentId, wakeId: request.event.id, code: "unknown_agent" };
105
- if (config.agents.find((agent) => agent.id === request.agentId)?.attention !== undefined)
131
+ const agent = config.agents.find((candidate) => candidate.id === request.agentId);
132
+ if (agent.attention !== undefined || agent.recording !== undefined)
106
133
  return { version: "noopolis.daimon.wake-result.v1", status: "rejected", agentId: request.agentId, wakeId: request.event.id, code: "durable_inbox_required" };
107
- if (hardReason() || (await fuse.admit(request.agentId, request.event.id, config.agents.find((agent) => agent.id === request.agentId)?.attention)).state !== "admitted")
134
+ // v1 has no `blocked` member on its wire; a drain answers it exactly as the latched stop does.
135
+ if (hardReason() || drainedSince !== undefined)
108
136
  return { version: "noopolis.daimon.wake-result.v1", status: "stopped", agentId: request.agentId, wakeId: request.event.id, code: "host_stopping" };
109
- return await host.wake(request);
137
+ v1Turns += 1;
138
+ try {
139
+ if ((await fuse.admit(request.agentId, request.event.id, config.agents.find((agent) => agent.id === request.agentId)?.attention)).state !== "admitted")
140
+ return { version: "noopolis.daimon.wake-result.v1", status: "stopped", agentId: request.agentId, wakeId: request.event.id, code: "host_stopping" };
141
+ return await host.wake(request);
142
+ }
143
+ finally {
144
+ v1Turns -= 1;
145
+ }
110
146
  },
111
147
  health: async (agentId) => await host.health(agentId),
112
148
  activity: async (request) => await host.activity(request),
113
149
  async start() {
114
150
  if (started)
115
151
  return;
152
+ const clock = readTaskClock();
116
153
  if (stopping)
117
154
  throw new Error("organization runtime control host has been stopped");
118
155
  if (!expectedToken?.trim())
@@ -138,10 +175,12 @@ function createControl(config, host, options, registry) {
138
175
  fusePoll.unref();
139
176
  if (config.version === "noopolis.daimon.organization-runtime.v2") {
140
177
  schedules = createScheduleController({ acceptanceStorePath: options.acceptanceStorePath, agents: config.agents, ...options.scheduleOptions,
178
+ // Calendar selection/state use task instants; due - taskNow remains a real delay.
179
+ now: () => { const realNow = (options.scheduleOptions?.now ?? Date.now)(); return clock?.at(realNow) ?? realNow; },
141
180
  accept: async (occurrence) => {
142
- if (dispatcher?.busy(occurrence.agentId) || hardReason())
181
+ if (dispatcher?.busy(occurrence.agentId) || hardReason() || drainedSince !== undefined)
143
182
  return false;
144
- const result = await accept({ token: expectedToken, agent_id: occurrence.agentId, delivery_id: occurrence.deliveryId, event: { version: "noopolis.daimon.wake.v2", kind: "schedule", text: occurrence.prompt, occurred_at: occurrence.occurredAt } });
183
+ const result = await accept({ token: expectedToken, agent_id: occurrence.agentId, delivery_id: occurrence.deliveryId, event: { version: "noopolis.daimon.wake.v2", kind: "schedule", text: occurrence.prompt, occurred_at: occurrence.occurredAt } }, true);
145
184
  return result.state === "accepted";
146
185
  }
147
186
  });
@@ -157,7 +196,8 @@ function createControl(config, host, options, registry) {
157
196
  throw error;
158
197
  }
159
198
  },
160
- accept,
199
+ // Do not expose the private provenance argument, even to JavaScript callers.
200
+ accept: (value) => accept(value),
161
201
  async wakeReceipt(token, acceptanceId) {
162
202
  if (!tokensEqual(expectedToken, token) || store === undefined)
163
203
  return undefined;
@@ -176,22 +216,30 @@ function createControl(config, host, options, registry) {
176
216
  return sealedActivity;
177
217
  return await projectActivity(store, "running");
178
218
  },
179
- async availability(token) {
219
+ availability,
220
+ async drain(token) {
180
221
  if (!tokensEqual(expectedToken, token) || !store || !fuse)
181
222
  return undefined;
182
- await fuse.pollOperatorStop();
183
- const items = await store.activityWithExecutionErrors();
184
- const executionErrors = new Map(items.filter((record) => (record.state === "accepted" || record.state === "running")
185
- && record.execution_error !== undefined).map((record) => [record.agent_id, record.execution_error]));
186
- const agents = await Promise.all(config.agents.map(async (agent) => ({
187
- agent_id: agent.id, pending: items.filter((item) => item.agent_id === agent.id && (item.state === "accepted" || item.state === "running" && !dispatcher?.activeExecutions().some((execution) => execution.agent_id === agent.id))).length,
188
- running: dispatcher?.activeExecutions().some((execution) => execution.agent_id === agent.id) ?? false,
189
- deferred: items.filter((item) => item.agent_id === agent.id && item.state === "accepted" && item.deferred).length,
190
- budget: await fuse.snapshot(agent.id, agent.attention),
191
- ...((dispatcher?.failure(agent.id) ?? executionErrors.get(agent.id))
192
- ? { error: dispatcher?.failure(agent.id) ?? executionErrors.get(agent.id) } : {})
193
- })));
194
- return { version: "noopolis.daimon.work-availability.v1", state: hardReason() ? "stopped" : agents.some((agent) => agent.budget.state !== "available" || agent.error) ? "paused" : "running", agents };
223
+ drainedSince ??= new Date().toISOString();
224
+ dispatcher?.pause();
225
+ return await availability(token);
226
+ },
227
+ async resume(token) {
228
+ if (!tokensEqual(expectedToken, token) || !store || !fuse)
229
+ return undefined;
230
+ if (drainedSince !== undefined) {
231
+ drainedSince = undefined;
232
+ // A latched operator stop or fatal fault outlives the drain: resume never reopens it.
233
+ if (!hardReason()) {
234
+ // An owed pass for every agent the drain withheld or that holds queued work;
235
+ // owed passes never count as new input, so deferred deliveries stay deferred.
236
+ const queued = new Set((await store.recoverable(knownAgents).catch(() => [])).filter((record) => !record.deferred).map((record) => record.agent_id));
237
+ if (drainedSince === undefined && !hardReason())
238
+ dispatcher?.resume(queued);
239
+ void schedules?.drain().catch(() => undefined);
240
+ }
241
+ }
242
+ return await availability(token);
195
243
  },
196
244
  async stop() {
197
245
  if (!started && stopping)
@@ -1 +1 @@
1
- {"version":3,"file":"organizationRuntimeHost.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,kBAAkB,CAAC;AAI5E,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAK/E,OAAO,EAML,KAAK,8BAA8B,EAInC,KAAK,uBAAuB,EAK7B,MAAM,0BAA0B,CAAC;AAElC,KAAK,gCAAgC,GAAG,CACtC,KAAK,EAAE,8BAA8B,EACrC,KAAK,CAAC,EAAE,UAAU,CAAC,gCAAgC,CAAC,UAAU,CAAC,CAAC,KAC7D,OAAO,CAAC,WAAW,CAAC,CAAC;AAG1B,KAAK,qBAAqB,GAAG,QAAQ,CAAC;IAAE,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAmBpF,iFAAiF;AACjF,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAE,qBAA0B,GAAG,uBAAuB,CAI3H;AAED,uFAAuF;AACvF,wBAAgB,0CAA0C,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,iBAAiB,GAAG,uBAAuB,CAmBjK;AAED,uFAAuF;AACvF,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,gCAAgC,EACzC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,gCAAgC,CAAC,GAC1D,uBAAuB,CAKzB;AA2RD,uFAAuF;AACvF,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAOrD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMtE"}
1
+ {"version":3,"file":"organizationRuntimeHost.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,kBAAkB,CAAC;AAG5E,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAK/E,OAAO,EAML,KAAK,8BAA8B,EAInC,KAAK,uBAAuB,EAK7B,MAAM,0BAA0B,CAAC;AAClC,KAAK,gCAAgC,GAAG,CACtC,KAAK,EAAE,8BAA8B,EACrC,KAAK,CAAC,EAAE,UAAU,CAAC,gCAAgC,CAAC,UAAU,CAAC,CAAC,KAC7D,OAAO,CAAC,WAAW,CAAC,CAAC;AAE1B,KAAK,qBAAqB,GAAG,QAAQ,CAAC;IAAE,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAgBpF,iFAAiF;AACjF,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAE,qBAA0B,GAAG,uBAAuB,CAI3H;AACD,uFAAuF;AACvF,wBAAgB,0CAA0C,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,iBAAiB,GAAG,uBAAuB,CAmBjK;AAED,uFAAuF;AACvF,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,gCAAgC,EACzC,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,gCAAgC,CAAC,GAC1D,uBAAuB,CAKzB;AA8RD,uFAAuF;AACvF,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAOrD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMtE"}
@@ -1,3 +1,5 @@
1
+ import { consumeDurableAdmission, registerRecordingVerifier } from "./organizationRuntimeAdmission.js";
2
+ import { readTaskClock, taskClockChildEnvironment } from "./taskClock.js";
1
3
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
2
4
  import { sanitizeWakeCompletionText } from "./wakeAcceptanceTypes.js";
3
5
  import { startOrganizationRuntimeEngine } from "./engineDispatcher.js";
@@ -123,6 +125,10 @@ function createHost(config, factory, preflight = undefined) {
123
125
  return startPromise;
124
126
  if (stopRequested)
125
127
  throw new Error("organization runtime host has been stopped");
128
+ const clock = readTaskClock();
129
+ for (const agent of config.agents)
130
+ for (const server of agent.mcp ?? [])
131
+ taskClockChildEnvironment(server.env, clock);
126
132
  if (controlToken === undefined || !controlToken.trim()) {
127
133
  throw new Error(`required control token ${config.host.controlTokenEnv} is missing or blank`);
128
134
  }
@@ -200,6 +206,7 @@ function createHost(config, factory, preflight = undefined) {
200
206
  return startPromise;
201
207
  };
202
208
  const wake = async (request) => {
209
+ const durable = consumeDurableAdmission(request);
203
210
  let parsed;
204
211
  try {
205
212
  parsed = parseOrganizationRuntimeWakeRequest(request);
@@ -218,6 +225,8 @@ function createHost(config, factory, preflight = undefined) {
218
225
  addActivity(parsed.agentId, "wake_rejected", parsed.event.id);
219
226
  return rejected(parsed, "unknown_agent");
220
227
  }
228
+ if (agent.config.recording && !durable)
229
+ return rejected(parsed, "durable_inbox_required");
221
230
  if (agent.pending.length >= maxPendingWakes) {
222
231
  addActivity(parsed.agentId, "wake_rejected", parsed.event.id);
223
232
  return rejected(parsed, "queue_full");
@@ -304,13 +313,13 @@ function createHost(config, factory, preflight = undefined) {
304
313
  stopPromise = undefined; });
305
314
  return attempt;
306
315
  };
307
- return { start, wake, health, activity: activityPage, stop };
316
+ return registerRecordingVerifier({ start, wake, health, activity: activityPage, stop }, async (id) => {
317
+ await readiness?.paths.forAgent(agents.get(id).config).verifyRecording?.();
318
+ });
308
319
  }
309
320
  function toCoreWake(request) {
310
321
  return {
311
- id: request.event.id,
312
- kind: request.event.kind === "external" ? "manual" : request.event.kind,
313
- text: request.event.text
322
+ id: request.event.id, kind: request.event.kind === "external" ? "manual" : request.event.kind, text: request.event.text
314
323
  };
315
324
  }
316
325
  function agentHealthState(agent) {
@@ -1 +1 @@
1
- {"version":3,"file":"organizationRuntimeParsing.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeParsing.ts"],"names":[],"mappings":"AAEA,OAAO,EAcL,KAAK,yBAAyB,EAK9B,KAAK,8BAA8B,EACpC,MAAM,0BAA0B,CAAC;AAOlC,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAuBxF;AAED,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAWlG;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,yBAAyB,CAEpG;AAED,eAAO,MAAM,2BAA2B,0CAAoC,CAAC"}
1
+ {"version":3,"file":"organizationRuntimeParsing.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeParsing.ts"],"names":[],"mappings":"AAGA,OAAO,EAcL,KAAK,yBAAyB,EAK9B,KAAK,8BAA8B,EACpC,MAAM,0BAA0B,CAAC;AAOlC,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAwBxF;AAED,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAWlG;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,yBAAyB,CAEpG;AAED,eAAO,MAAM,2BAA2B,0CAAoC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { GROK_BROKER_MODELS, GROK_BROKER_REASONING_EFFORTS } from "../contracts/grokWorkerContract.js";
2
2
  import { parseAttention } from "./attention.js";
3
+ import { isolateRecording, parseRecording } from "./organizationRuntimeRecording.js";
3
4
  import { ORGANIZATION_RUNTIME_CODEX_REASONING_EFFORTS, ORGANIZATION_RUNTIME_CODEX_WORKSPACE_NO_NETWORK_POLICY, ORGANIZATION_RUNTIME_MAX_AGENTS, ORGANIZATION_RUNTIME_MAX_CONFIG_BYTES, ORGANIZATION_RUNTIME_MAX_INSTRUCTIONS_CODEPOINTS, ORGANIZATION_RUNTIME_MAX_STRING_BYTES, ORGANIZATION_RUNTIME_MAX_STRING_CODEPOINTS, ORGANIZATION_RUNTIME_MAX_WAKE_TEXT_BYTES, ORGANIZATION_RUNTIME_MAX_SCHEDULE_INTERVAL_MS, ORGANIZATION_RUNTIME_MAX_SCHEDULE_JITTER_SECONDS, ORGANIZATION_RUNTIME_V2_VERSION, ORGANIZATION_RUNTIME_VERSION } from "./organizationRuntime.js";
4
5
  const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
5
6
  const ENGINE_KINDS = new Set(["codex", "grok", "agy"]);
@@ -29,6 +30,7 @@ export function parseOrganizationRuntimeConfig(value) {
29
30
  });
30
31
  isolated(agents);
31
32
  isolatedMemory(agents);
33
+ isolateRecording(agents);
32
34
  return { version: version, host, agents };
33
35
  }
34
36
  export function parseOrganizationRuntimeWakeRequest(value) {
@@ -58,10 +60,11 @@ export function validateOrganizationRuntimeConfig(value) {
58
60
  export const isOrganizationRuntimeConfig = validateOrganizationRuntimeConfig;
59
61
  function parseAgent(value, label, v2) {
60
62
  const agent = object(value, label);
61
- exactOptional(agent, v2 ? ["id", "name", "instructions", "workspacePath", "runtimeHomePath", "engine", "schedule"] : ["id", "name", "instructions", "workspacePath", "runtimeHomePath", "engine"], ["mcp", "moltnet", "memory", "attention"], label);
63
+ exactOptional(agent, v2 ? ["id", "name", "instructions", "workspacePath", "runtimeHomePath", "engine", "schedule"] : ["id", "name", "instructions", "workspacePath", "runtimeHomePath", "engine"], ["mcp", "moltnet", "memory", "attention", "recording"], label);
62
64
  return {
63
65
  id: nonEmpty(agent.id, `${label}.id`), name: nonEmpty(agent.name, `${label}.name`), instructions: nonEmpty(agent.instructions, `${label}.instructions`, ORGANIZATION_RUNTIME_MAX_INSTRUCTIONS_CODEPOINTS), workspacePath: absolute(agent.workspacePath, `${label}.workspacePath`), runtimeHomePath: absolute(agent.runtimeHomePath, `${label}.runtimeHomePath`), engine: engine(agent.engine, `${label}.engine`),
64
66
  ...(agent.attention === undefined ? {} : { attention: parseAttention(agent.attention) }),
67
+ ...(agent.recording === undefined ? {} : { recording: parseRecording(agent.recording) }),
65
68
  ...(v2 ? { schedule: schedule(agent.schedule, `${label}.schedule`) } : {}),
66
69
  ...(agent.mcp === undefined ? {} : { mcp: mcpServers(agent.mcp, `${label}.mcp`) }),
67
70
  ...(agent.moltnet === undefined ? {} : { moltnet: moltnet(agent.moltnet, `${label}.moltnet`) }),
@@ -1 +1 @@
1
- {"version":3,"file":"organizationRuntimeReadiness.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeReadiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjG,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAmC,KAAK,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAEhH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,KAAK,EAAE,gCAAgC,CAAC;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,gCAAgC,CAAC,CA8D3C"}
1
+ {"version":3,"file":"organizationRuntimeReadiness.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeReadiness.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAIjG,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAmC,KAAK,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AAEhH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,KAAK,EAAE,gCAAgC,CAAC;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,gCAAgC,CAAC,CAiE3C"}
@@ -1,9 +1,17 @@
1
+ import { reportEngineTaskClockGap } from "../pi/engineTaskClock.js";
1
2
  import { startAgySubscriptionRealm } from "./agySubscriptionRealm.js";
2
3
  import { prepareEngineExecutable, verifyAgySubscriptionEnrollment } from "./engineReadiness.js";
4
+ import { readTaskClock } from "./taskClock.js";
5
+ import { memoryClockOptions } from "../pi/memoryClock.js";
3
6
  import { prepareOrganizationRuntimePaths } from "./physicalReadiness.js";
4
7
  import { materializePortableCredential } from "./portableCredentialMaterial.js";
5
8
  import { EngineBrokerControlClient } from "./engineBrokerControlClient.js";
6
9
  export async function prepareProductionReadiness(config) {
10
+ const clock = readTaskClock();
11
+ for (const agent of config.agents)
12
+ reportEngineTaskClockGap(agent.engine.kind);
13
+ if (config.agents.some((agent) => agent.memory !== undefined))
14
+ memoryClockOptions(clock);
7
15
  const paths = await prepareOrganizationRuntimePaths(config.agents);
8
16
  let realm;
9
17
  let grokBroker;
@@ -0,0 +1,7 @@
1
+ import { type WakeMomentRecording } from "../contracts/wakeMomentContract.js";
2
+ import type { OrganizationRuntimeAgentConfig } from "./organizationRuntime.js";
3
+ /** Receives the parser's already cloned JSON data; no I/O or caller hooks. */
4
+ export declare function parseRecording(value: unknown): WakeMomentRecording;
5
+ export declare function isolateRecording(agents: readonly OrganizationRuntimeAgentConfig[]): void;
6
+ export declare function pathsOverlap(left: string, right: string): boolean;
7
+ //# sourceMappingURL=organizationRuntimeRecording.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organizationRuntimeRecording.d.ts","sourceRoot":"","sources":["../../src/runtime/organizationRuntimeRecording.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC5F,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAE/E,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAgBlE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,8BAA8B,EAAE,GAAG,IAAI,CAYxF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,58 @@
1
+ import path from "node:path";
2
+ import { WAKE_MOMENTS } from "../contracts/wakeMomentContract.js";
3
+ /** Receives the parser's already cloned JSON data; no I/O or caller hooks. */
4
+ export function parseRecording(value) {
5
+ const input = exact(value, ["directory", "keepMs", "snapshots"]);
6
+ const directory = absolute(input.directory);
7
+ if (typeof input.keepMs !== "number" || !Number.isSafeInteger(input.keepMs) || input.keepMs <= 0)
8
+ throw new TypeError("recording.keepMs must be a positive safe integer");
9
+ if (!Array.isArray(input.snapshots) || input.snapshots.length > WAKE_MOMENTS.maxSnapshotRoots)
10
+ throw new TypeError("recording.snapshots must have 0..16 roots");
11
+ const ids = new Set(), paths = new Set();
12
+ const snapshots = input.snapshots.map((value) => {
13
+ const root = exact(value, ["id", "path"]);
14
+ if (typeof root.id !== "string" || !/^[A-Za-z0-9._-]{1,128}$/.test(root.id))
15
+ throw new TypeError("recording snapshot id is invalid");
16
+ const source = absolute(root.path);
17
+ if (ids.has(root.id) || paths.has(source))
18
+ throw new TypeError("recording snapshots must have unique ids and paths");
19
+ if (pathsOverlap(source, directory))
20
+ throw new TypeError("recording snapshot and directory must not overlap");
21
+ ids.add(root.id);
22
+ paths.add(source);
23
+ return { id: root.id, path: source };
24
+ });
25
+ return { directory, keepMs: input.keepMs, snapshots };
26
+ }
27
+ export function isolateRecording(agents) {
28
+ for (const agent of agents) {
29
+ if (!agent.recording)
30
+ continue;
31
+ for (const peer of agents) {
32
+ for (const root of [peer.workspacePath, peer.runtimeHomePath, ...(peer.id === agent.id || !peer.recording ? [] : [peer.recording.directory])]) {
33
+ if (pathsOverlap(agent.recording.directory, root))
34
+ throw new TypeError("recording.directory must not overlap runtime roots");
35
+ }
36
+ for (const snapshot of peer.recording?.snapshots ?? []) {
37
+ if (pathsOverlap(agent.recording.directory, snapshot.path))
38
+ throw new TypeError("recording.directory must not overlap snapshot roots");
39
+ }
40
+ }
41
+ }
42
+ }
43
+ export function pathsOverlap(left, right) {
44
+ return left === right || left === "/" || right === "/" || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
45
+ }
46
+ function exact(value, keys) {
47
+ if (!value || typeof value !== "object" || Array.isArray(value))
48
+ throw new TypeError("recording requires objects");
49
+ if (Object.keys(value).length !== keys.length || keys.some((key) => !Object.hasOwn(value, key)))
50
+ throw new TypeError("recording has invalid fields");
51
+ return value;
52
+ }
53
+ function absolute(value) {
54
+ if (typeof value !== "string" || !path.posix.isAbsolute(value) || value.includes("\0") || [...value].length > 4_096 || Buffer.byteLength(value) > 16_384)
55
+ throw new TypeError("recording paths must be bounded absolute POSIX paths");
56
+ // Match workspacePath/runtimeHomePath: canonicalize before testing equality/overlap.
57
+ return path.posix.normalize(value).replace(/\/+$/, "") || "/";
58
+ }
@@ -1,4 +1,12 @@
1
+ import { type Stats } from "node:fs";
2
+ import { lstat } from "node:fs/promises";
1
3
  import type { OrganizationRuntimeAgentConfig } from "./organizationRuntime.js";
4
+ type Identity = Readonly<{
5
+ dev: number;
6
+ ino: number;
7
+ uid: number;
8
+ mode: number;
9
+ }>;
2
10
  /**
3
11
  * `private` is every engine's runtime home: 0700, nothing but the runtime user.
4
12
  *
@@ -24,10 +32,12 @@ export type OrganizationRuntimePathAuthority = Readonly<{
24
32
  workspacePath: string;
25
33
  runtimeHomePath: string;
26
34
  verify(): Promise<void>;
35
+ verifyRecording?(): Promise<void>;
27
36
  }>;
28
37
  close(): Promise<void>;
29
38
  }>;
30
39
  export declare function prepareOrganizationRuntimePaths(agents: readonly OrganizationRuntimeAgentConfig[]): Promise<OrganizationRuntimePathAuthority>;
40
+ export declare function assertNoSymlinkComponents(target: string, stat?: typeof lstat): Promise<void>;
31
41
  /** Identity of the process Daimon runs as; a seam so every refusal is testable unprivileged. */
32
42
  export type RuntimeIdentity = Readonly<{
33
43
  uid: number;
@@ -43,5 +53,7 @@ type DirectoryEntry = Readonly<{
43
53
  }>;
44
54
  /** Pure shape check for a caller-prepared runtime root. */
45
55
  export declare function assertRuntimeDirectory(entry: DirectoryEntry, label: string, shape: DirectoryShape, runtime: RuntimeIdentity): void;
56
+ export declare function identity(entry: Stats): Identity;
57
+ export declare function sameIdentity(left: Identity, right: Identity): boolean;
46
58
  export {};
47
59
  //# sourceMappingURL=physicalReadiness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"physicalReadiness.d.ts","sourceRoot":"","sources":["../../src/runtime/physicalReadiness.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAG/E;;;;;;;;;;;;;;GAcG;AACH,KAAK,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,oBAAoB,CAAC;AAGhE;;;GAGG;AACH,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,8BAA8B,GAAG,QAAQ,CAAC;QACxD,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KACzB,CAAC,CAAC;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC,CAAC;AAOH,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,SAAS,8BAA8B,EAAE,GAChD,OAAO,CAAC,gCAAgC,CAAC,CA8C3C;AA4CD,gGAAgG;AAChG,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9F,KAAK,cAAc,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,IAAI,OAAO,CAAC;IAAC,cAAc,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AAE9H,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAalI"}
1
+ {"version":3,"file":"physicalReadiness.d.ts","sourceRoot":"","sources":["../../src/runtime/physicalReadiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAU,KAAK,EAAkB,MAAM,kBAAkB,CAAC;AAIjE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAG/E,KAAK,QAAQ,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAClF;;;;;;;;;;;;;;GAcG;AACH,KAAK,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,oBAAoB,CAAC;AAGhE;;;GAGG;AACH,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,8BAA8B,GAAG,QAAQ,CAAC;QACxD,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,eAAe,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KACnC,CAAC,CAAC;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC,CAAC;AAOH,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,SAAS,8BAA8B,EAAE,GAChD,OAAO,CAAC,gCAAgC,CAAC,CA0D3C;AAiCD,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CASzG;AAgBD,gGAAgG;AAChG,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC9F,KAAK,cAAc,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,IAAI,OAAO,CAAC;IAAC,cAAc,IAAI,OAAO,CAAA;CAAE,CAAC,CAAC;AAE9H,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAalI;AAMD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAA0F;AAC1I,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAmH"}
@@ -1,7 +1,8 @@
1
1
  import { constants } from "node:fs";
2
- import { lstat, open, realpath } from "node:fs/promises";
2
+ import { access, lstat, open, realpath } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { GROK_ENGINE_BROKER } from "../contracts/runtimeContractManifest.js";
5
+ import { pathsOverlap } from "./organizationRuntimeRecording.js";
5
6
  /** Only a brokered Grok agent's home is worker-traversable; every other engine keeps 0700. */
6
7
  function runtimeHomeShape(agent) {
7
8
  return agent.engine.kind === "grok" ? "worker-traversable" : "private";
@@ -9,12 +10,15 @@ function runtimeHomeShape(agent) {
9
10
  export async function prepareOrganizationRuntimePaths(agents) {
10
11
  const workspaces = new Map();
11
12
  const homes = new Map();
13
+ const recordings = new Map();
12
14
  try {
13
15
  for (const agent of agents) {
14
16
  workspaces.set(agent.id, await verifyDirectory(agent.workspacePath, "workspacePath", "safe"));
15
17
  homes.set(agent.id, await verifyDirectory(agent.runtimeHomePath, "runtimeHomePath", runtimeHomeShape(agent)));
18
+ if (agent.recording)
19
+ recordings.set(agent.id, await verifyDirectory(agent.recording.directory, "recording.directory", "private"));
16
20
  }
17
- const roots = [...workspaces.values(), ...homes.values()];
21
+ const roots = [...workspaces.values(), ...homes.values(), ...recordings.values()];
18
22
  for (let left = 0; left < roots.length; left += 1)
19
23
  for (let right = left + 1; right < roots.length; right += 1) {
20
24
  const first = roots[left];
@@ -23,9 +27,15 @@ export async function prepareOrganizationRuntimePaths(agents) {
23
27
  throw new Error(`physical runtime paths overlap: ${first.configured} and ${second.configured}`);
24
28
  }
25
29
  }
30
+ for (const agent of agents)
31
+ for (const source of agent.recording?.snapshots ?? []) {
32
+ const real = await readableSnapshotDirectory(source.path);
33
+ if (real !== undefined && [...recordings.values()].some((store) => pathsOverlap(store.real, real)))
34
+ throw new Error("physical recording and snapshot paths overlap");
35
+ }
26
36
  }
27
37
  catch (error) {
28
- const closeError = await closeAll([...workspaces.values(), ...homes.values()]);
38
+ const closeError = await closeAll([...workspaces.values(), ...homes.values(), ...recordings.values()]);
29
39
  if (closeError !== undefined)
30
40
  throw new AggregateError([error, closeError], "runtime path validation cleanup failed");
31
41
  throw error;
@@ -49,12 +59,20 @@ export async function prepareOrganizationRuntimePaths(agents) {
49
59
  const home = homes.get(agent.id);
50
60
  if (workspace === undefined || home === undefined)
51
61
  throw new Error(`no runtime path authority for ${agent.id}`);
52
- return { workspacePath: workspace.real, runtimeHomePath: home.real, verify: () => verify(agent) };
62
+ return { workspacePath: workspace.real, runtimeHomePath: home.real, verify: () => verify(agent),
63
+ verifyRecording: async () => {
64
+ if (closed)
65
+ throw new Error("runtime path authority is closed");
66
+ const recording = recordings.get(agent.id);
67
+ if (recording)
68
+ await verifyIdentity(recording, "recording.directory", "private");
69
+ }
70
+ };
53
71
  },
54
72
  async close() {
55
73
  if (closed)
56
74
  return;
57
- const failure = await closeAll([...workspaces.values(), ...homes.values()]);
75
+ const failure = await closeAll([...workspaces.values(), ...homes.values(), ...recordings.values()]);
58
76
  if (failure !== undefined)
59
77
  throw failure;
60
78
  closed = true;
@@ -93,17 +111,33 @@ async function verifyIdentity(directory, label, shape) {
93
111
  if (await realpath(directory.configured) !== directory.real)
94
112
  throw new Error(`${label} changed after readiness validation`);
95
113
  }
96
- async function assertNoSymlinkComponents(target) {
114
+ export async function assertNoSymlinkComponents(target, stat = lstat) {
97
115
  const parsed = path.parse(target);
98
116
  let current = parsed.root;
99
117
  for (const part of path.relative(parsed.root, target).split(path.sep).filter(Boolean)) {
100
118
  current = path.join(current, part);
101
119
  // macOS exposes /var as a system compatibility symlink to /private/var.
102
120
  // It is an OS-root alias, not a caller-controlled component.
103
- if ((await lstat(current)).isSymbolicLink() && current !== "/var")
121
+ if ((await stat(current)).isSymbolicLink() && current !== "/var")
104
122
  throw new Error(`path contains symlink: ${current}`);
105
123
  }
106
124
  }
125
+ /** Shared sources have no owner/privacy requirement. Missing sources are empty captures. */
126
+ async function readableSnapshotDirectory(target) {
127
+ try {
128
+ await assertNoSymlinkComponents(target);
129
+ const entry = await lstat(target);
130
+ if (!entry.isDirectory() || entry.isSymbolicLink())
131
+ throw new Error("recording snapshot must be a readable real directory");
132
+ await access(target, constants.R_OK | constants.X_OK);
133
+ return await realpath(target);
134
+ }
135
+ catch (error) {
136
+ if (error.code === "ENOENT")
137
+ return undefined;
138
+ throw error;
139
+ }
140
+ }
107
141
  /** Pure shape check for a caller-prepared runtime root. */
108
142
  export function assertRuntimeDirectory(entry, label, shape, runtime) {
109
143
  if (!entry.isDirectory() || entry.isSymbolicLink())
@@ -127,8 +161,8 @@ export function assertRuntimeDirectory(entry, label, shape, runtime) {
127
161
  function assertDirectory(entry, label, shape) {
128
162
  assertRuntimeDirectory(entry, label, shape, { uid: process.getuid?.() ?? -1, gid: process.getgid?.() ?? -1 });
129
163
  }
130
- function identity(entry) { return { dev: entry.dev, ino: entry.ino, uid: entry.uid, mode: entry.mode & 0o7777 }; }
131
- function sameIdentity(left, right) { return left.dev === right.dev && left.ino === right.ino && left.uid === right.uid && left.mode === right.mode; }
164
+ export function identity(entry) { return { dev: entry.dev, ino: entry.ino, uid: entry.uid, mode: entry.mode & 0o7777 }; }
165
+ export function sameIdentity(left, right) { return left.dev === right.dev && left.ino === right.ino && left.uid === right.uid && left.mode === right.mode; }
132
166
  function overlaps(left, right) { return left === right || left.startsWith(`${right}${path.sep}`) || right.startsWith(`${left}${path.sep}`); }
133
167
  function noFollow() { return constants.O_NOFOLLOW ?? 0; }
134
168
  function directoryFlag() { return constants.O_DIRECTORY ?? 0; }
@@ -1 +1 @@
1
- {"version":3,"file":"productionAgentTools.d.ts","sourceRoot":"","sources":["../../src/runtime/productionAgentTools.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAMtE,OAAO,KAAK,EAAE,8BAA8B,EAAgC,MAAM,0BAA0B,CAAC;AAM7G,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAe/E,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,8BAA8B,EAAE,WAAW,GAAE,2BAAgC,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAUhK"}
1
+ {"version":3,"file":"productionAgentTools.d.ts","sourceRoot":"","sources":["../../src/runtime/productionAgentTools.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAMtE,OAAO,KAAK,EAAE,8BAA8B,EAAgC,MAAM,0BAA0B,CAAC;AAO7G,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAe/E,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,8BAA8B,EAAE,WAAW,GAAE,2BAAgC,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAahK"}