ai-sdk-provider-claude-code 3.4.3 → 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.js CHANGED
@@ -2,12 +2,29 @@
2
2
  import { NoSuchModelError as NoSuchModelError2 } from "@ai-sdk/provider";
3
3
 
4
4
  // src/claude-code-language-model.ts
5
- import { NoSuchModelError } from "@ai-sdk/provider";
5
+ import { NoSuchModelError, APICallError as APICallError2, LoadAPIKeyError as LoadAPIKeyError2 } from "@ai-sdk/provider";
6
6
  import { generateId } from "@ai-sdk/provider-utils";
7
7
 
8
8
  // src/convert-to-claude-code-messages.ts
9
9
  var IMAGE_URL_WARNING = "Image URLs are not supported by this provider; supply base64/data URLs.";
10
10
  var IMAGE_CONVERSION_WARNING = "Unable to convert image content; supply base64/data URLs.";
11
+ var MAX_TOOL_CALL_INPUT_LENGTH = 1e3;
12
+ function serializeToolCallInput(input) {
13
+ let serialized;
14
+ if (input === void 0) {
15
+ serialized = "";
16
+ } else {
17
+ try {
18
+ serialized = JSON.stringify(input) ?? String(input);
19
+ } catch {
20
+ serialized = String(input);
21
+ }
22
+ }
23
+ if (serialized.length > MAX_TOOL_CALL_INPUT_LENGTH) {
24
+ return `${serialized.slice(0, MAX_TOOL_CALL_INPUT_LENGTH)}...[truncated]`;
25
+ }
26
+ return serialized;
27
+ }
11
28
  function normalizeBase64(base64) {
12
29
  return base64.replace(/\s+/g, "");
13
30
  }
@@ -190,8 +207,9 @@ function convertToClaudeCodeMessages(prompt) {
190
207
  }
191
208
  const toolCalls = message.content.filter((part) => part.type === "tool-call");
192
209
  if (toolCalls.length > 0) {
193
- assistantContent += `
194
- [Tool calls made]`;
210
+ const serializedCalls = toolCalls.map((call) => `[Tool call: ${call.toolName}(${serializeToolCallInput(call.input)})]`).join("\n");
211
+ assistantContent += assistantContent ? `
212
+ ${serializedCalls}` : serializedCalls;
195
213
  }
196
214
  }
197
215
  const formattedAssistant = `Assistant: ${assistantContent}`;
@@ -400,6 +418,9 @@ function mapClaudeCodeFinishReason(subtype, stopReason) {
400
418
  // src/validation.ts
401
419
  import { z } from "zod";
402
420
  import { existsSync } from "fs";
421
+ function isBlankResume(value) {
422
+ return typeof value === "string" && value.trim() === "";
423
+ }
403
424
  var loggerFunctionSchema = z.object({
404
425
  debug: z.any().refine((val) => typeof val === "function", {
405
426
  message: "debug must be a function"
@@ -420,23 +441,29 @@ var claudeCodeSettingsSchema = z.object({
420
441
  appendSystemPrompt: z.string().optional(),
421
442
  systemPrompt: z.union([
422
443
  z.string(),
444
+ z.array(z.string()),
423
445
  z.object({
424
446
  type: z.literal("preset"),
425
447
  preset: z.literal("claude_code"),
426
- append: z.string().optional()
448
+ append: z.string().optional(),
449
+ excludeDynamicSections: z.boolean().optional()
427
450
  })
428
451
  ]).optional(),
429
452
  maxTurns: z.number().int().min(1).max(100).optional(),
430
453
  maxThinkingTokens: z.number().int().positive().max(1e5).optional(),
431
454
  thinking: z.union([
432
- z.object({ type: z.literal("adaptive") }).strict(),
455
+ z.object({
456
+ type: z.literal("adaptive"),
457
+ display: z.enum(["summarized", "omitted"]).optional()
458
+ }).strict(),
433
459
  z.object({
434
460
  type: z.literal("enabled"),
435
- budgetTokens: z.number().int().positive().optional()
461
+ budgetTokens: z.number().int().positive().optional(),
462
+ display: z.enum(["summarized", "omitted"]).optional()
436
463
  }).strict(),
437
464
  z.object({ type: z.literal("disabled") }).strict()
438
465
  ]).optional(),
439
- effort: z.enum(["low", "medium", "high", "max"]).optional(),
466
+ effort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
440
467
  promptSuggestions: z.boolean().optional(),
441
468
  cwd: z.string().refine(
442
469
  (val) => {
@@ -449,11 +476,19 @@ var claudeCodeSettingsSchema = z.object({
449
476
  ).optional(),
450
477
  executable: z.enum(["bun", "deno", "node"]).optional(),
451
478
  executableArgs: z.array(z.string()).optional(),
452
- permissionMode: z.enum(["default", "acceptEdits", "bypassPermissions", "plan", "delegate", "dontAsk"]).optional(),
479
+ // Mirrors the SDK 0.3.x PermissionMode union ('auto' and 'dontAsk' were
480
+ // added in 0.3.x; 'delegate' was dropped AND is rejected by the CLI's
481
+ // --permission-mode flag parser, so it is rejected here too).
482
+ permissionMode: z.enum(["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"]).optional(),
453
483
  permissionPromptToolName: z.string().optional(),
454
484
  continue: z.boolean().optional(),
455
485
  resume: z.string().optional(),
456
- sessionId: z.string().optional(),
486
+ // The CLI rejects --session-id values that are not valid UUIDs, so
487
+ // enforce the UUID shape here instead of failing at query time.
488
+ sessionId: z.string().refine(
489
+ (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),
490
+ { message: "sessionId must be a valid UUID (the CLI rejects non-UUID session IDs)" }
491
+ ).optional(),
457
492
  allowedTools: z.array(z.string()).optional(),
458
493
  disallowedTools: z.array(z.string()).optional(),
459
494
  betas: z.array(z.string()).optional(),
@@ -462,7 +497,9 @@ var claudeCodeSettingsSchema = z.object({
462
497
  maxBudgetUsd: z.number().min(0).optional(),
463
498
  plugins: z.array(
464
499
  z.object({
465
- type: z.string(),
500
+ // SDK SdkPluginConfig: only 'local' is supported; the SDK throws
501
+ // 'Unsupported plugin type' at query time for anything else.
502
+ type: z.literal("local"),
466
503
  path: z.string()
467
504
  }).passthrough()
468
505
  ).optional(),
@@ -477,12 +514,41 @@ var claudeCodeSettingsSchema = z.object({
477
514
  preset: z.literal("claude_code")
478
515
  })
479
516
  ]).optional(),
517
+ skills: z.union([z.array(z.string()), z.literal("all")]).optional(),
518
+ settings: z.union([
519
+ z.string(),
520
+ z.record(z.string(), z.any())
521
+ // inline Settings object
522
+ ]).optional(),
523
+ managedSettings: z.record(z.string(), z.any()).optional(),
524
+ toolAliases: z.record(z.string(), z.string()).optional(),
525
+ toolConfig: z.object({
526
+ askUserQuestion: z.object({
527
+ previewFormat: z.enum(["markdown", "html"]).optional()
528
+ }).passthrough().optional()
529
+ }).passthrough().optional(),
530
+ planModeInstructions: z.string().optional(),
531
+ title: z.string().optional(),
532
+ forwardSubagentText: z.boolean().optional(),
533
+ agentProgressSummaries: z.boolean().optional(),
534
+ includeHookEvents: z.boolean().optional(),
535
+ taskBudget: z.object({ total: z.number().positive() }).strict().optional(),
536
+ sessionStore: z.any().refine(
537
+ (val) => val === void 0 || typeof val === "object" && val !== null && typeof val.append === "function" && typeof val.load === "function",
538
+ { message: "sessionStore must be an object with append() and load() functions" }
539
+ ).optional(),
540
+ sessionStoreFlush: z.enum(["batched", "eager"]).optional(),
541
+ loadTimeoutMs: z.number().int().positive().optional(),
480
542
  settingSources: z.array(z.enum(["user", "project", "local"])).optional(),
481
543
  streamingInput: z.enum(["auto", "always", "off"]).optional(),
482
544
  // Hooks and tool-permission callback (permissive validation of shapes)
483
545
  canUseTool: z.any().refine((v) => v === void 0 || typeof v === "function", {
484
546
  message: "canUseTool must be a function"
485
547
  }).optional(),
548
+ onUserDialog: z.any().refine((v) => v === void 0 || typeof v === "function", {
549
+ message: "onUserDialog must be a function"
550
+ }).optional(),
551
+ supportedDialogKinds: z.array(z.string()).optional(),
486
552
  hooks: z.record(
487
553
  z.string(),
488
554
  z.array(
@@ -535,7 +601,8 @@ var claudeCodeSettingsSchema = z.object({
535
601
  tools: z.array(z.string()).optional(),
536
602
  disallowedTools: z.array(z.string()).optional(),
537
603
  prompt: z.string(),
538
- model: z.enum(["sonnet", "opus", "haiku", "inherit"]).optional(),
604
+ // SDK 0.3.x AgentDefinition accepts any model alias or full model ID
605
+ model: z.string().optional(),
539
606
  mcpServers: z.array(
540
607
  z.union([
541
608
  z.string(),
@@ -566,10 +633,14 @@ var claudeCodeSettingsSchema = z.object({
566
633
  }).optional(),
567
634
  onStreamStart: z.any().refine((val) => val === void 0 || typeof val === "function", {
568
635
  message: "onStreamStart must be a function"
636
+ }).optional(),
637
+ // Callback invoked with the predicted next user prompt (active unless promptSuggestions: false)
638
+ onPromptSuggestion: z.any().refine((val) => val === void 0 || typeof val === "function", {
639
+ message: "onPromptSuggestion must be a function"
569
640
  }).optional()
570
641
  }).strict();
571
642
  function validateModelId(modelId) {
572
- const knownModels = ["opus", "sonnet", "haiku"];
643
+ const knownModels = ["opus", "sonnet", "haiku", "fable"];
573
644
  if (!modelId || modelId.trim() === "") {
574
645
  throw new Error("Model ID cannot be empty");
575
646
  }
@@ -593,6 +664,51 @@ function validateSettings(settings) {
593
664
  return { valid: false, warnings, errors };
594
665
  }
595
666
  const validSettings = result.data;
667
+ const sdkOptionsRecord = validSettings.sdkOptions;
668
+ const effective = (key) => {
669
+ const override = sdkOptionsRecord?.[key];
670
+ return override !== void 0 ? override : validSettings[key];
671
+ };
672
+ const effSessionStore = effective("sessionStore");
673
+ const effectiveResumeId = () => {
674
+ for (const candidate of [sdkOptionsRecord?.resume, validSettings.resume]) {
675
+ if (typeof candidate === "string" && !isBlankResume(candidate)) {
676
+ return candidate;
677
+ }
678
+ }
679
+ return void 0;
680
+ };
681
+ if (effSessionStore !== void 0 && effective("persistSession") === false) {
682
+ errors.push(
683
+ "sessionStore cannot be combined with persistSession: false. Transcript mirroring requires local session writes; remove persistSession: false or drop sessionStore."
684
+ );
685
+ return { valid: false, warnings, errors };
686
+ }
687
+ if (effSessionStore !== void 0 && effective("enableFileCheckpointing") === true) {
688
+ errors.push(
689
+ "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."
690
+ );
691
+ return { valid: false, warnings, errors };
692
+ }
693
+ if (effective("continue") === true && effSessionStore !== void 0 && effectiveResumeId() === void 0 && typeof effSessionStore.listSessions !== "function") {
694
+ errors.push(
695
+ "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."
696
+ );
697
+ return { valid: false, warnings, errors };
698
+ }
699
+ const effSettingsOption = effective("settings");
700
+ if (effective("sandbox") !== void 0 && typeof effSettingsOption === "string" && !(effSettingsOption.trim().startsWith("{") && effSettingsOption.trim().endsWith("}"))) {
701
+ errors.push(
702
+ "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."
703
+ );
704
+ return { valid: false, warnings, errors };
705
+ }
706
+ if (effective("sessionId") !== void 0 && effective("forkSession") !== true && (effective("continue") === true || effectiveResumeId() !== void 0)) {
707
+ errors.push(
708
+ "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."
709
+ );
710
+ return { valid: false, warnings, errors };
711
+ }
596
712
  if (validSettings.maxTurns && validSettings.maxTurns > 20) {
597
713
  warnings.push(
598
714
  `High maxTurns value (${validSettings.maxTurns}) may lead to long-running conversations`
@@ -621,11 +737,30 @@ function validateSettings(settings) {
621
737
  if (validSettings.disallowedTools) {
622
738
  validateToolNames(validSettings.disallowedTools, "disallowed");
623
739
  }
624
- if (validSettings.allowedTools?.includes("Skill") && !validSettings.settingSources) {
740
+ const effDialogKinds = effective("supportedDialogKinds");
741
+ if (Array.isArray(effDialogKinds) && effDialogKinds.length > 0 && effective("onUserDialog") == null) {
742
+ errors.push(
743
+ "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."
744
+ );
745
+ return { valid: false, warnings, errors };
746
+ }
747
+ const effAllowedTools = effective("allowedTools");
748
+ if (Array.isArray(effAllowedTools) && effAllowedTools.includes("Skill") && !effective("settingSources")) {
625
749
  warnings.push(
626
750
  "allowedTools includes 'Skill' but settingSources is not set. Skills require settingSources (e.g., ['user', 'project']) to load skill definitions."
627
751
  );
628
752
  }
753
+ if (validSettings.agents) {
754
+ const knownAgentModelAliases = ["sonnet", "opus", "haiku", "fable", "inherit"];
755
+ for (const [agentName, agent] of Object.entries(validSettings.agents)) {
756
+ const agentModel = agent.model;
757
+ if (agentModel !== void 0 && !knownAgentModelAliases.includes(agentModel) && !agentModel.includes("-")) {
758
+ warnings.push(
759
+ `Unknown model alias '${agentModel}' for agent '${agentName}'. Known aliases are: ${knownAgentModelAliases.join(", ")}; full model IDs (e.g. 'claude-sonnet-4-5') are also accepted.`
760
+ );
761
+ }
762
+ }
763
+ }
629
764
  return { valid: true, warnings, errors };
630
765
  } catch (error) {
631
766
  errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
@@ -646,6 +781,137 @@ function validateSessionId(sessionId) {
646
781
  return void 0;
647
782
  }
648
783
 
784
+ // src/sanitize-json-schema.ts
785
+ var SUBSCHEMA_MAP_KEYWORDS = [
786
+ "properties",
787
+ "patternProperties",
788
+ "$defs",
789
+ "definitions",
790
+ "dependentSchemas",
791
+ // draft-07 `dependencies` values are either subschemas (schema form) or
792
+ // arrays of property-name strings (array form); sanitizeNode passes
793
+ // string arrays through untouched, so walking both forms is safe.
794
+ "dependencies"
795
+ ];
796
+ var SUBSCHEMA_KEYWORDS = [
797
+ "items",
798
+ // may also be an array of subschemas (draft-07 tuple form)
799
+ "additionalItems",
800
+ "additionalProperties",
801
+ "unevaluatedItems",
802
+ "unevaluatedProperties",
803
+ "not",
804
+ "contains",
805
+ "propertyNames",
806
+ "contentSchema",
807
+ "if",
808
+ "then",
809
+ "else"
810
+ ];
811
+ var SUBSCHEMA_LIST_KEYWORDS = ["prefixItems", "anyOf", "oneOf", "allOf"];
812
+ function sanitizeJsonSchemaForOutputFormat(schema) {
813
+ const strippedFormatPaths = [];
814
+ const sanitized = sanitizeNode(schema, "#", /* @__PURE__ */ new WeakSet(), strippedFormatPaths);
815
+ return {
816
+ schema: sanitized ?? schema,
817
+ strippedFormatPaths
818
+ };
819
+ }
820
+ function sanitizeNode(node, path, visiting, strippedFormatPaths) {
821
+ if (typeof node !== "object" || node === null) {
822
+ return node;
823
+ }
824
+ if (visiting.has(node)) {
825
+ return node;
826
+ }
827
+ visiting.add(node);
828
+ try {
829
+ if (Array.isArray(node)) {
830
+ return sanitizeList(node, path, visiting, strippedFormatPaths);
831
+ }
832
+ const record = node;
833
+ let result = record;
834
+ const setKey = (key, value) => {
835
+ if (result === record) {
836
+ result = { ...record };
837
+ }
838
+ result[key] = value;
839
+ };
840
+ if (typeof record.format === "string") {
841
+ const format = record.format;
842
+ const existingDescription = record.description;
843
+ result = { ...record };
844
+ delete result.format;
845
+ if (typeof existingDescription === "string" && existingDescription.length > 0) {
846
+ result.description = `${existingDescription} (expected format: ${format})`;
847
+ } else if (existingDescription === void 0 || existingDescription === "") {
848
+ result.description = `Expected format: ${format}`;
849
+ }
850
+ strippedFormatPaths.push(path);
851
+ }
852
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
853
+ const map = record[keyword];
854
+ if (typeof map !== "object" || map === null || Array.isArray(map)) continue;
855
+ const mapRecord = map;
856
+ let newMap = mapRecord;
857
+ for (const [name, child] of Object.entries(mapRecord)) {
858
+ const sanitizedChild = sanitizeNode(
859
+ child,
860
+ `${path}/${keyword}/${name}`,
861
+ visiting,
862
+ strippedFormatPaths
863
+ );
864
+ if (sanitizedChild !== child) {
865
+ if (newMap === mapRecord) {
866
+ newMap = { ...mapRecord };
867
+ }
868
+ newMap[name] = sanitizedChild;
869
+ }
870
+ }
871
+ if (newMap !== mapRecord) {
872
+ setKey(keyword, newMap);
873
+ }
874
+ }
875
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
876
+ const child = record[keyword];
877
+ if (typeof child !== "object" || child === null) continue;
878
+ const sanitizedChild = sanitizeNode(
879
+ child,
880
+ `${path}/${keyword}`,
881
+ visiting,
882
+ strippedFormatPaths
883
+ );
884
+ if (sanitizedChild !== child) {
885
+ setKey(keyword, sanitizedChild);
886
+ }
887
+ }
888
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
889
+ const list = record[keyword];
890
+ if (!Array.isArray(list)) continue;
891
+ const sanitizedList = sanitizeList(list, `${path}/${keyword}`, visiting, strippedFormatPaths);
892
+ if (sanitizedList !== list) {
893
+ setKey(keyword, sanitizedList);
894
+ }
895
+ }
896
+ return result;
897
+ } finally {
898
+ visiting.delete(node);
899
+ }
900
+ }
901
+ function sanitizeList(list, path, visiting, strippedFormatPaths) {
902
+ let result = list;
903
+ for (let i = 0; i < list.length; i++) {
904
+ const sanitizedChild = sanitizeNode(list[i], `${path}/${i}`, visiting, strippedFormatPaths);
905
+ if (sanitizedChild !== list[i]) {
906
+ if (result === list) {
907
+ result = [...list];
908
+ }
909
+ result[i] = sanitizedChild;
910
+ }
911
+ }
912
+ return result;
913
+ }
914
+
649
915
  // src/logger.ts
650
916
  var defaultLogger = {
651
917
  // eslint-disable-next-line no-console
@@ -692,6 +958,8 @@ function createVerboseLogger(logger, verbose = false) {
692
958
 
693
959
  // src/claude-code-language-model.ts
694
960
  import { query } from "@anthropic-ai/claude-agent-sdk";
961
+ var PROVIDER_VERSION = "3.5.0";
962
+ var DEFAULT_CLIENT_APP = `ai-sdk-provider-claude-code/${PROVIDER_VERSION}`;
695
963
  var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
696
964
  var MIN_TRUNCATION_LENGTH = 512;
697
965
  function isClaudeCodeTruncationError(error, bufferedText) {
@@ -723,6 +991,34 @@ function isClaudeCodeTruncationError(error, bufferedText) {
723
991
  }
724
992
  return true;
725
993
  }
994
+ 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.";
995
+ function extractJsonObjectText(text) {
996
+ const trimmed = text.trim();
997
+ if (!trimmed) {
998
+ return void 0;
999
+ }
1000
+ const candidates = [trimmed];
1001
+ 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);
1002
+ candidates.push(...fencedBlocks.reverse());
1003
+ for (const candidate of candidates) {
1004
+ if (!candidate) continue;
1005
+ try {
1006
+ const parsed = JSON.parse(candidate);
1007
+ if (typeof parsed === "object" && parsed !== null) {
1008
+ return candidate;
1009
+ }
1010
+ } catch {
1011
+ }
1012
+ }
1013
+ return void 0;
1014
+ }
1015
+ function getStructuredErrorKind(error) {
1016
+ if (typeof error === "object" && error !== null && "errorKind" in error) {
1017
+ const kind = error.errorKind;
1018
+ if (typeof kind === "string") return kind;
1019
+ }
1020
+ return void 0;
1021
+ }
726
1022
  function isAbortError(err) {
727
1023
  if (err && typeof err === "object") {
728
1024
  const e = err;
@@ -733,6 +1029,7 @@ function isAbortError(err) {
733
1029
  }
734
1030
  var DEFAULT_INHERITED_ENV_VARS = process.platform === "win32" ? [
735
1031
  "APPDATA",
1032
+ "COMSPEC",
736
1033
  "HOMEDRIVE",
737
1034
  "HOMEPATH",
738
1035
  "LOCALAPPDATA",
@@ -747,23 +1044,89 @@ var DEFAULT_INHERITED_ENV_VARS = process.platform === "win32" ? [
747
1044
  "WINDIR"
748
1045
  ] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER", "LANG", "LC_ALL", "TMPDIR"];
749
1046
  var CLAUDE_ENV_VARS = ["CLAUDE_CONFIG_DIR"];
1047
+ var NETWORK_ENV_VARS = [
1048
+ "HTTP_PROXY",
1049
+ "HTTPS_PROXY",
1050
+ "NO_PROXY",
1051
+ "http_proxy",
1052
+ "https_proxy",
1053
+ "no_proxy",
1054
+ "NODE_EXTRA_CA_CERTS",
1055
+ "SSL_CERT_FILE",
1056
+ "SSL_CERT_DIR"
1057
+ ];
1058
+ var CLOUD_ENV_VARS = ["GCLOUD_PROJECT", "CLOUD_ML_REGION"];
1059
+ var INHERITED_ENV_PREFIXES = ["ANTHROPIC_", "CLAUDE_", "AWS_", "GOOGLE_"];
750
1060
  function getBaseProcessEnv() {
751
1061
  const env = {};
752
- const allowedKeys = /* @__PURE__ */ new Set([...DEFAULT_INHERITED_ENV_VARS, ...CLAUDE_ENV_VARS]);
753
- for (const key of allowedKeys) {
1062
+ const allowedKeys = /* @__PURE__ */ new Set([
1063
+ ...DEFAULT_INHERITED_ENV_VARS,
1064
+ ...CLAUDE_ENV_VARS,
1065
+ ...NETWORK_ENV_VARS,
1066
+ ...CLOUD_ENV_VARS
1067
+ ]);
1068
+ const addIfSafe = (key) => {
754
1069
  const value = process.env[key];
755
1070
  if (typeof value !== "string") {
756
- continue;
1071
+ return;
757
1072
  }
758
1073
  if (value.startsWith("()")) {
759
- continue;
1074
+ return;
760
1075
  }
761
1076
  env[key] = value;
1077
+ };
1078
+ for (const key of allowedKeys) {
1079
+ addIfSafe(key);
1080
+ }
1081
+ for (const key of Object.keys(process.env)) {
1082
+ if (INHERITED_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1083
+ addIfSafe(key);
1084
+ }
762
1085
  }
763
1086
  return env;
764
1087
  }
765
1088
  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).";
766
1089
  var SDK_OPTIONS_BLOCKLIST = /* @__PURE__ */ new Set(["model", "abortController", "prompt", "outputFormat"]);
1090
+ var SUBAGENT_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
1091
+ function isSubagentToolName(name) {
1092
+ return SUBAGENT_TOOL_NAMES.has(name);
1093
+ }
1094
+ function resolveToolParentId(messageLevel, blockLevel, inferFallback) {
1095
+ if (messageLevel !== void 0) return messageLevel;
1096
+ if (typeof blockLevel === "string") return blockLevel;
1097
+ return inferFallback();
1098
+ }
1099
+ function computeRetractedToolCallIds(retracted, descriptors) {
1100
+ const ids = /* @__PURE__ */ new Set();
1101
+ for (const { toolCallId, uuid } of descriptors) {
1102
+ if (uuid !== void 0 && retracted.has(uuid)) {
1103
+ ids.add(toolCallId);
1104
+ }
1105
+ }
1106
+ return ids;
1107
+ }
1108
+ function applySupersede(message, evict, logger, guard = "array") {
1109
+ const supersedes = message.supersedes;
1110
+ const triggered = guard === "truthy" ? Boolean(supersedes && supersedes.length > 0) : Array.isArray(supersedes) && supersedes.length > 0;
1111
+ if (!triggered) {
1112
+ return false;
1113
+ }
1114
+ logger.debug(`[claude-code] Assistant message supersedes ${supersedes.length} prior message(s)`);
1115
+ evict(new Set(supersedes));
1116
+ return true;
1117
+ }
1118
+ function buildRetractionEvictor(evict) {
1119
+ return (uuids) => evict(new Set(uuids));
1120
+ }
1121
+ var INFORMATIONAL_SYSTEM_SUBTYPES = /* @__PURE__ */ new Set([
1122
+ "notification",
1123
+ "status",
1124
+ "task_updated",
1125
+ "session_state_changed",
1126
+ "commands_changed",
1127
+ "memory_recall",
1128
+ "plugin_install"
1129
+ ]);
767
1130
  function isContentBlock(item) {
768
1131
  return typeof item === "object" && item !== null && "type" in item;
769
1132
  }
@@ -986,6 +1349,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
986
1349
  // 100KB warning threshold
987
1350
  static MAX_DELTA_CALC_SIZE = 1e4;
988
1351
  // 10KB delta computation threshold
1352
+ // Upper bound for draining post-result messages (prompt_suggestion) so a
1353
+ // lingering CLI subprocess cannot be held open indefinitely after finish.
1354
+ static PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS = 1e4;
989
1355
  modelId;
990
1356
  settings;
991
1357
  sessionId;
@@ -1033,27 +1399,71 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1033
1399
  return sanitized;
1034
1400
  }
1035
1401
  getEffectiveResume(sdkOptions) {
1036
- return sdkOptions?.resume ?? this.settings.resume ?? this.sessionId;
1037
- }
1038
- extractTextAndThinking(content) {
1039
- if (!Array.isArray(content)) return { text: "", thinking: [] };
1040
- let text = "";
1041
- const thinking = [];
1042
- for (const part of content) {
1043
- if (!isContentBlock(part)) continue;
1044
- if (part.type === "text" && typeof part.text === "string") {
1045
- text += part.text;
1046
- } else if (part.type === "thinking" && typeof part.thinking === "string") {
1047
- thinking.push(part.thinking);
1402
+ for (const candidate of [sdkOptions?.resume, this.settings.resume, this.sessionId]) {
1403
+ if (typeof candidate === "string" && !isBlankResume(candidate)) {
1404
+ return candidate;
1048
1405
  }
1049
1406
  }
1050
- if (text.length > 0 && typeof text !== "string") {
1051
- throw new Error("extractTextAndThinking: accumulated text must be a string");
1052
- }
1053
- if (thinking.some((t) => typeof t !== "string")) {
1054
- throw new Error("extractTextAndThinking: all thinking entries must be strings");
1407
+ return void 0;
1408
+ }
1409
+ /**
1410
+ * Single source of truth for the CLI's `--session-id` exclusivity rule.
1411
+ *
1412
+ * The CLI rejects `--session-id` together with `--resume`/`--continue`
1413
+ * unless `--fork-session` is also set (forkSession then names the forked
1414
+ * session's own ID). This predicate captures "there IS a resume/continue
1415
+ * target AND we are not forking", i.e. the case where a session id must NOT
1416
+ * coexist. It is referenced by both:
1417
+ * - the pre-merge forwarding guard (via its inverse), which decides whether
1418
+ * to forward `settings.sessionId` onto the base options, and
1419
+ * - the post-merge exclusivity drop, which removes any session id that the
1420
+ * generic sdkOptions overlay (or the auto-resume turn) re-introduced.
1421
+ *
1422
+ * Keeping one definition guarantees both sites agree on what "conflicts with
1423
+ * a session id" means. The mirror in validation.ts (construction-time)
1424
+ * intentionally stays separate: it reads settings+sdkOptions, not a built
1425
+ * opts object.
1426
+ */
1427
+ static sessionIdConflictsWithResumeOrContinue(args) {
1428
+ return (args.resumePresent || args.continue) && !args.forkSession;
1429
+ }
1430
+ /**
1431
+ * Owns ALL session-id / resume cross-option resolution on the FINAL merged
1432
+ * options, in the single correct order. Called once, immediately after the
1433
+ * generic sdkOptions overlay in createQueryOptions.
1434
+ *
1435
+ * Two concerns, in this exact order (order matters: step 1 can change whether
1436
+ * step 2 sees a resume target):
1437
+ *
1438
+ * 1. Blank-resume restoration. The overlay copies the raw `sdkOptions.resume`
1439
+ * verbatim, which can re-introduce a blank/whitespace value over the
1440
+ * base `resume` that getEffectiveResume already normalized. The SDK treats
1441
+ * a blank resume as absent, so a blank must NOT clobber the computed
1442
+ * fallback — restore `effectiveResume` (already blank-stripped; may itself
1443
+ * be undefined for a genuinely new session) rather than leaving '' or
1444
+ * forcing undefined, which would erase a real settings.resume / captured
1445
+ * session id.
1446
+ *
1447
+ * 2. Session-id exclusivity. Drop `opts.sessionId` whenever it conflicts with
1448
+ * a resume/continue target (see sessionIdConflictsWithResumeOrContinue).
1449
+ * This runs on the merged opts so it catches a sessionId re-added by the
1450
+ * sdkOptions overlay AND the auto-resumed second turn (where resume was
1451
+ * populated from the captured session id). It complements — does not
1452
+ * replace — the pre-merge forwarding guard, which governs whether
1453
+ * settings.sessionId was forwarded BEFORE the overlay could mutate
1454
+ * forkSession/continue/resume.
1455
+ */
1456
+ applySessionResolution(opts, effectiveResume) {
1457
+ if (isBlankResume(opts.resume)) {
1458
+ opts.resume = effectiveResume;
1459
+ }
1460
+ if (opts.sessionId !== void 0 && _ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
1461
+ resumePresent: opts.resume !== void 0,
1462
+ continue: opts.continue === true,
1463
+ forkSession: opts.forkSession === true
1464
+ })) {
1465
+ opts.sessionId = void 0;
1055
1466
  }
1056
- return { text, thinking };
1057
1467
  }
1058
1468
  extractToolUses(content) {
1059
1469
  return filterContentBlocks(content, "tool_use").map((block) => {
@@ -1147,6 +1557,153 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1147
1557
  }
1148
1558
  return result;
1149
1559
  }
1560
+ /**
1561
+ * Builds a provider-executed `tool-call` part from an assistant `tool_use`
1562
+ * block. Shared by doGenerate (content part) and doStream (stream part) so
1563
+ * the two paths cannot drift in field shape.
1564
+ */
1565
+ buildToolCallPart(toolCallId, toolName, input, parentToolCallId) {
1566
+ return {
1567
+ type: "tool-call",
1568
+ toolCallId,
1569
+ toolName,
1570
+ input,
1571
+ providerExecuted: true,
1572
+ dynamic: true,
1573
+ // V3 field: indicates tool is provider-defined (not in user's tools map)
1574
+ providerMetadata: {
1575
+ "claude-code": {
1576
+ // rawInput preserves the original serialized format before AI SDK normalization.
1577
+ // Use this if you need the exact string sent to the Claude CLI, which may differ
1578
+ // from the `input` field after AI SDK processing.
1579
+ rawInput: input,
1580
+ parentToolCallId: parentToolCallId ?? null
1581
+ }
1582
+ }
1583
+ };
1584
+ }
1585
+ /**
1586
+ * Builds a provider-executed `tool-result` part from a user-message
1587
+ * `tool_result` block, applying normalization and `maxToolResultSize`
1588
+ * truncation. Shared by doGenerate and doStream.
1589
+ */
1590
+ buildToolResultPart(toolCallId, toolName, result, isError, parentToolCallId) {
1591
+ const normalizedResult = this.normalizeToolResult(result);
1592
+ const rawResult = typeof result === "string" ? result : result === void 0 ? (
1593
+ // tool_result blocks may omit `content` entirely; '' keeps both
1594
+ // `result` (NonNullable<JSONValue>) and rawResult (JSONValue) valid.
1595
+ ""
1596
+ ) : (() => {
1597
+ try {
1598
+ return JSON.stringify(result) ?? String(result);
1599
+ } catch {
1600
+ return String(result);
1601
+ }
1602
+ })();
1603
+ const maxToolResultSize = this.settings.maxToolResultSize;
1604
+ const truncatedResult = truncateToolResultForStream(normalizedResult, maxToolResultSize);
1605
+ const truncatedRawResult = truncateToolResultForStream(rawResult, maxToolResultSize);
1606
+ const rawResultTruncated = truncatedRawResult !== rawResult;
1607
+ return {
1608
+ type: "tool-result",
1609
+ toolCallId,
1610
+ toolName,
1611
+ // `?? ''`: absent `content` (undefined) and string results that
1612
+ // normalize to JSON null (e.g. the string "null") must not violate the
1613
+ // NonNullable<JSONValue> contract of LanguageModelV3ToolResult.result.
1614
+ result: truncatedResult ?? "",
1615
+ isError,
1616
+ providerExecuted: true,
1617
+ dynamic: true,
1618
+ // V3 field: indicates tool is provider-defined
1619
+ providerMetadata: {
1620
+ "claude-code": {
1621
+ // rawResult preserves the original CLI output string before JSON parsing.
1622
+ // Use this when you need the exact string returned by the tool, especially
1623
+ // if the `result` field has been parsed/normalized and you need the original format.
1624
+ rawResult: truncatedRawResult,
1625
+ rawResultTruncated,
1626
+ parentToolCallId: parentToolCallId ?? null
1627
+ }
1628
+ }
1629
+ };
1630
+ }
1631
+ serializeToolError(error) {
1632
+ return typeof error === "string" ? error : typeof error === "object" && error !== null ? (() => {
1633
+ try {
1634
+ return JSON.stringify(error) ?? String(error);
1635
+ } catch {
1636
+ return String(error);
1637
+ }
1638
+ })() : String(error);
1639
+ }
1640
+ /**
1641
+ * Builds a provider-executed `tool-error` STREAM part from a user-message
1642
+ * `tool_error` block (doStream only; AI SDK core handles tool-error stream
1643
+ * parts natively).
1644
+ */
1645
+ buildToolErrorPart(toolCallId, toolName, error, parentToolCallId) {
1646
+ const rawError = this.serializeToolError(error);
1647
+ return {
1648
+ type: "tool-error",
1649
+ toolCallId,
1650
+ toolName,
1651
+ error: rawError,
1652
+ providerExecuted: true,
1653
+ dynamic: true,
1654
+ // V3 field: indicates tool is provider-defined
1655
+ providerMetadata: {
1656
+ "claude-code": {
1657
+ rawError,
1658
+ parentToolCallId: parentToolCallId ?? null
1659
+ }
1660
+ }
1661
+ };
1662
+ }
1663
+ /**
1664
+ * Builds a V3 `tool-result` CONTENT part with `isError: true` from a
1665
+ * user-message `tool_error` block (doGenerate only). The V3 content union
1666
+ * has no `tool-error` member and AI SDK core's asContent() silently drops
1667
+ * unknown content part types, so an extension tool-error part would never
1668
+ * reach `generateText` users — an isError tool-result, by contrast,
1669
+ * round-trips into a proper tool-error part in steps content.
1670
+ */
1671
+ buildToolErrorResultPart(toolCallId, toolName, error, parentToolCallId) {
1672
+ const rawError = this.serializeToolError(error);
1673
+ return {
1674
+ type: "tool-result",
1675
+ toolCallId,
1676
+ toolName,
1677
+ result: rawError,
1678
+ isError: true,
1679
+ providerExecuted: true,
1680
+ dynamic: true,
1681
+ // V3 field: indicates tool is provider-defined
1682
+ providerMetadata: {
1683
+ "claude-code": {
1684
+ rawError,
1685
+ parentToolCallId: parentToolCallId ?? null
1686
+ }
1687
+ }
1688
+ };
1689
+ }
1690
+ /**
1691
+ * Policy (P3): late-frame drop guard, shared by all four tool_result/tool_error
1692
+ * sites (doGenerate result+error, doStream result+error). When a frame's tool
1693
+ * id is tombstoned (its tool-call was retracted by a supersede/refusal-fallback
1694
+ * signal), the frame must be DROPPED rather than re-synthesized into an orphan
1695
+ * tool-call. Centralizing the predicate + debug message keeps the four sites in
1696
+ * lockstep so a future tombstone change can't be applied to only some of them.
1697
+ */
1698
+ isRetractedToolFrame(id, tombstone, frameKind) {
1699
+ if (!tombstone.has(id)) {
1700
+ return false;
1701
+ }
1702
+ this.logger.debug(
1703
+ `[claude-code] Dropping tool ${frameKind} for retracted (superseded) tool ID: ${id}`
1704
+ );
1705
+ return true;
1706
+ }
1150
1707
  generateAllWarnings(options, prompt) {
1151
1708
  const warnings = [];
1152
1709
  const unsupportedParams = [];
@@ -1167,6 +1724,27 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1167
1724
  });
1168
1725
  }
1169
1726
  }
1727
+ if (options.tools !== void 0 && options.tools.length > 0) {
1728
+ warnings.push({
1729
+ type: "unsupported",
1730
+ feature: "tools",
1731
+ 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)."
1732
+ });
1733
+ }
1734
+ if (options.toolChoice !== void 0 && options.toolChoice.type !== "auto") {
1735
+ warnings.push({
1736
+ type: "unsupported",
1737
+ feature: "toolChoice",
1738
+ details: `Claude Code CLI does not support toolChoice '${options.toolChoice.type}'. Only automatic tool selection is available; the toolChoice parameter will be ignored.`
1739
+ });
1740
+ }
1741
+ if (options.maxOutputTokens !== void 0) {
1742
+ warnings.push({
1743
+ type: "unsupported",
1744
+ feature: "maxOutputTokens",
1745
+ details: "Claude Code CLI does not accept an output token cap. The maxOutputTokens parameter will be ignored."
1746
+ });
1747
+ }
1170
1748
  if (this.modelValidationWarning) {
1171
1749
  warnings.push({
1172
1750
  type: "other",
@@ -1199,7 +1777,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1199
1777
  const opts = {
1200
1778
  model: this.getModel(),
1201
1779
  abortController,
1202
- resume: effectiveResume ?? this.settings.resume ?? this.sessionId,
1780
+ resume: effectiveResume,
1203
1781
  pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
1204
1782
  maxTurns: this.settings.maxTurns,
1205
1783
  maxThinkingTokens: this.settings.maxThinkingTokens,
@@ -1225,6 +1803,12 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1225
1803
  mcpServers: this.settings.mcpServers,
1226
1804
  canUseTool: this.settings.canUseTool
1227
1805
  };
1806
+ if (this.settings.onUserDialog !== void 0) {
1807
+ opts.onUserDialog = this.settings.onUserDialog;
1808
+ }
1809
+ if (this.settings.supportedDialogKinds !== void 0) {
1810
+ opts.supportedDialogKinds = this.settings.supportedDialogKinds;
1811
+ }
1228
1812
  if (this.settings.systemPrompt !== void 0) {
1229
1813
  opts.systemPrompt = this.settings.systemPrompt;
1230
1814
  } else if (this.settings.customSystemPrompt !== void 0) {
@@ -1244,6 +1828,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1244
1828
  }
1245
1829
  if (this.settings.settingSources !== void 0) {
1246
1830
  opts.settingSources = this.settings.settingSources;
1831
+ } else {
1832
+ opts.settingSources = [];
1247
1833
  }
1248
1834
  if (this.settings.additionalDirectories !== void 0) {
1249
1835
  opts.additionalDirectories = this.settings.additionalDirectories;
@@ -1251,6 +1837,48 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1251
1837
  if (this.settings.agents !== void 0) {
1252
1838
  opts.agents = this.settings.agents;
1253
1839
  }
1840
+ if (this.settings.skills !== void 0) {
1841
+ opts.skills = this.settings.skills;
1842
+ }
1843
+ if (this.settings.settings !== void 0) {
1844
+ opts.settings = this.settings.settings;
1845
+ }
1846
+ if (this.settings.managedSettings !== void 0) {
1847
+ opts.managedSettings = this.settings.managedSettings;
1848
+ }
1849
+ if (this.settings.toolAliases !== void 0) {
1850
+ opts.toolAliases = this.settings.toolAliases;
1851
+ }
1852
+ if (this.settings.toolConfig !== void 0) {
1853
+ opts.toolConfig = this.settings.toolConfig;
1854
+ }
1855
+ if (this.settings.planModeInstructions !== void 0) {
1856
+ opts.planModeInstructions = this.settings.planModeInstructions;
1857
+ }
1858
+ if (this.settings.title !== void 0) {
1859
+ opts.title = this.settings.title;
1860
+ }
1861
+ if (this.settings.forwardSubagentText !== void 0) {
1862
+ opts.forwardSubagentText = this.settings.forwardSubagentText;
1863
+ }
1864
+ if (this.settings.agentProgressSummaries !== void 0) {
1865
+ opts.agentProgressSummaries = this.settings.agentProgressSummaries;
1866
+ }
1867
+ if (this.settings.includeHookEvents !== void 0) {
1868
+ opts.includeHookEvents = this.settings.includeHookEvents;
1869
+ }
1870
+ if (this.settings.taskBudget !== void 0) {
1871
+ opts.taskBudget = this.settings.taskBudget;
1872
+ }
1873
+ if (this.settings.sessionStore !== void 0) {
1874
+ opts.sessionStore = this.settings.sessionStore;
1875
+ }
1876
+ if (this.settings.sessionStoreFlush !== void 0) {
1877
+ opts.sessionStoreFlush = this.settings.sessionStoreFlush;
1878
+ }
1879
+ if (this.settings.loadTimeoutMs !== void 0) {
1880
+ opts.loadTimeoutMs = this.settings.loadTimeoutMs;
1881
+ }
1254
1882
  if (this.settings.includePartialMessages !== void 0) {
1255
1883
  opts.includePartialMessages = this.settings.includePartialMessages;
1256
1884
  }
@@ -1275,7 +1903,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1275
1903
  if (this.settings.hooks) {
1276
1904
  opts.hooks = this.settings.hooks;
1277
1905
  }
1278
- if (this.settings.sessionId !== void 0) {
1906
+ const effectiveForkSession = sdkOptions?.forkSession ?? this.settings.forkSession;
1907
+ const effectiveContinue = sdkOptions?.continue ?? this.settings.continue;
1908
+ if (this.settings.sessionId !== void 0 && !_ClaudeCodeLanguageModel.sessionIdConflictsWithResumeOrContinue({
1909
+ resumePresent: opts.resume !== void 0,
1910
+ continue: effectiveContinue === true,
1911
+ forkSession: effectiveForkSession === true
1912
+ })) {
1279
1913
  opts.sessionId = this.settings.sessionId;
1280
1914
  }
1281
1915
  if (this.settings.debug !== void 0) {
@@ -1291,7 +1925,17 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1291
1925
  const rest = { ...sdkOverrides };
1292
1926
  delete rest.env;
1293
1927
  delete rest.stderr;
1294
- Object.assign(opts, rest);
1928
+ for (const [key, value] of Object.entries(rest)) {
1929
+ if (value !== void 0) {
1930
+ opts[key] = value;
1931
+ }
1932
+ }
1933
+ }
1934
+ this.applySessionResolution(opts, effectiveResume);
1935
+ if (typeof opts.fallbackModel === "string" && opts.fallbackModel === opts.model) {
1936
+ throw new Error(
1937
+ `fallbackModel cannot be the same as the model ('${String(opts.model)}'). Specify a different model for fallbackModel, or remove it.`
1938
+ );
1295
1939
  }
1296
1940
  const userStderrCallback = sdkStderr ?? this.settings.stderr;
1297
1941
  if (stderrCollector || userStderrCallback) {
@@ -1300,14 +1944,27 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1300
1944
  if (userStderrCallback) userStderrCallback(data);
1301
1945
  };
1302
1946
  }
1303
- if (this.settings.env !== void 0 || sdkEnv !== void 0) {
1304
- const baseEnv = getBaseProcessEnv();
1305
- opts.env = { ...baseEnv, ...this.settings.env, ...sdkEnv };
1947
+ const mergedEnv = {
1948
+ ...getBaseProcessEnv(),
1949
+ ...this.settings.env,
1950
+ ...sdkEnv
1951
+ };
1952
+ if (!("CLAUDE_AGENT_SDK_CLIENT_APP" in mergedEnv)) {
1953
+ mergedEnv.CLAUDE_AGENT_SDK_CLIENT_APP = DEFAULT_CLIENT_APP;
1306
1954
  }
1955
+ opts.env = mergedEnv;
1307
1956
  if (responseFormat?.type === "json" && responseFormat.schema) {
1957
+ const { schema: sanitizedSchema, strippedFormatPaths } = sanitizeJsonSchemaForOutputFormat(
1958
+ responseFormat.schema
1959
+ );
1960
+ if (strippedFormatPaths.length > 0) {
1961
+ this.logger.debug(
1962
+ `[claude-code] Stripped unsupported 'format' keywords from outputFormat schema (hints folded into descriptions; client-side Zod validation still enforces them) at: ${strippedFormatPaths.join(", ")}`
1963
+ );
1964
+ }
1308
1965
  opts.outputFormat = {
1309
1966
  type: "json_schema",
1310
- schema: responseFormat.schema
1967
+ schema: sanitizedSchema
1311
1968
  };
1312
1969
  }
1313
1970
  return opts;
@@ -1316,6 +1973,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1316
1973
  if (isAbortError(error)) {
1317
1974
  throw error;
1318
1975
  }
1976
+ if (error instanceof APICallError2 || error instanceof LoadAPIKeyError2) {
1977
+ return error;
1978
+ }
1319
1979
  const isErrorWithMessage = (err) => {
1320
1980
  return typeof err === "object" && err !== null && "message" in err;
1321
1981
  };
@@ -1332,11 +1992,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1332
1992
  "claude auth login",
1333
1993
  "/login",
1334
1994
  // CLI returns "Please run /login"
1335
- "invalid api key"
1995
+ "invalid api key",
1996
+ "oauth_org_not_allowed"
1997
+ // SDK 0.3.x assistant error kind: OAuth org not permitted
1336
1998
  ];
1337
1999
  const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
2000
+ const errorKind = getStructuredErrorKind(error);
1338
2001
  const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
1339
- const isAuthError = authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
2002
+ const isAuthError = errorKind === "authentication_failed" || errorKind === "oauth_org_not_allowed" || authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
1340
2003
  if (isAuthError) {
1341
2004
  return createAuthenticationError({
1342
2005
  message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
@@ -1351,9 +2014,30 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1351
2014
  // It's controlled by the consumer via AbortSignal
1352
2015
  });
1353
2016
  }
1354
- const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1355
2017
  const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
1356
2018
  const stderr = stderrFromError || collectedStderr || void 0;
2019
+ if (errorKind === "overloaded" || errorKind === "rate_limit" || errorMessage.includes("overloaded")) {
2020
+ return createAPICallError({
2021
+ message: isErrorWithMessage(error) && error.message ? error.message : "Anthropic API is overloaded. Please retry.",
2022
+ code: errorCode || void 0,
2023
+ exitCode,
2024
+ stderr,
2025
+ promptExcerpt: messagesPrompt.substring(0, 200),
2026
+ isRetryable: true
2027
+ });
2028
+ }
2029
+ if (errorKind === "model_not_found" || errorMessage.includes("model_not_found") || errorMessage.includes("no such model")) {
2030
+ const originalMessage = isErrorWithMessage(error) && error.message ? error.message : "Model not found";
2031
+ return createAPICallError({
2032
+ 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.`,
2033
+ code: errorCode || void 0,
2034
+ exitCode,
2035
+ stderr,
2036
+ promptExcerpt: messagesPrompt.substring(0, 200),
2037
+ isRetryable: false
2038
+ });
2039
+ }
2040
+ const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1357
2041
  return createAPICallError({
1358
2042
  message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
1359
2043
  code: errorCode || void 0,
@@ -1389,6 +2073,152 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1389
2073
  }).join(", ");
1390
2074
  this.logger.warn(`[claude-code] MCP servers not connected: ${details}`);
1391
2075
  }
2076
+ /**
2077
+ * Handles SDK 0.3.x system messages other than 'init', shared by doGenerate
2078
+ * and doStream:
2079
+ * - 'api_retry' is counted into providerMetadata (`apiRetries`) and debug-logged.
2080
+ * - 'permission_denied' is warn-logged and recorded into providerMetadata
2081
+ * (`permissionDenials`); without this a denial is invisible until the
2082
+ * model talks about it.
2083
+ * - 'model_refusal_fallback' is debug-logged (the superseding assistant
2084
+ * message is handled by the text-dedup guard in the message loops).
2085
+ * - 'thinking_tokens' deltas are accumulated into providerMetadata
2086
+ * (`estimatedThinkingTokens`); the estimate is explicitly not the
2087
+ * authoritative billed output tokens, so it is surfaced as metadata
2088
+ * instead of feeding `usage.outputTokens.reasoning`.
2089
+ * - The subtypes in {@link INFORMATIONAL_SYSTEM_SUBTYPES} are intentionally
2090
+ * informational and only debug-logged.
2091
+ */
2092
+ handleSystemMessage(message, tracking, onRetractedUuids) {
2093
+ switch (message.subtype) {
2094
+ case "api_retry":
2095
+ tracking.apiRetries += 1;
2096
+ this.logger.debug(
2097
+ `[claude-code] API retry ${message.attempt}/${message.max_retries} in ${message.retry_delay_ms}ms - Status: ${message.error_status ?? "unknown"}, Error: ${message.error}`
2098
+ );
2099
+ break;
2100
+ case "permission_denied": {
2101
+ const reason = message.decision_reason ?? message.message;
2102
+ tracking.permissionDenials.push({
2103
+ toolName: message.tool_name,
2104
+ toolUseId: message.tool_use_id,
2105
+ ...reason !== void 0 && { reason }
2106
+ });
2107
+ this.logger.warn(
2108
+ `[claude-code] Permission denied - Tool: ${message.tool_name}${reason ? `, Reason: ${reason}` : ""}`
2109
+ );
2110
+ break;
2111
+ }
2112
+ case "mirror_error": {
2113
+ const mirrorError = message.error ?? "unknown error";
2114
+ const mirrorSessionId = message.key?.sessionId ?? message.session_id ?? "unknown";
2115
+ tracking.mirrorErrors.push({ error: mirrorError, sessionId: mirrorSessionId });
2116
+ this.logger.warn(
2117
+ `[claude-code] SessionStore mirror error (transcript batch dropped) - Session: ${mirrorSessionId}, Error: ${mirrorError}`
2118
+ );
2119
+ break;
2120
+ }
2121
+ case "model_refusal_fallback": {
2122
+ this.logger.debug(
2123
+ `[claude-code] Model refusal fallback - ${message.original_model} -> ${message.fallback_model} (direction: ${message.direction})`
2124
+ );
2125
+ const retractedUuids = message.retracted_message_uuids;
2126
+ if (onRetractedUuids && retractedUuids && retractedUuids.length > 0) {
2127
+ this.logger.debug(
2128
+ `[claude-code] Refusal fallback retracts ${retractedUuids.length} message uuid(s)`
2129
+ );
2130
+ onRetractedUuids(retractedUuids);
2131
+ }
2132
+ break;
2133
+ }
2134
+ case "thinking_tokens":
2135
+ tracking.estimatedThinkingTokens += message.estimated_tokens_delta;
2136
+ this.logger.debug(
2137
+ `[claude-code] Thinking tokens estimate - block total: ${message.estimated_tokens}, delta: ${message.estimated_tokens_delta}, accumulated: ${tracking.estimatedThinkingTokens}`
2138
+ );
2139
+ break;
2140
+ default:
2141
+ if (INFORMATIONAL_SYSTEM_SUBTYPES.has(message.subtype)) {
2142
+ this.logger.debug(
2143
+ `[claude-code] Ignoring informational system message: ${message.subtype}`
2144
+ );
2145
+ } else {
2146
+ this.logger.debug(`[claude-code] Unhandled system message subtype: ${message.subtype}`);
2147
+ }
2148
+ break;
2149
+ }
2150
+ }
2151
+ /**
2152
+ * Merges the result message's `permission_denials` list into the tracked
2153
+ * denials. PreToolUse-hook denies bypass canUseTool and emit no
2154
+ * `permission_denied` system event (per the SDK docs on
2155
+ * SDKPermissionDeniedMessage), so the result list is the only place they
2156
+ * surface. Entries already recorded from stream-time events are deduped by
2157
+ * `tool_use_id`.
2158
+ */
2159
+ mergeResultPermissionDenials(message, tracking) {
2160
+ for (const denial of message.permission_denials ?? []) {
2161
+ const alreadyTracked = tracking.permissionDenials.some(
2162
+ (d) => d.toolUseId !== void 0 && d.toolUseId === denial.tool_use_id
2163
+ );
2164
+ if (!alreadyTracked) {
2165
+ tracking.permissionDenials.push({
2166
+ toolName: denial.tool_name,
2167
+ toolUseId: denial.tool_use_id
2168
+ });
2169
+ }
2170
+ }
2171
+ }
2172
+ /**
2173
+ * Bounded post-result drain for the `prompt_suggestion` message
2174
+ * (promptSuggestions: true), shared by doGenerate and doStream. The
2175
+ * suggestion arrives AFTER the result message; the SDK emits at most one
2176
+ * per turn, so stop once it is delivered, and a timeout closes the
2177
+ * iterator (tearing down the subprocess) if the CLI lingers after the
2178
+ * result without emitting one. Advances the response's own generator, so
2179
+ * the caller's surrounding loop resumes to a finished iterator.
2180
+ */
2181
+ async drainPromptSuggestion(response, onPromptSuggestion) {
2182
+ const iterator = response[Symbol.asyncIterator]();
2183
+ let drainTimer;
2184
+ const drainTimeout = new Promise((resolve) => {
2185
+ drainTimer = setTimeout(
2186
+ () => resolve("timeout"),
2187
+ _ClaudeCodeLanguageModel.PROMPT_SUGGESTION_DRAIN_TIMEOUT_MS
2188
+ );
2189
+ drainTimer.unref?.();
2190
+ });
2191
+ try {
2192
+ while (true) {
2193
+ const winner = await Promise.race([iterator.next(), drainTimeout]);
2194
+ if (winner === "timeout") {
2195
+ this.logger.debug("[claude-code] Post-result drain timed out; closing SDK iterator");
2196
+ void iterator.return?.().catch(() => {
2197
+ });
2198
+ break;
2199
+ }
2200
+ if (winner.done) {
2201
+ break;
2202
+ }
2203
+ const trailingMessage = winner.value;
2204
+ this.logger.debug(`[claude-code] Post-result message type: ${trailingMessage.type}`);
2205
+ if (trailingMessage.type === "prompt_suggestion") {
2206
+ onPromptSuggestion(trailingMessage.suggestion);
2207
+ void iterator.return?.().catch(() => {
2208
+ });
2209
+ break;
2210
+ }
2211
+ }
2212
+ } catch (drainError) {
2213
+ this.logger.debug(
2214
+ `[claude-code] Error draining post-result messages: ${drainError instanceof Error ? drainError.message : String(drainError)}`
2215
+ );
2216
+ } finally {
2217
+ if (drainTimer !== void 0) {
2218
+ clearTimeout(drainTimer);
2219
+ }
2220
+ }
2221
+ }
1392
2222
  async doGenerate(options) {
1393
2223
  this.logger.debug(`[claude-code] Starting doGenerate request with model: ${this.modelId}`);
1394
2224
  this.logger.debug(`[claude-code] Response format: ${options.responseFormat?.type ?? "none"}`);
@@ -1405,9 +2235,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1405
2235
  let abortListener;
1406
2236
  if (options.abortSignal?.aborted) {
1407
2237
  abortController.abort(options.abortSignal.reason);
1408
- } else if (options.abortSignal) {
1409
- abortListener = () => abortController.abort(options.abortSignal?.reason);
1410
- options.abortSignal.addEventListener("abort", abortListener, { once: true });
1411
2238
  }
1412
2239
  let collectedStderr = "";
1413
2240
  const stderrCollector = (data) => {
@@ -1422,15 +2249,77 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1422
2249
  sdkOptions,
1423
2250
  effectiveResume
1424
2251
  );
2252
+ if (options.abortSignal && !options.abortSignal.aborted) {
2253
+ abortListener = () => abortController.abort(options.abortSignal?.reason);
2254
+ options.abortSignal.addEventListener("abort", abortListener, { once: true });
2255
+ }
1425
2256
  let text = "";
1426
- const thinkingTraces = [];
2257
+ const contentSegments = [];
2258
+ const joinTextSegments = () => contentSegments.filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
2259
+ const joinFinalTurnTextSegments = () => {
2260
+ let start = 0;
2261
+ for (let i = 0; i < contentSegments.length; i++) {
2262
+ const kind = contentSegments[i]?.kind;
2263
+ if (kind === "tool-result" || kind === "tool-error") {
2264
+ start = i + 1;
2265
+ }
2266
+ }
2267
+ return contentSegments.slice(start).filter((segment) => segment.kind === "text").map((segment) => segment.text).join("");
2268
+ };
2269
+ const knownTools = /* @__PURE__ */ new Map();
2270
+ const retractedToolIds = /* @__PURE__ */ new Set();
2271
+ const evictBuffered = (retracted) => {
2272
+ if (retracted.size === 0) return;
2273
+ const retractedToolCallIds = computeRetractedToolCallIds(
2274
+ retracted,
2275
+ contentSegments.filter(
2276
+ (segment) => segment.kind === "tool-call" || segment.kind === "tool-result" || segment.kind === "tool-error"
2277
+ ).map((segment) => ({
2278
+ toolCallId: segment.toolCallId,
2279
+ uuid: segment.uuid
2280
+ }))
2281
+ );
2282
+ for (const toolCallId of retractedToolCallIds) {
2283
+ retractedToolIds.add(toolCallId);
2284
+ knownTools.delete(toolCallId);
2285
+ activeTaskTools.delete(toolCallId);
2286
+ }
2287
+ for (let i = contentSegments.length - 1; i >= 0; i--) {
2288
+ const segment = contentSegments[i];
2289
+ if (segment === void 0) continue;
2290
+ const segmentUuid = "uuid" in segment ? segment.uuid : void 0;
2291
+ const retractsSegment = segmentUuid !== void 0 && retracted.has(segmentUuid) || (segment.kind === "tool-result" || segment.kind === "tool-error") && retractedToolCallIds.has(segment.toolCallId);
2292
+ if (retractsSegment) {
2293
+ contentSegments.splice(i, 1);
2294
+ }
2295
+ }
2296
+ };
2297
+ const activeTaskTools = /* @__PURE__ */ new Map();
2298
+ const getFallbackParentId = () => {
2299
+ if (activeTaskTools.size === 1) {
2300
+ return activeTaskTools.keys().next().value ?? null;
2301
+ }
2302
+ return null;
2303
+ };
1427
2304
  let structuredOutput;
2305
+ let receivedResultMessage = false;
1428
2306
  let usage = createEmptyUsage();
1429
2307
  let finishReason = { unified: "stop", raw: void 0 };
1430
2308
  let wasTruncated = false;
1431
2309
  let costUsd;
1432
2310
  let durationMs;
1433
2311
  let modelUsage;
2312
+ let ttftMs;
2313
+ let ttftStreamMs;
2314
+ let timeToRequestMs;
2315
+ let warmSpareClaimed;
2316
+ let terminalReason;
2317
+ const metadataTracking = {
2318
+ apiRetries: 0,
2319
+ permissionDenials: [],
2320
+ mirrorErrors: [],
2321
+ estimatedThinkingTokens: 0
2322
+ };
1434
2323
  const warnings = this.generateAllWarnings(options, messagesPrompt);
1435
2324
  if (messageWarnings) {
1436
2325
  messageWarnings.forEach((warning) => {
@@ -1476,23 +2365,195 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1476
2365
  options: queryOptions
1477
2366
  });
1478
2367
  this.settings.onQueryCreated?.(response);
1479
- for await (const message of response) {
2368
+ let lastAssistantErrorKind;
2369
+ const sdkIterator = response[Symbol.asyncIterator]();
2370
+ const detachableResponse = {
2371
+ [Symbol.asyncIterator]: () => ({
2372
+ next: () => sdkIterator.next(),
2373
+ return: () => {
2374
+ void sdkIterator.return?.().catch(() => {
2375
+ });
2376
+ return Promise.resolve({ done: true, value: void 0 });
2377
+ }
2378
+ })
2379
+ };
2380
+ for await (const message of detachableResponse) {
1480
2381
  this.logger.debug(`[claude-code] Received message type: ${message.type}`);
1481
2382
  if (message.type === "assistant") {
1482
- const { text: messageText, thinking: messageThinking } = this.extractTextAndThinking(
1483
- message.message.content
1484
- );
1485
- text += messageText;
1486
- thinkingTraces.push(...messageThinking);
2383
+ if (typeof message.error === "string") {
2384
+ lastAssistantErrorKind = message.error;
2385
+ }
2386
+ applySupersede(message, evictBuffered, this.logger, "truthy");
2387
+ const messageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
2388
+ const sdkParentToolUseId = message.parent_tool_use_id;
2389
+ const content = message.message.content;
2390
+ if (Array.isArray(content)) {
2391
+ for (const block of content) {
2392
+ if (!isContentBlock(block)) continue;
2393
+ if (block.type === "text" && typeof block.text === "string") {
2394
+ if (block.text.length > 0) {
2395
+ contentSegments.push({
2396
+ kind: "text",
2397
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2398
+ text: block.text
2399
+ });
2400
+ }
2401
+ } else if (block.type === "thinking" && typeof block.thinking === "string") {
2402
+ contentSegments.push({
2403
+ kind: "reasoning",
2404
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2405
+ text: block.thinking
2406
+ });
2407
+ } else if (block.type === "tool_use") {
2408
+ const [tool3] = this.extractToolUses([block]);
2409
+ if (!tool3) continue;
2410
+ const parentToolCallId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
2411
+ sdkParentToolUseId,
2412
+ tool3.parentToolUseId,
2413
+ getFallbackParentId
2414
+ );
2415
+ this.logger.debug(
2416
+ `[claude-code] Tool use detected - Tool: ${tool3.name}, ID: ${tool3.id}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${parentToolCallId}`
2417
+ );
2418
+ knownTools.set(tool3.id, { name: tool3.name, parentToolCallId });
2419
+ if (isSubagentToolName(tool3.name)) {
2420
+ activeTaskTools.set(tool3.id, { startTime: Date.now() });
2421
+ }
2422
+ contentSegments.push({
2423
+ kind: "tool-call",
2424
+ ...messageUuid !== void 0 && { uuid: messageUuid },
2425
+ toolCallId: tool3.id,
2426
+ part: this.buildToolCallPart(
2427
+ tool3.id,
2428
+ tool3.name,
2429
+ this.serializeToolInput(tool3.input),
2430
+ parentToolCallId
2431
+ )
2432
+ });
2433
+ }
2434
+ }
2435
+ }
2436
+ text = joinTextSegments();
2437
+ } else if (message.type === "user") {
2438
+ if (!message.message?.content) {
2439
+ this.logger.warn(
2440
+ `[claude-code] Unexpected user message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
2441
+ );
2442
+ continue;
2443
+ }
2444
+ const sdkParentToolUseIdForResults = message.parent_tool_use_id;
2445
+ const resultMessageUuid = typeof message.uuid === "string" ? message.uuid : void 0;
2446
+ const content = message.message.content;
2447
+ for (const result of this.extractToolResults(content)) {
2448
+ if (this.isRetractedToolFrame(result.id, retractedToolIds, "result")) {
2449
+ continue;
2450
+ }
2451
+ const known = knownTools.get(result.id);
2452
+ const toolName = result.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2453
+ this.logger.debug(
2454
+ `[claude-code] Tool result received - Tool: ${toolName}, ID: ${result.id}`
2455
+ );
2456
+ let parentToolCallId;
2457
+ if (known) {
2458
+ known.name = toolName;
2459
+ parentToolCallId = known.parentToolCallId;
2460
+ } else {
2461
+ this.logger.warn(
2462
+ `[claude-code] Received tool result for unknown tool ID: ${result.id}`
2463
+ );
2464
+ parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
2465
+ knownTools.set(result.id, { name: toolName, parentToolCallId });
2466
+ contentSegments.push({
2467
+ kind: "tool-call",
2468
+ toolCallId: result.id,
2469
+ part: this.buildToolCallPart(result.id, toolName, "", parentToolCallId)
2470
+ });
2471
+ }
2472
+ if (isSubagentToolName(toolName)) {
2473
+ activeTaskTools.delete(result.id);
2474
+ }
2475
+ contentSegments.push({
2476
+ kind: "tool-result",
2477
+ ...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
2478
+ toolCallId: result.id,
2479
+ part: this.buildToolResultPart(
2480
+ result.id,
2481
+ toolName,
2482
+ result.result,
2483
+ result.isError,
2484
+ parentToolCallId
2485
+ )
2486
+ });
2487
+ }
2488
+ for (const error of this.extractToolErrors(content)) {
2489
+ if (this.isRetractedToolFrame(error.id, retractedToolIds, "error")) {
2490
+ continue;
2491
+ }
2492
+ const known = knownTools.get(error.id);
2493
+ const toolName = error.name ?? known?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2494
+ this.logger.debug(
2495
+ `[claude-code] Tool error received - Tool: ${toolName}, ID: ${error.id}`
2496
+ );
2497
+ let parentToolCallId;
2498
+ if (known) {
2499
+ known.name = toolName;
2500
+ parentToolCallId = known.parentToolCallId;
2501
+ } else {
2502
+ this.logger.warn(
2503
+ `[claude-code] Received tool error for unknown tool ID: ${error.id}`
2504
+ );
2505
+ parentToolCallId = isSubagentToolName(toolName) ? null : resolveToolParentId(sdkParentToolUseIdForResults, void 0, getFallbackParentId);
2506
+ knownTools.set(error.id, { name: toolName, parentToolCallId });
2507
+ contentSegments.push({
2508
+ kind: "tool-call",
2509
+ toolCallId: error.id,
2510
+ part: this.buildToolCallPart(error.id, toolName, "", parentToolCallId)
2511
+ });
2512
+ }
2513
+ if (isSubagentToolName(toolName)) {
2514
+ activeTaskTools.delete(error.id);
2515
+ }
2516
+ contentSegments.push({
2517
+ kind: "tool-error",
2518
+ ...resultMessageUuid !== void 0 && { uuid: resultMessageUuid },
2519
+ toolCallId: error.id,
2520
+ part: this.buildToolErrorResultPart(
2521
+ error.id,
2522
+ toolName,
2523
+ error.error,
2524
+ parentToolCallId
2525
+ )
2526
+ });
2527
+ }
1487
2528
  } else if (message.type === "result") {
1488
2529
  done();
2530
+ receivedResultMessage = true;
1489
2531
  this.setSessionId(message.session_id);
1490
2532
  costUsd = message.total_cost_usd;
1491
2533
  durationMs = message.duration_ms;
1492
2534
  modelUsage = message.modelUsage;
2535
+ if ("ttft_ms" in message) {
2536
+ ttftMs = message.ttft_ms;
2537
+ }
2538
+ if ("ttft_stream_ms" in message) {
2539
+ ttftStreamMs = message.ttft_stream_ms;
2540
+ }
2541
+ if ("time_to_request_ms" in message) {
2542
+ timeToRequestMs = message.time_to_request_ms;
2543
+ }
2544
+ if ("warm_spare_claimed" in message) {
2545
+ warmSpareClaimed = message.warm_spare_claimed;
2546
+ }
2547
+ terminalReason = message.terminal_reason;
2548
+ this.mergeResultPermissionDenials(message, metadataTracking);
1493
2549
  if ("is_error" in message && message.is_error === true) {
1494
- const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
1495
- throw Object.assign(new Error(errorMessage), { exitCode: 1 });
2550
+ const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
2551
+ const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
2552
+ const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
2553
+ throw Object.assign(new Error(errorMessage), {
2554
+ exitCode: 1,
2555
+ errorKind: lastAssistantErrorKind
2556
+ });
1496
2557
  }
1497
2558
  if (message.subtype === "error_max_structured_output_retries") {
1498
2559
  throw new Error(
@@ -1515,10 +2576,21 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1515
2576
  const stopReason = "stop_reason" in message ? message.stop_reason : void 0;
1516
2577
  finishReason = mapClaudeCodeFinishReason(message.subtype, stopReason);
1517
2578
  this.logger.debug(`[claude-code] Finish reason: ${finishReason.unified}`);
2579
+ const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
2580
+ if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
2581
+ await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
2582
+ }
2583
+ break;
1518
2584
  } else if (message.type === "system" && message.subtype === "init") {
1519
2585
  this.logMcpConnectionIssues(message.mcp_servers);
1520
2586
  this.setSessionId(message.session_id);
1521
2587
  this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
2588
+ } else if (message.type === "system") {
2589
+ this.handleSystemMessage(
2590
+ message,
2591
+ metadataTracking,
2592
+ buildRetractionEvictor(evictBuffered)
2593
+ );
1522
2594
  }
1523
2595
  }
1524
2596
  } catch (error) {
@@ -1548,15 +2620,45 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1548
2620
  options.abortSignal.removeEventListener("abort", abortListener);
1549
2621
  }
1550
2622
  }
1551
- const finalText = structuredOutput !== void 0 ? JSON.stringify(structuredOutput) : text;
2623
+ if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && receivedResultMessage && structuredOutput === void 0 && !wasTruncated && finishReason.unified === "stop") {
2624
+ const recoveredJsonText = extractJsonObjectText(joinFinalTurnTextSegments()) ?? extractJsonObjectText(joinTextSegments());
2625
+ if (recoveredJsonText !== void 0) {
2626
+ this.logger.warn(
2627
+ "[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."
2628
+ );
2629
+ structuredOutput = JSON.parse(recoveredJsonText);
2630
+ } else {
2631
+ throw createAPICallError({
2632
+ message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
2633
+ promptExcerpt: messagesPrompt.substring(0, 200),
2634
+ isRetryable: false
2635
+ });
2636
+ }
2637
+ }
2638
+ const thinkingTraces = contentSegments.filter((segment) => segment.kind === "reasoning").map((segment) => segment.text);
2639
+ const contentParts = [];
2640
+ for (const segment of contentSegments) {
2641
+ if (segment.kind === "reasoning") {
2642
+ contentParts.push({ type: "reasoning", text: segment.text });
2643
+ } else if (segment.kind === "text") {
2644
+ if (structuredOutput !== void 0) continue;
2645
+ const last = contentParts[contentParts.length - 1];
2646
+ if (last !== void 0 && last.type === "text") {
2647
+ last.text += segment.text;
2648
+ } else {
2649
+ contentParts.push({ type: "text", text: segment.text });
2650
+ }
2651
+ } else {
2652
+ contentParts.push(segment.part);
2653
+ }
2654
+ }
2655
+ if (structuredOutput !== void 0) {
2656
+ contentParts.push({ type: "text", text: JSON.stringify(structuredOutput) });
2657
+ } else if (!contentParts.some((part) => part.type === "text")) {
2658
+ contentParts.push({ type: "text", text: "" });
2659
+ }
1552
2660
  return {
1553
- content: [
1554
- ...thinkingTraces.map((trace) => ({
1555
- type: "reasoning",
1556
- text: trace
1557
- })),
1558
- { type: "text", text: finalText }
1559
- ],
2661
+ content: contentParts,
1560
2662
  usage,
1561
2663
  finishReason,
1562
2664
  warnings,
@@ -1574,6 +2676,21 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1574
2676
  ...costUsd !== void 0 && { costUsd },
1575
2677
  ...durationMs !== void 0 && { durationMs },
1576
2678
  ...modelUsage !== void 0 && { modelUsage },
2679
+ ...ttftMs !== void 0 && { ttftMs },
2680
+ ...ttftStreamMs !== void 0 && { ttftStreamMs },
2681
+ ...timeToRequestMs !== void 0 && { timeToRequestMs },
2682
+ ...warmSpareClaimed !== void 0 && { warmSpareClaimed },
2683
+ ...terminalReason !== void 0 && { terminalReason },
2684
+ ...metadataTracking.apiRetries > 0 && { apiRetries: metadataTracking.apiRetries },
2685
+ ...metadataTracking.permissionDenials.length > 0 && {
2686
+ permissionDenials: metadataTracking.permissionDenials
2687
+ },
2688
+ ...metadataTracking.mirrorErrors.length > 0 && {
2689
+ mirrorErrors: metadataTracking.mirrorErrors
2690
+ },
2691
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
2692
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
2693
+ },
1577
2694
  ...wasTruncated && { truncated: true },
1578
2695
  ...thinkingTraces.length > 0 && { thinkingTraces }
1579
2696
  }
@@ -1596,9 +2713,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1596
2713
  let abortListener;
1597
2714
  if (options.abortSignal?.aborted) {
1598
2715
  abortController.abort(options.abortSignal.reason);
1599
- } else if (options.abortSignal) {
1600
- abortListener = () => abortController.abort(options.abortSignal?.reason);
1601
- options.abortSignal.addEventListener("abort", abortListener, { once: true });
1602
2716
  }
1603
2717
  let collectedStderr = "";
1604
2718
  const stderrCollector = (data) => {
@@ -1613,6 +2727,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1613
2727
  sdkOptions,
1614
2728
  effectiveResume
1615
2729
  );
2730
+ if (options.abortSignal && !options.abortSignal.aborted) {
2731
+ abortListener = () => abortController.abort(options.abortSignal?.reason);
2732
+ options.abortSignal.addEventListener("abort", abortListener, { once: true });
2733
+ }
1616
2734
  if (queryOptions.includePartialMessages === void 0) {
1617
2735
  queryOptions.includePartialMessages = true;
1618
2736
  }
@@ -1643,6 +2761,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1643
2761
  done = () => resolve(void 0);
1644
2762
  });
1645
2763
  const toolStates = /* @__PURE__ */ new Map();
2764
+ const retractedStreamToolIds = /* @__PURE__ */ new Set();
1646
2765
  const activeTaskTools = /* @__PURE__ */ new Map();
1647
2766
  const getFallbackParentId = () => {
1648
2767
  if (activeTaskTools.size === 1) {
@@ -1665,24 +2784,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1665
2784
  return;
1666
2785
  }
1667
2786
  closeToolInput(toolId, state);
1668
- controller.enqueue({
1669
- type: "tool-call",
1670
- toolCallId: toolId,
1671
- toolName: state.name,
1672
- input: state.lastSerializedInput ?? "",
1673
- providerExecuted: true,
1674
- dynamic: true,
1675
- // V3 field: indicates tool is provider-defined (not in user's tools map)
1676
- providerMetadata: {
1677
- "claude-code": {
1678
- // rawInput preserves the original serialized format before AI SDK normalization.
1679
- // Use this if you need the exact string sent to the Claude CLI, which may differ
1680
- // from the `input` field after AI SDK processing.
1681
- rawInput: state.lastSerializedInput ?? "",
1682
- parentToolCallId: state.parentToolCallId ?? null
1683
- }
1684
- }
1685
- });
2787
+ controller.enqueue(
2788
+ this.buildToolCallPart(
2789
+ toolId,
2790
+ state.name,
2791
+ state.lastSerializedInput ?? "",
2792
+ state.parentToolCallId
2793
+ )
2794
+ );
1686
2795
  state.callEmitted = true;
1687
2796
  };
1688
2797
  const finalizeToolCalls = () => {
@@ -1693,16 +2802,61 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1693
2802
  };
1694
2803
  let usage = createEmptyUsage();
1695
2804
  let accumulatedText = "";
2805
+ const textSegments = [];
1696
2806
  let textPartId;
1697
2807
  let streamedTextLength = 0;
2808
+ let emittedTextSinceLastAssistant = "";
1698
2809
  let hasReceivedStreamEvents = false;
1699
2810
  let hasStreamedJson = false;
2811
+ let lastAssistantErrorKind;
2812
+ const metadataTracking = {
2813
+ apiRetries: 0,
2814
+ permissionDenials: [],
2815
+ mirrorErrors: [],
2816
+ estimatedThinkingTokens: 0
2817
+ };
1700
2818
  const toolBlocksByIndex = /* @__PURE__ */ new Map();
1701
2819
  const toolInputAccumulators = /* @__PURE__ */ new Map();
1702
2820
  const textBlocksByIndex = /* @__PURE__ */ new Map();
1703
2821
  let textStreamedViaContentBlock = false;
1704
2822
  const reasoningBlocksByIndex = /* @__PURE__ */ new Map();
1705
2823
  let currentReasoningPartId;
2824
+ const evictLive = (retracted) => {
2825
+ if (retracted.size === 0) return;
2826
+ for (let i = textSegments.length - 1; i >= 0; i--) {
2827
+ const segmentUuid = textSegments[i]?.uuid;
2828
+ if (segmentUuid !== void 0 && retracted.has(segmentUuid)) {
2829
+ textSegments.splice(i, 1);
2830
+ }
2831
+ }
2832
+ accumulatedText = textSegments.map((segment) => segment.text).join("");
2833
+ const retractedToolCallIds = computeRetractedToolCallIds(
2834
+ retracted,
2835
+ [...toolStates].map(([toolId, state]) => ({
2836
+ toolCallId: toolId,
2837
+ uuid: state.messageUuid
2838
+ }))
2839
+ );
2840
+ for (const toolId of retractedToolCallIds) {
2841
+ const state = toolStates.get(toolId);
2842
+ if (!state) continue;
2843
+ activeTaskTools.delete(toolId);
2844
+ if (!state.callEmitted) {
2845
+ closeToolInput(toolId, state);
2846
+ toolStates.delete(toolId);
2847
+ retractedStreamToolIds.add(toolId);
2848
+ toolInputAccumulators.delete(toolId);
2849
+ for (const [blockIndex, mappedId] of toolBlocksByIndex) {
2850
+ if (mappedId === toolId) {
2851
+ toolBlocksByIndex.delete(blockIndex);
2852
+ }
2853
+ }
2854
+ this.logger.debug(
2855
+ `[claude-code] Retracted pending tool call from superseded message - ID: ${toolId}`
2856
+ );
2857
+ }
2858
+ }
2859
+ };
1706
2860
  try {
1707
2861
  controller.enqueue({ type: "stream-start", warnings });
1708
2862
  if (effectiveCanUseTool && effectivePermissionPromptToolName) {
@@ -1752,6 +2906,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1752
2906
  });
1753
2907
  accumulatedText += deltaText;
1754
2908
  streamedTextLength += deltaText.length;
2909
+ emittedTextSinceLastAssistant += deltaText;
1755
2910
  }
1756
2911
  if (event.type === "content_block_delta" && event.delta.type === "input_json_delta" && "partial_json" in event.delta && event.delta.partial_json) {
1757
2912
  const jsonDelta = event.delta.partial_json;
@@ -1794,23 +2949,33 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1794
2949
  const toolName = typeof toolBlock.name === "string" && toolBlock.name.length > 0 ? toolBlock.name : _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
1795
2950
  hasReceivedStreamEvents = true;
1796
2951
  if (textPartId) {
2952
+ const closedTextId = textPartId;
1797
2953
  controller.enqueue({
1798
2954
  type: "text-end",
1799
- id: textPartId
2955
+ id: closedTextId
1800
2956
  });
1801
2957
  textPartId = void 0;
2958
+ for (const [idx, blockTextId] of textBlocksByIndex) {
2959
+ if (blockTextId === closedTextId) {
2960
+ textBlocksByIndex.delete(idx);
2961
+ break;
2962
+ }
2963
+ }
1802
2964
  }
1803
2965
  toolBlocksByIndex.set(blockIndex, toolId);
1804
2966
  toolInputAccumulators.set(toolId, "");
1805
2967
  let state = toolStates.get(toolId);
1806
2968
  if (!state) {
1807
- const currentParentId = toolName === "Task" ? null : getFallbackParentId();
2969
+ const partialParentId = message.parent_tool_use_id;
2970
+ const currentParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(partialParentId, void 0, getFallbackParentId);
2971
+ const envelopeUuid = message.uuid;
1808
2972
  state = {
1809
2973
  name: toolName,
1810
2974
  inputStarted: false,
1811
2975
  inputClosed: false,
1812
2976
  callEmitted: false,
1813
- parentToolCallId: currentParentId
2977
+ parentToolCallId: currentParentId,
2978
+ ...typeof envelopeUuid === "string" && { messageUuid: envelopeUuid }
1814
2979
  };
1815
2980
  toolStates.set(toolId, state);
1816
2981
  }
@@ -1830,7 +2995,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1830
2995
  }
1831
2996
  }
1832
2997
  });
1833
- if (toolName === "Task") {
2998
+ if (isSubagentToolName(toolName)) {
1834
2999
  activeTaskTools.set(toolId, { startTime: Date.now() });
1835
3000
  }
1836
3001
  state.inputStarted = true;
@@ -1857,11 +3022,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1857
3022
  const blockIndex = "index" in event ? event.index : -1;
1858
3023
  hasReceivedStreamEvents = true;
1859
3024
  if (textPartId) {
3025
+ const closedTextId = textPartId;
1860
3026
  controller.enqueue({
1861
3027
  type: "text-end",
1862
- id: textPartId
3028
+ id: closedTextId
1863
3029
  });
1864
3030
  textPartId = void 0;
3031
+ for (const [idx, blockTextId] of textBlocksByIndex) {
3032
+ if (blockTextId === closedTextId) {
3033
+ textBlocksByIndex.delete(idx);
3034
+ break;
3035
+ }
3036
+ }
1865
3037
  }
1866
3038
  const reasoningPartId = generateId();
1867
3039
  reasoningBlocksByIndex.set(blockIndex, reasoningPartId);
@@ -1907,20 +3079,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1907
3079
  const effectiveInput = accumulatedInput || state.lastSerializedInput || "";
1908
3080
  state.lastSerializedInput = effectiveInput;
1909
3081
  if (!state.callEmitted) {
1910
- controller.enqueue({
1911
- type: "tool-call",
1912
- toolCallId: toolId,
1913
- toolName: state.name,
1914
- input: effectiveInput,
1915
- providerExecuted: true,
1916
- dynamic: true,
1917
- providerMetadata: {
1918
- "claude-code": {
1919
- rawInput: effectiveInput,
1920
- parentToolCallId: state.parentToolCallId ?? null
1921
- }
1922
- }
1923
- });
3082
+ controller.enqueue(
3083
+ this.buildToolCallPart(
3084
+ toolId,
3085
+ state.name,
3086
+ effectiveInput,
3087
+ state.parentToolCallId
3088
+ )
3089
+ );
1924
3090
  state.callEmitted = true;
1925
3091
  }
1926
3092
  }
@@ -1962,6 +3128,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1962
3128
  continue;
1963
3129
  }
1964
3130
  if (message.type === "assistant") {
3131
+ if (typeof message.error === "string") {
3132
+ lastAssistantErrorKind = message.error;
3133
+ }
3134
+ const supersedesPriorMessages = applySupersede(message, evictLive, this.logger);
1965
3135
  if (!message.message?.content) {
1966
3136
  this.logger.warn(
1967
3137
  `[claude-code] Unexpected assistant message structure: missing content field. Message type: ${message.type}. This may indicate an SDK protocol violation.`
@@ -1972,29 +3142,41 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1972
3142
  const content = message.message.content;
1973
3143
  const tools = this.extractToolUses(content);
1974
3144
  if (textPartId && tools.length > 0) {
3145
+ const closedTextId = textPartId;
1975
3146
  controller.enqueue({
1976
3147
  type: "text-end",
1977
- id: textPartId
3148
+ id: closedTextId
1978
3149
  });
1979
3150
  textPartId = void 0;
3151
+ for (const [idx, blockTextId] of textBlocksByIndex) {
3152
+ if (blockTextId === closedTextId) {
3153
+ textBlocksByIndex.delete(idx);
3154
+ break;
3155
+ }
3156
+ }
1980
3157
  }
1981
3158
  for (const tool3 of tools) {
1982
3159
  const toolId = tool3.id;
1983
3160
  let state = toolStates.get(toolId);
1984
3161
  if (!state) {
1985
- const currentParentId = tool3.name === "Task" ? null : sdkParentToolUseId ?? tool3.parentToolUseId ?? getFallbackParentId();
3162
+ const currentParentId = isSubagentToolName(tool3.name) ? null : resolveToolParentId(
3163
+ sdkParentToolUseId,
3164
+ tool3.parentToolUseId,
3165
+ getFallbackParentId
3166
+ );
1986
3167
  state = {
1987
3168
  name: tool3.name,
1988
3169
  inputStarted: false,
1989
3170
  inputClosed: false,
1990
3171
  callEmitted: false,
1991
- parentToolCallId: currentParentId
3172
+ parentToolCallId: currentParentId,
3173
+ ...typeof message.uuid === "string" && { messageUuid: message.uuid }
1992
3174
  };
1993
3175
  toolStates.set(toolId, state);
1994
3176
  this.logger.debug(
1995
3177
  `[claude-code] New tool use detected - Tool: ${tool3.name}, ID: ${toolId}, SDK parent: ${sdkParentToolUseId}, resolved parent: ${currentParentId}`
1996
3178
  );
1997
- } else if (!state.parentToolCallId && sdkParentToolUseId && tool3.name !== "Task") {
3179
+ } else if (!state.parentToolCallId && sdkParentToolUseId && !isSubagentToolName(tool3.name)) {
1998
3180
  state.parentToolCallId = sdkParentToolUseId;
1999
3181
  this.logger.debug(
2000
3182
  `[claude-code] Retroactive parent context - Tool: ${tool3.name}, ID: ${toolId}, parent: ${sdkParentToolUseId}`
@@ -2018,7 +3200,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2018
3200
  }
2019
3201
  }
2020
3202
  });
2021
- if (tool3.name === "Task") {
3203
+ if (isSubagentToolName(tool3.name)) {
2022
3204
  activeTaskTools.set(toolId, { startTime: Date.now() });
2023
3205
  }
2024
3206
  state.inputStarted = true;
@@ -2047,10 +3229,76 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2047
3229
  }
2048
3230
  const text = content.map((c) => c.type === "text" ? c.text : "").join("");
2049
3231
  if (text) {
2050
- if (hasReceivedStreamEvents) {
3232
+ if (supersedesPriorMessages) {
3233
+ textSegments.push({
3234
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3235
+ text
3236
+ });
3237
+ accumulatedText = textSegments.map((segment) => segment.text).join("");
3238
+ if (emittedTextSinceLastAssistant === text) {
3239
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3240
+ this.logger.debug(
3241
+ "[claude-code] Skipping text emission for superseding assistant message (replacement already streamed)"
3242
+ );
3243
+ } else if (emittedTextSinceLastAssistant.length > 0 && text.startsWith(emittedTextSinceLastAssistant)) {
3244
+ if (options.responseFormat?.type !== "json") {
3245
+ const suffix = text.slice(emittedTextSinceLastAssistant.length);
3246
+ if (suffix) {
3247
+ if (!textPartId) {
3248
+ textPartId = generateId();
3249
+ controller.enqueue({ type: "text-start", id: textPartId });
3250
+ }
3251
+ controller.enqueue({
3252
+ type: "text-delta",
3253
+ id: textPartId,
3254
+ delta: suffix
3255
+ });
3256
+ emittedTextSinceLastAssistant = text;
3257
+ }
3258
+ }
3259
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3260
+ this.logger.debug(
3261
+ "[claude-code] Emitted unstreamed suffix of superseding assistant message"
3262
+ );
3263
+ } else if (options.responseFormat?.type !== "json") {
3264
+ if (textPartId) {
3265
+ const closedTextId = textPartId;
3266
+ controller.enqueue({
3267
+ type: "text-end",
3268
+ id: closedTextId
3269
+ });
3270
+ textPartId = void 0;
3271
+ for (const [idx, blockTextId] of textBlocksByIndex) {
3272
+ if (blockTextId === closedTextId) {
3273
+ textBlocksByIndex.delete(idx);
3274
+ break;
3275
+ }
3276
+ }
3277
+ }
3278
+ textPartId = generateId();
3279
+ controller.enqueue({
3280
+ type: "text-start",
3281
+ id: textPartId
3282
+ });
3283
+ controller.enqueue({
3284
+ type: "text-delta",
3285
+ id: textPartId,
3286
+ delta: text
3287
+ });
3288
+ streamedTextLength = Math.max(streamedTextLength, text.length);
3289
+ this.logger.debug(
3290
+ "[claude-code] Emitted superseding assistant message as a new text part (canonical replacement)"
3291
+ );
3292
+ }
3293
+ } else if (hasReceivedStreamEvents) {
2051
3294
  const newTextStart = streamedTextLength;
2052
3295
  const deltaText = text.length > newTextStart ? text.slice(newTextStart) : "";
2053
3296
  accumulatedText = text;
3297
+ textSegments.length = 0;
3298
+ textSegments.push({
3299
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3300
+ text
3301
+ });
2054
3302
  if (options.responseFormat?.type !== "json" && deltaText) {
2055
3303
  if (!textPartId) {
2056
3304
  textPartId = generateId();
@@ -2068,6 +3316,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2068
3316
  streamedTextLength = text.length;
2069
3317
  } else {
2070
3318
  accumulatedText += text;
3319
+ textSegments.push({
3320
+ ...typeof message.uuid === "string" && { uuid: message.uuid },
3321
+ text
3322
+ });
2071
3323
  if (options.responseFormat?.type !== "json") {
2072
3324
  if (!textPartId) {
2073
3325
  textPartId = generateId();
@@ -2084,6 +3336,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2084
3336
  }
2085
3337
  }
2086
3338
  }
3339
+ emittedTextSinceLastAssistant = "";
2087
3340
  } else if (message.type === "user") {
2088
3341
  if (!message.message?.content) {
2089
3342
  this.logger.warn(
@@ -2104,13 +3357,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2104
3357
  break;
2105
3358
  }
2106
3359
  }
2107
- accumulatedText = "";
2108
- streamedTextLength = 0;
2109
3360
  this.logger.debug("[claude-code] Closed text part due to user message");
2110
3361
  }
3362
+ accumulatedText = "";
3363
+ textSegments.length = 0;
3364
+ streamedTextLength = 0;
3365
+ emittedTextSinceLastAssistant = "";
2111
3366
  const sdkParentToolUseIdForResults = message.parent_tool_use_id;
2112
3367
  const content = message.message.content;
2113
3368
  for (const result of this.extractToolResults(content)) {
3369
+ if (this.isRetractedToolFrame(result.id, retractedStreamToolIds, "result")) {
3370
+ continue;
3371
+ }
2114
3372
  let state = toolStates.get(result.id);
2115
3373
  const toolName = result.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2116
3374
  this.logger.debug(
@@ -2120,7 +3378,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2120
3378
  this.logger.warn(
2121
3379
  `[claude-code] Received tool result for unknown tool ID: ${result.id}`
2122
3380
  );
2123
- const resolvedParentId = toolName === "Task" ? null : sdkParentToolUseIdForResults ?? getFallbackParentId();
3381
+ const resolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
3382
+ sdkParentToolUseIdForResults,
3383
+ void 0,
3384
+ getFallbackParentId
3385
+ );
2124
3386
  state = {
2125
3387
  name: toolName,
2126
3388
  inputStarted: false,
@@ -2154,50 +3416,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2154
3416
  }
2155
3417
  }
2156
3418
  state.name = toolName;
2157
- const normalizedResult = this.normalizeToolResult(result.result);
2158
- const rawResult = typeof result.result === "string" ? result.result : (() => {
2159
- try {
2160
- return JSON.stringify(result.result);
2161
- } catch {
2162
- return String(result.result);
2163
- }
2164
- })();
2165
- const maxToolResultSize = this.settings.maxToolResultSize;
2166
- const truncatedResult = truncateToolResultForStream(
2167
- normalizedResult,
2168
- maxToolResultSize
2169
- );
2170
- const truncatedRawResult = truncateToolResultForStream(
2171
- rawResult,
2172
- maxToolResultSize
2173
- );
2174
- const rawResultTruncated = truncatedRawResult !== rawResult;
2175
3419
  emitToolCall(result.id, state);
2176
- if (toolName === "Task") {
3420
+ if (isSubagentToolName(toolName)) {
2177
3421
  activeTaskTools.delete(result.id);
2178
3422
  }
2179
- controller.enqueue({
2180
- type: "tool-result",
2181
- toolCallId: result.id,
2182
- toolName,
2183
- result: truncatedResult,
2184
- isError: result.isError,
2185
- providerExecuted: true,
2186
- dynamic: true,
2187
- // V3 field: indicates tool is provider-defined
2188
- providerMetadata: {
2189
- "claude-code": {
2190
- // rawResult preserves the original CLI output string before JSON parsing.
2191
- // Use this when you need the exact string returned by the tool, especially
2192
- // if the `result` field has been parsed/normalized and you need the original format.
2193
- rawResult: truncatedRawResult,
2194
- rawResultTruncated,
2195
- parentToolCallId: state.parentToolCallId ?? null
2196
- }
2197
- }
2198
- });
3423
+ controller.enqueue(
3424
+ this.buildToolResultPart(
3425
+ result.id,
3426
+ toolName,
3427
+ result.result,
3428
+ result.isError,
3429
+ state.parentToolCallId
3430
+ )
3431
+ );
2199
3432
  }
2200
3433
  for (const error of this.extractToolErrors(content)) {
3434
+ if (this.isRetractedToolFrame(error.id, retractedStreamToolIds, "error")) {
3435
+ continue;
3436
+ }
2201
3437
  let state = toolStates.get(error.id);
2202
3438
  const toolName = error.name ?? state?.name ?? _ClaudeCodeLanguageModel.UNKNOWN_TOOL_NAME;
2203
3439
  this.logger.debug(
@@ -2207,7 +3443,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2207
3443
  this.logger.warn(
2208
3444
  `[claude-code] Received tool error for unknown tool ID: ${error.id}`
2209
3445
  );
2210
- const errorResolvedParentId = toolName === "Task" ? null : sdkParentToolUseIdForResults ?? getFallbackParentId();
3446
+ const errorResolvedParentId = isSubagentToolName(toolName) ? null : resolveToolParentId(
3447
+ sdkParentToolUseIdForResults,
3448
+ void 0,
3449
+ getFallbackParentId
3450
+ );
2211
3451
  state = {
2212
3452
  name: toolName,
2213
3453
  inputStarted: true,
@@ -2218,37 +3458,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2218
3458
  toolStates.set(error.id, state);
2219
3459
  }
2220
3460
  emitToolCall(error.id, state);
2221
- if (toolName === "Task") {
3461
+ if (isSubagentToolName(toolName)) {
2222
3462
  activeTaskTools.delete(error.id);
2223
3463
  }
2224
- const rawError = typeof error.error === "string" ? error.error : typeof error.error === "object" && error.error !== null ? (() => {
2225
- try {
2226
- return JSON.stringify(error.error);
2227
- } catch {
2228
- return String(error.error);
2229
- }
2230
- })() : String(error.error);
2231
- controller.enqueue({
2232
- type: "tool-error",
2233
- toolCallId: error.id,
2234
- toolName,
2235
- error: rawError,
2236
- providerExecuted: true,
2237
- dynamic: true,
2238
- // V3 field: indicates tool is provider-defined
2239
- providerMetadata: {
2240
- "claude-code": {
2241
- rawError,
2242
- parentToolCallId: state.parentToolCallId ?? null
2243
- }
2244
- }
2245
- });
3464
+ controller.enqueue(
3465
+ this.buildToolErrorPart(error.id, toolName, error.error, state.parentToolCallId)
3466
+ );
2246
3467
  }
2247
3468
  } else if (message.type === "result") {
2248
3469
  done();
3470
+ this.mergeResultPermissionDenials(message, metadataTracking);
2249
3471
  if ("is_error" in message && message.is_error === true) {
2250
- const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
2251
- throw Object.assign(new Error(errorMessage), { exitCode: 1 });
3472
+ const resultText = "result" in message && typeof message.result === "string" ? message.result : void 0;
3473
+ const errorsText = "errors" in message && Array.isArray(message.errors) ? message.errors.filter((e) => typeof e === "string").join("; ") : "";
3474
+ const errorMessage = resultText ?? (errorsText || "Claude Code CLI returned an error");
3475
+ throw Object.assign(new Error(errorMessage), {
3476
+ exitCode: 1,
3477
+ errorKind: lastAssistantErrorKind
3478
+ });
2252
3479
  }
2253
3480
  if (message.subtype === "error_max_structured_output_retries") {
2254
3481
  throw new Error(
@@ -2296,6 +3523,38 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2296
3523
  type: "text-end",
2297
3524
  id: jsonTextId
2298
3525
  });
3526
+ } else if (options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0 && finishReason.unified === "stop") {
3527
+ const recoveredJsonText = extractJsonObjectText(accumulatedText);
3528
+ if (recoveredJsonText === void 0) {
3529
+ throw createAPICallError({
3530
+ message: MISSING_STRUCTURED_OUTPUT_ERROR_MESSAGE,
3531
+ promptExcerpt: messagesPrompt.substring(0, 200),
3532
+ isRetryable: false
3533
+ });
3534
+ }
3535
+ this.logger.warn(
3536
+ "[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."
3537
+ );
3538
+ if (textPartId) {
3539
+ controller.enqueue({
3540
+ type: "text-end",
3541
+ id: textPartId
3542
+ });
3543
+ }
3544
+ const recoveredTextId = generateId();
3545
+ controller.enqueue({
3546
+ type: "text-start",
3547
+ id: recoveredTextId
3548
+ });
3549
+ controller.enqueue({
3550
+ type: "text-delta",
3551
+ id: recoveredTextId,
3552
+ delta: recoveredJsonText
3553
+ });
3554
+ controller.enqueue({
3555
+ type: "text-end",
3556
+ id: recoveredTextId
3557
+ });
2299
3558
  } else if (textPartId) {
2300
3559
  controller.enqueue({
2301
3560
  type: "text-end",
@@ -2333,6 +3592,32 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2333
3592
  ...message.modelUsage !== void 0 && {
2334
3593
  modelUsage: message.modelUsage
2335
3594
  },
3595
+ // SDK 0.3.x timing metadata (ttft_* only present on SDKResultSuccess)
3596
+ ..."ttft_ms" in message && message.ttft_ms !== void 0 && { ttftMs: message.ttft_ms },
3597
+ ..."ttft_stream_ms" in message && message.ttft_stream_ms !== void 0 && {
3598
+ ttftStreamMs: message.ttft_stream_ms
3599
+ },
3600
+ ..."time_to_request_ms" in message && message.time_to_request_ms !== void 0 && {
3601
+ timeToRequestMs: message.time_to_request_ms
3602
+ },
3603
+ ..."warm_spare_claimed" in message && message.warm_spare_claimed !== void 0 && {
3604
+ warmSpareClaimed: message.warm_spare_claimed
3605
+ },
3606
+ ...message.terminal_reason !== void 0 && {
3607
+ terminalReason: message.terminal_reason
3608
+ },
3609
+ ...metadataTracking.apiRetries > 0 && {
3610
+ apiRetries: metadataTracking.apiRetries
3611
+ },
3612
+ ...metadataTracking.permissionDenials.length > 0 && {
3613
+ permissionDenials: metadataTracking.permissionDenials
3614
+ },
3615
+ ...metadataTracking.mirrorErrors.length > 0 && {
3616
+ mirrorErrors: metadataTracking.mirrorErrors
3617
+ },
3618
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
3619
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
3620
+ },
2336
3621
  // JSON validation warnings are collected during streaming and included
2337
3622
  // in providerMetadata since the AI SDK's finish event doesn't support
2338
3623
  // a top-level warnings field (unlike stream-start which was already emitted)
@@ -2343,6 +3628,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2343
3628
  }
2344
3629
  });
2345
3630
  controller.close();
3631
+ const effectivePromptSuggestions = sdkOptions?.promptSuggestions ?? this.settings.promptSuggestions;
3632
+ if (this.settings.onPromptSuggestion && effectivePromptSuggestions !== false) {
3633
+ await this.drainPromptSuggestion(response, this.settings.onPromptSuggestion);
3634
+ }
2346
3635
  return;
2347
3636
  } else if (message.type === "system" && message.subtype === "init") {
2348
3637
  this.logMcpConnectionIssues(message.mcp_servers);
@@ -2354,6 +3643,15 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2354
3643
  timestamp: /* @__PURE__ */ new Date(),
2355
3644
  modelId: this.modelId
2356
3645
  });
3646
+ } else if (message.type === "system") {
3647
+ this.handleSystemMessage(
3648
+ message,
3649
+ metadataTracking,
3650
+ buildRetractionEvictor(evictLive)
3651
+ );
3652
+ } else if (message.type === "prompt_suggestion") {
3653
+ this.logger.debug("[claude-code] Received prompt suggestion");
3654
+ this.settings.onPromptSuggestion?.(message.suggestion);
2357
3655
  }
2358
3656
  }
2359
3657
  finalizeToolCalls();
@@ -2404,6 +3702,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2404
3702
  "claude-code": {
2405
3703
  ...this.sessionId !== void 0 && { sessionId: this.sessionId },
2406
3704
  truncated: true,
3705
+ ...metadataTracking.apiRetries > 0 && {
3706
+ apiRetries: metadataTracking.apiRetries
3707
+ },
3708
+ ...metadataTracking.permissionDenials.length > 0 && {
3709
+ permissionDenials: metadataTracking.permissionDenials
3710
+ },
3711
+ ...metadataTracking.mirrorErrors.length > 0 && {
3712
+ mirrorErrors: metadataTracking.mirrorErrors
3713
+ },
3714
+ ...metadataTracking.estimatedThinkingTokens > 0 && {
3715
+ estimatedThinkingTokens: metadataTracking.estimatedThinkingTokens
3716
+ },
2407
3717
  ...streamWarnings.length > 0 && {
2408
3718
  warnings: warningsJson
2409
3719
  }
@@ -2518,11 +3828,60 @@ function createClaudeCode(options = {}) {
2518
3828
  var claudeCode = createClaudeCode();
2519
3829
 
2520
3830
  // src/index.ts
2521
- import { createSdkMcpServer as createSdkMcpServer2, tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
3831
+ import {
3832
+ createSdkMcpServer as createSdkMcpServer2,
3833
+ tool as tool2,
3834
+ SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
3835
+ InMemorySessionStore,
3836
+ HOOK_EVENTS,
3837
+ AbortError
3838
+ } from "@anthropic-ai/claude-agent-sdk";
3839
+ import {
3840
+ listSessions,
3841
+ forkSession,
3842
+ getSessionInfo,
3843
+ getSessionMessages,
3844
+ deleteSession,
3845
+ renameSession,
3846
+ tagSession,
3847
+ listSubagents,
3848
+ getSubagentMessages,
3849
+ foldSessionSummary,
3850
+ importSessionToStore
3851
+ } from "@anthropic-ai/claude-agent-sdk";
3852
+ import { startup } from "@anthropic-ai/claude-agent-sdk";
2522
3853
 
2523
3854
  // src/mcp-helpers.ts
2524
3855
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
2525
3856
  import "zod";
3857
+ function buildIsErrorResult(text) {
3858
+ return {
3859
+ isError: true,
3860
+ content: [{ type: "text", text }]
3861
+ };
3862
+ }
3863
+ async function normalizeToolResultToText(toolName, result) {
3864
+ if (result != null && typeof result[Symbol.asyncIterator] === "function") {
3865
+ let last;
3866
+ for await (const chunk of result) {
3867
+ last = chunk;
3868
+ }
3869
+ result = last;
3870
+ }
3871
+ if (typeof result === "string") {
3872
+ return { text: result };
3873
+ }
3874
+ try {
3875
+ return { text: JSON.stringify(result) ?? "undefined" };
3876
+ } catch (serializationError) {
3877
+ const reason = serializationError instanceof Error ? serializationError.message : String(serializationError);
3878
+ return {
3879
+ isError: buildIsErrorResult(
3880
+ `Tool "${toolName}" succeeded but its result could not be serialized to JSON: ${reason}`
3881
+ )
3882
+ };
3883
+ }
3884
+ }
2526
3885
  function createCustomMcpServer(config) {
2527
3886
  const defs = Object.entries(config.tools).map(
2528
3887
  ([name, def]) => tool(
@@ -2535,18 +3894,96 @@ function createCustomMcpServer(config) {
2535
3894
  );
2536
3895
  return createSdkMcpServer({ name: config.name, version: config.version, tools: defs });
2537
3896
  }
3897
+ var AI_SDK_SCHEMA_SYMBOL = Symbol.for("vercel.ai.schema");
3898
+ function isAiSdkJsonSchema(schema) {
3899
+ return typeof schema === "object" && schema !== null && AI_SDK_SCHEMA_SYMBOL in schema;
3900
+ }
3901
+ function isZodObjectSchema(schema) {
3902
+ if (typeof schema !== "object" || schema === null) {
3903
+ return false;
3904
+ }
3905
+ const candidate = schema;
3906
+ if (!("_zod" in candidate) && !("_def" in candidate)) {
3907
+ return false;
3908
+ }
3909
+ const typeTag = candidate._zod?.def?.type ?? candidate._def?.typeName;
3910
+ if (typeTag !== "object" && typeTag !== "ZodObject") {
3911
+ return false;
3912
+ }
3913
+ return typeof candidate.shape === "object" && candidate.shape !== null;
3914
+ }
3915
+ function createAiSdkMcpServer(name, tools) {
3916
+ const defs = Object.entries(tools).map(([toolName, def]) => {
3917
+ const execute = def.execute;
3918
+ if (typeof execute !== "function") {
3919
+ throw new Error(
3920
+ `createAiSdkMcpServer: tool "${toolName}" has no execute function. Only tools that execute locally can be bridged to the Claude Code CLI.`
3921
+ );
3922
+ }
3923
+ if (isAiSdkJsonSchema(def.inputSchema)) {
3924
+ throw new Error(
3925
+ `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.`
3926
+ );
3927
+ }
3928
+ if (!isZodObjectSchema(def.inputSchema)) {
3929
+ throw new Error(
3930
+ `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.`
3931
+ );
3932
+ }
3933
+ const zodSchema = def.inputSchema;
3934
+ return tool(
3935
+ toolName,
3936
+ def.description ?? "",
3937
+ zodSchema.shape,
3938
+ async (args, extra) => {
3939
+ try {
3940
+ const extraInfo = extra ?? {};
3941
+ const result = await execute.call(def, args, {
3942
+ toolCallId: extraInfo.requestId !== void 0 ? String(extraInfo.requestId) : void 0,
3943
+ abortSignal: extraInfo.signal
3944
+ });
3945
+ const normalized = await normalizeToolResultToText(toolName, result);
3946
+ if ("isError" in normalized) {
3947
+ return normalized.isError;
3948
+ }
3949
+ return { content: [{ type: "text", text: normalized.text }] };
3950
+ } catch (error) {
3951
+ return buildIsErrorResult(error instanceof Error ? error.message : String(error));
3952
+ }
3953
+ }
3954
+ );
3955
+ });
3956
+ return createSdkMcpServer({ name, tools: defs });
3957
+ }
2538
3958
  export {
3959
+ AbortError,
2539
3960
  ClaudeCodeLanguageModel,
3961
+ HOOK_EVENTS,
3962
+ InMemorySessionStore,
3963
+ SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
2540
3964
  claudeCode,
2541
3965
  createAPICallError,
3966
+ createAiSdkMcpServer,
2542
3967
  createAuthenticationError,
2543
3968
  createClaudeCode,
2544
3969
  createCustomMcpServer,
2545
3970
  createSdkMcpServer2 as createSdkMcpServer,
2546
3971
  createTimeoutError,
3972
+ deleteSession,
3973
+ foldSessionSummary,
3974
+ forkSession,
2547
3975
  getErrorMetadata,
3976
+ getSessionInfo,
3977
+ getSessionMessages,
3978
+ getSubagentMessages,
3979
+ importSessionToStore,
2548
3980
  isAuthenticationError,
2549
3981
  isTimeoutError,
3982
+ listSessions,
3983
+ listSubagents,
3984
+ renameSession,
3985
+ startup,
3986
+ tagSession,
2550
3987
  tool2 as tool
2551
3988
  };
2552
3989
  //# sourceMappingURL=index.js.map