@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,223 @@
1
+ import { constants } from "node:fs";
2
+ import { open } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ import { WAKE_MOMENTS } from "../contracts/wakeMomentContract.js";
6
+ import { assertNoSymlinkComponents, assertRuntimeDirectory, identity, sameIdentity } from "./physicalReadiness.js";
7
+ import { WakeMomentFault, WakeMomentIo } from "./wakeMomentIo.js";
8
+ export { WakeMomentFault } from "./wakeMomentIo.js";
9
+ /** Diagnostics never include source paths, file contents, or arbitrary exception text. */
10
+ export function wakeMomentFailure(error) {
11
+ if (error instanceof WakeMomentFault)
12
+ return error.reason;
13
+ const code = error?.code;
14
+ return ["EACCES", "EPERM", "ENOENT", "ENOSPC", "EIO", "ELOOP", "ENOTDIR", "EEXIST", "EMFILE", "ENAMETOOLONG", "EXDEV"].includes(code ?? "") ? code : "recording_io_failed";
15
+ }
16
+ /** Pin the caller's root, then re-check the same dev/ino/uid/mode at every write boundary. */
17
+ export async function pinMomentStore(directory, io, verifyPrepared) {
18
+ await assertNoSymlinkComponents(directory, io.fs.lstat);
19
+ const before = await io.fs.lstat(directory);
20
+ assertPrivate(before);
21
+ // A raw handle is shared by the capture and separately bounded finalization scopes.
22
+ const fd = await io.call("pinStore", () => open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW));
23
+ const expected = identity(before);
24
+ const store = {
25
+ async verify(scope, target) {
26
+ try {
27
+ await scope.call("verifyPreparedStore", async () => { await verifyPrepared?.(); });
28
+ await assertNoSymlinkComponents(directory, scope.fs.lstat);
29
+ const entry = await scope.fs.lstat(directory);
30
+ const opened = await scope.call("statStore", () => fd.stat());
31
+ assertPrivate(entry);
32
+ if (!sameIdentity(expected, identity(entry)) || !sameIdentity(expected, identity(opened)))
33
+ throw new Error("changed");
34
+ if (target?.startsWith(`${directory}${path.sep}`))
35
+ await assertNoSymlinkComponents(path.dirname(target), scope.fs.lstat);
36
+ }
37
+ catch {
38
+ scope.check();
39
+ throw new WakeMomentFault("recording_store_changed");
40
+ }
41
+ },
42
+ close: () => fd.close()
43
+ };
44
+ try {
45
+ await store.verify(io);
46
+ return store;
47
+ }
48
+ catch (error) {
49
+ void fd.close().catch(() => undefined);
50
+ throw error;
51
+ }
52
+ }
53
+ function assertPrivate(entry) {
54
+ assertRuntimeDirectory(entry, "recording.directory", "private", { uid: process.getuid?.() ?? -1, gid: process.getgid?.() ?? -1 });
55
+ }
56
+ export async function openMomentDirectory(directory, io = new WakeMomentIo()) {
57
+ await assertNoSymlinkComponents(directory, io.fs.lstat);
58
+ const fd = await io.fs.open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
59
+ try {
60
+ assertPrivate(await fd.stat());
61
+ return fd;
62
+ }
63
+ catch (error) {
64
+ await fd.close();
65
+ throw error;
66
+ }
67
+ }
68
+ export async function ensureMomentDirectory(directory, io = new WakeMomentIo()) {
69
+ let created = false;
70
+ try {
71
+ await io.fs.mkdir(directory, { mode: 0o700 });
72
+ created = true;
73
+ }
74
+ catch (error) {
75
+ if (error.code !== "EEXIST")
76
+ throw error;
77
+ }
78
+ const fd = await openMomentDirectory(directory, io);
79
+ try {
80
+ await fd.sync();
81
+ }
82
+ finally {
83
+ await fd.close();
84
+ }
85
+ if (created)
86
+ await syncMomentDirectory(path.dirname(directory), io);
87
+ }
88
+ export async function syncMomentDirectory(directory, io = new WakeMomentIo()) {
89
+ const fd = await io.fs.open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
90
+ try {
91
+ await fd.sync();
92
+ }
93
+ finally {
94
+ await fd.close();
95
+ }
96
+ }
97
+ /** Refuses symlinks, special files and hard-linked metadata, including a FIFO before it can block. */
98
+ export async function openMomentFile(file, flags = constants.O_RDONLY, io = new WakeMomentIo()) {
99
+ const fd = await io.fs.open(file, flags | constants.O_NOFOLLOW | constants.O_NONBLOCK, 0o600);
100
+ try {
101
+ const stat = await fd.stat();
102
+ if (!stat.isFile() || stat.nlink !== 1)
103
+ throw new WakeMomentFault("invalid_metadata_file");
104
+ return fd;
105
+ }
106
+ catch (error) {
107
+ await fd.close();
108
+ throw error;
109
+ }
110
+ }
111
+ export function manifestBytes(value, max = WAKE_MOMENTS.maxManifestBytes) {
112
+ const bytes = JSON.stringify(value);
113
+ if (Buffer.byteLength(bytes) > Math.min(max, WAKE_MOMENTS.maxManifestBytes))
114
+ throw new WakeMomentFault("manifest_size_limit");
115
+ return bytes;
116
+ }
117
+ export async function readMomentJson(file, io = new WakeMomentIo(), max = WAKE_MOMENTS.maxManifestBytes) {
118
+ const fd = await openMomentFile(file, constants.O_RDONLY, io);
119
+ try {
120
+ const limit = Math.min(max, WAKE_MOMENTS.maxManifestBytes);
121
+ if ((await fd.stat()).size > limit)
122
+ throw new WakeMomentFault("manifest_size_limit");
123
+ const chunks = [];
124
+ let length = 0;
125
+ for (;;) {
126
+ const buffer = Buffer.alloc(Math.min(64 * 1024, limit + 1 - length));
127
+ const { bytesRead } = await fd.read(buffer, 0, buffer.length, null);
128
+ if (!bytesRead)
129
+ break;
130
+ length += bytesRead;
131
+ if (length > limit)
132
+ throw new WakeMomentFault("manifest_size_limit");
133
+ chunks.push(buffer.subarray(0, bytesRead));
134
+ }
135
+ return JSON.parse(Buffer.concat(chunks, length).toString("utf8"));
136
+ }
137
+ finally {
138
+ await fd.close();
139
+ }
140
+ }
141
+ /** Unlike FileHandle.writeFile, each syscall is admitted through the cancellation fence. */
142
+ export async function writeMomentBytes(fd, bytes) {
143
+ const buffer = typeof bytes === "string" ? Buffer.from(bytes) : bytes;
144
+ for (let offset = 0; offset < buffer.length;) {
145
+ const { bytesWritten } = await fd.write(buffer, offset, Math.min(64 * 1024, buffer.length - offset), null);
146
+ if (!bytesWritten)
147
+ throw new WakeMomentFault("short_write");
148
+ offset += bytesWritten;
149
+ }
150
+ }
151
+ export async function writeMomentJson(file, bytes, io, cleanup, verifyReplacement) {
152
+ let temporary;
153
+ do {
154
+ temporary = `${file}.${randomUUID()}.tmp`;
155
+ } while (await momentPathExists(temporary, io));
156
+ const fd = await openMomentFile(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, io);
157
+ cleanup.add(temporary); // Exclusive creation, never the name alone, establishes cleanup ownership.
158
+ try {
159
+ await writeMomentBytes(fd, bytes);
160
+ await fd.sync();
161
+ await fd.close();
162
+ let reservation;
163
+ try {
164
+ reservation = await openMomentFile(file, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, io);
165
+ cleanup.add(file);
166
+ }
167
+ catch (error) {
168
+ if (!verifyReplacement || error.code !== "EEXIST")
169
+ throw error;
170
+ await verifyReplacement(); // The caller must establish ownership before replacement.
171
+ reservation = await openMomentFile(file, constants.O_RDONLY, io);
172
+ }
173
+ await reservation.close();
174
+ await io.fs.rename(temporary, file);
175
+ cleanup.delete(temporary);
176
+ await syncMomentDirectory(path.dirname(file), io);
177
+ }
178
+ finally {
179
+ await fd.close();
180
+ }
181
+ }
182
+ export async function momentPathExists(target, io = new WakeMomentIo()) {
183
+ try {
184
+ await io.fs.lstat(target);
185
+ return true;
186
+ }
187
+ catch (error) {
188
+ if (error.code === "ENOENT")
189
+ return false;
190
+ throw error;
191
+ }
192
+ }
193
+ /** Never recurse until root identity AND the target's parent components have just been verified. */
194
+ export async function cleanMomentPaths(paths, store, io) {
195
+ for (const target of paths) {
196
+ await removeMomentPath(target, store, io);
197
+ paths.delete(target);
198
+ }
199
+ }
200
+ /** Explicit traversal keeps recursive cleanup cancellable between individual filesystem calls. */
201
+ export async function removeMomentPath(target, store, io) {
202
+ await store.verify(io, target);
203
+ let entry;
204
+ try {
205
+ entry = await io.fs.lstat(target);
206
+ }
207
+ catch (error) {
208
+ if (error.code === "ENOENT")
209
+ return;
210
+ throw error;
211
+ }
212
+ if (entry.isDirectory()) {
213
+ await assertNoSymlinkComponents(target, io.fs.lstat);
214
+ for await (const child of await io.fs.opendir(target))
215
+ await removeMomentPath(path.join(target, child.name), store, io);
216
+ await store.verify(io, target);
217
+ await io.fs.rmdir(target);
218
+ }
219
+ else {
220
+ await store.verify(io, target);
221
+ await io.fs.unlink(target);
222
+ }
223
+ }
@@ -0,0 +1,26 @@
1
+ import { WakeMomentIo } from "./wakeMomentIo.js";
2
+ export declare const SNAPSHOT_NAME: RegExp;
3
+ export declare const snapshotRootName: (id: string) => string;
4
+ declare const INDEX_VERSION = "noopolis.daimon.wake-latest.v1";
5
+ export type SnapshotIndex = {
6
+ version: typeof INDEX_VERSION;
7
+ snapshot: string;
8
+ oldest: string | null;
9
+ };
10
+ export declare function snapshotName(startedAt: string, executionId: string): string;
11
+ export declare function snapshotTime(name: string): number;
12
+ /** Eligibility precedes BOTH base selection and retention protection, not only deletion. */
13
+ export declare function eligibleSnapshot(directory: string, name: string, io: WakeMomentIo, rollback?: ReadonlySet<string>): Promise<boolean>;
14
+ /** Only a missing/invalid index or an expired oldest name requires history enumeration. */
15
+ export declare function completeSnapshotNames(directory: string, io?: WakeMomentIo, rollback?: ReadonlySet<string>): Promise<string[]>;
16
+ export declare function indexFromNames(names: string[]): SnapshotIndex | undefined;
17
+ export declare function latestSnapshotIndex(directory: string, io: WakeMomentIo, rollback?: ReadonlySet<string>): Promise<{
18
+ index?: SnapshotIndex;
19
+ names?: string[];
20
+ }>;
21
+ /** Called after manifest publication, before the marker: a crash leaves an invalid index,
22
+ * never a valid stale pointer hiding a newer complete snapshot. Clock rollback keeps the newer base. */
23
+ export declare function indexWithSnapshot(previous: SnapshotIndex | undefined, name: string): SnapshotIndex;
24
+ export declare function writeSnapshotIndex(directory: string, index: SnapshotIndex, io: WakeMomentIo, cleanup: Set<string>): Promise<void>;
25
+ export {};
26
+ //# sourceMappingURL=wakeMomentHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeMomentHistory.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeMomentHistory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,eAAO,MAAM,aAAa,QAAyC,CAAC;AACpE,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,KAAG,MAAoE,CAAC;AACnH,QAAA,MAAM,aAAa,mCAAmC,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvG,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAG3E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,4FAA4F;AAC5F,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAU1I;AAED,2FAA2F;AAC3F,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,eAAqB,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAczI;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,GAAG,SAAS,CAEzE;AAUD,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;IAAE,KAAK,CAAC,EAAE,aAAa,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAOnK;AAED;wGACwG;AACxG,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAMlG;AAED,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CASvI"}
@@ -0,0 +1,109 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { WAKE_MOMENTS } from "../contracts/wakeMomentContract.js";
4
+ import { readMomentJson, writeMomentJson } from "./wakeMomentFs.js";
5
+ import { WakeMomentFault, WakeMomentIo } from "./wakeMomentIo.js";
6
+ export const SNAPSHOT_NAME = /^\d{8}T\d{9}Z-[A-Za-z0-9._-]{1,200}$/;
7
+ export const snapshotRootName = (id) => id === "." || id === ".." ? id.replaceAll(".", "%2E") : id;
8
+ const INDEX_VERSION = "noopolis.daimon.wake-latest.v1";
9
+ const INDEX_BYTES = 1024;
10
+ export function snapshotName(startedAt, executionId) {
11
+ const execution = /^[A-Za-z0-9._-]{1,128}$/.test(executionId) ? executionId : `sha256-${createHash("sha256").update(executionId).digest("hex")}`;
12
+ return `${startedAt.replace(/[-:.]/g, "")}-${execution}`;
13
+ }
14
+ export function snapshotTime(name) {
15
+ if (!SNAPSHOT_NAME.test(name))
16
+ return NaN;
17
+ const value = `${name.slice(0, 4)}-${name.slice(4, 6)}-${name.slice(6, 8)}T${name.slice(9, 11)}:${name.slice(11, 13)}:${name.slice(13, 15)}.${name.slice(15, 18)}Z`;
18
+ const time = Date.parse(value);
19
+ return Number.isFinite(time) && new Date(time).toISOString() === value ? time : NaN;
20
+ }
21
+ /** Eligibility precedes BOTH base selection and retention protection, not only deletion. */
22
+ export async function eligibleSnapshot(directory, name, io, rollback) {
23
+ if (!Number.isFinite(snapshotTime(name)))
24
+ return false;
25
+ const target = path.join(directory, name);
26
+ if ([target, `${target}.manifest.json`, `${target}${WAKE_MOMENTS.completionSuffix}`].some((file) => rollback?.has(file)))
27
+ return false;
28
+ try {
29
+ const entry = await io.fs.lstat(target), manifest = await io.fs.lstat(`${target}.manifest.json`);
30
+ const marker = await io.fs.lstat(`${target}${WAKE_MOMENTS.completionSuffix}`);
31
+ return entry.isDirectory() && manifest.isFile() && manifest.nlink === 1
32
+ && marker.isFile() && marker.size === 0 && marker.nlink === 1;
33
+ }
34
+ catch (error) {
35
+ if (error.code === "ENOENT")
36
+ return false;
37
+ throw error;
38
+ }
39
+ }
40
+ /** Only a missing/invalid index or an expired oldest name requires history enumeration. */
41
+ export async function completeSnapshotNames(directory, io = new WakeMomentIo(), rollback) {
42
+ const names = [];
43
+ try {
44
+ for await (const entry of await io.fs.opendir(directory)) {
45
+ if (entry.isDirectory() && await eligibleSnapshot(directory, entry.name, io, rollback))
46
+ names.push(entry.name);
47
+ }
48
+ }
49
+ catch (error) {
50
+ if (error.code !== "ENOENT")
51
+ throw error;
52
+ }
53
+ names.sort((a, b) => snapshotTime(b) - snapshotTime(a) || b.localeCompare(a));
54
+ // Timestamp ties use publication metadata; no historical manifest contents are read.
55
+ const stamps = new Map();
56
+ for (let i = 0; i < names.length; i++)
57
+ if (snapshotTime(names[i]) === snapshotTime(names[i - 1] ?? "") || snapshotTime(names[i]) === snapshotTime(names[i + 1] ?? "")) {
58
+ stamps.set(names[i], (await io.fs.lstat(path.join(directory, `${names[i]}.manifest.json`), { bigint: true })).mtimeNs);
59
+ }
60
+ return names.sort((a, b) => snapshotTime(b) - snapshotTime(a) || ((stamps.get(a) ?? 0n) < (stamps.get(b) ?? 0n) ? 1 : (stamps.get(a) ?? 0n) > (stamps.get(b) ?? 0n) ? -1 : b.localeCompare(a)));
61
+ }
62
+ export function indexFromNames(names) {
63
+ return names[0] ? { version: INDEX_VERSION, snapshot: names[0], oldest: names.length > 1 ? names[names.length - 1] : null } : undefined;
64
+ }
65
+ function isSnapshotIndex(value) {
66
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 3)
67
+ return false;
68
+ const index = value;
69
+ return index.version === INDEX_VERSION && typeof index.snapshot === "string" && Number.isFinite(snapshotTime(index.snapshot))
70
+ && (index.oldest === null || (typeof index.oldest === "string" && index.oldest !== index.snapshot
71
+ && Number.isFinite(snapshotTime(index.oldest)) && snapshotTime(index.oldest) <= snapshotTime(index.snapshot)));
72
+ }
73
+ export async function latestSnapshotIndex(directory, io, rollback) {
74
+ try {
75
+ const value = await readMomentJson(path.join(directory, "latest"), io, INDEX_BYTES);
76
+ if (isSnapshotIndex(value) && await eligibleSnapshot(directory, value.snapshot, io, rollback))
77
+ return { index: value };
78
+ }
79
+ catch {
80
+ io.check(); /* The bounded index is a cache; recover from missing/damaged metadata. */
81
+ }
82
+ const names = await completeSnapshotNames(directory, io, rollback);
83
+ return { index: indexFromNames(names), names };
84
+ }
85
+ /** Called after manifest publication, before the marker: a crash leaves an invalid index,
86
+ * never a valid stale pointer hiding a newer complete snapshot. Clock rollback keeps the newer base. */
87
+ export function indexWithSnapshot(previous, name) {
88
+ if (!previous)
89
+ return { version: INDEX_VERSION, snapshot: name, oldest: null };
90
+ const newest = snapshotTime(name) >= snapshotTime(previous.snapshot) ? name : previous.snapshot;
91
+ const others = [previous.oldest, newest === name ? previous.snapshot : name].filter((entry) => entry !== null);
92
+ others.sort((a, b) => snapshotTime(a) - snapshotTime(b));
93
+ return { version: INDEX_VERSION, snapshot: newest, oldest: others[0] };
94
+ }
95
+ export async function writeSnapshotIndex(directory, index, io, cleanup) {
96
+ const target = path.join(directory, "latest");
97
+ await writeMomentJson(target, JSON.stringify(index), io, cleanup, async () => {
98
+ let owned = false;
99
+ try {
100
+ owned = isSnapshotIndex(await readMomentJson(target, io, INDEX_BYTES));
101
+ }
102
+ catch {
103
+ io.check();
104
+ }
105
+ if (!owned)
106
+ throw new WakeMomentFault("invalid_latest_index");
107
+ });
108
+ cleanup.delete(target);
109
+ }
@@ -0,0 +1,22 @@
1
+ import * as fs from "node:fs/promises";
2
+ export declare class WakeMomentFault extends Error {
3
+ readonly reason: string;
4
+ constructor(reason: string);
5
+ }
6
+ /** Internal fault-injection seam, never configuration or a public runtime export. */
7
+ export type WakeMomentIoProbe = (operation: string, target?: string, entries?: number) => void | Promise<void>;
8
+ /** Every filesystem call checks cancellation before issuing I/O and after it settles. */
9
+ export declare class WakeMomentIo {
10
+ private readonly probe?;
11
+ readonly fs: typeof fs;
12
+ verifyWrite?: (target?: string) => Promise<void>;
13
+ private aborted;
14
+ private expires;
15
+ constructor(probe?: WakeMomentIoProbe | undefined);
16
+ check(): void;
17
+ call<T>(operation: string, action: () => Promise<T>, target?: string): Promise<T>;
18
+ bounded<T>(milliseconds: number, action: () => Promise<T>): Promise<T>;
19
+ private handle;
20
+ private directory;
21
+ }
22
+ //# sourceMappingURL=wakeMomentIo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeMomentIo.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeMomentIo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAKvC,qBAAa,eAAgB,SAAQ,KAAK;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CACpC;AAED,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/G,yFAAyF;AACzF,qBAAa,YAAY;IAMX,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IALnC,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAY;gBAEE,KAAK,CAAC,EAAE,iBAAiB,YAAA;IAgBtD,KAAK,IAAI,IAAI;IAOP,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAoBjF,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAa5E,OAAO,CAAC,MAAM;IASd,OAAO,CAAC,SAAS;CAkBlB"}
@@ -0,0 +1,114 @@
1
+ import * as fs from "node:fs/promises";
2
+ import { performance } from "node:perf_hooks";
3
+ export class WakeMomentFault extends Error {
4
+ reason;
5
+ constructor(reason) {
6
+ super(reason);
7
+ this.reason = reason;
8
+ }
9
+ }
10
+ /** Every filesystem call checks cancellation before issuing I/O and after it settles. */
11
+ export class WakeMomentIo {
12
+ probe;
13
+ fs;
14
+ verifyWrite;
15
+ aborted = false;
16
+ expires = Infinity;
17
+ constructor(probe) {
18
+ this.probe = probe;
19
+ this.fs = new Proxy(fs, {
20
+ get: (target, key) => {
21
+ const method = Reflect.get(target, key);
22
+ if (typeof method !== "function")
23
+ return method;
24
+ return (...args) => this.call(String(key), async () => {
25
+ const result = await Reflect.apply(method, target, args);
26
+ if (key === "open")
27
+ return this.handle(result, String(args[0]));
28
+ if (key === "opendir")
29
+ return this.directory(result, String(args[0]));
30
+ if (key === "readdir")
31
+ await this.probe?.("directoryEntries", String(args[0]), result.length);
32
+ return result;
33
+ }, typeof args[key === "symlink" || key === "link" ? 1 : 0] === "string" ? String(args[key === "symlink" || key === "link" ? 1 : 0]) : undefined);
34
+ }
35
+ });
36
+ }
37
+ check() {
38
+ if (this.aborted || performance.now() >= this.expires) {
39
+ this.aborted = true;
40
+ throw new WakeMomentFault("capture_deadline_exceeded");
41
+ }
42
+ }
43
+ async call(operation, action, target) {
44
+ this.check();
45
+ await this.probe?.(operation, target);
46
+ this.check();
47
+ if (["open", "mkdir", "rename", "rm", "unlink", "rmdir", "link", "symlink", "write", "writeFile", "chmod"].includes(operation)) {
48
+ await this.verifyWrite?.(target);
49
+ this.check();
50
+ }
51
+ const result = await action();
52
+ try {
53
+ this.check();
54
+ }
55
+ catch (error) {
56
+ // An open already in flight can finish after abandonment; release its handle.
57
+ if (result && typeof result.close === "function") {
58
+ void result.close().catch(() => undefined);
59
+ }
60
+ throw error;
61
+ }
62
+ return result;
63
+ }
64
+ async bounded(milliseconds, action) {
65
+ this.expires = performance.now() + milliseconds;
66
+ let timer;
67
+ const deadline = new Promise((_resolve, reject) => {
68
+ timer = setTimeout(() => {
69
+ this.aborted = true;
70
+ reject(new WakeMomentFault("capture_deadline_exceeded"));
71
+ }, milliseconds);
72
+ });
73
+ try {
74
+ return await Promise.race([action(), deadline]);
75
+ }
76
+ finally {
77
+ clearTimeout(timer);
78
+ this.aborted = true;
79
+ }
80
+ }
81
+ handle(handle, target) {
82
+ return new Proxy(handle, { get: (fd, key) => {
83
+ const method = Reflect.get(fd, key, fd);
84
+ if (typeof method !== "function")
85
+ return method;
86
+ if (key === "close")
87
+ return method.bind(fd); // Closing never publishes data.
88
+ return (...args) => this.call(String(key), async () => Reflect.apply(method, fd, args), target);
89
+ } });
90
+ }
91
+ directory(directory, target) {
92
+ const io = this;
93
+ return new Proxy(directory, { get: (dir, key) => {
94
+ if (key === Symbol.asyncIterator)
95
+ return async function* () {
96
+ try {
97
+ for (;;) {
98
+ const entry = await io.call("readdir", () => dir.read(), target);
99
+ if (!entry)
100
+ break;
101
+ await io.probe?.("directoryEntries", target, 1);
102
+ io.check();
103
+ yield entry;
104
+ }
105
+ }
106
+ finally {
107
+ await dir.close();
108
+ }
109
+ };
110
+ const method = Reflect.get(dir, key, dir);
111
+ return typeof method === "function" ? method.bind(dir) : method;
112
+ } });
113
+ }
114
+ }
@@ -0,0 +1,33 @@
1
+ import { WAKE_MOMENTS } from "../contracts/wakeMomentContract.js";
2
+ import type { OrganizationRuntimeAgentConfig } from "./organizationRuntime.js";
3
+ import type { StoredWakeAcceptanceRecord } from "./wakeAcceptanceRecord.js";
4
+ import { type WakeMomentIoProbe } from "./wakeMomentIo.js";
5
+ import { type WakeSnapshot } from "./wakeMomentSnapshot.js";
6
+ export { appendWakeMomentRow } from "./wakeMomentRows.js";
7
+ export { pruneWakeMoments } from "./wakeMomentRetention.js";
8
+ export type WakeMomentRow = {
9
+ version: typeof WAKE_MOMENTS.version;
10
+ agent_id: string;
11
+ execution_id: string;
12
+ started_at: string;
13
+ deliveries: {
14
+ acceptance_id: string;
15
+ delivery_id: string;
16
+ kind: string;
17
+ occurred_at: string;
18
+ }[];
19
+ snapshots: WakeSnapshot[];
20
+ error?: string;
21
+ };
22
+ /** Internal seams, intentionally absent from the public runtime barrel/config schema. */
23
+ export type WakeMomentOptions = {
24
+ now?: () => number;
25
+ captureDeadlineMs?: number;
26
+ finalizationDeadlineMs?: number;
27
+ maxManifestBytes?: number;
28
+ probe?: WakeMomentIoProbe;
29
+ verifyStore?: () => Promise<void>;
30
+ };
31
+ /** Advisory, per attempt, and completely inert for an agent without recording. */
32
+ export declare function recordWakeMoment(agent: OrganizationRuntimeAgentConfig, executionId: string, records: readonly StoredWakeAcceptanceRecord[], options?: WakeMomentOptions): Promise<void>;
33
+ //# sourceMappingURL=wakeMomentRecorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeMomentRecorder.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeMomentRecorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,OAAO,YAAY,CAAC,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IACjG,UAAU,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChG,SAAS,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3C,CAAC;AAEF,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF,CAAC;AAEF,kFAAkF;AAClF,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,8BAA8B,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,0BAA0B,EAAE,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDjM"}
@@ -0,0 +1,81 @@
1
+ import { WAKE_MOMENTS } from "../contracts/wakeMomentContract.js";
2
+ import { cleanMomentPaths, pinMomentStore, wakeMomentFailure } from "./wakeMomentFs.js";
3
+ import { WakeMomentIo } from "./wakeMomentIo.js";
4
+ import { captureWakeSnapshot } from "./wakeMomentSnapshot.js";
5
+ import { appendWakeMomentRow } from "./wakeMomentRows.js";
6
+ import { pruneWakeMoments } from "./wakeMomentRetention.js";
7
+ export { appendWakeMomentRow } from "./wakeMomentRows.js";
8
+ export { pruneWakeMoments } from "./wakeMomentRetention.js";
9
+ /** Advisory, per attempt, and completely inert for an agent without recording. */
10
+ export async function recordWakeMoment(agent, executionId, records, options = {}) {
11
+ const recording = agent.recording;
12
+ if (!recording)
13
+ return;
14
+ const now = options.now ?? Date.now, io = new WakeMomentIo(options.probe), cleanup = new Set();
15
+ const errors = [];
16
+ let store, row;
17
+ try {
18
+ await io.bounded(options.captureDeadlineMs ?? WAKE_MOMENTS.captureDeadlineMs, async () => {
19
+ store = await pinMomentStore(recording.directory, io, options.verifyStore);
20
+ io.verifyWrite = (target) => store.verify(io, target);
21
+ await store.verify(io); // Immediately before capture, against the pinned root.
22
+ row = {
23
+ version: WAKE_MOMENTS.version, agent_id: agent.id, execution_id: executionId, started_at: new Date(now()).toISOString(),
24
+ deliveries: records.map(({ acceptance_id, delivery_id, event }) => ({ acceptance_id, delivery_id, kind: event.kind, occurred_at: event.occurred_at })), snapshots: []
25
+ };
26
+ for (const root of recording.snapshots) {
27
+ try {
28
+ const captured = await captureWakeSnapshot({ directory: recording.directory, root, startedAt: row.started_at, executionId, io, cleanup, maxManifestBytes: options.maxManifestBytes });
29
+ io.check();
30
+ row.snapshots.push(captured.snapshot);
31
+ if (captured.note)
32
+ errors.push(`${root.id}: ${captured.note}`);
33
+ }
34
+ catch (error) {
35
+ io.check(); // Deadline abandons the remaining roots, not just this one.
36
+ if (wakeMomentFailure(error) === "recording_store_changed")
37
+ throw error;
38
+ errors.push(`${root.id}: ${wakeMomentFailure(error)}`);
39
+ }
40
+ }
41
+ });
42
+ }
43
+ catch (error) {
44
+ errors.push(wakeMomentFailure(error));
45
+ }
46
+ // Capture is fenced before the row or cognition: late I/O completions cannot issue another write.
47
+ const finalizer = new WakeMomentIo(options.probe);
48
+ try {
49
+ await finalizer.bounded(options.finalizationDeadlineMs ?? WAKE_MOMENTS.finalizationDeadlineMs, async () => {
50
+ if (!store || !row) {
51
+ logFailure(errors.join("; "));
52
+ return;
53
+ }
54
+ finalizer.verifyWrite = (target) => store.verify(finalizer, target);
55
+ if (errors.length)
56
+ row.error = errors.join("; ").slice(0, 2048);
57
+ // A stuck best-effort cleanup must not prevent the error row from being attempted.
58
+ const [cleaned] = await Promise.all([
59
+ cleanMomentPaths(cleanup, store, finalizer).then(() => true, (error) => { logFailure(wakeMomentFailure(error)); return false; }),
60
+ appendWakeMomentRow(recording.directory, row, finalizer)
61
+ ]);
62
+ if (cleaned) {
63
+ finalizer.check();
64
+ await pruneWakeMoments(recording, agent.id, now(), row, finalizer, store);
65
+ }
66
+ });
67
+ }
68
+ catch (error) {
69
+ logFailure(wakeMomentFailure(error));
70
+ }
71
+ finally {
72
+ if (store)
73
+ void store.close().catch(() => undefined);
74
+ }
75
+ }
76
+ function logFailure(reason) {
77
+ try {
78
+ console.error(`daimon: wake recording failed: ${reason}`);
79
+ }
80
+ catch { /* Logging is advisory too. */ }
81
+ }
@@ -0,0 +1,7 @@
1
+ import { type WakeMomentRecording } from "../contracts/wakeMomentContract.js";
2
+ import { type MomentStore } from "./wakeMomentFs.js";
3
+ import { WakeMomentIo } from "./wakeMomentIo.js";
4
+ import type { WakeMomentRow } from "./wakeMomentRecorder.js";
5
+ /** First-line age check; a full sweep every 64 calls also handles foreign prefixes/clock rollback. */
6
+ export declare function pruneWakeMoments(recording: WakeMomentRecording, agentId: string, now?: number, current?: WakeMomentRow, scope?: WakeMomentIo, authority?: MomentStore): Promise<void>;
7
+ //# sourceMappingURL=wakeMomentRetention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakeMomentRetention.d.ts","sourceRoot":"","sources":["../../src/runtime/wakeMomentRetention.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC5F,OAAO,EAAkI,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrL,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAkB7D,sGAAsG;AACtG,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAa,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/L"}