From f96f807bacd33db47bb28ceead09078a97ce9587 Mon Sep 17 00:00:00 2001 From: iceteaSA <171169159+iceteaSA@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:19:43 +0200 Subject: [PATCH 1/2] fix(opencode): accept effort-marked tool continuations --- packages/opencode/src/effort-history.ts | 114 +++++++++++++++- packages/opencode/src/index.ts | 4 + .../opencode/src/tests/effort-history.test.ts | 122 ++++++++++++++++++ packages/opencode/src/tests/index.test.ts | 55 ++++++++ 4 files changed, 291 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/effort-history.ts b/packages/opencode/src/effort-history.ts index 2e1c0bf1..a41b43e7 100644 --- a/packages/opencode/src/effort-history.ts +++ b/packages/opencode/src/effort-history.ts @@ -48,7 +48,11 @@ type RequestEffortPlan = { } export class EffortMarkerCorrelationError extends Error { - constructor(message: string) { + constructor( + message: string, + readonly check = 'unspecified', + readonly details: Record = {}, + ) { super(message) this.name = 'EffortMarkerCorrelationError' } @@ -472,6 +476,67 @@ function consumeInternalMarkers(body: Record): { return { messages } } +function isToolResultContinuation( + messages: ParsedUserMessage[], + userMessageIndex: number, +): boolean { + const userMessage = messages[userMessageIndex]?.value + const assistantMessage = messages[userMessageIndex - 1]?.value + if ( + !isRecord(userMessage) || + userMessage.role !== 'user' || + !Array.isArray(userMessage.content) || + userMessage.content.length === 0 || + !isRecord(assistantMessage) || + assistantMessage.role !== 'assistant' || + !Array.isArray(assistantMessage.content) + ) { + return false + } + const toolUseIds = new Set( + assistantMessage.content.flatMap((block) => + isRecord(block) && + block.type === 'tool_use' && + typeof block.id === 'string' + ? [block.id] + : [], + ), + ) + return userMessage.content.every( + (block) => + isRecord(block) && + block.type === 'tool_result' && + typeof block.tool_use_id === 'string' && + toolUseIds.has(block.tool_use_id), + ) +} + +function hasOnlyToolContinuationsAfterAnchor( + messages: ParsedUserMessage[], + anchorMessageIndex: number, +): boolean { + if (anchorMessageIndex < 0) return false + const trailingUserIndexes = messages.flatMap((message, index) => + index > anchorMessageIndex && + isRecord(message.value) && + message.value.role === 'user' + ? [index] + : [], + ) + return ( + trailingUserIndexes.length > 0 && + trailingUserIndexes.every((index) => + isToolResultContinuation(messages, index), + ) + ) +} + +function providerMessageId(value: unknown): string | null { + if (!isRecord(value)) return null + if (typeof value.id === 'string') return value.id + return typeof value.message_id === 'string' ? value.message_id : null +} + function resolveExpectedPlan( requestPlan: RequestEffortPlan, resolvedPlan: OpenCodeEffortMarkerPlan | undefined, @@ -633,15 +698,41 @@ export function applyOpenCodeEffortMarkers( (message) => isRecord(message.value) && message.value.role === 'user', ) const anchor = anchors[0] + const lastUserMessage = consumed.messages[lastUserMessageIndex]?.value + const validToolContinuationSuffix = hasOnlyToolContinuationsAfterAnchor( + consumed.messages, + anchorMessageIndex, + ) if ( requestPlan.markerCount > 0 && (anchors.length !== 1 || - anchorMessageIndex !== lastUserMessageIndex || + (anchorMessageIndex !== lastUserMessageIndex && + !validToolContinuationSuffix) || !anchor || anchor.scope !== requestPlan.scope) ) { throw new EffortMarkerCorrelationError( - 'Missing or invalid internal Fable 5.1 effort anchor', + 'Missing or invalid internal Fable 5.1 effort anchor placement', + 'anchor_placement', + { + anchorBoundaryId: anchor?.boundary ?? null, + plannedBoundaryId: expectedPlan?.anchor?.boundary ?? null, + lastUserMessageId: providerMessageId(lastUserMessage), + anchorMessageIndex, + lastUserMessageIndex, + validToolContinuationSuffix, + anchorsFound: anchors.length, + markerCount: requestPlan.markerCount, + scope: requestPlan.scope, + foundScope: anchor?.scope ?? null, + expectedAnchorHash: expectedPlan?.anchor + ? digest(expectedPlan.anchor.token) + : null, + foundAnchorHash: anchor ? digest(anchor.token) : null, + anchorMatchesExpected: + expectedPlan?.anchor != null && + anchor?.token === expectedPlan.anchor.token, + }, ) } if (requestPlan.markerCount === 0 && anchors.length !== 0) { @@ -651,7 +742,22 @@ export function applyOpenCodeEffortMarkers( } if (expectedPlan?.anchor && anchor?.token !== expectedPlan.anchor.token) { throw new EffortMarkerCorrelationError( - 'Missing or invalid internal Fable 5.1 effort anchor', + 'Mismatched internal Fable 5.1 effort anchor token', + 'anchor_token', + { + anchorBoundaryId: anchor?.boundary ?? null, + plannedBoundaryId: expectedPlan.anchor.boundary, + lastUserMessageId: providerMessageId(lastUserMessage), + anchorMessageIndex, + lastUserMessageIndex, + anchorsFound: anchors.length, + markerCount: requestPlan.markerCount, + scope: requestPlan.scope, + foundScope: anchor?.scope ?? null, + expectedAnchorHash: digest(expectedPlan.anchor.token), + foundAnchorHash: anchor ? digest(anchor.token) : null, + anchorMatchesExpected: false, + }, ) } let effectiveBaseline = requestPlan.baseline diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 171a9b4d..d295bff2 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -348,6 +348,10 @@ const PRIME_MESSAGES_URL = 'https://api.anthropic.com/v1/messages' function effortMarkerFailureResponse( error: EffortMarkerCorrelationError, ): Response { + logger.warn('effort-history', 'refused uncorrelated Fable 5.1 request', { + check: error.check, + ...error.details, + }) return new Response( JSON.stringify({ type: 'error', diff --git a/packages/opencode/src/tests/effort-history.test.ts b/packages/opencode/src/tests/effort-history.test.ts index c0e4a55e..0ad2624e 100644 --- a/packages/opencode/src/tests/effort-history.test.ts +++ b/packages/opencode/src/tests/effort-history.test.ts @@ -323,6 +323,128 @@ describe('OpenCode Fable 5.1 effort markers', () => { ]) }) + test('accepts authenticated effort history on a tool continuation', () => { + const messages = [ + user('msg_low', 'ses_tool_continuation', 'claude-fable-5-1', 'low'), + assistant('msg_prior', 'ses_tool_continuation'), + user('msg_current', 'ses_tool_continuation', 'claude-fable-5-1', 'high'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + expect(plan?.markerCount).toBe(1) + + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { role: 'user', content: 'msg_low' }, + { role: 'assistant', content: [{ type: 'text', text: 'prior' }] }, + { + role: 'user', + content: messages[2]?.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + }, + { + role: 'assistant', + content: [ + { type: 'tool_use', id: 'tool_1', name: 'Read', input: {} }, + ], + }, + { + role: 'user', + content: [ + { type: 'tool_result', tool_use_id: 'tool_1', content: 'result' }, + ], + }, + ], + } + + expect( + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toEqual({ found: 1, inserted: 1 }) + expect(body.output_config).toEqual({ effort: 'low' }) + expect(JSON.stringify(body)).not.toContain('cortexkit-internal-effort') + }) + + test('rejects a plain user boundary appended after the effort anchor', () => { + const messages = [ + user('msg_low', 'ses_user_suffix', 'claude-fable-5-1', 'low'), + user('msg_current', 'ses_user_suffix', 'claude-fable-5-1', 'high'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { role: 'user', content: 'msg_low' }, + { + role: 'user', + content: messages[1]?.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + }, + { role: 'user', content: 'unexpected boundary' }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Missing or invalid internal Fable 5.1 effort anchor placement') + }) + + test('distinguishes a mismatched anchor token from invalid placement', () => { + const marked = [ + user('msg_low', 'ses_anchor_token', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_anchor_token', 'claude-fable-5-1', 'high'), + user('msg_current_a', 'ses_anchor_token', 'claude-fable-5-1', 'high'), + ] + const expected = [ + user('msg_low', 'ses_anchor_token', 'claude-fable-5-1', 'low'), + user('msg_high', 'ses_anchor_token', 'claude-fable-5-1', 'high'), + user('msg_current_b', 'ses_anchor_token', 'claude-fable-5-1', 'high'), + ] + const markedPlan = markOpenCodeEffortTransitions(marked) + const expectedPlan = markOpenCodeEffortTransitions(expected) + expect(markedPlan).not.toBeNull() + expect(expectedPlan).not.toBeNull() + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: marked.map((message) => ({ + role: 'user', + content: message.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + })), + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan( + expectedPlan as NonNullable, + ), + expectedPlan as NonNullable, + ), + ).toThrow('Mismatched internal Fable 5.1 effort anchor token') + }) + test('rejects non-prefix transition loss even with the resolved plan', () => { const messages = [ user('msg_low', 'ses_non_prefix', 'claude-fable-5-1', 'low'), diff --git a/packages/opencode/src/tests/index.test.ts b/packages/opencode/src/tests/index.test.ts index 54de6cda..e24714a8 100644 --- a/packages/opencode/src/tests/index.test.ts +++ b/packages/opencode/src/tests/index.test.ts @@ -10216,6 +10216,7 @@ describe('Fable 5.1 request-scoped effort history', () => { afterEach(() => { globalThis.fetch = originalFetch + __setLogTestSink(null) }) test('preserves effort boundaries when OpenCode lowers multiple assistant records into one message', async () => { @@ -10542,7 +10543,11 @@ describe('Fable 5.1 request-scoped effort history', () => { const transitionMarker = internalTexts?.find((text) => text.startsWith(EFFORT_MARKER_PREFIX), ) + const anchorMarker = internalTexts?.find((text) => + text.includes('cortexkit-internal-effort-anchor'), + ) expect(transitionMarker).toBeString() + expect(anchorMarker).toBeString() const correlatedHeaders = { headers: {} as Record } await plugin['chat.headers']( { @@ -10617,6 +10622,56 @@ describe('Fable 5.1 request-scoped effort history', () => { expect((await duplicateTransition.json()).error.message).toBe( 'Multiple internal Fable 5.1 effort markers on one user boundary', ) + + const refusalLogs: LogTestRecord[] = [] + __setLogTestSink((record) => refusalLogs.push(record)) + const misplacedAnchor = await auth.fetch(MESSAGES_URL, { + method: 'POST', + headers: { + 'x-session-affinity': 'ses_effort_misplaced_anchor', + ...(effortPlanHeader + ? { 'x-cortexkit-effort-plan': effortPlanHeader } + : {}), + }, + body: JSON.stringify({ + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { + role: 'user', + content: [ + { type: 'text', text: 'correlation failure' }, + { type: 'text', text: transitionMarker }, + { type: 'text', text: anchorMarker }, + ], + }, + { role: 'user', content: 'unexpected boundary' }, + ], + }), + }) + expect(misplacedAnchor.status).toBe(400) + expect((await misplacedAnchor.json()).error.message).toBe( + 'Missing or invalid internal Fable 5.1 effort anchor placement', + ) + expect(refusalLogs).toContainEqual({ + level: 'warn', + channel: 'effort-history', + message: 'refused uncorrelated Fable 5.1 request', + payload: expect.objectContaining({ + check: 'anchor_placement', + anchorBoundaryId: 'msg_marked_high', + plannedBoundaryId: 'msg_marked_high', + lastUserMessageId: null, + anchorMessageIndex: 0, + lastUserMessageIndex: 1, + anchorsFound: 1, + markerCount: 1, + validToolContinuationSuffix: false, + expectedAnchorHash: expect.stringMatching(/^[0-9a-f]{64}$/), + foundAnchorHash: expect.stringMatching(/^[0-9a-f]{64}$/), + anchorMatchesExpected: true, + }), + }) expect(messagesCalled).toBe(false) }) }) From d673403788fe211a597d9f81bed3dfcbcdaeb016 Mon Sep 17 00:00:00 2001 From: iceteaSA <171169159+iceteaSA@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:28:10 +0200 Subject: [PATCH 2/2] test(opencode): pin effort continuation pairing --- .../opencode/src/tests/effort-history.test.ts | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/packages/opencode/src/tests/effort-history.test.ts b/packages/opencode/src/tests/effort-history.test.ts index 0ad2624e..3044c6aa 100644 --- a/packages/opencode/src/tests/effort-history.test.ts +++ b/packages/opencode/src/tests/effort-history.test.ts @@ -406,6 +406,133 @@ describe('OpenCode Fable 5.1 effort markers', () => { ).toThrow('Missing or invalid internal Fable 5.1 effort anchor placement') }) + test('rejects a plain user turn after an assistant reply', () => { + const messages = [ + user('msg_low', 'ses_plain_turn', 'claude-fable-5-1', 'low'), + user('msg_current', 'ses_plain_turn', 'claude-fable-5-1', 'high'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { role: 'user', content: 'msg_low' }, + { + role: 'user', + content: messages[1]?.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + }, + { role: 'assistant', content: [{ type: 'text', text: 'reply' }] }, + { role: 'user', content: [{ type: 'text', text: 'next turn' }] }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Missing or invalid internal Fable 5.1 effort anchor placement') + }) + + test('rejects a tool result that does not match the preceding tool use', () => { + const messages = [ + user('msg_low', 'ses_tool_mismatch', 'claude-fable-5-1', 'low'), + user('msg_current', 'ses_tool_mismatch', 'claude-fable-5-1', 'high'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { role: 'user', content: 'msg_low' }, + { + role: 'user', + content: messages[1]?.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + }, + { + role: 'assistant', + content: [ + { type: 'tool_use', id: 'tool_expected', name: 'Read', input: {} }, + ], + }, + { + role: 'user', + content: [ + { + type: 'tool_result', + tool_use_id: 'tool_other', + content: 'result', + }, + ], + }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Missing or invalid internal Fable 5.1 effort anchor placement') + }) + + test('rejects a tool-result user message mixed with text', () => { + const messages = [ + user('msg_low', 'ses_mixed_tool_result', 'claude-fable-5-1', 'low'), + user('msg_current', 'ses_mixed_tool_result', 'claude-fable-5-1', 'high'), + ] + const plan = markOpenCodeEffortTransitions(messages) + expect(plan).not.toBeNull() + const body = { + model: 'claude-fable-5-1', + output_config: { effort: 'high' }, + messages: [ + { role: 'user', content: 'msg_low' }, + { + role: 'user', + content: messages[1]?.parts.map((part) => ({ + type: 'text', + text: part.text, + })), + }, + { + role: 'assistant', + content: [ + { type: 'tool_use', id: 'tool_1', name: 'Read', input: {} }, + ], + }, + { + role: 'user', + content: [ + { type: 'tool_result', tool_use_id: 'tool_1', content: 'result' }, + { type: 'text', text: 'extra user text' }, + ], + }, + ], + } + + expect(() => + applyOpenCodeEffortMarkers( + body, + true, + encodeOpenCodeEffortPlan(plan as NonNullable), + plan as NonNullable, + ), + ).toThrow('Missing or invalid internal Fable 5.1 effort anchor placement') + }) + test('distinguishes a mismatched anchor token from invalid placement', () => { const marked = [ user('msg_low', 'ses_anchor_token', 'claude-fable-5-1', 'low'),