@esankhan3/anvil-agent-core 0.3.0 → 0.4.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/__tests__/gemini-adapter-recording.test.d.ts +11 -0
- package/dist/__tests__/gemini-adapter-recording.test.d.ts.map +1 -0
- package/dist/__tests__/gemini-adapter-recording.test.js +108 -0
- package/dist/__tests__/gemini-adapter-recording.test.js.map +1 -0
- package/dist/__tests__/legacy-adapter-language-model.test.d.ts +11 -0
- package/dist/__tests__/legacy-adapter-language-model.test.d.ts.map +1 -0
- package/dist/__tests__/legacy-adapter-language-model.test.js +102 -0
- package/dist/__tests__/legacy-adapter-language-model.test.js.map +1 -0
- package/dist/__tests__/ollama-adapter-recording.test.d.ts +15 -0
- package/dist/__tests__/ollama-adapter-recording.test.d.ts.map +1 -0
- package/dist/__tests__/ollama-adapter-recording.test.js +128 -0
- package/dist/__tests__/ollama-adapter-recording.test.js.map +1 -0
- package/dist/__tests__/openrouter-prefill.integration.test.d.ts +19 -0
- package/dist/__tests__/openrouter-prefill.integration.test.d.ts.map +1 -0
- package/dist/__tests__/openrouter-prefill.integration.test.js +410 -0
- package/dist/__tests__/openrouter-prefill.integration.test.js.map +1 -0
- package/dist/__tests__/prefill-truncate.test.d.ts +8 -0
- package/dist/__tests__/prefill-truncate.test.d.ts.map +1 -0
- package/dist/__tests__/prefill-truncate.test.js +75 -0
- package/dist/__tests__/prefill-truncate.test.js.map +1 -0
- package/dist/__tests__/router-circuit-breaker.test.js +1 -1
- package/dist/__tests__/router-circuit-breaker.test.js.map +1 -1
- package/dist/__tests__/router-classify-unified.test.d.ts +12 -0
- package/dist/__tests__/router-classify-unified.test.d.ts.map +1 -0
- package/dist/__tests__/router-classify-unified.test.js +108 -0
- package/dist/__tests__/router-classify-unified.test.js.map +1 -0
- package/dist/__tests__/router-fallback.test.js +1 -1
- package/dist/__tests__/router-fallback.test.js.map +1 -1
- package/dist/__tests__/router-model-registry.test.js +24 -0
- package/dist/__tests__/router-model-registry.test.js.map +1 -1
- package/dist/__tests__/router-retry.test.js +3 -5
- package/dist/__tests__/router-retry.test.js.map +1 -1
- package/dist/__tests__/router-runagent.test.d.ts +14 -0
- package/dist/__tests__/router-runagent.test.d.ts.map +1 -0
- package/dist/__tests__/router-runagent.test.js +95 -0
- package/dist/__tests__/router-runagent.test.js.map +1 -0
- package/dist/__tests__/router-scaffold.test.js +2 -1
- package/dist/__tests__/router-scaffold.test.js.map +1 -1
- package/dist/__tests__/router-spend-ledger.test.js +1 -1
- package/dist/__tests__/router-spend-ledger.test.js.map +1 -1
- package/dist/__tests__/router-stream-fallback.test.d.ts +13 -0
- package/dist/__tests__/router-stream-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/router-stream-fallback.test.js +129 -0
- package/dist/__tests__/router-stream-fallback.test.js.map +1 -0
- package/dist/__tests__/router-telemetry.test.js +3 -1
- package/dist/__tests__/router-telemetry.test.js.map +1 -1
- package/dist/__tests__/util/mock-upstream.d.ts +77 -0
- package/dist/__tests__/util/mock-upstream.d.ts.map +1 -0
- package/dist/__tests__/util/mock-upstream.js +219 -0
- package/dist/__tests__/util/mock-upstream.js.map +1 -0
- package/dist/adk-adapter.d.ts.map +1 -1
- package/dist/adk-adapter.js +51 -0
- package/dist/adk-adapter.js.map +1 -1
- package/dist/agent/session/adapter.d.ts +15 -0
- package/dist/agent/session/adapter.d.ts.map +1 -1
- package/dist/agent/session/adapter.js +3 -0
- package/dist/agent/session/adapter.js.map +1 -1
- package/dist/agent/session/index.d.ts +1 -0
- package/dist/agent/session/index.d.ts.map +1 -1
- package/dist/agent/session/index.js +1 -0
- package/dist/agent/session/index.js.map +1 -1
- package/dist/agent/session/language-model-bridge.d.ts.map +1 -1
- package/dist/agent/session/language-model-bridge.js +22 -0
- package/dist/agent/session/language-model-bridge.js.map +1 -1
- package/dist/agent/session/legacy-adapter-language-model.d.ts +27 -0
- package/dist/agent/session/legacy-adapter-language-model.d.ts.map +1 -0
- package/dist/agent/session/legacy-adapter-language-model.js +221 -0
- package/dist/agent/session/legacy-adapter-language-model.js.map +1 -0
- package/dist/agent/session/types.d.ts +20 -0
- package/dist/agent/session/types.d.ts.map +1 -1
- package/dist/agent/session/types.js.map +1 -1
- package/dist/claude-adapter.d.ts.map +1 -1
- package/dist/claude-adapter.js +27 -0
- package/dist/claude-adapter.js.map +1 -1
- package/dist/gemini-adapter.d.ts.map +1 -1
- package/dist/gemini-adapter.js +46 -0
- package/dist/gemini-adapter.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/ollama-adapter.d.ts.map +1 -1
- package/dist/ollama-adapter.js +118 -8
- package/dist/ollama-adapter.js.map +1 -1
- package/dist/openrouter-adapter.d.ts.map +1 -1
- package/dist/openrouter-adapter.js +289 -15
- package/dist/openrouter-adapter.js.map +1 -1
- package/dist/prefill/strip.d.ts +49 -0
- package/dist/prefill/strip.d.ts.map +1 -0
- package/dist/prefill/strip.js +93 -0
- package/dist/prefill/strip.js.map +1 -0
- package/dist/prefill/translate.d.ts +55 -0
- package/dist/prefill/translate.d.ts.map +1 -0
- package/dist/prefill/translate.js +162 -0
- package/dist/prefill/translate.js.map +1 -0
- package/dist/prefill/truncate.d.ts +62 -0
- package/dist/prefill/truncate.d.ts.map +1 -0
- package/dist/prefill/truncate.js +77 -0
- package/dist/prefill/truncate.js.map +1 -0
- package/dist/router/errors.d.ts +24 -7
- package/dist/router/errors.d.ts.map +1 -1
- package/dist/router/errors.js +71 -14
- package/dist/router/errors.js.map +1 -1
- package/dist/router/index.d.ts +3 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/model-registry.d.ts +22 -0
- package/dist/router/model-registry.d.ts.map +1 -1
- package/dist/router/model-registry.js +64 -1
- package/dist/router/model-registry.js.map +1 -1
- package/dist/router/provider-registry-resolver.d.ts +25 -0
- package/dist/router/provider-registry-resolver.d.ts.map +1 -0
- package/dist/router/provider-registry-resolver.js +84 -0
- package/dist/router/provider-registry-resolver.js.map +1 -0
- package/dist/router/retry.d.ts +3 -0
- package/dist/router/retry.d.ts.map +1 -1
- package/dist/router/retry.js +4 -1
- package/dist/router/retry.js.map +1 -1
- package/dist/router/router.d.ts +79 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +256 -2
- package/dist/router/router.js.map +1 -1
- package/dist/router/types.d.ts +1 -1
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.js +1 -0
- package/dist/router/types.js.map +1 -1
- package/dist/turn-recorder/__tests__/turn-recorder.test.d.ts +20 -0
- package/dist/turn-recorder/__tests__/turn-recorder.test.d.ts.map +1 -0
- package/dist/turn-recorder/__tests__/turn-recorder.test.js +282 -0
- package/dist/turn-recorder/__tests__/turn-recorder.test.js.map +1 -0
- package/dist/turn-recorder/hash.d.ts +10 -0
- package/dist/turn-recorder/hash.d.ts.map +1 -0
- package/dist/turn-recorder/hash.js +35 -0
- package/dist/turn-recorder/hash.js.map +1 -0
- package/dist/turn-recorder/index.d.ts +119 -0
- package/dist/turn-recorder/index.d.ts.map +1 -0
- package/dist/turn-recorder/index.js +326 -0
- package/dist/turn-recorder/index.js.map +1 -0
- package/dist/turn-recorder/types.d.ts +227 -0
- package/dist/turn-recorder/types.d.ts.map +1 -0
- package/dist/turn-recorder/types.js +20 -0
- package/dist/turn-recorder/types.js.map +1 -0
- package/dist/types.d.ts +35 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for TurnRecorder — v2 ADR §2.1 / §2.5.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* a) startTurn records `assistant-start` WITHOUT an input-hash guard, so
|
|
6
|
+
* a resume whose prompt drifted (live-requeried KB/memory, cross-vendor
|
|
7
|
+
* continuation) replays the recorded transcript instead of aborting.
|
|
8
|
+
* b) Concurrent recorder instances do not cross-pollinate turn counters.
|
|
9
|
+
* c) runTool records `tool_use:N` AND `tool_result:N`, executes the
|
|
10
|
+
* exec callback exactly once on live, returns its NeutralToolResult.
|
|
11
|
+
* d) Replay path — when the underlying runtime returns recorded
|
|
12
|
+
* payloads, runTool short-circuits the executor (exec never fires).
|
|
13
|
+
* e) endTurn records `assistant-end` with provenance + usage.
|
|
14
|
+
* f) flushPartial emits an AssistantPartial through the sink with the
|
|
15
|
+
* same turnUuid that startTurn minted; idempotent across calls.
|
|
16
|
+
* g) NullTurnRecorder factory produces a recorder whose calls all
|
|
17
|
+
* complete cleanly with no persisted side effects.
|
|
18
|
+
*/
|
|
19
|
+
import { describe, it } from 'node:test';
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
21
|
+
import { TurnRecorder, createNullTurnRecorder, createNullEffectRuntime, } from '../index.js';
|
|
22
|
+
/** Test-double EffectRuntime that records every call and supports
|
|
23
|
+
* pre-seeding replay payloads keyed by effect name. */
|
|
24
|
+
function buildFakeRuntime(seed = {}) {
|
|
25
|
+
const log = [];
|
|
26
|
+
const runtime = {
|
|
27
|
+
async effect(name, fn, opts) {
|
|
28
|
+
if (name in seed) {
|
|
29
|
+
log.push({ name, opts, fnRan: false, payload: seed[name] });
|
|
30
|
+
return seed[name];
|
|
31
|
+
}
|
|
32
|
+
const payload = await fn();
|
|
33
|
+
log.push({ name, opts, fnRan: true, payload });
|
|
34
|
+
return payload;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
return { runtime, log };
|
|
38
|
+
}
|
|
39
|
+
describe('TurnRecorder', () => {
|
|
40
|
+
it('records assistant-start WITHOUT an input-hash guard (resume tolerates prompt drift)', async () => {
|
|
41
|
+
const { runtime, log } = buildFakeRuntime();
|
|
42
|
+
const sink = [];
|
|
43
|
+
const recorder = new TurnRecorder({
|
|
44
|
+
runtime,
|
|
45
|
+
partialSink: (p) => { sink.push(p); },
|
|
46
|
+
runId: 'r1', stepId: 'step-a',
|
|
47
|
+
uuid: (() => { let n = 0; return () => `uuid-${n++}`; })(),
|
|
48
|
+
});
|
|
49
|
+
const { turn, turnUuid } = await recorder.startTurn({
|
|
50
|
+
model: 'openrouter/qwen3',
|
|
51
|
+
provider: 'openrouter',
|
|
52
|
+
system: 'sys',
|
|
53
|
+
messages: [{ role: 'user', content: 'hi' }],
|
|
54
|
+
});
|
|
55
|
+
assert.equal(turn, 0);
|
|
56
|
+
assert.equal(turnUuid, 'uuid-0');
|
|
57
|
+
assert.equal(log.length, 1);
|
|
58
|
+
assert.equal(log[0].name, 'turn:0:assistant-start');
|
|
59
|
+
// The turn's input (system + messages) embeds live-queried KB/memory that
|
|
60
|
+
// is rebuilt in a fresh process on resume, so it is NOT byte-stable. The
|
|
61
|
+
// assistant-start effect must NOT carry a fatal input-hash guard — else
|
|
62
|
+
// benign prompt drift aborts replay with a DeterminismViolationError and
|
|
63
|
+
// breaks resume. On replay the recorded transcript (assistant-end) is
|
|
64
|
+
// authoritative; the rebuilt prompt is never re-sent.
|
|
65
|
+
assert.equal(log[0].opts?.idempotencyKey, undefined, 'assistant-start must not be guarded by an input hash');
|
|
66
|
+
});
|
|
67
|
+
it('seeds the turn counter from deps.initialTurn (§2.5.1 resume)', async () => {
|
|
68
|
+
const { runtime, log } = buildFakeRuntime();
|
|
69
|
+
const recorder = new TurnRecorder({
|
|
70
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's',
|
|
71
|
+
initialTurn: 3, // prior adapter recorded turns 0..2
|
|
72
|
+
});
|
|
73
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
74
|
+
assert.equal(turn, 3, 'resumed recorder continues past the prior adapter, not from 0');
|
|
75
|
+
assert.equal(log[0].name, 'turn:3:assistant-start');
|
|
76
|
+
});
|
|
77
|
+
it('keeps per-recorder turn counters isolated', async () => {
|
|
78
|
+
const a = new TurnRecorder({
|
|
79
|
+
runtime: buildFakeRuntime().runtime,
|
|
80
|
+
partialSink: () => { },
|
|
81
|
+
runId: 'r', stepId: 's',
|
|
82
|
+
});
|
|
83
|
+
const b = new TurnRecorder({
|
|
84
|
+
runtime: buildFakeRuntime().runtime,
|
|
85
|
+
partialSink: () => { },
|
|
86
|
+
runId: 'r', stepId: 's',
|
|
87
|
+
});
|
|
88
|
+
await a.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
89
|
+
await a.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
90
|
+
const { turn: bTurn } = await b.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
91
|
+
assert.equal(bTurn, 0, 'second recorder starts at turn 0, ignoring first recorder');
|
|
92
|
+
});
|
|
93
|
+
it('runTool records tool_use AND tool_result, exec runs exactly once', async () => {
|
|
94
|
+
const { runtime, log } = buildFakeRuntime();
|
|
95
|
+
const recorder = new TurnRecorder({
|
|
96
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's',
|
|
97
|
+
});
|
|
98
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
99
|
+
let execCalls = 0;
|
|
100
|
+
const result = await recorder.runTool(turn, 'write_file', { path: 'x' }, 'idem-1', async () => {
|
|
101
|
+
execCalls += 1;
|
|
102
|
+
return { toolUseId: 'tc-1', toolName: 'write_file', ok: true, content: 'ok' };
|
|
103
|
+
});
|
|
104
|
+
assert.equal(execCalls, 1);
|
|
105
|
+
assert.equal(result.content, 'ok');
|
|
106
|
+
const names = log.map((e) => e.name);
|
|
107
|
+
assert.deepEqual(names, [
|
|
108
|
+
'turn:0:assistant-start',
|
|
109
|
+
'turn:0:tool_use:0',
|
|
110
|
+
'turn:0:tool_result:0',
|
|
111
|
+
]);
|
|
112
|
+
assert.equal(log[1].opts?.idempotencyKey, 'idem-1');
|
|
113
|
+
});
|
|
114
|
+
it('replay short-circuits the executor — recorded tool_result wins', async () => {
|
|
115
|
+
const recordedResult = {
|
|
116
|
+
toolUseId: 'tc-1', toolName: 'write_file', ok: true, content: 'recorded-output',
|
|
117
|
+
};
|
|
118
|
+
const { runtime, log } = buildFakeRuntime({
|
|
119
|
+
'turn:0:tool_result:0': recordedResult,
|
|
120
|
+
});
|
|
121
|
+
const recorder = new TurnRecorder({
|
|
122
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's',
|
|
123
|
+
});
|
|
124
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
125
|
+
let execCalls = 0;
|
|
126
|
+
const result = await recorder.runTool(turn, 'write_file', { path: 'x' }, 'idem-1', async () => {
|
|
127
|
+
execCalls += 1;
|
|
128
|
+
return { toolUseId: 'tc-1', toolName: 'write_file', ok: true, content: 'LIVE-VALUE' };
|
|
129
|
+
});
|
|
130
|
+
assert.equal(execCalls, 0, 'exec must NOT fire when tool_result is replayed');
|
|
131
|
+
assert.equal(result.content, 'recorded-output');
|
|
132
|
+
// tool_use was still recorded structurally; only tool_result was replayed.
|
|
133
|
+
assert.equal(log.find((e) => e.name === 'turn:0:tool_result:0')?.fnRan, false);
|
|
134
|
+
});
|
|
135
|
+
it('endTurn records assistant-end with provenance + usage', async () => {
|
|
136
|
+
const { runtime, log } = buildFakeRuntime();
|
|
137
|
+
const recorder = new TurnRecorder({
|
|
138
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's',
|
|
139
|
+
});
|
|
140
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
141
|
+
await recorder.endTurn(turn, 'completed text', 'end_turn', { inputTokens: 12, outputTokens: 34 }, { segments: [{ model: 'm', provider: 'openrouter', range: [0, 14], source: 'live' }] });
|
|
142
|
+
const end = log.find((e) => e.name === 'turn:0:assistant-end');
|
|
143
|
+
assert.ok(end, 'assistant-end must be recorded');
|
|
144
|
+
assert.deepEqual(end.payload.usage, { inputTokens: 12, outputTokens: 34 });
|
|
145
|
+
assert.equal(end.payload.stopReason, 'end_turn');
|
|
146
|
+
});
|
|
147
|
+
it('flushPartial emits to sink with the turn\'s minted turnUuid', async () => {
|
|
148
|
+
const { runtime } = buildFakeRuntime();
|
|
149
|
+
const sink = [];
|
|
150
|
+
const recorder = new TurnRecorder({
|
|
151
|
+
runtime,
|
|
152
|
+
partialSink: (p) => { sink.push(p); },
|
|
153
|
+
runId: 'run-x', stepId: 'step-y',
|
|
154
|
+
uuid: () => 'fixed-uuid',
|
|
155
|
+
nowIso: () => '2026-05-25T00:00:00Z',
|
|
156
|
+
});
|
|
157
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
158
|
+
recorder.flushPartial(turn, 'partial text here', 2, 'upstream');
|
|
159
|
+
assert.equal(sink.length, 1);
|
|
160
|
+
assert.deepEqual(sink[0], {
|
|
161
|
+
runId: 'run-x',
|
|
162
|
+
stepId: 'step-y',
|
|
163
|
+
turnUuid: 'fixed-uuid',
|
|
164
|
+
turn: 0,
|
|
165
|
+
text: 'partial text here',
|
|
166
|
+
toolUsesEmitted: 2,
|
|
167
|
+
reason: 'upstream',
|
|
168
|
+
recordedAt: '2026-05-25T00:00:00Z',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
it('flushPartial is a no-op when startTurn was never called', () => {
|
|
172
|
+
const sink = [];
|
|
173
|
+
const recorder = new TurnRecorder({
|
|
174
|
+
runtime: createNullEffectRuntime(),
|
|
175
|
+
partialSink: (p) => { sink.push(p); },
|
|
176
|
+
runId: 'r', stepId: 's',
|
|
177
|
+
});
|
|
178
|
+
recorder.flushPartial(0, 'stale', 0, 'abort');
|
|
179
|
+
assert.equal(sink.length, 0);
|
|
180
|
+
});
|
|
181
|
+
it('startTurn reconstructs `replayed` from a recorded assistant-end via peekRecorded (§H3)', async () => {
|
|
182
|
+
// Simulate a turn already in the durable log: assistant-end + one
|
|
183
|
+
// completed tool. peekRecorded returns these; the recorder must
|
|
184
|
+
// reconstruct the full AssistantTurn so the adapter skips the network.
|
|
185
|
+
const recorded = {
|
|
186
|
+
'turn:0:assistant-start': { turnUuid: 'u', model: 'm', provider: 'openrouter' },
|
|
187
|
+
'turn:0:tool_use:0': { name: 'write_file', arguments: { path: 'a.txt' }, idempotencyKey: 'k0' },
|
|
188
|
+
'turn:0:tool_result:0': { toolUseId: 'tc0', toolName: 'write_file', ok: true, content: 'wrote' },
|
|
189
|
+
'turn:0:assistant-end': {
|
|
190
|
+
text: 'all done',
|
|
191
|
+
stopReason: 'end_turn',
|
|
192
|
+
usage: { inputTokens: 11, outputTokens: 7 },
|
|
193
|
+
provenance: { segments: [{ model: 'm', provider: 'openrouter', range: [0, 8], source: 'live' }] },
|
|
194
|
+
historyDelta: [{ role: 'assistant', content: 'all done' }],
|
|
195
|
+
model: 'm',
|
|
196
|
+
provider: 'openrouter',
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
const runtime = {
|
|
200
|
+
async effect(name, fn) {
|
|
201
|
+
return (name in recorded ? recorded[name] : await fn());
|
|
202
|
+
},
|
|
203
|
+
peekRecorded(name) {
|
|
204
|
+
return (name in recorded ? recorded[name] : undefined);
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
const recorder = new TurnRecorder({
|
|
208
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's', uuid: () => 'u',
|
|
209
|
+
});
|
|
210
|
+
const { turn, turnUuid, replayed } = await recorder.startTurn({
|
|
211
|
+
model: 'm', provider: 'openrouter', messages: [{ role: 'user', content: 'hi' }],
|
|
212
|
+
});
|
|
213
|
+
assert.equal(turn, 0);
|
|
214
|
+
assert.equal(turnUuid, 'u');
|
|
215
|
+
assert.ok(replayed, 'assistant-end present → replayed reconstructed');
|
|
216
|
+
assert.equal(replayed.text, 'all done');
|
|
217
|
+
assert.equal(replayed.stopReason, 'end_turn');
|
|
218
|
+
assert.deepEqual(replayed.usage, { inputTokens: 11, outputTokens: 7 });
|
|
219
|
+
assert.equal(replayed.toolUses.length, 1);
|
|
220
|
+
assert.deepEqual(replayed.toolUses[0], {
|
|
221
|
+
id: 'tc0', name: 'write_file', arguments: { path: 'a.txt' }, idempotencyKey: 'k0',
|
|
222
|
+
});
|
|
223
|
+
assert.equal(replayed.toolResults.length, 1);
|
|
224
|
+
assert.equal(replayed.toolResults[0].content, 'wrote');
|
|
225
|
+
assert.deepEqual(replayed.historyDelta, [{ role: 'assistant', content: 'all done' }]);
|
|
226
|
+
});
|
|
227
|
+
it('startTurn does NOT reconstruct when assistant-end is absent (live path)', async () => {
|
|
228
|
+
// Only assistant-start recorded — the turn never finished. No replay.
|
|
229
|
+
const recorded = {
|
|
230
|
+
'turn:0:assistant-start': { turnUuid: 'u', model: 'm', provider: 'openrouter' },
|
|
231
|
+
};
|
|
232
|
+
const runtime = {
|
|
233
|
+
async effect(name, fn) {
|
|
234
|
+
return (name in recorded ? recorded[name] : await fn());
|
|
235
|
+
},
|
|
236
|
+
peekRecorded(name) {
|
|
237
|
+
return (name in recorded ? recorded[name] : undefined);
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
const recorder = new TurnRecorder({
|
|
241
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's', uuid: () => 'u',
|
|
242
|
+
});
|
|
243
|
+
const { replayed } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
244
|
+
assert.equal(replayed, undefined, 'no assistant-end → no replay, adapter runs live');
|
|
245
|
+
});
|
|
246
|
+
it('endTurn records historyDelta + model/provider into the assistant-end payload (§H3)', async () => {
|
|
247
|
+
const recorded = new Map();
|
|
248
|
+
const runtime = {
|
|
249
|
+
async effect(name, fn) {
|
|
250
|
+
const v = await fn();
|
|
251
|
+
recorded.set(name, v);
|
|
252
|
+
return v;
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
const recorder = new TurnRecorder({
|
|
256
|
+
runtime, partialSink: () => { }, runId: 'r', stepId: 's', uuid: () => 'u',
|
|
257
|
+
});
|
|
258
|
+
const { turn } = await recorder.startTurn({ model: 'qwen/q', provider: 'openrouter', messages: [] });
|
|
259
|
+
const delta = [{ role: 'assistant', content: 'x', tool_calls: [{ id: 'tc' }] }];
|
|
260
|
+
await recorder.endTurn(turn, 'x', 'tool_use', { inputTokens: 3, outputTokens: 1 }, { segments: [] }, delta);
|
|
261
|
+
const end = recorded.get('turn:0:assistant-end');
|
|
262
|
+
assert.deepEqual(end.historyDelta, delta);
|
|
263
|
+
assert.equal(end.model, 'qwen/q');
|
|
264
|
+
assert.equal(end.provider, 'openrouter');
|
|
265
|
+
});
|
|
266
|
+
it('NullTurnRecorder factory yields a working recorder that persists nothing', async () => {
|
|
267
|
+
const recorder = createNullTurnRecorder();
|
|
268
|
+
const { turn } = await recorder.startTurn({ model: 'm', provider: 'openrouter', messages: [] });
|
|
269
|
+
let execCalls = 0;
|
|
270
|
+
const result = await recorder.runTool(turn, 'noop', {}, 'idem', async () => {
|
|
271
|
+
execCalls += 1;
|
|
272
|
+
return { toolUseId: 't', toolName: 'noop', ok: true, content: 'ran' };
|
|
273
|
+
});
|
|
274
|
+
await recorder.endTurn(turn, 'text', 'end_turn', { inputTokens: 1, outputTokens: 1 }, { segments: [] });
|
|
275
|
+
recorder.flushPartial(turn, '', 0, 'abort');
|
|
276
|
+
// All structural calls succeed; exec ran live because there's no
|
|
277
|
+
// replay log; no persistence side-effect we can observe.
|
|
278
|
+
assert.equal(execCalls, 1);
|
|
279
|
+
assert.equal(result.content, 'ran');
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
//# sourceMappingURL=turn-recorder.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-recorder.test.js","sourceRoot":"","sources":["../../../src/turn-recorder/__tests__/turn-recorder.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAiBrB;wDACwD;AACxD,SAAS,gBAAgB,CAAC,OAAgC,EAAE;IAI1D,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAsB;QACjC,KAAK,CAAC,MAAM,CAAI,IAAY,EAAE,EAAoB,EAAE,IAA0B;YAC5E,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBACjB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC5D,OAAO,IAAI,CAAC,IAAI,CAAM,CAAC;YACzB,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,EAAE,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/C,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAC1B,CAAC;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAuB,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO;YACP,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ;YAC7B,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;SAC3D,CAAC,CAAC;QAEH,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;YAClD,KAAK,EAAE,kBAAkB;YACzB,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;SAC5C,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACtB,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;QACpD,0EAA0E;QAC1E,yEAAyE;QACzE,wEAAwE;QACxE,yEAAyE;QACzE,sEAAsE;QACtE,sDAAsD;QACtD,MAAM,CAAC,KAAK,CACV,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,EAC3B,SAAS,EACT,sDAAsD,CACvD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;YACvD,WAAW,EAAE,CAAC,EAAE,oCAAoC;SACrD,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAChG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,+DAA+D,CAAC,CAAC;QACvF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC;YACzB,OAAO,EAAE,gBAAgB,EAAE,CAAC,OAAO;YACnC,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;YACrB,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC;YACzB,OAAO,EAAE,gBAAgB,EAAE,CAAC,OAAO;YACnC,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC;YACrB,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxB,CAAC,CAAC;QAEH,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAEhG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,2DAA2D,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxD,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAEhG,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC5F,SAAS,IAAI,CAAC,CAAC;YACf,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE;YACtB,wBAAwB;YACxB,mBAAmB;YACnB,sBAAsB;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,cAAc,GAAsB;YACxC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB;SAChF,CAAC;QACF,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,CAAC;YACxC,sBAAsB,EAAE,cAAc;SACvC,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxD,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAEhG,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC5F,SAAS,IAAI,CAAC,CAAC;YACf,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QACxF,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,EAAE,iDAAiD,CAAC,CAAC;QAC9E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QAChD,2EAA2E;QAC3E,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxD,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAEhG,MAAM,QAAQ,CAAC,OAAO,CACpB,IAAI,EACJ,gBAAgB,EAChB,UAAU,EACV,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,EACrC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CACvF,CAAC;QAEF,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,CAAC;QAC/D,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;QACjD,MAAM,CAAC,SAAS,CAAE,GAAI,CAAC,OAA8B,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,KAAK,CAAE,GAAI,CAAC,OAAkC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAuB,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO;YACP,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ;YAChC,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY;YACxB,MAAM,EAAE,GAAG,EAAE,CAAC,sBAAsB;SACrC,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAEhG,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,mBAAmB,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;QAEhE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YACxB,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,mBAAmB;YACzB,eAAe,EAAE,CAAC;YAClB,MAAM,EAAE,UAAU;YAClB,UAAU,EAAE,sBAAsB;SACnC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAuB,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,uBAAuB,EAAE;YAClC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;SACxB,CAAC,CAAC;QACH,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;QACtG,kEAAkE;QAClE,gEAAgE;QAChE,uEAAuE;QACvE,MAAM,QAAQ,GAA4B;YACxC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE;YAC/E,mBAAmB,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;YAC/F,sBAAsB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;YAChG,sBAAsB,EAAE;gBACtB,IAAI,EAAE,UAAU;gBAChB,UAAU,EAAE,UAAU;gBACtB,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE;gBAC3C,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE;gBACjG,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;gBAC1D,KAAK,EAAE,GAAG;gBACV,QAAQ,EAAE,YAAY;aACvB;SACF,CAAC;QACF,MAAM,OAAO,GAAsB;YACjC,KAAK,CAAC,MAAM,CAAI,IAAY,EAAE,EAAoB;gBAChD,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAM,CAAC;YAC/D,CAAC;YACD,YAAY,CAAc,IAAY;gBACpC,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAkB,CAAC;YAC1E,CAAC;SACF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG;SACzE,CAAC,CAAC;QAEH,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC;YAC5D,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;SAChF,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACtB,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,gDAAgD,CAAC,CAAC;QACtE,MAAM,CAAC,KAAK,CAAC,QAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,QAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC/C,MAAM,CAAC,SAAS,CAAC,QAAS,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,KAAK,CAAC,QAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,QAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YACtC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,IAAI;SAClF,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,QAAS,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,QAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,CAAC,SAAS,CAAC,QAAS,CAAC,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,sEAAsE;QACtE,MAAM,QAAQ,GAA4B;YACxC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE;SAChF,CAAC;QACF,MAAM,OAAO,GAAsB;YACjC,KAAK,CAAC,MAAM,CAAI,IAAY,EAAE,EAAoB;gBAChD,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAM,CAAC;YAC/D,CAAC;YACD,YAAY,CAAc,IAAY;gBACpC,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAkB,CAAC;YAC1E,CAAC;SACF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG;SACzE,CAAC,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,iDAAiD,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QAClG,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC5C,MAAM,OAAO,GAAsB;YACjC,KAAK,CAAC,MAAM,CAAI,IAAY,EAAE,EAAoB;gBAChD,MAAM,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtB,OAAO,CAAC,CAAC;YACX,CAAC;SACF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC;YAChC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG;SACzE,CAAC,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACrG,MAAM,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAChF,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QAE5G,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,sBAAsB,CAA4B,CAAC;QAC5E,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,QAAQ,GAAG,sBAAsB,EAAE,CAAC;QAC1C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAChG,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE;YACzE,SAAS,IAAI,CAAC,CAAC;YACf,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACxE,CAAC,CAAC,CAAC;QACH,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACxG,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5C,iEAAiE;QACjE,yDAAyD;QACzD,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content hashing for replay idempotency keys.
|
|
3
|
+
*
|
|
4
|
+
* Stable JSON-stringify with sorted object keys → sha256 hex. Mirrors
|
|
5
|
+
* core-pipeline's `effect-helpers.ts:contentHash` semantics so an
|
|
6
|
+
* idempotency key computed inside agent-core round-trips against the
|
|
7
|
+
* durable engine's own checks.
|
|
8
|
+
*/
|
|
9
|
+
export declare function contentHashFromArgs(value: unknown): string;
|
|
10
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/turn-recorder/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE1D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content hashing for replay idempotency keys.
|
|
3
|
+
*
|
|
4
|
+
* Stable JSON-stringify with sorted object keys → sha256 hex. Mirrors
|
|
5
|
+
* core-pipeline's `effect-helpers.ts:contentHash` semantics so an
|
|
6
|
+
* idempotency key computed inside agent-core round-trips against the
|
|
7
|
+
* durable engine's own checks.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
export function contentHashFromArgs(value) {
|
|
11
|
+
return createHash('sha256').update(stableStringify(value)).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
function stableStringify(value) {
|
|
14
|
+
if (value === null)
|
|
15
|
+
return 'null';
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return 'undefined';
|
|
18
|
+
if (typeof value === 'number')
|
|
19
|
+
return Number.isFinite(value) ? String(value) : 'null';
|
|
20
|
+
if (typeof value === 'boolean')
|
|
21
|
+
return value ? 'true' : 'false';
|
|
22
|
+
if (typeof value === 'string')
|
|
23
|
+
return JSON.stringify(value);
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'object') {
|
|
28
|
+
const keys = Object.keys(value).sort();
|
|
29
|
+
const entries = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`);
|
|
30
|
+
return `{${entries.join(',')}}`;
|
|
31
|
+
}
|
|
32
|
+
// Symbols, functions, bigints fall through as their string form.
|
|
33
|
+
return JSON.stringify(String(value));
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/turn-recorder/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACtF,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACrD,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,eAAe,CAAE,KAAiC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,CAAC;IACD,iEAAiE;IACjE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TurnRecorder — splits one LLM invocation into per-turn sub-effects so
|
|
3
|
+
* the durable engine can replay tool results and resume mid-stream after
|
|
4
|
+
* a chain-fallback model swap.
|
|
5
|
+
*
|
|
6
|
+
* Effect-name vocabulary (per the v2 ADR §2.1):
|
|
7
|
+
* - `turn:<N>:assistant-start` — request payload (recorded, NOT input-hash-guarded: see startTurn)
|
|
8
|
+
* - `turn:<N>:tool_use:<M>` — intent (name + parsed args)
|
|
9
|
+
* - `turn:<N>:tool_result:<M>` — neutral result; replay short-circuits the executor
|
|
10
|
+
* - `turn:<N>:assistant-end` — completed assistant text + provenance
|
|
11
|
+
*
|
|
12
|
+
* Surface note vs ADR §2.5: the ADR sketches recordToolUse / recordToolResult
|
|
13
|
+
* as two separate calls. That split has no clean replay-vs-execute boundary
|
|
14
|
+
* through the `effect(name, fn)` interface — once the runtime accepts a fn
|
|
15
|
+
* for `tool_result:N`, fn already either ran (live) or didn't (replay), and
|
|
16
|
+
* the caller can't tell from outside. So the implementation collapses to a
|
|
17
|
+
* single `runTool(turn, name, args, key, exec)` that owns the
|
|
18
|
+
* intent-record → execute-or-replay → result-record sequence atomically.
|
|
19
|
+
* Same on-disk shape; cleaner adapter code; ADR §2.5 to be amended in
|
|
20
|
+
* follow-up.
|
|
21
|
+
*
|
|
22
|
+
* NullTurnRecorder factory: pass a `createNullRuntime()`-built runtime to
|
|
23
|
+
* make every call a no-op pass-through with live execution. Adapters that
|
|
24
|
+
* haven't been ported through their own H-phase use this default, so the
|
|
25
|
+
* recorder calls compile and run today without behavior change.
|
|
26
|
+
*/
|
|
27
|
+
import type { AssistantStartRequest, AssistantTurn, EffectRuntimeLike, NeutralToolResult, PartialReason, PartialSink, Provenance, TurnRecorderDeps, TurnTokenUsage } from './types.js';
|
|
28
|
+
export declare class TurnRecorder {
|
|
29
|
+
private readonly deps;
|
|
30
|
+
/** Monotonic turn counter, scoped to this recorder (one per adapter.run).
|
|
31
|
+
* Seeded from deps.initialTurn so a sendInput-resume recorder continues
|
|
32
|
+
* past the prior adapter's turns (§2.5.1). */
|
|
33
|
+
private turnCounter;
|
|
34
|
+
/** Per-turn tool_use index counter — bumped serially per call. */
|
|
35
|
+
private readonly toolIdxByTurn;
|
|
36
|
+
/** Per-turn uuid stash so flushPartial can echo the right one. */
|
|
37
|
+
private readonly turnUuids;
|
|
38
|
+
/** Per-turn (model, provider) stash from startTurn so endTurn can
|
|
39
|
+
* stamp the assistant-end payload — the H3 cost rollup prices each
|
|
40
|
+
* turn by its authoring model. */
|
|
41
|
+
private readonly turnMeta;
|
|
42
|
+
/** Per-repo effect-name scope (e.g. `service-a:`); '' for single-stage. */
|
|
43
|
+
private readonly prefix;
|
|
44
|
+
constructor(deps: TurnRecorderDeps);
|
|
45
|
+
/** Scoped effect name for a turn sub-effect. */
|
|
46
|
+
private key;
|
|
47
|
+
/**
|
|
48
|
+
* Open a new assistant turn. Records the request payload as an
|
|
49
|
+
* `assistant-start` effect; on replay, returns the recorded turn's
|
|
50
|
+
* full transcript so the adapter can skip the upstream call entirely.
|
|
51
|
+
*/
|
|
52
|
+
startTurn(req: AssistantStartRequest): Promise<{
|
|
53
|
+
turn: number;
|
|
54
|
+
turnUuid: string;
|
|
55
|
+
/** Present iff the matching `assistant-end` is already in the log. */
|
|
56
|
+
replayed?: AssistantTurn;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Peek the durable log for a fully-recorded turn N. Returns the
|
|
60
|
+
* reconstructed `AssistantTurn` iff `turn:N:assistant-end` exists;
|
|
61
|
+
* undefined on the live path (peek unsupported, or end not yet
|
|
62
|
+
* recorded). Pure reads only — no effect cursor is advanced here.
|
|
63
|
+
*/
|
|
64
|
+
private tryReconstructTurn;
|
|
65
|
+
/**
|
|
66
|
+
* Record a tool intent + execute (or replay) the tool. Combines what
|
|
67
|
+
* the ADR drafted as two calls into one atomic sequence. The
|
|
68
|
+
* `idempotencyKey` is hashed by the caller (or fed in pre-computed)
|
|
69
|
+
* and guards against args drift on replay.
|
|
70
|
+
*
|
|
71
|
+
* Replay behavior: the inner `tool_result:M` effect is replayed
|
|
72
|
+
* verbatim — `exec()` is NOT invoked. Side effects (file writes, bash
|
|
73
|
+
* runs) stay as the prior model left them.
|
|
74
|
+
*/
|
|
75
|
+
runTool(turn: number, name: string, args: Record<string, unknown>, idempotencyKey: string, exec: () => Promise<NeutralToolResult>): Promise<NeutralToolResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Close an assistant turn with the completed text, stop reason, usage,
|
|
78
|
+
* and provenance. Records `assistant-end`; on replay this is a no-op.
|
|
79
|
+
*/
|
|
80
|
+
endTurn(turn: number, text: string, stopReason: string, usage: TurnTokenUsage, provenance: Provenance,
|
|
81
|
+
/**
|
|
82
|
+
* Provider-native messages appended to history this turn (assistant
|
|
83
|
+
* + tool-result messages). Recorded so crash-resume re-appends them
|
|
84
|
+
* byte-for-byte (§ AssistantTurn.historyDelta). Default [] for the
|
|
85
|
+
* terminal no-tool turn (nothing appended). Opaque to the recorder.
|
|
86
|
+
*/
|
|
87
|
+
historyDelta?: unknown[]): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Flush an in-progress assistant text to the partial sink. Called from
|
|
90
|
+
* an adapter's catch block BEFORE re-throwing UpstreamError. Does NOT
|
|
91
|
+
* go through `effect()` — partial events are not subject to the
|
|
92
|
+
* (name, idx) counter; they're durable signals scoped by turnUuid.
|
|
93
|
+
*
|
|
94
|
+
* Safe to call when text is empty (sink decides whether to bother).
|
|
95
|
+
* Safe to call multiple times for the same turn — the sink handles
|
|
96
|
+
* dedup via turnUuid + recordedAt.
|
|
97
|
+
*/
|
|
98
|
+
flushPartial(turn: number, text: string, toolUsesEmitted: number, reason: PartialReason): void;
|
|
99
|
+
private bumpToolIdx;
|
|
100
|
+
private uuid;
|
|
101
|
+
private nowIso;
|
|
102
|
+
}
|
|
103
|
+
export declare function createNullEffectRuntime(): EffectRuntimeLike;
|
|
104
|
+
export declare function createNullPartialSink(): PartialSink;
|
|
105
|
+
/**
|
|
106
|
+
* Convenience factory: a TurnRecorder that does the right structural
|
|
107
|
+
* thing (records → executes → emits) but persists nothing. Adapters use
|
|
108
|
+
* this when their caller hasn't supplied a real runtime + sink. Identical
|
|
109
|
+
* observable behavior to today's adapters; opens the door for the H2+
|
|
110
|
+
* wiring to inject the real instances without touching adapter code
|
|
111
|
+
* again.
|
|
112
|
+
*/
|
|
113
|
+
export declare function createNullTurnRecorder(opts?: {
|
|
114
|
+
runId?: string;
|
|
115
|
+
stepId?: string;
|
|
116
|
+
}): TurnRecorder;
|
|
117
|
+
export type { TurnRecorderDeps } from './types.js';
|
|
118
|
+
export * from './types.js';
|
|
119
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/turn-recorder/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAEV,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,UAAU,EAEV,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAY,CAAC;AAsDpB,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC;;mDAE+C;IAC/C,OAAO,CAAC,WAAW,CAAS;IAC5B,kEAAkE;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,kEAAkE;IAClE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD;;uCAEmC;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgE;IAEzF,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,IAAI,EAAE,gBAAgB;IAMlC,gDAAgD;IAChD,OAAO,CAAC,GAAG;IAIX;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC;QACnD,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,sEAAsE;QACtE,QAAQ,CAAC,EAAE,aAAa,CAAC;KAC1B,CAAC;IAoDF;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IA8C1B;;;;;;;;;OASG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,GACrC,OAAO,CAAC,iBAAiB,CAAC;IA2B7B;;;OAGG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,UAAU;IACtB;;;;;OAKG;IACH,YAAY,GAAE,OAAO,EAAO,GAC3B,OAAO,CAAC,IAAI,CAAC;IAmBhB;;;;;;;;;OASG;IACH,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,aAAa,GACpB,IAAI;IA4BP,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,MAAM;CAGf;AAUD,wBAAgB,uBAAuB,IAAI,iBAAiB,CAM3D;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,CAAC,EAAE;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,YAAY,CAOf;AAED,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,cAAc,YAAY,CAAC"}
|