agentfootprint 7.5.0 → 7.6.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/README.md +20 -0
- package/dist/adapters/llm/BedrockProvider.js +113 -9
- package/dist/adapters/llm/BedrockProvider.js.map +1 -1
- package/dist/core/slots/buildMessagesSlot.js +19 -1
- package/dist/core/slots/buildMessagesSlot.js.map +1 -1
- package/dist/core/slots/buildSystemPromptSlot.js +19 -1
- package/dist/core/slots/buildSystemPromptSlot.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +27 -1
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/core/slots/helpers.js +39 -1
- package/dist/core/slots/helpers.js.map +1 -1
- package/dist/debug.js +10 -1
- package/dist/debug.js.map +1 -1
- package/dist/esm/adapters/llm/BedrockProvider.d.ts +48 -0
- package/dist/esm/adapters/llm/BedrockProvider.js +113 -9
- package/dist/esm/adapters/llm/BedrockProvider.js.map +1 -1
- package/dist/esm/core/slots/buildMessagesSlot.js +20 -2
- package/dist/esm/core/slots/buildMessagesSlot.js.map +1 -1
- package/dist/esm/core/slots/buildSystemPromptSlot.js +20 -2
- package/dist/esm/core/slots/buildSystemPromptSlot.js.map +1 -1
- package/dist/esm/core/slots/buildToolsSlot.js +28 -2
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/slots/helpers.d.ts +29 -1
- package/dist/esm/core/slots/helpers.js +36 -0
- package/dist/esm/core/slots/helpers.js.map +1 -1
- package/dist/esm/debug.d.ts +1 -0
- package/dist/esm/debug.js +8 -0
- package/dist/esm/debug.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +12 -1
- package/dist/esm/lib/recorded-chat/index.d.ts +11 -0
- package/dist/esm/lib/recorded-chat/index.js +11 -0
- package/dist/esm/lib/recorded-chat/index.js.map +1 -0
- package/dist/esm/lib/recorded-chat/recordedChat.d.ts +20 -0
- package/dist/esm/lib/recorded-chat/recordedChat.js +240 -0
- package/dist/esm/lib/recorded-chat/recordedChat.js.map +1 -0
- package/dist/esm/lib/recorded-chat/types.d.ts +182 -0
- package/dist/esm/lib/recorded-chat/types.js +31 -0
- package/dist/esm/lib/recorded-chat/types.js.map +1 -0
- package/dist/esm/recorders/core/types.d.ts +8 -1
- package/dist/esm/recorders/core/types.js.map +1 -1
- package/dist/lib/recorded-chat/index.js +15 -0
- package/dist/lib/recorded-chat/index.js.map +1 -0
- package/dist/lib/recorded-chat/recordedChat.js +244 -0
- package/dist/lib/recorded-chat/recordedChat.js.map +1 -0
- package/dist/lib/recorded-chat/types.js +32 -0
- package/dist/lib/recorded-chat/types.js.map +1 -0
- package/dist/recorders/core/types.js.map +1 -1
- package/dist/types/adapters/llm/BedrockProvider.d.ts +48 -0
- package/dist/types/adapters/llm/BedrockProvider.d.ts.map +1 -1
- package/dist/types/core/slots/buildMessagesSlot.d.ts.map +1 -1
- package/dist/types/core/slots/buildSystemPromptSlot.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/core/slots/helpers.d.ts +29 -1
- package/dist/types/core/slots/helpers.d.ts.map +1 -1
- package/dist/types/debug.d.ts +1 -0
- package/dist/types/debug.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +12 -1
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/lib/recorded-chat/index.d.ts +12 -0
- package/dist/types/lib/recorded-chat/index.d.ts.map +1 -0
- package/dist/types/lib/recorded-chat/recordedChat.d.ts +21 -0
- package/dist/types/lib/recorded-chat/recordedChat.d.ts.map +1 -0
- package/dist/types/lib/recorded-chat/types.d.ts +183 -0
- package/dist/types/lib/recorded-chat/types.d.ts.map +1 -0
- package/dist/types/recorders/core/types.d.ts +8 -1
- package/dist/types/recorders/core/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recordedChat — the session-scoped turn recorder.
|
|
3
|
+
*
|
|
4
|
+
* `recordedChat({ makeAgent })` returns a `RecordedChat`: `send()` runs one
|
|
5
|
+
* recorded turn through the consumer's agent factory and freezes that turn's
|
|
6
|
+
* evidence immediately; `reason(k)` / `rerunTurn(k)` / `fork(k)` are the
|
|
7
|
+
* per-turn transparency loop, composing with the existing context-bisect
|
|
8
|
+
* surface (`localizeContextBug`, `rerunWithoutSources`) rather than
|
|
9
|
+
* duplicating it. See `./types.ts` for the three traps this owns.
|
|
10
|
+
*
|
|
11
|
+
* Compose-don't-inherit house style: a module-private session class,
|
|
12
|
+
* factory-returned; no base classes.
|
|
13
|
+
*/
|
|
14
|
+
import type { RecordedChat, RecordedChatOptions } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Record a chat turn-by-turn so any reply can be explained, counterfactually
|
|
17
|
+
* re-run, and forked — without re-writing the correctness-critical glue every
|
|
18
|
+
* chat host gets subtly wrong. See the [recordedChat guide](doc:recorded-chat).
|
|
19
|
+
*/
|
|
20
|
+
export declare function recordedChat(options: RecordedChatOptions): RecordedChat;
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recordedChat — the session-scoped turn recorder.
|
|
3
|
+
*
|
|
4
|
+
* `recordedChat({ makeAgent })` returns a `RecordedChat`: `send()` runs one
|
|
5
|
+
* recorded turn through the consumer's agent factory and freezes that turn's
|
|
6
|
+
* evidence immediately; `reason(k)` / `rerunTurn(k)` / `fork(k)` are the
|
|
7
|
+
* per-turn transparency loop, composing with the existing context-bisect
|
|
8
|
+
* surface (`localizeContextBug`, `rerunWithoutSources`) rather than
|
|
9
|
+
* duplicating it. See `./types.ts` for the three traps this owns.
|
|
10
|
+
*
|
|
11
|
+
* Compose-don't-inherit house style: a module-private session class,
|
|
12
|
+
* factory-returned; no base classes.
|
|
13
|
+
*/
|
|
14
|
+
import { llmCallIdsFromEvents, localizeContextBug, rerunWithoutSources, } from '../context-bisect/index.js';
|
|
15
|
+
const DEFAULT_HEADER = 'Recent conversation:';
|
|
16
|
+
const DEFAULT_USER_LABEL = 'User';
|
|
17
|
+
const DEFAULT_ASSISTANT_LABEL = 'Assistant';
|
|
18
|
+
/** Deterministic key for spec dedup — recursively sorts object keys. */
|
|
19
|
+
function canonicalKey(value) {
|
|
20
|
+
return JSON.stringify(value, (_key, val) => {
|
|
21
|
+
if (val !== null && typeof val === 'object' && !Array.isArray(val)) {
|
|
22
|
+
const sorted = {};
|
|
23
|
+
for (const k of Object.keys(val).sort()) {
|
|
24
|
+
sorted[k] = val[k];
|
|
25
|
+
}
|
|
26
|
+
return sorted;
|
|
27
|
+
}
|
|
28
|
+
return val;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Union of ablation specs, deduped by canonical JSON (order-preserving, first wins). */
|
|
32
|
+
function dedupeSpecs(specs) {
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const spec of specs) {
|
|
36
|
+
const key = canonicalKey(spec);
|
|
37
|
+
if (seen.has(key))
|
|
38
|
+
continue;
|
|
39
|
+
seen.add(key);
|
|
40
|
+
out.push(spec);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
class RecordedChatSession {
|
|
45
|
+
makeAgentFn;
|
|
46
|
+
formatOpt;
|
|
47
|
+
header;
|
|
48
|
+
userLabel;
|
|
49
|
+
assistantLabel;
|
|
50
|
+
/** seed + one (user, assistant) pair per recorded turn. */
|
|
51
|
+
entries;
|
|
52
|
+
seedMessages;
|
|
53
|
+
removedSpecs;
|
|
54
|
+
turnsInternal = [];
|
|
55
|
+
reports = new Map();
|
|
56
|
+
/** result -> turnIndex, the in-process fork-provenance guard. */
|
|
57
|
+
rerunOwner = new WeakMap();
|
|
58
|
+
sending = false;
|
|
59
|
+
forkedFromValue = undefined;
|
|
60
|
+
constructor(options) {
|
|
61
|
+
this.makeAgentFn = options.makeAgent;
|
|
62
|
+
this.formatOpt = options.format;
|
|
63
|
+
this.header = options.format?.header ?? DEFAULT_HEADER;
|
|
64
|
+
this.userLabel = options.format?.userLabel ?? DEFAULT_USER_LABEL;
|
|
65
|
+
this.assistantLabel = options.format?.assistantLabel ?? DEFAULT_ASSISTANT_LABEL;
|
|
66
|
+
this.seedMessages = Object.freeze((options.seed ?? []).map((m) => ({ ...m })));
|
|
67
|
+
this.entries = [...this.seedMessages];
|
|
68
|
+
this.removedSpecs = Object.freeze([...(options.removed ?? [])]);
|
|
69
|
+
}
|
|
70
|
+
get turns() {
|
|
71
|
+
return this.turnsInternal;
|
|
72
|
+
}
|
|
73
|
+
get seed() {
|
|
74
|
+
return this.seedMessages;
|
|
75
|
+
}
|
|
76
|
+
get removed() {
|
|
77
|
+
return this.removedSpecs;
|
|
78
|
+
}
|
|
79
|
+
get forkedFrom() {
|
|
80
|
+
return this.forkedFromValue;
|
|
81
|
+
}
|
|
82
|
+
renderLine(message) {
|
|
83
|
+
const label = message.role === 'user' ? this.userLabel : this.assistantLabel;
|
|
84
|
+
return `${label}: ${message.text}`;
|
|
85
|
+
}
|
|
86
|
+
composeMessage(lines, userMessage) {
|
|
87
|
+
if (lines.length === 0)
|
|
88
|
+
return `${this.userLabel}: ${userMessage}`;
|
|
89
|
+
return `${this.header}\n${lines.join('\n')}\n\n${this.userLabel}: ${userMessage}`;
|
|
90
|
+
}
|
|
91
|
+
async send(userMessage, options) {
|
|
92
|
+
if (this.sending) {
|
|
93
|
+
throw new Error('recordedChat.send: a turn is already in flight — one turn at a time; await the previous send');
|
|
94
|
+
}
|
|
95
|
+
this.sending = true;
|
|
96
|
+
try {
|
|
97
|
+
// Compose the message ONCE, at record time, from the frozen transcript
|
|
98
|
+
// (trap 2 — byte-exact history is threaded HERE and replayed verbatim).
|
|
99
|
+
const transcriptBefore = Object.freeze(this.entries.map((m) => this.renderLine(m)));
|
|
100
|
+
const message = this.composeMessage(transcriptBefore, userMessage);
|
|
101
|
+
const agent = await this.makeAgentFn({ specs: this.removedSpecs, seed: 0 });
|
|
102
|
+
const events = [];
|
|
103
|
+
// Attach the tap BEFORE run() — listener-presence gating drops events
|
|
104
|
+
// otherwise (agentfootprint invariant). Unsubscribe in finally.
|
|
105
|
+
const off = agent.on('*', (event) => {
|
|
106
|
+
events.push(event);
|
|
107
|
+
});
|
|
108
|
+
let out;
|
|
109
|
+
try {
|
|
110
|
+
const raw = await agent.run({
|
|
111
|
+
message,
|
|
112
|
+
...(options?.identity !== undefined ? { identity: options.identity } : {}),
|
|
113
|
+
});
|
|
114
|
+
if (typeof raw !== 'string') {
|
|
115
|
+
throw new Error("recordedChat.send: the run paused (askHuman/checkIn) — recorded chat turns don't " +
|
|
116
|
+
'support pauses; drive pausing agents with agent.run() directly');
|
|
117
|
+
}
|
|
118
|
+
out = raw;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
off();
|
|
122
|
+
}
|
|
123
|
+
// Capture IMMEDIATELY (trap 1) — getLastSnapshot() is last-run-only, but
|
|
124
|
+
// the per-turn agent is discarded here, so clobbering is impossible.
|
|
125
|
+
const snapshot = agent.getLastSnapshot();
|
|
126
|
+
if (snapshot === undefined) {
|
|
127
|
+
throw new Error('recordedChat.send: the run produced no snapshot — cannot record the turn');
|
|
128
|
+
}
|
|
129
|
+
const lastLlmCallId = llmCallIdsFromEvents(events).at(-1);
|
|
130
|
+
const index = this.turnsInternal.length;
|
|
131
|
+
this.entries.push({ role: 'user', text: userMessage }, { role: 'assistant', text: out });
|
|
132
|
+
const turn = Object.freeze({
|
|
133
|
+
index,
|
|
134
|
+
userMessage,
|
|
135
|
+
reply: out,
|
|
136
|
+
message,
|
|
137
|
+
transcriptBefore,
|
|
138
|
+
artifacts: Object.freeze({ snapshot, events: Object.freeze(events) }),
|
|
139
|
+
lastLlmCallId,
|
|
140
|
+
identity: options?.identity,
|
|
141
|
+
});
|
|
142
|
+
this.turnsInternal.push(turn);
|
|
143
|
+
return turn;
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
this.sending = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
turn(k) {
|
|
150
|
+
const n = this.turnsInternal.length;
|
|
151
|
+
if (!Number.isInteger(k) || k < 0 || k >= n) {
|
|
152
|
+
const range = n === 0 ? '(no turns recorded yet)' : `0..${n - 1}`;
|
|
153
|
+
throw new RangeError(`recordedChat.turn: no turn ${k} — valid range is ${range}`);
|
|
154
|
+
}
|
|
155
|
+
return this.turnsInternal[k];
|
|
156
|
+
}
|
|
157
|
+
async reason(k, options) {
|
|
158
|
+
const turn = this.turn(k);
|
|
159
|
+
const { fresh, atStep, ...rest } = options;
|
|
160
|
+
const memo = this.reports.get(k);
|
|
161
|
+
if (memo !== undefined && fresh !== true)
|
|
162
|
+
return memo;
|
|
163
|
+
const resolvedAtStep = atStep ?? turn.lastLlmCallId;
|
|
164
|
+
const report = await localizeContextBug({
|
|
165
|
+
...rest,
|
|
166
|
+
artifacts: turn.artifacts,
|
|
167
|
+
...(resolvedAtStep !== undefined ? { atStep: resolvedAtStep } : {}),
|
|
168
|
+
});
|
|
169
|
+
this.reports.set(k, report);
|
|
170
|
+
return report;
|
|
171
|
+
}
|
|
172
|
+
async rerunTurn(k, options) {
|
|
173
|
+
const turn = this.turn(k);
|
|
174
|
+
const { report: reportOption, ...rerunRest } = options;
|
|
175
|
+
let report = reportOption ?? this.reports.get(k);
|
|
176
|
+
if (report === undefined) {
|
|
177
|
+
report = await this.reason(k, { embedder: options.embedder });
|
|
178
|
+
}
|
|
179
|
+
// Derive the runner from the SAME factory (traps 2 + 3): the frozen
|
|
180
|
+
// message bytes are replayed verbatim, and the session's persistent
|
|
181
|
+
// removals ALWAYS apply on top of the probe's specs (empty specs = the
|
|
182
|
+
// baseline probe, which replays the identical turn).
|
|
183
|
+
const runner = async (specs, run) => {
|
|
184
|
+
const agent = await this.makeAgentFn({
|
|
185
|
+
specs: [...this.removedSpecs, ...specs],
|
|
186
|
+
seed: run.seed,
|
|
187
|
+
});
|
|
188
|
+
const out = await agent.run({
|
|
189
|
+
message: turn.message,
|
|
190
|
+
...(turn.identity !== undefined ? { identity: turn.identity } : {}),
|
|
191
|
+
});
|
|
192
|
+
if (typeof out !== 'string') {
|
|
193
|
+
throw new Error('recordedChat.rerunTurn: a probe run paused (askHuman/checkIn) — recorded chat re-runs ' +
|
|
194
|
+
"don't support pauses");
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
};
|
|
198
|
+
const result = await rerunWithoutSources({
|
|
199
|
+
...rerunRest,
|
|
200
|
+
report,
|
|
201
|
+
runner,
|
|
202
|
+
originalAnswer: turn.reply,
|
|
203
|
+
});
|
|
204
|
+
this.rerunOwner.set(result, k);
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
fork(k, options) {
|
|
208
|
+
const turn = this.turn(k);
|
|
209
|
+
const fromRerun = options?.fromRerun;
|
|
210
|
+
if (fromRerun !== undefined && this.rerunOwner.get(fromRerun) !== k) {
|
|
211
|
+
throw new Error(`recordedChat.fork: that result was not produced by this session's rerunTurn for turn ${k} — ` +
|
|
212
|
+
're-run this turn first, then fork');
|
|
213
|
+
}
|
|
214
|
+
const reply = fromRerun?.answer ?? turn.reply;
|
|
215
|
+
const cut = this.seedMessages.length + 2 * k;
|
|
216
|
+
const seed = [
|
|
217
|
+
...this.entries.slice(0, cut).map((m) => ({ ...m })),
|
|
218
|
+
{ role: 'user', text: turn.userMessage },
|
|
219
|
+
{ role: 'assistant', text: reply },
|
|
220
|
+
];
|
|
221
|
+
const removed = dedupeSpecs([...this.removedSpecs, ...(fromRerun?.removed ?? [])]);
|
|
222
|
+
const child = new RecordedChatSession({
|
|
223
|
+
makeAgent: this.makeAgentFn,
|
|
224
|
+
...(this.formatOpt !== undefined ? { format: this.formatOpt } : {}),
|
|
225
|
+
seed,
|
|
226
|
+
removed,
|
|
227
|
+
});
|
|
228
|
+
child.forkedFromValue = { parent: this, turnIndex: k, viaRerun: fromRerun !== undefined };
|
|
229
|
+
return child;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Record a chat turn-by-turn so any reply can be explained, counterfactually
|
|
234
|
+
* re-run, and forked — without re-writing the correctness-critical glue every
|
|
235
|
+
* chat host gets subtly wrong. See the [recordedChat guide](doc:recorded-chat).
|
|
236
|
+
*/
|
|
237
|
+
export function recordedChat(options) {
|
|
238
|
+
return new RecordedChatSession(options);
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=recordedChat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordedChat.js","sourceRoot":"","sources":["../../../../src/lib/recorded-chat/recordedChat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recorded-chat types — the session-scoped turn recorder over the existing
|
|
3
|
+
* context-bisect product loop (localizeContextBug + rerunWithoutSources +
|
|
4
|
+
* removableSources).
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS: multi-turn chat on agentfootprint is a HOST convention —
|
|
7
|
+
* `AgentInput` has no history field, so hosts thread the transcript into the
|
|
8
|
+
* `message` string themselves. Every chat host that wanted per-turn
|
|
9
|
+
* transparency then re-wrote the same correctness-critical glue and got three
|
|
10
|
+
* things subtly wrong:
|
|
11
|
+
*
|
|
12
|
+
* 1. `agent.getLastSnapshot()` is last-run-only — a second `run()` clobbers
|
|
13
|
+
* it, so reasoning about turn K after turn K+1 ran silently attributes
|
|
14
|
+
* the wrong run.
|
|
15
|
+
* 2. History is a convention — the transcript preamble must be BYTE-identical
|
|
16
|
+
* between the recorded turn and its counterfactual re-run, or the re-run
|
|
17
|
+
* ablates a subtly different scenario without failing anything.
|
|
18
|
+
* 3. The `AblationRunner` duplicates turn construction — the re-run is only
|
|
19
|
+
* valid if the rebuilt turn matches the recorded one (same system, facts,
|
|
20
|
+
* preamble).
|
|
21
|
+
*
|
|
22
|
+
* `recordedChat({ makeAgent })` owns exactly those three: per-turn artifact
|
|
23
|
+
* freezing (1), byte-exact history threading (2), and deriving the turn-K
|
|
24
|
+
* runner from the SAME `makeAgent` that ran the turn (3). It COMPOSES with the
|
|
25
|
+
* 7.5 surface — `reason()` calls `localizeContextBug`, `rerunTurn()` delegates
|
|
26
|
+
* to `rerunWithoutSources` and returns its result UNMODIFIED (the honesty
|
|
27
|
+
* tiers are not hidden). Session registries, UI joins, comparators and
|
|
28
|
+
* persistence stay host-side by design (see the guide's "What stays yours").
|
|
29
|
+
*/
|
|
30
|
+
import type { RuntimeSnapshot } from 'footprintjs';
|
|
31
|
+
import type { Agent } from '../../core/Agent.js';
|
|
32
|
+
import type { MemoryIdentity } from '../../memory/identity/types.js';
|
|
33
|
+
import type { AblationSpec, CapturedEventLike, ContextBugReport, LocalizeContextBugOptions, RerunWithoutSourcesOptions, RerunWithoutSourcesResult } from '../context-bisect/index.js';
|
|
34
|
+
/**
|
|
35
|
+
* The ONE agent factory — live turns, rerun probes, baseline probes, and
|
|
36
|
+
* fork turns all go through it (the single source of truth the
|
|
37
|
+
* `AblationRunner` contract already demands).
|
|
38
|
+
*
|
|
39
|
+
* Contract (the `AblationRunner` contract, now enforced-by-shape for live
|
|
40
|
+
* turns too):
|
|
41
|
+
* - Return a FRESH agent with a FRESH provider on every call — scripted
|
|
42
|
+
* mock providers are stateful (replies consume in order).
|
|
43
|
+
* - Apply `specs` at CONSTRUCTION (the documented seam): filter facts /
|
|
44
|
+
* tools / memory entries with `applyAblations(specs, { ... })` before
|
|
45
|
+
* building. `specs` is ALWAYS the union of the session's persistent
|
|
46
|
+
* removals (forks carry these) and the probe's ablation specs; it is
|
|
47
|
+
* empty for a plain live turn in an unforked session.
|
|
48
|
+
* - `seed` varies 0..N-1 across a probe's samples (always 0 for live
|
|
49
|
+
* turns). Thread it into any stochastic knob; ignore it when the agent
|
|
50
|
+
* is deterministic.
|
|
51
|
+
*/
|
|
52
|
+
export type MakeChatAgent = (build: {
|
|
53
|
+
readonly specs: readonly AblationSpec[];
|
|
54
|
+
readonly seed: number;
|
|
55
|
+
}) => Agent | Promise<Agent>;
|
|
56
|
+
/** How transcript lines and the per-turn message string are rendered. */
|
|
57
|
+
export interface ChatFormat {
|
|
58
|
+
/** Preamble header line. Default `'Recent conversation:'`. */
|
|
59
|
+
readonly header?: string;
|
|
60
|
+
/** Label for user lines (`'<userLabel>: ...'`). Default `'User'`. */
|
|
61
|
+
readonly userLabel?: string;
|
|
62
|
+
/** Label for reply lines. Default `'Assistant'`. */
|
|
63
|
+
readonly assistantLabel?: string;
|
|
64
|
+
}
|
|
65
|
+
/** One transcript entry — the structured form of a rendered line. */
|
|
66
|
+
export interface ChatMessage {
|
|
67
|
+
readonly role: 'user' | 'assistant';
|
|
68
|
+
readonly text: string;
|
|
69
|
+
}
|
|
70
|
+
export interface RecordedChatOptions {
|
|
71
|
+
readonly makeAgent: MakeChatAgent;
|
|
72
|
+
readonly format?: ChatFormat;
|
|
73
|
+
/**
|
|
74
|
+
* Transcript the session OPENS with (frozen; rendered as the seed
|
|
75
|
+
* preamble of every turn). `fork()` fills this; hosts may also pass a
|
|
76
|
+
* persisted transcript to rehydrate a session — authenticity of a
|
|
77
|
+
* hand-supplied seed is then the host's responsibility.
|
|
78
|
+
*/
|
|
79
|
+
readonly seed?: readonly ChatMessage[];
|
|
80
|
+
/**
|
|
81
|
+
* Session-persistent removals: every turn (and every probe) in this
|
|
82
|
+
* session runs with these specs applied. `fork()` fills this so the
|
|
83
|
+
* what-if world STAYS the what-if world.
|
|
84
|
+
*/
|
|
85
|
+
readonly removed?: readonly AblationSpec[];
|
|
86
|
+
}
|
|
87
|
+
/** One recorded turn — everything frozen at record time. */
|
|
88
|
+
export interface ChatTurn {
|
|
89
|
+
/** 0-based position in `turns`. */
|
|
90
|
+
readonly index: number;
|
|
91
|
+
readonly userMessage: string;
|
|
92
|
+
/** The agent's reply (agentfootprint `AgentOutput` string). */
|
|
93
|
+
readonly reply: string;
|
|
94
|
+
/**
|
|
95
|
+
* The EXACT message string `agent.run()` received — composed once at
|
|
96
|
+
* record time. Re-runs of this turn replay these bytes verbatim; the
|
|
97
|
+
* byte-exact-history guarantee lives here.
|
|
98
|
+
*/
|
|
99
|
+
readonly message: string;
|
|
100
|
+
/** Rendered transcript lines the turn opened with (frozen copy). */
|
|
101
|
+
readonly transcriptBefore: readonly string[];
|
|
102
|
+
/**
|
|
103
|
+
* Localize-ready evidence: pass straight to
|
|
104
|
+
* `localizeContextBug({ artifacts: turn.artifacts, ... })`.
|
|
105
|
+
*/
|
|
106
|
+
readonly artifacts: {
|
|
107
|
+
readonly snapshot: RuntimeSnapshot;
|
|
108
|
+
readonly events: readonly CapturedEventLike[];
|
|
109
|
+
};
|
|
110
|
+
/** runtimeStageId of the turn's LAST LLM call — the default `atStep`. */
|
|
111
|
+
readonly lastLlmCallId: string | undefined;
|
|
112
|
+
/** The identity `send()` was given, threaded into re-runs unchanged. */
|
|
113
|
+
readonly identity: MemoryIdentity | undefined;
|
|
114
|
+
}
|
|
115
|
+
export interface SendOptions {
|
|
116
|
+
/** Multi-tenant memory scope, passed through to `agent.run()`. */
|
|
117
|
+
readonly identity?: MemoryIdentity;
|
|
118
|
+
}
|
|
119
|
+
/** `reason(k)` options — localize passthrough minus what the turn provides. */
|
|
120
|
+
export interface ReasonOptions extends Omit<LocalizeContextBugOptions, 'artifacts' | 'rerun'> {
|
|
121
|
+
/** Recompute even if a memoized report exists (e.g. a new scorer). */
|
|
122
|
+
readonly fresh?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/** `rerunTurn(k)` options — `rerunWithoutSources` minus what the turn provides. */
|
|
125
|
+
export interface RerunTurnOptions extends Omit<RerunWithoutSourcesOptions, 'report' | 'runner' | 'originalAnswer'> {
|
|
126
|
+
/**
|
|
127
|
+
* The report to resolve `ignore` against. Default: the turn's memoized
|
|
128
|
+
* `reason(k)` report (computed with this call's `embedder` + the default
|
|
129
|
+
* scorer if none exists yet).
|
|
130
|
+
*/
|
|
131
|
+
readonly report?: ContextBugReport;
|
|
132
|
+
}
|
|
133
|
+
export interface ForkOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Seed the fork's last reply with this counterfactual answer. MUST be a
|
|
136
|
+
* result produced by THIS session's `rerunTurn(k, ...)` for the SAME k
|
|
137
|
+
* (identity-checked) — a fabricated fork would be a lie. Omitted: the
|
|
138
|
+
* fork continues from the ORIGINAL reply (a plain branch, no ablation).
|
|
139
|
+
*/
|
|
140
|
+
readonly fromRerun?: RerunWithoutSourcesResult;
|
|
141
|
+
}
|
|
142
|
+
export interface ForkOrigin {
|
|
143
|
+
readonly parent: RecordedChat;
|
|
144
|
+
readonly turnIndex: number;
|
|
145
|
+
/** True when the fork was seeded from a re-run's counterfactual answer. */
|
|
146
|
+
readonly viaRerun: boolean;
|
|
147
|
+
}
|
|
148
|
+
export interface RecordedChat {
|
|
149
|
+
/** Runs one recorded turn. One send at a time — concurrent sends throw. */
|
|
150
|
+
send(userMessage: string, options?: SendOptions): Promise<ChatTurn>;
|
|
151
|
+
/** Turn K's frozen record. Out-of-range throws (names the valid range). */
|
|
152
|
+
turn(k: number): ChatTurn;
|
|
153
|
+
readonly turns: readonly ChatTurn[];
|
|
154
|
+
/** The transcript the session opened with (forks / rehydration; else `[]`). */
|
|
155
|
+
readonly seed: readonly ChatMessage[];
|
|
156
|
+
/** The session's persistent removals (forks carry these; else `[]`). */
|
|
157
|
+
readonly removed: readonly AblationSpec[];
|
|
158
|
+
/** Set on sessions created by `fork()`; `undefined` otherwise. */
|
|
159
|
+
readonly forkedFrom: ForkOrigin | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* Turn K's `ContextBugReport` (agentfootprint's, unmodified) via
|
|
162
|
+
* `localizeContextBug` over the frozen artifacts, `atStep` defaulted to
|
|
163
|
+
* the turn's last LLM call. Memoized per turn; `fresh: true` recomputes.
|
|
164
|
+
*/
|
|
165
|
+
reason(k: number, options: ReasonOptions): Promise<ContextBugReport>;
|
|
166
|
+
/**
|
|
167
|
+
* Re-run turn K without the named sources — same recorded history bytes,
|
|
168
|
+
* same user message, minus the sources — delegating to
|
|
169
|
+
* `rerunWithoutSources` with a runner derived from `makeAgent`. Returns
|
|
170
|
+
* agentfootprint's `RerunWithoutSourcesResult` UNMODIFIED (a `verdict`
|
|
171
|
+
* only when `checkBaseline: true` — the honesty tiers are not hidden).
|
|
172
|
+
*/
|
|
173
|
+
rerunTurn(k: number, options: RerunTurnOptions): Promise<RerunWithoutSourcesResult>;
|
|
174
|
+
/**
|
|
175
|
+
* Branch, never rewrite: a NEW `RecordedChat` (same `makeAgent`, same
|
|
176
|
+
* format) whose seed is the conversation through turn K with the reply
|
|
177
|
+
* swapped for the re-run's counterfactual answer (or kept, without
|
|
178
|
+
* `fromRerun`), and whose `removed` is this session's removals plus the
|
|
179
|
+
* re-run's `removed` specs. The original session is never touched.
|
|
180
|
+
*/
|
|
181
|
+
fork(k: number, options?: ForkOptions): RecordedChat;
|
|
182
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recorded-chat types — the session-scoped turn recorder over the existing
|
|
3
|
+
* context-bisect product loop (localizeContextBug + rerunWithoutSources +
|
|
4
|
+
* removableSources).
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS: multi-turn chat on agentfootprint is a HOST convention —
|
|
7
|
+
* `AgentInput` has no history field, so hosts thread the transcript into the
|
|
8
|
+
* `message` string themselves. Every chat host that wanted per-turn
|
|
9
|
+
* transparency then re-wrote the same correctness-critical glue and got three
|
|
10
|
+
* things subtly wrong:
|
|
11
|
+
*
|
|
12
|
+
* 1. `agent.getLastSnapshot()` is last-run-only — a second `run()` clobbers
|
|
13
|
+
* it, so reasoning about turn K after turn K+1 ran silently attributes
|
|
14
|
+
* the wrong run.
|
|
15
|
+
* 2. History is a convention — the transcript preamble must be BYTE-identical
|
|
16
|
+
* between the recorded turn and its counterfactual re-run, or the re-run
|
|
17
|
+
* ablates a subtly different scenario without failing anything.
|
|
18
|
+
* 3. The `AblationRunner` duplicates turn construction — the re-run is only
|
|
19
|
+
* valid if the rebuilt turn matches the recorded one (same system, facts,
|
|
20
|
+
* preamble).
|
|
21
|
+
*
|
|
22
|
+
* `recordedChat({ makeAgent })` owns exactly those three: per-turn artifact
|
|
23
|
+
* freezing (1), byte-exact history threading (2), and deriving the turn-K
|
|
24
|
+
* runner from the SAME `makeAgent` that ran the turn (3). It COMPOSES with the
|
|
25
|
+
* 7.5 surface — `reason()` calls `localizeContextBug`, `rerunTurn()` delegates
|
|
26
|
+
* to `rerunWithoutSources` and returns its result UNMODIFIED (the honesty
|
|
27
|
+
* tiers are not hidden). Session registries, UI joins, comparators and
|
|
28
|
+
* persistence stay host-side by design (see the guide's "What stays yours").
|
|
29
|
+
*/
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/lib/recorded-chat/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
|
|
@@ -81,13 +81,20 @@ export interface EvictionRecord {
|
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* Budget-pressure warning — emitted before evictions fire.
|
|
84
|
+
*
|
|
85
|
+
* `capTokens` / `projectedTokens` are historical names: slot budgets are
|
|
86
|
+
* measured in CHARS. Renaming them would be breaking, so they stay.
|
|
87
|
+
*
|
|
88
|
+
* `planAction: 'none'` means no mitigation was performed — nothing was
|
|
89
|
+
* evicted or truncated and the full content still went to the LLM. It is
|
|
90
|
+
* the honest reading of a slot that composed over its `budgetCap`.
|
|
84
91
|
*/
|
|
85
92
|
export interface BudgetPressureRecord {
|
|
86
93
|
readonly slot: ContextSlot;
|
|
87
94
|
readonly capTokens: number;
|
|
88
95
|
readonly projectedTokens: number;
|
|
89
96
|
readonly overflowBy: number;
|
|
90
|
-
readonly planAction: 'evict' | 'summarize' | 'abort';
|
|
97
|
+
readonly planAction: 'evict' | 'summarize' | 'abort' | 'none';
|
|
91
98
|
}
|
|
92
99
|
export declare const COMPOSITION_KEYS: {
|
|
93
100
|
readonly SLOT_COMPOSED: "slotCompositions";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/recorders/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/recorders/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoGH,uEAAuE;AACvE,0EAA0E;AAC1E,4BAA4B;AAC5B,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa,EAAE,kBAAkB;IACjC,OAAO,EAAE,eAAe;IACxB,eAAe,EAAE,qBAAqB;CAC9B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* recorded-chat — the session-scoped turn recorder over the context-bisect
|
|
4
|
+
* product loop. Absorbs the three correctness traps every chat host re-writes
|
|
5
|
+
* (per-turn artifact freezing, byte-exact history threading, same-factory
|
|
6
|
+
* runner derivation) while COMPOSING with the 7.5 surface, not duplicating it.
|
|
7
|
+
*
|
|
8
|
+
* Exported from `agentfootprint/debug` (every symbol it composes with lives
|
|
9
|
+
* there). Named exports — `export *` is not the house style.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.recordedChat = void 0;
|
|
13
|
+
var recordedChat_js_1 = require("./recordedChat.js");
|
|
14
|
+
Object.defineProperty(exports, "recordedChat", { enumerable: true, get: function () { return recordedChat_js_1.recordedChat; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/recorded-chat/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,qDAAiD;AAAxC,+GAAA,YAAY,OAAA"}
|