From fc305a169069f9d0c65b4b2d70b301977e4eab7f Mon Sep 17 00:00:00 2001 From: iceteaSA <171169159+iceteaSA@users.noreply.github.com> Date: Sat, 19 Sep 2026 00:14:16 +0200 Subject: [PATCH] fix(effort): accept merged effort boundaries and emit the last transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consecutive host user records collapse into one wire user message, so a single boundary can legitimately carry several transition markers. The per-message count guard rejected that shape, killing the turn with a synthetic 400; a failed turn leaves a contentless assistant record that cannot separate the user records around it, so the fault self-amplified. Drop the redundant count guard — the flat checks already pin the ordered token identity across all messages. Scope-check every transition on a boundary, not just the first, and emit the last transition's effort for a merged boundary (the first would apply a stale value). --- packages/opencode/src/effort-history.ts | 22 +-- .../opencode/src/tests/effort-history.test.ts | 184 ++++++++++++++++++ packages/opencode/src/tests/index.test.ts | 8 +- 3 files changed, 201 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/effort-history.ts b/packages/opencode/src/effort-history.ts index 2e1c0bf1..b7c715cf 100644 --- a/packages/opencode/src/effort-history.ts +++ b/packages/opencode/src/effort-history.ts @@ -609,16 +609,16 @@ export function applyOpenCodeEffortMarkers( } for (const message of consumed.messages) { - if (message.transitions.length > 1) { - throw new EffortMarkerCorrelationError( - 'Multiple internal Fable 5.1 effort markers on one user boundary', - ) - } - const transition = message.transitions[0] - if (transition && transition.scope !== requestPlan.scope) { - throw new EffortMarkerCorrelationError( - 'Fable 5.1 effort marker scope mismatch', - ) + // Consecutive host user records collapse into one wire message, so a + // boundary may legitimately carry several transitions. The flat checks + // below pin their order and identity; every one still needs its scope + // verified, not just the first. + for (const transition of message.transitions) { + if (transition.scope !== requestPlan.scope) { + throw new EffortMarkerCorrelationError( + 'Fable 5.1 effort marker scope mismatch', + ) + } } if (message.anchors.length > 1) { throw new EffortMarkerCorrelationError( @@ -719,7 +719,7 @@ export function applyOpenCodeEffortMarkers( let inserted = 0 const rewritten: unknown[] = [] for (const message of consumed.messages) { - const transition = message.transitions[0] + const transition = message.transitions.at(-1) if (transition && applyConfig) { rewritten.push({ role: 'system', diff --git a/packages/opencode/src/tests/effort-history.test.ts b/packages/opencode/src/tests/effort-history.test.ts index c0e4a55e..8a841933 100644 --- a/packages/opencode/src/tests/effort-history.test.ts +++ b/packages/opencode/src/tests/effort-history.test.ts @@ -579,4 +579,188 @@ describe('OpenCode Fable 5.1 effort markers', () => { expect(markOpenCodeEffortTransitions(otherModel)).toBeNull() expect(markerTexts(otherModel)).toEqual([]) }) + + test('accepts a merged boundary whose transitions are a correctly-ordered run and emits the last effort', () => { + const messages = [ + user('msg_low', 'ses_merged', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_merged', 'claude-fable-5-1', 'high'), + user('msg_max', 'ses_merged', 'claude-fable-5-1', 'max'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + expect(plan?.markerCount).toBe(2) + + const highMarker = markerTexts([messages[1]!])[0] + const maxMarker = markerTexts([messages[2]!])[0] + const anchor = messages[2]!.parts + .map((part) => part.text) + .find((text) => text.startsWith(EFFORT_ANCHOR_PREFIX)) + expect(highMarker).toBeDefined() + expect(maxMarker).toBeDefined() + expect(anchor).toBeDefined() + + const body: { + model: string + output_config: { effort: string } + messages: unknown[] + } = { + model: 'claude-fable-5-1', + output_config: { effort: 'low' }, + messages: [ + { role: 'user', content: [{ type: 'text', text: 'msg_low' }] }, + { + role: 'user', + content: [ + { type: 'text', text: highMarker }, + { type: 'text', text: maxMarker }, + { type: 'text', text: anchor }, + ], + }, + ], + } + + expect( + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toEqual({ found: 2, inserted: 1 }) + expect(body.messages).toContainEqual({ + role: 'system', + content: [], + output_config: { effort: 'max' }, + }) + expect(body.messages).not.toContainEqual({ + role: 'system', + content: [], + output_config: { effort: 'high' }, + }) + expect(JSON.stringify(body)).not.toContain('cortexkit-internal-effort') + }) + + test('rejects a merged boundary whose transitions are out of order', () => { + const messages = [ + user('msg_low', 'ses_merged_order', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_merged_order', 'claude-fable-5-1', 'high'), + user('msg_max', 'ses_merged_order', 'claude-fable-5-1', 'max'), + ] + const plan = markOpenCodeEffortTransitions(messages) + const highMarker = markerTexts([messages[1]!])[0] + const maxMarker = markerTexts([messages[2]!])[0] + const anchor = messages[2]!.parts + .map((part) => part.text) + .find((text) => text.startsWith(EFFORT_ANCHOR_PREFIX)) + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'low' }, + messages: [ + { role: 'user', content: [{ type: 'text', text: 'msg_low' }] }, + { + role: 'user', + content: [ + { type: 'text', text: maxMarker }, + { type: 'text', text: highMarker }, + { type: 'text', text: anchor }, + ], + }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Fable 5.1 effort marker non-prefix loss') + }) + + test('rejects a merged boundary whose transitions are duplicated', () => { + const messages = [ + user('msg_low', 'ses_merged_dup', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_merged_dup', 'claude-fable-5-1', 'high'), + user('msg_max', 'ses_merged_dup', 'claude-fable-5-1', 'max'), + ] + const plan = markOpenCodeEffortTransitions(messages) + const highMarker = markerTexts([messages[1]!])[0] + const maxMarker = markerTexts([messages[2]!])[0] + const anchor = messages[2]!.parts + .map((part) => part.text) + .find((text) => text.startsWith(EFFORT_ANCHOR_PREFIX)) + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'low' }, + messages: [ + { role: 'user', content: [{ type: 'text', text: 'msg_low' }] }, + { + role: 'user', + content: [ + { type: 'text', text: highMarker }, + { type: 'text', text: highMarker }, + { type: 'text', text: maxMarker }, + { type: 'text', text: anchor }, + ], + }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Fable 5.1 effort marker correlation failed: expected 2, found 3') + }) + + test('rejects a foreign-scope marker at index 1 of a merged boundary', () => { + const messages = [ + user('msg_low', 'ses_merged_scope', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_merged_scope', 'claude-fable-5-1', 'high'), + user('msg_max', 'ses_merged_scope', 'claude-fable-5-1', 'max'), + ] + const plan = markOpenCodeEffortTransitions(messages) + const highMarker = markerTexts([messages[1]!])[0] + const anchor = messages[2]!.parts + .map((part) => part.text) + .find((text) => text.startsWith(EFFORT_ANCHOR_PREFIX)) + + // A valid marker minted for a different session carries a foreign scope. + const foreignMessages = [ + user('msg_foreign_low', 'ses_foreign_scope', 'claude-fable-5-1', 'low'), + user('msg_foreign_high', 'ses_foreign_scope', 'claude-fable-5-1', 'high'), + ] + markOpenCodeEffortTransitions(foreignMessages) + const foreignMarker = markerTexts([foreignMessages[1]!])[0] + expect(foreignMarker).toBeDefined() + + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'low' }, + messages: [ + { role: 'user', content: [{ type: 'text', text: 'msg_low' }] }, + { + role: 'user', + content: [ + { type: 'text', text: highMarker }, + { type: 'text', text: foreignMarker }, + { type: 'text', text: anchor }, + ], + }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Fable 5.1 effort marker scope mismatch') + }) }) diff --git a/packages/opencode/src/tests/index.test.ts b/packages/opencode/src/tests/index.test.ts index 54de6cda..722de497 100644 --- a/packages/opencode/src/tests/index.test.ts +++ b/packages/opencode/src/tests/index.test.ts @@ -65,7 +65,7 @@ import { } from '@cortexkit/anthropic-auth-core' import { SubcCallError } from '@cortexkit/subc-client' import { getOpenCodeClaustrumEnrollmentPaths } from '../claustrum-enrollment-registry' -import { EFFORT_MARKER_PREFIX } from '../effort-history' +import { EFFORT_ANCHOR_PREFIX, EFFORT_MARKER_PREFIX } from '../effort-history' import { AnthropicAuthPlugin } from '../index' import { LANE_START_REQUEST_HEADER, LANE_START_TEXT } from '../lane-start' import { @@ -10542,6 +10542,9 @@ describe('Fable 5.1 request-scoped effort history', () => { const transitionMarker = internalTexts?.find((text) => text.startsWith(EFFORT_MARKER_PREFIX), ) + const anchorMarker = internalTexts?.find((text) => + text.startsWith(EFFORT_ANCHOR_PREFIX), + ) expect(transitionMarker).toBeString() const correlatedHeaders = { headers: {} as Record } await plugin['chat.headers']( @@ -10612,10 +10615,11 @@ describe('Fable 5.1 request-scoped effort history', () => { const duplicateTransition = await send('ses_effort_duplicate_transition', [ transitionMarker, transitionMarker, + anchorMarker, ]) expect(duplicateTransition.status).toBe(400) expect((await duplicateTransition.json()).error.message).toBe( - 'Multiple internal Fable 5.1 effort markers on one user boundary', + 'Fable 5.1 effort marker correlation failed: expected 1, found 2', ) expect(messagesCalled).toBe(false) })