Skip to content
Open
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
34 changes: 28 additions & 6 deletions src/result-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,17 +158,39 @@ function collectImages(images: ImageContent[]): ImageContent[] | null {
return images;
}

function unwrapJsonEnvelope(record: Record<string, unknown>, fallback: unknown): unknown {
if ('json' in record) {
return record.json ?? null;
}
if ('data' in record) {
return Object.keys(record).length === 1 ? (record.data ?? null) : fallback;
}
return null;
}

function parseStructuredContent(value: unknown): unknown {
if (value === undefined || value === null) {
return null;
}
if (typeof value === 'string') {
return tryParseJson(value);
}
if (typeof value !== 'object') {
return null;
}

return unwrapJsonEnvelope(value as Record<string, unknown>, value);
}

// tryParseJson pulls JSON payloads out of structured responses or raw strings.
function tryParseJson(value: unknown): unknown {
if (value === undefined || value === null) {
return null;
}
if (typeof value === 'object') {
if ('json' in (value as Record<string, unknown>)) {
return (value as Record<string, unknown>).json ?? null;
}
if ('data' in (value as Record<string, unknown>)) {
return (value as Record<string, unknown>).data ?? null;
const unwrapped = unwrapJsonEnvelope(value as Record<string, unknown>, value);
if (unwrapped !== null) {
return unwrapped;
}
}
if (typeof value === 'string') {
Expand Down Expand Up @@ -222,7 +244,7 @@ export function createCallResult<T = unknown>(raw: T): CallResult<T> {
},
json<J = unknown>() {
const collected = getCollectedContent();
const parsedStructured = tryParseJson(collected.structuredContent);
const parsedStructured = parseStructuredContent(collected.structuredContent);
if (parsedStructured !== null) {
return parsedStructured as J;
}
Expand Down
26 changes: 26 additions & 0 deletions tests/cli-output-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@ describe('printCallOutput format selection', () => {
expect(JSON.parse(String(logged))).toEqual({ source: 'json' });
},
],
[
'auto prints the full structuredContent object instead of only its data field',
'auto',
{
structuredContent: {
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
},
content: [
{
type: 'text',
text: '{"status":"error","summary":"Failed to create base: name is required","data":{},"meta":{}}',
},
],
},
(logged: unknown) => {
expect(JSON.parse(String(logged))).toEqual({
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
});
},
],
[
'text prefers text over markdown/json',
'text',
Expand Down
71 changes: 71 additions & 0 deletions tests/result-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,77 @@ describe('createCallResult json extraction', () => {
const result = createCallResult(response);
expect(result.json()).toEqual({ nested: true });
});

it('returns the full structuredContent object when data is only one field among many', () => {
const response = {
raw: {
structuredContent: {
error: {
type: 'USER_ERROR',
message: 'Base name is required and cannot be empty or only whitespace',
retryable: false,
code: 'INVALID_NAME',
},
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
trace_id: 'trace-123',
},
},
};
const result = createCallResult(response);
expect(result.json()).toEqual({
error: {
type: 'USER_ERROR',
message: 'Base name is required and cannot be empty or only whitespace',
retryable: false,
code: 'INVALID_NAME',
},
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
trace_id: 'trace-123',
});
});

it('returns the full parsed json object when text content includes data plus error fields', () => {
const response = {
content: [
{
type: 'text',
text: JSON.stringify({
error: {
type: 'USER_ERROR',
message: 'Base name is required and cannot be empty or only whitespace',
retryable: false,
code: 'INVALID_NAME',
},
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
trace_id: 'trace-123',
}),
},
],
};
const result = createCallResult(response);
expect(result.json()).toEqual({
error: {
type: 'USER_ERROR',
message: 'Base name is required and cannot be empty or only whitespace',
retryable: false,
code: 'INVALID_NAME',
},
status: 'error',
summary: 'Failed to create base: name is required',
data: {},
meta: {},
trace_id: 'trace-123',
});
});
});

describe('createCallResult structured accessors', () => {
Expand Down