zelari-code 1.46.1 → 1.47.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/cli/app.js +4 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/budget/tokenBudget.js +14 -12
- package/dist/cli/budget/tokenBudget.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +7 -4
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/observationStore.js +219 -0
- package/dist/cli/hooks/observationStore.js.map +1 -0
- package/dist/cli/hooks/observationStore.test.js +135 -0
- package/dist/cli/hooks/observationStore.test.js.map +1 -0
- package/dist/cli/hooks/sessionSurface.js +171 -0
- package/dist/cli/hooks/sessionSurface.js.map +1 -0
- package/dist/cli/hooks/sessionSurface.test.js +112 -0
- package/dist/cli/hooks/sessionSurface.test.js.map +1 -0
- package/dist/cli/hooks/useChatTurn.js +25 -0
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/hooks/useSession.js +5 -3
- package/dist/cli/hooks/useSession.js.map +1 -1
- package/dist/cli/main.bundled.js +1680 -808
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/metrics.js +4 -0
- package/dist/cli/metrics.js.map +1 -1
- package/dist/cli/provider/capabilities.js +53 -0
- package/dist/cli/provider/capabilities.js.map +1 -0
- package/dist/cli/provider/capabilities.test.js +46 -0
- package/dist/cli/provider/capabilities.test.js.map +1 -0
- package/dist/cli/provider/openai-compatible.js +2 -1
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/toolRegistry.js +28 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/observeBatch.js +254 -0
- package/dist/cli/tools/observeBatch.js.map +1 -0
- package/dist/cli/tools/observeBatch.test.js +329 -0
- package/dist/cli/tools/observeBatch.test.js.map +1 -0
- package/dist/cli/tools/retrieveObservation.js +82 -0
- package/dist/cli/tools/retrieveObservation.js.map +1 -0
- package/dist/cli/utils/contextGrowthSummary.js +55 -0
- package/dist/cli/utils/contextGrowthSummary.js.map +1 -0
- package/dist/cli/utils/contextGrowthSummary.test.js +74 -0
- package/dist/cli/utils/contextGrowthSummary.test.js.map +1 -0
- package/dist/cli/utils/doctor.js +26 -0
- package/dist/cli/utils/doctor.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* observationStore + retrieve_observation — Fase 2 acceptance.
|
|
3
|
+
*/
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { promises as fs } from 'node:fs';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { SessionJsonlWriter } from '@zelari/core/harness';
|
|
9
|
+
import { createBrainEvent } from '@zelari/core/events';
|
|
10
|
+
import { getObservationBySeq, getSeqForToolCallId, indexEventsForTests, ingestLiveEvent, invalidateObservationIndex, loadObservationIndex, } from './observationStore.js';
|
|
11
|
+
import { formatObservationStub } from './sessionSurface.js';
|
|
12
|
+
import { createRetrieveObservationTool, parseSeqRef } from '../tools/retrieveObservation.js';
|
|
13
|
+
const abort = new AbortController().signal;
|
|
14
|
+
function ctx(sessionId) {
|
|
15
|
+
return {
|
|
16
|
+
signal: abort,
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
sessionId,
|
|
19
|
+
audit: () => undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
describe('parseSeqRef', () => {
|
|
23
|
+
it('accepts number, #N and ref=#N', () => {
|
|
24
|
+
expect(parseSeqRef(12)).toBe(12);
|
|
25
|
+
expect(parseSeqRef('#12')).toBe(12);
|
|
26
|
+
expect(parseSeqRef('ref=#12')).toBe(12);
|
|
27
|
+
expect(parseSeqRef('OBSERVATION ref=#3')).toBe(3);
|
|
28
|
+
expect(parseSeqRef('nope')).toBeNull();
|
|
29
|
+
expect(parseSeqRef(0)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('observationStore from JSONL', () => {
|
|
33
|
+
let dir;
|
|
34
|
+
afterEach(async () => {
|
|
35
|
+
invalidateObservationIndex();
|
|
36
|
+
if (dir)
|
|
37
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
it('assigns monotonic 1-based seq in file order and rematerializes', async () => {
|
|
40
|
+
dir = await fs.mkdtemp(path.join(os.tmpdir(), 'zelari-obs-'));
|
|
41
|
+
const sid = 'sess-a';
|
|
42
|
+
const w = new SessionJsonlWriter(sid, { baseDir: dir, flushIntervalMs: 60_000 });
|
|
43
|
+
const start1 = createBrainEvent('tool_execution_start', sid, {
|
|
44
|
+
toolCallId: 'c1',
|
|
45
|
+
toolName: 'read_file',
|
|
46
|
+
args: { path: 'a.ts' },
|
|
47
|
+
});
|
|
48
|
+
const end1 = createBrainEvent('tool_execution_end', sid, {
|
|
49
|
+
toolCallId: 'c1',
|
|
50
|
+
result: 'FILE-ONE-BODY',
|
|
51
|
+
isError: false,
|
|
52
|
+
durationMs: 11,
|
|
53
|
+
});
|
|
54
|
+
const start2 = createBrainEvent('tool_execution_start', sid, {
|
|
55
|
+
toolCallId: 'c2',
|
|
56
|
+
toolName: 'grep_content',
|
|
57
|
+
args: { pattern: 'x' },
|
|
58
|
+
});
|
|
59
|
+
const end2 = createBrainEvent('tool_execution_end', sid, {
|
|
60
|
+
toolCallId: 'c2',
|
|
61
|
+
result: 'FILE-TWO-BODY',
|
|
62
|
+
isError: false,
|
|
63
|
+
durationMs: 22,
|
|
64
|
+
});
|
|
65
|
+
void w.append(start1);
|
|
66
|
+
void w.append(end1);
|
|
67
|
+
void w.append(start2);
|
|
68
|
+
void w.append(end2);
|
|
69
|
+
await w.flush();
|
|
70
|
+
const index = await loadObservationIndex(sid, dir);
|
|
71
|
+
expect(index.bySeq.size).toBe(2);
|
|
72
|
+
expect(await getSeqForToolCallId(sid, 'c2', dir)).toBe(2);
|
|
73
|
+
const rec = await getObservationBySeq(sid, 1, dir);
|
|
74
|
+
expect(rec?.result).toBe('FILE-ONE-BODY');
|
|
75
|
+
expect(rec?.toolName).toBe('read_file');
|
|
76
|
+
const tool = createRetrieveObservationTool({ sessionId: sid, baseDir: dir });
|
|
77
|
+
const hit = await tool.execute({ seq: '#2' }, ctx(sid));
|
|
78
|
+
expect(hit.ok).toBe(true);
|
|
79
|
+
if (hit.ok) {
|
|
80
|
+
expect(hit.value.result).toBe('FILE-TWO-BODY');
|
|
81
|
+
expect(hit.value.tool).toBe('grep_content');
|
|
82
|
+
expect(hit.value.seq).toBe(2);
|
|
83
|
+
}
|
|
84
|
+
const miss = await tool.execute({ seq: 99 }, ctx(sid));
|
|
85
|
+
expect(miss.ok).toBe(false);
|
|
86
|
+
await w.close();
|
|
87
|
+
});
|
|
88
|
+
it('ingestLiveEvent is idempotent and retrieve works before flush', async () => {
|
|
89
|
+
const sid = 'live-sess';
|
|
90
|
+
const start = createBrainEvent('tool_execution_start', sid, {
|
|
91
|
+
toolCallId: 'live-1',
|
|
92
|
+
toolName: 'list_files',
|
|
93
|
+
args: {},
|
|
94
|
+
});
|
|
95
|
+
const end = createBrainEvent('tool_execution_end', sid, {
|
|
96
|
+
toolCallId: 'live-1',
|
|
97
|
+
result: 'LIVE-BODY',
|
|
98
|
+
isError: false,
|
|
99
|
+
durationMs: 5,
|
|
100
|
+
});
|
|
101
|
+
ingestLiveEvent(sid, start);
|
|
102
|
+
ingestLiveEvent(sid, end);
|
|
103
|
+
ingestLiveEvent(sid, end); // idempotent
|
|
104
|
+
const rec = await getObservationBySeq(sid, 1);
|
|
105
|
+
expect(rec?.result).toBe('LIVE-BODY');
|
|
106
|
+
expect(await getSeqForToolCallId(sid, 'live-1')).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
it('refuses to rematerialize a stub that leaked into the log', async () => {
|
|
109
|
+
const sid = 'stub-sess';
|
|
110
|
+
const stub = formatObservationStub({ seq: 1, tool: 'read_file', bytes: 10 });
|
|
111
|
+
indexEventsForTests(sid, [
|
|
112
|
+
createBrainEvent('tool_execution_end', sid, {
|
|
113
|
+
toolCallId: 's1',
|
|
114
|
+
result: stub,
|
|
115
|
+
isError: false,
|
|
116
|
+
durationMs: 1,
|
|
117
|
+
}),
|
|
118
|
+
]);
|
|
119
|
+
const tool = createRetrieveObservationTool({ sessionId: sid, baseDir: os.tmpdir() });
|
|
120
|
+
const res = await tool.execute({ seq: 1 }, ctx(sid));
|
|
121
|
+
expect(res.ok).toBe(false);
|
|
122
|
+
if (!res.ok)
|
|
123
|
+
expect(res.meta?.warnings).toContain('STUB_IN_LOG');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('retrieve_observation session guard', () => {
|
|
127
|
+
it('fails clearly without a real session id', async () => {
|
|
128
|
+
const tool = createRetrieveObservationTool();
|
|
129
|
+
const res = await tool.execute({ seq: 1 }, ctx('cli'));
|
|
130
|
+
expect(res.ok).toBe(false);
|
|
131
|
+
if (!res.ok)
|
|
132
|
+
expect(res.meta?.warnings).toContain('NO_SESSION');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=observationStore.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observationStore.test.js","sourceRoot":"","sources":["../../../src/cli/hooks/observationStore.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { envNumber } from '../utils/envNumber.js';
|
|
2
|
+
/** Marker prefix — also used by retrieve_observation to refuse rematerializing a stub. */
|
|
3
|
+
export const OBSERVATION_STUB_PREFIX = 'OBSERVATION ref=#';
|
|
4
|
+
export const DEFAULT_HOT_TAIL = 4;
|
|
5
|
+
export const DEFAULT_MAX_BODY_CHARS = 4_000;
|
|
6
|
+
/** Kill switch: ZELARI_SESSION_SURFACE=0 restores the pre-Fase-2 pruner. */
|
|
7
|
+
export function sessionSurfaceEnabled() {
|
|
8
|
+
return process.env.ZELARI_SESSION_SURFACE !== '0';
|
|
9
|
+
}
|
|
10
|
+
/** True when a tool-result body is already a Fase-2 stub (never un-prune). */
|
|
11
|
+
export function isObservationStub(content) {
|
|
12
|
+
return content.startsWith(OBSERVATION_STUB_PREFIX);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse `OBSERVATION ref=#N …` into fields. Returns null if the body is not a stub.
|
|
16
|
+
* Extra key=value pairs after the known ones are preserved as `extra`.
|
|
17
|
+
*/
|
|
18
|
+
export function parseObservationStub(content) {
|
|
19
|
+
if (!isObservationStub(content))
|
|
20
|
+
return null;
|
|
21
|
+
const first = content.split('\n', 1)[0] ?? content;
|
|
22
|
+
const seqMatch = first.match(/^OBSERVATION ref=#(\d+)\b/);
|
|
23
|
+
if (!seqMatch)
|
|
24
|
+
return null;
|
|
25
|
+
const fields = { seq: Number(seqMatch[1]) };
|
|
26
|
+
const tool = first.match(/\btool=([A-Za-z0-9._-]+)/);
|
|
27
|
+
if (tool)
|
|
28
|
+
fields.tool = tool[1];
|
|
29
|
+
const status = first.match(/\bstatus=([A-Za-z0-9._-]+)/);
|
|
30
|
+
if (status)
|
|
31
|
+
fields.status = status[1];
|
|
32
|
+
const bytes = first.match(/\bbytes=(\d+)/);
|
|
33
|
+
if (bytes)
|
|
34
|
+
fields.bytes = Number(bytes[1]);
|
|
35
|
+
const known = /^(ref|#\d+|tool|status|bytes)$/;
|
|
36
|
+
const extras = [];
|
|
37
|
+
for (const m of first.matchAll(/\b([A-Za-z_][A-Za-z0-9._-]*)=([^\s]+)/g)) {
|
|
38
|
+
if (!known.test(m[1]))
|
|
39
|
+
extras.push(`${m[1]}=${m[2]}`);
|
|
40
|
+
}
|
|
41
|
+
if (extras.length)
|
|
42
|
+
fields.extra = extras.join(' ');
|
|
43
|
+
return fields;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Deterministic stub. Same fields → byte-identical string (cache-safe).
|
|
47
|
+
* `seq` is the 1-based index of this tool_execution_end in the session log.
|
|
48
|
+
*/
|
|
49
|
+
export function formatObservationStub(fields) {
|
|
50
|
+
const parts = [`${OBSERVATION_STUB_PREFIX}${fields.seq}`];
|
|
51
|
+
if (fields.tool)
|
|
52
|
+
parts.push(`tool=${fields.tool}`);
|
|
53
|
+
if (fields.status)
|
|
54
|
+
parts.push(`status=${fields.status}`);
|
|
55
|
+
if (fields.bytes !== undefined)
|
|
56
|
+
parts.push(`bytes=${fields.bytes}`);
|
|
57
|
+
if (fields.extra)
|
|
58
|
+
parts.push(fields.extra);
|
|
59
|
+
return `${parts.join(' ')}\n[retrieve_observation #${fields.seq}]`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract Ground-Truth status from a tool-result body (Fase 0 footer), if any.
|
|
63
|
+
* Footer shape: `\n[observation status=complete filesWalked=12 …]`
|
|
64
|
+
*/
|
|
65
|
+
export function statusFromToolBody(body) {
|
|
66
|
+
const m = body.match(/\[observation\s+([^\]]+)\]\s*$/);
|
|
67
|
+
if (!m)
|
|
68
|
+
return undefined;
|
|
69
|
+
const status = m[1].match(/\bstatus=([A-Za-z0-9._-]+)/);
|
|
70
|
+
return status?.[1];
|
|
71
|
+
}
|
|
72
|
+
function resolveSurfaceLimits(opts) {
|
|
73
|
+
return {
|
|
74
|
+
hotTail: opts?.hotTail ??
|
|
75
|
+
envNumber(process.env.ZELARI_SURFACE_HOT_TAIL, { default: DEFAULT_HOT_TAIL, min: 0 }),
|
|
76
|
+
maxBodyChars: opts?.maxBodyChars ??
|
|
77
|
+
envNumber(process.env.ZELARI_SURFACE_MAX_BODY_CHARS, {
|
|
78
|
+
default: DEFAULT_MAX_BODY_CHARS,
|
|
79
|
+
min: 64,
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the stable observation seq for a tool message.
|
|
85
|
+
*
|
|
86
|
+
* Preference order (all deterministic):
|
|
87
|
+
* 1. already a stub → keep its seq (monotone — never rewrite the number)
|
|
88
|
+
* 2. `lookup(toolCallId)` from the session log
|
|
89
|
+
* 3. 1-based index among role:'tool' messages in THIS history (offline / tests)
|
|
90
|
+
*/
|
|
91
|
+
export function resolveObservationSeq(message, indexAmongTools, lookup) {
|
|
92
|
+
const existing = parseObservationStub(message.content ?? '');
|
|
93
|
+
if (existing)
|
|
94
|
+
return existing.seq;
|
|
95
|
+
const id = message.toolCallId;
|
|
96
|
+
if (id && lookup) {
|
|
97
|
+
const fromLog = lookup(id);
|
|
98
|
+
if (typeof fromLog === 'number' && fromLog > 0)
|
|
99
|
+
return fromLog;
|
|
100
|
+
}
|
|
101
|
+
return indexAmongTools + 1;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Infer a tool name from the preceding assistant(tool_calls) that declared
|
|
105
|
+
* this toolCallId. Deterministic walk — no side tables.
|
|
106
|
+
*/
|
|
107
|
+
export function inferToolName(messages, toolIndex, toolCallId) {
|
|
108
|
+
if (!toolCallId)
|
|
109
|
+
return undefined;
|
|
110
|
+
for (let i = toolIndex - 1; i >= 0; i--) {
|
|
111
|
+
const m = messages[i];
|
|
112
|
+
if (m.role !== 'assistant' || !m.toolCalls)
|
|
113
|
+
continue;
|
|
114
|
+
const hit = m.toolCalls.find((c) => c.id === toolCallId);
|
|
115
|
+
if (hit)
|
|
116
|
+
return hit.name;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Project oversized / cold tool-result bodies onto addressable stubs.
|
|
122
|
+
*
|
|
123
|
+
* Invariants:
|
|
124
|
+
* - never mutates the input array or its messages
|
|
125
|
+
* - already-stubbed bodies are left byte-identical
|
|
126
|
+
* - same (messages, opts, lookup) → byte-identical output (cache-safe)
|
|
127
|
+
* - returns the SAME array reference when nothing changed
|
|
128
|
+
*/
|
|
129
|
+
export function projectSessionSurface(messages, opts, lookup) {
|
|
130
|
+
const { hotTail, maxBodyChars } = resolveSurfaceLimits(opts);
|
|
131
|
+
const stats = { projected: 0, charsOmitted: 0, alreadyStubbed: 0 };
|
|
132
|
+
const toolPositions = [];
|
|
133
|
+
for (let i = 0; i < messages.length; i++) {
|
|
134
|
+
if (messages[i].role === 'tool')
|
|
135
|
+
toolPositions.push(i);
|
|
136
|
+
}
|
|
137
|
+
if (toolPositions.length === 0) {
|
|
138
|
+
return { messages: messages, stats };
|
|
139
|
+
}
|
|
140
|
+
const hotStart = Math.max(0, toolPositions.length - hotTail);
|
|
141
|
+
const protect = new Set(toolPositions.slice(hotStart));
|
|
142
|
+
let changed = false;
|
|
143
|
+
const out = messages.map((m, i) => {
|
|
144
|
+
if (m.role !== 'tool')
|
|
145
|
+
return m;
|
|
146
|
+
const body = m.content ?? '';
|
|
147
|
+
if (isObservationStub(body)) {
|
|
148
|
+
stats.alreadyStubbed += 1;
|
|
149
|
+
return m;
|
|
150
|
+
}
|
|
151
|
+
if (protect.has(i) || body.length <= maxBodyChars)
|
|
152
|
+
return m;
|
|
153
|
+
const toolOrdinal = toolPositions.indexOf(i);
|
|
154
|
+
const seq = resolveObservationSeq(m, toolOrdinal, lookup);
|
|
155
|
+
const stub = formatObservationStub({
|
|
156
|
+
seq,
|
|
157
|
+
tool: inferToolName(messages, i, m.toolCallId),
|
|
158
|
+
status: statusFromToolBody(body),
|
|
159
|
+
bytes: body.length,
|
|
160
|
+
});
|
|
161
|
+
changed = true;
|
|
162
|
+
stats.projected += 1;
|
|
163
|
+
stats.charsOmitted += Math.max(0, body.length - stub.length);
|
|
164
|
+
return { ...m, content: stub };
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
messages: changed ? out : messages,
|
|
168
|
+
stats,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=sessionSurface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionSurface.js","sourceRoot":"","sources":["../../../src/cli/hooks/sessionSurface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sessionSurface — Fase 2 acceptance: deterministic projection, monotone
|
|
3
|
+
* stubs, byte-identical replay, no un-pruning.
|
|
4
|
+
*/
|
|
5
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
6
|
+
import { formatObservationStub, isObservationStub, parseObservationStub, projectSessionSurface, resolveObservationSeq, statusFromToolBody, } from './sessionSurface.js';
|
|
7
|
+
function tool(id, content) {
|
|
8
|
+
return { role: 'tool', toolCallId: id, content };
|
|
9
|
+
}
|
|
10
|
+
function assistantCall(id, name) {
|
|
11
|
+
return { role: 'assistant', content: '', toolCalls: [{ id, name, args: {} }] };
|
|
12
|
+
}
|
|
13
|
+
describe('observation stub codec', () => {
|
|
14
|
+
it('round-trips fields byte-identically', () => {
|
|
15
|
+
const stub = formatObservationStub({
|
|
16
|
+
seq: 12,
|
|
17
|
+
tool: 'grep_content',
|
|
18
|
+
status: 'complete',
|
|
19
|
+
bytes: 4096,
|
|
20
|
+
});
|
|
21
|
+
expect(stub).toBe('OBSERVATION ref=#12 tool=grep_content status=complete bytes=4096\n[retrieve_observation #12]');
|
|
22
|
+
expect(isObservationStub(stub)).toBe(true);
|
|
23
|
+
expect(parseObservationStub(stub)).toEqual({
|
|
24
|
+
seq: 12,
|
|
25
|
+
tool: 'grep_content',
|
|
26
|
+
status: 'complete',
|
|
27
|
+
bytes: 4096,
|
|
28
|
+
});
|
|
29
|
+
expect(formatObservationStub(parseObservationStub(stub))).toBe(stub);
|
|
30
|
+
});
|
|
31
|
+
it('rejects non-stubs', () => {
|
|
32
|
+
expect(isObservationStub('hello')).toBe(false);
|
|
33
|
+
expect(parseObservationStub('hello')).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
it('reads Fase-0 footer status', () => {
|
|
36
|
+
expect(statusFromToolBody('body\n[observation status=partial filesWalked=3]')).toBe('partial');
|
|
37
|
+
expect(statusFromToolBody('plain')).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('projectSessionSurface', () => {
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
delete process.env.ZELARI_SURFACE_HOT_TAIL;
|
|
43
|
+
delete process.env.ZELARI_SURFACE_MAX_BODY_CHARS;
|
|
44
|
+
delete process.env.ZELARI_SESSION_SURFACE;
|
|
45
|
+
});
|
|
46
|
+
it('returns the same reference when nothing needs projecting', () => {
|
|
47
|
+
const msgs = [
|
|
48
|
+
{ role: 'user', content: 'hi' },
|
|
49
|
+
tool('c1', 'tiny'),
|
|
50
|
+
];
|
|
51
|
+
const r = projectSessionSurface(msgs, { hotTail: 4, maxBodyChars: 4000 });
|
|
52
|
+
expect(r.messages).toBe(msgs);
|
|
53
|
+
expect(r.stats.projected).toBe(0);
|
|
54
|
+
});
|
|
55
|
+
it('keeps the hot tail full-fidelity and stubs older oversized bodies', () => {
|
|
56
|
+
const big = 'B'.repeat(500);
|
|
57
|
+
const msgs = [];
|
|
58
|
+
for (let i = 1; i <= 6; i++) {
|
|
59
|
+
msgs.push(assistantCall(`c${i}`, 'read_file'));
|
|
60
|
+
msgs.push(tool(`c${i}`, big + `\n[observation status=complete bytes=${big.length}]`));
|
|
61
|
+
}
|
|
62
|
+
const r = projectSessionSurface(msgs, { hotTail: 2, maxBodyChars: 100 });
|
|
63
|
+
expect(r.stats.projected).toBe(4);
|
|
64
|
+
const tools = r.messages.filter((m) => m.role === 'tool');
|
|
65
|
+
expect(tools.slice(0, 4).every((m) => isObservationStub(m.content))).toBe(true);
|
|
66
|
+
expect(tools.slice(4).every((m) => !isObservationStub(m.content))).toBe(true);
|
|
67
|
+
expect(tools[0].content).toContain('tool=read_file');
|
|
68
|
+
expect(tools[0].content).toContain('status=complete');
|
|
69
|
+
expect(tools[0].content).toContain('[retrieve_observation #1]');
|
|
70
|
+
});
|
|
71
|
+
it('is deterministic: same input → byte-identical surface', () => {
|
|
72
|
+
const big = 'X'.repeat(800);
|
|
73
|
+
const msgs = [
|
|
74
|
+
assistantCall('a', 'grep_content'),
|
|
75
|
+
tool('a', big),
|
|
76
|
+
assistantCall('b', 'grep_content'),
|
|
77
|
+
tool('b', big),
|
|
78
|
+
assistantCall('c', 'list_files'),
|
|
79
|
+
tool('c', big),
|
|
80
|
+
];
|
|
81
|
+
const a = projectSessionSurface(msgs, { hotTail: 1, maxBodyChars: 50 });
|
|
82
|
+
const b = projectSessionSurface(msgs, { hotTail: 1, maxBodyChars: 50 });
|
|
83
|
+
expect(JSON.stringify(a.messages)).toBe(JSON.stringify(b.messages));
|
|
84
|
+
expect(a.stats).toEqual(b.stats);
|
|
85
|
+
});
|
|
86
|
+
it('never un-prunes an existing stub (monotone / cache-safe)', () => {
|
|
87
|
+
const stub = formatObservationStub({ seq: 7, tool: 'read_file', bytes: 9999 });
|
|
88
|
+
const msgs = [tool('old', stub), tool('hot', 'H'.repeat(50))];
|
|
89
|
+
const r = projectSessionSurface(msgs, { hotTail: 4, maxBodyChars: 10 });
|
|
90
|
+
expect(r.messages[0].content).toBe(stub);
|
|
91
|
+
expect(r.stats.alreadyStubbed).toBe(1);
|
|
92
|
+
expect(r.stats.projected).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
it('uses lookup(toolCallId) for stable seq when provided', () => {
|
|
95
|
+
const big = 'Z'.repeat(200);
|
|
96
|
+
const msgs = [
|
|
97
|
+
assistantCall('call-z', 'bash'),
|
|
98
|
+
tool('call-z', big),
|
|
99
|
+
assistantCall('keep', 'bash'),
|
|
100
|
+
tool('keep', big),
|
|
101
|
+
];
|
|
102
|
+
const lookup = (id) => (id === 'call-z' ? 42 : undefined);
|
|
103
|
+
const r = projectSessionSurface(msgs, { hotTail: 1, maxBodyChars: 10 }, lookup);
|
|
104
|
+
expect(r.messages[1].content).toContain('ref=#42');
|
|
105
|
+
expect(r.messages[1].content).toContain('[retrieve_observation #42]');
|
|
106
|
+
});
|
|
107
|
+
it('falls back to 1-based ordinal when lookup misses', () => {
|
|
108
|
+
const seq = resolveObservationSeq(tool('x', 'body'), 3);
|
|
109
|
+
expect(seq).toBe(4);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=sessionSurface.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionSurface.test.js","sourceRoot":"","sources":["../../../src/cli/hooks/sessionSurface.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// from app.tsx. Runtime is correct; tighten signatures in a follow-up.
|
|
3
3
|
import { useState, useRef, useCallback } from "react";
|
|
4
4
|
import { AgentHarness } from "@zelari/core/harness";
|
|
5
|
+
import { ingestLiveEvent } from "./observationStore.js";
|
|
5
6
|
import { getMetricsLogger } from "../metrics.js";
|
|
6
7
|
import { calculateCost } from "../modelPricing.js";
|
|
7
8
|
import { providerFromEnv, providerConfigFor, resolveActiveProvider, } from "../provider/openai-compatible.js";
|
|
@@ -538,6 +539,8 @@ export function useChatTurn(params) {
|
|
|
538
539
|
// matching start event (keyed by toolCallId) for metrics.
|
|
539
540
|
const toolNameById = new Map();
|
|
540
541
|
const metrics = getMetricsLogger();
|
|
542
|
+
// Fase M: last log-only context_metrics event (arrives before agent_end).
|
|
543
|
+
let lastGrowth = null;
|
|
541
544
|
let realUsage = null;
|
|
542
545
|
try {
|
|
543
546
|
for await (const event of harness.run()) {
|
|
@@ -580,6 +583,11 @@ export function useChatTurn(params) {
|
|
|
580
583
|
// to serialize the render loop with mkdir+appendFile per delta.
|
|
581
584
|
// Turn boundaries below call flush() so the tail is durable.
|
|
582
585
|
void writerRef.current.append(event);
|
|
586
|
+
if (sessionId)
|
|
587
|
+
ingestLiveEvent(sessionId, event);
|
|
588
|
+
}
|
|
589
|
+
if (event.type === "context_metrics") {
|
|
590
|
+
lastGrowth = event;
|
|
583
591
|
}
|
|
584
592
|
if (event.type === "agent_end") {
|
|
585
593
|
metrics.record({
|
|
@@ -589,6 +597,17 @@ export function useChatTurn(params) {
|
|
|
589
597
|
model: envConfig.model,
|
|
590
598
|
latencyMs: event.durationMs,
|
|
591
599
|
ok: event.reason === "stop",
|
|
600
|
+
// Fase M: context-growth counters (log-only event, folded here).
|
|
601
|
+
...(lastGrowth
|
|
602
|
+
? {
|
|
603
|
+
toolRoundTrips: lastGrowth.toolRoundTrips,
|
|
604
|
+
intermediateToolBytes: lastGrowth.intermediateToolBytes,
|
|
605
|
+
requests: lastGrowth.requests,
|
|
606
|
+
historyBytesAtRequest: lastGrowth.historyBytesLast,
|
|
607
|
+
historyBytesPeak: lastGrowth.historyBytesPeak,
|
|
608
|
+
cacheHitTokens: lastGrowth.cacheHitTokens,
|
|
609
|
+
}
|
|
610
|
+
: {}),
|
|
592
611
|
// v1.35: real usage landed on message_end (which precedes
|
|
593
612
|
// agent_end), so historical spend can be aggregated from
|
|
594
613
|
// metrics.jsonl — previously these fields were always absent.
|
|
@@ -1173,12 +1192,16 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
1173
1192
|
// Drain remaining events silently after the abort decision.
|
|
1174
1193
|
if (writerRef.current)
|
|
1175
1194
|
void writerRef.current.append(event);
|
|
1195
|
+
if (sessionId)
|
|
1196
|
+
ingestLiveEvent(sessionId, event);
|
|
1176
1197
|
continue;
|
|
1177
1198
|
}
|
|
1178
1199
|
if (writerRef.current) {
|
|
1179
1200
|
// Same batching as the single-agent path: fire-and-forget per event,
|
|
1180
1201
|
// flush at the turn boundary in `finally`.
|
|
1181
1202
|
void writerRef.current.append(event);
|
|
1203
|
+
if (sessionId)
|
|
1204
|
+
ingestLiveEvent(sessionId, event);
|
|
1182
1205
|
}
|
|
1183
1206
|
if (event.type === "council_mode") {
|
|
1184
1207
|
councilRunMode = event.runMode;
|
|
@@ -1682,6 +1705,8 @@ async function runZelariMissionInTui(userMessage, deps, emit) {
|
|
|
1682
1705
|
onEvent: async (event) => {
|
|
1683
1706
|
if (writerRef.current)
|
|
1684
1707
|
await writerRef.current.append(event);
|
|
1708
|
+
if (sessionId)
|
|
1709
|
+
ingestLiveEvent(sessionId, event);
|
|
1685
1710
|
if (event.type === "tool_execution_start") {
|
|
1686
1711
|
const name = event.toolName ?? "tool";
|
|
1687
1712
|
appendSystem(setMessages, `[build@kraken] → ${name}`, Date.now());
|