@hiper2d/ai-agents 0.24.0 → 0.25.0

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.
package/dist/index.d.ts CHANGED
@@ -162,6 +162,17 @@ declare class ZodSchemaConverter {
162
162
  * Convert Zod schema to Mistral/DeepSeek JSON Schema format
163
163
  */
164
164
  static toMistralSchema(zodSchema: z.ZodSchema): any;
165
+ /**
166
+ * JSON Schema for Anthropic structured outputs (`output_config.format`). Anthropic takes
167
+ * standard JSON Schema with a few rules: every object carries `additionalProperties: false`,
168
+ * nullability is `anyOf: [inner, {type: "null"}]` (the OpenAPI `nullable: true` the Google
169
+ * path uses is an unknown keyword there, and a `["string", "null"]` type array is refused
170
+ * next to an enum), unions are `anyOf`, and value constraints (min/max, lengths) are not
171
+ * accepted — the converter never emits those. Optional keys stay out of `required`, which
172
+ * Anthropic allows.
173
+ */
174
+ static toAnthropicSchema(zodSchema: z.ZodSchema): any;
175
+ private static toStandardNullability;
165
176
  /**
166
177
  * Convert Zod schema to human-readable prompt description for Anthropic
167
178
  */
@@ -1424,8 +1435,14 @@ declare class ClaudeAgent extends AbstractAgent {
1424
1435
  private buildTokenUsage;
1425
1436
  private convertRole;
1426
1437
  /**
1427
- * New method using Zod with Anthropic's Claude API
1428
- * Since Anthropic doesn't support native JSON schemas, we generate prompt descriptions
1438
+ * Schema-validated ask on Anthropic structured outputs: the Zod schema goes out as JSON
1439
+ * Schema in `output_config.format`, so the API constrains the reply to that shape and the
1440
+ * model cannot answer in another one. Until 0.25.0 the schema was only described in prose
1441
+ * appended to the last message and the reply parsed as text; Sonnet 5.5 once answered a
1442
+ * vote with a tool-call shaped `<invoke name="json">` block instead (2026-10-18). The
1443
+ * field descriptions travel inside the schema, so nothing is appended to the prompt any
1444
+ * more. The reply is still run through the shared lenient parser + Zod on our side: same
1445
+ * error messages for the host's retry hints, and a guard for a null-formatted reply.
1429
1446
  */
1430
1447
  doAskWithZodSchema<T>(zodSchema: z.ZodSchema<T>, messages: AIMessage[]): Promise<[T, string, TokenUsage$1?, string?]>;
1431
1448
  /**
package/dist/index.js CHANGED
@@ -426,6 +426,41 @@ var ZodSchemaConverter = class {
426
426
  additionalProperties: false
427
427
  });
428
428
  }
429
+ /**
430
+ * JSON Schema for Anthropic structured outputs (`output_config.format`). Anthropic takes
431
+ * standard JSON Schema with a few rules: every object carries `additionalProperties: false`,
432
+ * nullability is `anyOf: [inner, {type: "null"}]` (the OpenAPI `nullable: true` the Google
433
+ * path uses is an unknown keyword there, and a `["string", "null"]` type array is refused
434
+ * next to an enum), unions are `anyOf`, and value constraints (min/max, lengths) are not
435
+ * accepted — the converter never emits those. Optional keys stay out of `required`, which
436
+ * Anthropic allows.
437
+ */
438
+ static toAnthropicSchema(zodSchema) {
439
+ const schema = this.zodToJsonSchema(zodSchema, { strict: true, includeDescription: true, additionalProperties: false });
440
+ return this.toStandardNullability(schema);
441
+ }
442
+ static toStandardNullability(schema) {
443
+ if (Array.isArray(schema)) {
444
+ return schema.map((item) => this.toStandardNullability(item));
445
+ }
446
+ if (typeof schema !== "object" || schema === null) {
447
+ return schema;
448
+ }
449
+ const { nullable, oneOf, ...rest } = schema;
450
+ const result = {};
451
+ for (const [key, value] of Object.entries(rest)) {
452
+ result[key] = key === "enum" || key === "required" ? value : this.toStandardNullability(value);
453
+ }
454
+ if (oneOf) {
455
+ result.anyOf = this.toStandardNullability(oneOf);
456
+ }
457
+ if (nullable === true) {
458
+ const { description, anyOf, ...inner } = result;
459
+ const variants = anyOf ? [...anyOf, { type: "null" }] : [inner, { type: "null" }];
460
+ return { anyOf: variants, ...description !== void 0 ? { description } : {} };
461
+ }
462
+ return result;
463
+ }
429
464
  /**
430
465
  * Convert Zod schema to human-readable prompt description for Anthropic
431
466
  */
@@ -1149,12 +1184,17 @@ var SupportedAiModels = {
1149
1184
  // ceiling, so 'high' leaves the dynamic range open. No `temperature`: Gemini 4 accepts it
1150
1185
  // but Google deprecated sampling params (see the Pro note). Story-pipeline speed as Game
1151
1186
  // Master not measured yet — run the host's live story suite before trusting it there.
1187
+ // Pinned medium 2026-10-14 (was high since it shipped on 2026-10-13). Two days of
1188
+ // production turns at high measured p50 21s / p90 40s with three 60s timeouts, and the
1189
+ // werewolf day-2 vote fixture took 16-34s (380-415 output tokens). At medium the same
1190
+ // prompt took 4-11s (75-110 output tokens) with the same vote target and a reason just
1191
+ // as specific — the same trade Grok 4.7 made on 2026-10-06.
1152
1192
  [LLM_CONSTANTS.GEMINI_ARGON]: {
1153
1193
  displayName: "Gemini 4 Argon",
1154
1194
  modelApiName: "gemini-4-argon",
1155
1195
  apiKeyName: API_KEY_CONSTANTS.GOOGLE,
1156
1196
  hasThinking: true,
1157
- reasoningEffort: "high",
1197
+ reasoningEffort: "medium",
1158
1198
  tags: ["expensive"]
1159
1199
  },
1160
1200
  [LLM_CONSTANTS.GEMINI_PRO]: {
@@ -3046,26 +3086,22 @@ var ClaudeAgent = class extends AbstractAgent {
3046
3086
  throw new Error(this.errorMessages.unsupportedRole(role));
3047
3087
  }
3048
3088
  /**
3049
- * New method using Zod with Anthropic's Claude API
3050
- * Since Anthropic doesn't support native JSON schemas, we generate prompt descriptions
3089
+ * Schema-validated ask on Anthropic structured outputs: the Zod schema goes out as JSON
3090
+ * Schema in `output_config.format`, so the API constrains the reply to that shape and the
3091
+ * model cannot answer in another one. Until 0.25.0 the schema was only described in prose
3092
+ * appended to the last message and the reply parsed as text; Sonnet 5.5 once answered a
3093
+ * vote with a tool-call shaped `<invoke name="json">` block instead (2026-10-18). The
3094
+ * field descriptions travel inside the schema, so nothing is appended to the prompt any
3095
+ * more. The reply is still run through the shared lenient parser + Zod on our side: same
3096
+ * error messages for the host's retry hints, and a guard for a null-formatted reply.
3051
3097
  */
3052
3098
  async doAskWithZodSchema(zodSchema, messages) {
3053
3099
  const aiMessages = this.prepareMessages(messages);
3054
3100
  this.logAsking(messages);
3055
3101
  this.logMessages(messages);
3056
3102
  try {
3057
- const schemaDescription = ZodSchemaConverter.toPromptDescription(zodSchema);
3058
- const lastMessage = aiMessages[aiMessages.length - 1];
3059
- const fullPrompt = `${lastMessage.content}
3060
-
3061
- ${schemaDescription}`;
3062
- const messagesWithSchema = [...aiMessages];
3063
- messagesWithSchema[messagesWithSchema.length - 1] = {
3064
- ...lastMessage,
3065
- content: fullPrompt
3066
- };
3067
3103
  const canUseThinking = this.enableThinking;
3068
- const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(messagesWithSchema) : this.convertToAnthropicMessages(messagesWithSchema);
3104
+ const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(aiMessages) : this.convertToAnthropicMessages(aiMessages);
3069
3105
  this.applyCacheBreakpoint(anthropicMessages);
3070
3106
  const params = {
3071
3107
  ...this.defaultParams,
@@ -3089,6 +3125,10 @@ ${schemaDescription}`;
3089
3125
  } else {
3090
3126
  params.temperature = this.temperature;
3091
3127
  }
3128
+ params.output_config = {
3129
+ ...params.output_config ?? {},
3130
+ format: { type: "json_schema", schema: ZodSchemaConverter.toAnthropicSchema(zodSchema) }
3131
+ };
3092
3132
  let response;
3093
3133
  try {
3094
3134
  response = await this.client.messages.create(params);