@wrongstack/core 0.310.1 → 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.
- package/dist/chronicle/index.js +84 -6391
- package/dist/chronicle/project-server.js +84 -4868
- package/dist/chunk-2BUUIMQB.js +225 -0
- package/dist/chunk-2FK4WTFF.js +207 -0
- package/dist/chunk-2FQL4PDC.js +292 -0
- package/dist/chunk-2KKODIQU.js +2256 -0
- package/dist/chunk-2YU4WVBW.js +134 -0
- package/dist/chunk-3E244UDA.js +356 -0
- package/dist/chunk-3XPVB7BA.js +135 -0
- package/dist/chunk-4LJJYBAU.js +388 -0
- package/dist/chunk-4VWO3ZXL.js +342 -0
- package/dist/chunk-4XV6IR37.js +187 -0
- package/dist/chunk-4ZHTKJHM.js +183 -0
- package/dist/chunk-52RTVH4M.js +361 -0
- package/dist/chunk-57GEWK5X.js +26 -0
- package/dist/chunk-5DSMEVA4.js +931 -0
- package/dist/chunk-5RP3QTRL.js +1223 -0
- package/dist/chunk-5S437PRC.js +50 -0
- package/dist/chunk-5XDUGTKN.js +511 -0
- package/dist/chunk-62V2AFVG.js +422 -0
- package/dist/chunk-65RDIOR2.js +2470 -0
- package/dist/chunk-6AKOTGLM.js +597 -0
- package/dist/chunk-7ZHZE5EO.js +28 -0
- package/dist/chunk-A455ATZB.js +140 -0
- package/dist/chunk-AD7ZFZCS.js +46 -0
- package/dist/chunk-AI4LLQ2U.js +856 -0
- package/dist/chunk-ASX6AW36.js +789 -0
- package/dist/chunk-AT4V4XGI.js +3522 -0
- package/dist/chunk-AWNQ6ED6.js +311 -0
- package/dist/chunk-B4AMLZPB.js +9 -0
- package/dist/chunk-BAKVJLEV.js +417 -0
- package/dist/chunk-BJHVULHO.js +55 -0
- package/dist/chunk-CB2HFKTP.js +9 -0
- package/dist/chunk-CE4ZOCOI.js +1640 -0
- package/dist/chunk-CQQL4ZOQ.js +151 -0
- package/dist/chunk-DEIQMBD2.js +65 -0
- package/dist/chunk-E6I5RXVK.js +58 -0
- package/dist/chunk-EFEHDQGY.js +1589 -0
- package/dist/chunk-EODDVJBM.js +82 -0
- package/dist/chunk-EPC3GZD4.js +308 -0
- package/dist/chunk-FAYRX2AD.js +13 -0
- package/dist/chunk-FFSX2QDQ.js +362 -0
- package/dist/chunk-FHAMQS7Q.js +5856 -0
- package/dist/chunk-FIV4IJL7.js +2263 -0
- package/dist/chunk-FNXVT7HF.js +52 -0
- package/dist/chunk-FSWCWSTS.js +395 -0
- package/dist/chunk-FW5LDDMJ.js +16464 -0
- package/dist/chunk-FXQLIX3K.js +211 -0
- package/dist/chunk-G5AAKEEZ.js +228 -0
- package/dist/chunk-G5S3S5ZN.js +38 -0
- package/dist/chunk-GAZEWQEZ.js +231 -0
- package/dist/chunk-GFJOMSHG.js +125 -0
- package/dist/chunk-GFXZTHNI.js +158 -0
- package/dist/chunk-GM54GJBU.js +960 -0
- package/dist/chunk-GPQ3NJPR.js +900 -0
- package/dist/chunk-GTCU6DZ3.js +1 -0
- package/dist/chunk-H5E77KUJ.js +15 -0
- package/dist/chunk-H72H4XDU.js +1023 -0
- package/dist/chunk-HA4ZYA6T.js +178 -0
- package/dist/chunk-I45KAW2N.js +5020 -0
- package/dist/chunk-IQ35HMQL.js +1396 -0
- package/dist/chunk-IYFBFX34.js +86 -0
- package/dist/chunk-JJPMWPB6.js +109 -0
- package/dist/chunk-JTHKOYWU.js +663 -0
- package/dist/chunk-JVGPR3TS.js +365 -0
- package/dist/chunk-JVKB2TBL.js +48 -0
- package/dist/chunk-JX7VBZDK.js +31 -0
- package/dist/chunk-K3G6JBUC.js +5232 -0
- package/dist/chunk-K4YUNKU7.js +589 -0
- package/dist/chunk-KBZGE6LN.js +710 -0
- package/dist/chunk-KDQJCT2J.js +45 -0
- package/dist/chunk-KI4L2DUL.js +560 -0
- package/dist/chunk-KJ2ZTQSL.js +47 -0
- package/dist/chunk-KSJ5UTP7.js +154 -0
- package/dist/chunk-LHOA5DWA.js +28 -0
- package/dist/chunk-LX5QWLBL.js +27 -0
- package/dist/chunk-LZDDLNCZ.js +279 -0
- package/dist/chunk-M2WF6AXG.js +115 -0
- package/dist/chunk-M7BS2K6U.js +1 -0
- package/dist/chunk-NDBIPFNY.js +102 -0
- package/dist/chunk-NEMIWEXC.js +1199 -0
- package/dist/chunk-NFAYHPRH.js +323 -0
- package/dist/chunk-O37BQ65S.js +21 -0
- package/dist/chunk-O74MJHXU.js +187 -0
- package/dist/chunk-OJZEX73F.js +112 -0
- package/dist/chunk-PAAWLLZX.js +74 -0
- package/dist/chunk-PFXFQQRT.js +339 -0
- package/dist/chunk-PFZO3C6H.js +14 -0
- package/dist/chunk-PHH7LD45.js +113 -0
- package/dist/chunk-QRTG4XTI.js +188 -0
- package/dist/chunk-QVOVMXJR.js +1351 -0
- package/dist/chunk-RCQYT2I4.js +757 -0
- package/dist/chunk-RD6DEE36.js +1178 -0
- package/dist/chunk-RJIZEJ42.js +30 -0
- package/dist/chunk-RPOA7IAE.js +150 -0
- package/dist/chunk-SN2DQRL6.js +1172 -0
- package/dist/chunk-SQJVFEBV.js +41 -0
- package/dist/chunk-SQP64RCS.js +5391 -0
- package/dist/chunk-SVT4RNTR.js +49 -0
- package/dist/chunk-SWUFN7XM.js +43 -0
- package/dist/chunk-SXMVYC5H.js +174 -0
- package/dist/chunk-SZ6AYUIZ.js +23 -0
- package/dist/chunk-T3KGZBU5.js +3171 -0
- package/dist/chunk-TBRL2RCC.js +9 -0
- package/dist/chunk-TRT2HRWZ.js +18 -0
- package/dist/chunk-TYW2GI5I.js +1077 -0
- package/dist/chunk-UIWONWVG.js +4959 -0
- package/dist/chunk-VB3H5WWB.js +151 -0
- package/dist/chunk-W4A6QA3D.js +258 -0
- package/dist/chunk-WBPVKEEE.js +227 -0
- package/dist/chunk-WEBMSQP2.js +123 -0
- package/dist/chunk-WOEZV3Q4.js +333 -0
- package/dist/chunk-WQQON3SV.js +473 -0
- package/dist/chunk-WQTOVMGV.js +119 -0
- package/dist/chunk-WZO23AWO.js +2779 -0
- package/dist/chunk-XBDP4QPW.js +7 -0
- package/dist/chunk-XEBHSJLT.js +103 -0
- package/dist/chunk-XQ2ECNK3.js +1 -0
- package/dist/chunk-XQ2XGIBF.js +2086 -0
- package/dist/chunk-Y2GE5TSI.js +355 -0
- package/dist/chunk-YBUJCZ57.js +351 -0
- package/dist/chunk-YNCVSFGF.js +149 -0
- package/dist/chunk-YUPMXOGP.js +282 -0
- package/dist/chunk-YY6APYUM.js +49 -0
- package/dist/chunk-Z2IJUO45.js +691 -0
- package/dist/chunk-ZEHUCJHY.js +129 -0
- package/dist/chunk-ZYU2YOJ6.js +1496 -0
- package/dist/chunk-ZZ4V3WIZ.js +163 -0
- package/dist/coordination/agents/index.js +145 -6260
- package/dist/coordination/index.js +365 -34323
- package/dist/coordination/mailbox-project-server.js +48 -3220
- package/dist/coordination/mailbox-types.d.ts +13 -1
- package/dist/coordination/provider-status-tracker.d.ts +4 -0
- package/dist/coordination/sqlite-mailbox.d.ts +2 -1
- package/dist/core/agent.d.ts +14 -1
- package/dist/core/index.js +267 -12479
- package/dist/core/model-ref.js +5 -23
- package/dist/core/provider-runner.d.ts +36 -1
- package/dist/design/index.js +50 -1087
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1599 -21887
- package/dist/execution/prompt-enhancer.js +16 -535
- package/dist/execution/retry-policy.d.ts +31 -1
- package/dist/extension/index.js +7 -311
- package/dist/goal/index.js +33 -3435
- package/dist/hooks/index.js +14 -1138
- package/dist/hq/index.js +112 -5139
- package/dist/hq/protocol.js +21 -668
- package/dist/index.js +1410 -92655
- package/dist/infrastructure/index.js +64 -4811
- package/dist/kernel/events/provider-events.d.ts +15 -0
- package/dist/kernel/events/wrongtrace-events.d.ts +26 -0
- package/dist/kernel/events.d.ts +16 -7
- package/dist/kernel/index.js +14 -992
- package/dist/models/index.js +40 -1117
- package/dist/notifications/index.js +3 -108
- package/dist/observability/index.js +17 -279
- package/dist/plugin/index.js +584 -14835
- package/dist/prompts/index.js +14 -948
- package/dist/registry/index.js +9 -822
- package/dist/registry-R23UBE7H.js +14 -0
- package/dist/replay/index.js +6 -84
- package/dist/review-finding-store-DKAV3AS6.js +21 -0
- package/dist/review-report-store-OWY3EC3N.js +19 -0
- package/dist/security/index.js +114 -2202
- package/dist/session-catalog/index.js +36 -2103
- package/dist/session-catalog/project-server.js +42 -1435
- package/dist/skills/index.js +35 -1578
- package/dist/storage/index.js +182 -15600
- package/dist/tasking/index.js +10 -676
- package/dist/tools/index.js +52 -9589
- package/dist/types/config/autonomy.d.ts +5 -0
- package/dist/types/config/tools.d.ts +2 -2
- package/dist/types/config/ui.d.ts +1 -1
- package/dist/types/errors.d.ts +18 -0
- package/dist/types/index.js +121 -1341
- package/dist/types/provider-runner.d.ts +8 -0
- package/dist/types/session-markers.js +8 -144
- package/dist/utils/error.js +3 -4
- package/dist/utils/expect-defined.js +3 -9
- package/dist/utils/heap-watchdog.js +6 -706
- package/dist/utils/index.js +269 -5685
- package/dist/utils/sage-output-block.js +5 -42
- package/dist/wiring/proxy-rewrite.d.ts +92 -4
- package/dist/wiring/proxy-rewrite.js +20 -61
- package/dist/worktree/index.js +6 -887
- 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
|