@zelari/core 2.37.2 → 2.38.0
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/README.md +1 -1
- package/dist/core/AgentHarness.d.ts +9 -1
- package/dist/core/AgentHarness.d.ts.map +1 -1
- package/dist/core/AgentHarness.js +34 -7
- package/dist/core/AgentHarness.js.map +1 -1
- package/dist/core/requestSnapshot.d.ts +19 -4
- package/dist/core/requestSnapshot.d.ts.map +1 -1
- package/dist/core/requestSnapshot.js +103 -30
- package/dist/core/requestSnapshot.js.map +1 -1
- package/dist/runtime/providers.d.ts +8 -0
- package/dist/runtime/providers.d.ts.map +1 -1
- package/dist/runtime/providers.js.map +1 -1
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +1 -0
- package/dist/session/index.js.map +1 -1
- package/dist/session/replay.d.ts +18 -0
- package/dist/session/replay.d.ts.map +1 -1
- package/dist/session/replay.js +19 -4
- package/dist/session/replay.js.map +1 -1
- package/dist/session/replayCache.d.ts +76 -0
- package/dist/session/replayCache.d.ts.map +1 -0
- package/dist/session/replayCache.js +188 -0
- package/dist/session/replayCache.js.map +1 -0
- package/dist/verification/commandConcurrency.d.ts +34 -0
- package/dist/verification/commandConcurrency.d.ts.map +1 -0
- package/dist/verification/commandConcurrency.js +45 -0
- package/dist/verification/commandConcurrency.js.map +1 -0
- package/dist/verification/engine.d.ts +25 -0
- package/dist/verification/engine.d.ts.map +1 -1
- package/dist/verification/engine.js +58 -6
- package/dist/verification/engine.js.map +1 -1
- package/dist/verification/index.d.ts +1 -0
- package/dist/verification/index.d.ts.map +1 -1
- package/dist/verification/index.js +1 -0
- package/dist/verification/index.js.map +1 -1
- package/dist/verification/runWithLimit.d.ts +24 -0
- package/dist/verification/runWithLimit.d.ts.map +1 -0
- package/dist/verification/runWithLimit.js +50 -0
- package/dist/verification/runWithLimit.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -20,12 +20,28 @@
|
|
|
20
20
|
* - tools are sorted by `name.localeCompare` — the same canonical order
|
|
21
21
|
* the OpenAI-compatible provider applies on the wire, so the snapshot
|
|
22
22
|
* reflects the actual request bytes,
|
|
23
|
-
* - snapshots deep-clone their payload: later mutation of
|
|
24
|
-
* message array must not rewrite history.
|
|
23
|
+
* - snapshots deep-clone their payload in `full` mode: later mutation of
|
|
24
|
+
* the live message array must not rewrite history.
|
|
25
|
+
*
|
|
26
|
+
* Int4b (`ZELARI_REQUEST_SNAPSHOT=full|lite|off`, default `full`):
|
|
27
|
+
* - `full` — current behavior (eager fingerprints, structuredClone).
|
|
28
|
+
* - `lite` — shallow first-level copies; fingerprints are lazy getters
|
|
29
|
+
* (same digest as `full` when read); header fingerprint memoized on
|
|
30
|
+
* tools-array identity + system key. Messages are not mutated after
|
|
31
|
+
* append in the tool loop, so shallow copies are safe on the hot path.
|
|
32
|
+
* - `off` — harness skips snapshot construction (metering stays; audit
|
|
33
|
+
* trail of routed requests is reduced).
|
|
25
34
|
*
|
|
26
35
|
* @since v1.36.0 — context/cache upgrade (routed request snapshots)
|
|
27
36
|
*/
|
|
28
37
|
import { createHash } from 'node:crypto';
|
|
38
|
+
/** Default stays `full` until lite is dogfooded (Int4b). Unknown values → full. */
|
|
39
|
+
export function resolveRequestSnapshotMode(env = process.env) {
|
|
40
|
+
const raw = env.ZELARI_REQUEST_SNAPSHOT?.trim().toLowerCase();
|
|
41
|
+
if (raw === 'lite' || raw === 'off')
|
|
42
|
+
return raw;
|
|
43
|
+
return 'full';
|
|
44
|
+
}
|
|
29
45
|
/**
|
|
30
46
|
* Deterministic JSON: object keys sorted recursively, arrays in order.
|
|
31
47
|
* No external deps (AGENTS.MD: zero new heavy dependencies).
|
|
@@ -48,52 +64,109 @@ export function stableStringify(value) {
|
|
|
48
64
|
export function sha256Hex(input) {
|
|
49
65
|
return createHash('sha256').update(input, 'utf8').digest('hex').slice(0, 32);
|
|
50
66
|
}
|
|
51
|
-
function
|
|
67
|
+
function cloneMessagesDeep(messages) {
|
|
52
68
|
return structuredClone(messages);
|
|
53
69
|
}
|
|
70
|
+
function cloneMessagesLite(messages) {
|
|
71
|
+
return messages.map((m) => ({ ...m }));
|
|
72
|
+
}
|
|
54
73
|
/** Canonical tool order — must match the wire order (openai-compatible.ts). */
|
|
55
74
|
export function canonicalTools(tools) {
|
|
56
75
|
return [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
57
76
|
}
|
|
77
|
+
function cloneToolsDeep(tools) {
|
|
78
|
+
return canonicalTools(tools).map((t) => structuredClone(t));
|
|
79
|
+
}
|
|
80
|
+
function cloneToolsLite(tools) {
|
|
81
|
+
return canonicalTools(tools).map((t) => ({ ...t }));
|
|
82
|
+
}
|
|
83
|
+
let headerMemo = null;
|
|
84
|
+
/** Test-only: drop the lite header memo. */
|
|
85
|
+
export function __resetRequestSnapshotMemoForTests() {
|
|
86
|
+
headerMemo = null;
|
|
87
|
+
}
|
|
58
88
|
/**
|
|
59
89
|
* Build a snapshot from the parameters of a provider call.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
90
|
+
* `full` (default): the snapshot owns deep clones.
|
|
91
|
+
* `lite`: shallow first-level copies + lazy fingerprints (same digest).
|
|
62
92
|
*/
|
|
63
93
|
export function createRoutedRequestSnapshot(params) {
|
|
64
|
-
|
|
65
|
-
// leading run of role:'system' the request carried (1 or 2 messages in
|
|
66
|
-
// the current builders — the split must not assume a fixed count).
|
|
94
|
+
const mode = params.mode ?? resolveRequestSnapshotMode();
|
|
67
95
|
let split = 0;
|
|
68
96
|
while (split < params.messages.length && params.messages[split].role === 'system') {
|
|
69
97
|
split++;
|
|
70
98
|
}
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tools
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
const lite = mode === 'lite';
|
|
100
|
+
const cloneMsg = lite ? cloneMessagesLite : cloneMessagesDeep;
|
|
101
|
+
const systemMessages = cloneMsg(params.messages.slice(0, split));
|
|
102
|
+
const conversation = cloneMsg(params.messages.slice(split));
|
|
103
|
+
const createdAt = Date.now();
|
|
104
|
+
const { provider, model } = params;
|
|
105
|
+
if (!lite) {
|
|
106
|
+
const tools = cloneToolsDeep(params.tools);
|
|
107
|
+
const header = stableStringify({ provider, model, systemMessages, tools });
|
|
108
|
+
const request = stableStringify({
|
|
109
|
+
provider,
|
|
110
|
+
model,
|
|
111
|
+
systemMessages,
|
|
112
|
+
tools,
|
|
113
|
+
conversation,
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
provider,
|
|
117
|
+
model,
|
|
118
|
+
systemMessages,
|
|
119
|
+
conversation,
|
|
120
|
+
tools,
|
|
121
|
+
headerFingerprint: sha256Hex(header),
|
|
122
|
+
requestFingerprint: sha256Hex(request),
|
|
123
|
+
createdAt,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const systemKey = sha256Hex(stableStringify(systemMessages));
|
|
127
|
+
let tools;
|
|
128
|
+
let headerFp;
|
|
129
|
+
if (headerMemo &&
|
|
130
|
+
headerMemo.toolsRef === params.tools &&
|
|
131
|
+
headerMemo.provider === provider &&
|
|
132
|
+
headerMemo.model === model &&
|
|
133
|
+
headerMemo.systemKey === systemKey) {
|
|
134
|
+
tools = headerMemo.tools;
|
|
135
|
+
headerFp = headerMemo.headerFingerprint;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
tools = cloneToolsLite(params.tools);
|
|
139
|
+
}
|
|
140
|
+
let requestFp;
|
|
141
|
+
const snap = {
|
|
142
|
+
provider,
|
|
143
|
+
model,
|
|
90
144
|
systemMessages,
|
|
91
145
|
conversation,
|
|
92
146
|
tools,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
147
|
+
createdAt,
|
|
148
|
+
get headerFingerprint() {
|
|
149
|
+
if (headerFp === undefined) {
|
|
150
|
+
headerFp = sha256Hex(stableStringify({ provider, model, systemMessages, tools }));
|
|
151
|
+
headerMemo = {
|
|
152
|
+
toolsRef: params.tools,
|
|
153
|
+
provider,
|
|
154
|
+
model,
|
|
155
|
+
systemKey,
|
|
156
|
+
tools,
|
|
157
|
+
headerFingerprint: headerFp,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return headerFp;
|
|
161
|
+
},
|
|
162
|
+
get requestFingerprint() {
|
|
163
|
+
if (requestFp === undefined) {
|
|
164
|
+
requestFp = sha256Hex(stableStringify({ provider, model, systemMessages, tools, conversation }));
|
|
165
|
+
}
|
|
166
|
+
return requestFp;
|
|
167
|
+
},
|
|
96
168
|
};
|
|
169
|
+
return snap;
|
|
97
170
|
}
|
|
98
171
|
/**
|
|
99
172
|
* Best-effort check (P11): is `messages` an exact prefix-extension of the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestSnapshot.js","sourceRoot":"","sources":["../../src/core/requestSnapshot.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"requestSnapshot.js","sourceRoot":"","sources":["../../src/core/requestSnapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,6 +49,14 @@ export interface ShellResult {
|
|
|
49
49
|
stderr: string;
|
|
50
50
|
durationMs: number;
|
|
51
51
|
timedOut: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Set by caching decorators, not by providers (Int2b): true when this
|
|
54
|
+
* observation was served from a previous identical execution instead of
|
|
55
|
+
* being re-run. Absent on every provider-produced result, so existing
|
|
56
|
+
* consumers are unaffected; verification annotates such observations
|
|
57
|
+
* (`detail` suffix, `cached: true` in the `verification.evidence` payload).
|
|
58
|
+
*/
|
|
59
|
+
cached?: boolean;
|
|
52
60
|
}
|
|
53
61
|
export interface ShellExecOptions {
|
|
54
62
|
/** Workspace-relative cwd (default: workspace root). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/runtime/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,0EAA0E;AAC1E,qBAAa,wBAAyB,SAAQ,KAAK;aAG/B,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM;IAHnC,QAAQ,CAAC,IAAI,2BAA2B;gBAEtB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM;CAKpC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1D,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,+DAA+D;IAC/D,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED,6CAA6C;AAC7C,qBAAa,cAAe,YAAW,iBAAiB;IAG1C,QAAQ,CAAC,IAAI,EAAE,MAAM;IAFjC,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;gBAEZ,IAAI,EAAE,MAAM;IAEjC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAG7B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,4DAA4D;IAC5D,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/runtime/providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/runtime/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,0EAA0E;AAC1E,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAG/B;IACA;IAHT,IAAI,GAAG,uBAAuB,CAAC;IACxC,YACkB,IAAY,EACZ,SAAiB;QAEjC,KAAK,CAAC,gCAAgC,SAAS,WAAW,IAAI,GAAG,CAAC,CAAC;QAHnD,SAAI,GAAJ,IAAI,CAAQ;QACZ,cAAS,GAAT,SAAS,CAAQ;QAGjC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAYD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,GAAW;IACrD,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,wBAAwB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6CAA6C;AAC7C,MAAM,OAAO,cAAc;IAGJ;IAFZ,IAAI,GAAG,OAAgB,CAAC;IAEjC,YAAqB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAErC,OAAO,CAAC,GAAW;QACjB,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/runtime/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,0EAA0E;AAC1E,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAG/B;IACA;IAHT,IAAI,GAAG,uBAAuB,CAAC;IACxC,YACkB,IAAY,EACZ,SAAiB;QAEjC,KAAK,CAAC,gCAAgC,SAAS,WAAW,IAAI,GAAG,CAAC,CAAC;QAHnD,SAAI,GAAJ,IAAI,CAAQ;QACZ,cAAS,GAAT,SAAS,CAAQ;QAGjC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAYD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,GAAW;IACrD,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,wBAAwB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6CAA6C;AAC7C,MAAM,OAAO,cAAc;IAGJ;IAFZ,IAAI,GAAG,OAAgB,CAAC;IAEjC,YAAqB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAErC,OAAO,CAAC,GAAW;QACjB,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;CACF;AA0DD,+EAA+E;AAC/E,MAAM,OAAO,oBAAoB;IACtB,SAAS,GAAG,KAAK,CAAC;IAE3B,KAAK,CAAC,OAAO,CAAC,IAAkB;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,+DAA+D,IAAI,CAAC,IAAI,IAAI;SACzF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAqB,IAAI,oBAAoB,EAAE,CAAC"}
|
package/dist/session/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './compaction.js';
|
|
|
11
11
|
export * from './agentAdapter.js';
|
|
12
12
|
export * from './writer.js';
|
|
13
13
|
export * from './replay.js';
|
|
14
|
+
export * from './replayCache.js';
|
|
14
15
|
export * from './store.js';
|
|
15
16
|
export * from './lineage.js';
|
|
16
17
|
export * from './exportSession.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
package/dist/session/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from './compaction.js';
|
|
|
11
11
|
export * from './agentAdapter.js';
|
|
12
12
|
export * from './writer.js';
|
|
13
13
|
export * from './replay.js';
|
|
14
|
+
export * from './replayCache.js';
|
|
14
15
|
export * from './store.js';
|
|
15
16
|
export * from './lineage.js';
|
|
16
17
|
export * from './exportSession.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
package/dist/session/replay.d.ts
CHANGED
|
@@ -24,6 +24,24 @@ export interface ReplayReport {
|
|
|
24
24
|
ok: boolean;
|
|
25
25
|
}
|
|
26
26
|
export declare function readSessionLog(filePath: string): Promise<ReplayReport>;
|
|
27
|
+
/** Whole-file text → ReplayReport (shared with the incremental reader, Int4a). */
|
|
28
|
+
export declare function parseSessionLogText(filePath: string, content: string): ReplayReport;
|
|
29
|
+
/**
|
|
30
|
+
* Parse ONE batch of raw JSONL lines (as produced by `split('\n')`), carrying
|
|
31
|
+
* the running `expected` seq and the 0-based index of the batch's first line.
|
|
32
|
+
* The full reader above and the incremental byte-append reader (replayCache.ts)
|
|
33
|
+
* share this, so both report the SAME events/issues for the same log — that
|
|
34
|
+
* equivalence is the replay-cache contract.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseSessionLogLines(lines: readonly string[], opts?: {
|
|
37
|
+
expected?: number;
|
|
38
|
+
linesConsumed?: number;
|
|
39
|
+
}): {
|
|
40
|
+
events: SessionEventEnvelope[];
|
|
41
|
+
issues: ReplayIssue[];
|
|
42
|
+
expected: number;
|
|
43
|
+
linesConsumed: number;
|
|
44
|
+
};
|
|
27
45
|
/** Loose summary of a verification.run event (defensive reads). */
|
|
28
46
|
export interface VerificationRunSummary {
|
|
29
47
|
seq: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/session/replay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE/E,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,iBAAiB,GACjB,eAAe,GACf,SAAS,GACT,kBAAkB,CAAC;AAEvB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,4DAA4D;IAC5D,EAAE,EAAE,OAAO,CAAC;CACb;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/session/replay.ts"],"names":[],"mappings":"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"}
|
package/dist/session/replay.js
CHANGED
|
@@ -20,15 +20,30 @@ export async function readSessionLog(filePath) {
|
|
|
20
20
|
}
|
|
21
21
|
throw err;
|
|
22
22
|
}
|
|
23
|
+
return parseSessionLogText(filePath, content);
|
|
24
|
+
}
|
|
25
|
+
/** Whole-file text → ReplayReport (shared with the incremental reader, Int4a). */
|
|
26
|
+
export function parseSessionLogText(filePath, content) {
|
|
27
|
+
const { events, issues } = parseSessionLogLines(content.split('\n'));
|
|
28
|
+
return { path: filePath, events, issues, ok: issues.length === 0 };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse ONE batch of raw JSONL lines (as produced by `split('\n')`), carrying
|
|
32
|
+
* the running `expected` seq and the 0-based index of the batch's first line.
|
|
33
|
+
* The full reader above and the incremental byte-append reader (replayCache.ts)
|
|
34
|
+
* share this, so both report the SAME events/issues for the same log — that
|
|
35
|
+
* equivalence is the replay-cache contract.
|
|
36
|
+
*/
|
|
37
|
+
export function parseSessionLogLines(lines, opts = {}) {
|
|
23
38
|
const events = [];
|
|
24
39
|
const issues = [];
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
const base = opts.linesConsumed ?? 0;
|
|
41
|
+
let expected = opts.expected ?? 1;
|
|
27
42
|
for (let i = 0; i < lines.length; i++) {
|
|
28
43
|
const trimmed = lines[i].trim();
|
|
29
44
|
if (!trimmed)
|
|
30
45
|
continue;
|
|
31
|
-
const lineNo = i + 1;
|
|
46
|
+
const lineNo = base + i + 1;
|
|
32
47
|
let parsed;
|
|
33
48
|
try {
|
|
34
49
|
parsed = JSON.parse(trimmed);
|
|
@@ -65,7 +80,7 @@ export async function readSessionLog(filePath) {
|
|
|
65
80
|
expected = envelope.seq + 1;
|
|
66
81
|
}
|
|
67
82
|
}
|
|
68
|
-
return {
|
|
83
|
+
return { events, issues, expected, linesConsumed: base + lines.length };
|
|
69
84
|
}
|
|
70
85
|
function parseVerification(e) {
|
|
71
86
|
const raw = Array.isArray(e.data.results) ? e.data.results : [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/session/replay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EACL,0BAA0B,GAE3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAuB,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAwB,MAAM,eAAe,CAAC;AAyB/E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QAC9D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/session/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session/replayCache.ts — Int4a incremental replay cache for long sessions.
|
|
3
|
+
*
|
|
4
|
+
* `readSessionLog()` re-reads the WHOLE JSONL and Zod-parses every line on
|
|
5
|
+
* every call, and the spine does it 2–8 times per turn: O(n²) in session
|
|
6
|
+
* length. This cache keeps the parsed events per file and, on a later call,
|
|
7
|
+
* reads only the bytes appended since the last read (COMPLETE lines only).
|
|
8
|
+
*
|
|
9
|
+
* Invariants:
|
|
10
|
+
* - `byteSize` is what the entry OWNS: every complete line consumed plus the
|
|
11
|
+
* bytes held in `partial`. It always sits on a UTF-8 character boundary, so
|
|
12
|
+
* the next read starts exactly there and no byte is ever parsed twice.
|
|
13
|
+
* - A tail without its final '\n' stays in `partial` and is NOT an event yet:
|
|
14
|
+
* it appears exactly once, when the writer completes the line. A tail
|
|
15
|
+
* ending INSIDE a multi-byte character is not consumed at all (re-read from
|
|
16
|
+
* a boundary) — a torn write can never poison the cache with U+FFFD.
|
|
17
|
+
* - Shrink (`size < byteSize`) or a backwards mtime (rotation / in-place
|
|
18
|
+
* rewrite) ⇒ full re-read through the SAME parse path as `readSessionLog`,
|
|
19
|
+
* so a replaced log is never served from stale entries.
|
|
20
|
+
* - The returned ReplayReport is fresh (caller-owned arrays); the envelopes
|
|
21
|
+
* inside are shared and treat-as-immutable — the spine contract.
|
|
22
|
+
* - Known deviation: a COLD read (empty cache / rotation) consumes a
|
|
23
|
+
* non-terminated last line exactly as `readSessionLog` does today; only
|
|
24
|
+
* appends observed while warm are deferred into `partial`. The writer
|
|
25
|
+
* always terminates lines with '\n', so this needs external truncation.
|
|
26
|
+
*
|
|
27
|
+
* Kill switch `ZELARI_SPINE_REPLAY_CACHE`: DEFAULT OFF in this merge (dogfood
|
|
28
|
+
* first) — set `1`/`true`/`yes`/`on` to enable. When unset/0/false/off,
|
|
29
|
+
* `readSessionLogCached` calls the existing `readSessionLog` verbatim.
|
|
30
|
+
*/
|
|
31
|
+
import { type ReplayIssue, type ReplayReport } from './replay.js';
|
|
32
|
+
import type { SessionEventEnvelope } from './types.js';
|
|
33
|
+
/** `ZELARI_SPINE_REPLAY_CACHE` (default OFF): truthy spelling = 1/true/yes/on. */
|
|
34
|
+
export declare function isReplayCacheEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
35
|
+
export interface ReplayCacheEntry {
|
|
36
|
+
/** Bytes owned by this entry: complete lines + the bytes held in `partial`. */
|
|
37
|
+
byteSize: number;
|
|
38
|
+
mtimeMs: number;
|
|
39
|
+
events: SessionEventEnvelope[];
|
|
40
|
+
issues: ReplayIssue[];
|
|
41
|
+
/** Seq the next event must carry to be accepted gap-free. */
|
|
42
|
+
expectedSeq: number;
|
|
43
|
+
/** Tail without a final '\n' (writer crash mid-line) — not yet an event. */
|
|
44
|
+
partial: string;
|
|
45
|
+
/**
|
|
46
|
+
* Lines consumed so far, blank/corrupt ones included, so a new issue keeps
|
|
47
|
+
* its ORIGINAL 1-based file line number (`linesConsumed + index + 1`).
|
|
48
|
+
* Additive to the planned entry shape: without it an incremental read would
|
|
49
|
+
* renumber issues and stop being equivalent to `readSessionLog`.
|
|
50
|
+
*/
|
|
51
|
+
linesConsumed: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Incremental replay cache. Own ONE instance per session (the spine mirror owns
|
|
55
|
+
* its own); entries are keyed by file path, so concurrent sessions never share
|
|
56
|
+
* decoded state.
|
|
57
|
+
*/
|
|
58
|
+
export declare class SessionLogCache {
|
|
59
|
+
private readonly entries;
|
|
60
|
+
/** Files currently cached (diagnostics). */
|
|
61
|
+
get size(): number;
|
|
62
|
+
/** The cached entry for `filePath` (copy; tests + diagnostics), if any. */
|
|
63
|
+
peek(filePath: string): ReplayCacheEntry | undefined;
|
|
64
|
+
/** Read the log, incrementally when a usable entry exists. */
|
|
65
|
+
read(filePath: string): Promise<ReplayReport>;
|
|
66
|
+
/** Append-path read: only the bytes written since the last read are parsed. */
|
|
67
|
+
private extend;
|
|
68
|
+
/** Cold / rotated read: same parse as `readSessionLog`, then seed the entry. */
|
|
69
|
+
private full;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cached read of a session log. Without a cache — or with the kill switch off
|
|
73
|
+
* (the default in this merge) — this is `readSessionLog` verbatim.
|
|
74
|
+
*/
|
|
75
|
+
export declare function readSessionLogCached(filePath: string, cache?: SessionLogCache, env?: NodeJS.ProcessEnv): Promise<ReplayReport>;
|
|
76
|
+
//# sourceMappingURL=replayCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replayCache.d.ts","sourceRoot":"","sources":["../../src/session/replayCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAMvD,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAGlF;AAED,MAAM,WAAW,gBAAgB;IAC/B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAuCD;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAE/D,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,2EAA2E;IAC3E,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAKpD,8DAA8D;IACxD,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBnD,+EAA+E;YACjE,MAAM;IAwCpB,gFAAgF;YAClE,IAAI;CA6BnB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,eAAe,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,YAAY,CAAC,CAGvB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session/replayCache.ts — Int4a incremental replay cache for long sessions.
|
|
3
|
+
*
|
|
4
|
+
* `readSessionLog()` re-reads the WHOLE JSONL and Zod-parses every line on
|
|
5
|
+
* every call, and the spine does it 2–8 times per turn: O(n²) in session
|
|
6
|
+
* length. This cache keeps the parsed events per file and, on a later call,
|
|
7
|
+
* reads only the bytes appended since the last read (COMPLETE lines only).
|
|
8
|
+
*
|
|
9
|
+
* Invariants:
|
|
10
|
+
* - `byteSize` is what the entry OWNS: every complete line consumed plus the
|
|
11
|
+
* bytes held in `partial`. It always sits on a UTF-8 character boundary, so
|
|
12
|
+
* the next read starts exactly there and no byte is ever parsed twice.
|
|
13
|
+
* - A tail without its final '\n' stays in `partial` and is NOT an event yet:
|
|
14
|
+
* it appears exactly once, when the writer completes the line. A tail
|
|
15
|
+
* ending INSIDE a multi-byte character is not consumed at all (re-read from
|
|
16
|
+
* a boundary) — a torn write can never poison the cache with U+FFFD.
|
|
17
|
+
* - Shrink (`size < byteSize`) or a backwards mtime (rotation / in-place
|
|
18
|
+
* rewrite) ⇒ full re-read through the SAME parse path as `readSessionLog`,
|
|
19
|
+
* so a replaced log is never served from stale entries.
|
|
20
|
+
* - The returned ReplayReport is fresh (caller-owned arrays); the envelopes
|
|
21
|
+
* inside are shared and treat-as-immutable — the spine contract.
|
|
22
|
+
* - Known deviation: a COLD read (empty cache / rotation) consumes a
|
|
23
|
+
* non-terminated last line exactly as `readSessionLog` does today; only
|
|
24
|
+
* appends observed while warm are deferred into `partial`. The writer
|
|
25
|
+
* always terminates lines with '\n', so this needs external truncation.
|
|
26
|
+
*
|
|
27
|
+
* Kill switch `ZELARI_SPINE_REPLAY_CACHE`: DEFAULT OFF in this merge (dogfood
|
|
28
|
+
* first) — set `1`/`true`/`yes`/`on` to enable. When unset/0/false/off,
|
|
29
|
+
* `readSessionLogCached` calls the existing `readSessionLog` verbatim.
|
|
30
|
+
*/
|
|
31
|
+
import { promises as fs } from 'node:fs';
|
|
32
|
+
import { readSessionLog, parseSessionLogLines, } from './replay.js';
|
|
33
|
+
/** `ZELARI_SPINE_REPLAY_CACHE` (default OFF): truthy spelling = 1/true/yes/on. */
|
|
34
|
+
export function isReplayCacheEnabled(env = process.env) {
|
|
35
|
+
const v = (env.ZELARI_SPINE_REPLAY_CACHE ?? '').trim().toLowerCase();
|
|
36
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
37
|
+
}
|
|
38
|
+
const NL = 0x0a;
|
|
39
|
+
/** >0 when `buf` ends inside a multi-byte UTF-8 sequence (bytes to leave unread). */
|
|
40
|
+
function utf8IncompleteTailBytes(buf) {
|
|
41
|
+
const max = Math.min(4, buf.length);
|
|
42
|
+
for (let back = 1; back <= max; back++) {
|
|
43
|
+
const b = buf[buf.length - back];
|
|
44
|
+
if (b < 0x80)
|
|
45
|
+
return 0; // ASCII tail — complete
|
|
46
|
+
if (b >= 0xc0) {
|
|
47
|
+
// Lead byte: it must be followed by (need - 1) continuation bytes.
|
|
48
|
+
const need = b >= 0xf0 ? 4 : b >= 0xe0 ? 3 : 2;
|
|
49
|
+
return back >= need ? 0 : need - back;
|
|
50
|
+
}
|
|
51
|
+
// 0x80..0xbf = continuation byte — walk back to its lead byte.
|
|
52
|
+
}
|
|
53
|
+
return 0; // no lead byte within 4 bytes: malformed anyway, let JSON.parse judge
|
|
54
|
+
}
|
|
55
|
+
/** Read exactly `length` bytes at `position` (a live writer may shorten reads). */
|
|
56
|
+
async function readAt(fh, position, length) {
|
|
57
|
+
const buf = Buffer.allocUnsafe(length);
|
|
58
|
+
let done = 0;
|
|
59
|
+
while (done < length) {
|
|
60
|
+
const { bytesRead } = await fh.read(buf, done, length - done, position + done);
|
|
61
|
+
if (bytesRead === 0)
|
|
62
|
+
break; // truncated under us — parse what we got
|
|
63
|
+
done += bytesRead;
|
|
64
|
+
}
|
|
65
|
+
return done === length ? buf : buf.subarray(0, done);
|
|
66
|
+
}
|
|
67
|
+
/** Fresh report over the cached arrays — a consumer may mutate them freely. */
|
|
68
|
+
function toReport(filePath, entry) {
|
|
69
|
+
const events = entry.events.slice();
|
|
70
|
+
const issues = entry.issues.slice();
|
|
71
|
+
return { path: filePath, events, issues, ok: issues.length === 0 };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Incremental replay cache. Own ONE instance per session (the spine mirror owns
|
|
75
|
+
* its own); entries are keyed by file path, so concurrent sessions never share
|
|
76
|
+
* decoded state.
|
|
77
|
+
*/
|
|
78
|
+
export class SessionLogCache {
|
|
79
|
+
entries = new Map();
|
|
80
|
+
/** Files currently cached (diagnostics). */
|
|
81
|
+
get size() {
|
|
82
|
+
return this.entries.size;
|
|
83
|
+
}
|
|
84
|
+
/** The cached entry for `filePath` (copy; tests + diagnostics), if any. */
|
|
85
|
+
peek(filePath) {
|
|
86
|
+
const entry = this.entries.get(filePath);
|
|
87
|
+
return entry ? { ...entry } : undefined;
|
|
88
|
+
}
|
|
89
|
+
/** Read the log, incrementally when a usable entry exists. */
|
|
90
|
+
async read(filePath) {
|
|
91
|
+
const cached = this.entries.get(filePath);
|
|
92
|
+
let stat;
|
|
93
|
+
try {
|
|
94
|
+
stat = await fs.stat(filePath);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err.code === 'ENOENT') {
|
|
98
|
+
this.entries.delete(filePath);
|
|
99
|
+
return { path: filePath, events: [], issues: [], ok: true };
|
|
100
|
+
}
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
if (cached && stat.size >= cached.byteSize && stat.mtimeMs >= cached.mtimeMs) {
|
|
104
|
+
if (stat.size > cached.byteSize)
|
|
105
|
+
return this.extend(filePath, cached, stat);
|
|
106
|
+
return toReport(filePath, cached); // nothing new — no I/O at all
|
|
107
|
+
}
|
|
108
|
+
return this.full(filePath, stat);
|
|
109
|
+
}
|
|
110
|
+
/** Append-path read: only the bytes written since the last read are parsed. */
|
|
111
|
+
async extend(filePath, cached, stat) {
|
|
112
|
+
const fh = await fs.open(filePath, 'r');
|
|
113
|
+
let chunk;
|
|
114
|
+
try {
|
|
115
|
+
chunk = await readAt(fh, cached.byteSize, stat.size - cached.byteSize);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await fh.close();
|
|
119
|
+
}
|
|
120
|
+
const nl = chunk.lastIndexOf(NL);
|
|
121
|
+
const headEnd = nl === -1 ? 0 : nl + 1; // end of the last COMPLETE line
|
|
122
|
+
const tail = chunk.subarray(headEnd);
|
|
123
|
+
// PERF-4a: bytes of the tail that decode cleanly. A tail ending INSIDE a
|
|
124
|
+
// multi-byte char stays unconsumed and is re-read from a boundary, so
|
|
125
|
+
// `partial` is never a U+FFFD-mangled fragment.
|
|
126
|
+
const keep = tail.subarray(0, tail.length - utf8IncompleteTailBytes(tail));
|
|
127
|
+
// The old `partial` is folded into `head` only once a newline arrives; while
|
|
128
|
+
// no line completes it stays in front of the unconsumed tail.
|
|
129
|
+
const head = cached.partial + chunk.subarray(0, headEnd).toString('utf8');
|
|
130
|
+
const lines = headEnd > 0 ? head.split('\n').slice(0, -1) : [];
|
|
131
|
+
const parsed = parseSessionLogLines(lines, {
|
|
132
|
+
expected: cached.expectedSeq,
|
|
133
|
+
linesConsumed: cached.linesConsumed,
|
|
134
|
+
});
|
|
135
|
+
const entry = {
|
|
136
|
+
byteSize: cached.byteSize + headEnd + keep.length,
|
|
137
|
+
mtimeMs: stat.mtimeMs,
|
|
138
|
+
events: parsed.events.length > 0 ? cached.events.concat(parsed.events) : cached.events,
|
|
139
|
+
issues: parsed.issues.length > 0 ? cached.issues.concat(parsed.issues) : cached.issues,
|
|
140
|
+
expectedSeq: parsed.expected,
|
|
141
|
+
partial: (headEnd > 0 ? '' : cached.partial) + keep.toString('utf8'),
|
|
142
|
+
linesConsumed: parsed.linesConsumed,
|
|
143
|
+
};
|
|
144
|
+
this.entries.set(filePath, entry);
|
|
145
|
+
return toReport(filePath, entry);
|
|
146
|
+
}
|
|
147
|
+
/** Cold / rotated read: same parse as `readSessionLog`, then seed the entry. */
|
|
148
|
+
async full(filePath, stat) {
|
|
149
|
+
let content;
|
|
150
|
+
try {
|
|
151
|
+
content = await fs.readFile(filePath, 'utf-8');
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
if (err.code === 'ENOENT') {
|
|
155
|
+
this.entries.delete(filePath);
|
|
156
|
+
return { path: filePath, events: [], issues: [], ok: true };
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
const lines = content.split('\n');
|
|
161
|
+
const parsed = parseSessionLogLines(lines);
|
|
162
|
+
// Count the lines this read consumed: the split's trailing '' is the
|
|
163
|
+
// artificial piece after the final '\n'; a torn tail is already parsed.
|
|
164
|
+
const linesConsumed = content.length === 0 ? 0 : content.endsWith('\n') ? lines.length - 1 : lines.length;
|
|
165
|
+
const entry = {
|
|
166
|
+
// Bytes actually read (never the pre-read stat: the log may have grown).
|
|
167
|
+
byteSize: Buffer.byteLength(content, 'utf8'),
|
|
168
|
+
mtimeMs: stat.mtimeMs,
|
|
169
|
+
events: parsed.events,
|
|
170
|
+
issues: parsed.issues,
|
|
171
|
+
expectedSeq: parsed.expected,
|
|
172
|
+
partial: '',
|
|
173
|
+
linesConsumed,
|
|
174
|
+
};
|
|
175
|
+
this.entries.set(filePath, entry);
|
|
176
|
+
return { path: filePath, events: entry.events.slice(), issues: entry.issues.slice(), ok: entry.issues.length === 0 };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Cached read of a session log. Without a cache — or with the kill switch off
|
|
181
|
+
* (the default in this merge) — this is `readSessionLog` verbatim.
|
|
182
|
+
*/
|
|
183
|
+
export async function readSessionLogCached(filePath, cache, env = process.env) {
|
|
184
|
+
if (!cache || !isReplayCacheEnabled(env))
|
|
185
|
+
return readSessionLog(filePath);
|
|
186
|
+
return cache.read(filePath);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=replayCache.js.map
|