extrait 0.4.0 → 0.5.2

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.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
1
4
  // src/extract.ts
2
5
  import { jsonrepair } from "jsonrepair";
3
6
 
@@ -697,45 +700,38 @@ function unwrap(schema) {
697
700
  let optional = false;
698
701
  let nullable = false;
699
702
  while (true) {
700
- const typeName = current?._def?.typeName;
703
+ const typeName = current?._def?.type;
701
704
  if (!typeName) {
702
705
  break;
703
706
  }
704
- if (typeName === "ZodOptional") {
707
+ if (typeName === "optional") {
705
708
  optional = true;
706
709
  current = current._def?.innerType ?? current;
707
710
  continue;
708
711
  }
709
- if (typeName === "ZodDefault") {
712
+ if (typeName === "default") {
710
713
  optional = true;
711
714
  current = current._def?.innerType ?? current;
712
715
  continue;
713
716
  }
714
- if (typeName === "ZodNullable") {
717
+ if (typeName === "nullable") {
715
718
  nullable = true;
716
719
  current = current._def?.innerType ?? current;
717
720
  continue;
718
721
  }
719
- if (typeName === "ZodEffects") {
720
- current = current._def?.schema ?? current;
721
- continue;
722
- }
723
- if (typeName === "ZodBranded") {
724
- current = current._def?.type ?? current;
725
- continue;
726
- }
727
- if (typeName === "ZodCatch") {
728
- current = current._def?.innerType ?? current;
722
+ if (typeName === "pipe") {
723
+ const outType = current._def?.out?._def?.type;
724
+ if (outType === "transform") {
725
+ current = current._def?.in ?? current;
726
+ } else {
727
+ current = current._def?.out ?? current;
728
+ }
729
729
  continue;
730
730
  }
731
- if (typeName === "ZodReadonly") {
731
+ if (typeName === "catch" || typeName === "readonly") {
732
732
  current = current._def?.innerType ?? current;
733
733
  continue;
734
734
  }
735
- if (typeName === "ZodPipeline") {
736
- current = current._def?.out ?? current;
737
- continue;
738
- }
739
735
  break;
740
736
  }
741
737
  return {
@@ -749,81 +745,74 @@ function formatCore(schema, depth, seen) {
749
745
  return "unknown";
750
746
  }
751
747
  seen.add(schema);
752
- const typeName = schema?._def?.typeName;
748
+ const typeName = schema?._def?.type;
753
749
  switch (typeName) {
754
- case "ZodString":
750
+ case "string":
755
751
  return "string";
756
- case "ZodNumber":
752
+ case "number":
757
753
  return isIntegerNumber(schema) ? "int" : "number";
758
- case "ZodBoolean":
754
+ case "boolean":
759
755
  return "boolean";
760
- case "ZodBigInt":
756
+ case "bigint":
761
757
  return "bigint";
762
- case "ZodDate":
758
+ case "date":
763
759
  return "Date";
764
- case "ZodUndefined":
760
+ case "undefined":
765
761
  return "undefined";
766
- case "ZodNull":
762
+ case "null":
767
763
  return "null";
768
- case "ZodAny":
764
+ case "any":
769
765
  return "any";
770
- case "ZodUnknown":
766
+ case "unknown":
771
767
  return "unknown";
772
- case "ZodNever":
768
+ case "never":
773
769
  return "never";
774
- case "ZodVoid":
770
+ case "void":
775
771
  return "void";
776
- case "ZodLiteral": {
777
- const value = schema._def?.value;
772
+ case "literal": {
773
+ const value = schema._def?.values?.[0];
778
774
  return JSON.stringify(value);
779
775
  }
780
- case "ZodEnum": {
781
- const values = schema._def?.values ?? [];
782
- return values.map((value) => JSON.stringify(value)).join(" | ") || "string";
776
+ case "enum": {
777
+ const entries = schema._def?.entries;
778
+ const values = Object.values(entries ?? {});
779
+ const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
780
+ return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
783
781
  }
784
- case "ZodNativeEnum": {
785
- const values = Object.values(schema._def?.values ?? {});
786
- const unique = [...new Set(values.filter((value) => typeof value !== "string" || Number.isNaN(Number(value))))];
787
- return unique.map((value) => JSON.stringify(value)).join(" | ") || "string";
788
- }
789
- case "ZodArray": {
790
- const inner = formatType(schema._def?.type ?? schema, depth, seen);
782
+ case "array": {
783
+ const inner = formatType(schema._def?.element ?? schema, depth, seen);
791
784
  return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
792
785
  }
793
- case "ZodTuple": {
786
+ case "tuple": {
794
787
  const items = (schema._def?.items ?? []).map((item) => formatType(item, depth, seen));
795
788
  return `[${items.join(", ")}]`;
796
789
  }
797
- case "ZodUnion": {
790
+ case "union": {
798
791
  const options = (schema._def?.options ?? []).map((option) => formatType(option, depth, seen));
799
792
  return options.join(" | ") || "unknown";
800
793
  }
801
- case "ZodDiscriminatedUnion": {
802
- const options = Array.from((schema._def?.options ?? new Map).values()).map((option) => formatType(option, depth, seen));
803
- return options.join(" | ") || "unknown";
804
- }
805
- case "ZodIntersection": {
794
+ case "intersection": {
806
795
  const left = formatType(schema._def?.left ?? schema, depth, seen);
807
796
  const right = formatType(schema._def?.right ?? schema, depth, seen);
808
797
  return `${left} & ${right}`;
809
798
  }
810
- case "ZodRecord": {
799
+ case "record": {
811
800
  const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
812
801
  const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
813
802
  return `Record<${keyType}, ${valueType}>`;
814
803
  }
815
- case "ZodMap": {
804
+ case "map": {
816
805
  const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
817
806
  const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
818
807
  return `Map<${keyType}, ${valueType}>`;
819
808
  }
820
- case "ZodSet": {
809
+ case "set": {
821
810
  const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
822
811
  return `Set<${valueType}>`;
823
812
  }
824
- case "ZodObject":
813
+ case "object":
825
814
  return formatObject(schema, depth, seen);
826
- case "ZodLazy":
815
+ case "lazy":
827
816
  return "unknown";
828
817
  default:
829
818
  return "unknown";
@@ -859,40 +848,28 @@ function requiresParentheses(typeText) {
859
848
  }
860
849
  function isIntegerNumber(schema) {
861
850
  const checks = schema._def?.checks ?? [];
862
- return checks.some((check) => check.kind === "int");
851
+ return checks.some((check) => check.isInt === true);
863
852
  }
864
853
  function readSchemaDescription(schema) {
865
854
  let current = schema;
866
- while (current?._def?.typeName) {
867
- const direct = current._def.description;
868
- if (typeof direct === "string" && direct.trim().length > 0) {
869
- return sanitizeDescription(direct);
870
- }
871
- const fallback = current.description;
872
- if (typeof fallback === "string" && fallback.trim().length > 0) {
873
- return sanitizeDescription(fallback);
855
+ while (current?._def?.type) {
856
+ const desc = current.description;
857
+ if (typeof desc === "string" && desc.trim().length > 0) {
858
+ return sanitizeDescription(desc);
874
859
  }
875
- const typeName = current._def.typeName;
876
- if (typeName === "ZodOptional" || typeName === "ZodDefault" || typeName === "ZodNullable") {
860
+ const typeName = current._def.type;
861
+ if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
877
862
  current = current._def.innerType ?? current;
878
863
  continue;
879
864
  }
880
- if (typeName === "ZodEffects") {
881
- current = current._def.schema ?? current;
882
- continue;
883
- }
884
- if (typeName === "ZodBranded") {
885
- current = current._def.type ?? current;
865
+ if (typeName === "pipe") {
866
+ current = current._def.in ?? current;
886
867
  continue;
887
868
  }
888
- if (typeName === "ZodCatch" || typeName === "ZodReadonly") {
869
+ if (typeName === "catch" || typeName === "readonly") {
889
870
  current = current._def.innerType ?? current;
890
871
  continue;
891
872
  }
892
- if (typeName === "ZodPipeline") {
893
- current = current._def.out ?? current;
894
- continue;
895
- }
896
873
  break;
897
874
  }
898
875
  return;
@@ -2177,6 +2154,12 @@ function buildHeaders(options) {
2177
2154
  };
2178
2155
  }
2179
2156
  function buildMessages(request) {
2157
+ if (Array.isArray(request.messages) && request.messages.length > 0) {
2158
+ return request.messages.map((message) => toOpenAIMessage(message));
2159
+ }
2160
+ if (typeof request.prompt !== "string" || request.prompt.trim().length === 0) {
2161
+ throw new Error("LLMRequest must include a prompt or messages.");
2162
+ }
2180
2163
  const messages = [];
2181
2164
  if (request.systemPrompt) {
2182
2165
  messages.push({ role: "system", content: request.systemPrompt });
@@ -2188,18 +2171,16 @@ function buildResponsesInput(request) {
2188
2171
  if (isRecord2(request.body) && "input" in request.body) {
2189
2172
  return request.body.input;
2190
2173
  }
2191
- const input = [];
2192
- if (request.systemPrompt) {
2193
- input.push({
2194
- role: "system",
2195
- content: request.systemPrompt
2196
- });
2174
+ if (Array.isArray(request.messages) && request.messages.length > 0) {
2175
+ return request.messages.map((message) => toOpenAIMessage(message));
2197
2176
  }
2198
- input.push({
2199
- role: "user",
2200
- content: request.prompt
2201
- });
2202
- return input;
2177
+ return buildMessages(request);
2178
+ }
2179
+ function toOpenAIMessage(message) {
2180
+ return {
2181
+ role: message.role,
2182
+ content: message.content
2183
+ };
2203
2184
  }
2204
2185
  function toResponsesTools(tools) {
2205
2186
  if (!Array.isArray(tools) || tools.length === 0) {
@@ -2611,6 +2592,7 @@ function createAnthropicCompatibleAdapter(options) {
2611
2592
  if (hasMCPClients(request.mcpClients)) {
2612
2593
  return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
2613
2594
  }
2595
+ const input = resolveAnthropicInput(request);
2614
2596
  const response = await fetcher(buildURL(options.baseURL, path), {
2615
2597
  method: "POST",
2616
2598
  headers: buildHeaders2(options),
@@ -2618,8 +2600,8 @@ function createAnthropicCompatibleAdapter(options) {
2618
2600
  ...options.defaultBody,
2619
2601
  ...request.body,
2620
2602
  model: options.model,
2621
- system: request.systemPrompt,
2622
- messages: [{ role: "user", content: request.prompt }],
2603
+ system: input.systemPrompt,
2604
+ messages: input.messages,
2623
2605
  temperature: request.temperature,
2624
2606
  max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2625
2607
  stream: true
@@ -2670,6 +2652,7 @@ function createAnthropicCompatibleAdapter(options) {
2670
2652
  };
2671
2653
  }
2672
2654
  async function completePassThrough(options, fetcher, path, request) {
2655
+ const input = resolveAnthropicInput(request);
2673
2656
  const response = await fetcher(buildURL(options.baseURL, path), {
2674
2657
  method: "POST",
2675
2658
  headers: buildHeaders2(options),
@@ -2677,8 +2660,8 @@ async function completePassThrough(options, fetcher, path, request) {
2677
2660
  ...options.defaultBody,
2678
2661
  ...request.body,
2679
2662
  model: options.model,
2680
- system: request.systemPrompt,
2681
- messages: [{ role: "user", content: request.prompt }],
2663
+ system: input.systemPrompt,
2664
+ messages: input.messages,
2682
2665
  temperature: request.temperature,
2683
2666
  max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
2684
2667
  stream: false
@@ -2705,7 +2688,8 @@ async function completePassThrough(options, fetcher, path, request) {
2705
2688
  }
2706
2689
  async function completeWithMCPToolLoop(options, fetcher, path, request) {
2707
2690
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
2708
- let messages = [{ role: "user", content: request.prompt }];
2691
+ const input = resolveAnthropicInput(request);
2692
+ let messages = input.messages;
2709
2693
  let aggregatedUsage;
2710
2694
  let finishReason;
2711
2695
  let lastPayload;
@@ -2721,7 +2705,7 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2721
2705
  ...options.defaultBody,
2722
2706
  ...request.body,
2723
2707
  model: options.model,
2724
- system: request.systemPrompt,
2708
+ system: input.systemPrompt,
2725
2709
  messages,
2726
2710
  temperature: request.temperature,
2727
2711
  max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
@@ -2788,7 +2772,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2788
2772
  }
2789
2773
  async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks) {
2790
2774
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
2791
- let messages = [{ role: "user", content: request.prompt }];
2775
+ const input = resolveAnthropicInput(request);
2776
+ let messages = input.messages;
2792
2777
  let aggregatedUsage;
2793
2778
  let finishReason;
2794
2779
  let lastPayload;
@@ -2805,7 +2790,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2805
2790
  ...options.defaultBody,
2806
2791
  ...request.body,
2807
2792
  model: options.model,
2808
- system: request.systemPrompt,
2793
+ system: input.systemPrompt,
2809
2794
  messages,
2810
2795
  temperature: request.temperature,
2811
2796
  max_tokens: resolveMaxTokens(request.maxTokens, options.defaultMaxTokens),
@@ -2916,6 +2901,59 @@ function buildHeaders2(options) {
2916
2901
  ...options.headers
2917
2902
  };
2918
2903
  }
2904
+ function resolveAnthropicInput(request) {
2905
+ if (Array.isArray(request.messages) && request.messages.length > 0) {
2906
+ return toAnthropicInput(request.messages);
2907
+ }
2908
+ if (typeof request.prompt !== "string" || request.prompt.trim().length === 0) {
2909
+ throw new Error("LLMRequest must include a prompt or messages.");
2910
+ }
2911
+ return {
2912
+ systemPrompt: request.systemPrompt,
2913
+ messages: [{ role: "user", content: request.prompt }]
2914
+ };
2915
+ }
2916
+ function toAnthropicInput(messages) {
2917
+ const systemParts = [];
2918
+ const normalizedMessages = [];
2919
+ let sawNonSystem = false;
2920
+ for (const message of messages) {
2921
+ if (message.role === "system") {
2922
+ if (sawNonSystem) {
2923
+ throw new Error('Anthropic-compatible messages only support "system" turns at the beginning.');
2924
+ }
2925
+ systemParts.push(stringifyAnthropicSystemContent(message.content));
2926
+ continue;
2927
+ }
2928
+ sawNonSystem = true;
2929
+ normalizedMessages.push({
2930
+ role: message.role,
2931
+ content: message.content
2932
+ });
2933
+ }
2934
+ if (normalizedMessages.length === 0) {
2935
+ throw new Error("Anthropic-compatible requests require at least one non-system message.");
2936
+ }
2937
+ return {
2938
+ systemPrompt: systemParts.length > 0 ? systemParts.join(`
2939
+
2940
+ `) : undefined,
2941
+ messages: normalizedMessages
2942
+ };
2943
+ }
2944
+ function stringifyAnthropicSystemContent(content) {
2945
+ if (typeof content === "string") {
2946
+ return content;
2947
+ }
2948
+ if (content === null || content === undefined) {
2949
+ return "";
2950
+ }
2951
+ try {
2952
+ return JSON.stringify(content, null, 2) ?? "";
2953
+ } catch {
2954
+ return String(content);
2955
+ }
2956
+ }
2919
2957
  function resolveMaxTokens(value, fallback) {
2920
2958
  const requested = toFiniteNumber(value);
2921
2959
  if (requested !== undefined && requested > 0) {
@@ -3802,22 +3840,25 @@ async function structured(adapter, schemaOrOptions, promptInput, callOptions) {
3802
3840
  const useOutdent = normalized.outdent ?? true;
3803
3841
  const resolvedPrompt = applyPromptOutdent(resolvePrompt(normalized.prompt, { mode }), useOutdent);
3804
3842
  const resolvedSystemPrompt = applyOutdentToOptionalPrompt(normalized.systemPrompt, useOutdent);
3805
- const prompt = shouldInjectFormat(resolvedPrompt.prompt, normalized.schemaInstruction) ? formatPrompt(normalized.schema, resolvedPrompt.prompt, {
3806
- schemaInstruction: normalized.schemaInstruction
3807
- }) : resolvedPrompt.prompt.trim();
3808
- const systemPrompt = mergeSystemPrompts(resolvedPrompt.systemPrompt, resolvedSystemPrompt);
3843
+ const preparedPrompt = prepareStructuredPromptPayload(resolvedPrompt, resolvedSystemPrompt, normalized.schema, normalized.schemaInstruction);
3844
+ const resolvedRequest = normalized.timeout?.tool !== undefined && normalized.request?.mcpClients !== undefined ? {
3845
+ ...normalized.request,
3846
+ mcpClients: applyToolTimeout(normalized.request.mcpClients, normalized.timeout.tool)
3847
+ } : normalized.request;
3809
3848
  const first = await executeAttempt(adapter, {
3810
- prompt,
3849
+ prompt: preparedPrompt.prompt,
3850
+ messages: preparedPrompt.messages,
3811
3851
  schema: normalized.schema,
3812
3852
  parseOptions,
3813
3853
  stream: streamConfig,
3814
- request: normalized.request,
3815
- systemPrompt,
3854
+ request: resolvedRequest,
3855
+ systemPrompt: preparedPrompt.systemPrompt,
3816
3856
  observe: normalized.observe,
3817
3857
  debug: debugConfig,
3818
3858
  attemptNumber: 1,
3819
3859
  selfHeal: false,
3820
- selfHealEnabled: selfHealConfig.enabled
3860
+ selfHealEnabled: selfHealConfig.enabled,
3861
+ timeout: normalized.timeout
3821
3862
  });
3822
3863
  attempts.push(first.trace);
3823
3864
  if (first.trace.success) {
@@ -3871,13 +3912,14 @@ async function structured(adapter, schemaOrOptions, promptInput, callOptions) {
3871
3912
  schema: normalized.schema,
3872
3913
  parseOptions,
3873
3914
  stream: streamConfig,
3874
- request: normalized.request,
3875
- systemPrompt,
3915
+ request: resolvedRequest,
3916
+ systemPrompt: preparedPrompt.systemPrompt,
3876
3917
  observe: normalized.observe,
3877
3918
  debug: debugConfig,
3878
3919
  attemptNumber,
3879
3920
  selfHeal: true,
3880
- selfHealEnabled: selfHealConfig.enabled
3921
+ selfHealEnabled: selfHealConfig.enabled,
3922
+ timeout: normalized.timeout
3881
3923
  });
3882
3924
  attempts.push(healed.trace);
3883
3925
  if (healed.trace.success) {
@@ -3934,12 +3976,15 @@ function isPromptResolver(value) {
3934
3976
  return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
3935
3977
  }
3936
3978
  function normalizePromptPayload(value) {
3937
- if (typeof value.prompt !== "string") {
3938
- throw new Error("Structured prompt payload must include a string prompt.");
3979
+ const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
3980
+ const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
3981
+ if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
3982
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
3939
3983
  }
3940
3984
  return {
3941
- prompt: value.prompt,
3942
- systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined
3985
+ prompt,
3986
+ systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
3987
+ messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
3943
3988
  };
3944
3989
  }
3945
3990
  function applyPromptOutdent(payload, enabled) {
@@ -3947,10 +3992,24 @@ function applyPromptOutdent(payload, enabled) {
3947
3992
  return payload;
3948
3993
  }
3949
3994
  return {
3950
- prompt: structuredOutdent.string(payload.prompt),
3951
- systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled)
3995
+ prompt: typeof payload.prompt === "string" ? structuredOutdent.string(payload.prompt) : undefined,
3996
+ systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
3997
+ messages: payload.messages?.map((message) => ({
3998
+ ...message,
3999
+ content: typeof message.content === "string" ? structuredOutdent.string(message.content) : message.content
4000
+ }))
3952
4001
  };
3953
4002
  }
4003
+ function isLLMMessage(value) {
4004
+ if (typeof value !== "object" || value === null) {
4005
+ return false;
4006
+ }
4007
+ const candidate = value;
4008
+ if (candidate.role !== "system" && candidate.role !== "user" && candidate.role !== "assistant" && candidate.role !== "tool") {
4009
+ return false;
4010
+ }
4011
+ return "content" in candidate;
4012
+ }
3954
4013
  function applyOutdentToOptionalPrompt(value, enabled) {
3955
4014
  if (!enabled || typeof value !== "string") {
3956
4015
  return value;
@@ -3966,6 +4025,73 @@ function mergeSystemPrompts(primary, secondary) {
3966
4025
 
3967
4026
  `);
3968
4027
  }
4028
+ function prepareStructuredPromptPayload(payload, systemPrompt, schema, schemaInstruction) {
4029
+ if (Array.isArray(payload.messages) && payload.messages.length > 0) {
4030
+ const messages = payload.messages.map((message) => ({ ...message }));
4031
+ const mergedSystemPrompt = mergeSystemPrompts(payload.systemPrompt, systemPrompt);
4032
+ const systemMessages = mergedSystemPrompt ? [{ role: "system", content: mergedSystemPrompt }] : [];
4033
+ return {
4034
+ messages: injectStructuredFormatIntoMessages([...systemMessages, ...messages], schema, schemaInstruction)
4035
+ };
4036
+ }
4037
+ const resolvedPrompt = payload.prompt?.trim();
4038
+ if (!resolvedPrompt) {
4039
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4040
+ }
4041
+ return {
4042
+ prompt: shouldInjectFormat(resolvedPrompt, schemaInstruction) ? formatPrompt(schema, resolvedPrompt, {
4043
+ schemaInstruction
4044
+ }) : resolvedPrompt,
4045
+ systemPrompt: mergeSystemPrompts(payload.systemPrompt, systemPrompt)
4046
+ };
4047
+ }
4048
+ function injectStructuredFormatIntoMessages(messages, schema, schemaInstruction) {
4049
+ const lastUserIndex = findLastUserMessageIndex(messages);
4050
+ if (lastUserIndex === -1) {
4051
+ throw new Error("Structured prompts with messages must include at least one user message.");
4052
+ }
4053
+ const target = messages[lastUserIndex];
4054
+ if (Array.isArray(target?.content)) {
4055
+ const parts = target.content;
4056
+ const textIndex = parts.findIndex((p) => p.type === "text");
4057
+ const existingText = textIndex !== -1 ? (parts[textIndex]?.text ?? "").trim() : "";
4058
+ const formatted2 = shouldInjectFormat(existingText, schemaInstruction) ? formatPrompt(schema, existingText, { schemaInstruction }) : existingText;
4059
+ let newParts;
4060
+ if (textIndex !== -1) {
4061
+ newParts = parts.map((p, i) => i === textIndex ? { ...p, text: formatted2 } : p);
4062
+ } else {
4063
+ newParts = [{ type: "text", text: formatted2 }, ...parts];
4064
+ }
4065
+ return messages.map((message, index) => index === lastUserIndex ? { ...message, content: newParts } : message);
4066
+ }
4067
+ const content = typeof target?.content === "string" ? target.content.trim() : stringifyPromptContent(target?.content);
4068
+ const formatted = shouldInjectFormat(content, schemaInstruction) ? formatPrompt(schema, content, { schemaInstruction }) : content.trim();
4069
+ return messages.map((message, index) => index === lastUserIndex ? {
4070
+ ...message,
4071
+ content: formatted
4072
+ } : message);
4073
+ }
4074
+ function findLastUserMessageIndex(messages) {
4075
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
4076
+ if (messages[index]?.role === "user") {
4077
+ return index;
4078
+ }
4079
+ }
4080
+ return -1;
4081
+ }
4082
+ function stringifyPromptContent(content) {
4083
+ if (typeof content === "string") {
4084
+ return content;
4085
+ }
4086
+ if (content === null || content === undefined) {
4087
+ return "";
4088
+ }
4089
+ try {
4090
+ return JSON.stringify(content, null, 2) ?? "";
4091
+ } catch {
4092
+ return String(content);
4093
+ }
4094
+ }
3969
4095
  function shouldInjectFormat(prompt, schemaInstruction) {
3970
4096
  const instruction = resolveSchemaInstruction(schemaInstruction);
3971
4097
  return !prompt.trimStart().startsWith(instruction);
@@ -4144,6 +4270,7 @@ function normalizeDebugConfig(option) {
4144
4270
  async function executeAttempt(adapter, input) {
4145
4271
  const response = await callModel(adapter, {
4146
4272
  prompt: input.prompt,
4273
+ messages: input.messages,
4147
4274
  systemPrompt: input.systemPrompt,
4148
4275
  request: input.request,
4149
4276
  stream: input.stream,
@@ -4151,7 +4278,8 @@ async function executeAttempt(adapter, input) {
4151
4278
  debug: input.debug,
4152
4279
  attempt: input.attemptNumber,
4153
4280
  selfHeal: input.selfHeal,
4154
- selfHealEnabled: input.selfHealEnabled
4281
+ selfHealEnabled: input.selfHealEnabled,
4282
+ timeout: input.timeout
4155
4283
  });
4156
4284
  const parsed = parseWithObserve(response.text, input.schema, input.parseOptions, {
4157
4285
  observe: input.observe,
@@ -4178,9 +4306,32 @@ async function executeAttempt(adapter, input) {
4178
4306
  trace
4179
4307
  };
4180
4308
  }
4309
+ function withToolTimeout(client, toolTimeoutMs) {
4310
+ return {
4311
+ id: client.id,
4312
+ listTools: client.listTools.bind(client),
4313
+ close: client.close?.bind(client),
4314
+ async callTool(params) {
4315
+ let timeoutId;
4316
+ const timeoutPromise = new Promise((_, reject) => {
4317
+ timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
4318
+ });
4319
+ try {
4320
+ return await Promise.race([client.callTool(params), timeoutPromise]);
4321
+ } finally {
4322
+ clearTimeout(timeoutId);
4323
+ }
4324
+ }
4325
+ };
4326
+ }
4327
+ function applyToolTimeout(clients, toolTimeoutMs) {
4328
+ return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
4329
+ }
4181
4330
  async function callModel(adapter, options) {
4331
+ const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
4182
4332
  const requestPayload = {
4183
4333
  prompt: options.prompt,
4334
+ messages: options.messages,
4184
4335
  systemPrompt: options.systemPrompt,
4185
4336
  temperature: options.request?.temperature,
4186
4337
  maxTokens: options.request?.maxTokens,
@@ -4191,7 +4342,7 @@ async function callModel(adapter, options) {
4191
4342
  onToolExecution: options.request?.onToolExecution,
4192
4343
  toolDebug: options.request?.toolDebug,
4193
4344
  body: options.request?.body,
4194
- signal: options.request?.signal
4345
+ signal: requestSignal
4195
4346
  };
4196
4347
  emitDebugRequest(options.debug, {
4197
4348
  provider: adapter.provider,
@@ -4491,6 +4642,7 @@ function emitObserve(observe, event) {
4491
4642
  }
4492
4643
  function emitDebugRequest(config, input) {
4493
4644
  const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4645
+ const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4494
4646
  const lines = [
4495
4647
  color(config, title(config, [
4496
4648
  "[structured][request]",
@@ -4504,7 +4656,9 @@ function emitDebugRequest(config, input) {
4504
4656
  `stream=${input.stream}`
4505
4657
  ].join(" ")),
4506
4658
  color(config, "prompt:", "yellow"),
4507
- input.requestPayload.prompt,
4659
+ input.requestPayload.prompt ?? "(none)",
4660
+ color(config, "messages:", "yellow"),
4661
+ requestMessages,
4508
4662
  color(config, "systemPrompt:", "yellow"),
4509
4663
  input.requestPayload.systemPrompt ?? "(none)",
4510
4664
  color(config, "request.body:", "yellow"),
@@ -4571,7 +4725,8 @@ function mergeStructuredOptions(defaults, overrides) {
4571
4725
  },
4572
4726
  stream: mergeObjectLike(defaults?.stream, overrides?.stream),
4573
4727
  selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
4574
- debug: mergeObjectLike(defaults?.debug, overrides?.debug)
4728
+ debug: mergeObjectLike(defaults?.debug, overrides?.debug),
4729
+ timeout: mergeObjectLike(defaults?.timeout, overrides?.timeout)
4575
4730
  };
4576
4731
  }
4577
4732
  function mergeObjectLike(defaults, overrides) {
@@ -4664,6 +4819,53 @@ function toImplementation(clientInfo) {
4664
4819
  version: clientInfo?.version ?? "0.1.0"
4665
4820
  };
4666
4821
  }
4822
+ // src/image.ts
4823
+ import { extname } from "path";
4824
+ var IMAGE_SIZE_MAP = {
4825
+ low: 256,
4826
+ mid: 512,
4827
+ high: 1024,
4828
+ xhigh: 1280
4829
+ };
4830
+ var IMAGE_MIME_TYPES = {
4831
+ ".png": "image/png",
4832
+ ".jpg": "image/jpeg",
4833
+ ".jpeg": "image/jpeg",
4834
+ ".gif": "image/gif",
4835
+ ".webp": "image/webp"
4836
+ };
4837
+ var MIME_TO_SHARP_FORMAT = {
4838
+ "image/jpeg": "jpeg",
4839
+ "image/png": "png",
4840
+ "image/webp": "webp",
4841
+ "image/gif": "gif"
4842
+ };
4843
+ function images(input) {
4844
+ const inputs = Array.isArray(input) ? input : [input];
4845
+ return inputs.map(({ base64, mimeType }) => ({
4846
+ type: "image_url",
4847
+ image_url: { url: `data:${mimeType};base64,${base64}` }
4848
+ }));
4849
+ }
4850
+ async function resizeImage(source, size, mimeType) {
4851
+ const resolvedMime = mimeType ?? (typeof source === "string" ? IMAGE_MIME_TYPES[extname(source).toLowerCase()] ?? "image/jpeg" : "image/jpeg");
4852
+ let sharp;
4853
+ try {
4854
+ sharp = (await import("sharp")).default;
4855
+ } catch {
4856
+ throw new Error('resizeImage() requires "sharp" to be installed. Run: bun add sharp');
4857
+ }
4858
+ const input = source instanceof ArrayBuffer ? Buffer.from(source) : source;
4859
+ let img = sharp(input);
4860
+ if (size !== "raw") {
4861
+ const targetPx = typeof size === "number" ? size : IMAGE_SIZE_MAP[size];
4862
+ img = img.resize(targetPx, targetPx, { fit: "inside", withoutEnlargement: true });
4863
+ }
4864
+ const sharpFormat = MIME_TO_SHARP_FORMAT[resolvedMime] ?? "jpeg";
4865
+ const outputMime = MIME_TO_SHARP_FORMAT[resolvedMime] ? resolvedMime : "image/jpeg";
4866
+ const buf = await img.toFormat(sharpFormat).toBuffer();
4867
+ return { base64: buf.toString("base64"), mimeType: outputMime };
4868
+ }
4667
4869
  // src/prompt.ts
4668
4870
  function toPromptString(value) {
4669
4871
  if (value === null || value === undefined) {
@@ -4725,35 +4927,28 @@ function toPromptMessage(input, values) {
4725
4927
  }
4726
4928
  return renderPromptTemplate(input, values);
4727
4929
  }
4728
- function joinMessages(messages) {
4729
- return messages.join(`
4730
-
4731
- `);
4732
- }
4733
4930
 
4734
4931
  class PromptMessageBuilderImpl {
4735
- systemMessages = [];
4736
- userMessages = [];
4932
+ messages = [];
4737
4933
  system(input, ...values) {
4738
- const message = toPromptMessage(input, values);
4739
- if (message.length > 0) {
4740
- this.systemMessages.push(message);
4741
- }
4742
- return this;
4934
+ return this.pushMessage("system", input, values);
4743
4935
  }
4744
4936
  user(input, ...values) {
4937
+ return this.pushMessage("user", input, values);
4938
+ }
4939
+ assistant(input, ...values) {
4940
+ return this.pushMessage("assistant", input, values);
4941
+ }
4942
+ pushMessage(role, input, values) {
4745
4943
  const message = toPromptMessage(input, values);
4746
4944
  if (message.length > 0) {
4747
- this.userMessages.push(message);
4945
+ this.messages.push({ role, content: message });
4748
4946
  }
4749
4947
  return this;
4750
4948
  }
4751
4949
  build() {
4752
- const prompt = joinMessages(this.userMessages);
4753
- const systemPrompt = joinMessages(this.systemMessages);
4754
4950
  return {
4755
- prompt,
4756
- systemPrompt: systemPrompt.length > 0 ? systemPrompt : undefined
4951
+ messages: this.messages.map((message) => ({ ...message }))
4757
4952
  };
4758
4953
  }
4759
4954
  resolvePrompt(_context) {
@@ -4863,8 +5058,8 @@ function inferSchemaExample(schema) {
4863
5058
  }
4864
5059
  function getObjectShape(schema) {
4865
5060
  const unwrapped = unwrap2(schema).schema;
4866
- const typeName = unwrapped._def?.typeName;
4867
- if (typeName !== "ZodObject") {
5061
+ const typeName = unwrapped._def?.type;
5062
+ if (typeName !== "object") {
4868
5063
  return null;
4869
5064
  }
4870
5065
  const rawShape = unwrapped._def?.shape;
@@ -4875,33 +5070,25 @@ function getObjectShape(schema) {
4875
5070
  }
4876
5071
  function readDefaultValue(schema) {
4877
5072
  let current = schema;
4878
- while (current?._def?.typeName) {
4879
- const typeName = current._def.typeName;
4880
- if (typeName === "ZodDefault") {
4881
- const raw = current._def.defaultValue;
4882
- if (typeof raw === "function") {
4883
- try {
5073
+ while (current?._def?.type) {
5074
+ const typeName = current._def.type;
5075
+ if (typeName === "default") {
5076
+ try {
5077
+ const raw = current._def.defaultValue;
5078
+ if (typeof raw === "function") {
4884
5079
  return raw();
4885
- } catch {
4886
- return;
4887
5080
  }
5081
+ return raw;
5082
+ } catch {
5083
+ return;
4888
5084
  }
4889
- return raw;
4890
5085
  }
4891
- if (typeName === "ZodOptional" || typeName === "ZodNullable" || typeName === "ZodCatch" || typeName === "ZodReadonly") {
5086
+ if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
4892
5087
  current = current._def.innerType ?? current;
4893
5088
  continue;
4894
5089
  }
4895
- if (typeName === "ZodEffects") {
4896
- current = current._def.schema ?? current;
4897
- continue;
4898
- }
4899
- if (typeName === "ZodBranded") {
4900
- current = current._def.type ?? current;
4901
- continue;
4902
- }
4903
- if (typeName === "ZodPipeline") {
4904
- current = current._def.out ?? current;
5090
+ if (typeName === "pipe") {
5091
+ current = current._def.in ?? current;
4905
5092
  continue;
4906
5093
  }
4907
5094
  return;
@@ -4910,34 +5097,22 @@ function readDefaultValue(schema) {
4910
5097
  }
4911
5098
  function readSchemaDescription2(schema) {
4912
5099
  let current = schema;
4913
- while (current?._def?.typeName) {
4914
- const raw = current._def.description;
4915
- if (typeof raw === "string" && raw.trim().length > 0) {
4916
- return raw.trim();
5100
+ while (current?._def?.type) {
5101
+ const desc = current.description;
5102
+ if (typeof desc === "string" && desc.trim().length > 0) {
5103
+ return desc.trim();
4917
5104
  }
4918
- const fallback = current.description;
4919
- if (typeof fallback === "string" && fallback.trim().length > 0) {
4920
- return fallback.trim();
4921
- }
4922
- const typeName = current._def.typeName;
4923
- if (typeName === "ZodOptional" || typeName === "ZodDefault" || typeName === "ZodNullable") {
5105
+ const typeName = current._def.type;
5106
+ if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
4924
5107
  current = current._def.innerType ?? current;
4925
5108
  continue;
4926
5109
  }
4927
- if (typeName === "ZodCatch" || typeName === "ZodReadonly") {
5110
+ if (typeName === "catch" || typeName === "readonly") {
4928
5111
  current = current._def.innerType ?? current;
4929
5112
  continue;
4930
5113
  }
4931
- if (typeName === "ZodEffects") {
4932
- current = current._def.schema ?? current;
4933
- continue;
4934
- }
4935
- if (typeName === "ZodBranded") {
4936
- current = current._def.type ?? current;
4937
- continue;
4938
- }
4939
- if (typeName === "ZodPipeline") {
4940
- current = current._def.out ?? current;
5114
+ if (typeName === "pipe") {
5115
+ current = current._def.in ?? current;
4941
5116
  continue;
4942
5117
  }
4943
5118
  break;
@@ -4947,27 +5122,19 @@ function readSchemaDescription2(schema) {
4947
5122
  function unwrap2(schema) {
4948
5123
  let current = schema;
4949
5124
  let optional = false;
4950
- while (current?._def?.typeName) {
4951
- const typeName = current._def.typeName;
4952
- if (typeName === "ZodOptional" || typeName === "ZodDefault") {
5125
+ while (current?._def?.type) {
5126
+ const typeName = current._def.type;
5127
+ if (typeName === "optional" || typeName === "default") {
4953
5128
  optional = true;
4954
5129
  current = current._def.innerType ?? current;
4955
5130
  continue;
4956
5131
  }
4957
- if (typeName === "ZodNullable" || typeName === "ZodCatch" || typeName === "ZodReadonly") {
5132
+ if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
4958
5133
  current = current._def.innerType ?? current;
4959
5134
  continue;
4960
5135
  }
4961
- if (typeName === "ZodEffects") {
4962
- current = current._def.schema ?? current;
4963
- continue;
4964
- }
4965
- if (typeName === "ZodBranded") {
4966
- current = current._def.type ?? current;
4967
- continue;
4968
- }
4969
- if (typeName === "ZodPipeline") {
4970
- current = current._def.out ?? current;
5136
+ if (typeName === "pipe") {
5137
+ current = current._def.in ?? current;
4971
5138
  continue;
4972
5139
  }
4973
5140
  break;
@@ -4984,11 +5151,13 @@ export {
4984
5151
  sanitizeThink,
4985
5152
  s,
4986
5153
  resolveSchemaInstruction,
5154
+ resizeImage,
4987
5155
  registerBuiltinProviders,
4988
5156
  prompt,
4989
5157
  parseLLMOutput,
4990
5158
  inspectSchemaMetadata,
4991
5159
  inferSchemaExample,
5160
+ images,
4992
5161
  formatZodIssues,
4993
5162
  formatPrompt,
4994
5163
  extractMarkdownCodeBlocks,