@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.mjs CHANGED
@@ -248,6 +248,41 @@ var ZodSchemaConverter = class {
248
248
  additionalProperties: false
249
249
  });
250
250
  }
251
+ /**
252
+ * JSON Schema for Anthropic structured outputs (`output_config.format`). Anthropic takes
253
+ * standard JSON Schema with a few rules: every object carries `additionalProperties: false`,
254
+ * nullability is `anyOf: [inner, {type: "null"}]` (the OpenAPI `nullable: true` the Google
255
+ * path uses is an unknown keyword there, and a `["string", "null"]` type array is refused
256
+ * next to an enum), unions are `anyOf`, and value constraints (min/max, lengths) are not
257
+ * accepted — the converter never emits those. Optional keys stay out of `required`, which
258
+ * Anthropic allows.
259
+ */
260
+ static toAnthropicSchema(zodSchema) {
261
+ const schema = this.zodToJsonSchema(zodSchema, { strict: true, includeDescription: true, additionalProperties: false });
262
+ return this.toStandardNullability(schema);
263
+ }
264
+ static toStandardNullability(schema) {
265
+ if (Array.isArray(schema)) {
266
+ return schema.map((item) => this.toStandardNullability(item));
267
+ }
268
+ if (typeof schema !== "object" || schema === null) {
269
+ return schema;
270
+ }
271
+ const { nullable, oneOf, ...rest } = schema;
272
+ const result = {};
273
+ for (const [key, value] of Object.entries(rest)) {
274
+ result[key] = key === "enum" || key === "required" ? value : this.toStandardNullability(value);
275
+ }
276
+ if (oneOf) {
277
+ result.anyOf = this.toStandardNullability(oneOf);
278
+ }
279
+ if (nullable === true) {
280
+ const { description, anyOf, ...inner } = result;
281
+ const variants = anyOf ? [...anyOf, { type: "null" }] : [inner, { type: "null" }];
282
+ return { anyOf: variants, ...description !== void 0 ? { description } : {} };
283
+ }
284
+ return result;
285
+ }
251
286
  /**
252
287
  * Convert Zod schema to human-readable prompt description for Anthropic
253
288
  */
@@ -971,12 +1006,17 @@ var SupportedAiModels = {
971
1006
  // ceiling, so 'high' leaves the dynamic range open. No `temperature`: Gemini 4 accepts it
972
1007
  // but Google deprecated sampling params (see the Pro note). Story-pipeline speed as Game
973
1008
  // Master not measured yet — run the host's live story suite before trusting it there.
1009
+ // Pinned medium 2026-10-14 (was high since it shipped on 2026-10-13). Two days of
1010
+ // production turns at high measured p50 21s / p90 40s with three 60s timeouts, and the
1011
+ // werewolf day-2 vote fixture took 16-34s (380-415 output tokens). At medium the same
1012
+ // prompt took 4-11s (75-110 output tokens) with the same vote target and a reason just
1013
+ // as specific — the same trade Grok 4.7 made on 2026-10-06.
974
1014
  [LLM_CONSTANTS.GEMINI_ARGON]: {
975
1015
  displayName: "Gemini 4 Argon",
976
1016
  modelApiName: "gemini-4-argon",
977
1017
  apiKeyName: API_KEY_CONSTANTS.GOOGLE,
978
1018
  hasThinking: true,
979
- reasoningEffort: "high",
1019
+ reasoningEffort: "medium",
980
1020
  tags: ["expensive"]
981
1021
  },
982
1022
  [LLM_CONSTANTS.GEMINI_PRO]: {
@@ -2868,26 +2908,22 @@ var ClaudeAgent = class extends AbstractAgent {
2868
2908
  throw new Error(this.errorMessages.unsupportedRole(role));
2869
2909
  }
2870
2910
  /**
2871
- * New method using Zod with Anthropic's Claude API
2872
- * Since Anthropic doesn't support native JSON schemas, we generate prompt descriptions
2911
+ * Schema-validated ask on Anthropic structured outputs: the Zod schema goes out as JSON
2912
+ * Schema in `output_config.format`, so the API constrains the reply to that shape and the
2913
+ * model cannot answer in another one. Until 0.25.0 the schema was only described in prose
2914
+ * appended to the last message and the reply parsed as text; Sonnet 5.5 once answered a
2915
+ * vote with a tool-call shaped `<invoke name="json">` block instead (2026-10-18). The
2916
+ * field descriptions travel inside the schema, so nothing is appended to the prompt any
2917
+ * more. The reply is still run through the shared lenient parser + Zod on our side: same
2918
+ * error messages for the host's retry hints, and a guard for a null-formatted reply.
2873
2919
  */
2874
2920
  async doAskWithZodSchema(zodSchema, messages) {
2875
2921
  const aiMessages = this.prepareMessages(messages);
2876
2922
  this.logAsking(messages);
2877
2923
  this.logMessages(messages);
2878
2924
  try {
2879
- const schemaDescription = ZodSchemaConverter.toPromptDescription(zodSchema);
2880
- const lastMessage = aiMessages[aiMessages.length - 1];
2881
- const fullPrompt = `${lastMessage.content}
2882
-
2883
- ${schemaDescription}`;
2884
- const messagesWithSchema = [...aiMessages];
2885
- messagesWithSchema[messagesWithSchema.length - 1] = {
2886
- ...lastMessage,
2887
- content: fullPrompt
2888
- };
2889
2925
  const canUseThinking = this.enableThinking;
2890
- const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(messagesWithSchema) : this.convertToAnthropicMessages(messagesWithSchema);
2926
+ const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(aiMessages) : this.convertToAnthropicMessages(aiMessages);
2891
2927
  this.applyCacheBreakpoint(anthropicMessages);
2892
2928
  const params = {
2893
2929
  ...this.defaultParams,
@@ -2911,6 +2947,10 @@ ${schemaDescription}`;
2911
2947
  } else {
2912
2948
  params.temperature = this.temperature;
2913
2949
  }
2950
+ params.output_config = {
2951
+ ...params.output_config ?? {},
2952
+ format: { type: "json_schema", schema: ZodSchemaConverter.toAnthropicSchema(zodSchema) }
2953
+ };
2914
2954
  let response;
2915
2955
  try {
2916
2956
  response = await this.client.messages.create(params);