Skip to content
Source
Sign inSign up

ContextHandoffBudget.test.ts

672 lines21.7 KB

1import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2import { assert, describe, it } from "@effect/vitest";
3import {
4 ContextHandoffId,
5 PROVIDER_SEND_TURN_MAX_ATTACHMENTS,
6 ProviderDriverKind,
7 ProviderInstanceId,
8 ProviderThreadId,
9 RunId,
10 ThreadId,
11 TurnItemId,
12 OrchestrationV2ContextHandoff,
13 type OrchestrationV2HistoricalMessage,
14 type OrchestrationV2ProviderThread,
15} from "@t3tools/contracts";
16import * as DateTime from "effect/DateTime";
17import * as Effect from "effect/Effect";
18import * as Schema from "effect/Schema";
19import {
20 contextUsageForHandoff,
21 handoffBudget,
22 handoffCoverage,
23 historyCost,
24 historyResponseItems,
25 selectHistory,
26 historicalMessage,
27} from "@t3tools/provider-core/server/handoffBudget";
28import { projectContextHandoffForWire } from "@t3tools/shared/wireProjection";
29import { deliverContextHandoffs } from "./ContextHandoffDelivery.ts";
30​
31const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));
32const decodeHandoff = Schema.decodeUnknownSync(OrchestrationV2ContextHandoff);
33​
34const now = DateTime.makeUnsafe("2026-09-17T00:00:00Z");
35const threadId = ThreadId.make("thread:handoff");
36const providerThread: OrchestrationV2ProviderThread = {
37 id: ProviderThreadId.make("provider-thread:target"),
38 driver: ProviderDriverKind.make("codex"),
39 providerInstanceId: ProviderInstanceId.make("codex"),
40 providerSessionId: null,
41 appThreadId: threadId,
42 ownerNodeId: null,
43 nativeThreadRef: {
44 driver: ProviderDriverKind.make("codex"),
45 strength: "strong",
46 nativeId: "native:target",
47 },
48 nativeConversationHeadRef: null,
49 status: "idle",
50 firstRunOrdinal: null,
51 lastRunOrdinal: null,
52 handoffIds: [],
53 forkedFrom: null,
54 createdAt: now,
55 updatedAt: now,
56};
57const message = (
58 id: string,
59 role: "user" | "assistant",
60 text: string,
61): OrchestrationV2HistoricalMessage => ({
62 itemId: TurnItemId.make(id),
63 role,
64 text,
65 threadId,
66 runId: RunId.make("run:source"),
67 providerThreadId: ProviderThreadId.make("provider-thread:source"),
68 status: "interrupted",
69 kind: role === "user" ? "user_message" : "assistant_message",
70});
71const messages = [
72 message("item:one", "user", "Preserve every line.\n\n And this indentation.\n"),
73 message("item:two", "assistant", "Partial work: 日本語 🧪 مرحبا\n" + "x".repeat(600)),
74];
75const handoff: OrchestrationV2ContextHandoff = {
76 id: ContextHandoffId.make("handoff:one"),
77 threadId,
78 targetRunId: RunId.make("run:target"),
79 fromProviderThreadIds: [],
80 toProviderThreadId: providerThread.id,
81 coveredRunOrdinals: { from: 1, to: 2 },
82 strategy: "full_thread_summary",
83 status: "ready",
84 summaryMessageId: null,
85 summaryText: "",
86 history: {
87 messages,
88 coverage: "Historical context; retrieve thread:handoff with t3_thread_read.",
89 omittedItems: 0,
90 },
91 createdByProviderInstanceId: null,
92 createdAt: now,
93 updatedAt: now,
94};
95​
96describe("handoff budget", () => {
97 it("keeps old preview handoffs readable and delivery history off the wire", () => {
98 const projected = projectContextHandoffForWire({
99 ...handoff,
100 summaryText: "private transcript and command output",
101 delivery: {
102 nativeThreadId: "native:target",
103 status: "injected",
104 itemIds: [messages[0]!.itemId],
105 },
106 });
107 assert.equal(projected.summaryText, "");
108 assert.isUndefined(projected.history);
109 assert.isUndefined(projected.delivery);
110 assert.equal(decodeHandoff(projected).id, handoff.id);
111 assert.deepEqual(handoff.history?.messages, messages);
112 });
113 it("carries command outcomes as attributed activity and excludes reasoning", () => {
114 const base = {
115 id: TurnItemId.make("item:command"),
116 threadId,
117 runId: RunId.make("run:source"),
118 nodeId: null,
119 providerThreadId: providerThread.id,
120 providerTurnId: null,
121 nativeItemRef: null,
122 parentItemId: null,
123 ordinal: 1,
124 status: "failed" as const,
125 title: null,
126 startedAt: now,
127 completedAt: now,
128 updatedAt: now,
129 };
130 const command = historicalMessage({
131 ...base,
132 type: "command_execution",
133 input: "vp test",
134 output: "Failure near the end: " + "界".repeat(300),
135 exitCode: 1,
136 });
137 assert.equal(command?.role, "assistant");
138 assert.equal(command?.kind, "command_execution");
139 assert.include(command!.text, "Exit code: 1");
140 assert.include(command!.text, "界".repeat(300));
141 assert.equal(
142 historicalMessage({
143 ...base,
144 type: "file_change",
145 fileName: "src/a.ts",
146 changes: [
147 { operation: "update", path: "src/a.ts" },
148 { operation: "add", path: "src/b.ts" },
149 { operation: "update", path: "src/new.ts", oldPath: "src/old.ts" },
150 ],
151 })?.text,
152 "File change: src/a.ts, src/b.ts, src/new.ts",
153 );
154 assert.isNull(
155 historicalMessage({
156 ...base,
157 type: "reasoning",
158 text: "private reasoning",
159 streaming: false,
160 }),
161 );
162 assert.equal(historyResponseItems([command!], "Activity")[1]?.type, "message");
163 });
164​
165 it("omits oversized multilingual items whole, preserves original constraints and recent work", () => {
166 const candidates = [
167 messages[0]!,
168 message("huge", "assistant", "界🧪".repeat(20_000)),
169 message("old", "assistant", "a".repeat(4_000)),
170 messages[1]!,
171 ];
172 const selected = selectHistory({
173 messages: candidates,
174 coverage: "thread:handoff runs 1-4",
175 budget: 3_000,
176 });
177 assert.deepEqual(selected.messages, messages);
178 assert.equal(selected.omittedItems, 2);
179 assert.isAtMost(historyCost(selected.messages, selected.context), 3_000);
180 });
181​
182 it("points history recovery at the omitted items instead of a read from the thread's start", () => {
183 const omitted = message("item:omitted", "assistant", "o".repeat(4_000));
184 const selected = selectHistory({
185 messages: [
186 messages[0]!,
187 omitted,
188 ...Array.from({ length: 3 }, (_, i) => message(`item:recent-${i}`, "assistant", "recent")),
189 ],
190 coverage: handoffCoverage({ threadId, coveredRunOrdinals: { from: 1, to: 46 }, items: [] }),
191 budget: 3_000,
192 });
193 assert.deepEqual(selected.omittedItemIds, [omitted.itemId]);
194 assert.include(selected.context, omitted.itemId);
195 });
196​
197 it("counts JSON escaping and UTF-8 bytes across many messages", () => {
198 const candidates = Array.from({ length: 500 }, (_, i) =>
199 message(`item:${i}`, i % 2 ? "assistant" : "user", '\u0000\\"🧪界'.repeat(15)),
200 );
201 for (const budget of [1_024, 4_000, 16_000]) {
202 const selected = selectHistory({
203 messages: candidates,
204 coverage: "Retrieve omitted history",
205 budget,
206 });
207 assert.isAtMost(historyCost(selected.messages, selected.context), budget);
208 assert.isAbove(selected.omittedItems, 0);
209 }
210 });
211​
212 it("fits the final envelope at intermediate selected/omitted digit boundaries", () => {
213 const candidates = Array.from({ length: 20 }, (_, index) =>
214 message(`boundary:${index}`, "user", "Short request"),
215 );
216 for (let budget = 4_000; budget <= 9_000; budget++) {
217 const selected = selectHistory({
218 messages: candidates,
219 coverage: "Recover history",
220 omittedItems: 90,
221 budget,
222 });
223 assert.isAtMost(historyCost(selected.messages, selected.context), budget);
224 }
225 });
226​
227 it("subtracts native usage, input, attachments, instructions and space for work", () => {
228 const base = {
229 tokenCap: 16_000,
230 userText: "Continue",
231 attachments: [],
232 providerThread,
233 nativeContextEstimate: 0,
234 modelContextWindow: 32_000,
235 };
236 assert.isBelow(handoffBudget(base), 16_000);
237 assert.isBelow(handoffBudget({ ...base, nativeContextEstimate: 8_000 }), handoffBudget(base));
238 assert.equal(handoffBudget({ ...base, userText: "界".repeat(30_000) }), 0);
239 assert.equal(
240 handoffBudget({
241 ...base,
242 providerThread: {
243 ...providerThread,
244 contextUsage: { usedTokens: 23_000, maxTokens: 24_000 },
245 },
246 }),
247 0,
248 );
249 for (const sizeBytes of [100_000, 10 * 1024 * 1024]) {
250 const attachments = [
251 {
252 type: "image",
253 id: "attachment",
254 name: "image.png",
255 mimeType: "image/png",
256 sizeBytes,
257 },
258 ];
259 const withImage = handoffBudget({ ...base, attachments });
260 assert.isAbove(withImage, 4_000);
261 assert.isBelow(withImage, handoffBudget(base));
262 assert.equal(handoffBudget({ ...base, attachments: [...attachments, ...attachments] }), 0);
263 assert.equal(
264 handoffBudget({
265 ...base,
266 tokenCap: 64_000,
267 modelContextWindow: 1_000_000,
268 userText: "x".repeat(70_000),
269 attachments,
270 providerThread: {
271 ...providerThread,
272 contextUsage: { usedTokens: 0, maxTokens: 1_000_000 },
273 },
274 }),
275 64_000,
276 );
277 }
278 assert.equal(handoffBudget({ ...base, tokenCap: 2_000 }), 2_000);
279 });
280​
281 it("keeps measured occupancy across a model change", () => {
282 const previous = {
283 usedTokens: 37_321,
284 maxTokens: 258_400,
285 autoCompactThreshold: 32_000,
286 };
287 assert.isNull(
288 contextUsageForHandoff({
289 sameNativeThread: false,
290 sameSelection: false,
291 reuseTelemetry: false,
292 previousUsage: previous,
293 }),
294 );
295 assert.equal(
296 contextUsageForHandoff({
297 sameNativeThread: true,
298 sameSelection: true,
299 reuseTelemetry: true,
300 previousUsage: previous,
301 }),
302 previous,
303 );
304 assert.deepEqual(
305 contextUsageForHandoff({
306 sameNativeThread: true,
307 sameSelection: false,
308 reuseTelemetry: true,
309 previousUsage: previous,
310 }),
311 { usedTokens: 37_321, maxTokens: 258_400 },
312 );
313 assert.deepEqual(
314 contextUsageForHandoff({
315 sameNativeThread: true,
316 sameSelection: false,
317 reuseTelemetry: false,
318 previousUsage: previous,
319 }),
320 { usedTokens: 37_321, maxTokens: 258_400 },
321 );
322 assert.deepEqual(
323 contextUsageForHandoff({
324 sameNativeThread: true,
325 sameSelection: false,
326 reuseTelemetry: false,
327 previousUsage: { usedTokens: 30_000, maxTokens: 32_000, autoCompactThreshold: 31_000 },
328 knownModelWindow: 1_000_000,
329 }),
330 { usedTokens: 30_000, maxTokens: 1_000_000 },
331 );
332​
333 const preserved = contextUsageForHandoff({
334 sameNativeThread: true,
335 sameSelection: false,
336 reuseTelemetry: false,
337 previousUsage: previous,
338 });
339 assert.equal(
340 handoffBudget({
341 tokenCap: 16_000,
342 userText: "Continue work",
343 attachments: [
344 {
345 type: "image",
346 id: "screenshot-a",
347 name: "a.png",
348 mimeType: "image/png",
349 sizeBytes: 100_000,
350 },
351 {
352 type: "image",
353 id: "screenshot-b",
354 name: "b.png",
355 mimeType: "image/png",
356 sizeBytes: 100_000,
357 },
358 ],
359 providerThread: { ...providerThread, contextUsage: preserved },
360 nativeContextEstimate: 0,
361 modelContextWindow: preserved?.maxTokens,
362 }),
363 16_000,
364 );
365 assert.equal(
366 handoffBudget({
367 tokenCap: 16_000,
368 userText: "Continue work",
369 attachments: [],
370 providerThread,
371 nativeContextEstimate: 120_000,
372 }),
373 0,
374 );
375 });
376 it("reserves context for image batches up to the attachment limit, honoring smaller known windows", () => {
377 let previousBudget = 16_000;
378 for (let count = 1; count <= PROVIDER_SEND_TURN_MAX_ATTACHMENTS; count++) {
379 const input = {
380 tokenCap: 16_000,
381 userText: "Compare these screenshots",
382 providerThread,
383 nativeContextEstimate: 0,
384 attachments: Array.from({ length: count }, (_, index) => ({
385 type: "image",
386 id: `image-${index}`,
387 name: "image.png",
388 mimeType: "image/png",
389 sizeBytes: 100_000,
390 })),
391 };
392 const budget = handoffBudget(input);
393 assert.isAtLeast(budget, 0);
394 assert.isAtMost(budget, previousBudget);
395 previousBudget = budget;
396 if (count <= 8) assert.equal(budget, 16_000);
397 if (count === 10) {
398 assert.isAbove(budget, 0);
399 assert.isBelow(budget, 16_000);
400 }
401 if (count === PROVIDER_SEND_TURN_MAX_ATTACHMENTS) assert.equal(budget, 0);
402 if (budget > 0) {
403 const selected = selectHistory({ messages, coverage: "Recover omitted history", budget });
404 assert.isAtMost(historyCost(selected.messages, selected.context), budget);
405 }
406 assert.equal(handoffBudget({ ...input, modelContextWindow: 2_000_000 }), 16_000);
407 assert.equal(handoffBudget({ ...input, modelContextWindow: 20_000 }), 0);
408 assert.equal(
409 handoffBudget({
410 ...input,
411 providerThread: { ...providerThread, contextUsage: { usedTokens: 0, maxTokens: 20_000 } },
412 }),
413 0,
414 );
415 assert.equal(
416 handoffBudget({
417 ...input,
418 modelContextWindow: 1_000_000,
419 providerThread: {
420 ...providerThread,
421 contextUsage: { usedTokens: 0, maxTokens: 1_000_000, autoCompactThreshold: 20_000 },
422 },
423 }),
424 0,
425 );
426 }
427 });
428});
429​
430describe("handoff delivery", () => {
431 it.effect.each([
432 { native: true, label: "injected" },
433 { native: false, label: "inline" },
434 ])("records omitted recovery coverage separately from $label text", ({ native }) =>
435 Effect.gen(function* () {
436 const omittedBeforeDelivery = TurnItemId.make("item:omitted-during-preparation");
437 const oversized = message("item:oversized", "user", "x".repeat(20_000));
438 let durable: OrchestrationV2ContextHandoff = {
439 ...handoff,
440 history: {
441 ...handoff.history!,
442 messages: [...messages, oversized],
443 omittedItems: 1,
444 omittedItemIds: [omittedBeforeDelivery],
445 },
446 };
447 const result = yield* deliverContextHandoffs({
448 handoffs: [durable],
449 providerThread,
450 budget: 16_000,
451 alreadyDeliveredItemIds: new Set(),
452 inject: (value) => {
453 assert.include(value.context, "omitted 2 items");
454 assert.notInclude(
455 value.messages.map((item) => item.itemId),
456 oversized.itemId,
457 );
458 return Effect.succeed(native);
459 },
460 persist: (value) =>
461 Effect.sync(() => {
462 durable = value;
463 }),
464 });
465 assert.equal(durable.delivery?.status, native ? "injected" : "pending");
466 yield* result.delivered;
467 assert.equal(durable.delivery?.status, native ? "injected" : "inline");
468 assert.deepEqual(
469 durable.delivery?.itemIds,
470 messages.map((item) => item.itemId),
471 );
472 assert.deepEqual(durable.delivery?.omittedItemIds, [omittedBeforeDelivery, oversized.itemId]);
473 assert.deepEqual(decodeHandoff(durable).delivery, durable.delivery);
474 }),
475 );
476​
477 it.effect("persists successful injection before turn start and skips it on retry", () =>
478 Effect.gen(function* () {
479 let durable = handoff;
480 const history: unknown[] = [];
481 const statuses: string[] = [];
482 const input = {
483 providerThread,
484 budget: 16_000,
485 alreadyDeliveredItemIds: new Set<string>(),
486 inject: (value: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
487 Effect.sync(() => {
488 history.push(...historyResponseItems(value.messages, value.context));
489 return true;
490 }),
491 persist: (value: OrchestrationV2ContextHandoff) =>
492 Effect.sync(() => {
493 durable = value;
494 statuses.push(value.delivery!.status);
495 }),
496 };
497 const first = yield* deliverContextHandoffs({ ...input, handoffs: [durable] });
498 assert.equal(first.context, "");
499 assert.deepEqual(statuses, ["pending", "injected"]);
500 assert.lengthOf(history, 3);
501 const second = yield* deliverContextHandoffs({ ...input, handoffs: [durable] });
502 assert.equal(second.context, "");
503 assert.lengthOf(history, 3);
504 }),
505 );
506​
507 it.effect(
508 "uses the same selection on explicit native fallback, marking only accepted input",
509 () =>
510 Effect.gen(function* () {
511 let durable = handoff;
512 const result = yield* deliverContextHandoffs({
513 handoffs: [handoff],
514 providerThread,
515 budget: 16_000,
516 alreadyDeliveredItemIds: new Set(),
517 inject: () => Effect.succeed(false),
518 persist: (value) =>
519 Effect.sync(() => {
520 durable = value;
521 }),
522 });
523 assert.include(result.context, messages[0]!.text);
524 assert.include(result.context, messages[1]!.text);
525 assert.equal(durable.delivery?.status, "pending");
526 yield* result.delivered;
527 assert.equal(durable.delivery?.status, "inline");
528 }),
529 );
530​
531 it.effect(
532 "defers unsupported compaction history until a normal turn without marking delivery uncertain",
533 () =>
534 Effect.gen(function* () {
535 let durable = handoff;
536 const persist = (value: OrchestrationV2ContextHandoff) =>
537 Effect.sync(() => {
538 durable = value;
539 });
540 const compact = yield* deliverContextHandoffs({
541 handoffs: [durable],
542 providerThread,
543 budget: 16_000,
544 alreadyDeliveredItemIds: new Set(),
545 deferInline: true,
546 inject: () => Effect.succeed(false),
547 persist,
548 });
549 yield* compact.delivered;
550 assert.equal(compact.context, "");
551 assert.isUndefined((() => durable.delivery)());
552 const next = yield* deliverContextHandoffs({
553 handoffs: [durable],
554 providerThread,
555 budget: 16_000,
556 alreadyDeliveredItemIds: new Set(),
557 inject: () => Effect.succeed(false),
558 persist,
559 });
560 assert.include(next.context, messages[0]!.text);
561 yield* next.delivered;
562 assert.equal(durable.delivery?.status, "inline");
563 }),
564 );
565​
566 it.effect("bounds accumulated recovery markers while keeping omitted history discoverable", () =>
567 Effect.gen(function* () {
568 const many = Array.from({ length: 100 }, (_, index) => ({
569 ...handoff,
570 id: ContextHandoffId.make(`handoff:retry:${index}`),
571 }));
572 let captured: ProviderAdapter.ProviderAdapterV2HistoricalContext | undefined;
573 const result = yield* deliverContextHandoffs({
574 handoffs: many,
575 providerThread,
576 budget: 2_500,
577 alreadyDeliveredItemIds: new Set(),
578 inject: (history) =>
579 Effect.sync(() => {
580 captured = history;
581 return true;
582 }),
583 persist: () => Effect.void,
584 });
585 assert.equal(result.context, "");
586 assert.isDefined(captured);
587 assert.include(captured.context, "detailed coverage references omitted");
588 assert.include(captured.context, "t3_thread_read");
589 assert.include(captured.context, threadId);
590 assert.isAtMost(historyCost(captured.messages, captured.context), 2_500);
591 assert.isAbove(captured.messages.length, 0);
592 }),
593 );
594​
595 it.effect("does not redeliver after ambiguous injection failure", () =>
596 Effect.gen(function* () {
597 let durable = handoff;
598 let calls = 0;
599 const input = {
600 providerThread,
601 budget: 16_000,
602 alreadyDeliveredItemIds: new Set<string>(),
603 inject: () =>
604 Effect.sync(() => {
605 calls++;
606 }).pipe(Effect.andThen(Effect.fail("connection lost"))),
607 persist: (value: OrchestrationV2ContextHandoff) =>
608 Effect.sync(() => {
609 durable = value;
610 }),
611 };
612 yield* deliverContextHandoffs({ ...input, handoffs: [durable] }).pipe(Effect.result);
613 assert.equal(durable.delivery?.status, "pending");
614 const retry = yield* deliverContextHandoffs({ ...input, handoffs: [durable] }).pipe(
615 Effect.result,
616 );
617 assert.equal(retry._tag, "Failure");
618 assert.equal(calls, 1);
619 }),
620 );
621​
622 it.effect("budgets multiple handoffs together and skips previously delivered items", () =>
623 Effect.gen(function* () {
624 const extra = {
625 ...handoff,
626 id: ContextHandoffId.make("handoff:two"),
627 history: {
628 ...handoff.history!,
629 messages: [...messages, message("item:three", "assistant", "Latest result")],
630 },
631 };
632 let delivered: ReadonlyArray<unknown> = [];
633 yield* deliverContextHandoffs({
634 handoffs: [handoff, extra],
635 providerThread,
636 budget: 2_500,
637 alreadyDeliveredItemIds: new Set(["item:one"]),
638 inject: (value) =>
639 Effect.sync(() => {
640 delivered = historyResponseItems(value.messages, value.context);
641 return true;
642 }),
643 persist: () => Effect.void,
644 });
645 const serialized = yield* encodeJson(delivered);
646 assert.isAtMost(Buffer.byteLength(serialized), 2_500);
647 assert.notInclude(serialized, "Preserve every line");
648 assert.equal(serialized.split("Partial work").length - 1, 1);
649 assert.include(serialized, "Latest result");
650 }),
651 );
652​
653 it.effect("fails before delivery when even the coverage marker cannot fit", () =>
654 Effect.gen(function* () {
655 let calls = 0;
656 const result = yield* deliverContextHandoffs({
657 handoffs: [handoff],
658 providerThread,
659 budget: 0,
660 alreadyDeliveredItemIds: new Set(),
661 inject: () =>
662 Effect.sync(() => {
663 calls++;
664 return true;
665 }),
666 persist: () => Effect.void,
667 }).pipe(Effect.result);
668 assert.equal(result._tag, "Failure");
669 assert.equal(calls, 0);
670 }),
671 );
672});