ai-sdk-provider-claude-code 3.4.4 → 3.5.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.cjs CHANGED
@@ -20,17 +20,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AbortError: () => import_claude_agent_sdk3.AbortError,
23
24
  ClaudeCodeLanguageModel: () => ClaudeCodeLanguageModel,
25
+ HOOK_EVENTS: () => import_claude_agent_sdk3.HOOK_EVENTS,
26
+ InMemorySessionStore: () => import_claude_agent_sdk3.InMemorySessionStore,
27
+ SYSTEM_PROMPT_DYNAMIC_BOUNDARY: () => import_claude_agent_sdk3.SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
24
28
  claudeCode: () => claudeCode,
25
29
  createAPICallError: () => createAPICallError,
30
+ createAiSdkMcpServer: () => createAiSdkMcpServer,
26
31
  createAuthenticationError: () => createAuthenticationError,
27
32
  createClaudeCode: () => createClaudeCode,
28
33
  createCustomMcpServer: () => createCustomMcpServer,
29
34
  createSdkMcpServer: () => import_claude_agent_sdk3.createSdkMcpServer,
30
35
  createTimeoutError: () => createTimeoutError,
36
+ deleteSession: () => import_claude_agent_sdk4.deleteSession,
37
+ foldSessionSummary: () => import_claude_agent_sdk4.foldSessionSummary,
38
+ forkSession: () => import_claude_agent_sdk4.forkSession,
31
39
  getErrorMetadata: () => getErrorMetadata,
40
+ getSessionInfo: () => import_claude_agent_sdk4.getSessionInfo,
41
+ getSessionMessages: () => import_claude_agent_sdk4.getSessionMessages,
42
+ getSubagentMessages: () => import_claude_agent_sdk4.getSubagentMessages,
43
+ importSessionToStore: () => import_claude_agent_sdk4.importSessionToStore,
32
44
  isAuthenticationError: () => isAuthenticationError,
33
45
  isTimeoutError: () => isTimeoutError,
46
+ listSessions: () => import_claude_agent_sdk4.listSessions,
47
+ listSubagents: () => import_claude_agent_sdk4.listSubagents,
48
+ renameSession: () => import_claude_agent_sdk4.renameSession,
49
+ startup: () => import_claude_agent_sdk5.startup,
50
+ tagSession: () => import_claude_agent_sdk4.tagSession,
34
51
  tool: () => import_claude_agent_sdk3.tool
35
52
  });
36
53
  module.exports = __toCommonJS(index_exports);
@@ -45,6 +62,23 @@ var import_provider_utils = require("@ai-sdk/provider-utils");
45
62
  // src/convert-to-claude-code-messages.ts
46
63
  var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply base64/data URLs.";
47
64
  var IMAGE_CONVERSION_WARNING = "Unable to convert image content; supply base64/data URLs.";
65
+ var MAX_TOOL_CALL_INPUT_LENGTH = 1e3;
66
+ function serializeToolCallInput(input) {
67
+ let serialized;
68
+ if (input === void 0) {
69
+ serialized = "";
70
+ } else {
71
+ try {
72
+ serialized = JSON.stringify(input) ?? String(input);
73
+ } catch {
74
+ serialized = String(input);
75
+ }
76
+ }
77
+ if (serialized.length > MAX_TOOL_CALL_INPUT_LENGTH) {
78
+ return `${serialized.slice(0, MAX_TOOL_CALL_INPUT_LENGTH)}...[truncated]`;
79
+ }
80
+ return serialized;
81
+ }
48
82
  function normalizeBase64(base64) {
49
83
  return base64.replace(/\s+/g, "");
50
84
  }
@@ -227,8 +261,9 @@ function convertToClaudeCodeMessages(prompt) {
227
261
  }
228
262
  const toolCalls = message.content.filter((part) => part.type === "tool-call");
229
263
  if (toolCalls.length > 0) {
230
- assistantContent += `
231
- [Tool calls made]`;
264
+ const serializedCalls = toolCalls.map((call) => `[Tool call: ${call.toolName}(${serializeToolCallInput(call.input)})]`).join("\n");
265
+ assistantContent += assistantContent ? `
266
+ ${serializedCalls}` : serializedCalls;
232
267
  }
233
268
  }
234
269
  const formattedAssistant = `Assistant: ${assistantContent}`;
@@ -437,6 +472,9 @@ function mapClaudeCodeFinishReason(subtype, stopReason) {
437
472
  // src/validation.ts
438
473
  var import_zod = require("zod");
439
474
  var import_fs = require("fs");
475
+ function isBlankResume(value) {
476
+ return typeof value === "string" && value.trim() === "";
477
+ }
440
478
  var loggerFunctionSchema = import_zod.z.object({
441
479
  debug: import_zod.z.any().refine((val) => typeof val === "function", {
442
480
  message: "debug must be a function"
@@ -457,23 +495,29 @@ var claudeCodeSettingsSchema = import_zod.z.object({
457
495
  appendSystemPrompt: import_zod.z.string().optional(),
458
496
  systemPrompt: import_zod.z.union([
459
497
  import_zod.z.string(),
498
+ import_zod.z.array(import_zod.z.string()),
460
499
  import_zod.z.object({
461
500
  type: import_zod.z.literal("preset"),
462
501
  preset: import_zod.z.literal("claude_code"),
463
- append: import_zod.z.string().optional()
502
+ append: import_zod.z.string().optional(),
503
+ excludeDynamicSections: import_zod.z.boolean().optional()
464
504
  })
465
505
  ]).optional(),
466
506
  maxTurns: import_zod.z.number().int().min(1).max(100).optional(),
467
507
  maxThinkingTokens: import_zod.z.number().int().positive().max(1e5).optional(),
468
508
  thinking: import_zod.z.union([
469
- import_zod.z.object({ type: import_zod.z.literal("adaptive") }).strict(),
509
+ import_zod.z.object({
510
+ type: import_zod.z.literal("adaptive"),
511
+ display: import_zod.z.enum(["summarized", "omitted"]).optional()
512
+ }).strict(),
470
513
  import_zod.z.object({
471
514
  type: import_zod.z.literal("enabled"),
472
- budgetTokens: import_zod.z.number().int().positive().optional()
515
+ budgetTokens: import_zod.z.number().int().positive().optional(),
516
+ display: import_zod.z.enum(["summarized", "omitted"]).optional()
473
517
  }).strict(),
474
518
  import_zod.z.object({ type: import_zod.z.literal("disabled") }).strict()
475
519
  ]).optional(),
476
- effort: import_zod.z.enum(["low", "medium", "high", "max"]).optional(),
520
+ effort: import_zod.z.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
477
521
  promptSuggestions: import_zod.z.boolean().optional(),
478
522
  cwd: import_zod.z.string().refine(
479
523
  (val) => {
@@ -486,11 +530,19 @@ var claudeCodeSettingsSchema = import_zod.z.object({
486
530
  ).optional(),
487
531
  executable: import_zod.z.enum(["bun", "deno", "node"]).optional(),
488
532
  executableArgs: import_zod.z.array(import_zod.z.string()).optional(),
489
- permissionMode: import_zod.z.enum(["default", "acceptEdits", "bypassPermissions", "plan", "delegate", "dontAsk"]).optional(),
533
+ // Mirrors the SDK 0.3.x PermissionMode union ('auto' and 'dontAsk' were
534
+ // added in 0.3.x; 'delegate' was dropped AND is rejected by the CLI's
535
+ // --permission-mode flag parser, so it is rejected here too).
536
+ permissionMode: import_zod.z.enum(["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"]).optional(),
490
537
  permissionPromptToolName: import_zod.z.string().optional(),
491
538
  continue: import_zod.z.boolean().optional(),
492
539
  resume: import_zod.z.string().optional(),
493
- sessionId: import_zod.z.string().optional(),
540
+ // The CLI rejects --session-id values that are not valid UUIDs, so
541
+ // enforce the UUID shape here instead of failing at query time.
542
+ sessionId: import_zod.z.string().refine(
543
+ (val) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(val),
544
+ { message: "sessionId must be a valid UUID (the CLI rejects non-UUID session IDs)" }
545
+ ).optional(),
494
546
  allowedTools: import_zod.z.array(import_zod.z.string()).optional(),
495
547
  disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
496
548
  betas: import_zod.z.array(import_zod.z.string()).optional(),
@@ -499,7 +551,9 @@ var claudeCodeSettingsSchema = import_zod.z.object({
499
551
  maxBudgetUsd: import_zod.z.number().min(0).optional(),
500
552
  plugins: import_zod.z.array(
501
553
  import_zod.z.object({
502
- type: import_zod.z.string(),
554
+ // SDK SdkPluginConfig: only 'local' is supported; the SDK throws
555
+ // 'Unsupported plugin type' at query time for anything else.
556
+ type: import_zod.z.literal("local"),
503
557
  path: import_zod.z.string()
504
558
  }).passthrough()
505
559
  ).optional(),
@@ -514,12 +568,41 @@ var claudeCodeSettingsSchema = import_zod.z.object({
514
568
  preset: import_zod.z.literal("claude_code")
515
569
  })
516
570
  ]).optional(),
571
+ skills: import_zod.z.union([import_zod.z.array(import_zod.z.string()), import_zod.z.literal("all")]).optional(),
572
+ settings: import_zod.z.union([
573
+ import_zod.z.string(),
574
+ import_zod.z.record(import_zod.z.string(), import_zod.z.any())
575
+ // inline Settings object
576
+ ]).optional(),
577
+ managedSettings: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(),
578
+ toolAliases: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
579
+ toolConfig: import_zod.z.object({
580
+ askUserQuestion: import_zod.z.object({
581
+ previewFormat: import_zod.z.enum(["markdown", "html"]).optional()
582
+ }).passthrough().optional()
583
+ }).passthrough().optional(),
584
+ planModeInstructions: import_zod.z.string().optional(),
585
+ title: import_zod.z.string().optional(),
586
+ forwardSubagentText: import_zod.z.boolean().optional(),
587
+ agentProgressSummaries: import_zod.z.boolean().optional(),
588
+ includeHookEvents: import_zod.z.boolean().optional(),
589
+ taskBudget: import_zod.z.object({ total: import_zod.z.number().positive() }).strict().optional(),
590
+ sessionStore: import_zod.z.any().refine(
591
+ (val) => val === void 0 || typeof val === "object" && val !== null && typeof val.append === "function" && typeof val.load === "function",
592
+ { message: "sessionStore must be an object with append() and load() functions" }
593
+ ).optional(),
594
+ sessionStoreFlush: import_zod.z.enum(["batched", "eager"]).optional(),
595
+ loadTimeoutMs: import_zod.z.number().int().positive().optional(),
517
596
  settingSources: import_zod.z.array(import_zod.z.enum(["user", "project", "local"])).optional(),
518
597
  streamingInput: import_zod.z.enum(["auto", "always", "off"]).optional(),
519
598
  // Hooks and tool-permission callback (permissive validation of shapes)
520
599
  canUseTool: import_zod.z.any().refine((v) => v === void 0 || typeof v === "function", {
521
600
  message: "canUseTool must be a function"
522
601
  }).optional(),
602
+ onUserDialog: import_zod.z.any().refine((v) => v === void 0 || typeof v === "function", {
603
+ message: "onUserDialog must be a function"
604
+ }).optional(),
605
+ supportedDialogKinds: import_zod.z.array(import_zod.z.string()).optional(),
523
606
  hooks: import_zod.z.record(
524
607
  import_zod.z.string(),
525
608
  import_zod.z.array(
@@ -572,7 +655,8 @@ var claudeCodeSettingsSchema = import_zod.z.object({
572
655
  tools: import_zod.z.array(import_zod.z.string()).optional(),
573
656
  disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
574
657
  prompt: import_zod.z.string(),
575
- model: import_zod.z.enum(["sonnet", "opus", "haiku", "inherit"]).optional(),
658
+ // SDK 0.3.x AgentDefinition accepts any model alias or full model ID
659
+ model: import_zod.z.string().optional(),
576
660
  mcpServers: import_zod.z.array(
577
661
  import_zod.z.union([
578
662
  import_zod.z.string(),
@@ -603,10 +687,14 @@ var claudeCodeSettingsSchema = import_zod.z.object({
603
687
  }).optional(),
604
688
  onStreamStart: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
605
689
  message: "onStreamStart must be a function"
690
+ }).optional(),
691
+ // Callback invoked with the predicted next user prompt (active unless promptSuggestions: false)
692
+ onPromptSuggestion: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
693
+ message: "onPromptSuggestion must be a function"
606
694
  }).optional()
607
695
  }).strict();
608
696
  function validateModelId(modelId) {
609
- const knownModels = ["opus", "sonnet", "haiku"];
697
+ const knownModels = ["opus", "sonnet", "haiku", "fable"];
610
698
  if (!modelId || modelId.trim() === "") {
611
699
  throw new Error("Model ID cannot be empty");
612
700
  }
@@ -630,6 +718,51 @@ function validateSettings(settings) {
630
718
  return { valid: false, warnings, errors };
631
719
  }
632
720
  const validSettings = result.data;
721
+ const sdkOptionsRecord = validSettings.sdkOptions;
722
+ const effective = (key) => {
723
+ const override = sdkOptionsRecord?.[key];
724
+ return override !== void 0 ? override : validSettings[key];
725
+ };
726
+ const effSessionStore = effective("sessionStore");
727
+ const effectiveResumeId = () => {
728
+ for (const candidate of [sdkOptionsRecord?.resume, validSettings.resume]) {
729
+ if (typeof candidate === "string" && !isBlankResume(candidate)) {
730
+ return candidate;
731
+ }
732
+ }
733
+ return void 0;
734
+ };
735
+ if (effSessionStore !== void 0 && effective("persistSession") === false) {
736
+ errors.push(
737
+ "sessionStore cannot be combined with persistSession: false. Transcript mirroring requires local session writes; remove persistSession: false or drop sessionStore."
738
+ );
739
+ return { valid: false, warnings, errors };
740
+ }
741
+ if (effSessionStore !== void 0 && effective("enableFileCheckpointing") === true) {
742
+ errors.push(
743
+ "sessionStore cannot be combined with enableFileCheckpointing: true. Checkpoint backup blobs are not mirrored to the store (rewindFiles() fails after a store-backed resume); remove enableFileCheckpointing or drop sessionStore."
744
+ );
745
+ return { valid: false, warnings, errors };
746
+ }
747
+ if (effective("continue") === true && effSessionStore !== void 0 && effectiveResumeId() === void 0 && typeof effSessionStore.listSessions !== "function") {
748
+ errors.push(
749
+ "continue: true with sessionStore requires the store to implement listSessions() (used to discover the most recent session). Implement listSessions(), pass resume with an explicit session ID, or drop continue."
750
+ );
751
+ return { valid: false, warnings, errors };
752
+ }
753
+ const effSettingsOption = effective("settings");
754
+ if (effective("sandbox") !== void 0 && typeof effSettingsOption === "string" && !(effSettingsOption.trim().startsWith("{") && effSettingsOption.trim().endsWith("}"))) {
755
+ errors.push(
756
+ "sandbox cannot be combined with a settings file path. Pass settings as an inline Settings object, or move the sandbox configuration into the settings file and drop the sandbox option."
757
+ );
758
+ return { valid: false, warnings, errors };
759
+ }
760
+ if (effective("sessionId") !== void 0 && effective("forkSession") !== true && (effective("continue") === true || effectiveResumeId() !== void 0)) {
761
+ errors.push(
762
+ "sessionId cannot be combined with continue or resume unless forkSession: true is also set (it then names the forked session's ID). Remove sessionId, remove continue/resume, or add forkSession: true."
763
+ );
764
+ return { valid: false, warnings, errors };
765
+ }
633
766
  if (validSettings.maxTurns && validSettings.maxTurns > 20) {
634
767
  warnings.push(
635
768
  `High maxTurns value (${validSettings.maxTurns}) may lead to long-running conversations`
@@ -658,11 +791,30 @@ function validateSettings(settings) {
658
791
  if (validSettings.disallowedTools) {
659
792
  validateToolNames(validSettings.disallowedTools, "disallowed");
660
793
  }
661
- if (validSettings.allowedTools?.includes("Skill") && !validSettings.settingSources) {
794
+ const effDialogKinds = effective("supportedDialogKinds");
795
+ if (Array.isArray(effDialogKinds) && effDialogKinds.length > 0 && effective("onUserDialog") == null) {
796
+ errors.push(
797
+ "supportedDialogKinds is set without onUserDialog. The SDK requires the onUserDialog callback to render declared dialog kinds and throws when a non-empty list is passed without it; provide onUserDialog or remove supportedDialogKinds."
798
+ );
799
+ return { valid: false, warnings, errors };
800
+ }
801
+ const effAllowedTools = effective("allowedTools");
802
+ if (Array.isArray(effAllowedTools) && effAllowedTools.includes("Skill") && !effective("settingSources")) {
662
803
  warnings.push(
663
804
  "allowedTools includes 'Skill' but settingSources is not set. Skills require settingSources (e.g., ['user', 'project']) to load skill definitions."
664
805
  );
665
806
  }
807
+ if (validSettings.agents) {
808
+ const knownAgentModelAliases = ["sonnet", "opus", "haiku", "fable", "inherit"];
809
+ for (const [agentName, agent] of Object.entries(validSettings.agents)) {
810
+ const agentModel = agent.model;
811
+ if (agentModel !== void 0 && !knownAgentModelAliases.includes(agentModel) && !agentModel.includes("-")) {
812
+ warnings.push(
813
+ `Unknown model alias '${agentModel}' for agent '${agentName}'. Known aliases are: ${knownAgentModelAliases.join(", ")}; full model IDs (e.g. 'claude-sonnet-4-5') are also accepted.`
814
+ );
815
+ }
816
+ }
817
+ }
666
818
  return { valid: true, warnings, errors };
667
819
  } catch (error) {
668
820
  errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
@@ -683,6 +835,137 @@ function validateSessionId(sessionId) {
683
835
  return void 0;
684
836
  }
685
837
 
838
+ // src/sanitize-json-schema.ts
839
+ var SUBSCHEMA_MAP_KEYWORDS = [
840
+ "properties",
841
+ "patternProperties",
842
+ "$defs",
843
+ "definitions",
844
+ "dependentSchemas",
845
+ // draft-07 `dependencies` values are either subschemas (schema form) or
846
+ // arrays of property-name strings (array form); sanitizeNode passes
847
+ // string arrays through untouched, so walking both forms is safe.
848
+ "dependencies"
849
+ ];
850
+ var SUBSCHEMA_KEYWORDS = [
851
+ "items",
852
+ // may also be an array of subschemas (draft-07 tuple form)
853
+ "additionalItems",
854
+ "additionalProperties",
855
+ "unevaluatedItems",
856
+ "unevaluatedProperties",
857
+ "not",
858
+ "contains",
859
+ "propertyNames",
860
+ "contentSchema",
861
+ "if",
862
+ "then",
863
+ "else"
864
+ ];
865
+ var SUBSCHEMA_LIST_KEYWORDS = ["prefixItems", "anyOf", "oneOf", "allOf"];
866
+ function sanitizeJsonSchemaForOutputFormat(schema) {
867
+ const strippedFormatPaths = [];
868
+ const sanitized = sanitizeNode(schema, "#", /* @__PURE__ */ new WeakSet(), strippedFormatPaths);
869
+ return {
870
+ schema: sanitized ?? schema,
871
+ strippedFormatPaths
872
+ };
873
+ }
874
+ function sanitizeNode(node, path, visiting, strippedFormatPaths) {
875
+ if (typeof node !== "object" || node === null) {
876
+ return node;
877
+ }
878
+ if (visiting.has(node)) {
879
+ return node;
880
+ }
881
+ visiting.add(node);
882
+ try {
883
+ if (Array.isArray(node)) {
884
+ return sanitizeList(node, path, visiting, strippedFormatPaths);
885
+ }
886
+ const record = node;
887
+ let result = record;
888
+ const setKey = (key, value) => {
889
+ if (result === record) {
890
+ result = { ...record };
891
+ }
892
+ result[key] = value;
893
+ };
894
+ if (typeof record.format === "string") {
895
+ const format = record.format;
896
+ const existingDescription = record.description;
897
+ result = { ...record };
898
+ delete result.format;
899
+ if (typeof existingDescription === "string" && existingDescription.length > 0) {
900
+ result.description = `${existingDescription} (expected format: ${format})`;
901
+ } else if (existingDescription === void 0 || existingDescription === "") {
902
+ result.description = `Expected format: ${format}`;
903
+ }
904
+ strippedFormatPaths.push(path);
905
+ }
906
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
907
+ const map = record[keyword];
908
+ if (typeof map !== "object" || map === null || Array.isArray(map)) continue;
909
+ const mapRecord = map;
910
+ let newMap = mapRecord;
911
+ for (const [name, child] of Object.entries(mapRecord)) {
912
+ const sanitizedChild = sanitizeNode(
913
+ child,
914
+ `${path}/${keyword}/${name}`,
915
+ visiting,
916
+ strippedFormatPaths
917
+ );
918
+ if (sanitizedChild !== child) {
919
+ if (newMap === mapRecord) {
920
+ newMap = { ...mapRecord };
921
+ }
922
+ newMap[name] = sanitizedChild;
923
+ }
924
+ }
925
+ if (newMap !== mapRecord) {
926
+ setKey(keyword, newMap);
927
+ }
928
+ }
929
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
930
+ const child = record[keyword];
931
+ if (typeof child !== "object" || child === null) continue;
932
+ const sanitizedChild = sanitizeNode(
933
+ child,
934
+ `${path}/${keyword}`,
935
+ visiting,
936
+ strippedFormatPaths
937
+ );
938
+ if (sanitizedChild !== child) {
939
+ setKey(keyword, sanitizedChild);
940
+ }
941
+ }
942
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
943
+ const list = record[keyword];
944
+ if (!Array.isArray(list)) continue;
945
+ const sanitizedList = sanitizeList(list, `${path}/${keyword}`, visiting, strippedFormatPaths);
946
+ if (sanitizedList !== list) {
947
+ setKey(keyword, sanitizedList);
948
+ }
949
+ }
950
+ return result;
951
+ } finally {
952
+ visiting.delete(node);
953
+ }
954
+ }
955
+ function sanitizeList(list, path, visiting, strippedFormatPaths) {
956
+ let result = list;
957
+ for (let i = 0; i < list.length; i++) {
958
+ const sanitizedChild = sanitizeNode(list[i], `${path}/${i}`, visiting, strippedFormatPaths);
959
+ if (sanitizedChild !== list[i]) {
960
+ if (result === list) {
961
+ result = [...list];
962
+ }
963
+ result[i] = sanitizedChild;
964
+ }
965
+ }
966
+ return result;
967
+ }
968
+
686
969
  // src/logger.ts
687
970
  var defaultLogger = {
688
971
  // eslint-disable-next-line no-console
@@ -729,6 +1012,8 @@ function createVerboseLogger(logger, verbose = false) {
729
1012
 
730
1013
  // src/claude-code-language-model.ts
731
1014
  var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
1015
+ var PROVIDER_VERSION = "3.5.0";
1016
+ var DEFAULT_CLIENT_APP = `ai-sdk-provider-claude-code/${PROVIDER_VERSION}`;
732
1017
  var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
733
1018
  var MIN_TRUNCATION_LENGTH = 512;
734
1019
  function isClaudeCodeTruncationError(error, bufferedText) {
@@ -760,6 +1045,34 @@ function isClaudeCodeTruncationError(error, bufferedText) {
760
1045
  }
761
1046
  return true;
762
1047
  }
1048
+ var MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE = "Structured output was requested (responseFormat with a JSON schema) but the Claude Code CLI returned no structured_output, and the prose response could not be parsed as JSON. This usually means the schema contains constructs the CLI cannot enforce (e.g. complex regex patterns with lookaheads/backreferences), causing it to silently fall back to prose. Simplify the generation schema and validate strictly client-side. See the 'Structured Outputs' section of the ai-sdk-provider-claude-code README for the list of known limitations.";
1049
+ function extractJsonObjectText(text) {
1050
+ const trimmed = text.trim();
1051
+ if (!trimmed) {
1052
+ return void 0;
1053
+ }
1054
+ const candidates = [trimmed];
1055
+ const fencedBlocks = Array.from(trimmed.matchAll(/```(?:json)?\s*\n?([\s\S]*?)```/gi)).map((match) => match[1]?.trim()).filter((block) => block !== void 0 && block.length > 0);
1056
+ candidates.push(...fencedBlocks.reverse());
1057
+ for (const candidate of candidates) {
1058
+ if (!candidate) continue;
1059
+ try {
1060
+ const parsed = JSON.parse(candidate);
1061
+ if (typeof parsed === "object" && parsed !== null) {
1062
+ return candidate;
1063
+ }
1064
+ } catch {
1065
+ }
1066
+ }
1067
+ return void 0;
1068
+ }
1069
+ function getStructuredErrorKind(error) {
1070
+ if (typeof error === "object" && error !== null && "errorKind" in error) {
1071
+ const kind = error.errorKind;
1072
+ if (typeof kind === "string") return kind;
1073
+ }
1074
+ return void 0;
1075
+ }
763
1076
  function isAbortError(err) {
764
1077
  if (err && typeof err === "object") {
765
1078
  const e = err;
@@ -770,6 +1083,7 @@ function isAbortError(err) {
770
1083
  }
771
1084
  var DEFAULT_INHERITED_ENV_VARS = process.platform === "win32" ? [
772
1085
  "APPDATA",
1086
+ "COMSPEC",
773
1087
  "HOMEDRIVE",
774
1088
  "HOMEPATH",
775
1089
  "LOCALAPPDATA",
@@ -784,23 +1098,89 @@ var DEFAULT_INHERITED_ENV_VARS = process.platform === "win32" ? [
784
1098
  "WINDIR"
785
1099
  ] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER", "LANG", "LC_ALL", "TMPDIR"];
786
1100
  var CLAUDE_ENV_VARS = ["CLAUDE_CONFIG_DIR"];
1101
+ var NETWORK_ENV_VARS = [
1102
+ "HTTP_PROXY",
1103
+ "HTTPS_PROXY",
1104
+ "NO_PROXY",
1105
+ "http_proxy",
1106
+ "https_proxy",
1107
+ "no_proxy",
1108
+ "NODE_EXTRA_CA_CERTS",
1109
+ "SSL_CERT_FILE",
1110
+ "SSL_CERT_DIR"
1111
+ ];
1112
+ var CLOUD_ENV_VARS = ["GCLOUD_PROJECT", "CLOUD_ML_REGION"];
1113
+ var INHERITED_ENV_PREFIXES = ["ANTHROPIC_", "CLAUDE_", "AWS_", "GOOGLE_"];
787
1114
  function getBaseProcessEnv() {
788
1115
  const env = {};
789
- const allowedKeys = /* @__PURE__ */ new Set([...DEFAULT_INHERITED_ENV_VARS, ...CLAUDE_ENV_VARS]);
790
- for (const key of allowedKeys) {
1116
+ const allowedKeys = /* @__PURE__ */ new Set([
1117
+ ...DEFAULT_INHERITED_ENV_VARS,
1118
+ ...CLAUDE_ENV_VARS,
1119
+ ...NETWORK_ENV_VARS,
1120
+ ...CLOUD_ENV_VARS
1121
+ ]);
1122
+ const addIfSafe = (key) => {
791
1123
  const value = process.env[key];
792
1124
  if (typeof value !== "string") {
793
- continue;
1125
+ return;
794
1126
  }
795
1127
  if (value.startsWith("()")) {
796
- continue;
1128
+ return;
797
1129
  }
798
1130
  env[key] = value;
1131
+ };
1132
+ for (const key of allowedKeys) {
1133
+ addIfSafe(key);
1134
+ }
1135
+ for (const key of Object.keys(process.env)) {
1136
+ if (INHERITED_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1137
+ addIfSafe(key);
1138
+ }
799
1139
  }
800
1140
  return env;
801
1141
  }
802
1142
  var STREAMING_FEATURE_WARNING = "Claude Agent SDK features (hooks/MCP/images) require streaming input. Set `streamingInput: 'always'` or provide `canUseTool` (auto streams only when canUseTool is set).";
803
1143
  var SDK_OPTIONS_BLOCKLIST = /* @__PURE__ */ new Set(["model", "abortController", "prompt", "outputFormat"]);
1144
+ var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
1145
+ function isSubagentToolName(name) {
1146
+ return SUBAGENT_TOOL_NAMES.has(name);
1147
+ }
1148
+ function resolveToolParentId(messageLevel, blockLevel, inferFallback) {
1149
+ if (messageLevel !== void 0) return messageLevel;
1150
+ if (typeof blockLevel === "string") return blockLevel;
1151
+ return inferFallback();
1152
+ }
1153
+ function computeRetractedToolCallIds(retracted, descriptors) {
1154
+ const ids = /* @__PURE__ */ new Set();
1155
+ for (const { toolCallId, uuid } of descriptors) {
1156
+ if (uuid !== void 0 && retracted.has(uuid)) {
1157
+ ids.add(toolCallId);
1158
+ }
1159
+ }
1160
+ return ids;
1161
+ }
1162
+ function applySupersede(message, evict, logger, guard = "array") {
1163
+ const supersedes = message.supersedes;
1164
+ const triggered = guard === "truthy" ? Boolean(supersedes && supersedes.length > 0) : Array.isArray(supersedes) && supersedes.length > 0;
1165
+ if (!triggered) {
1166
+ return false;
1167
+ }
1168
+ logger.debug(`[claude-code] Assistant message supersedes ${supersedes.length} prior message(s)`);
1169
+ evict(new Set(supersedes));
1170
+ return true;
1171
+ }
1172
+ function buildRetractionEvictor(evict) {
1173
+ return (uuids) => evict(new Set(uuids));
1174
+ }
1175
+ var INFORMATIONAL_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set([
1176
+ "notification",
1177
+ "status",
1178
+ "task_updated",
1179
+ "session_state_changed",
1180
+ "commands_changed",
1181
+ "memory_recall",
1182
+ "plugin_install"
1183
+ ]);
804
1184
  function isContentBlock(item) {
805
1185
  return typeof item === "object" && item !== null && "type" in item;
806
1186
  }
@@ -1023,6 +1403,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1023
1403
  // 100KB warning threshold
1024
1404
  static MAX_DELTA_CALC_SIZE = 1e4;
1025
1405
  // 10KB delta computation threshold
1406
+ // Upper bound for draining post-result messages (prompt_suggestion) so a
1407
+ // lingering CLI subprocess cannot be held open indefinitely after finish.
1408
+ static PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS = 1e4;
1026
1409
  modelId;
1027
1410
  settings;
1028
1411
  sessionId;
@@ -1070,27 +1453,71 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1070
1453
  return sanitized;
1071
1454
  }
1072
1455
  getEffectiveResume(sdkOptions) {
1073
- return sdkOptions?.resume ?? this.settings.resume ?? this.sessionId;
1074
- }
1075
- extractTextAndThinking(content) {
1076
- if (!Array.isArray(content)) return { text: "", thinking: [] };
1077
- let text = "";
1078
- const thinking = [];
1079
- for (const part of content) {
1080
- if (!isContentBlock(part)) continue;
1081
- if (part.type === "text" && typeof part.text === "string") {
1082
- text += part.text;
1083
- } else if (part.type === "thinking" && typeof part.thinking === "string") {
1084
- thinking.push(part.thinking);
1456
+ for (const candidate of [sdkOptions?.resume, this.settings.resume, this.sessionId]) {
1457
+ if (typeof candidate === "string" && !isBlankResume(candidate)) {
1458
+ return candidate;
1085
1459
  }
1086
1460
  }
1087
- if (text.length > 0 && typeof text !== "string") {
1088
- throw new Error("extractTextAndThinking: accumulated text must be a string");
1089
- }
1090
- if (thinking.some((t) => typeof t !== "string")) {
1091
- throw new Error("extractTextAndThinking: all thinking entries must be strings");
1461
+ return void 0;
1462
+ }
1463
+ /**
1464
+ * Single source of truth for the CLI's `--session-id` exclusivity rule.
1465
+ *
1466
+ * The CLI rejects `--session-id` together with `--resume`/`--continue`
1467
+ * unless `--fork-session` is also set (forkSession then names the forked
1468
+ * session's own ID). This predicate captures "there IS a resume/continue
1469
+ * target AND we are not forking", i.e. the case where a session id must NOT
1470
+ * coexist. It is referenced by both:
1471
+ * - the pre-merge forwarding guard (via its inverse), which decides whether
1472
+ * to forward `settings.sessionId` onto the base options, and
1473
+ * - the post-merge exclusivity drop, which removes any session id that the
1474
+ * generic sdkOptions overlay (or the auto-resume turn) re-introduced.
1475
+ *
1476
+ * Keeping one definition guarantees both sites agree on what "conflicts with
1477
+ * a session id" means. The mirror in validation.ts (construction-time)
1478
+ * intentionally stays separate: it reads settings+sdkOptions, not a built
1479
+ * opts object.
1480
+ */
1481
+ static sessionIdConflictsWithResumeOrContinue(args) {
1482
+ return (args.resumePresent || args.continue) && !args.forkSession;
1483
+ }
1484
+ /**
1485
+ * Owns ALL session-id / resume cross-option resolution on the FINAL merged
1486
+ * options, in the single correct order. Called once, immediately after the
1487
+ * generic sdkOptions overlay in createQueryOptions.
1488
+ *
1489
+ * Two concerns, in this exact order (order matters: step 1 can change whether
1490
+ * step 2 sees a resume target):
1491
+ *
1492
+ * 1. Blank-resume restoration. The overlay copies the raw `sdkOptions.resume`
1493
+ * verbatim, which can re-introduce a blank/whitespace value over the
1494
+ * base `resume` that getEffectiveResume already normalized. The SDK treats
1495
+ * a blank resume as absent, so a blank must NOT clobber the computed
1496
+ * fallback — restore `effectiveResume` (already blank-stripped; may itself
1497
+ * be undefined for a genuinely new session) rather than leaving '' or
1498
+ * forcing undefined, which would erase a real settings.resume / captured
1499
+ * session id.
1500
+ *
1501
+ * 2. Session-id exclusivity. Drop `opts.sessionId` whenever it conflicts with
1502
+ * a resume/continue target (see sessionIdConflictsWithResumeOrContinue).
1503
+ * This runs on the merged opts so it catches a sessionId re-added by the
1504
+ * sdkOptions overlay AND the auto-resumed second turn (where resume was
1505
+ * populated from the captured session id). It complements — does not
1506
+ * replace — the pre-merge forwarding guard, which governs whether
1507
+ * settings.sessionId was forwarded BEFORE the overlay could mutate
1508
+ * forkSession/continue/resume.
1509
+ */
1510
+ applySessionResolution(opts, effectiveResume) {
1511
+ if (isBlankResume(opts.resume)) {
1512
+ opts.resume = effectiveResume;
1513
+ }
1514
+ if (opts.sessionId !== void 0 && _ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
1515
+ resumePresent: opts.resume !== void 0,
1516
+ continue: opts.continue === true,
1517
+ forkSession: opts.forkSession === true
1518
+ })) {
1519
+ opts.sessionId = void 0;
1092
1520
  }
1093
- return { text, thinking };
1094
1521
  }
1095
1522
  extractToolUses(content) {
1096
1523
  return filterContentBlocks(content, "tool_use").map((block) => {
@@ -1184,6 +1611,153 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1184
1611
  }
1185
1612
  return result;
1186
1613
  }
1614
+ /**
1615
+ * Builds a provider-executed `tool-call` part from an assistant `tool_use`
1616
+ * block. Shared by doGenerate (content part) and doStream (stream part) so
1617
+ * the two paths cannot drift in field shape.
1618
+ */
1619
+ buildToolCallPart(toolCallId, toolName, input, parentToolCallId) {
1620
+ return {
1621
+ type: "tool-call",
1622
+ toolCallId,
1623
+ toolName,
1624
+ input,
1625
+ providerExecuted: true,
1626
+ dynamic: true,
1627
+ // V3 field: indicates tool is provider-defined (not in user's tools map)
1628
+ providerMetadata: {
1629
+ "claude-code": {
1630
+ // rawInput preserves the original serialized format before AI SDK normalization.
1631
+ // Use this if you need the exact string sent to the Claude CLI, which may differ
1632
+ // from the `input` field after AI SDK processing.
1633
+ rawInput: input,
1634
+ parentToolCallId: parentToolCallId ?? null
1635
+ }
1636
+ }
1637
+ };
1638
+ }
1639
+ /**
1640
+ * Builds a provider-executed `tool-result` part from a user-message
1641
+ * `tool_result` block, applying normalization and `maxToolResultSize`
1642
+ * truncation. Shared by doGenerate and doStream.
1643
+ */
1644
+ buildToolResultPart(toolCallId, toolName, result, isError, parentToolCallId) {
1645
+ const normalizedResult = this.normalizeToolResult(result);
1646
+ const rawResult = typeof result === "string" ? result : result === void 0 ? (
1647
+ // tool_result blocks may omit `content` entirely; '' keeps both
1648
+ // `result` (NonNullable<JSONValue>) and rawResult (JSONValue) valid.
1649
+ ""
1650
+ ) : (() => {
1651
+ try {
1652
+ return JSON.stringify(result) ?? String(result);
1653
+ } catch {
1654
+ return String(result);
1655
+ }
1656
+ })();
1657
+ const maxToolResultSize = this.settings.maxToolResultSize;
1658
+ const truncatedResult = truncateToolResultForStream(normalizedResult, maxToolResultSize);
1659
+ const truncatedRawResult = truncateToolResultForStream(rawResult, maxToolResultSize);
1660
+ const rawResultTruncated = truncatedRawResult !== rawResult;
1661
+ return {
1662
+ type: "tool-result",
1663
+ toolCallId,
1664
+ toolName,
1665
+ // `?? ''`: absent `content` (undefined) and string results that
1666
+ // normalize to JSON null (e.g. the string "null") must not violate the
1667
+ // NonNullable<JSONValue> contract of LanguageModelV3ToolResult.result.
1668
+ result: truncatedResult ?? "",
1669
+ isError,
1670
+ providerExecuted: true,
1671
+ dynamic: true,
1672
+ // V3 field: indicates tool is provider-defined
1673
+ providerMetadata: {
1674
+ "claude-code": {
1675
+ // rawResult preserves the original CLI output string before JSON parsing.
1676
+ // Use this when you need the exact string returned by the tool, especially
1677
+ // if the `result` field has been parsed/normalized and you need the original format.
1678
+ rawResult: truncatedRawResult,
1679
+ rawResultTruncated,
1680
+ parentToolCallId: parentToolCallId ?? null
1681
+ }
1682
+ }
1683
+ };
1684
+ }
1685
+ serializeToolError(error) {
1686
+ return typeof error === "string" ? error : typeof error === "object" && error !== null ? (() => {
1687
+ try {
1688
+ return JSON.stringify(error) ?? String(error);
1689
+ } catch {
1690
+ return String(error);
1691
+ }
1692
+ })() : String(error);
1693
+ }
1694
+ /**
1695
+ * Builds a provider-executed `tool-error` STREAM part from a user-message
1696
+ * `tool_error` block (doStream only; AI SDK core handles tool-error stream
1697
+ * parts natively).
1698
+ */
1699
+ buildToolErrorPart(toolCallId, toolName, error, parentToolCallId) {
1700
+ const rawError = this.serializeToolError(error);
1701
+ return {
1702
+ type: "tool-error",
1703
+ toolCallId,
1704
+ toolName,
1705
+ error: rawError,
1706
+ providerExecuted: true,
1707
+ dynamic: true,
1708
+ // V3 field: indicates tool is provider-defined
1709
+ providerMetadata: {
1710
+ "claude-code": {
1711
+ rawError,
1712
+ parentToolCallId: parentToolCallId ?? null
1713
+ }
1714
+ }
1715
+ };
1716
+ }
1717
+ /**
1718
+ * Builds a V3 `tool-result` CONTENT part with `isError: true` from a
1719
+ * user-message `tool_error` block (doGenerate only). The V3 content union
1720
+ * has no `tool-error` member and AI SDK core's asContent() silently drops
1721
+ * unknown content part types, so an extension tool-error part would never
1722
+ * reach `generateText` users — an isError tool-result, by contrast,
1723
+ * round-trips into a proper tool-error part in steps content.
1724
+ */
1725
+ buildToolErrorResultPart(toolCallId, toolName, error, parentToolCallId) {
1726
+ const rawError = this.serializeToolError(error);
1727
+ return {
1728
+ type: "tool-result",
1729
+ toolCallId,
1730
+ toolName,
1731
+ result: rawError,
1732
+ isError: true,
1733
+ providerExecuted: true,
1734
+ dynamic: true,
1735
+ // V3 field: indicates tool is provider-defined
1736
+ providerMetadata: {
1737
+ "claude-code": {
1738
+ rawError,
1739
+ parentToolCallId: parentToolCallId ?? null
1740
+ }
1741
+ }
1742
+ };
1743
+ }
1744
+ /**
1745
+ * Policy (P3): late-frame drop guard, shared by all four tool_result/tool_error
1746
+ * sites (doGenerate result+error, doStream result+error). When a frame's tool
1747
+ * id is tombstoned (its tool-call was retracted by a supersede/refusal-fallback
1748
+ * signal), the frame must be DROPPED rather than re-synthesized into an orphan
1749
+ * tool-call. Centralizing the predicate + debug message keeps the four sites in
1750
+ * lockstep so a future tombstone change can't be applied to only some of them.
1751
+ */
1752
+ isRetractedToolFrame(id, tombstone, frameKind) {
1753
+ if (!tombstone.has(id)) {
1754
+ return false;
1755
+ }
1756
+ this.logger.debug(
1757
+ `[claude-code] Dropping tool ${frameKind} for retracted (superseded) tool ID: ${id}`
1758
+ );
1759
+ return true;
1760
+ }
1187
1761
  generateAllWarnings(options, prompt) {
1188
1762
  const warnings = [];
1189
1763
  const unsupportedParams = [];
@@ -1204,6 +1778,27 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1204
1778
  });
1205
1779
  }
1206
1780
  }
1781
+ if (options.tools !== void 0 && options.tools.length > 0) {
1782
+ warnings.push({
1783
+ type: "unsupported",
1784
+ feature: "tools",
1785
+ details: "The Claude Code CLI executes its own tools; AI SDK tools cannot be auto-bridged at the provider layer and will be ignored. To expose custom tools to the CLI, build an in-process MCP server with the createAiSdkMcpServer helper (exported by this package) and pass it via the mcpServers setting (plus allowedTools)."
1786
+ });
1787
+ }
1788
+ if (options.toolChoice !== void 0 && options.toolChoice.type !== "auto") {
1789
+ warnings.push({
1790
+ type: "unsupported",
1791
+ feature: "toolChoice",
1792
+ details: `Claude Code CLI does not support toolChoice '${options.toolChoice.type}'. Only automatic tool selection is available; the toolChoice parameter will be ignored.`
1793
+ });
1794
+ }
1795
+ if (options.maxOutputTokens !== void 0) {
1796
+ warnings.push({
1797
+ type: "unsupported",
1798
+ feature: "maxOutputTokens",
1799
+ details: "Claude Code CLI does not accept an output token cap. The maxOutputTokens parameter will be ignored."
1800
+ });
1801
+ }
1207
1802
  if (this.modelValidationWarning) {
1208
1803
  warnings.push({
1209
1804
  type: "other",
@@ -1236,7 +1831,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1236
1831
  const opts = {
1237
1832
  model: this.getModel(),
1238
1833
  abortController,
1239
- resume: effectiveResume ?? this.settings.resume ?? this.sessionId,
1834
+ resume: effectiveResume,
1240
1835
  pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
1241
1836
  maxTurns: this.settings.maxTurns,
1242
1837
  maxThinkingTokens: this.settings.maxThinkingTokens,
@@ -1262,6 +1857,12 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1262
1857
  mcpServers: this.settings.mcpServers,
1263
1858
  canUseTool: this.settings.canUseTool
1264
1859
  };
1860
+ if (this.settings.onUserDialog !== void 0) {
1861
+ opts.onUserDialog = this.settings.onUserDialog;
1862
+ }
1863
+ if (this.settings.supportedDialogKinds !== void 0) {
1864
+ opts.supportedDialogKinds = this.settings.supportedDialogKinds;
1865
+ }
1265
1866
  if (this.settings.systemPrompt !== void 0) {
1266
1867
  opts.systemPrompt = this.settings.systemPrompt;
1267
1868
  } else if (this.settings.customSystemPrompt !== void 0) {
@@ -1281,6 +1882,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1281
1882
  }
1282
1883
  if (this.settings.settingSources !== void 0) {
1283
1884
  opts.settingSources = this.settings.settingSources;
1885
+ } else {
1886
+ opts.settingSources = [];
1284
1887
  }
1285
1888
  if (this.settings.additionalDirectories !== void 0) {
1286
1889
  opts.additionalDirectories = this.settings.additionalDirectories;
@@ -1288,6 +1891,48 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1288
1891
  if (this.settings.agents !== void 0) {
1289
1892
  opts.agents = this.settings.agents;
1290
1893
  }
1894
+ if (this.settings.skills !== void 0) {
1895
+ opts.skills = this.settings.skills;
1896
+ }
1897
+ if (this.settings.settings !== void 0) {
1898
+ opts.settings = this.settings.settings;
1899
+ }
1900
+ if (this.settings.managedSettings !== void 0) {
1901
+ opts.managedSettings = this.settings.managedSettings;
1902
+ }
1903
+ if (this.settings.toolAliases !== void 0) {
1904
+ opts.toolAliases = this.settings.toolAliases;
1905
+ }
1906
+ if (this.settings.toolConfig !== void 0) {
1907
+ opts.toolConfig = this.settings.toolConfig;
1908
+ }
1909
+ if (this.settings.planModeInstructions !== void 0) {
1910
+ opts.planModeInstructions = this.settings.planModeInstructions;
1911
+ }
1912
+ if (this.settings.title !== void 0) {
1913
+ opts.title = this.settings.title;
1914
+ }
1915
+ if (this.settings.forwardSubagentText !== void 0) {
1916
+ opts.forwardSubagentText = this.settings.forwardSubagentText;
1917
+ }
1918
+ if (this.settings.agentProgressSummaries !== void 0) {
1919
+ opts.agentProgressSummaries = this.settings.agentProgressSummaries;
1920
+ }
1921
+ if (this.settings.includeHookEvents !== void 0) {
1922
+ opts.includeHookEvents = this.settings.includeHookEvents;
1923
+ }
1924
+ if (this.settings.taskBudget !== void 0) {
1925
+ opts.taskBudget = this.settings.taskBudget;
1926
+ }
1927
+ if (this.settings.sessionStore !== void 0) {
1928
+ opts.sessionStore = this.settings.sessionStore;
1929
+ }
1930
+ if (this.settings.sessionStoreFlush !== void 0) {
1931
+ opts.sessionStoreFlush = this.settings.sessionStoreFlush;
1932
+ }
1933
+ if (this.settings.loadTimeoutMs !== void 0) {
1934
+ opts.loadTimeoutMs = this.settings.loadTimeoutMs;
1935
+ }
1291
1936
  if (this.settings.includePartialMessages !== void 0) {
1292
1937
  opts.includePartialMessages = this.settings.includePartialMessages;
1293
1938
  }
@@ -1312,7 +1957,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1312
1957
  if (this.settings.hooks) {
1313
1958
  opts.hooks = this.settings.hooks;
1314
1959
  }
1315
- if (this.settings.sessionId !== void 0) {
1960
+ const effectiveForkSession = sdkOptions?.forkSession ?? this.settings.forkSession;
1961
+ const effectiveContinue = sdkOptions?.continue ?? this.settings.continue;
1962
+ if (this.settings.sessionId !== void 0 && !_ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
1963
+ resumePresent: opts.resume !== void 0,
1964
+ continue: effectiveContinue === true,
1965
+ forkSession: effectiveForkSession === true
1966
+ })) {
1316
1967
  opts.sessionId = this.settings.sessionId;
1317
1968
  }
1318
1969
  if (this.settings.debug !== void 0) {
@@ -1328,7 +1979,17 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1328
1979
  const rest = { ...sdkOverrides };
1329
1980
  delete rest.env;
1330
1981
  delete rest.stderr;
1331
- Object.assign(opts, rest);
1982
+ for (const [key, value] of Object.entries(rest)) {
1983
+ if (value !== void 0) {
1984
+ opts[key] = value;
1985
+ }
1986
+ }
1987
+ }
1988
+ this.applySessionResolution(opts, effectiveResume);
1989
+ if (typeof opts.fallbackModel === "string" && opts.fallbackModel === opts.model) {
1990
+ throw new Error(
1991
+ `fallbackModel cannot be the same as the model ('${String(opts.model)}'). Specify a different model for fallbackModel, or remove it.`
1992
+ );
1332
1993
  }
1333
1994
  const userStderrCallback = sdkStderr ?? this.settings.stderr;
1334
1995
  if (stderrCollector || userStderrCallback) {
@@ -1337,14 +1998,27 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1337
1998
  if (userStderrCallback) userStderrCallback(data);
1338
1999
  };
1339
2000
  }
1340
- if (this.settings.env !== void 0 || sdkEnv !== void 0) {
1341
- const baseEnv = getBaseProcessEnv();
1342
- opts.env = { ...baseEnv, ...this.settings.env, ...sdkEnv };
2001
+ const mergedEnv = {
2002
+ ...getBaseProcessEnv(),
2003
+ ...this.settings.env,
2004
+ ...sdkEnv
2005
+ };
2006
+ if (!("CLAUDE_AGENT_SDK_CLIENT_APP" in mergedEnv)) {
2007
+ mergedEnv.CLAUDE_AGENT_SDK_CLIENT_APP = DEFAULT_CLIENT_APP;
1343
2008
  }
2009
+ opts.env = mergedEnv;
1344
2010
  if (responseFormat?.type === "json" && responseFormat.schema) {
2011
+ const { schema: sanitizedSchema, strippedFormatPaths } = sanitizeJsonSchemaForOutputFormat(
2012
+ responseFormat.schema
2013
+ );
2014
+ if (strippedFormatPaths.length > 0) {
2015
+ this.logger.debug(
2016
+ `[claude-code] Stripped unsupported 'format' keywords from outputFormat schema (hints folded into descriptions; client-side Zod validation still enforces them) at: ${strippedFormatPaths.join(", ")}`
2017
+ );
2018
+ }
1345
2019
  opts.outputFormat = {
1346
2020
  type: "json_schema",
1347
- schema: responseFormat.schema
2021
+ schema: sanitizedSchema
1348
2022
  };
1349
2023
  }
1350
2024
  return opts;
@@ -1353,6 +2027,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1353
2027
  if (isAbortError(error)) {
1354
2028
  throw error;
1355
2029
  }
2030
+ if (error instanceof import_provider2.APICallError || error instanceof import_provider2.LoadAPIKeyError) {
2031
+ return error;
2032
+ }
1356
2033
  const isErrorWithMessage = (err) => {
1357
2034
  return typeof err === "object" && err !== null && "message" in err;
1358
2035
  };
@@ -1369,11 +2046,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1369
2046
  "claude auth login",
1370
2047
  "/login",
1371
2048
  // CLI returns "Please run /login"
1372
- "invalid api key"
2049
+ "invalid api key",
2050
+ "oauth_org_not_allowed"
2051
+ // SDK 0.3.x assistant error kind: OAuth org not permitted
1373
2052
  ];
1374
2053
  const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
2054
+ const errorKind = getStructuredErrorKind(error);
1375
2055
  const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
1376
- const isAuthError = authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
2056
+ const isAuthError = errorKind === "authentication_failed" || errorKind === "oauth_org_not_allowed" || authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
1377
2057
  if (isAuthError) {
1378
2058
  return createAuthenticationError({
1379
2059
  message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
@@ -1388,9 +2068,30 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1388
2068
  // It's controlled by the consumer via AbortSignal
1389
2069
  });
1390
2070
  }
1391
- const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1392
2071
  const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
1393
2072
  const stderr = stderrFromError || collectedStderr || void 0;
2073
+ if (errorKind === "overloaded" || errorKind === "rate_limit" || errorMessage.includes("overloaded")) {
2074
+ return createAPICallError({
2075
+ message: isErrorWithMessage(error) && error.message ? error.message : "Anthropic API is overloaded. Please retry.",
2076
+ code: errorCode || void 0,
2077
+ exitCode,
2078
+ stderr,
2079
+ promptExcerpt: messagesPrompt.substring(0, 200),
2080
+ isRetryable: true
2081
+ });
2082
+ }
2083
+ if (errorKind === "model_not_found" || errorMessage.includes("model_not_found") || errorMessage.includes("no such model")) {
2084
+ const originalMessage = isErrorWithMessage(error) && error.message ? error.message : "Model not found";
2085
+ return createAPICallError({
2086
+ message: `${originalMessage}. The requested model was not found. Verify the model id passed to the provider (e.g. 'opus', 'sonnet', 'haiku', or a full model name) and that your account has access to it.`,
2087
+ code: errorCode || void 0,
2088
+ exitCode,
2089
+ stderr,
2090
+ promptExcerpt: messagesPrompt.substring(0, 200),
2091
+ isRetryable: false
2092
+ });
2093
+ }
2094
+ const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1394
2095
  return createAPICallError({
1395
2096
  message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
1396
2097
  code: errorCode || void 0,
@@ -1426,6 +2127,152 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1426
2127
  }).join(", ");
1427
2128
  this.logger.warn(`[claude-code] MCP servers not connected: ${details}`);
1428
2129
  }
2130
+ /**
2131
+ * Handles SDK 0.3.x system messages other than 'init', shared by doGenerate
2132
+ * and doStream:
2133
+ * - 'api_retry' is counted into providerMetadata (`apiRetries`) and debug-logged.
2134
+ * - 'permission_denied' is warn-logged and recorded into providerMetadata
2135
+ * (`permissionDenials`); without this a denial is invisible until the
2136
+ * model talks about it.
2137
+ * - 'model_refusal_fallback' is debug-logged (the superseding assistant
2138
+ * message is handled by the text-dedup guard in the message loops).
2139
+ * - 'thinking_tokens' deltas are accumulated into providerMetadata
2140
+ * (`estimatedThinkingTokens`); the estimate is explicitly not the
2141
+ * authoritative billed output tokens, so it is surfaced as metadata
2142
+ * instead of feeding `usage.outputTokens.reasoning`.
2143
+ * - The subtypes in {@link INFORMATIONAL_SYSTEM_SUBTYPES} are intentionally
2144
+ * informational and only debug-logged.
2145
+ */
2146
+ handleSystemMessage(message, tracking, onRetractedUuids) {
2147
+ switch (message.subtype) {
2148
+ case "api_retry":
2149
+ tracking.apiRetries += 1;
2150
+ this.logger.debug(
2151
+ `[claude-code] API retry ${message.attempt}/${message.max_retries} in ${message.retry_delay_ms}ms - Status: ${message.error_status ?? "unknown"}, Error: ${message.error}`
2152
+ );
2153
+ break;
2154
+ case "permission_denied": {
2155
+ const reason = message.decision_reason ?? message.message;
2156
+ tracking.permissionDenials.push({
2157
+ toolName: message.tool_name,
2158
+ toolUseId: message.tool_use_id,
2159
+ ...reason !== void 0 && { reason }
2160
+ });
2161
+ this.logger.warn(
2162
+ `[claude-code] Permission denied - Tool: ${message.tool_name}${reason ? `, Reason: ${reason}` : ""}`
2163
+ );
2164
+ break;
2165
+ }
2166
+ case "mirror_error": {
2167
+ const mirrorError = message.error ?? "unknown error";
2168
+ const mirrorSessionId = message.key?.sessionId ?? message.session_id ?? "unknown";
2169
+ tracking.mirrorErrors.push({ error: mirrorError, sessionId: mirrorSessionId });
2170
+ this.logger.warn(
2171
+ `[claude-code] SessionStore mirror error (transcript batch dropped) - Session: ${mirrorSessionId}, Error: ${mirrorError}`
2172
+ );
2173
+ break;
2174
+ }
2175
+ case "model_refusal_fallback": {
2176
+ this.logger.debug(
2177
+ `[claude-code] Model refusal fallback - ${message.original_model} -> ${message.fallback_model} (direction: ${message.direction})`
2178
+ );
2179
+ const retractedUuids = message.retracted_message_uuids;
2180
+ if (onRetractedUuids && retractedUuids && retractedUuids.length > 0) {
2181
+ this.logger.debug(
2182
+ `[claude-code] Refusal fallback retracts ${retractedUuids.length} message uuid(s)`
2183
+ );
2184
+ onRetractedUuids(retractedUuids);
2185
+ }
2186
+ break;
2187
+ }
2188
+ case "thinking_tokens":
2189
+ tracking.estimatedThinkingTokens += message.estimated_tokens_delta;
2190
+ this.logger.debug(
2191
+ `[claude-code] Thinking tokens estimate - block total: ${message.estimated_tokens}, delta: ${message.estimated_tokens_delta}, accumulated: ${tracking.estimatedThinkingTokens}`
2192
+ );
2193
+ break;
2194
+ default:
2195
+ if (INFORMATIONAL_SYSTEM_SUBTYPES.has(message.subtype)) {
2196
+ this.logger.debug(
2197
+ `[claude-code] Ignoring informational system message: ${message.subtype}`
2198
+ );
2199
+ } else {
2200
+ this.logger.debug(`[claude-code] Unhandled system message subtype: ${message.subtype}`);
2201
+ }
2202
+ break;
2203
+ }
2204
+ }
2205
+ /**
2206
+ * Merges the result message's `permission_denials` list into the tracked
2207
+ * denials. PreToolUse-hook denies bypass canUseTool and emit no
2208
+ * `permission_denied` system event (per the SDK docs on
2209
+ * SDKPermissionDeniedMessage), so the result list is the only place they
2210
+ * surface. Entries already recorded from stream-time events are deduped by
2211
+ * `tool_use_id`.
2212
+ */
2213
+ mergeResultPermissionDenials(message, tracking) {
2214
+ for (const denial of message.permission_denials ?? []) {
2215
+ const alreadyTracked = tracking.permissionDenials.some(
2216
+ (d) => d.toolUseId !== void 0 && d.toolUseId === denial.tool_use_id
2217
+ );
2218
+ if (!alreadyTracked) {
2219
+ tracking.permissionDenials.push({
2220
+ toolName: denial.tool_name,
2221
+ toolUseId: denial.tool_use_id
2222
+ });
2223
+ }
2224
+ }
2225
+ }
2226
+ /**
2227
+ * Bounded post-result drain for the `prompt_suggestion` message
2228
+ * (promptSuggestions: true), shared by doGenerate and doStream. The
2229
+ * suggestion arrives AFTER the result message; the SDK emits at most one
2230
+ * per turn, so stop once it is delivered, and a timeout closes the
2231
+ * iterator (tearing down the subprocess) if the CLI lingers after the
2232
+ * result without emitting one. Advances the response's own generator, so
2233
+ * the caller's surrounding loop resumes to a finished iterator.
2234
+ */
2235
+ async drainPromptSuggestion(response, onPromptSuggestion) {
2236
+ const iterator = response[Symbol.asyncIterator]();
2237
+ let drainTimer;
2238
+ const drainTimeout = new Promise((resolve) => {
2239
+ drainTimer = setTimeout(
2240
+ () => resolve("timeout"),
2241
+ _ClaudeCodeLanguageModel.PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS
2242
+ );
2243
+ drainTimer.unref?.();
2244
+ });
2245
+ try {
2246
+ while (true) {
2247
+ const winner = await Promise.race([iterator.next(), drainTimeout]);
2248
+ if (winner === "timeout") {
2249
+ this.logger.debug("[claude-code] Post-result drain timed out; closing SDK iterator");
2250
+ void iterator.return?.().catch(() => {
2251
+ });
2252
+ break;
2253
+ }
2254
+ if (winner.done) {
2255
+ break;
2256
+ }
2257
+ const trailingMessage = winner.value;
2258
+ this.logger.debug(`[claude-code] Post-result message type: ${trailingMessage.type}`);
2259
+ if (trailingMessage.type === "prompt_suggestion") {
2260
+ onPromptSuggestion(trailingMessage.suggestion);
2261
+ void iterator.return?.().catch(() => {
2262
+ });
2263
+ break;
2264
+ }
2265
+ }
2266
+ } catch (drainError) {
2267
+ this.logger.debug(
2268
+ `[claude-code] Error draining post-result messages: ${drainError instanceof Error ? drainError.message : String(drainError)}`
2269
+ );
2270
+ } finally {
2271
+ if (drainTimer !== void 0) {
2272
+ clearTimeout(drainTimer);
2273
+ }
2274
+ }
2275
+ }
1429
2276
  async doGenerate(options) {
1430
2277
  this.logger.debug(`[claude-code] Starting doGenerate request with model: ${this.modelId}`);
1431
2278
  this.logger.debug(`[claude-code] Response format: ${options.responseFormat?.type ?? "none"}`);
@@ -1442,9 +2289,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1442
2289
  let abortListener;
1443
2290
  if (options.abortSignal?.aborted) {
1444
2291
  abortController.abort(options.abortSignal.reason);
1445
- } else if (options.abortSignal) {
1446
- abortListener = () => abortController.abort(options.abortSignal?.reason);
1447
- options.abortSignal.addEventListener("abort", abortListener, { once: true });
1448
2292
  }
1449
2293
  let collectedStderr = "";
1450
2294
  const stderrCollector = (data) => {
@@ -1459,15 +2303,77 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1459
2303
  sdkOptions,
1460
2304
  effectiveResume
1461
2305
  );
2306
+ if (options.abortSignal && !options.abortSignal.aborted) {
2307
+ abortListener = () => abortController.abort(options.abortSignal?.reason);
2308
+ options.abortSignal.addEventListener("abort", abortListener, { once: true });
2309
+ }
1462
2310
  let text = "";
1463
- const thinkingTraces = [];
2311
+ const contentSegments = [];
2312
+ const joinTextSegments = () => contentSegments.filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
2313
+ const joinFinalTurnTextSegments = () => {
2314
+ let start = 0;
2315
+ for (let i = 0; i < contentSegments.length; i++) {
2316
+ const kind = contentSegments[i]?.kind;
2317
+ if (kind === "tool-result" || kind === "tool-error") {
2318
+ start = i + 1;
2319
+ }
2320
+ }
2321
+ return contentSegments.slice(start).filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
2322
+ };
2323
+ const knownTools = /* @__PURE__ */ new Map();
2324
+ const retractedToolIds = /* @__PURE__ */ new Set();
2325
+ const evictBuffered = (retracted) => {
2326
+ if (retracted.size === 0) return;
2327
+ const retractedToolCallIds = computeRetractedToolCallIds(
2328
+ retracted,
2329
+ contentSegments.filter(
2330
+ (segment) => segment.kind === "tool-call" || segment.kind === "tool-result" || segment.kind === "tool-error"
2331
+ ).map((segment) => ({
2332
+ toolCallId: segment.toolCallId,
2333
+ uuid: segment.uuid
2334
+ }))
2335
+ );
2336
+ for (const toolCallId of retractedToolCallIds) {
2337
+ retractedToolIds.add(toolCallId);
2338
+ knownTools.delete(toolCallId);
2339
+ activeTaskTools.delete(toolCallId);
2340
+ }
2341
+ for (let i = contentSegments.length - 1; i >= 0; i--) {
2342
+ const segment = contentSegments[i];
2343
+ if (segment === void 0) continue;
2344
+ const segmentUuid = "uuid" in segment ? segment.uuid : void 0;
2345
+ const retractsSegment = segmentUuid !== void 0 && retracted.has(segmentUuid) || (segment.kind === "tool-result" || segment.kind === "tool-error") && retractedToolCallIds.has(segment.toolCallId);
2346
+ if (retractsSegment) {
2347
+ contentSegments.splice(i, 1);
2348
+ }
2349
+ }
2350
+ };
2351
+ const activeTaskTools = /* @__PURE__ */ new Map();
2352
+ const getFallbackParentId = () => {
2353
+ if (activeTaskTools.size === 1) {
2354
+ return activeTaskTools.keys().next().value ?? null;
2355
+ }
2356
+ return null;
2357
+ };
1464
2358
  let structuredOutput;
2359
+ let receivedResultMessage = false;
1465
2360
  let usage = createEmptyUsage();
1466
2361
  let finishReason = { unified: "stop", raw: void 0 };
1467
2362
  let wasTruncated = false;
1468
2363
  let costUsd;
1469
2364
  let durationMs;
1470
2365
  let modelUsage;
2366
+ let ttftMs;
2367
+ let ttftStreamMs;
2368
+ let timeToRequestMs;
2369
+ let warmSpareClaimed;
2370
+ let terminalReason;
2371
+ const metadataTracking = {
2372
+ apiRetries: 0,
2373
+ permissionDenials: [],
2374
+ mirrorErrors: [],
2375
+ estimatedThinkingTokens: 0
2376
+ };
1471
2377
  const warnings = this.generateAllWarnings(options, messagesPrompt);
1472
2378
  if (messageWarnings) {
1473
2379
  messageWarnings.forEach((warning) => {
@@ -1513,23 +2419,195 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1513
2419
  options: queryOptions
1514
2420
  });
1515
2421
  this.settings.onQueryCreated?.(response);
1516
- for await (const message of response) {
2422
+ let lastAssistantErrorKind;
2423
+ const sdkIterator = response[Symbol.asyncIterator]();
2424
+ const detachableResponse = {
2425
+ [Symbol.asyncIterator]: () => ({
2426
+ next: () => sdkIterator.next(),
2427
+ return: () => {
2428
+ void sdkIterator.return?.().catch(() => {
2429
+ });
2430
+ return Promise.resolve({ done: true, value: void 0 });
2431
+ }
2432
+ })
2433
+ };
2434
+ for await (const message of detachableResponse) {
1517
2435
  this.logger.debug(`[claude-code] Received message type: ${message.type}`);
1518
2436
  if (message.type === "assistant") {
1519
- const { text: messageText, thinking: messageThinking } = this.extractTextAndThinking(
1520
- message.message.content
1521
- );
1522
- text += messageText;
1523
- thinkingTraces.push(...messageThinking);
2437
+ if (typeof message.error === "string") {
2438
+ lastAssistantErrorKind = message.error;
2439
+ }
2440
+ applySupersede(message, evictBuffered, this.logger, "truthy");
2441
+ const messageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
2442
+ const sdkParentToolUseId = message.parent_tool_use_id;
2443
+ const content = message.message.content;
2444
+ if (Array.isArray(content)) {
2445
+ for (const block of content) {
2446
+ if (!isContentBlock(block)) continue;
2447
+ if (block.type === "text" && typeof block.text === "string") {
2448
+ if (block.text.length > 0) {
2449
+ contentSegments.push({
2450
+ kind: "text",
2451
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2452
+ text: block.text
2453
+ });
2454
+ }
2455
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
2456
+ contentSegments.push({
2457
+ kind: "reasoning",
2458
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2459
+ text: block.thinking
2460
+ });
2461
+ } else if (block.type === "tool_use") {
2462
+ const [tool3] = this.extractToolUses([block]);
2463
+ if (!tool3) continue;
2464
+ const parentToolCallId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
2465
+ sdkParentToolUseId,
2466
+ tool3.parentToolUseId,
2467
+ getFallbackParentId
2468
+ );
2469
+ this.logger.debug(
2470
+ `[claude-code] Tool use detected - Tool: ${tool3.name}, ID: ${tool3.id}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${parentToolCallId}`
2471
+ );
2472
+ knownTools.set(tool3.id, { name: tool3.name, parentToolCallId });
2473
+ if (isSubagentToolName(tool3.name)) {
2474
+ activeTaskTools.set(tool3.id, { startTime: Date.now() });
2475
+ }
2476
+ contentSegments.push({
2477
+ kind: "tool-call",
2478
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2479
+ toolCallId: tool3.id,
2480
+ part: this.buildToolCallPart(
2481
+ tool3.id,
2482
+ tool3.name,
2483
+ this.serializeToolInput(tool3.input),
2484
+ parentToolCallId
2485
+ )
2486
+ });
2487
+ }
2488
+ }
2489
+ }
2490
+ text = joinTextSegments();
2491
+ } else if (message.type === "user") {
2492
+ if (!message.message?.content) {
2493
+ this.logger.warn(
2494
+ `[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
2495
+ );
2496
+ continue;
2497
+ }
2498
+ const sdkParentToolUseIdForResults = message.parent_tool_use_id;
2499
+ const resultMessageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
2500
+ const content = message.message.content;
2501
+ for (const result of this.extractToolResults(content)) {
2502
+ if (this.isRetractedToolFrame(result.id, retractedToolIds, "result")) {
2503
+ continue;
2504
+ }
2505
+ const known = knownTools.get(result.id);
2506
+ const toolName = result.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2507
+ this.logger.debug(
2508
+ `[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
2509
+ );
2510
+ let parentToolCallId;
2511
+ if (known) {
2512
+ known.name = toolName;
2513
+ parentToolCallId = known.parentToolCallId;
2514
+ } else {
2515
+ this.logger.warn(
2516
+ `[claude-code] Received tool result for unknown tool ID: ${result.id}`
2517
+ );
2518
+ parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
2519
+ knownTools.set(result.id, { name: toolName, parentToolCallId });
2520
+ contentSegments.push({
2521
+ kind: "tool-call",
2522
+ toolCallId: result.id,
2523
+ part: this.buildToolCallPart(result.id, toolName, "", parentToolCallId)
2524
+ });
2525
+ }
2526
+ if (isSubagentToolName(toolName)) {
2527
+ activeTaskTools.delete(result.id);
2528
+ }
2529
+ contentSegments.push({
2530
+ kind: "tool-result",
2531
+ ...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
2532
+ toolCallId: result.id,
2533
+ part: this.buildToolResultPart(
2534
+ result.id,
2535
+ toolName,
2536
+ result.result,
2537
+ result.isError,
2538
+ parentToolCallId
2539
+ )
2540
+ });
2541
+ }
2542
+ for (const error of this.extractToolErrors(content)) {
2543
+ if (this.isRetractedToolFrame(error.id, retractedToolIds, "error")) {
2544
+ continue;
2545
+ }
2546
+ const known = knownTools.get(error.id);
2547
+ const toolName = error.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2548
+ this.logger.debug(
2549
+ `[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
2550
+ );
2551
+ let parentToolCallId;
2552
+ if (known) {
2553
+ known.name = toolName;
2554
+ parentToolCallId = known.parentToolCallId;
2555
+ } else {
2556
+ this.logger.warn(
2557
+ `[claude-code] Received tool error for unknown tool ID: ${error.id}`
2558
+ );
2559
+ parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
2560
+ knownTools.set(error.id, { name: toolName, parentToolCallId });
2561
+ contentSegments.push({
2562
+ kind: "tool-call",
2563
+ toolCallId: error.id,
2564
+ part: this.buildToolCallPart(error.id, toolName, "", parentToolCallId)
2565
+ });
2566
+ }
2567
+ if (isSubagentToolName(toolName)) {
2568
+ activeTaskTools.delete(error.id);
2569
+ }
2570
+ contentSegments.push({
2571
+ kind: "tool-error",
2572
+ ...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
2573
+ toolCallId: error.id,
2574
+ part: this.buildToolErrorResultPart(
2575
+ error.id,
2576
+ toolName,
2577
+ error.error,
2578
+ parentToolCallId
2579
+ )
2580
+ });
2581
+ }
1524
2582
  } else if (message.type === "result") {
1525
2583
  done();
2584
+ receivedResultMessage = true;
1526
2585
  this.setSessionId(message.session_id);
1527
2586
  costUsd = message.total_cost_usd;
1528
2587
  durationMs = message.duration_ms;
1529
2588
  modelUsage = message.modelUsage;
2589
+ if ("ttft_ms" in message) {
2590
+ ttftMs = message.ttft_ms;
2591
+ }
2592
+ if ("ttft_stream_ms" in message) {
2593
+ ttftStreamMs = message.ttft_stream_ms;
2594
+ }
2595
+ if ("time_to_request_ms" in message) {
2596
+ timeToRequestMs = message.time_to_request_ms;
2597
+ }
2598
+ if ("warm_spare_claimed" in message) {
2599
+ warmSpareClaimed = message.warm_spare_claimed;
2600
+ }
2601
+ terminalReason = message.terminal_reason;
2602
+ this.mergeResultPermissionDenials(message, metadataTracking);
1530
2603
  if ("is_error" in message && message.is_error === true) {
1531
- const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
1532
- throw Object.assign(new Error(errorMessage), { exitCode: 1 });
2604
+ const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
2605
+ const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
2606
+ const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
2607
+ throw Object.assign(new Error(errorMessage), {
2608
+ exitCode: 1,
2609
+ errorKind: lastAssistantErrorKind
2610
+ });
1533
2611
  }
1534
2612
  if (message.subtype === "error_max_structured_output_retries") {
1535
2613
  throw new Error(
@@ -1552,10 +2630,21 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1552
2630
  const stopReason = "stop_reason" in message ? message.stop_reason : void 0;
1553
2631
  finishReason = mapClaudeCodeFinishReason(message.subtype, stopReason);
1554
2632
  this.logger.debug(`[claude-code] Finish reason: ${finishReason.unified}`);
2633
+ const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
2634
+ if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
2635
+ await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
2636
+ }
2637
+ break;
1555
2638
  } else if (message.type === "system" && message.subtype === "init") {
1556
2639
  this.logMcpConnectionIssues(message.mcp_servers);
1557
2640
  this.setSessionId(message.session_id);
1558
2641
  this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
2642
+ } else if (message.type === "system") {
2643
+ this.handleSystemMessage(
2644
+ message,
2645
+ metadataTracking,
2646
+ buildRetractionEvictor(evictBuffered)
2647
+ );
1559
2648
  }
1560
2649
  }
1561
2650
  } catch (error) {
@@ -1585,15 +2674,45 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1585
2674
  options.abortSignal.removeEventListener("abort", abortListener);
1586
2675
  }
1587
2676
  }
1588
- const finalText = structuredOutput !== void 0 ? JSON.stringify(structuredOutput) : text;
2677
+ if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && receivedResultMessage && structuredOutput === void 0 && !wasTruncated && finishReason.unified === "stop") {
2678
+ const recoveredJsonText = extractJsonObjectText(joinFinalTurnTextSegments()) ?? extractJsonObjectText(joinTextSegments());
2679
+ if (recoveredJsonText !== void 0) {
2680
+ this.logger.warn(
2681
+ "[claude-code] outputFormat was requested but the CLI returned no structured_output; recovered JSON by parsing the prose response. The schema likely contains constructs the CLI cannot enforce - see the README structured-output limitations."
2682
+ );
2683
+ structuredOutput = JSON.parse(recoveredJsonText);
2684
+ } else {
2685
+ throw createAPICallError({
2686
+ message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
2687
+ promptExcerpt: messagesPrompt.substring(0, 200),
2688
+ isRetryable: false
2689
+ });
2690
+ }
2691
+ }
2692
+ const thinkingTraces = contentSegments.filter((segment) => segment.kind === "reasoning").map((segment) => segment.text);
2693
+ const contentParts = [];
2694
+ for (const segment of contentSegments) {
2695
+ if (segment.kind === "reasoning") {
2696
+ contentParts.push({ type: "reasoning", text: segment.text });
2697
+ } else if (segment.kind === "text") {
2698
+ if (structuredOutput !== void 0) continue;
2699
+ const last = contentParts[contentParts.length - 1];
2700
+ if (last !== void 0 && last.type === "text") {
2701
+ last.text += segment.text;
2702
+ } else {
2703
+ contentParts.push({ type: "text", text: segment.text });
2704
+ }
2705
+ } else {
2706
+ contentParts.push(segment.part);
2707
+ }
2708
+ }
2709
+ if (structuredOutput !== void 0) {
2710
+ contentParts.push({ type: "text", text: JSON.stringify(structuredOutput) });
2711
+ } else if (!contentParts.some((part) => part.type === "text")) {
2712
+ contentParts.push({ type: "text", text: "" });
2713
+ }
1589
2714
  return {
1590
- content: [
1591
- ...thinkingTraces.map((trace) => ({
1592
- type: "reasoning",
1593
- text: trace
1594
- })),
1595
- { type: "text", text: finalText }
1596
- ],
2715
+ content: contentParts,
1597
2716
  usage,
1598
2717
  finishReason,
1599
2718
  warnings,
@@ -1611,6 +2730,21 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1611
2730
  ...costUsd !== void 0 && { costUsd },
1612
2731
  ...durationMs !== void 0 && { durationMs },
1613
2732
  ...modelUsage !== void 0 && { modelUsage },
2733
+ ...ttftMs !== void 0 && { ttftMs },
2734
+ ...ttftStreamMs !== void 0 && { ttftStreamMs },
2735
+ ...timeToRequestMs !== void 0 && { timeToRequestMs },
2736
+ ...warmSpareClaimed !== void 0 && { warmSpareClaimed },
2737
+ ...terminalReason !== void 0 && { terminalReason },
2738
+ ...metadataTracking.apiRetries > 0 && { apiRetries: metadataTracking.apiRetries },
2739
+ ...metadataTracking.permissionDenials.length > 0 && {
2740
+ permissionDenials: metadataTracking.permissionDenials
2741
+ },
2742
+ ...metadataTracking.mirrorErrors.length > 0 && {
2743
+ mirrorErrors: metadataTracking.mirrorErrors
2744
+ },
2745
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
2746
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
2747
+ },
1614
2748
  ...wasTruncated && { truncated: true },
1615
2749
  ...thinkingTraces.length > 0 && { thinkingTraces }
1616
2750
  }
@@ -1633,9 +2767,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1633
2767
  let abortListener;
1634
2768
  if (options.abortSignal?.aborted) {
1635
2769
  abortController.abort(options.abortSignal.reason);
1636
- } else if (options.abortSignal) {
1637
- abortListener = () => abortController.abort(options.abortSignal?.reason);
1638
- options.abortSignal.addEventListener("abort", abortListener, { once: true });
1639
2770
  }
1640
2771
  let collectedStderr = "";
1641
2772
  const stderrCollector = (data) => {
@@ -1650,6 +2781,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1650
2781
  sdkOptions,
1651
2782
  effectiveResume
1652
2783
  );
2784
+ if (options.abortSignal && !options.abortSignal.aborted) {
2785
+ abortListener = () => abortController.abort(options.abortSignal?.reason);
2786
+ options.abortSignal.addEventListener("abort", abortListener, { once: true });
2787
+ }
1653
2788
  if (queryOptions.includePartialMessages === void 0) {
1654
2789
  queryOptions.includePartialMessages = true;
1655
2790
  }
@@ -1680,6 +2815,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1680
2815
  done = () => resolve(void 0);
1681
2816
  });
1682
2817
  const toolStates = /* @__PURE__ */ new Map();
2818
+ const retractedStreamToolIds = /* @__PURE__ */ new Set();
1683
2819
  const activeTaskTools = /* @__PURE__ */ new Map();
1684
2820
  const getFallbackParentId = () => {
1685
2821
  if (activeTaskTools.size === 1) {
@@ -1702,24 +2838,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1702
2838
  return;
1703
2839
  }
1704
2840
  closeToolInput(toolId, state);
1705
- controller.enqueue({
1706
- type: "tool-call",
1707
- toolCallId: toolId,
1708
- toolName: state.name,
1709
- input: state.lastSerializedInput ?? "",
1710
- providerExecuted: true,
1711
- dynamic: true,
1712
- // V3 field: indicates tool is provider-defined (not in user's tools map)
1713
- providerMetadata: {
1714
- "claude-code": {
1715
- // rawInput preserves the original serialized format before AI SDK normalization.
1716
- // Use this if you need the exact string sent to the Claude CLI, which may differ
1717
- // from the `input` field after AI SDK processing.
1718
- rawInput: state.lastSerializedInput ?? "",
1719
- parentToolCallId: state.parentToolCallId ?? null
1720
- }
1721
- }
1722
- });
2841
+ controller.enqueue(
2842
+ this.buildToolCallPart(
2843
+ toolId,
2844
+ state.name,
2845
+ state.lastSerializedInput ?? "",
2846
+ state.parentToolCallId
2847
+ )
2848
+ );
1723
2849
  state.callEmitted = true;
1724
2850
  };
1725
2851
  const finalizeToolCalls = () => {
@@ -1730,16 +2856,61 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1730
2856
  };
1731
2857
  let usage = createEmptyUsage();
1732
2858
  let accumulatedText = "";
2859
+ const textSegments = [];
1733
2860
  let textPartId;
1734
2861
  let streamedTextLength = 0;
2862
+ let emittedTextSinceLastAssistant = "";
1735
2863
  let hasReceivedStreamEvents = false;
1736
2864
  let hasStreamedJson = false;
2865
+ let lastAssistantErrorKind;
2866
+ const metadataTracking = {
2867
+ apiRetries: 0,
2868
+ permissionDenials: [],
2869
+ mirrorErrors: [],
2870
+ estimatedThinkingTokens: 0
2871
+ };
1737
2872
  const toolBlocksByIndex = /* @__PURE__ */ new Map();
1738
2873
  const toolInputAccumulators = /* @__PURE__ */ new Map();
1739
2874
  const textBlocksByIndex = /* @__PURE__ */ new Map();
1740
2875
  let textStreamedViaContentBlock = false;
1741
2876
  const reasoningBlocksByIndex = /* @__PURE__ */ new Map();
1742
2877
  let currentReasoningPartId;
2878
+ const evictLive = (retracted) => {
2879
+ if (retracted.size === 0) return;
2880
+ for (let i = textSegments.length - 1; i >= 0; i--) {
2881
+ const segmentUuid = textSegments[i]?.uuid;
2882
+ if (segmentUuid !== void 0 && retracted.has(segmentUuid)) {
2883
+ textSegments.splice(i, 1);
2884
+ }
2885
+ }
2886
+ accumulatedText = textSegments.map((segment) => segment.text).join("");
2887
+ const retractedToolCallIds = computeRetractedToolCallIds(
2888
+ retracted,
2889
+ [...toolStates].map(([toolId, state]) => ({
2890
+ toolCallId: toolId,
2891
+ uuid: state.messageUuid
2892
+ }))
2893
+ );
2894
+ for (const toolId of retractedToolCallIds) {
2895
+ const state = toolStates.get(toolId);
2896
+ if (!state) continue;
2897
+ activeTaskTools.delete(toolId);
2898
+ if (!state.callEmitted) {
2899
+ closeToolInput(toolId, state);
2900
+ toolStates.delete(toolId);
2901
+ retractedStreamToolIds.add(toolId);
2902
+ toolInputAccumulators.delete(toolId);
2903
+ for (const [blockIndex, mappedId] of toolBlocksByIndex) {
2904
+ if (mappedId === toolId) {
2905
+ toolBlocksByIndex.delete(blockIndex);
2906
+ }
2907
+ }
2908
+ this.logger.debug(
2909
+ `[claude-code] Retracted pending tool call from superseded message - ID: ${toolId}`
2910
+ );
2911
+ }
2912
+ }
2913
+ };
1743
2914
  try {
1744
2915
  controller.enqueue({ type: "stream-start", warnings });
1745
2916
  if (effectiveCanUseTool && effectivePermissionPromptToolName) {
@@ -1789,6 +2960,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1789
2960
  });
1790
2961
  accumulatedText += deltaText;
1791
2962
  streamedTextLength += deltaText.length;
2963
+ emittedTextSinceLastAssistant += deltaText;
1792
2964
  }
1793
2965
  if (event.type === "content_block_delta" && event.delta.type === "input_json_delta" && "partial_json" in event.delta && event.delta.partial_json) {
1794
2966
  const jsonDelta = event.delta.partial_json;
@@ -1848,13 +3020,16 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1848
3020
  toolInputAccumulators.set(toolId, "");
1849
3021
  let state = toolStates.get(toolId);
1850
3022
  if (!state) {
1851
- const currentParentId = toolName === "Task" ? null : getFallbackParentId();
3023
+ const partialParentId = message.parent_tool_use_id;
3024
+ const currentParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(partialParentId, void 0, getFallbackParentId);
3025
+ const envelopeUuid = message.uuid;
1852
3026
  state = {
1853
3027
  name: toolName,
1854
3028
  inputStarted: false,
1855
3029
  inputClosed: false,
1856
3030
  callEmitted: false,
1857
- parentToolCallId: currentParentId
3031
+ parentToolCallId: currentParentId,
3032
+ ...typeof envelopeUuid === "string" && { messageUuid: envelopeUuid }
1858
3033
  };
1859
3034
  toolStates.set(toolId, state);
1860
3035
  }
@@ -1874,7 +3049,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1874
3049
  }
1875
3050
  }
1876
3051
  });
1877
- if (toolName === "Task") {
3052
+ if (isSubagentToolName(toolName)) {
1878
3053
  activeTaskTools.set(toolId, { startTime: Date.now() });
1879
3054
  }
1880
3055
  state.inputStarted = true;
@@ -1958,20 +3133,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1958
3133
  const effectiveInput = accumulatedInput || state.lastSerializedInput || "";
1959
3134
  state.lastSerializedInput = effectiveInput;
1960
3135
  if (!state.callEmitted) {
1961
- controller.enqueue({
1962
- type: "tool-call",
1963
- toolCallId: toolId,
1964
- toolName: state.name,
1965
- input: effectiveInput,
1966
- providerExecuted: true,
1967
- dynamic: true,
1968
- providerMetadata: {
1969
- "claude-code": {
1970
- rawInput: effectiveInput,
1971
- parentToolCallId: state.parentToolCallId ?? null
1972
- }
1973
- }
1974
- });
3136
+ controller.enqueue(
3137
+ this.buildToolCallPart(
3138
+ toolId,
3139
+ state.name,
3140
+ effectiveInput,
3141
+ state.parentToolCallId
3142
+ )
3143
+ );
1975
3144
  state.callEmitted = true;
1976
3145
  }
1977
3146
  }
@@ -2013,6 +3182,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2013
3182
  continue;
2014
3183
  }
2015
3184
  if (message.type === "assistant") {
3185
+ if (typeof message.error === "string") {
3186
+ lastAssistantErrorKind = message.error;
3187
+ }
3188
+ const supersedesPriorMessages = applySupersede(message, evictLive, this.logger);
2016
3189
  if (!message.message?.content) {
2017
3190
  this.logger.warn(
2018
3191
  `[claude-code] Unexpected assistant message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
@@ -2040,19 +3213,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2040
3213
  const toolId = tool3.id;
2041
3214
  let state = toolStates.get(toolId);
2042
3215
  if (!state) {
2043
- const currentParentId = tool3.name === "Task" ? null : sdkParentToolUseId ?? tool3.parentToolUseId ?? getFallbackParentId();
3216
+ const currentParentId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
3217
+ sdkParentToolUseId,
3218
+ tool3.parentToolUseId,
3219
+ getFallbackParentId
3220
+ );
2044
3221
  state = {
2045
3222
  name: tool3.name,
2046
3223
  inputStarted: false,
2047
3224
  inputClosed: false,
2048
3225
  callEmitted: false,
2049
- parentToolCallId: currentParentId
3226
+ parentToolCallId: currentParentId,
3227
+ ...typeof message.uuid === "string" && { messageUuid: message.uuid }
2050
3228
  };
2051
3229
  toolStates.set(toolId, state);
2052
3230
  this.logger.debug(
2053
3231
  `[claude-code] New tool use detected - Tool: ${tool3.name}, ID: ${toolId}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${currentParentId}`
2054
3232
  );
2055
- } else if (!state.parentToolCallId && sdkParentToolUseId && tool3.name !== "Task") {
3233
+ } else if (!state.parentToolCallId && sdkParentToolUseId && !isSubagentToolName(tool3.name)) {
2056
3234
  state.parentToolCallId = sdkParentToolUseId;
2057
3235
  this.logger.debug(
2058
3236
  `[claude-code] Retroactive parent context - Tool: ${tool3.name}, ID: ${toolId}, parent: ${sdkParentToolUseId}`
@@ -2076,7 +3254,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2076
3254
  }
2077
3255
  }
2078
3256
  });
2079
- if (tool3.name === "Task") {
3257
+ if (isSubagentToolName(tool3.name)) {
2080
3258
  activeTaskTools.set(toolId, { startTime: Date.now() });
2081
3259
  }
2082
3260
  state.inputStarted = true;
@@ -2105,10 +3283,76 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2105
3283
  }
2106
3284
  const text = content.map((c) => c.type === "text" ? c.text : "").join("");
2107
3285
  if (text) {
2108
- if (hasReceivedStreamEvents) {
3286
+ if (supersedesPriorMessages) {
3287
+ textSegments.push({
3288
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3289
+ text
3290
+ });
3291
+ accumulatedText = textSegments.map((segment) => segment.text).join("");
3292
+ if (emittedTextSinceLastAssistant === text) {
3293
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3294
+ this.logger.debug(
3295
+ "[claude-code] Skipping text emission for superseding assistant message (replacement already streamed)"
3296
+ );
3297
+ } else if (emittedTextSinceLastAssistant.length > 0 && text.startsWith(emittedTextSinceLastAssistant)) {
3298
+ if (options.responseFormat?.type !== "json") {
3299
+ const suffix = text.slice(emittedTextSinceLastAssistant.length);
3300
+ if (suffix) {
3301
+ if (!textPartId) {
3302
+ textPartId = (0, import_provider_utils.generateId)();
3303
+ controller.enqueue({ type: "text-start", id: textPartId });
3304
+ }
3305
+ controller.enqueue({
3306
+ type: "text-delta",
3307
+ id: textPartId,
3308
+ delta: suffix
3309
+ });
3310
+ emittedTextSinceLastAssistant = text;
3311
+ }
3312
+ }
3313
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3314
+ this.logger.debug(
3315
+ "[claude-code] Emitted unstreamed suffix of superseding assistant message"
3316
+ );
3317
+ } else if (options.responseFormat?.type !== "json") {
3318
+ if (textPartId) {
3319
+ const closedTextId = textPartId;
3320
+ controller.enqueue({
3321
+ type: "text-end",
3322
+ id: closedTextId
3323
+ });
3324
+ textPartId = void 0;
3325
+ for (const [idx, blockTextId] of textBlocksByIndex) {
3326
+ if (blockTextId === closedTextId) {
3327
+ textBlocksByIndex.delete(idx);
3328
+ break;
3329
+ }
3330
+ }
3331
+ }
3332
+ textPartId = (0, import_provider_utils.generateId)();
3333
+ controller.enqueue({
3334
+ type: "text-start",
3335
+ id: textPartId
3336
+ });
3337
+ controller.enqueue({
3338
+ type: "text-delta",
3339
+ id: textPartId,
3340
+ delta: text
3341
+ });
3342
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3343
+ this.logger.debug(
3344
+ "[claude-code] Emitted superseding assistant message as a new text part (canonical replacement)"
3345
+ );
3346
+ }
3347
+ } else if (hasReceivedStreamEvents) {
2109
3348
  const newTextStart = streamedTextLength;
2110
3349
  const deltaText = text.length > newTextStart ? text.slice(newTextStart) : "";
2111
3350
  accumulatedText = text;
3351
+ textSegments.length = 0;
3352
+ textSegments.push({
3353
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3354
+ text
3355
+ });
2112
3356
  if (options.responseFormat?.type !== "json" && deltaText) {
2113
3357
  if (!textPartId) {
2114
3358
  textPartId = (0, import_provider_utils.generateId)();
@@ -2126,6 +3370,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2126
3370
  streamedTextLength = text.length;
2127
3371
  } else {
2128
3372
  accumulatedText += text;
3373
+ textSegments.push({
3374
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3375
+ text
3376
+ });
2129
3377
  if (options.responseFormat?.type !== "json") {
2130
3378
  if (!textPartId) {
2131
3379
  textPartId = (0, import_provider_utils.generateId)();
@@ -2142,6 +3390,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2142
3390
  }
2143
3391
  }
2144
3392
  }
3393
+ emittedTextSinceLastAssistant = "";
2145
3394
  } else if (message.type === "user") {
2146
3395
  if (!message.message?.content) {
2147
3396
  this.logger.warn(
@@ -2162,13 +3411,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2162
3411
  break;
2163
3412
  }
2164
3413
  }
2165
- accumulatedText = "";
2166
- streamedTextLength = 0;
2167
3414
  this.logger.debug("[claude-code] Closed text part due to user message");
2168
3415
  }
3416
+ accumulatedText = "";
3417
+ textSegments.length = 0;
3418
+ streamedTextLength = 0;
3419
+ emittedTextSinceLastAssistant = "";
2169
3420
  const sdkParentToolUseIdForResults = message.parent_tool_use_id;
2170
3421
  const content = message.message.content;
2171
3422
  for (const result of this.extractToolResults(content)) {
3423
+ if (this.isRetractedToolFrame(result.id, retractedStreamToolIds, "result")) {
3424
+ continue;
3425
+ }
2172
3426
  let state = toolStates.get(result.id);
2173
3427
  const toolName = result.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2174
3428
  this.logger.debug(
@@ -2178,7 +3432,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2178
3432
  this.logger.warn(
2179
3433
  `[claude-code] Received tool result for unknown tool ID: ${result.id}`
2180
3434
  );
2181
- const resolvedParentId = toolName === "Task" ? null : sdkParentToolUseIdForResults ?? getFallbackParentId();
3435
+ const resolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
3436
+ sdkParentToolUseIdForResults,
3437
+ void 0,
3438
+ getFallbackParentId
3439
+ );
2182
3440
  state = {
2183
3441
  name: toolName,
2184
3442
  inputStarted: false,
@@ -2212,50 +3470,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2212
3470
  }
2213
3471
  }
2214
3472
  state.name = toolName;
2215
- const normalizedResult = this.normalizeToolResult(result.result);
2216
- const rawResult = typeof result.result === "string" ? result.result : (() => {
2217
- try {
2218
- return JSON.stringify(result.result);
2219
- } catch {
2220
- return String(result.result);
2221
- }
2222
- })();
2223
- const maxToolResultSize = this.settings.maxToolResultSize;
2224
- const truncatedResult = truncateToolResultForStream(
2225
- normalizedResult,
2226
- maxToolResultSize
2227
- );
2228
- const truncatedRawResult = truncateToolResultForStream(
2229
- rawResult,
2230
- maxToolResultSize
2231
- );
2232
- const rawResultTruncated = truncatedRawResult !== rawResult;
2233
3473
  emitToolCall(result.id, state);
2234
- if (toolName === "Task") {
3474
+ if (isSubagentToolName(toolName)) {
2235
3475
  activeTaskTools.delete(result.id);
2236
3476
  }
2237
- controller.enqueue({
2238
- type: "tool-result",
2239
- toolCallId: result.id,
2240
- toolName,
2241
- result: truncatedResult,
2242
- isError: result.isError,
2243
- providerExecuted: true,
2244
- dynamic: true,
2245
- // V3 field: indicates tool is provider-defined
2246
- providerMetadata: {
2247
- "claude-code": {
2248
- // rawResult preserves the original CLI output string before JSON parsing.
2249
- // Use this when you need the exact string returned by the tool, especially
2250
- // if the `result` field has been parsed/normalized and you need the original format.
2251
- rawResult: truncatedRawResult,
2252
- rawResultTruncated,
2253
- parentToolCallId: state.parentToolCallId ?? null
2254
- }
2255
- }
2256
- });
3477
+ controller.enqueue(
3478
+ this.buildToolResultPart(
3479
+ result.id,
3480
+ toolName,
3481
+ result.result,
3482
+ result.isError,
3483
+ state.parentToolCallId
3484
+ )
3485
+ );
2257
3486
  }
2258
3487
  for (const error of this.extractToolErrors(content)) {
3488
+ if (this.isRetractedToolFrame(error.id, retractedStreamToolIds, "error")) {
3489
+ continue;
3490
+ }
2259
3491
  let state = toolStates.get(error.id);
2260
3492
  const toolName = error.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2261
3493
  this.logger.debug(
@@ -2265,7 +3497,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2265
3497
  this.logger.warn(
2266
3498
  `[claude-code] Received tool error for unknown tool ID: ${error.id}`
2267
3499
  );
2268
- const errorResolvedParentId = toolName === "Task" ? null : sdkParentToolUseIdForResults ?? getFallbackParentId();
3500
+ const errorResolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
3501
+ sdkParentToolUseIdForResults,
3502
+ void 0,
3503
+ getFallbackParentId
3504
+ );
2269
3505
  state = {
2270
3506
  name: toolName,
2271
3507
  inputStarted: true,
@@ -2276,37 +3512,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2276
3512
  toolStates.set(error.id, state);
2277
3513
  }
2278
3514
  emitToolCall(error.id, state);
2279
- if (toolName === "Task") {
3515
+ if (isSubagentToolName(toolName)) {
2280
3516
  activeTaskTools.delete(error.id);
2281
3517
  }
2282
- const rawError = typeof error.error === "string" ? error.error : typeof error.error === "object" && error.error !== null ? (() => {
2283
- try {
2284
- return JSON.stringify(error.error);
2285
- } catch {
2286
- return String(error.error);
2287
- }
2288
- })() : String(error.error);
2289
- controller.enqueue({
2290
- type: "tool-error",
2291
- toolCallId: error.id,
2292
- toolName,
2293
- error: rawError,
2294
- providerExecuted: true,
2295
- dynamic: true,
2296
- // V3 field: indicates tool is provider-defined
2297
- providerMetadata: {
2298
- "claude-code": {
2299
- rawError,
2300
- parentToolCallId: state.parentToolCallId ?? null
2301
- }
2302
- }
2303
- });
3518
+ controller.enqueue(
3519
+ this.buildToolErrorPart(error.id, toolName, error.error, state.parentToolCallId)
3520
+ );
2304
3521
  }
2305
3522
  } else if (message.type === "result") {
2306
3523
  done();
3524
+ this.mergeResultPermissionDenials(message, metadataTracking);
2307
3525
  if ("is_error" in message && message.is_error === true) {
2308
- const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
2309
- throw Object.assign(new Error(errorMessage), { exitCode: 1 });
3526
+ const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
3527
+ const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
3528
+ const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
3529
+ throw Object.assign(new Error(errorMessage), {
3530
+ exitCode: 1,
3531
+ errorKind: lastAssistantErrorKind
3532
+ });
2310
3533
  }
2311
3534
  if (message.subtype === "error_max_structured_output_retries") {
2312
3535
  throw new Error(
@@ -2354,6 +3577,38 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2354
3577
  type: "text-end",
2355
3578
  id: jsonTextId
2356
3579
  });
3580
+ } else if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && finishReason.unified === "stop") {
3581
+ const recoveredJsonText = extractJsonObjectText(accumulatedText);
3582
+ if (recoveredJsonText === void 0) {
3583
+ throw createAPICallError({
3584
+ message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
3585
+ promptExcerpt: messagesPrompt.substring(0, 200),
3586
+ isRetryable: false
3587
+ });
3588
+ }
3589
+ this.logger.warn(
3590
+ "[claude-code] outputFormat was requested but the CLI returned no structured_output; recovered JSON by parsing the prose response. The schema likely contains constructs the CLI cannot enforce - see the README structured-output limitations."
3591
+ );
3592
+ if (textPartId) {
3593
+ controller.enqueue({
3594
+ type: "text-end",
3595
+ id: textPartId
3596
+ });
3597
+ }
3598
+ const recoveredTextId = (0, import_provider_utils.generateId)();
3599
+ controller.enqueue({
3600
+ type: "text-start",
3601
+ id: recoveredTextId
3602
+ });
3603
+ controller.enqueue({
3604
+ type: "text-delta",
3605
+ id: recoveredTextId,
3606
+ delta: recoveredJsonText
3607
+ });
3608
+ controller.enqueue({
3609
+ type: "text-end",
3610
+ id: recoveredTextId
3611
+ });
2357
3612
  } else if (textPartId) {
2358
3613
  controller.enqueue({
2359
3614
  type: "text-end",
@@ -2391,6 +3646,32 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2391
3646
  ...message.modelUsage !== void 0 && {
2392
3647
  modelUsage: message.modelUsage
2393
3648
  },
3649
+ // SDK 0.3.x timing metadata (ttft_* only present on SDKResultSuccess)
3650
+ ..."ttft_ms" in message && message.ttft_ms !== void 0 && { ttftMs: message.ttft_ms },
3651
+ ..."ttft_stream_ms" in message && message.ttft_stream_ms !== void 0 && {
3652
+ ttftStreamMs: message.ttft_stream_ms
3653
+ },
3654
+ ..."time_to_request_ms" in message && message.time_to_request_ms !== void 0 && {
3655
+ timeToRequestMs: message.time_to_request_ms
3656
+ },
3657
+ ..."warm_spare_claimed" in message && message.warm_spare_claimed !== void 0 && {
3658
+ warmSpareClaimed: message.warm_spare_claimed
3659
+ },
3660
+ ...message.terminal_reason !== void 0 && {
3661
+ terminalReason: message.terminal_reason
3662
+ },
3663
+ ...metadataTracking.apiRetries > 0 && {
3664
+ apiRetries: metadataTracking.apiRetries
3665
+ },
3666
+ ...metadataTracking.permissionDenials.length > 0 && {
3667
+ permissionDenials: metadataTracking.permissionDenials
3668
+ },
3669
+ ...metadataTracking.mirrorErrors.length > 0 && {
3670
+ mirrorErrors: metadataTracking.mirrorErrors
3671
+ },
3672
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
3673
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
3674
+ },
2394
3675
  // JSON validation warnings are collected during streaming and included
2395
3676
  // in providerMetadata since the AI SDK's finish event doesn't support
2396
3677
  // a top-level warnings field (unlike stream-start which was already emitted)
@@ -2401,6 +3682,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2401
3682
  }
2402
3683
  });
2403
3684
  controller.close();
3685
+ const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
3686
+ if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
3687
+ await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
3688
+ }
2404
3689
  return;
2405
3690
  } else if (message.type === "system" && message.subtype === "init") {
2406
3691
  this.logMcpConnectionIssues(message.mcp_servers);
@@ -2412,6 +3697,15 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2412
3697
  timestamp: /* @__PURE__ */ new Date(),
2413
3698
  modelId: this.modelId
2414
3699
  });
3700
+ } else if (message.type === "system") {
3701
+ this.handleSystemMessage(
3702
+ message,
3703
+ metadataTracking,
3704
+ buildRetractionEvictor(evictLive)
3705
+ );
3706
+ } else if (message.type === "prompt_suggestion") {
3707
+ this.logger.debug("[claude-code] Received prompt suggestion");
3708
+ this.settings.onPromptSuggestion?.(message.suggestion);
2415
3709
  }
2416
3710
  }
2417
3711
  finalizeToolCalls();
@@ -2462,6 +3756,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2462
3756
  "claude-code": {
2463
3757
  ...this.sessionId !== void 0 && { sessionId: this.sessionId },
2464
3758
  truncated: true,
3759
+ ...metadataTracking.apiRetries > 0 && {
3760
+ apiRetries: metadataTracking.apiRetries
3761
+ },
3762
+ ...metadataTracking.permissionDenials.length > 0 && {
3763
+ permissionDenials: metadataTracking.permissionDenials
3764
+ },
3765
+ ...metadataTracking.mirrorErrors.length > 0 && {
3766
+ mirrorErrors: metadataTracking.mirrorErrors
3767
+ },
3768
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
3769
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
3770
+ },
2465
3771
  ...streamWarnings.length > 0 && {
2466
3772
  warnings: warningsJson
2467
3773
  }
@@ -2577,10 +3883,40 @@ var claudeCode = createClaudeCode();
2577
3883
 
2578
3884
  // src/index.ts
2579
3885
  var import_claude_agent_sdk3 = require("@anthropic-ai/claude-agent-sdk");
3886
+ var import_claude_agent_sdk4 = require("@anthropic-ai/claude-agent-sdk");
3887
+ var import_claude_agent_sdk5 = require("@anthropic-ai/claude-agent-sdk");
2580
3888
 
2581
3889
  // src/mcp-helpers.ts
2582
3890
  var import_claude_agent_sdk2 = require("@anthropic-ai/claude-agent-sdk");
2583
3891
  var import_zod2 = require("zod");
3892
+ function buildIsErrorResult(text) {
3893
+ return {
3894
+ isError: true,
3895
+ content: [{ type: "text", text }]
3896
+ };
3897
+ }
3898
+ async function normalizeToolResultToText(toolName, result) {
3899
+ if (result != null && typeof result[Symbol.asyncIterator] === "function") {
3900
+ let last;
3901
+ for await (const chunk of result) {
3902
+ last = chunk;
3903
+ }
3904
+ result = last;
3905
+ }
3906
+ if (typeof result === "string") {
3907
+ return { text: result };
3908
+ }
3909
+ try {
3910
+ return { text: JSON.stringify(result) ?? "undefined" };
3911
+ } catch (serializationError) {
3912
+ const reason = serializationError instanceof Error ? serializationError.message : String(serializationError);
3913
+ return {
3914
+ isError: buildIsErrorResult(
3915
+ `Tool "${toolName}" succeeded but its result could not be serialized to JSON: ${reason}`
3916
+ )
3917
+ };
3918
+ }
3919
+ }
2584
3920
  function createCustomMcpServer(config) {
2585
3921
  const defs = Object.entries(config.tools).map(
2586
3922
  ([name, def]) => (0, import_claude_agent_sdk2.tool)(
@@ -2593,19 +3929,97 @@ function createCustomMcpServer(config) {
2593
3929
  );
2594
3930
  return (0, import_claude_agent_sdk2.createSdkMcpServer)({ name: config.name, version: config.version, tools: defs });
2595
3931
  }
3932
+ var AI_SDK_SCHEMA_SYMBOL = Symbol.for("vercel.ai.schema");
3933
+ function isAiSdkJsonSchema(schema) {
3934
+ return typeof schema === "object" && schema !== null && AI_SDK_SCHEMA_SYMBOL in schema;
3935
+ }
3936
+ function isZodObjectSchema(schema) {
3937
+ if (typeof schema !== "object" || schema === null) {
3938
+ return false;
3939
+ }
3940
+ const candidate = schema;
3941
+ if (!("_zod" in candidate) && !("_def" in candidate)) {
3942
+ return false;
3943
+ }
3944
+ const typeTag = candidate._zod?.def?.type ?? candidate._def?.typeName;
3945
+ if (typeTag !== "object" && typeTag !== "ZodObject") {
3946
+ return false;
3947
+ }
3948
+ return typeof candidate.shape === "object" && candidate.shape !== null;
3949
+ }
3950
+ function createAiSdkMcpServer(name, tools) {
3951
+ const defs = Object.entries(tools).map(([toolName, def]) => {
3952
+ const execute = def.execute;
3953
+ if (typeof execute !== "function") {
3954
+ throw new Error(
3955
+ `createAiSdkMcpServer: tool "${toolName}" has no execute function. Only tools that execute locally can be bridged to the Claude Code CLI.`
3956
+ );
3957
+ }
3958
+ if (isAiSdkJsonSchema(def.inputSchema)) {
3959
+ throw new Error(
3960
+ `createAiSdkMcpServer: tool "${toolName}" uses a JSON Schema-based inputSchema (e.g. the AI SDK's jsonSchema() helper). Only Zod object schemas are supported because the Agent SDK's tool() requires a Zod shape. Define inputSchema with z.object({...}) instead.`
3961
+ );
3962
+ }
3963
+ if (!isZodObjectSchema(def.inputSchema)) {
3964
+ throw new Error(
3965
+ `createAiSdkMcpServer: tool "${toolName}" has an inputSchema that is not a Zod object schema. Pass the same z.object({...}) schema you would give to the AI SDK tool() helper.`
3966
+ );
3967
+ }
3968
+ const zodSchema = def.inputSchema;
3969
+ return (0, import_claude_agent_sdk2.tool)(
3970
+ toolName,
3971
+ def.description ?? "",
3972
+ zodSchema.shape,
3973
+ async (args, extra) => {
3974
+ try {
3975
+ const extraInfo = extra ?? {};
3976
+ const result = await execute.call(def, args, {
3977
+ toolCallId: extraInfo.requestId !== void 0 ? String(extraInfo.requestId) : void 0,
3978
+ abortSignal: extraInfo.signal
3979
+ });
3980
+ const normalized = await normalizeToolResultToText(toolName, result);
3981
+ if ("isError" in normalized) {
3982
+ return normalized.isError;
3983
+ }
3984
+ return { content: [{ type: "text", text: normalized.text }] };
3985
+ } catch (error) {
3986
+ return buildIsErrorResult(error instanceof Error ? error.message : String(error));
3987
+ }
3988
+ }
3989
+ );
3990
+ });
3991
+ return (0, import_claude_agent_sdk2.createSdkMcpServer)({ name, tools: defs });
3992
+ }
2596
3993
  // Annotate the CommonJS export names for ESM import in node:
2597
3994
  0 && (module.exports = {
3995
+ AbortError,
2598
3996
  ClaudeCodeLanguageModel,
3997
+ HOOK_EVENTS,
3998
+ InMemorySessionStore,
3999
+ SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
2599
4000
  claudeCode,
2600
4001
  createAPICallError,
4002
+ createAiSdkMcpServer,
2601
4003
  createAuthenticationError,
2602
4004
  createClaudeCode,
2603
4005
  createCustomMcpServer,
2604
4006
  createSdkMcpServer,
2605
4007
  createTimeoutError,
4008
+ deleteSession,
4009
+ foldSessionSummary,
4010
+ forkSession,
2606
4011
  getErrorMetadata,
4012
+ getSessionInfo,
4013
+ getSessionMessages,
4014
+ getSubagentMessages,
4015
+ importSessionToStore,
2607
4016
  isAuthenticationError,
2608
4017
  isTimeoutError,
4018
+ listSessions,
4019
+ listSubagents,
4020
+ renameSession,
4021
+ startup,
4022
+ tagSession,
2609
4023
  tool
2610
4024
  });
2611
4025
  //# sourceMappingURL=index.cjs.map