Skip to content

Commit a35b572

Browse files
fix(core): #2337 review-4 — move appendTextFallbackForNonObject to leaf wire/textFallback.ts (break value cycle)
1 parent 2cdb395 commit a35b572

4 files changed

Lines changed: 25 additions & 19 deletions

File tree

packages/core/src/wire/codec.ts

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -328,20 +328,3 @@ export function isSpecNotificationMethod(method: string): boolean {
328328
}
329329

330330
const ALL_CODECS: readonly WireCodec[] = [rev2025Codec, rev2026Codec];
331-
332-
/**
333-
* SEP-2106 §4.3 TextContent auto-append, era-agnostic, called from BOTH
334-
* codecs' {@link WireCodec.projectCallToolResult}: when `structuredContent`
335-
* is a non-object value (array/primitive/`null`) and the handler authored no
336-
* `type:'text'` block, append `{type:'text', text: JSON.stringify(value)}`.
337-
* Object-shaped (or absent) `structuredContent` returns the same reference.
338-
*/
339-
export function appendTextFallbackForNonObject(result: CallToolResult): CallToolResult {
340-
const sc = result.structuredContent;
341-
if (sc === undefined) return result;
342-
const isNonObjectValue = typeof sc !== 'object' || sc === null || Array.isArray(sc);
343-
if (!isNonObjectValue) return result;
344-
const hasTextContent = result.content?.some(c => c.type === 'text') ?? false;
345-
if (hasTextContent) return result;
346-
return { ...result, content: [...(result.content ?? []), { type: 'text' as const, text: JSON.stringify(sc) }] };
347-
}

packages/core/src/wire/rev2025-11-25/codec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import type * as z from 'zod/v4';
3030

3131
import type { CallToolResult, Result } from '../../types/types.js';
3232
import type { DecodedResult, EnvelopeIssue, LiftedWireMaterial, OutboundEnvelopeMaterial, ValidateOutcome, WireCodec } from '../codec.js';
33-
import { appendTextFallbackForNonObject } from '../codec.js';
33+
import { appendTextFallbackForNonObject } from '../textFallback.js';
3434
import { isNonObjectJsonSchemaRoot, wrapOutputSchemaForLegacy } from './legacyWrap.js';
3535
import { getNotificationSchema, getRequestSchema, getResultSchema, hasNotificationMethod2025, hasRequestMethod2025 } from './registry.js';
3636
import { CreateMessageResultSchema, CreateMessageResultWithToolsSchema } from './schemas.js';

packages/core/src/wire/rev2026-07-28/codec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
} from '../../types/constants.js';
3737
import type { CallToolResult, Result } from '../../types/types.js';
3838
import type { DecodedResult, EnvelopeIssue, LiftedWireMaterial, OutboundEnvelopeMaterial, ValidateOutcome, WireCodec } from '../codec.js';
39-
import { appendTextFallbackForNonObject } from '../codec.js';
39+
import { appendTextFallbackForNonObject } from '../textFallback.js';
4040
import { fillCacheFields, stampResultType } from './encodeContract.js';
4141
import { getInputRequestSchema2026, getInputResponseSchema2026 } from './inputRequired.js';
4242
import {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { CallToolResult } from '../types/types.js';
2+
3+
/**
4+
* SEP-2106 §4.3 TextContent auto-append, era-agnostic, called from BOTH
5+
* codecs' {@link WireCodec.projectCallToolResult}: when `structuredContent`
6+
* is a non-object value (array/primitive/`null`) and the handler authored no
7+
* `type:'text'` block, append `{type:'text', text: JSON.stringify(value)}`.
8+
* Object-shaped (or absent) `structuredContent` returns the same reference.
9+
*
10+
* Leaf module: imported by both era codec modules, so it must NOT import from
11+
* `./codec.js` (which value-imports the rev codecs at top level — that would
12+
* make a runtime cycle and a TDZ hazard for entries that evaluate a rev codec
13+
* module first).
14+
*/
15+
export function appendTextFallbackForNonObject(result: CallToolResult): CallToolResult {
16+
const sc = result.structuredContent;
17+
if (sc === undefined) return result;
18+
const isNonObjectValue = typeof sc !== 'object' || sc === null || Array.isArray(sc);
19+
if (!isNonObjectValue) return result;
20+
const hasTextContent = result.content?.some(c => c.type === 'text') ?? false;
21+
if (hasTextContent) return result;
22+
return { ...result, content: [...(result.content ?? []), { type: 'text' as const, text: JSON.stringify(sc) }] };
23+
}

0 commit comments

Comments
 (0)