@superdurable/dex-agents 0.2.0 → 0.3.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.
Files changed (70) hide show
  1. package/README.md +44 -6
  2. package/dist/src/agent-context-compaction.d.ts +150 -4
  3. package/dist/src/agent-context-compaction.js +253 -5
  4. package/dist/src/agent-context-compaction.js.map +1 -1
  5. package/dist/src/agent-session-codecs.js +1 -0
  6. package/dist/src/agent-session-codecs.js.map +1 -1
  7. package/dist/src/agent-session-failure.d.ts +58 -0
  8. package/dist/src/agent-session-failure.js +126 -0
  9. package/dist/src/agent-session-failure.js.map +1 -0
  10. package/dist/src/agent-session-flow-graph-cli.d.ts +2 -0
  11. package/dist/src/agent-session-flow-graph-cli.js +47 -0
  12. package/dist/src/agent-session-flow-graph-cli.js.map +1 -0
  13. package/dist/src/agent-session-flow-graph.d.ts +38 -0
  14. package/dist/src/agent-session-flow-graph.js +54 -0
  15. package/dist/src/agent-session-flow-graph.js.map +1 -0
  16. package/dist/src/agent-session-flow-graph.json +2187 -0
  17. package/dist/src/agent-session-flow.d.ts +23 -2
  18. package/dist/src/agent-session-flow.js +5 -0
  19. package/dist/src/agent-session-flow.js.map +1 -1
  20. package/dist/src/agent-session-spend-limit.d.ts +156 -0
  21. package/dist/src/agent-session-spend-limit.js +42 -0
  22. package/dist/src/agent-session-spend-limit.js.map +1 -0
  23. package/dist/src/agent-session-start.d.ts +49 -0
  24. package/dist/src/agent-session-start.js +67 -0
  25. package/dist/src/agent-session-start.js.map +1 -0
  26. package/dist/src/agent-session-step-options.d.ts +52 -1
  27. package/dist/src/agent-session-step-options.js +34 -2
  28. package/dist/src/agent-session-step-options.js.map +1 -1
  29. package/dist/src/agent-session-steps.d.ts +2 -0
  30. package/dist/src/agent-session-steps.js +80 -19
  31. package/dist/src/agent-session-steps.js.map +1 -1
  32. package/dist/src/agent-session-transitions.d.ts +12 -1
  33. package/dist/src/agent-session-transitions.js +53 -1
  34. package/dist/src/agent-session-transitions.js.map +1 -1
  35. package/dist/src/agent-session-values.d.ts +7 -2
  36. package/dist/src/agent-session-values.js.map +1 -1
  37. package/dist/src/index.d.ts +6 -1
  38. package/dist/src/index.js +5 -0
  39. package/dist/src/index.js.map +1 -1
  40. package/dist/src/stepwise/agent-definition.d.ts +8 -0
  41. package/dist/src/stepwise/index.d.ts +2 -1
  42. package/dist/src/stepwise/index.js +2 -1
  43. package/dist/src/stepwise/index.js.map +1 -1
  44. package/dist/src/stepwise/stepwise-agent-runner.d.ts +3 -21
  45. package/dist/src/stepwise/stepwise-agent-runner.js +2 -29
  46. package/dist/src/stepwise/stepwise-agent-runner.js.map +1 -1
  47. package/dist/src/stepwise/stepwise-agent-session-flow.js +1 -0
  48. package/dist/src/stepwise/stepwise-agent-session-flow.js.map +1 -1
  49. package/dist/src/testing/agent-session-test-client.js +2 -5
  50. package/dist/src/testing/agent-session-test-client.js.map +1 -1
  51. package/dist/src/testing/context-window.d.ts +194 -0
  52. package/dist/src/testing/context-window.js +360 -0
  53. package/dist/src/testing/context-window.js.map +1 -0
  54. package/dist/src/testing/index.d.ts +1 -0
  55. package/dist/src/testing/index.js +1 -0
  56. package/dist/src/testing/index.js.map +1 -1
  57. package/dist/src/ui-stream/agent-event-sse.d.ts +3 -2
  58. package/dist/src/ui-stream/agent-event-sse.js +11 -3
  59. package/dist/src/ui-stream/agent-event-sse.js.map +1 -1
  60. package/dist/src/ui-stream/agent-session-record-reader.d.ts +2 -0
  61. package/dist/src/ui-stream/agent-session-record-reader.js +5 -0
  62. package/dist/src/ui-stream/agent-session-record-reader.js.map +1 -1
  63. package/dist/src/ui-stream/agent-ui-message-stream-responses.js +3 -2
  64. package/dist/src/ui-stream/agent-ui-message-stream-responses.js.map +1 -1
  65. package/dist/src/ui-stream/agent-ui-message-stream.js +17 -3
  66. package/dist/src/ui-stream/agent-ui-message-stream.js.map +1 -1
  67. package/dist/src/ui-stream/agent-ui-messages.d.ts +2 -0
  68. package/dist/src/ui-stream/agent-ui-messages.js +5 -3
  69. package/dist/src/ui-stream/agent-ui-messages.js.map +1 -1
  70. package/package.json +6 -3
@@ -0,0 +1,194 @@
1
+ import { type CompactionPolicy } from "../agent-context-compaction.js";
2
+ import type { AgentMessage, AgentSnapshot } from "../agent-session-values.js";
3
+ import { AgentSessionTestClient } from "./agent-session-test-client.js";
4
+ import { KillableWorkerProcess } from "./agent-worker-process.js";
5
+ /** Environment variable holding the JSON of {@link ContextWindowSettings}. */
6
+ export declare const contextWindowSettingsVariable = "DEX_AGENTS_CONTEXT_WINDOW_SETTINGS";
7
+ /** Tool that the scripted model calls once per turn. */
8
+ export declare const contextWindowToolName = "lookup";
9
+ /** Start of the error that the scripted model throws for an oversized request. */
10
+ export declare const contextWindowExceededError = "scripted model: input exceeds the context window";
11
+ /** Gate that holds a summary call of a range that names `gate-summary`. */
12
+ export declare const contextWindowSummaryGateName = "summary";
13
+ /**
14
+ * Sizes of the scripted model and the session of one context window test. A Worker child process reads them from
15
+ * {@link contextWindowSettingsVariable}.
16
+ */
17
+ export interface ContextWindowSettings {
18
+ /** Context window of the scripted model, at four characters per token. */
19
+ readonly windowTokens: number;
20
+ /** Whether the session gets a compaction policy. */
21
+ readonly isCompactionEnabled: boolean;
22
+ /** Size of each tool result in characters. */
23
+ readonly toolOutputCharacters: number;
24
+ /** Size of each model answer in characters. */
25
+ readonly answerCharacters: number;
26
+ /** `AgentSessionOptions.messageWindowSize`. */
27
+ readonly messageWindowSize: number;
28
+ /** `AgentSessionOptions.messageChunkSize`. */
29
+ readonly messageChunkSize: number;
30
+ /** `AgentSessionOptions.retainedMessageLimit`; `null` keeps every message. */
31
+ readonly retainedMessageLimit: number | null;
32
+ }
33
+ /** The settings of a test that changes nothing. */
34
+ export declare const defaultContextWindowSettings: ContextWindowSettings;
35
+ /**
36
+ * Returns the Worker environment that carries the settings.
37
+ * @param settings - The test's settings.
38
+ * @returns One entry under {@link contextWindowSettingsVariable}.
39
+ */
40
+ export declare function contextWindowSettingsEnvironment(settings: ContextWindowSettings): Record<string, string>;
41
+ /**
42
+ * Reads the settings a test gave this Worker.
43
+ * @returns The test's settings, or the defaults outside a Worker child.
44
+ */
45
+ export declare function readContextWindowSettings(): ContextWindowSettings;
46
+ /**
47
+ * Returns the `AgentSessionOptions` fields for the settings.
48
+ * @param settings - The test's settings.
49
+ * @returns The compaction policy and the message storage options.
50
+ */
51
+ export declare function contextWindowSessionOptions(settings: ContextWindowSettings): {
52
+ readonly compaction?: CompactionPolicy;
53
+ readonly messageWindowSize: number;
54
+ readonly messageChunkSize: number;
55
+ readonly retainedMessageLimit: number | null;
56
+ };
57
+ /** What the scripted model does for one request: call the tool with a tag, or answer with a text. */
58
+ export type ContextWindowReply = {
59
+ readonly kind: "tool_call";
60
+ readonly tag: string;
61
+ } | {
62
+ readonly kind: "text";
63
+ readonly text: string;
64
+ };
65
+ /** One request the scripted model saw. */
66
+ export interface ContextWindowRequest {
67
+ /** `"model"` for an agent's request, `"summary"` for a summary call. */
68
+ readonly kind: "model" | "summary";
69
+ /** Characters of the request, instructions included. */
70
+ readonly characters: number;
71
+ /** The whole request as text, in the framework's format. */
72
+ readonly input: string;
73
+ }
74
+ /**
75
+ * Answers model requests of a framework adapter and logs every request, like a provider with a small context
76
+ * window. Each user message is `turn <tag>`: the model calls {@link contextWindowToolName} once with the tag, then
77
+ * answers {@link answerText}. It holds no state between calls, so it survives a Worker restart.
78
+ */
79
+ export declare class ContextWindowModelScript {
80
+ private readonly stateDirectory;
81
+ private readonly settings;
82
+ /**
83
+ * @param stateDirectory - Directory shared with the test.
84
+ * @param settings - Sizes of the test.
85
+ */
86
+ constructor(stateDirectory: string, settings: ContextWindowSettings);
87
+ /**
88
+ * Returns what the tool answers.
89
+ * @param tag - The tool call's `tag` argument.
90
+ * @returns Text of `toolOutputCharacters` that names the tag.
91
+ */
92
+ toolOutput(tag: string): string;
93
+ /**
94
+ * Returns the answer of the turn with this tag.
95
+ * @param tag - The tag of the user message.
96
+ * @returns Text of `answerCharacters` that names the tag.
97
+ */
98
+ answerText(tag: string): string;
99
+ /**
100
+ * Answers one model request.
101
+ * @param request - The system prompt, the whole request as text, and whether the running turn has its result.
102
+ * @returns The tool call or the answer.
103
+ * @throws {Error} When the request exceeds the window or names no turn.
104
+ */
105
+ reply(request: {
106
+ readonly instructions: string | undefined;
107
+ readonly inputText: string;
108
+ readonly hasToolResult: boolean;
109
+ }): Promise<ContextWindowReply>;
110
+ }
111
+ /**
112
+ * Returns every request the scripted model saw, in order.
113
+ * @param stateDirectory - The test's state directory.
114
+ * @returns The logged requests.
115
+ */
116
+ export declare function readContextWindowRequests(stateDirectory: string): ContextWindowRequest[];
117
+ /** Options of {@link ContextWindowSessionScenario}. */
118
+ export interface ContextWindowSessionScenarioOptions {
119
+ /** Dex Server address of the test's engine. */
120
+ readonly serverAddress: string;
121
+ /** Session client for the adapter's Flow. */
122
+ readonly sessions: AgentSessionTestClient;
123
+ /** Directory shared with the Worker child processes. */
124
+ readonly stateDirectory: string;
125
+ /** Sizes of the test. */
126
+ readonly settings: ContextWindowSettings;
127
+ /** Compiled module of the Worker child, which calls `runAgentWorkerProcess`. */
128
+ readonly workerEntryModulePath: string;
129
+ /** Environment that tells the Worker child where {@link stateDirectory} is. */
130
+ readonly workerEnvironment: Readonly<Record<string, string>>;
131
+ }
132
+ /**
133
+ * Drives an adapter's session past a context window in Worker child processes, and checks what happened. Every
134
+ * adapter so proves one contract: the session keeps going, the model input shrinks, and the record stays readable.
135
+ */
136
+ export declare class ContextWindowSessionScenario {
137
+ private readonly options;
138
+ /** The session ID of this scenario. */
139
+ readonly sessionId: string;
140
+ private readonly workers;
141
+ /** @param options - The engine, the session client, and the Worker child. */
142
+ constructor(options: ContextWindowSessionScenarioOptions);
143
+ /**
144
+ * Starts a Worker child with the scenario's settings, or with other settings.
145
+ * @param settings - Settings of this Worker. Defaults to the scenario's.
146
+ * @returns The Worker.
147
+ */
148
+ startWorker(settings?: ContextWindowSettings): Promise<KillableWorkerProcess>;
149
+ /** Kills every Worker child the scenario started. */
150
+ close(): Promise<void>;
151
+ /**
152
+ * Returns the logged requests of one kind.
153
+ * @param kind - `"model"` or `"summary"`.
154
+ * @returns The requests in order.
155
+ */
156
+ requests(kind: ContextWindowRequest["kind"]): ContextWindowRequest[];
157
+ /**
158
+ * Sends one turn per tag and waits until the session finished them.
159
+ * @param tags - The turns' tags.
160
+ * @param isFirst - Whether the first tag starts the session.
161
+ * @returns The snapshot after the last turn.
162
+ */
163
+ runTurns(tags: readonly string[], isFirst?: boolean): Promise<AgentSnapshot>;
164
+ /**
165
+ * Reads every message history still holds, through the public RPC.
166
+ * @returns The messages in sequence order.
167
+ */
168
+ readStoredMessages(): Promise<AgentMessage[]>;
169
+ /**
170
+ * Counts the finished `CompactContext` Steps of the session.
171
+ * @returns The count.
172
+ */
173
+ countCompactContextSteps(): Promise<number>;
174
+ /**
175
+ * Checks the whole contract on a session longer than the window.
176
+ * @param turnCount - Turns to run.
177
+ * @returns The final snapshot.
178
+ */
179
+ assertOutlivesTheWindow(turnCount?: number): Promise<AgentSnapshot>;
180
+ /**
181
+ * Checks that retention deletes only chunks behind the summary. The settings need a small `retainedMessageLimit`.
182
+ * @param turnCount - Turns to run.
183
+ */
184
+ assertRetentionFollowsTheSummary(turnCount?: number): Promise<void>;
185
+ /**
186
+ * Checks that a session without a policy is unchanged and reaches the window.
187
+ * @param turnCount - Turns to run; the window must end the session first.
188
+ */
189
+ assertFailsAtTheWindowWithoutAPolicy(turnCount?: number): Promise<void>;
190
+ /** Checks that a session started without a policy compacts once a Worker has one. */
191
+ assertStartsCompactingAfterAnUpgrade(): Promise<void>;
192
+ /** Checks SIGKILL during `CompactContext`: one more summary call, one stored summary, no repeated model call. */
193
+ assertWorkerKillRerunsOnlyTheSummary(): Promise<void>;
194
+ }
@@ -0,0 +1,360 @@
1
+ // Copyright (c) 2026 Super Durable, Inc.
2
+ //
3
+ // Licensed under the Sustainable Use License 1.0.
4
+ // You may not use this file except in compliance with the License.
5
+ // See the LICENSE file in the repository root.
6
+ //
7
+ // SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
8
+ import assert from "node:assert/strict";
9
+ import { randomUUID } from "node:crypto";
10
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { contextSummaryInstructions, contextSummaryNoteHeader, } from "../agent-context-compaction.js";
13
+ import { KillableWorkerProcess } from "./agent-worker-process.js";
14
+ import { FileGate } from "./coordination.js";
15
+ import { pollUntil } from "./polling.js";
16
+ import { executeCompletedEvent, readStepHistory } from "./step-history.js";
17
+ /** Environment variable holding the JSON of {@link ContextWindowSettings}. */
18
+ export const contextWindowSettingsVariable = "DEX_AGENTS_CONTEXT_WINDOW_SETTINGS";
19
+ /** Tool that the scripted model calls once per turn. */
20
+ export const contextWindowToolName = "lookup";
21
+ /** Start of the error that the scripted model throws for an oversized request. */
22
+ export const contextWindowExceededError = "scripted model: input exceeds the context window";
23
+ /** Gate that holds a summary call of a range that names `gate-summary`. */
24
+ export const contextWindowSummaryGateName = "summary";
25
+ const requestLogName = "model-requests.jsonl";
26
+ const summaryGateTag = "gate-summary";
27
+ const turnCommandPattern = /turn ([A-Za-z0-9-]+)/gu;
28
+ const providerCharactersPerToken = 4;
29
+ const summaryTagLimit = 10;
30
+ /** The settings of a test that changes nothing. */
31
+ export const defaultContextWindowSettings = Object.freeze({
32
+ windowTokens: 6000,
33
+ isCompactionEnabled: true,
34
+ toolOutputCharacters: 2000,
35
+ answerCharacters: 1000,
36
+ messageWindowSize: 8,
37
+ messageChunkSize: 4,
38
+ retainedMessageLimit: 1000,
39
+ });
40
+ /**
41
+ * Returns the Worker environment that carries the settings.
42
+ * @param settings - The test's settings.
43
+ * @returns One entry under {@link contextWindowSettingsVariable}.
44
+ */
45
+ export function contextWindowSettingsEnvironment(settings) {
46
+ return { [contextWindowSettingsVariable]: JSON.stringify(settings) };
47
+ }
48
+ /**
49
+ * Reads the settings a test gave this Worker.
50
+ * @returns The test's settings, or the defaults outside a Worker child.
51
+ */
52
+ export function readContextWindowSettings() {
53
+ const value = process.env[contextWindowSettingsVariable];
54
+ return value === undefined ? defaultContextWindowSettings : JSON.parse(value);
55
+ }
56
+ /**
57
+ * Returns the `AgentSessionOptions` fields for the settings.
58
+ * @param settings - The test's settings.
59
+ * @returns The compaction policy and the message storage options.
60
+ */
61
+ export function contextWindowSessionOptions(settings) {
62
+ return {
63
+ ...(settings.isCompactionEnabled ? { compaction: { contextWindowTokens: settings.windowTokens } } : {}),
64
+ messageWindowSize: settings.messageWindowSize,
65
+ messageChunkSize: settings.messageChunkSize,
66
+ retainedMessageLimit: settings.retainedMessageLimit,
67
+ };
68
+ }
69
+ /**
70
+ * Answers model requests of a framework adapter and logs every request, like a provider with a small context
71
+ * window. Each user message is `turn <tag>`: the model calls {@link contextWindowToolName} once with the tag, then
72
+ * answers {@link answerText}. It holds no state between calls, so it survives a Worker restart.
73
+ */
74
+ export class ContextWindowModelScript {
75
+ stateDirectory;
76
+ settings;
77
+ /**
78
+ * @param stateDirectory - Directory shared with the test.
79
+ * @param settings - Sizes of the test.
80
+ */
81
+ constructor(stateDirectory, settings) {
82
+ this.stateDirectory = stateDirectory;
83
+ this.settings = settings;
84
+ }
85
+ /**
86
+ * Returns what the tool answers.
87
+ * @param tag - The tool call's `tag` argument.
88
+ * @returns Text of `toolOutputCharacters` that names the tag.
89
+ */
90
+ toolOutput(tag) {
91
+ return `lookup-body-${tag}:${"y".repeat(this.settings.toolOutputCharacters)}`;
92
+ }
93
+ /**
94
+ * Returns the answer of the turn with this tag.
95
+ * @param tag - The tag of the user message.
96
+ * @returns Text of `answerCharacters` that names the tag.
97
+ */
98
+ answerText(tag) {
99
+ return `answer-body-${tag}:${"x".repeat(this.settings.answerCharacters)}`;
100
+ }
101
+ /**
102
+ * Answers one model request.
103
+ * @param request - The system prompt, the whole request as text, and whether the running turn has its result.
104
+ * @returns The tool call or the answer.
105
+ * @throws {Error} When the request exceeds the window or names no turn.
106
+ */
107
+ async reply(request) {
108
+ const instructions = request.instructions ?? "";
109
+ const isSummary = instructions.includes(contextSummaryInstructions);
110
+ const characters = request.inputText.length + instructions.length;
111
+ const entry = { kind: isSummary ? "summary" : "model", characters, input: request.inputText };
112
+ appendFileSync(join(this.stateDirectory, requestLogName), `${JSON.stringify(entry)}\n`);
113
+ const windowCharacters = this.settings.windowTokens * providerCharactersPerToken;
114
+ if (characters > windowCharacters) {
115
+ throw new Error(`${contextWindowExceededError}: ${characters} characters, limit ${windowCharacters}`);
116
+ }
117
+ const tags = [...request.inputText.matchAll(turnCommandPattern)].map((match) => match[1] ?? "");
118
+ if (isSummary) {
119
+ if (tags.includes(summaryGateTag)) {
120
+ const gate = new FileGate(this.stateDirectory, contextWindowSummaryGateName);
121
+ gate.markReached();
122
+ await gate.waitForRelease();
123
+ }
124
+ return { kind: "text", text: `summary of turns: ${[...new Set(tags)].slice(-summaryTagLimit).join(" ")}` };
125
+ }
126
+ const tag = tags.at(-1);
127
+ if (tag === undefined) {
128
+ throw new Error("the scripted model needs a turn command");
129
+ }
130
+ return request.hasToolResult ? { kind: "text", text: this.answerText(tag) } : { kind: "tool_call", tag };
131
+ }
132
+ }
133
+ /**
134
+ * Returns every request the scripted model saw, in order.
135
+ * @param stateDirectory - The test's state directory.
136
+ * @returns The logged requests.
137
+ */
138
+ export function readContextWindowRequests(stateDirectory) {
139
+ const path = join(stateDirectory, requestLogName);
140
+ if (!existsSync(path)) {
141
+ return [];
142
+ }
143
+ return readFileSync(path, "utf8")
144
+ .split("\n")
145
+ .filter((line) => line !== "")
146
+ .map((line) => JSON.parse(line));
147
+ }
148
+ /**
149
+ * Drives an adapter's session past a context window in Worker child processes, and checks what happened. Every
150
+ * adapter so proves one contract: the session keeps going, the model input shrinks, and the record stays readable.
151
+ */
152
+ export class ContextWindowSessionScenario {
153
+ options;
154
+ /** The session ID of this scenario. */
155
+ sessionId = `session-${randomUUID()}`;
156
+ workers = [];
157
+ /** @param options - The engine, the session client, and the Worker child. */
158
+ constructor(options) {
159
+ this.options = options;
160
+ }
161
+ /**
162
+ * Starts a Worker child with the scenario's settings, or with other settings.
163
+ * @param settings - Settings of this Worker. Defaults to the scenario's.
164
+ * @returns The Worker.
165
+ */
166
+ async startWorker(settings = this.options.settings) {
167
+ const worker = await KillableWorkerProcess.start({
168
+ entryModulePath: this.options.workerEntryModulePath,
169
+ serverAddress: this.options.serverAddress,
170
+ logPath: join(this.options.stateDirectory, "worker.log"),
171
+ environment: { ...this.options.workerEnvironment, ...contextWindowSettingsEnvironment(settings) },
172
+ });
173
+ this.workers.push(worker);
174
+ return worker;
175
+ }
176
+ /** Kills every Worker child the scenario started. */
177
+ async close() {
178
+ for (const worker of this.workers.splice(0)) {
179
+ await worker.kill();
180
+ }
181
+ }
182
+ /**
183
+ * Returns the logged requests of one kind.
184
+ * @param kind - `"model"` or `"summary"`.
185
+ * @returns The requests in order.
186
+ */
187
+ requests(kind) {
188
+ return readContextWindowRequests(this.options.stateDirectory).filter((request) => request.kind === kind);
189
+ }
190
+ /**
191
+ * Sends one turn per tag and waits until the session finished them.
192
+ * @param tags - The turns' tags.
193
+ * @param isFirst - Whether the first tag starts the session.
194
+ * @returns The snapshot after the last turn.
195
+ */
196
+ async runTurns(tags, isFirst = true) {
197
+ const { sessions } = this.options;
198
+ let commands = tags.map((tag) => `turn ${tag}`);
199
+ let before = 0;
200
+ if (isFirst) {
201
+ await sessions.startSession(this.sessionId, commands[0] ?? "");
202
+ commands = commands.slice(1);
203
+ }
204
+ else {
205
+ before = (await sessions.readAgentState(this.sessionId))?.turnCount ?? 0;
206
+ }
207
+ for (const command of commands) {
208
+ assert.equal(await sessions.sendUserMessage(this.sessionId, command), true);
209
+ }
210
+ await sessions.waitForCompletedTurns(this.sessionId, before + tags.length, { timeoutMs: 180_000 });
211
+ return sessions.getAgentSnapshot(this.sessionId);
212
+ }
213
+ /**
214
+ * Reads every message history still holds, through the public RPC.
215
+ * @returns The messages in sequence order.
216
+ */
217
+ async readStoredMessages() {
218
+ const messages = [];
219
+ let beforeSequence = null;
220
+ for (;;) {
221
+ const page = await this.options.sessions.listAgentMessages(this.sessionId, beforeSequence, 200);
222
+ messages.unshift(...page.messages);
223
+ if (page.nextBeforeSequence === null) {
224
+ return messages;
225
+ }
226
+ beforeSequence = page.nextBeforeSequence;
227
+ }
228
+ }
229
+ /**
230
+ * Counts the finished `CompactContext` Steps of the session.
231
+ * @returns The count.
232
+ */
233
+ async countCompactContextSteps() {
234
+ const history = await readStepHistory(this.options.serverAddress, this.sessionId);
235
+ return history.filter((entry) => entry.stepType === "CompactContext" && entry.eventType === executeCompletedEvent)
236
+ .length;
237
+ }
238
+ /**
239
+ * Checks the whole contract on a session longer than the window.
240
+ * @param turnCount - Turns to run.
241
+ * @returns The final snapshot.
242
+ */
243
+ async assertOutlivesTheWindow(turnCount = 12) {
244
+ await this.startWorker();
245
+ const snapshot = await this.runTurns(numberedTags(turnCount));
246
+ assert.equal(snapshot.state?.lastTurnError ?? null, null);
247
+ assert.equal(snapshot.state?.status, "waiting_for_user_message");
248
+ assert.equal(snapshot.state?.turnCount, turnCount);
249
+ const summary = snapshot.contextSummary;
250
+ assert.ok(summary);
251
+ assert.match(summary.text, /^summary of turns:/u);
252
+ assert.ok((await this.countCompactContextSteps()) >= 1);
253
+ const limit = this.options.settings.windowTokens * providerCharactersPerToken;
254
+ const modelRequests = this.requests("model");
255
+ assert.ok(modelRequests.every((request) => request.characters <= limit));
256
+ const allRequests = readContextWindowRequests(this.options.stateDirectory);
257
+ allRequests.forEach((request, index) => {
258
+ if (request.kind === "summary") {
259
+ assert.ok((allRequests[index + 1]?.characters ?? 0) < (allRequests[index - 1]?.characters ?? 0));
260
+ }
261
+ });
262
+ const lastInput = modelRequests.at(-1)?.input ?? "";
263
+ assert.ok(lastInput.includes(contextSummaryNoteHeader));
264
+ assert.ok(lastInput.includes(summary.text.split("\n")[0] ?? ""));
265
+ // Turn 1 is behind the summary, so its bodies left the model input.
266
+ assert.ok(!lastInput.includes("answer-body-t1:"));
267
+ assert.ok(!lastInput.includes("lookup-body-t1:"));
268
+ const messages = await this.readStoredMessages();
269
+ assert.deepEqual(messages.map((message) => message.sequence), messages.map((_, index) => index + 1));
270
+ assert.equal(messages[0]?.text, "turn t1");
271
+ assert.ok(!messages.some((message) => message.frameworkItem.includes(contextSummaryNoteHeader)));
272
+ assert.ok(messages.some((message) => (message.text ?? "").includes("answer-body-t1:")));
273
+ const summaryCalls = this.requests("summary").length;
274
+ assert.equal(summaryCalls, await this.countCompactContextSteps());
275
+ assert.equal(snapshot.usage.modelCalls, modelRequests.length + summaryCalls);
276
+ return snapshot;
277
+ }
278
+ /**
279
+ * Checks that retention deletes only chunks behind the summary. The settings need a small `retainedMessageLimit`.
280
+ * @param turnCount - Turns to run.
281
+ */
282
+ async assertRetentionFollowsTheSummary(turnCount = 24) {
283
+ await this.startWorker();
284
+ const snapshot = await this.runTurns(numberedTags(turnCount));
285
+ assert.equal(snapshot.state?.lastTurnError ?? null, null);
286
+ const summary = snapshot.contextSummary;
287
+ assert.ok(summary);
288
+ const page = await this.options.sessions.listAgentMessages(this.sessionId, null, 200);
289
+ const deletedBefore = page.deletedBeforeSequence ?? null;
290
+ assert.ok(deletedBefore !== null && deletedBefore > 1);
291
+ assert.ok(deletedBefore - 1 <= summary.lastSequence);
292
+ const messages = await this.readStoredMessages();
293
+ assert.equal(messages[0]?.sequence, deletedBefore);
294
+ assert.deepEqual(messages.map((message) => message.sequence), messages.map((_, index) => deletedBefore + index));
295
+ const limit = this.options.settings.retainedMessageLimit;
296
+ assert.ok(limit !== null && messages.length >= limit);
297
+ assert.ok((messages.at(-1)?.text ?? "").startsWith(`answer-body-t${turnCount}:`));
298
+ }
299
+ /**
300
+ * Checks that a session without a policy is unchanged and reaches the window.
301
+ * @param turnCount - Turns to run; the window must end the session first.
302
+ */
303
+ async assertFailsAtTheWindowWithoutAPolicy(turnCount = 12) {
304
+ await this.startWorker();
305
+ const snapshot = await this.runTurns(numberedTags(turnCount));
306
+ assert.ok((snapshot.state?.lastTurnError ?? "").includes(contextWindowExceededError));
307
+ assert.equal(snapshot.contextSummary ?? null, null);
308
+ assert.equal(await this.countCompactContextSteps(), 0);
309
+ assert.equal(this.requests("summary").length, 0);
310
+ assert.ok(!this.requests("model").some((request) => request.input.includes(contextSummaryNoteHeader)));
311
+ }
312
+ /** Checks that a session started without a policy compacts once a Worker has one. */
313
+ async assertStartsCompactingAfterAnUpgrade() {
314
+ const worker = await this.startWorker({ ...this.options.settings, isCompactionEnabled: false });
315
+ await this.runTurns(["t1", "t2", "t3"]);
316
+ assert.equal(this.requests("summary").length, 0);
317
+ await worker.kill();
318
+ await this.startWorker();
319
+ const snapshot = await this.runTurns(numberedTags(12).slice(3), false);
320
+ assert.equal(snapshot.state?.lastTurnError ?? null, null);
321
+ assert.ok(snapshot.contextSummary);
322
+ assert.equal((await this.readStoredMessages())[0]?.text, "turn t1");
323
+ }
324
+ /** Checks SIGKILL during `CompactContext`: one more summary call, one stored summary, no repeated model call. */
325
+ async assertWorkerKillRerunsOnlyTheSummary() {
326
+ const { sessions, stateDirectory } = this.options;
327
+ const worker = await this.startWorker();
328
+ const commands = ["gate-summary", ...numberedTags(12).slice(1)].map((tag) => `turn ${tag}`);
329
+ await sessions.startSession(this.sessionId, commands[0] ?? "");
330
+ for (const command of commands.slice(1)) {
331
+ assert.equal(await sessions.sendUserMessage(this.sessionId, command), true);
332
+ }
333
+ const gate = new FileGate(stateDirectory, contextWindowSummaryGateName);
334
+ await gate.waitUntilReached();
335
+ const compacting = await pollUntil(async () => {
336
+ const snapshot = await sessions.getAgentSnapshot(this.sessionId);
337
+ return snapshot.state?.status === "compacting_context" ? snapshot : undefined;
338
+ }, { description: "the session compacting" });
339
+ assert.equal(compacting.contextSummary ?? null, null);
340
+ const modelCallsBeforeKill = this.requests("model").length;
341
+ await worker.kill();
342
+ gate.release();
343
+ await this.startWorker();
344
+ await sessions.waitForCompletedTurns(this.sessionId, commands.length, { timeoutMs: 180_000 });
345
+ const snapshot = await sessions.getAgentSnapshot(this.sessionId);
346
+ assert.equal(gate.reachedCount(), 2);
347
+ assert.equal(snapshot.state?.lastTurnError ?? null, null);
348
+ assert.ok(snapshot.contextSummary);
349
+ const modelRequests = this.requests("model");
350
+ // The model call that started compaction never ran, so the kill repeats no model call.
351
+ assert.ok(modelCallsBeforeKill < modelRequests.length);
352
+ const summaryCalls = this.requests("summary").length;
353
+ assert.equal(await this.countCompactContextSteps(), summaryCalls - 1);
354
+ assert.equal(snapshot.usage.modelCalls, modelRequests.length + summaryCalls - 1);
355
+ }
356
+ }
357
+ function numberedTags(count) {
358
+ return Array.from({ length: count }, (_, index) => `t${index + 1}`);
359
+ }
360
+ //# sourceMappingURL=context-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-window.js","sourceRoot":"","sources":["../../../src/testing/context-window.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export { AgentSessionTestClient, type AgentSessionWaitOptions, } from "./agent-session-test-client.js";
2
2
  export { AgentTestEnvironment, type AgentTestEnvironmentOptions, } from "./agent-test-environment.js";
3
3
  export { KillableWorkerProcess, agentWorkerBlobCacheDirectoryVariable, agentWorkerReadyLine, agentWorkerServerAddressVariable, runAgentWorkerProcess, type KillableWorkerProcessOptions, } from "./agent-worker-process.js";
4
+ export { ContextWindowModelScript, ContextWindowSessionScenario, contextWindowExceededError, contextWindowSessionOptions, contextWindowSettingsEnvironment, contextWindowSettingsVariable, contextWindowSummaryGateName, contextWindowToolName, defaultContextWindowSettings, readContextWindowRequests, readContextWindowSettings, type ContextWindowReply, type ContextWindowRequest, type ContextWindowSessionScenarioOptions, type ContextWindowSettings, } from "./context-window.js";
4
5
  export { FileGate, InvocationLog } from "./coordination.js";
5
6
  export { pollUntil, type PollOptions } from "./polling.js";
6
7
  export { ScriptedModelClient, scriptedModelApi, scriptedModelProvider, type ScriptedModelReply, type ScriptedModelResponder, type ScriptedToolCall, } from "./scripted-model-client.js";
@@ -8,6 +8,7 @@
8
8
  export { AgentSessionTestClient, } from "./agent-session-test-client.js";
9
9
  export { AgentTestEnvironment, } from "./agent-test-environment.js";
10
10
  export { KillableWorkerProcess, agentWorkerBlobCacheDirectoryVariable, agentWorkerReadyLine, agentWorkerServerAddressVariable, runAgentWorkerProcess, } from "./agent-worker-process.js";
11
+ export { ContextWindowModelScript, ContextWindowSessionScenario, contextWindowExceededError, contextWindowSessionOptions, contextWindowSettingsEnvironment, contextWindowSettingsVariable, contextWindowSummaryGateName, contextWindowToolName, defaultContextWindowSettings, readContextWindowRequests, readContextWindowSettings, } from "./context-window.js";
11
12
  export { FileGate, InvocationLog } from "./coordination.js";
12
13
  export { pollUntil } from "./polling.js";
13
14
  export { ScriptedModelClient, scriptedModelApi, scriptedModelProvider, } from "./scripted-model-client.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAE1D,OAAO,EACL,sBAAsB,GAEvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,GAErB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,qBAAqB,EACrB,qCAAqC,EACrC,oBAAoB,EACpB,gCAAgC,EAChC,qBAAqB,GAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAoB,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GAItB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GAGtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAE1D,OAAO,EACL,sBAAsB,GAEvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,GAErB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,qBAAqB,EACrB,qCAAqC,EACrC,oBAAoB,EACpB,gCAAgC,EAChC,qBAAqB,GAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,gCAAgC,EAChC,6BAA6B,EAC7B,4BAA4B,EAC5B,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,yBAAyB,GAK1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAoB,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,GAItB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GAGtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,yBAAyB,CAAC"}
@@ -63,8 +63,9 @@ export declare function formatAgentEventSseFrame(message: StreamMessage<AgentEve
63
63
  * Each event becomes one frame whose `id` is its Dex resume token and whose data is the `AgentEvent` wire
64
64
  * JSON: `type`, `turn_id`, `attempt`, `step_execution_id`, and `payload` text. Reconnecting with the last
65
65
  * `id` continues with the next event, with no repeated or missing event while the Stream retains them.
66
- * After each empty long poll it yields a keep-alive comment. The stream never ends on its own; stop
67
- * iterating, or abort `signal`, when the browser goes away.
66
+ * After each empty long poll it yields a keep-alive comment. The stream ends on its own only once the
67
+ * session's Flow failed and every event was sent; otherwise stop iterating, or abort `signal`, when the
68
+ * browser goes away.
68
69
  *
69
70
  * The browser applies the event rules of the session contract. Within one `step_execution_id` it keeps
70
71
  * only the highest `attempt` and drops the partial text of lower attempts. After `turn_completed`,