@wrongstack/core 0.313.0 → 0.313.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/chronicle/index.js +84 -6391
  2. package/dist/chronicle/project-server.js +84 -4868
  3. package/dist/chunk-2BUUIMQB.js +225 -0
  4. package/dist/chunk-2FK4WTFF.js +207 -0
  5. package/dist/chunk-2FQL4PDC.js +292 -0
  6. package/dist/chunk-2KKODIQU.js +2256 -0
  7. package/dist/chunk-2YU4WVBW.js +134 -0
  8. package/dist/chunk-3E244UDA.js +356 -0
  9. package/dist/chunk-3XPVB7BA.js +135 -0
  10. package/dist/chunk-4LJJYBAU.js +388 -0
  11. package/dist/chunk-4VWO3ZXL.js +342 -0
  12. package/dist/chunk-4XV6IR37.js +187 -0
  13. package/dist/chunk-4ZHTKJHM.js +183 -0
  14. package/dist/chunk-52RTVH4M.js +361 -0
  15. package/dist/chunk-57GEWK5X.js +26 -0
  16. package/dist/chunk-5DSMEVA4.js +931 -0
  17. package/dist/chunk-5RP3QTRL.js +1223 -0
  18. package/dist/chunk-5S437PRC.js +50 -0
  19. package/dist/chunk-5XDUGTKN.js +511 -0
  20. package/dist/chunk-62V2AFVG.js +422 -0
  21. package/dist/chunk-65RDIOR2.js +2470 -0
  22. package/dist/chunk-6AKOTGLM.js +597 -0
  23. package/dist/chunk-7ZHZE5EO.js +28 -0
  24. package/dist/chunk-A455ATZB.js +140 -0
  25. package/dist/chunk-AD7ZFZCS.js +46 -0
  26. package/dist/chunk-AI4LLQ2U.js +856 -0
  27. package/dist/chunk-ASX6AW36.js +789 -0
  28. package/dist/chunk-AT4V4XGI.js +3522 -0
  29. package/dist/chunk-AWNQ6ED6.js +311 -0
  30. package/dist/chunk-B4AMLZPB.js +9 -0
  31. package/dist/chunk-BAKVJLEV.js +417 -0
  32. package/dist/chunk-BJHVULHO.js +55 -0
  33. package/dist/chunk-CB2HFKTP.js +9 -0
  34. package/dist/chunk-CE4ZOCOI.js +1640 -0
  35. package/dist/chunk-CQQL4ZOQ.js +151 -0
  36. package/dist/chunk-DEIQMBD2.js +65 -0
  37. package/dist/chunk-E6I5RXVK.js +58 -0
  38. package/dist/chunk-EFEHDQGY.js +1589 -0
  39. package/dist/chunk-EODDVJBM.js +82 -0
  40. package/dist/chunk-EPC3GZD4.js +308 -0
  41. package/dist/chunk-FAYRX2AD.js +13 -0
  42. package/dist/chunk-FFSX2QDQ.js +362 -0
  43. package/dist/chunk-FHAMQS7Q.js +5856 -0
  44. package/dist/chunk-FIV4IJL7.js +2263 -0
  45. package/dist/chunk-FNXVT7HF.js +52 -0
  46. package/dist/chunk-FSWCWSTS.js +395 -0
  47. package/dist/chunk-FW5LDDMJ.js +16464 -0
  48. package/dist/chunk-FXQLIX3K.js +211 -0
  49. package/dist/chunk-G5AAKEEZ.js +228 -0
  50. package/dist/chunk-G5S3S5ZN.js +38 -0
  51. package/dist/chunk-GAZEWQEZ.js +231 -0
  52. package/dist/chunk-GFJOMSHG.js +125 -0
  53. package/dist/chunk-GFXZTHNI.js +158 -0
  54. package/dist/chunk-GM54GJBU.js +960 -0
  55. package/dist/chunk-GPQ3NJPR.js +900 -0
  56. package/dist/chunk-GTCU6DZ3.js +1 -0
  57. package/dist/chunk-H5E77KUJ.js +15 -0
  58. package/dist/chunk-H72H4XDU.js +1023 -0
  59. package/dist/chunk-HA4ZYA6T.js +178 -0
  60. package/dist/chunk-I45KAW2N.js +5020 -0
  61. package/dist/chunk-IQ35HMQL.js +1396 -0
  62. package/dist/chunk-IYFBFX34.js +86 -0
  63. package/dist/chunk-JJPMWPB6.js +109 -0
  64. package/dist/chunk-JTHKOYWU.js +663 -0
  65. package/dist/chunk-JVGPR3TS.js +365 -0
  66. package/dist/chunk-JVKB2TBL.js +48 -0
  67. package/dist/chunk-JX7VBZDK.js +31 -0
  68. package/dist/chunk-K3G6JBUC.js +5232 -0
  69. package/dist/chunk-K4YUNKU7.js +589 -0
  70. package/dist/chunk-KBZGE6LN.js +710 -0
  71. package/dist/chunk-KDQJCT2J.js +45 -0
  72. package/dist/chunk-KI4L2DUL.js +560 -0
  73. package/dist/chunk-KJ2ZTQSL.js +47 -0
  74. package/dist/chunk-KSJ5UTP7.js +154 -0
  75. package/dist/chunk-LHOA5DWA.js +28 -0
  76. package/dist/chunk-LX5QWLBL.js +27 -0
  77. package/dist/chunk-LZDDLNCZ.js +279 -0
  78. package/dist/chunk-M2WF6AXG.js +115 -0
  79. package/dist/chunk-M7BS2K6U.js +1 -0
  80. package/dist/chunk-NDBIPFNY.js +102 -0
  81. package/dist/chunk-NEMIWEXC.js +1199 -0
  82. package/dist/chunk-NFAYHPRH.js +323 -0
  83. package/dist/chunk-O37BQ65S.js +21 -0
  84. package/dist/chunk-O74MJHXU.js +187 -0
  85. package/dist/chunk-OJZEX73F.js +112 -0
  86. package/dist/chunk-PAAWLLZX.js +74 -0
  87. package/dist/chunk-PFXFQQRT.js +339 -0
  88. package/dist/chunk-PFZO3C6H.js +14 -0
  89. package/dist/chunk-PHH7LD45.js +113 -0
  90. package/dist/chunk-QRTG4XTI.js +188 -0
  91. package/dist/chunk-QVOVMXJR.js +1351 -0
  92. package/dist/chunk-RCQYT2I4.js +757 -0
  93. package/dist/chunk-RD6DEE36.js +1178 -0
  94. package/dist/chunk-RJIZEJ42.js +30 -0
  95. package/dist/chunk-RPOA7IAE.js +150 -0
  96. package/dist/chunk-SN2DQRL6.js +1172 -0
  97. package/dist/chunk-SQJVFEBV.js +41 -0
  98. package/dist/chunk-SQP64RCS.js +5391 -0
  99. package/dist/chunk-SVT4RNTR.js +49 -0
  100. package/dist/chunk-SWUFN7XM.js +43 -0
  101. package/dist/chunk-SXMVYC5H.js +174 -0
  102. package/dist/chunk-SZ6AYUIZ.js +23 -0
  103. package/dist/chunk-T3KGZBU5.js +3171 -0
  104. package/dist/chunk-TBRL2RCC.js +9 -0
  105. package/dist/chunk-TRT2HRWZ.js +18 -0
  106. package/dist/chunk-TYW2GI5I.js +1077 -0
  107. package/dist/chunk-UIWONWVG.js +4959 -0
  108. package/dist/chunk-VB3H5WWB.js +151 -0
  109. package/dist/chunk-W4A6QA3D.js +258 -0
  110. package/dist/chunk-WBPVKEEE.js +227 -0
  111. package/dist/chunk-WEBMSQP2.js +123 -0
  112. package/dist/chunk-WOEZV3Q4.js +333 -0
  113. package/dist/chunk-WQQON3SV.js +473 -0
  114. package/dist/chunk-WQTOVMGV.js +119 -0
  115. package/dist/chunk-WZO23AWO.js +2779 -0
  116. package/dist/chunk-XBDP4QPW.js +7 -0
  117. package/dist/chunk-XEBHSJLT.js +103 -0
  118. package/dist/chunk-XQ2ECNK3.js +1 -0
  119. package/dist/chunk-XQ2XGIBF.js +2086 -0
  120. package/dist/chunk-Y2GE5TSI.js +355 -0
  121. package/dist/chunk-YBUJCZ57.js +351 -0
  122. package/dist/chunk-YNCVSFGF.js +149 -0
  123. package/dist/chunk-YUPMXOGP.js +282 -0
  124. package/dist/chunk-YY6APYUM.js +49 -0
  125. package/dist/chunk-Z2IJUO45.js +691 -0
  126. package/dist/chunk-ZEHUCJHY.js +129 -0
  127. package/dist/chunk-ZYU2YOJ6.js +1496 -0
  128. package/dist/chunk-ZZ4V3WIZ.js +163 -0
  129. package/dist/coordination/agents/index.js +145 -6260
  130. package/dist/coordination/index.js +365 -34324
  131. package/dist/coordination/mailbox-project-server.js +48 -3276
  132. package/dist/coordination/provider-status-tracker.d.ts +4 -0
  133. package/dist/core/agent.d.ts +14 -1
  134. package/dist/core/index.js +267 -12480
  135. package/dist/core/model-ref.js +5 -23
  136. package/dist/core/provider-runner.d.ts +36 -1
  137. package/dist/design/index.js +50 -1087
  138. package/dist/execution/index.d.ts +1 -1
  139. package/dist/execution/index.js +1599 -21888
  140. package/dist/execution/prompt-enhancer.js +16 -535
  141. package/dist/execution/retry-policy.d.ts +31 -1
  142. package/dist/extension/index.js +7 -311
  143. package/dist/goal/index.js +33 -3435
  144. package/dist/hooks/index.js +14 -1138
  145. package/dist/hq/index.js +112 -5139
  146. package/dist/hq/protocol.js +21 -668
  147. package/dist/index.js +1410 -92656
  148. package/dist/infrastructure/index.js +64 -4811
  149. package/dist/kernel/events/provider-events.d.ts +15 -0
  150. package/dist/kernel/index.js +14 -992
  151. package/dist/models/index.js +40 -1117
  152. package/dist/notifications/index.js +3 -108
  153. package/dist/observability/index.js +17 -279
  154. package/dist/plugin/index.js +584 -14835
  155. package/dist/prompts/index.js +14 -948
  156. package/dist/registry/index.js +9 -822
  157. package/dist/registry-R23UBE7H.js +14 -0
  158. package/dist/replay/index.js +6 -84
  159. package/dist/review-finding-store-DKAV3AS6.js +21 -0
  160. package/dist/review-report-store-OWY3EC3N.js +19 -0
  161. package/dist/security/index.js +114 -2202
  162. package/dist/session-catalog/index.js +36 -2103
  163. package/dist/session-catalog/project-server.js +42 -1435
  164. package/dist/skills/index.js +35 -1578
  165. package/dist/storage/index.js +182 -15600
  166. package/dist/tasking/index.js +10 -676
  167. package/dist/tools/index.js +52 -9590
  168. package/dist/types/config/autonomy.d.ts +5 -0
  169. package/dist/types/config/ui.d.ts +1 -1
  170. package/dist/types/errors.d.ts +18 -0
  171. package/dist/types/index.js +121 -1342
  172. package/dist/types/provider-runner.d.ts +8 -0
  173. package/dist/types/session-markers.js +8 -144
  174. package/dist/utils/error.js +3 -4
  175. package/dist/utils/expect-defined.js +3 -9
  176. package/dist/utils/heap-watchdog.js +6 -706
  177. package/dist/utils/index.js +269 -5685
  178. package/dist/utils/sage-output-block.js +5 -42
  179. package/dist/wiring/proxy-rewrite.d.ts +18 -0
  180. package/dist/wiring/proxy-rewrite.js +18 -121
  181. package/dist/worktree/index.js +6 -887
  182. package/package.json +4 -4
@@ -0,0 +1,1199 @@
1
+ import {
2
+ isPidAlive
3
+ } from "./chunk-TRT2HRWZ.js";
4
+ import {
5
+ withSqliteExperimentalWarningSuppressed
6
+ } from "./chunk-LX5QWLBL.js";
7
+
8
+ // src/chronicle/sqlite-compaction.ts
9
+ import { existsSync, readFileSync, statSync } from "node:fs";
10
+ import { loadRuntimeDatabaseSync } from "@wrongstack/persistence";
11
+ var ChronicleCompactionBusyError = class extends Error {
12
+ constructor(pid) {
13
+ super(`Chronicle project server is still running (pid ${pid}); stop it before compacting`);
14
+ this.name = "ChronicleCompactionBusyError";
15
+ }
16
+ };
17
+ function chronicleSqliteAllocatedBytes(dbPath) {
18
+ return [dbPath, `${dbPath}-wal`, `${dbPath}-shm`].reduce((total, file) => {
19
+ try {
20
+ return total + statSync(file).size;
21
+ } catch (error) {
22
+ if (error.code === "ENOENT") return total;
23
+ throw error;
24
+ }
25
+ }, 0);
26
+ }
27
+ function compactChronicleSqlite(options) {
28
+ assertProjectServerStopped(options.endpointMetadataPath);
29
+ const beforeBytes = chronicleSqliteAllocatedBytes(options.dbPath);
30
+ if (!existsSync(options.dbPath)) {
31
+ return { beforeBytes, afterBytes: beforeBytes, reclaimedBytes: 0 };
32
+ }
33
+ const Database = withSqliteExperimentalWarningSuppressed(loadRuntimeDatabaseSync);
34
+ const db = new Database(options.dbPath);
35
+ try {
36
+ db.exec("PRAGMA busy_timeout = 1");
37
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
38
+ db.exec("VACUUM");
39
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
40
+ } finally {
41
+ db.close();
42
+ }
43
+ const afterBytes = chronicleSqliteAllocatedBytes(options.dbPath);
44
+ return {
45
+ beforeBytes,
46
+ afterBytes,
47
+ reclaimedBytes: Math.max(0, beforeBytes - afterBytes)
48
+ };
49
+ }
50
+ function assertProjectServerStopped(endpointMetadataPath) {
51
+ if (!endpointMetadataPath || !existsSync(endpointMetadataPath)) return;
52
+ try {
53
+ const metadata = JSON.parse(readFileSync(endpointMetadataPath, "utf8"));
54
+ if (typeof metadata.pid === "number" && isPidAlive(metadata.pid)) {
55
+ throw new ChronicleCompactionBusyError(metadata.pid);
56
+ }
57
+ } catch (error) {
58
+ if (error instanceof ChronicleCompactionBusyError) throw error;
59
+ }
60
+ }
61
+
62
+ // src/chronicle/provider-adapter.ts
63
+ function wireProviderAttemptsToChronicle(options) {
64
+ const seenToolManifests = /* @__PURE__ */ new Set();
65
+ const unsubs = [
66
+ options.events.on("provider.attempt.started", (event) => persist(options, event, {
67
+ eventType: "provider.attempt.started",
68
+ outcome: "started",
69
+ occurredAt: event.startedAt
70
+ }, seenToolManifests)),
71
+ options.events.on("provider.attempt.completed", (event) => persist(options, event, {
72
+ eventType: "provider.attempt.completed",
73
+ outcome: "success",
74
+ occurredAt: event.endedAt,
75
+ durationNs: millisecondsToNanoseconds(event.durationMs)
76
+ })),
77
+ options.events.on("provider.attempt.failed", (event) => persist(options, event, {
78
+ eventType: "provider.attempt.failed",
79
+ outcome: "failure",
80
+ occurredAt: event.endedAt,
81
+ durationNs: millisecondsToNanoseconds(event.durationMs)
82
+ }))
83
+ ];
84
+ return () => unsubs.forEach((unsubscribe) => {
85
+ unsubscribe();
86
+ });
87
+ }
88
+ function persist(options, event, base, seenToolManifests) {
89
+ const context = typeof options.context === "function" ? options.context() : options.context;
90
+ const input = {
91
+ ...base,
92
+ scope: {
93
+ ...context.scope,
94
+ sessionId: event.sessionId,
95
+ ...event.agentId ? { agentId: event.agentId } : {},
96
+ ...event.taskId ? { taskId: event.taskId } : {},
97
+ ...event.boardId ? { kanbanBoardId: event.boardId } : {}
98
+ },
99
+ correlation: {
100
+ ...context.correlation,
101
+ ...event.traceId ? { traceId: event.traceId } : {},
102
+ logicalRequestId: event.logicalRequestId,
103
+ ...event.promptManifestId ? { promptManifestId: event.promptManifestId } : {},
104
+ attemptId: event.attemptId
105
+ },
106
+ runtime: { providerId: event.providerId, modelId: event.model },
107
+ attributes: providerAttributes(event, seenToolManifests)
108
+ };
109
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
110
+ }
111
+ function providerAttributes(event, seenToolManifests) {
112
+ const {
113
+ sessionId: _sessionId,
114
+ traceId: _traceId,
115
+ agentId: _agentId,
116
+ providerId: _providerId,
117
+ model: _model,
118
+ logicalRequestId: _logicalRequestId,
119
+ promptManifestId: _promptManifestId,
120
+ attemptId: _attemptId,
121
+ ...rest
122
+ } = event;
123
+ const attributes = rest;
124
+ if (seenToolManifests && isManifest(attributes["promptManifest"])) {
125
+ attributes["promptManifest"] = compactManifest(attributes["promptManifest"], seenToolManifests);
126
+ }
127
+ return attributes;
128
+ }
129
+ var MANIFEST_MESSAGE_TAIL = 8;
130
+ var SEEN_TOOL_MANIFEST_CAP = 256;
131
+ function isManifest(value) {
132
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
133
+ }
134
+ function compactManifest(manifest, seenToolManifests) {
135
+ const compacted = { ...manifest };
136
+ const tools = compacted["tools"];
137
+ if (isManifest(tools) && typeof tools["manifestHash"] === "string") {
138
+ const manifestHash = tools["manifestHash"];
139
+ if (seenToolManifests.has(manifestHash)) {
140
+ const { names: _names, ...toolsWithoutNames } = tools;
141
+ compacted["tools"] = { ...toolsWithoutNames, namesRef: manifestHash };
142
+ } else {
143
+ if (seenToolManifests.size >= SEEN_TOOL_MANIFEST_CAP) seenToolManifests.clear();
144
+ seenToolManifests.add(manifestHash);
145
+ }
146
+ }
147
+ const messages = compacted["messages"];
148
+ if (Array.isArray(messages) && messages.length > MANIFEST_MESSAGE_TAIL) {
149
+ compacted["messages"] = messages.slice(-MANIFEST_MESSAGE_TAIL);
150
+ compacted["messagesTruncated"] = messages.length - MANIFEST_MESSAGE_TAIL;
151
+ }
152
+ return compacted;
153
+ }
154
+ function millisecondsToNanoseconds(durationMs) {
155
+ return Math.round(durationMs * 1e6).toString();
156
+ }
157
+
158
+ // src/chronicle/tool-adapter.ts
159
+ import { createHash } from "node:crypto";
160
+ var PREVIEW_MAX_BYTES = 2048;
161
+ function wireToolsToChronicle(options) {
162
+ const unsubs = [
163
+ options.events.on("tool.started", (event) => {
164
+ const input = scrubValue(options.scrubber, event.input);
165
+ persist2(options, event, {
166
+ eventType: "tool.started",
167
+ outcome: "started",
168
+ attributes: {
169
+ toolName: event.name,
170
+ input: capPreview(input),
171
+ inputHash: hashText(input),
172
+ inputBytes: Buffer.byteLength(input)
173
+ }
174
+ });
175
+ }),
176
+ options.events.on("permission.evaluated", (event) => {
177
+ persist2(options, event, {
178
+ eventType: "permission.evaluated",
179
+ outcome: event.effectiveDecision === "deny" ? "denied" : "success",
180
+ attributes: {
181
+ toolName: event.name,
182
+ inputHash: event.inputHash,
183
+ policyDecision: event.policyDecision,
184
+ effectiveDecision: event.effectiveDecision,
185
+ decisionSource: event.decisionSource,
186
+ reason: event.reason ? options.scrubber.scrub(event.reason) : void 0,
187
+ riskTier: event.riskTier,
188
+ yoloEnabled: event.yoloEnabled,
189
+ boundaryDecision: event.boundaryDecision,
190
+ boundaryReason: event.boundaryReason ? options.scrubber.scrub(event.boundaryReason) : void 0,
191
+ capabilityDowngraded: event.capabilityDowngraded
192
+ }
193
+ });
194
+ }),
195
+ options.events.on("tool.executed", (event) => {
196
+ const output = options.scrubber.scrub(event.output ?? "");
197
+ persist2(options, event, {
198
+ eventType: "tool.executed",
199
+ outcome: event.ok ? "success" : "failure",
200
+ durationNs: millisecondsToNanoseconds2(event.durationMs),
201
+ attributes: {
202
+ toolName: event.name,
203
+ ok: event.ok,
204
+ outputPreview: capPreview(output),
205
+ outputHash: hashText(output),
206
+ outputBytes: event.outputBytes,
207
+ outputTokens: event.outputTokens,
208
+ outputLines: event.outputLines,
209
+ metadata: event.metadata
210
+ }
211
+ });
212
+ }),
213
+ options.events.on("tool.failed", (event) => persist2(options, event, {
214
+ eventType: "tool.failed",
215
+ outcome: "failure",
216
+ durationNs: millisecondsToNanoseconds2(event.durationMs),
217
+ attributes: {
218
+ toolName: event.name,
219
+ category: event.category,
220
+ retryable: event.retryable,
221
+ detail: event.detail,
222
+ errorCode: event.errorCode,
223
+ errorSubsystem: event.errorSubsystem,
224
+ errorSeverity: event.errorSeverity
225
+ }
226
+ })),
227
+ options.events.on("tool.progress", (event) => {
228
+ if (event.event.type !== "file_changed") return;
229
+ const resource = progressResource(event);
230
+ persist2(options, event, {
231
+ eventType: "file.mutation.observed",
232
+ outcome: "started",
233
+ ...resource ? { resource } : {},
234
+ attributes: {
235
+ toolName: event.name,
236
+ progressType: event.event.type,
237
+ text: capPreview(options.scrubber.scrub(event.event.text ?? "")),
238
+ data: capPreview(scrubValue(options.scrubber, event.event.data)),
239
+ operation: event.event.operation
240
+ }
241
+ });
242
+ })
243
+ ];
244
+ return () => unsubs.forEach((unsubscribe) => {
245
+ unsubscribe();
246
+ });
247
+ }
248
+ function persist2(options, event, fields) {
249
+ const context = typeof options.context === "function" ? options.context() : options.context;
250
+ const input = {
251
+ ...fields,
252
+ scope: {
253
+ ...context.scope,
254
+ ...event.sessionId ? { sessionId: event.sessionId } : {},
255
+ ...event.agentId ? { agentId: event.agentId } : {},
256
+ ...event.taskId ? { taskId: event.taskId } : {},
257
+ ...event.boardId ? { kanbanBoardId: event.boardId } : {}
258
+ },
259
+ correlation: {
260
+ ...context.correlation,
261
+ ...event.traceId ? { traceId: event.traceId } : {},
262
+ ...event.logicalRequestId ? { logicalRequestId: event.logicalRequestId } : {},
263
+ ...event.promptManifestId ? { promptManifestId: event.promptManifestId } : {},
264
+ ...event.id ? { toolCallId: event.id } : {}
265
+ },
266
+ ...event.provider || event.model ? {
267
+ runtime: {
268
+ ...event.provider ? { providerId: event.provider } : {},
269
+ ...event.model ? { modelId: event.model } : {}
270
+ }
271
+ } : {}
272
+ };
273
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
274
+ }
275
+ function progressResource(event) {
276
+ if (event.event.type !== "file_changed" || !event.event.path) return void 0;
277
+ return {
278
+ kind: "file",
279
+ id: resourceId("file", event.event.path),
280
+ path: event.event.path,
281
+ ...event.event.line !== void 0 ? { lineStart: event.event.line } : {},
282
+ ...event.event.endLine !== void 0 ? { lineEnd: event.event.endLine } : {}
283
+ };
284
+ }
285
+ function scrubValue(scrubber, value) {
286
+ if (value === void 0) return "";
287
+ try {
288
+ return scrubber.scrub(JSON.stringify(value));
289
+ } catch {
290
+ return scrubber.scrub(String(value));
291
+ }
292
+ }
293
+ function resourceId(kind, value) {
294
+ return `${kind}_${hashText(value).slice(0, 24)}`;
295
+ }
296
+ function hashText(value) {
297
+ return createHash("sha256").update(value).digest("hex");
298
+ }
299
+ function capPreview(value) {
300
+ if (value.length <= PREVIEW_MAX_BYTES) {
301
+ if (Buffer.byteLength(value, "utf8") <= PREVIEW_MAX_BYTES) return value;
302
+ }
303
+ const buf = Buffer.from(value, "utf8");
304
+ if (buf.length <= PREVIEW_MAX_BYTES) return value;
305
+ let end = PREVIEW_MAX_BYTES;
306
+ while (end > 0 && (buf[end] & 192) === 128) end--;
307
+ return { preview: buf.subarray(0, end).toString("utf8"), truncated: true, totalBytes: buf.length };
308
+ }
309
+ function millisecondsToNanoseconds2(durationMs) {
310
+ return Math.round(durationMs * 1e6).toString();
311
+ }
312
+
313
+ // src/chronicle/process-adapter.ts
314
+ import { createHash as createHash2 } from "node:crypto";
315
+ function wireProcessesToChronicle(options) {
316
+ const unsubs = [
317
+ options.events.on("process.started", (event) => persist3(options, event, {
318
+ eventType: "process.started",
319
+ outcome: "started",
320
+ occurredAt: event.startedAt,
321
+ attributes: {
322
+ command: options.scrubber.scrub(event.command),
323
+ args: event.args.map((arg) => options.scrubber.scrub(arg)),
324
+ cwd: event.cwd,
325
+ parentPid: event.parentPid,
326
+ background: event.background
327
+ }
328
+ })),
329
+ options.events.on("process.completed", (event) => persist3(options, event, {
330
+ eventType: "process.completed",
331
+ outcome: event.exitCode === 0 ? "success" : event.timedOut ? "cancelled" : "failure",
332
+ occurredAt: event.endedAt,
333
+ durationNs: Math.round(event.durationMs * 1e6).toString(),
334
+ attributes: {
335
+ exitCode: event.exitCode,
336
+ signal: event.signal,
337
+ stdoutBytes: event.stdoutBytes,
338
+ stderrBytes: event.stderrBytes,
339
+ timedOut: event.timedOut
340
+ }
341
+ }))
342
+ ];
343
+ return () => unsubs.forEach((unsubscribe) => {
344
+ unsubscribe();
345
+ });
346
+ }
347
+ function persist3(options, event, fields) {
348
+ const context = typeof options.context === "function" ? options.context() : options.context;
349
+ const processKey = `${event.sessionId}\0${event.pid ?? "unknown"}\0${event.toolCallId}`;
350
+ const input = {
351
+ ...fields,
352
+ scope: {
353
+ ...context.scope,
354
+ sessionId: event.sessionId,
355
+ ...event.agentId ? { agentId: event.agentId } : {}
356
+ },
357
+ correlation: {
358
+ ...context.correlation,
359
+ ...event.traceId ? { traceId: event.traceId } : {},
360
+ toolCallId: event.toolCallId
361
+ },
362
+ runtime: {
363
+ ...event.pid !== void 0 ? { processId: event.pid } : {},
364
+ ..."parentPid" in event ? { parentProcessId: event.parentPid } : {}
365
+ },
366
+ resource: {
367
+ kind: "process",
368
+ id: `process_${createHash2("sha256").update(processKey).digest("hex").slice(0, 24)}`
369
+ }
370
+ };
371
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
372
+ }
373
+
374
+ // src/chronicle/health-monitor.ts
375
+ import { monitorEventLoopDelay, performance } from "node:perf_hooks";
376
+ function startChronicleHealthMonitor(options) {
377
+ const intervalMs = Math.max(5e3, options.intervalMs ?? 3e4);
378
+ const delay = monitorEventLoopDelay({ resolution: 20 });
379
+ delay.enable();
380
+ let previousCpu = process.cpuUsage();
381
+ let previousElu = performance.eventLoopUtilization();
382
+ const sample = () => {
383
+ const context = typeof options.context === "function" ? options.context() : options.context;
384
+ const memory = process.memoryUsage();
385
+ const cpu = process.cpuUsage(previousCpu);
386
+ previousCpu = process.cpuUsage();
387
+ const elu = performance.eventLoopUtilization(previousElu);
388
+ previousElu = performance.eventLoopUtilization();
389
+ const journalBeforeSample = options.journal.stats();
390
+ try {
391
+ options.events.emit("runtime.health.sampled", {
392
+ sessionId: context.scope.sessionId,
393
+ uptimeSeconds: process.uptime(),
394
+ eventLoop: {
395
+ utilization: elu.utilization,
396
+ activeMs: elu.active,
397
+ idleMs: elu.idle,
398
+ delayMeanMs: Number(delay.mean) / 1e6,
399
+ delayP95Ms: Number(delay.percentile(95)) / 1e6,
400
+ delayMaxMs: Number(delay.max) / 1e6
401
+ },
402
+ cpu: { userMicros: cpu.user, systemMicros: cpu.system },
403
+ memory: {
404
+ rssBytes: memory.rss,
405
+ heapTotalBytes: memory.heapTotal,
406
+ heapUsedBytes: memory.heapUsed,
407
+ externalBytes: memory.external,
408
+ arrayBuffersBytes: memory.arrayBuffers
409
+ },
410
+ chronicle: journalBeforeSample
411
+ });
412
+ } catch (error) {
413
+ options.onPersistError?.(error);
414
+ }
415
+ delay.reset();
416
+ };
417
+ const timer = setInterval(sample, intervalMs);
418
+ timer.unref?.();
419
+ return () => {
420
+ clearInterval(timer);
421
+ delay.disable();
422
+ };
423
+ }
424
+
425
+ // src/chronicle/decision-adapter.ts
426
+ import { createHash as createHash3 } from "node:crypto";
427
+ function wireDecisionsToChronicle(options) {
428
+ const write = (eventType, at, sessionId, requestId, attributes, outcome) => {
429
+ const context = typeof options.context === "function" ? options.context() : options.context;
430
+ const input = {
431
+ eventType,
432
+ occurredAt: new Date(at).toISOString(),
433
+ outcome,
434
+ scope: { ...context.scope, ...sessionId ? { sessionId } : {} },
435
+ correlation: context.correlation,
436
+ resource: { kind: "other", id: `decision:${requestId}` },
437
+ attributes: { decisionId: requestId, ...attributes }
438
+ };
439
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
440
+ };
441
+ const requestAttrs = (request) => ({
442
+ source: request.source,
443
+ risk: request.risk,
444
+ fallback: request.fallback,
445
+ questionHash: hash(request.question),
446
+ contextHash: hash(request.context),
447
+ optionCount: request.options?.length ?? 0,
448
+ options: request.options?.map((option) => ({
449
+ id: option.id,
450
+ risk: option.risk,
451
+ recommended: option.recommended ?? false,
452
+ labelHash: hash(option.label),
453
+ consequenceHash: hash(option.consequence)
454
+ }))
455
+ });
456
+ const decisionAttrs = (decision) => ({
457
+ type: decision.type,
458
+ ..."optionId" in decision && decision.optionId ? { optionId: decision.optionId } : {},
459
+ contentHash: hash("text" in decision ? decision.text : "prompt" in decision ? decision.prompt : decision.reason),
460
+ rationaleHash: hash("rationale" in decision ? decision.rationale : void 0)
461
+ });
462
+ const offs = [
463
+ options.events.on("brain.decision_requested", (e) => write("decision.requested", e.at, e.sessionId, e.request.id, requestAttrs(e.request), "started")),
464
+ options.events.on("brain.decision_answered", (e) => write("decision.resolved", e.at, e.sessionId, e.request.id, { ...requestAttrs(e.request), ...decisionAttrs(e.decision), resolver: "brain" }, "success")),
465
+ options.events.on("brain.decision_ask_human", (e) => write("decision.escalated", e.at, e.sessionId, e.request.id, { ...requestAttrs(e.request), ...decisionAttrs(e.decision) }, "started")),
466
+ options.events.on("brain.decision_denied", (e) => write("decision.denied", e.at, e.sessionId, e.request.id, { ...requestAttrs(e.request), ...decisionAttrs(e.decision) }, "denied")),
467
+ options.events.on("brain.human_answered", (e) => write("decision.human_answered", e.at, e.sessionId, e.id, { resolver: "human", optionId: e.optionId, denied: e.deny ?? false, answerHash: hash(e.text) }, e.deny ? "denied" : "success")),
468
+ options.events.on("brain.outcome", (e) => write("decision.outcome_observed", e.at, e.sessionId, e.requestId, { observedOutcome: e.outcome, detailHash: hash(e.detail) }, e.outcome))
469
+ ];
470
+ return () => offs.forEach((off) => {
471
+ off();
472
+ });
473
+ }
474
+ function hash(value) {
475
+ return value ? createHash3("sha256").update(value).digest("hex") : void 0;
476
+ }
477
+
478
+ // src/chronicle/domain-adapter.ts
479
+ import { createHash as createHash4 } from "node:crypto";
480
+ var SPECIALIZED_RE = /^(?:provider\.attempt\.|tool\.|process\.|brain\.decision_|brain\.human_answered$|brain\.outcome$|file\.activity$|provider\.(?:text_delta|thinking_delta)$|ctx\.pct$|subagent\.ctx_pct$|countdown\.tick$|coordinator\.stats$|session\.agents_updated$|network\.request\.(?:started|completed)$|runtime\.health\.sampled$|provider\.tool_use_(?:start|stop)$)/;
481
+ var CODING_SIGNAL_RE = /^(?:(?:agent|subagent|delegate|fleet|session|iteration|context|compaction|checkpoint|in_flight|memory|storage|trust|sdd|worktree|kanban|brain|token|budget|concurrency|provider|mcp|network)\.|file\.event$|error$)/;
482
+ var SENSITIVE_KEY = /(content|text|prompt|question|rationale|reason|detail|summary|description|context|input|output|error|message|secret|token|password|key)$/i;
483
+ var PRESERVE_STRING_KEY = /(^|_)(id|status|state|kind|type|source|model|provider|phase|risk|fallback|path|name|role|sha|branch|mode)$/i;
484
+ var TRUNCATED_ARRAYS = /* @__PURE__ */ new Set([
485
+ "recentMail",
486
+ "recentTools",
487
+ "recentCommands",
488
+ "activated",
489
+ "injected",
490
+ // SAGE per-memory rejection evidence — same cap (5) as activated/injected
491
+ // so the journal stays bounded under burst conditions. Each entry is
492
+ // ~120 bytes serialized; cap × entry-count = ~600B per injector_run.
493
+ "rejectedDetail"
494
+ ]);
495
+ var TRUNCATED_ARRAY_MAX = 5;
496
+ var DEFAULT_ARRAY_MAX = 20;
497
+ function wireDomainEventsToChronicle(options) {
498
+ return options.events.onAny((eventName, payload) => {
499
+ if (SPECIALIZED_RE.test(eventName)) return;
500
+ if (!CODING_SIGNAL_RE.test(eventName)) return;
501
+ const record = eventName === "memory.injector_run" ? dedupInjectorTrace(objectPayload(payload)) : objectPayload(payload);
502
+ if (eventName === "file.event" && record.operation === "read") return;
503
+ const context = typeof options.context === "function" ? options.context() : options.context;
504
+ const sessionId = stringField(record, "sessionId");
505
+ const agentId = stringField(record, "agentId") ?? stringField(record, "subagentId");
506
+ const taskId = stringField(record, "taskId");
507
+ const kanbanBoardId = stringField(record, "kanbanBoardId") ?? stringField(record, "boardId");
508
+ const input = {
509
+ eventType: eventName,
510
+ occurredAt: eventTime(record),
511
+ scope: {
512
+ ...context.scope,
513
+ ...sessionId ? { sessionId } : {},
514
+ ...agentId ? { agentId } : {},
515
+ ...taskId ? { taskId } : {},
516
+ ...kanbanBoardId ? { kanbanBoardId } : {}
517
+ },
518
+ correlation: {
519
+ ...context.correlation,
520
+ ...stringField(record, "traceId") ? { traceId: stringField(record, "traceId") } : {},
521
+ ...stringField(record, "toolCallId") ? { toolCallId: stringField(record, "toolCallId") } : {},
522
+ ...stringField(record, "attemptId") ? { attemptId: stringField(record, "attemptId") } : {},
523
+ ...stringField(record, "logicalRequestId") ? { logicalRequestId: stringField(record, "logicalRequestId") } : {},
524
+ ...stringField(record, "promptManifestId") ? { promptManifestId: stringField(record, "promptManifestId") } : {}
525
+ },
526
+ outcome: inferOutcome(eventName, record),
527
+ runtime: {
528
+ ...stringField(record, "providerId") ?? stringField(record, "provider") ? { providerId: stringField(record, "providerId") ?? stringField(record, "provider") } : {},
529
+ ...stringField(record, "modelId") ?? stringField(record, "model") ? { modelId: stringField(record, "modelId") ?? stringField(record, "model") } : {}
530
+ },
531
+ resource: inferResource(record, eventName),
532
+ attributes: sanitize(record),
533
+ tags: { collector: "eventbus-domain", family: eventName.split(".")[0] ?? "unknown" }
534
+ };
535
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
536
+ });
537
+ }
538
+ function objectPayload(value) {
539
+ return value && typeof value === "object" ? value : { value };
540
+ }
541
+ function dedupInjectorTrace(record) {
542
+ const activated = Array.isArray(record.activated) ? record.activated : [];
543
+ const injected = Array.isArray(record.injected) ? record.injected : [];
544
+ const activatedIds = new Set(
545
+ activated.map((item) => item && typeof item === "object" ? item.id : void 0).filter((id) => typeof id === "string")
546
+ );
547
+ return {
548
+ ...record,
549
+ injected: injected.map((item) => {
550
+ const id = item && typeof item === "object" ? item.id : void 0;
551
+ return typeof id === "string" && activatedIds.has(id) ? { id, ref: "activated" } : item;
552
+ })
553
+ };
554
+ }
555
+ function stringField(value, key) {
556
+ return typeof value[key] === "string" ? value[key] : void 0;
557
+ }
558
+ function eventTime(value) {
559
+ const raw = value.at ?? value.ts ?? value.timestamp;
560
+ if (typeof raw === "number" && Number.isFinite(raw)) return new Date(raw).toISOString();
561
+ if (typeof raw === "string" && Number.isFinite(Date.parse(raw)))
562
+ return new Date(raw).toISOString();
563
+ return void 0;
564
+ }
565
+ function inferOutcome(name, payload) {
566
+ if (payload.ok === false || /(?:failed|error|damaged|conflict|deadlock|denied|rejected|blocked)$/.test(name))
567
+ return "failure";
568
+ if (/(?:started|starting|retrying|threshold_reached)$/.test(name)) return "started";
569
+ if (/(?:cancelled|aborted)$/.test(name)) return "cancelled";
570
+ if (/(?:completed|finished|committed|merged|written|persisted|accepted|recovered|verified|connected|success)$/.test(
571
+ name
572
+ ) || payload.ok === true)
573
+ return "success";
574
+ return "unknown";
575
+ }
576
+ function inferResource(payload, eventName) {
577
+ if (eventName === "file.event" && typeof payload.filePath === "string" && payload.filePath.length > 0) {
578
+ return { kind: "file", id: `path:${payload.filePath}`, path: payload.filePath };
579
+ }
580
+ const candidates = [
581
+ ["memory", "memoryId", payload.memoryId],
582
+ ["task", "taskId", payload.taskId],
583
+ ["kanban", "boardId", payload.boardId ?? payload.runId],
584
+ ["artifact", "worktreeId", payload.worktreeId ?? payload.handleId],
585
+ ["file", "path", payload.filePath ?? payload.path],
586
+ ["other", "agentId", payload.agentId ?? payload.subagentId],
587
+ ["network", "serverAddress", payload.serverAddress],
588
+ ["other", "sessionId", payload.sessionId]
589
+ ];
590
+ const found = candidates.find(([, , value2]) => typeof value2 === "string" && value2.length > 0);
591
+ if (!found) return void 0;
592
+ const [kind, label, value] = found;
593
+ return { kind, id: `${label}:${value}`, ...kind === "file" ? { path: value } : {} };
594
+ }
595
+ function sanitize(value, key = "", depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
596
+ if (value === null || typeof value === "boolean" || typeof value === "number") return value;
597
+ if (typeof value === "bigint") return value.toString();
598
+ if (typeof value === "function") return { type: "function" };
599
+ if (typeof value === "string") {
600
+ if (PRESERVE_STRING_KEY.test(key) && !SENSITIVE_KEY.test(key)) return value.slice(0, 512);
601
+ if (SENSITIVE_KEY.test(key))
602
+ return { hash: digest(value), length: value.length, redacted: true };
603
+ return value.length <= 256 ? value : { hash: digest(value), length: value.length, truncated: true };
604
+ }
605
+ if (typeof value !== "object") return String(value);
606
+ if (seen.has(value)) return { circular: true };
607
+ if (depth >= 5) return { hash: digest(safeString(value)), depthLimited: true };
608
+ seen.add(value);
609
+ if (Array.isArray(value)) {
610
+ const cap = TRUNCATED_ARRAYS.has(key) ? TRUNCATED_ARRAY_MAX : DEFAULT_ARRAY_MAX;
611
+ const items = value.slice(0, cap).map((item) => sanitize(item, key, depth + 1, seen));
612
+ return value.length > cap ? { items, total: value.length, truncated: true } : items;
613
+ }
614
+ const output = {};
615
+ const entries = Object.entries(value);
616
+ for (const [childKey, child] of entries.slice(0, 100)) {
617
+ if (childKey === "ctx" || childKey === "provider" || childKey === "resolve" || childKey === "extend" || childKey === "deny")
618
+ continue;
619
+ output[childKey] = sanitize(child, childKey, depth + 1, seen);
620
+ }
621
+ if (entries.length > 100) output._truncatedKeys = entries.length - 100;
622
+ return output;
623
+ }
624
+ function safeString(value) {
625
+ try {
626
+ return JSON.stringify(value) ?? String(value);
627
+ } catch {
628
+ return String(value);
629
+ }
630
+ }
631
+ function digest(value) {
632
+ return createHash4("sha256").update(value).digest("hex");
633
+ }
634
+
635
+ // src/chronicle/stream-adapter.ts
636
+ import { createHash as createHash5 } from "node:crypto";
637
+ var MAX_STATE_AGE_MS = 3e5;
638
+ var STALE_STATE_SWEEP_INTERVAL_MS = Math.round(MAX_STATE_AGE_MS / 6);
639
+ function wireProviderStreamsToChronicle(options) {
640
+ const states = /* @__PURE__ */ new Map();
641
+ const key = (sessionId, agentId) => `${sessionId ?? "__default__"}\0${agentId ?? "__leader__"}`;
642
+ const update = (sessionId, agentId, text2, thinking) => {
643
+ const now = Date.now();
644
+ sweepStaleStates(states, flush, now);
645
+ const k = key(sessionId, agentId);
646
+ const state = states.get(k);
647
+ if (!state) return;
648
+ const bytes = Buffer.byteLength(text2);
649
+ state.firstChunkAtMs ??= now;
650
+ state.lastChunkAtMs = now;
651
+ if (thinking) {
652
+ state.thinkingChunks++;
653
+ state.thinkingBytes += bytes;
654
+ state.thinkingHash.update(text2);
655
+ } else {
656
+ state.textChunks++;
657
+ state.textBytes += bytes;
658
+ state.textHash.update(text2);
659
+ }
660
+ };
661
+ const flush = (sessionId, agentId, outcome) => {
662
+ const state = states.get(key(sessionId, agentId));
663
+ if (!state) return;
664
+ states.delete(key(sessionId, agentId));
665
+ const context = typeof options.context === "function" ? options.context() : options.context;
666
+ const endedAtMs = Date.now();
667
+ const input = {
668
+ eventType: "provider.stream.summarized",
669
+ outcome,
670
+ scope: { ...context.scope, ...state.sessionId ? { sessionId: state.sessionId } : {}, ...state.agentId ? { agentId: state.agentId } : {} },
671
+ correlation: { ...context.correlation, attemptId: state.attemptId, logicalRequestId: state.logicalRequestId },
672
+ runtime: { providerId: state.providerId, modelId: state.model },
673
+ durationNs: String(Math.max(0, endedAtMs - state.startedAtMs) * 1e6),
674
+ attributes: {
675
+ textChunks: state.textChunks,
676
+ textBytes: state.textBytes,
677
+ thinkingChunks: state.thinkingChunks,
678
+ thinkingBytes: state.thinkingBytes,
679
+ textHash: state.textHash.digest("hex"),
680
+ thinkingHash: state.thinkingHash.digest("hex"),
681
+ firstChunkLatencyMs: state.firstChunkAtMs === void 0 ? void 0 : state.firstChunkAtMs - state.startedAtMs,
682
+ streamActiveMs: state.firstChunkAtMs === void 0 || state.lastChunkAtMs === void 0 ? 0 : state.lastChunkAtMs - state.firstChunkAtMs
683
+ }
684
+ };
685
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
686
+ };
687
+ const offs = [
688
+ options.events.on("provider.attempt.started", (event) => states.set(key(event.sessionId, event.agentId), {
689
+ sessionId: event.sessionId,
690
+ ...event.agentId ? { agentId: event.agentId } : {},
691
+ attemptId: event.attemptId,
692
+ logicalRequestId: event.logicalRequestId,
693
+ providerId: event.providerId,
694
+ model: event.model,
695
+ startedAtMs: Date.parse(event.startedAt),
696
+ textChunks: 0,
697
+ textBytes: 0,
698
+ thinkingChunks: 0,
699
+ thinkingBytes: 0,
700
+ textHash: createHash5("sha256"),
701
+ thinkingHash: createHash5("sha256")
702
+ })),
703
+ options.events.on("provider.text_delta", (event) => update(event.sessionId, event.ctx.agentId, event.text, false)),
704
+ options.events.on("provider.thinking_delta", (event) => update(event.sessionId, event.ctx.agentId, event.text, true)),
705
+ options.events.on("provider.attempt.completed", (event) => flush(event.sessionId, event.agentId, "success")),
706
+ options.events.on("provider.attempt.failed", (event) => flush(event.sessionId, event.agentId, "failure"))
707
+ ];
708
+ const reaperTimer = setInterval(
709
+ () => sweepStaleStates(states, flush, Date.now()),
710
+ STALE_STATE_SWEEP_INTERVAL_MS
711
+ );
712
+ if (typeof reaperTimer.unref === "function") reaperTimer.unref();
713
+ return () => {
714
+ clearInterval(reaperTimer);
715
+ for (const state of [...states.values()]) flush(state.sessionId, state.agentId, "failure");
716
+ offs.forEach((off) => {
717
+ off();
718
+ });
719
+ };
720
+ }
721
+ function sweepStaleStates(states, flushFn, now) {
722
+ if (states.size === 0) return;
723
+ for (const s of states.values()) {
724
+ if (now - s.startedAtMs > MAX_STATE_AGE_MS) {
725
+ const sid = s.sessionId;
726
+ const aid = s.agentId;
727
+ flushFn(sid, aid, "failure");
728
+ }
729
+ }
730
+ }
731
+
732
+ // src/chronicle/review-adapter.ts
733
+ import { createHash as createHash6, randomUUID } from "node:crypto";
734
+ function wireReviewFindingsToChronicle(options) {
735
+ const unsubscribe = options.events.onPattern(
736
+ "chimera.review_complete",
737
+ (_event, raw) => {
738
+ const payload = raw;
739
+ if (!payload?.reviewText) return;
740
+ if (payload.status !== "success") return;
741
+ const context = typeof options.context === "function" ? options.context() : options.context;
742
+ const sessionId = payload.sessionId ?? payload.bundle?.cwd;
743
+ const agentId = payload.bundle?.fileProvenance?.find((e) => e.agentId)?.agentId;
744
+ const taskId = payload.bundle?.kanbanCard?.id;
745
+ const provider = payload.bundle?.config?.provider;
746
+ const model = payload.bundle?.config?.model;
747
+ const cascadeDepth = payload.bundle?.cascadeDepth ?? 0;
748
+ const now = (/* @__PURE__ */ new Date()).toISOString();
749
+ const findings = parseFindings(payload.reviewText);
750
+ for (const finding of findings) {
751
+ const eventId = randomUUID();
752
+ const input = {
753
+ eventType: `finding.${finding.severity}`,
754
+ occurredAt: now,
755
+ scope: {
756
+ ...context.scope,
757
+ ...sessionId ? { sessionId } : {},
758
+ ...agentId ? { agentId } : {},
759
+ ...taskId ? { taskId } : {}
760
+ },
761
+ correlation: context.correlation,
762
+ resource: finding.file ? {
763
+ kind: "file",
764
+ id: `finding:${finding.fingerprint}`,
765
+ path: finding.file,
766
+ ...finding.line !== void 0 ? { lineStart: finding.line } : {}
767
+ } : void 0,
768
+ outcome: "success",
769
+ attributes: {
770
+ findingId: eventId,
771
+ fingerprint: finding.fingerprint,
772
+ severity: finding.severity,
773
+ title: finding.title,
774
+ file: finding.file,
775
+ line: finding.line,
776
+ source: "chimera",
777
+ model,
778
+ provider,
779
+ cascadeDepth
780
+ },
781
+ tags: {
782
+ collector: "review-adapter",
783
+ family: "finding",
784
+ severity: finding.severity
785
+ }
786
+ };
787
+ options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
788
+ }
789
+ if (findings.length === 0) return;
790
+ const summaryInput = {
791
+ eventType: "review.completed",
792
+ occurredAt: now,
793
+ scope: {
794
+ ...context.scope,
795
+ ...sessionId ? { sessionId } : {},
796
+ ...agentId ? { agentId } : {},
797
+ ...taskId ? { taskId } : {}
798
+ },
799
+ correlation: context.correlation,
800
+ outcome: "success",
801
+ attributes: {
802
+ totalFindings: findings.length,
803
+ severityCounts: aggregateSeverities(findings),
804
+ files: payload.bundle?.files?.map((f) => f.path),
805
+ model,
806
+ provider,
807
+ cascadeDepth,
808
+ status: payload.status
809
+ },
810
+ tags: {
811
+ collector: "review-adapter",
812
+ family: "review"
813
+ }
814
+ };
815
+ options.journal.append(summaryInput).catch((error) => options.onPersistError?.(error, summaryInput));
816
+ }
817
+ );
818
+ return unsubscribe;
819
+ }
820
+ var SEVERITY_HEADING = /^###\s+(critical|high|medium|low)\b/i;
821
+ var ITEM_CAPTURE = /^\d+\.\s+(.*)$/;
822
+ var TAG_PREFIX = /^\[(\w+)\]\s*/;
823
+ var FILE_REF = /^(.+?)(?::(\d+))?\s+[—–]\s*(.+)$/;
824
+ var PATH_REF = /^([a-zA-Z_./\\][a-zA-Z0-9_./\\-]*\.\w+)(?::(\d+))?/;
825
+ function parseFindings(reviewText) {
826
+ const findings = [];
827
+ let currentSeverity = null;
828
+ for (const rawLine of reviewText.split("\n")) {
829
+ const line = rawLine.trim();
830
+ if (!line) continue;
831
+ const heading = line.match(SEVERITY_HEADING);
832
+ if (heading) {
833
+ currentSeverity = heading[1].toLowerCase();
834
+ continue;
835
+ }
836
+ if (!currentSeverity) continue;
837
+ const item = line.match(ITEM_CAPTURE);
838
+ if (!item) continue;
839
+ let remaining = item[1].trim();
840
+ if (!remaining) continue;
841
+ const tag = remaining.match(TAG_PREFIX);
842
+ if (tag) remaining = remaining.slice(tag[0].length).trim();
843
+ const refMatch = remaining.match(FILE_REF);
844
+ let file;
845
+ let lineNum;
846
+ let desc;
847
+ if (refMatch) {
848
+ file = refMatch[1].trim();
849
+ lineNum = refMatch[2] !== void 0 ? Number.parseInt(refMatch[2], 10) : void 0;
850
+ desc = refMatch[3].trim();
851
+ } else {
852
+ const pathFallback = remaining.match(PATH_REF);
853
+ if (pathFallback) {
854
+ file = pathFallback[1].trim();
855
+ lineNum = pathFallback[2] !== void 0 ? Number.parseInt(pathFallback[2], 10) : void 0;
856
+ const afterFile = remaining.slice(pathFallback[0].length).trim();
857
+ desc = afterFile.replace(/^[—–-]\s*/, "").trim() || remaining;
858
+ } else {
859
+ desc = remaining;
860
+ }
861
+ }
862
+ if (file && !file.includes("/") && !file.includes("\\") && !file.includes(".")) {
863
+ file = void 0;
864
+ desc = remaining;
865
+ }
866
+ const title = desc.split(/\.\s/)[0].trim() || desc;
867
+ const fingerprint = computeFingerprint(file ?? "", lineNum ?? null, title);
868
+ findings.push({
869
+ severity: currentSeverity,
870
+ file,
871
+ line: lineNum,
872
+ title,
873
+ fingerprint
874
+ });
875
+ }
876
+ return findings;
877
+ }
878
+ function computeFingerprint(file, line, title) {
879
+ const normalizedFile = file.replace(/\\/g, "/").trim().toLowerCase();
880
+ const lineStr = line != null && line >= 0 ? String(line) : "0";
881
+ const normalizedTitle = title.trim().toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
882
+ const hash2 = createHash6("sha256");
883
+ hash2.update(`${normalizedFile}:${lineStr}:${normalizedTitle}`);
884
+ return hash2.digest("hex");
885
+ }
886
+ function aggregateSeverities(findings) {
887
+ const counts = {
888
+ critical: 0,
889
+ high: 0,
890
+ medium: 0,
891
+ low: 0
892
+ };
893
+ for (const f of findings) {
894
+ const sev = f.severity;
895
+ if (sev in counts) counts[sev]++;
896
+ }
897
+ return counts;
898
+ }
899
+
900
+ // src/chronicle/rollup-adapter.ts
901
+ import { createHash as createHash7 } from "node:crypto";
902
+ var MAX_ROLLUP_RESOURCES = 100;
903
+ var DEFAULT_GAUGE_WINDOW_MS = 12e4;
904
+ function wireRollupsToChronicle(options) {
905
+ const buckets = /* @__PURE__ */ new Map();
906
+ const windowMs = Math.max(1e3, options.windowMs ?? 1e4);
907
+ const gaugeWindowMs = Math.max(windowMs, options.gaugeWindowMs ?? DEFAULT_GAUGE_WINDOW_MS);
908
+ const bucket = (key, seed) => {
909
+ let value = buckets.get(key);
910
+ if (!value) {
911
+ const now = Date.now();
912
+ value = {
913
+ ...seed,
914
+ startedAt: now,
915
+ updatedAt: now,
916
+ count: 0,
917
+ metrics: {},
918
+ categories: {},
919
+ digest: createHash7("sha256"),
920
+ resources: /* @__PURE__ */ new Map()
921
+ };
922
+ buckets.set(key, value);
923
+ }
924
+ return value;
925
+ };
926
+ const sample = (target, values, category, digest2) => {
927
+ target.count++;
928
+ target.updatedAt = Date.now();
929
+ for (const [name, value] of Object.entries(values)) {
930
+ const metric = target.metrics[name];
931
+ target.metrics[name] = metric ? {
932
+ sum: metric.sum + value,
933
+ min: Math.min(metric.min, value),
934
+ max: Math.max(metric.max, value),
935
+ last: value
936
+ } : { sum: value, min: value, max: value, last: value };
937
+ }
938
+ if (category) target.categories[category] = (target.categories[category] ?? 0) + 1;
939
+ if (digest2) target.digest.update(digest2);
940
+ };
941
+ const flush = (key) => {
942
+ const value = buckets.get(key);
943
+ if (!value || value.count === 0) return;
944
+ buckets.delete(key);
945
+ const context = typeof options.context === "function" ? options.context() : options.context;
946
+ const stats = Object.fromEntries(
947
+ Object.entries(value.metrics).map(([name, metric]) => [
948
+ name,
949
+ { ...metric, avg: metric.sum / value.count }
950
+ ])
951
+ );
952
+ const resources = value.resources.size > 0 ? [...value.resources].slice(0, MAX_ROLLUP_RESOURCES).map(([id, r]) => ({ id, kind: r.kind, ...r.path ? { path: r.path } : {} })) : void 0;
953
+ const input = {
954
+ eventType: "metrics.rollup",
955
+ outcome: "success",
956
+ occurredAt: new Date(value.updatedAt).toISOString(),
957
+ scope: {
958
+ ...context.scope,
959
+ ...value.sessionId ? { sessionId: value.sessionId } : {},
960
+ ...value.agentId ? { agentId: value.agentId } : {}
961
+ },
962
+ correlation: {
963
+ ...context.correlation,
964
+ ...value.toolCallId ? { toolCallId: value.toolCallId } : {}
965
+ },
966
+ durationNs: String(Math.max(0, value.updatedAt - value.startedAt) * 1e6),
967
+ attributes: {
968
+ signal: value.signal,
969
+ windowStart: new Date(value.startedAt).toISOString(),
970
+ windowEnd: new Date(value.updatedAt).toISOString(),
971
+ samples: value.count,
972
+ dimensions: value.dimensions,
973
+ stats,
974
+ categories: value.categories,
975
+ digest: value.digest.digest("hex"),
976
+ rawEventsRetained: false,
977
+ ...resources ? { resources, resourceCount: value.resources.size } : {}
978
+ }
979
+ };
980
+ void options.journal.append(input).catch((error) => options.onPersistError?.(error, input));
981
+ };
982
+ const gauge = (signal, event, dimension) => {
983
+ const sessionId = text(event.sessionId);
984
+ const dimensionValue = dimension ? text(event[dimension]) : void 0;
985
+ const key = `${signal}\0${sessionId ?? ""}\0${dimensionValue ?? ""}`;
986
+ const target = bucket(key, {
987
+ signal,
988
+ aggregateMs: gaugeWindowMs,
989
+ ...sessionId ? { sessionId } : {},
990
+ ...dimensionValue ? { agentId: dimensionValue } : {},
991
+ dimensions: dimensionValue && dimension ? { [dimension]: dimensionValue } : {}
992
+ });
993
+ sample(
994
+ target,
995
+ Object.fromEntries(
996
+ Object.entries(event).filter(([, value]) => typeof value === "number")
997
+ )
998
+ );
999
+ };
1000
+ const offs = [
1001
+ options.events.on("process.output", (event) => {
1002
+ const key = `process.output\0${event.sessionId}\0${event.toolCallId}\0${event.pid ?? ""}\0${event.stream}`;
1003
+ const target = bucket(key, {
1004
+ signal: "process.output",
1005
+ sessionId: event.sessionId,
1006
+ ...event.agentId ? { agentId: event.agentId } : {},
1007
+ toolCallId: event.toolCallId,
1008
+ dimensions: {
1009
+ stream: event.stream,
1010
+ toolName: event.toolName,
1011
+ pid: String(event.pid ?? "")
1012
+ }
1013
+ });
1014
+ sample(target, { bytes: event.bytes }, event.stream, event.chunkHash);
1015
+ }),
1016
+ options.events.on("process.completed", (event) => {
1017
+ for (const key of [...buckets.keys()])
1018
+ if (key.startsWith(`process.output\0${event.sessionId}\0${event.toolCallId}\0`)) flush(key);
1019
+ }),
1020
+ options.events.on("tool.progress", (event) => {
1021
+ if (event.event.type === "file_changed") return;
1022
+ const key = `tool.progress\0${event.sessionId ?? ""}\0${event.id}`;
1023
+ const target = bucket(key, {
1024
+ signal: "tool.progress",
1025
+ ...event.sessionId ? { sessionId: event.sessionId } : {},
1026
+ ...event.agentId ? { agentId: event.agentId } : {},
1027
+ toolCallId: event.id,
1028
+ dimensions: { toolName: event.name }
1029
+ });
1030
+ sample(
1031
+ target,
1032
+ { textBytes: Buffer.byteLength(event.event.text ?? "") },
1033
+ event.event.type,
1034
+ safeDigest(event.event)
1035
+ );
1036
+ }),
1037
+ options.events.on("tool.executed", (event) => {
1038
+ flush(`tool.progress\0${event.sessionId ?? ""}\0${event.id ?? ""}`);
1039
+ const metadata = event.metadata;
1040
+ if (!metadata || metadata.files.length === 0 && metadata.symbols.length === 0 && metadata.commands.length === 0)
1041
+ return;
1042
+ const key = `tool.resource\0${event.sessionId ?? ""}\0${event.id ?? ""}`;
1043
+ const target = bucket(key, {
1044
+ signal: "tool.resource.observed",
1045
+ ...event.sessionId ? { sessionId: event.sessionId } : {},
1046
+ ...event.agentId ? { agentId: event.agentId } : {},
1047
+ ...event.id ? { toolCallId: event.id } : {},
1048
+ dimensions: { toolName: event.name }
1049
+ });
1050
+ for (const file of metadata.files) {
1051
+ target.resources.set(resourceId2("file", file), { kind: "file", path: file });
1052
+ sample(target, {}, "file");
1053
+ }
1054
+ for (const symbol of metadata.symbols) {
1055
+ target.resources.set(resourceId2("symbol", symbol), { kind: "symbol" });
1056
+ sample(target, {}, "symbol");
1057
+ }
1058
+ for (const command of metadata.commands) {
1059
+ target.resources.set(resourceId2("command", command), { kind: "process" });
1060
+ sample(target, {}, "command");
1061
+ }
1062
+ flush(key);
1063
+ }),
1064
+ options.events.on(
1065
+ "tool.failed",
1066
+ (event) => flush(`tool.progress\0${event.sessionId}\0${event.id}`)
1067
+ ),
1068
+ options.events.on("ctx.pct", (event) => gauge("ctx.pct", event)),
1069
+ options.events.on(
1070
+ "subagent.ctx_pct",
1071
+ (event) => gauge("subagent.ctx_pct", event, "subagentId")
1072
+ ),
1073
+ options.events.on("countdown.tick", (event) => gauge("countdown.tick", event)),
1074
+ options.events.on("coordinator.stats", (event) => gauge("coordinator.stats", event)),
1075
+ // Periodic self-observation gauge (nested fields, so the flat gauge()
1076
+ // helper can't be reused directly — flatten before sampling).
1077
+ options.events.on("runtime.health.sampled", (event) => {
1078
+ const key = `runtime.health\0${event.sessionId ?? ""}`;
1079
+ const target = bucket(key, {
1080
+ signal: "runtime.health",
1081
+ aggregateMs: gaugeWindowMs,
1082
+ ...event.sessionId ? { sessionId: event.sessionId } : {},
1083
+ dimensions: {}
1084
+ });
1085
+ const chronicle = event.chronicle;
1086
+ sample(target, {
1087
+ "eventLoop.utilization": event.eventLoop.utilization,
1088
+ "eventLoop.delayMeanMs": event.eventLoop.delayMeanMs,
1089
+ "eventLoop.delayP95Ms": event.eventLoop.delayP95Ms,
1090
+ "eventLoop.delayMaxMs": event.eventLoop.delayMaxMs,
1091
+ "cpu.userMicros": event.cpu.userMicros,
1092
+ "cpu.systemMicros": event.cpu.systemMicros,
1093
+ "memory.rssBytes": event.memory.rssBytes,
1094
+ "memory.heapUsedBytes": event.memory.heapUsedBytes,
1095
+ "memory.heapTotalBytes": event.memory.heapTotalBytes,
1096
+ ...typeof chronicle?.pendingEvents === "number" ? { "chronicle.pendingEvents": chronicle.pendingEvents } : {},
1097
+ ...typeof chronicle?.rejectedEvents === "number" ? { "chronicle.rejectedEvents": chronicle.rejectedEvents } : {}
1098
+ });
1099
+ }),
1100
+ // Full-fleet snapshots fired on every agent-state flush (~14KB each) are
1101
+ // reduced to one windowed aggregate per session; the raw event is excluded
1102
+ // from the domain adapter. Sums preserve the fleet-level metric trail.
1103
+ options.events.on("session.agents_updated", (event) => {
1104
+ const key = `session.agents\0${event.sessionId ?? ""}`;
1105
+ const target = bucket(key, {
1106
+ signal: "session.agents",
1107
+ aggregateMs: gaugeWindowMs,
1108
+ ...event.sessionId ? { sessionId: event.sessionId } : {},
1109
+ dimensions: {}
1110
+ });
1111
+ const sums = {
1112
+ agents: 0,
1113
+ running: 0,
1114
+ iterations: 0,
1115
+ toolCalls: 0,
1116
+ costUsd: 0,
1117
+ tokensIn: 0,
1118
+ tokensOut: 0
1119
+ };
1120
+ for (const agent of event.agents) {
1121
+ sums.agents++;
1122
+ if (agent.status === "running") sums.running++;
1123
+ sums.iterations += numeric(agent.iterations);
1124
+ sums.toolCalls += numeric(agent.toolCalls);
1125
+ sums.costUsd += numeric(agent.costUsd);
1126
+ sums.tokensIn += numeric(agent.tokensIn);
1127
+ sums.tokensOut += numeric(agent.tokensOut);
1128
+ }
1129
+ sample(target, sums);
1130
+ }),
1131
+ // Per-request network chatter (started/completed pairs were the 2nd most
1132
+ // frequent raw family) becomes one aggregate per session×initiator×host
1133
+ // window with status-class categories. network.request.failed stays raw.
1134
+ options.events.on("network.request.completed", (event) => {
1135
+ const key = `network.request\0${event.sessionId}\0${event.initiator}\0${event.serverAddress}`;
1136
+ const target = bucket(key, {
1137
+ signal: "network.request",
1138
+ sessionId: event.sessionId,
1139
+ dimensions: { initiator: event.initiator, serverAddress: event.serverAddress }
1140
+ });
1141
+ sample(
1142
+ target,
1143
+ {
1144
+ durationMs: event.durationMs,
1145
+ ...event.requestBytes !== void 0 ? { requestBytes: event.requestBytes } : {},
1146
+ ...event.responseBytes !== void 0 ? { responseBytes: event.responseBytes } : {}
1147
+ },
1148
+ `${Math.floor(event.statusCode / 100)}xx`,
1149
+ event.requestId
1150
+ );
1151
+ })
1152
+ ];
1153
+ const timer = setInterval(() => {
1154
+ const now = Date.now();
1155
+ for (const [key, value] of buckets) {
1156
+ const due = value.aggregateMs === void 0 ? value.updatedAt <= now - windowMs : value.startedAt <= now - value.aggregateMs;
1157
+ if (due) flush(key);
1158
+ }
1159
+ }, windowMs);
1160
+ timer.unref?.();
1161
+ return () => {
1162
+ clearInterval(timer);
1163
+ for (const key of [...buckets.keys()]) flush(key);
1164
+ offs.forEach((off) => {
1165
+ off();
1166
+ });
1167
+ };
1168
+ }
1169
+ function text(value) {
1170
+ return typeof value === "string" ? value : void 0;
1171
+ }
1172
+ function numeric(value) {
1173
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
1174
+ }
1175
+ function safeDigest(value) {
1176
+ try {
1177
+ return createHash7("sha256").update(JSON.stringify(value)).digest("hex");
1178
+ } catch {
1179
+ return "unhashable";
1180
+ }
1181
+ }
1182
+ function resourceId2(kind, value) {
1183
+ return `${kind}_${createHash7("sha256").update(value).digest("hex").slice(0, 24)}`;
1184
+ }
1185
+
1186
+ export {
1187
+ ChronicleCompactionBusyError,
1188
+ compactChronicleSqlite,
1189
+ wireProviderAttemptsToChronicle,
1190
+ wireToolsToChronicle,
1191
+ wireProcessesToChronicle,
1192
+ startChronicleHealthMonitor,
1193
+ wireDecisionsToChronicle,
1194
+ wireDomainEventsToChronicle,
1195
+ wireProviderStreamsToChronicle,
1196
+ wireReviewFindingsToChronicle,
1197
+ wireRollupsToChronicle
1198
+ };
1199
+ //# sourceMappingURL=chunk-NEMIWEXC.js.map