Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/agents/__tests__/projection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const branch = (perMessageChars: number, count: number): AIMessage[] => {
messages.push(
i % 2 === 0
? (new HumanMessage(content) as unknown as AIMessage)
: new AIMessage(content),
: new AIMessage(content)
);
}
return messages;
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('projectAgentContextUsage', () => {
messages,
tokenCounter: countByChars,
fadingTier: {
v: 1,
v: 2,
budgetTokens: 10_000,
masked: true,
latched: true,
Expand Down
119 changes: 78 additions & 41 deletions src/messages/__tests__/truncationStability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,25 +181,33 @@ describe('fading ladder', () => {
});

it('validates and seeds persisted tiers', () => {
expect(isFadingTier({ v: 1, budgetTokens: 100, masked: true })).toBe(true);
expect(isFadingTier({ v: 2, budgetTokens: 100, masked: true })).toBe(false);
expect(isFadingTier({ v: 1, budgetTokens: 0, masked: true })).toBe(false);
expect(isFadingTier({ v: 1, budgetTokens: 100, masked: 'yes' })).toBe(
expect(isFadingTier({ v: 2, budgetTokens: 100, masked: true })).toBe(true);
/** Version 1 tiers could latch on reconstructed history widths; they re-derive. */
expect(isFadingTier({ v: 1, budgetTokens: 100, masked: true })).toBe(false);
expect(
seedFadingTier(100_000, { v: 1, budgetTokens: 6_250, masked: false })
).toEqual({
v: 2,
budgetTokens: 100_000,
masked: false,
});
expect(isFadingTier({ v: 2, budgetTokens: 0, masked: true })).toBe(false);
expect(isFadingTier({ v: 2, budgetTokens: 100, masked: 'yes' })).toBe(
false
);
expect(isFadingTier(null)).toBe(false);
expect(
seedFadingTier(100_000, { v: 1, budgetTokens: 50_000, masked: true })
seedFadingTier(100_000, { v: 2, budgetTokens: 50_000, masked: true })
).toEqual({
v: 1,
v: 2,
budgetTokens: 50_000,
masked: true,
latched: true,
});
expect(
seedFadingTier(100_000, { v: 1, budgetTokens: 150_000, masked: false })
seedFadingTier(100_000, { v: 2, budgetTokens: 150_000, masked: false })
).toEqual({
v: 1,
v: 2,
budgetTokens: 100_000,
masked: false,
latched: true,
Expand All @@ -217,7 +225,7 @@ describe('fading ladder', () => {
expect(tier).toEqual(createFadingTier(window));
tier = resolveFadingTier(tier, window, { ...base, contextPressure: 0.86 });
expect(tier).toEqual({
v: 1,
v: 2,
budgetTokens: 50_000,
masked: true,
latched: true,
Expand All @@ -236,7 +244,7 @@ describe('fading ladder', () => {

it('survives a mid-run budget correction and the return to the normal window', () => {
const latched: FadingTier = {
v: 1,
v: 2,
budgetTokens: 100_000,
masked: true,
latched: true,
Expand All @@ -258,14 +266,14 @@ describe('fading ladder', () => {

it('keeps a restored tier informative after clamping to a smaller window', () => {
const restored = seedFadingTier(30_000, {
v: 1,
v: 2,
budgetTokens: 50_000,
masked: false,
latched: true,
});

expect(restored).toEqual({
v: 1,
v: 2,
budgetTokens: 30_000,
masked: false,
latched: true,
Expand Down Expand Up @@ -301,7 +309,7 @@ describe('fading ladder', () => {
);

expect(tier).toEqual({
v: 1,
v: 2,
budgetTokens: 50_000,
masked: false,
latched: true,
Expand Down Expand Up @@ -335,7 +343,7 @@ describe('fading ladder', () => {
});

it('masks consumed results to a fraction of the fresh cap with a floor', () => {
const masked: FadingTier = { v: 1, budgetTokens: 100_000, masked: true };
const masked: FadingTier = { v: 2, budgetTokens: 100_000, masked: true };
const caps = resolveFadingCaps(masked);
expect(caps.consumedChars).toBe(Math.floor(caps.resultChars * 0.1));
expect(
Expand Down Expand Up @@ -494,7 +502,7 @@ describe('pruner keeps historical tool results byte-stable across turns', () =>
indexTokenCountMap: countMap(messages),
summarizationEnabled: false,
fadingTier: {
v: 1,
v: 2,
budgetTokens: 5000,
masked: true,
latched: true,
Expand Down Expand Up @@ -800,7 +808,7 @@ describe('pruner keeps historical tool results byte-stable across turns', () =>

it('restores legacy function calls identically across model windows', () => {
const tier: FadingTier = {
v: 1,
v: 2,
budgetTokens: 25_000,
masked: false,
latched: true,
Expand Down Expand Up @@ -993,7 +1001,7 @@ describe('pruner keeps historical tool results byte-stable across turns', () =>
summarizationEnabled: false,
thinkingEnabled: true,
fadingTier: {
v: 1,
v: 2,
budgetTokens: 10_000,
masked: false,
latched: true,
Expand All @@ -1011,7 +1019,7 @@ describe('pruner keeps historical tool results byte-stable across turns', () =>
]);
expect(projected.additional_kwargs.reasoning_content).toBeUndefined();
expect(JSON.stringify(projected.tool_calls?.[0].args)).toContain(
'_truncated'
'_inputPrefix'
);
expect(canonicalMessages[1].content).toBe('');
expect(
Expand Down Expand Up @@ -1135,9 +1143,9 @@ describe('isInformativeFadingTier', () => {
}
);

expect(
hasNonEmptyTextContent([accessorBlock, proxiedBlock] as never)
).toBe(false);
expect(hasNonEmptyTextContent([accessorBlock, proxiedBlock] as never)).toBe(
false
);
expect(hasNonEmptyTextContent(new Proxy([], {}) as never)).toBe(false);
});

Expand All @@ -1148,19 +1156,19 @@ describe('isInformativeFadingTier', () => {
);
expect(
isInformativeFadingTier(
{ v: 1, budgetTokens: 100_000, masked: true },
{ v: 2, budgetTokens: 100_000, masked: true },
100_000
)
).toBe(true);
expect(
isInformativeFadingTier(
{ v: 1, budgetTokens: 50_000, masked: false },
{ v: 2, budgetTokens: 50_000, masked: false },
100_000
)
).toBe(true);
expect(
isInformativeFadingTier(
{ v: 1, budgetTokens: 50_000, masked: false },
{ v: 2, budgetTokens: 50_000, masked: false },
undefined
)
).toBe(false);
Expand All @@ -1177,13 +1185,13 @@ describe('per-agent fading tier persistence', () => {

it('restores keyed tiers without applying the default tier to every agent', () => {
const defaultTier: FadingTier = {
v: 1,
v: 2,
budgetTokens: 50_000,
masked: false,
latched: true,
};
const workerTier: FadingTier = {
v: 1,
v: 2,
budgetTokens: 25_000,
masked: true,
latched: true,
Expand Down Expand Up @@ -1219,9 +1227,17 @@ describe('per-agent fading tier persistence', () => {
it('ignores malformed restored tiers instead of reporting them back', () => {
const graph = new StandardGraph({
agents: [graphAgent('default'), graphAgent('worker')],
fadingTier: { v: 1, budgetTokens: Number.NaN, masked: true } as FadingTier,
fadingTier: {
v: 2,
budgetTokens: Number.NaN,
masked: true,
} as FadingTier,
fadingTiers: {
worker: { v: 1, budgetTokens: 'abc', masked: true } as unknown as FadingTier,
worker: {
v: 2,
budgetTokens: 'abc',
masked: true,
} as unknown as FadingTier,
},
});

Expand All @@ -1233,7 +1249,7 @@ describe('per-agent fading tier persistence', () => {

it('restores a tier learned after a persistent initial summary', () => {
const postSummaryTier: FadingTier = {
v: 1,
v: 2,
budgetTokens: 25_000,
masked: true,
latched: true,
Expand All @@ -1256,7 +1272,7 @@ describe('per-agent fading tier persistence', () => {

it('round-trips prototype-sensitive agent IDs', () => {
const tier: FadingTier = {
v: 1,
v: 2,
budgetTokens: 25_000,
masked: true,
latched: true,
Expand All @@ -1282,31 +1298,46 @@ describe('seedFadingTier provenance', () => {
});

it('latches a seed that was tightened, masked, already latched, or clamped', () => {
expect(seedFadingTier(200_000, { v: 1, budgetTokens: 50_000, masked: false })).toEqual({
v: 1,
expect(
seedFadingTier(200_000, { v: 2, budgetTokens: 50_000, masked: false })
).toEqual({
v: 2,
budgetTokens: 50_000,
masked: false,
latched: true,
});
expect(seedFadingTier(200_000, { v: 1, budgetTokens: 200_000, masked: true })).toEqual({
v: 1,
expect(
seedFadingTier(200_000, { v: 2, budgetTokens: 200_000, masked: true })
).toEqual({
v: 2,
budgetTokens: 200_000,
masked: true,
latched: true,
});
expect(
seedFadingTier(200_000, { v: 1, budgetTokens: 200_000, masked: false, latched: true })
).toEqual({ v: 1, budgetTokens: 200_000, masked: false, latched: true });
expect(seedFadingTier(8_000, { v: 1, budgetTokens: 200_000, masked: false })).toEqual({
v: 1,
seedFadingTier(200_000, {
v: 2,
budgetTokens: 200_000,
masked: false,
latched: true,
})
).toEqual({ v: 2, budgetTokens: 200_000, masked: false, latched: true });
expect(
seedFadingTier(8_000, { v: 2, budgetTokens: 200_000, masked: false })
).toEqual({
v: 2,
budgetTokens: 8_000,
masked: false,
latched: true,
});
});

it('clamps a sub-floor budget up to the ladder floor so caps stay positive', () => {
const seeded = seedFadingTier(200_000, { v: 1, budgetTokens: 1, masked: false });
const seeded = seedFadingTier(200_000, {
v: 2,
budgetTokens: 1,
masked: false,
});
expect(seeded.budgetTokens).toBe(FADING_MIN_BUDGET_TOKENS);
expect(seeded.latched).toBe(true);
expect(resolveFadingCaps(seeded).resultChars).toBeGreaterThan(0);
Expand All @@ -1318,7 +1349,10 @@ describe('fadingRungForExchangeChars', () => {
it('deepens until a result plus its input fit, even when the result cap is tiny', () => {
const window = 200_000;
const target = 8_000;
const exchangeChars = (rung: number, maxToolResultChars?: number): number => {
const exchangeChars = (
rung: number,
maxToolResultChars?: number
): number => {
const budget = fadingBudgetTokens(window, rung);
const resultChars = calculateMaxToolResultChars(budget);
return (
Expand Down Expand Up @@ -1376,7 +1410,10 @@ describe('hasNonEmptyTextContent hostile iteration', () => {
false
);
expect(
hasNonEmptyTextContent({ type: 'text', text: 'x' } as unknown as BaseMessage['content'])
hasNonEmptyTextContent({
type: 'text',
text: 'x',
} as unknown as BaseMessage['content'])
).toBe(false);
});
});
22 changes: 18 additions & 4 deletions src/messages/__tests__/unicodeTruncation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,30 @@ describe('Unicode-safe tool truncation', () => {
for (let cap = 4; cap <= 200; cap++) {
const serialized = serializeToolCallInput(input, cap);
const projected = JSON.parse(serialized) as {
_truncated?: string;
_inputPrefix?: string;
_originalChars?: number;
};
expect(serialized.length).toBeLessThanOrEqual(cap);
expect(projected._truncated?.isWellFormed() ?? true).toBe(true);
expect(projected._inputPrefix?.isWellFormed() ?? true).toBe(true);
expect(serializeToolCallInput(projected, cap)).toBe(serialized);
if (projected._truncated != null) {
if (projected._inputPrefix != null) {
expect(projected._originalChars).toBe(JSON.stringify(input).length);
}
}
});

it('rewrites a legacy custom-tool input marker even when it fits', () => {
const message = customToolMessage(
'run --report\n… [truncated: 5400 chars]'
);

const projected = projectToolCallInputs([message], 1_000);

expect(customToolInput(projected[0])).toBe(
'run --report\n… [shortened; call completed: 5400 chars]'
);
});

it('keeps custom-tool input marker and markerless cuts well-formed without mutating history', () => {
const message = customToolMessage(emojiText);
for (let cap = 4; cap <= 100; cap++) {
Expand All @@ -124,7 +136,9 @@ describe('Unicode-safe tool truncation', () => {
expect(input.isWellFormed()).toBe(true);
expect(input.length).toBeLessThanOrEqual(cap);
if (cap > 50) {
expect(input).toContain(`\n… [truncated: ${emojiText.length} chars]`);
expect(input).toContain(
`\n… [shortened; call completed: ${emojiText.length} chars]`
);
const shrunk = projectToolCallInputs(projected, 50);
const direct = projectToolCallInputs([message], 50);
expect(customToolInput(shrunk[0])).toBe(customToolInput(direct[0]));
Expand Down
Loading
Loading