@ai-sdk/harness-claude-code 1.0.75 → 1.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -348,6 +348,15 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
348
348
  systemPrompt: createClaudeCodeSystemPrompt(start.instructions),
349
349
  thinking: start.thinking,
350
350
  ...(start.effort !== undefined ? { effort: start.effort } : {}),
351
+ ...(start.responseFormat?.type === 'json' &&
352
+ start.responseFormat.schema != null
353
+ ? {
354
+ outputFormat: {
355
+ type: 'json_schema' as const,
356
+ schema: start.responseFormat.schema,
357
+ },
358
+ }
359
+ : {}),
351
360
  includePartialMessages: true,
352
361
  // The `PostCompact` hook carries the compaction summary, which the
353
362
  // `compact_boundary` system message does not. Latch it for the unified
@@ -425,6 +434,20 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
425
434
  if (typeof msg.total_cost_usd === 'number') {
426
435
  totalCostUsd = (totalCostUsd ?? 0) + msg.total_cost_usd;
427
436
  }
437
+ if (
438
+ start.responseFormat?.type === 'json' &&
439
+ msg.structured_output !== undefined
440
+ ) {
441
+ const id = randomUUID();
442
+ emit({ type: 'text-start', id });
443
+ emit({
444
+ type: 'text-delta',
445
+ id,
446
+ delta: JSON.stringify(msg.structured_output),
447
+ });
448
+ emit({ type: 'text-end', id });
449
+ streamEventState.stepOpen = true;
450
+ }
428
451
  if (streamEventState.stepOpen) {
429
452
  emitFinishStep({
430
453
  state: streamEventState,
@@ -43,7 +43,7 @@ function toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {
43
43
  let zType =
44
44
  zodForConst(schema) ??
45
45
  zodForEnum(schema) ??
46
- zodForNullableUnion(schema) ??
46
+ zodForUnion(schema) ??
47
47
  zodForType(schema);
48
48
 
49
49
  if (isNullable(schema)) zType = zType.nullable();
@@ -90,16 +90,16 @@ function zodForEnum(schema: JsonSchemaObject): z.ZodTypeAny | undefined {
90
90
  return zodForLiterals(schema.enum);
91
91
  }
92
92
 
93
- function zodForNullableUnion(
94
- schema: JsonSchemaObject,
95
- ): z.ZodTypeAny | undefined {
93
+ function zodForUnion(schema: JsonSchemaObject): z.ZodTypeAny | undefined {
96
94
  const unionSchemas = schema.anyOf ?? schema.oneOf;
97
95
  if (!unionSchemas || unionSchemas.length < 2) return undefined;
98
96
 
99
- const nonNullSchemas = unionSchemas.filter(item => !isNullOnlySchema(item));
100
- if (nonNullSchemas.length !== 1) return undefined;
101
-
102
- return toZodType(nonNullSchemas[0]).nullable();
97
+ const options = unionSchemas.map(item => toZodType(item)) as unknown as [
98
+ z.ZodTypeAny,
99
+ z.ZodTypeAny,
100
+ ...z.ZodTypeAny[],
101
+ ];
102
+ return z.union(options);
103
103
  }
104
104
 
105
105
  function zodForLiterals(values: JsonLiteral[]): z.ZodTypeAny {
@@ -127,19 +127,6 @@ function isNullable(schema: JsonSchemaObject): boolean {
127
127
  );
128
128
  }
129
129
 
130
- function isNullOnlySchema(schema: JsonSchemaObject): boolean {
131
- return (
132
- schema.type === 'null' ||
133
- (Array.isArray(schema.type) &&
134
- schema.type.length === 1 &&
135
- schema.type[0] === 'null') ||
136
- schema.const === null ||
137
- (Array.isArray(schema.enum) &&
138
- schema.enum.length === 1 &&
139
- schema.enum[0] === null)
140
- );
141
- }
142
-
143
130
  function isJsonSchemaObject(input: unknown): input is JsonSchemaObject {
144
131
  return input != null && typeof input === 'object' && !Array.isArray(input);
145
132
  }
@@ -1540,6 +1540,16 @@ function createSession({
1540
1540
  isResume,
1541
1541
  modelId: model,
1542
1542
  doPromptTurn: async promptOpts => {
1543
+ if (
1544
+ promptOpts.responseFormat?.type === 'json' &&
1545
+ promptOpts.responseFormat.schema == null
1546
+ ) {
1547
+ throw new HarnessCapabilityUnsupportedError({
1548
+ message:
1549
+ "Harness 'claude-code' requires a JSON schema for structured output.",
1550
+ harnessId: 'claude-code',
1551
+ });
1552
+ }
1543
1553
  const control = wireTurn({
1544
1554
  emit: promptOpts.emit,
1545
1555
  abortSignal: promptOpts.abortSignal,
@@ -1553,6 +1563,9 @@ function createSession({
1553
1563
  description: t.description,
1554
1564
  inputSchema: t.inputSchema,
1555
1565
  })),
1566
+ ...(promptOpts.responseFormat == null
1567
+ ? {}
1568
+ : { responseFormat: promptOpts.responseFormat }),
1556
1569
  ...(promptOpts.instructions
1557
1570
  ? { instructions: promptOpts.instructions }
1558
1571
  : {}),
@@ -1576,6 +1589,16 @@ function createSession({
1576
1589
  return control;
1577
1590
  },
1578
1591
  doContinueTurn: async continueOpts => {
1592
+ if (
1593
+ continueOpts.responseFormat?.type === 'json' &&
1594
+ continueOpts.responseFormat.schema == null
1595
+ ) {
1596
+ throw new HarnessCapabilityUnsupportedError({
1597
+ message:
1598
+ "Harness 'claude-code' requires a JSON schema for structured output.",
1599
+ harnessId: 'claude-code',
1600
+ });
1601
+ }
1579
1602
  const control = wireTurn({
1580
1603
  emit: continueOpts.emit,
1581
1604
  abortSignal: continueOpts.abortSignal,
@@ -1611,6 +1634,9 @@ function createSession({
1611
1634
  description: t.description,
1612
1635
  inputSchema: t.inputSchema,
1613
1636
  })),
1637
+ ...(continueOpts.responseFormat == null
1638
+ ? {}
1639
+ : { responseFormat: continueOpts.responseFormat }),
1614
1640
  ...(continueOpts.instructions
1615
1641
  ? { instructions: continueOpts.instructions }
1616
1642
  : {}),