@hiper2d/ai-agents 0.24.1 → 0.26.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
  */
@@ -2873,26 +2908,22 @@ var ClaudeAgent = class extends AbstractAgent {
2873
2908
  throw new Error(this.errorMessages.unsupportedRole(role));
2874
2909
  }
2875
2910
  /**
2876
- * New method using Zod with Anthropic's Claude API
2877
- * 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.
2878
2919
  */
2879
2920
  async doAskWithZodSchema(zodSchema, messages) {
2880
2921
  const aiMessages = this.prepareMessages(messages);
2881
2922
  this.logAsking(messages);
2882
2923
  this.logMessages(messages);
2883
2924
  try {
2884
- const schemaDescription = ZodSchemaConverter.toPromptDescription(zodSchema);
2885
- const lastMessage = aiMessages[aiMessages.length - 1];
2886
- const fullPrompt = `${lastMessage.content}
2887
-
2888
- ${schemaDescription}`;
2889
- const messagesWithSchema = [...aiMessages];
2890
- messagesWithSchema[messagesWithSchema.length - 1] = {
2891
- ...lastMessage,
2892
- content: fullPrompt
2893
- };
2894
2925
  const canUseThinking = this.enableThinking;
2895
- const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(messagesWithSchema) : this.convertToAnthropicMessages(messagesWithSchema);
2926
+ const anthropicMessages = canUseThinking ? this.convertToAnthropicMessagesWithThinking(aiMessages) : this.convertToAnthropicMessages(aiMessages);
2896
2927
  this.applyCacheBreakpoint(anthropicMessages);
2897
2928
  const params = {
2898
2929
  ...this.defaultParams,
@@ -2916,6 +2947,10 @@ ${schemaDescription}`;
2916
2947
  } else {
2917
2948
  params.temperature = this.temperature;
2918
2949
  }
2950
+ params.output_config = {
2951
+ ...params.output_config ?? {},
2952
+ format: { type: "json_schema", schema: ZodSchemaConverter.toAnthropicSchema(zodSchema) }
2953
+ };
2919
2954
  let response;
2920
2955
  try {
2921
2956
  response = await this.client.messages.create(params);