UNPKG

ai-sdk-provider-codex-cli

Version:

AI SDK v7 provider for OpenAI Codex CLI (use ChatGPT Plus/Pro subscription)

6,492 lines • 224 kB
import { NoSuchModelError, LoadAPIKeyError, APICallError, UnsupportedFunctionalityError } from '@ai-sdk/provider';
import { spawn } from 'child_process';
import { randomUUID, randomBytes } from 'crypto';
import { createRequire } from 'module';
import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'fs';
import { tmpdir } from 'os';
import path, { join, dirname } from 'path';
import { z } from 'zod';
import { generateId, parseProviderOptions, getTopLevelMediaType, isFullMediaType, detectMediaType } from '@ai-sdk/provider-utils';
import { createServer } from 'http';
import { EventEmitter } from 'events';
import readline from 'readline';

// src/exec-provider.ts

// src/logger.ts
var defaultLogger = {
  debug: (message) => console.debug(`[DEBUG] ${message}`),
  info: (message) => console.info(`[INFO] ${message}`),
  warn: (message) => console.warn(`[WARN] ${message}`),
  error: (message) => console.error(`[ERROR] ${message}`)
};
var noopLogger = {
  debug: () => {
  },
  info: () => {
  },
  warn: () => {
  },
  error: () => {
  }
};
function getLogger(logger) {
  if (logger === false) {
    return noopLogger;
  }
  if (logger === void 0) {
    return defaultLogger;
  }
  return logger;
}
function createVerboseLogger(logger, verbose = false) {
  if (verbose) {
    return logger;
  }
  return {
    debug: () => {
    },
    // No-op when not verbose
    info: () => {
    },
    // No-op when not verbose
    warn: logger.warn.bind(logger),
    error: logger.error.bind(logger)
  };
}

// src/config-key-utils.ts
var MCP_SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
var CONFIG_OVERRIDE_KEY_PATTERN = /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$/;
function isValidMcpServerName(name) {
  return MCP_SERVER_NAME_PATTERN.test(name);
}
function isValidConfigOverrideKey(key) {
  return CONFIG_OVERRIDE_KEY_PATTERN.test(key);
}
function assertValidMcpServerName(name) {
  if (!isValidMcpServerName(name)) {
    throw new Error(`Invalid MCP server name '${name}'. Names must match /^[A-Za-z0-9_-]+$/.`);
  }
  return name;
}
function assertValidConfigOverrideKey(key) {
  if (!isValidConfigOverrideKey(key)) {
    throw new Error(
      `Invalid config override key '${key}'. Keys must match /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$/.`
    );
  }
  return key;
}

// src/shared-utils.ts
function createEmptyCodexUsage() {
  return {
    inputTokens: {
      total: void 0,
      noCache: void 0,
      cacheRead: void 0,
      cacheWrite: void 0
    },
    outputTokens: {
      total: void 0,
      text: void 0,
      reasoning: void 0
    },
    raw: void 0
  };
}
function addOptionalTokens(a, b) {
  if (a === void 0 && b === void 0) return void 0;
  return (a ?? 0) + (b ?? 0);
}
function isRawUsageObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function addRawUsage(a, b) {
  if (!isRawUsageObject(a)) return b;
  if (!isRawUsageObject(b)) return a;
  const sum = { ...a };
  for (const [key, value] of Object.entries(b)) {
    const previous = sum[key];
    sum[key] = typeof value === "number" && typeof previous === "number" ? previous + value : value;
  }
  return sum;
}
function addCodexUsage(a, b) {
  return {
    inputTokens: {
      total: addOptionalTokens(a.inputTokens.total, b.inputTokens.total),
      noCache: addOptionalTokens(a.inputTokens.noCache, b.inputTokens.noCache),
      cacheRead: addOptionalTokens(a.inputTokens.cacheRead, b.inputTokens.cacheRead),
      cacheWrite: addOptionalTokens(a.inputTokens.cacheWrite, b.inputTokens.cacheWrite)
    },
    outputTokens: {
      total: addOptionalTokens(a.outputTokens.total, b.outputTokens.total),
      text: addOptionalTokens(a.outputTokens.text, b.outputTokens.text),
      reasoning: addOptionalTokens(a.outputTokens.reasoning, b.outputTokens.reasoning)
    },
    raw: addRawUsage(a.raw, b.raw)
  };
}
function mapCodexCliFinishReason(reason) {
  switch (reason) {
    case "stop":
    case "end_turn":
    case void 0:
      return { unified: "stop", raw: reason };
    case "length":
    case "max_tokens":
      return { unified: "length", raw: reason };
    case "content_filter":
      return { unified: "content-filter", raw: reason };
    case "tool_calls":
      return { unified: "tool-calls", raw: reason };
    case "error":
      return { unified: "error", raw: reason };
    default:
      return { unified: "other", raw: reason };
  }
}
var DEPRECATED_APPROVAL_POLICY_ALIASES = Object.freeze({
  "on-failure": "on-request"
});
function normalizeApprovalPolicyAlias(value) {
  if (typeof value !== "string") return value;
  const normalized = DEPRECATED_APPROVAL_POLICY_ALIASES[value];
  return normalized === void 0 ? value : normalized;
}
function isDeprecatedApprovalPolicyAlias(value) {
  return typeof value === "string" && Object.hasOwn(DEPRECATED_APPROVAL_POLICY_ALIASES, value);
}
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
}
function sanitizeJsonSchema(value) {
  if (typeof value !== "object" || value === null) {
    return value;
  }
  if (Array.isArray(value)) {
    return value.map((item) => sanitizeJsonSchema(item));
  }
  const obj = value;
  const result = {};
  for (const [key, val] of Object.entries(obj)) {
    if (key === "properties" && typeof val === "object" && val !== null && !Array.isArray(val)) {
      const props = val;
      const sanitizedProps = {};
      for (const [propName, propSchema] of Object.entries(props)) {
        sanitizedProps[propName] = sanitizeJsonSchema(propSchema);
      }
      result[key] = sanitizedProps;
      continue;
    }
    if (key === "$schema" || key === "$id" || key === "$ref" || key === "$defs" || key === "definitions" || key === "title" || key === "examples" || key === "default" || key === "format" || key === "pattern") {
      continue;
    }
    result[key] = sanitizeJsonSchema(val);
  }
  return result;
}
function safeStringify(value) {
  if (typeof value === "string") return value;
  try {
    return JSON.stringify(value);
  } catch {
    return String(value);
  }
}
function mergeStringRecord(base, override) {
  if (override !== void 0) {
    if (Object.keys(override).length === 0) return {};
    return { ...base ?? {}, ...override };
  }
  if (base) return { ...base };
  return void 0;
}
function omitAuthorizationHeader(headers) {
  if (headers === void 0) return void 0;
  return Object.fromEntries(
    Object.entries(headers).filter(([key]) => key.toLowerCase() !== "authorization")
  );
}
function mcpHttpHeadersWithBearerToken(server) {
  const headers = server.httpHeaders ? { ...server.httpHeaders } : void 0;
  if (server.bearerToken === void 0) {
    return headers;
  }
  const hasAuthorizationHeader = Object.keys(headers ?? {}).some(
    (key) => key.toLowerCase() === "authorization"
  );
  return {
    ...headers ?? {},
    ...hasAuthorizationHeader ? {} : { Authorization: `Bearer ${server.bearerToken}` }
  };
}
function mergeSingleMcpServer(existing, incoming) {
  if (!existing || existing.transport !== incoming.transport) {
    return { ...incoming };
  }
  if (incoming.transport === "stdio") {
    const baseStdio = existing;
    const result2 = {
      transport: "stdio",
      command: incoming.command,
      args: incoming.args ?? baseStdio.args,
      env: mergeStringRecord(baseStdio.env, incoming.env),
      cwd: incoming.cwd ?? baseStdio.cwd,
      enabled: incoming.enabled ?? existing.enabled,
      startupTimeoutSec: incoming.startupTimeoutSec ?? existing.startupTimeoutSec,
      toolTimeoutSec: incoming.toolTimeoutSec ?? existing.toolTimeoutSec,
      enabledTools: incoming.enabledTools ?? existing.enabledTools,
      disabledTools: incoming.disabledTools ?? existing.disabledTools
    };
    return result2;
  }
  const baseHttp = existing;
  const hasIncomingAuth = incoming.bearerToken !== void 0 || incoming.bearerTokenEnvVar !== void 0;
  const bearerToken = hasIncomingAuth ? incoming.bearerToken : baseHttp.bearerToken;
  const bearerTokenEnvVar = hasIncomingAuth ? incoming.bearerTokenEnvVar : baseHttp.bearerTokenEnvVar;
  const baseHttpHeaders = hasIncomingAuth ? omitAuthorizationHeader(baseHttp.httpHeaders) : baseHttp.httpHeaders;
  const result = {
    transport: "http",
    url: incoming.url,
    bearerToken,
    bearerTokenEnvVar,
    httpHeaders: mergeStringRecord(baseHttpHeaders, incoming.httpHeaders),
    envHttpHeaders: mergeStringRecord(baseHttp.envHttpHeaders, incoming.envHttpHeaders),
    enabled: incoming.enabled ?? existing.enabled,
    startupTimeoutSec: incoming.startupTimeoutSec ?? existing.startupTimeoutSec,
    toolTimeoutSec: incoming.toolTimeoutSec ?? existing.toolTimeoutSec,
    enabledTools: incoming.enabledTools ?? existing.enabledTools,
    disabledTools: incoming.disabledTools ?? existing.disabledTools
  };
  return result;
}
function mergeMcpServers(base, override) {
  if (!base && !override) return void 0;
  const normalizedBase = {};
  for (const [rawName, server] of Object.entries(base ?? {})) {
    const name = assertValidMcpServerName(rawName);
    normalizedBase[name] = server;
  }
  if (!override) return normalizedBase;
  const merged = { ...normalizedBase };
  for (const [rawName, incoming] of Object.entries(override)) {
    const name = assertValidMcpServerName(rawName);
    const existing = merged[name];
    merged[name] = mergeSingleMcpServer(existing, incoming);
  }
  return merged;
}
function mapUnsupportedSettingsWarnings(options) {
  const unsupported = [];
  const add = (setting, name) => {
    if (setting !== void 0) {
      unsupported.push({
        type: "unsupported",
        feature: name,
        details: `Codex CLI does not support ${name}; it will be ignored.`
      });
    }
  };
  add(options.temperature, "temperature");
  add(options.topP, "topP");
  add(options.topK, "topK");
  add(options.maxOutputTokens, "maxOutputTokens");
  add(options.presencePenalty, "presencePenalty");
  add(options.frequencyPenalty, "frequencyPenalty");
  add(options.stopSequences?.length ? options.stopSequences : void 0, "stopSequences");
  add(options.seed, "seed");
  add(options.tools, "tools");
  const toolChoice = options.toolChoice;
  add(
    toolChoice !== void 0 && toolChoice.type === "auto" ? void 0 : toolChoice,
    "toolChoice"
  );
  return unsupported;
}
function mcpServersToConfigOverrides(mcpServers, rmcpClient) {
  const overrides = {};
  const setOverride = (key, value) => {
    assertValidConfigOverrideKey(key);
    overrides[key] = value;
  };
  if (rmcpClient !== void 0) {
    setOverride("features.rmcp_client", rmcpClient);
  }
  if (!mcpServers) {
    return overrides;
  }
  for (const [rawName, server] of Object.entries(mcpServers)) {
    const name = assertValidMcpServerName(rawName);
    const prefix = `mcp_servers.${name}`;
    if (server.enabled !== void 0) {
      setOverride(`${prefix}.enabled`, server.enabled);
    }
    if (server.startupTimeoutSec !== void 0) {
      setOverride(`${prefix}.startup_timeout_sec`, server.startupTimeoutSec);
    }
    if (server.toolTimeoutSec !== void 0) {
      setOverride(`${prefix}.tool_timeout_sec`, server.toolTimeoutSec);
    }
    if (server.enabledTools !== void 0) {
      setOverride(`${prefix}.enabled_tools`, server.enabledTools);
    }
    if (server.disabledTools !== void 0) {
      setOverride(`${prefix}.disabled_tools`, server.disabledTools);
    }
    if (server.transport === "stdio") {
      setOverride(`${prefix}.command`, server.command);
      if (server.args !== void 0) setOverride(`${prefix}.args`, server.args);
      if (server.env !== void 0) setOverride(`${prefix}.env`, server.env);
      if (server.cwd) setOverride(`${prefix}.cwd`, server.cwd);
    } else {
      setOverride(`${prefix}.url`, server.url);
      if (server.bearerTokenEnvVar !== void 0) {
        setOverride(`${prefix}.bearer_token_env_var`, server.bearerTokenEnvVar);
      }
      const httpHeaders = mcpHttpHeadersWithBearerToken(server);
      if (httpHeaders !== void 0) setOverride(`${prefix}.http_headers`, httpHeaders);
      if (server.envHttpHeaders !== void 0) {
        setOverride(`${prefix}.env_http_headers`, server.envHttpHeaders);
      }
    }
  }
  return overrides;
}

// src/types-shared.ts
var CODEX_REASONING_EFFORTS = [
  "none",
  "minimal",
  "low",
  "medium",
  "high",
  "xhigh",
  "max",
  "ultra"
];
var CODEX_SERVICE_TIERS = [
  "fast",
  "ultrafast",
  "default"
];

// src/validation.ts
var loggerFunctionSchema = z.object({
  debug: z.any().refine((val) => typeof val === "function", {
    message: "debug must be a function"
  }),
  info: z.any().refine((val) => typeof val === "function", {
    message: "info must be a function"
  }),
  warn: z.any().refine((val) => typeof val === "function", {
    message: "warn must be a function"
  }),
  error: z.any().refine((val) => typeof val === "function", {
    message: "error must be a function"
  })
});
var mcpServerBaseSchema = z.object({
  enabled: z.boolean().optional(),
  startupTimeoutSec: z.number().int().positive().optional(),
  toolTimeoutSec: z.number().int().positive().optional(),
  enabledTools: z.array(z.string()).optional(),
  disabledTools: z.array(z.string()).optional()
});
var mcpServerStdioSchema = mcpServerBaseSchema.extend({
  transport: z.literal("stdio"),
  command: z.string().min(1),
  args: z.array(z.string()).optional(),
  env: z.record(z.string(), z.string()).optional(),
  cwd: z.string().optional()
});
var mcpServerHttpSchema = mcpServerBaseSchema.extend({
  transport: z.literal("http"),
  url: z.string().min(1),
  bearerToken: z.string().optional(),
  bearerTokenEnvVar: z.string().optional(),
  httpHeaders: z.record(z.string(), z.string()).optional(),
  envHttpHeaders: z.record(z.string(), z.string()).optional()
});
var mcpServerSchema = z.discriminatedUnion("transport", [
  mcpServerStdioSchema,
  mcpServerHttpSchema
]);
var mcpServerNameSchema = z.string().min(1).refine((value) => isValidMcpServerName(value), {
  message: "MCP server names must match /^[A-Za-z0-9_-]+$/."
});
var mcpServersSchema = z.record(mcpServerNameSchema, mcpServerSchema);
var sdkMcpServerSchema = z.object({
  name: mcpServerNameSchema,
  _start: z.any().refine((val) => typeof val === "function", {
    message: "_start must be a function"
  }),
  _stop: z.any().refine((val) => typeof val === "function", {
    message: "_stop must be a function"
  })
}).passthrough();
var appServerMcpServersSchema = z.record(
  mcpServerNameSchema,
  z.union([mcpServerSchema, sdkMcpServerSchema])
);
var configOverrideKeySchema = z.string().min(1).refine((value) => isValidConfigOverrideKey(value), {
  message: "configOverrides keys must match /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$/."
});
var reasoningEffortSchema = z.enum(CODEX_REASONING_EFFORTS);
var serviceTierSchema = z.string().min(1);
var configOverridesSchema = z.record(
  configOverrideKeySchema,
  z.union([z.string(), z.number(), z.boolean(), z.object({}).passthrough(), z.array(z.any())])
).optional();
var sharedSettingsSchema = z.object({
  cwd: z.string().optional(),
  approvalMode: z.enum(["untrusted", "on-failure", "on-request", "never"]).optional(),
  sandboxMode: z.enum(["read-only", "workspace-write", "danger-full-access"]).optional(),
  env: z.record(z.string(), z.string()).optional(),
  verbose: z.boolean().optional(),
  logger: z.union([z.literal(false), loggerFunctionSchema]).optional(),
  reasoningEffort: reasoningEffortSchema.optional(),
  reasoningSummary: z.enum(["auto", "detailed"]).optional(),
  reasoningSummaryFormat: z.enum(["none", "experimental"]).optional(),
  modelVerbosity: z.enum(["low", "medium", "high"]).optional(),
  serviceTier: serviceTierSchema.optional(),
  mcpServers: mcpServersSchema.optional(),
  rmcpClient: z.boolean().optional(),
  configOverrides: configOverridesSchema
}).strict();
var execSettingsSchema = sharedSettingsSchema.extend({
  codexPath: z.string().optional(),
  addDirs: z.array(z.string().min(1)).optional(),
  fullAuto: z.boolean().optional(),
  dangerouslyBypassApprovalsAndSandbox: z.boolean().optional(),
  skipGitRepoCheck: z.boolean().optional(),
  color: z.enum(["always", "never", "auto"]).optional(),
  allowNpx: z.boolean().optional(),
  outputLastMessageFile: z.string().optional(),
  profile: z.string().optional(),
  oss: z.boolean().optional(),
  webSearch: z.boolean().optional()
}).strict();
var approvalRejectSchema = z.object({
  reject: z.object({
    sandbox_approval: z.boolean(),
    rules: z.boolean(),
    mcp_elicitations: z.boolean()
  })
});
var approvalGranularSchema = z.object({
  granular: z.object({
    sandbox_approval: z.boolean(),
    rules: z.boolean(),
    mcp_elicitations: z.boolean(),
    skill_approval: z.boolean().optional(),
    request_permissions: z.boolean().optional()
  })
});
var appServerApprovalPolicySchema = z.union([
  z.enum(["untrusted", "on-failure", "on-request", "never"]),
  approvalGranularSchema,
  approvalRejectSchema
]);
var sandboxPolicySchema = z.union([
  z.enum(["read-only", "workspace-write", "danger-full-access"]),
  z.object({
    type: z.string()
  }).passthrough()
]);
var serverRequestsSchema = z.object({
  onCommandExecutionApproval: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onCommandExecutionApproval must be a function"
  }).optional(),
  onFileChangeApproval: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onFileChangeApproval must be a function"
  }).optional(),
  onSkillApproval: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onSkillApproval must be a function"
  }).optional(),
  onMcpElicitation: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onMcpElicitation must be a function"
  }).optional(),
  onToolRequestUserInput: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onToolRequestUserInput must be a function"
  }).optional(),
  onDynamicToolCall: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onDynamicToolCall must be a function"
  }).optional(),
  onAuthRefresh: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onAuthRefresh must be a function"
  }).optional(),
  onUnhandled: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onUnhandled must be a function"
  }).optional()
}).strict().optional();
var appServerSettingsSchema = z.object({
  codexPath: z.string().optional(),
  cwd: z.string().optional(),
  env: z.record(z.string(), z.string()).optional(),
  verbose: z.boolean().optional(),
  logger: z.union([z.literal(false), loggerFunctionSchema]).optional(),
  personality: z.enum(["none", "friendly", "pragmatic"]).optional(),
  effort: reasoningEffortSchema.optional(),
  summary: z.enum(["auto", "concise", "detailed", "none"]).optional(),
  serviceTier: serviceTierSchema.optional(),
  approvalPolicy: appServerApprovalPolicySchema.optional(),
  sandboxPolicy: sandboxPolicySchema.optional(),
  baseInstructions: z.string().optional(),
  developerInstructions: z.string().optional(),
  mcpServers: appServerMcpServersSchema.optional(),
  rmcpClient: z.boolean().optional(),
  configOverrides: configOverridesSchema,
  autoApprove: z.boolean().optional(),
  persistExtendedHistory: z.boolean().optional(),
  connectionTimeoutMs: z.number().int().positive().optional(),
  requestTimeoutMs: z.number().int().positive().optional(),
  idleTimeoutMs: z.number().int().positive().optional(),
  minCodexVersion: z.string().regex(/^\d+\.\d+\.\d+(-[A-Za-z0-9.-]+)?$/, "minCodexVersion must be a semver string").optional(),
  threadMode: z.enum(["stateless", "persistent"]).optional(),
  resume: z.string().optional(),
  includeRawChunks: z.boolean().optional(),
  serverRequests: serverRequestsSchema,
  onSessionCreated: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onSessionCreated must be a function"
  }).optional()
}).strict();
var appServerProviderOptionsSchema = z.object({
  threadId: z.string().optional(),
  resume: z.string().optional(),
  threadMode: z.enum(["stateless", "persistent"]).optional(),
  includeRawChunks: z.boolean().optional(),
  personality: z.enum(["none", "friendly", "pragmatic"]).optional(),
  effort: reasoningEffortSchema.optional(),
  summary: z.enum(["auto", "concise", "detailed", "none"]).optional(),
  serviceTier: serviceTierSchema.optional(),
  approvalPolicy: appServerApprovalPolicySchema.optional(),
  sandboxPolicy: sandboxPolicySchema.optional(),
  baseInstructions: z.string().optional(),
  developerInstructions: z.string().optional(),
  mcpServers: appServerMcpServersSchema.optional(),
  rmcpClient: z.boolean().optional(),
  configOverrides: configOverridesSchema,
  autoApprove: z.boolean().optional(),
  persistExtendedHistory: z.boolean().optional(),
  serverRequests: serverRequestsSchema,
  onSessionCreated: z.any().refine((val) => val === void 0 || typeof val === "function", {
    message: "onSessionCreated must be a function"
  }).optional()
}).strict();
function parseValidationIssues(error) {
  let issues = [];
  if (error && typeof error === "object") {
    const v4 = error.issues;
    const v3 = error.errors;
    if (Array.isArray(v4)) issues = v4;
    else if (Array.isArray(v3)) issues = v3;
  }
  return issues.map((i) => {
    const path2 = Array.isArray(i?.path) ? i.path.join(".") : "";
    const message = i?.message || "Invalid value";
    return `${path2 ? path2 + ": " : ""}${message}`;
  });
}
function makeValidationResult(parsed, warnings) {
  if (!parsed.success) {
    return {
      valid: false,
      warnings,
      errors: parseValidationIssues(parsed.error)
    };
  }
  return { valid: true, warnings, errors: [] };
}
function validateExecSettings(settings) {
  const warnings = [];
  const parsed = execSettingsSchema.safeParse(settings);
  if (!parsed.success) return makeValidationResult(parsed, warnings);
  const s = parsed.data;
  if (s.fullAuto && s.dangerouslyBypassApprovalsAndSandbox) {
    warnings.push(
      "Both fullAuto and dangerouslyBypassApprovalsAndSandbox specified; fullAuto takes precedence."
    );
  }
  if (s.fullAuto && s.sandboxMode !== void 0 && s.sandboxMode !== "workspace-write") {
    warnings.push(
      `fullAuto is deprecated (Codex CLI 0.147 removed --full-auto); its sandbox mapping is ignored because sandboxMode '${s.sandboxMode}' is set explicitly (its approvalMode 'never' default and its precedence over dangerouslyBypassApprovalsAndSandbox still apply).`
    );
  } else if (s.fullAuto) {
    warnings.push(
      "fullAuto is deprecated (Codex CLI 0.147 removed --full-auto); it now defaults sandboxMode to 'workspace-write' and approvalMode to 'never' unless those are set explicitly."
    );
  }
  if (isDeprecatedApprovalPolicyAlias(s.approvalMode)) {
    warnings.push(
      `approvalMode '${s.approvalMode}' is deprecated (retired by Codex CLI 0.143); it is sent as 'on-request'.`
    );
  }
  return { valid: true, warnings, errors: [] };
}
function validateAppServerSettings(settings) {
  const warnings = [];
  const parsed = appServerSettingsSchema.safeParse(settings);
  if (!parsed.success) {
    return {
      valid: false,
      warnings,
      errors: parseValidationIssues(parsed.error)
    };
  }
  const s = parsed.data;
  if (s.autoApprove && s.approvalPolicy !== void 0) {
    warnings.push("autoApprove overrides approvalPolicy for server-initiated approval requests.");
  }
  if (isDeprecatedApprovalPolicyAlias(s.approvalPolicy)) {
    warnings.push(
      `approvalPolicy '${s.approvalPolicy}' is deprecated (retired by Codex CLI 0.143 and rejected by app-server >= 0.144); it is sent as 'on-request'.`
    );
  } else if (typeof s.approvalPolicy === "object" && s.approvalPolicy !== null && "reject" in s.approvalPolicy) {
    warnings.push(
      "approvalPolicy { reject } is deprecated (removed from the Codex app-server protocol); it is translated to the equivalent { granular } policy."
    );
  }
  if (s.threadMode === "persistent" && s.resume) {
    warnings.push(
      "threadMode=persistent ignores resume when an explicit thread is already active."
    );
  }
  return { valid: true, warnings, errors: [] };
}
function validateModelId(modelId) {
  if (!modelId || modelId.trim() === "") return "Model ID cannot be empty";
  return void 0;
}
function isTaggedFileData(value) {
  if (typeof value !== "object" || value === null || !("type" in value)) return false;
  return value.type === "data" || value.type === "url" || value.type === "reference" || value.type === "text";
}
function resolveImageMimeType(declaredType, data) {
  if (isFullMediaType(declaredType)) return declaredType;
  const bytes = typeof data === "string" || data instanceof Uint8Array ? data : data instanceof ArrayBuffer ? new Uint8Array(data) : void 0;
  if (bytes !== void 0) {
    const detected = detectMediaType({
      data: bytes,
      topLevelType: getTopLevelMediaType(declaredType)
    });
    if (detected) return detected;
  }
  return "image/png";
}
function extractImageData(part) {
  if (typeof part !== "object" || part === null) return null;
  const p = part;
  const isFilePart2 = p.type === "file";
  const declaredType = isFilePart2 ? p.mediaType || "image/png" : p.mimeType || "image/png";
  if (isFilePart2 && getTopLevelMediaType(declaredType).toLowerCase() !== "image") {
    return null;
  }
  let primaryInput = isFilePart2 ? p.data : p.image;
  if (isTaggedFileData(primaryInput)) {
    if (primaryInput.type === "data") {
      primaryInput = primaryInput.data;
    } else if (primaryInput.type === "url") {
      primaryInput = primaryInput.url;
    } else {
      return null;
    }
  }
  const mimeType = resolveImageMimeType(declaredType, primaryInput);
  if (typeof primaryInput === "string") {
    return extractFromString(primaryInput, mimeType);
  }
  if (typeof primaryInput === "object" && primaryInput !== null && primaryInput instanceof URL) {
    if (primaryInput.protocol === "data:") {
      const dataUrlStr = primaryInput.toString();
      return extractFromString(dataUrlStr, mimeType);
    }
    return null;
  }
  if (Buffer.isBuffer(primaryInput)) {
    const base64 = primaryInput.toString("base64");
    return { data: `data:${mimeType};base64,${base64}`, mimeType };
  }
  if (isBinaryInput(primaryInput)) {
    const buffer = Buffer.from(primaryInput);
    const base64 = buffer.toString("base64");
    return { data: `data:${mimeType};base64,${base64}`, mimeType };
  }
  if (isFilePart2 && typeof p.url === "string") {
    return extractFromString(p.url, mimeType);
  }
  if (!isFilePart2 && typeof p.data === "string") {
    return extractFromString(p.data, mimeType);
  }
  if (!isFilePart2 && typeof p.url === "string") {
    return extractFromString(p.url, mimeType);
  }
  return null;
}
function extractFromString(value, fallbackMimeType) {
  const trimmed = value.trim();
  if (/^file:\/\//i.test(trimmed)) {
    return null;
  }
  if (/^https?:\/\//i.test(trimmed)) {
    return null;
  }
  if (trimmed.startsWith("data:")) {
    const match = trimmed.match(/^data:([^;,]+);base64,([^,]+)$/);
    if (!match) {
      return null;
    }
    const payload2 = normalizeBase64Payload(match[2] ?? "");
    if (!payload2) {
      return null;
    }
    const mimeType = match[1] || fallbackMimeType;
    return { data: `data:${mimeType};base64,${payload2}`, mimeType };
  }
  const payload = normalizeBase64Payload(trimmed);
  if (!payload) {
    return null;
  }
  return {
    data: `data:${fallbackMimeType};base64,${payload}`,
    mimeType: fallbackMimeType
  };
}
function isBinaryInput(value) {
  if (value instanceof ArrayBuffer) {
    return true;
  }
  return value instanceof Uint8Array;
}
function writeImageToTempFile(imageData) {
  const dir = mkdtempSync(join(tmpdir(), "codex-img-"));
  const ext = getExtensionFromMimeType(imageData.mimeType);
  const filePath = join(dir, `image.${ext}`);
  const base64Match = imageData.data.match(/^data:[^;]+;base64,(.+)$/);
  if (!base64Match) {
    throw new Error("Invalid data URL format: expected data:[type];base64,[data]");
  }
  const payload = normalizeBase64Payload(base64Match[1] ?? "");
  if (!payload) {
    throw new Error("Invalid base64 image payload");
  }
  const buffer = Buffer.from(payload, "base64");
  writeFileSync(filePath, buffer);
  return filePath;
}
function cleanupTempImages(paths) {
  for (const filePath of paths) {
    try {
      rmSync(filePath, { force: true });
      const dir = filePath.replace(/[/\\][^/\\]+$/, "");
      if (dir.includes("codex-img-")) {
        rmSync(dir, { force: true, recursive: true });
      }
    } catch {
    }
  }
}
function getExtensionFromMimeType(mimeType) {
  if (!mimeType) return "png";
  const mapping = {
    "image/png": "png",
    "image/jpeg": "jpg",
    "image/jpg": "jpg",
    "image/gif": "gif",
    "image/webp": "webp",
    "image/bmp": "bmp",
    "image/svg+xml": "svg"
  };
  return mapping[mimeType.toLowerCase()] || mimeType.split("/")[1] || "png";
}
function normalizeBase64Payload(value) {
  const compact = value.replace(/\s+/g, "");
  if (!compact) return null;
  if (!/^[A-Za-z0-9+/]*={0,2}$/.test(compact)) return null;
  if (compact.length % 4 === 1) return null;
  const padded = compact.padEnd(Math.ceil(compact.length / 4) * 4, "=");
  try {
    const normalized = Buffer.from(padded, "base64").toString("base64");
    if (normalized !== padded) return null;
    return padded;
  } catch {
    return null;
  }
}

// src/converters/image-converter.ts
function isHttpUrl(value) {
  return /^https?:\/\//i.test(value.trim());
}
function isFileUrlValue(value) {
  if (value instanceof URL) {
    return value.protocol === "file:";
  }
  if (typeof value === "string") {
    return /^file:\/\//i.test(value.trim());
  }
  return false;
}
function asRemoteUrl(value) {
  if (value instanceof URL) {
    const asString = value.toString();
    return isHttpUrl(asString) ? asString : void 0;
  }
  if (typeof value === "string") {
    return isHttpUrl(value) ? value.trim() : void 0;
  }
  return void 0;
}
function isImageMediaType(mediaType) {
  return getTopLevelMediaType(mediaType).toLowerCase() === "image";
}
function isFilePart(part) {
  return part.type === "file" && "mediaType" in part && typeof part.mediaType === "string";
}
function isCompatImagePart(part) {
  return part.type === "image";
}
function isTaggedFileData2(value) {
  if (typeof value !== "object" || value === null || !("type" in value)) return false;
  return value.type === "data" || value.type === "url" || value.type === "reference" || value.type === "text";
}
function toImageReference(part) {
  if (isFilePart(part)) {
    if (!isImageMediaType(part.mediaType)) {
      return {
        kind: "unsupported",
        warning: `Unsupported file mediaType "${part.mediaType}"; only image/* is supported.`
      };
    }
    const data = part.data;
    if (isTaggedFileData2(data)) {
      if (data.type === "reference") {
        return {
          kind: "unsupported",
          warning: "File reference data is not supported for images."
        };
      }
      if (data.type === "text") {
        return {
          kind: "unsupported",
          warning: "Inline text file data is not supported for images."
        };
      }
      if (data.type === "url") {
        const remote2 = asRemoteUrl(data.url);
        if (remote2) {
          return { kind: "remote", url: remote2 };
        }
        if (isFileUrlValue(data.url)) {
          return {
            kind: "unsupported",
            warning: "file:// image URLs are not supported."
          };
        }
      }
      const image2 = extractImageData(part);
      if (image2) {
        return { kind: "local", image: image2 };
      }
      return {
        kind: "unsupported",
        warning: "Unsupported image format in message."
      };
    }
    const remote = asRemoteUrl(data);
    if (remote) {
      return { kind: "remote", url: remote };
    }
    const image = extractImageData(part);
    if (image) {
      return { kind: "local", image };
    }
    if (isFileUrlValue(data) || "url" in part && isFileUrlValue(part.url)) {
      return {
        kind: "unsupported",
        warning: "file:// image URLs are not supported."
      };
    }
    return {
      kind: "unsupported",
      warning: "Unsupported image format in message."
    };
  }
  if (isCompatImagePart(part)) {
    const remote = asRemoteUrl(part.image) ?? asRemoteUrl(part.url);
    if (remote) {
      return { kind: "remote", url: remote };
    }
    const image = extractImageData(part);
    if (image) {
      return { kind: "local", image };
    }
    if (isFileUrlValue(part.image) || isFileUrlValue(part.url) || isFileUrlValue(part.data)) {
      return {
        kind: "unsupported",
        warning: "file:// image URLs are not supported."
      };
    }
    return {
      kind: "unsupported",
      warning: "Unsupported image format in message."
    };
  }
  return void 0;
}

// src/converters/tool-result-converter.ts
function safeJsonStringify(value) {
  if (value === void 0) return "";
  if (typeof value === "string") return value;
  try {
    return JSON.stringify(value);
  } catch {
    return "[unserializable]";
  }
}
function describeTypeTag(value) {
  if (typeof value === "object" && value !== null && "type" in value) {
    return String(value.type);
  }
  return "unknown";
}
function formatToolResultOutput(output) {
  switch (output.type) {
    case "text":
      return { text: output.value, warnings: [] };
    case "json":
      return { text: safeJsonStringify(output.value), warnings: [] };
    case "execution-denied":
      return {
        text: output.reason ? `Execution denied: ${output.reason}` : "Execution denied",
        warnings: []
      };
    case "error-text":
      return { text: `Tool error: ${output.value}`, warnings: [] };
    case "error-json":
      return { text: `Tool error: ${safeJsonStringify(output.value)}`, warnings: [] };
    case "content": {
      const warnings = [];
      const parts = output.value.map((part) => {
        if (part.type === "text") return part.text;
        if (part.type === "file") {
          const prefix = isImageMediaType(part.mediaType) ? "image" : "file";
          const data = part.data;
          if (data.type === "data") {
            return prefix === "image" ? `[image-data: ${part.mediaType}]` : `[file-data: ${part.mediaType}${part.filename ? `, ${part.filename}` : ""}]`;
          }
          if (data.type === "url") return `[${prefix}-url: ${data.url}]`;
          if (data.type === "reference") {
            return prefix === "image" ? "[image-file-id]" : "[file-id]";
          }
          if (data.type === "text") return data.text;
          warnings.push({
            type: "unsupported",
            feature: "tool-result.content.file",
            details: `Unsupported tool file content data type "${describeTypeTag(data)}".`
          });
          return "[unsupported-tool-content-part]";
        }
        if (part.type === "custom") {
          warnings.push({
            type: "unsupported",
            feature: "tool-result.content.custom",
            details: "Custom tool content parts are not supported."
          });
          return "";
        }
        warnings.push({
          type: "unsupported",
          feature: `tool-result.content.${describeTypeTag(part)}`,
          details: `Unsupported tool content part "${describeTypeTag(part)}".`
        });
        return "[unsupported-tool-content-part]";
      }).filter((part) => part.length > 0);
      return { text: parts.join("\n"), warnings };
    }
    default:
      return {
        text: "[unsupported-tool-result-output]",
        warnings: [
          {
            type: "unsupported",
            feature: `tool-result.output.${describeTypeTag(output)}`,
            details: `Unsupported tool result output type "${describeTypeTag(output)}".`
          }
        ]
      };
  }
}

// src/converters/prompt-converter.ts
function isTextPart(part) {
  return part.type === "text" && typeof part.text === "string";
}
function isReasoningPart(part) {
  return part.type === "reasoning" && typeof part.text === "string";
}
function isToolCallPart(part) {
  const typed = part;
  return part.type === "tool-call" && typeof typed.toolName === "string" && typeof typed.toolCallId === "string" && "input" in typed;
}
function isToolResultPart(part) {
  const typed = part;
  return part.type === "tool-result" && typeof typed.toolName === "string" && typed.output !== void 0;
}
function isToolApprovalResponsePart(part) {
  const typed = part;
  return part.type === "tool-approval-response" && typeof typed.approvalId === "string" && typeof typed.approved === "boolean" && (typed.reason === void 0 || typeof typed.reason === "string");
}
function collectSystemInstruction(prompt) {
  const parts = [];
  for (const message of prompt) {
    if (message.role !== "system") continue;
    if (typeof message.content === "string") {
      const text = message.content.trim();
      if (text.length > 0) parts.push(text);
      continue;
    }
    for (const part of message.content) {
      if (!isTextPart(part)) continue;
      const text = part.text.trim();
      if (text.length > 0) parts.push(text);
    }
  }
  if (parts.length === 0) return void 0;
  return parts.join("\n\n");
}
function normalizeUserInputFromMessages(messages) {
  const warnings = [];
  const localImages = [];
  const remoteImageUrls = [];
  const userTextParts = [];
  let sawImages = false;
  for (const message of messages) {
    if (message.role !== "user") continue;
    if (typeof message.content === "string") {
      const text = message.content.trim();
      if (text.length > 0) userTextParts.push(text);
      continue;
    }
    const inlineTextParts = [];
    for (const part of message.content) {
      if (isTextPart(part)) {
        const text = part.text.trim();
        if (text.length > 0) inlineTextParts.push(text);
        continue;
      }
      if (isFilePart(part) || isCompatImagePart(part)) {
        const image = toImageReference(part);
        if (!image) continue;
        sawImages = true;
        if (image.kind === "local") {
          localImages.push(image.image);
        } else if (image.kind === "remote") {
          remoteImageUrls.push(image.url);
        } else {
          warnings.push({
            type: "unsupported",
            feature: `prompt.user.${part.type}`,
            details: image.warning
          });
        }
        continue;
      }
      warnings.push({
        type: "unsupported",
        feature: `prompt.user.${part.type}`,
        details: `Unsupported user content part "${part.type}".`
      });
    }
    const joined = inlineTextParts.join("\n").trim();
    if (joined.length > 0) {
      userTextParts.push(joined);
    }
  }
  return {
    text: userTextParts.join("\n").trim(),
    localImages,
    remoteImageUrls,
    warnings,
    sawImages
  };
}
function formatAssistantParts(parts) {
  const warnings = [];
  const lines = [];
  const textParts = [];
  for (const part of parts) {
    if (isTextPart(part)) {
      const text2 = part.text.trim();
      if (text2.length > 0) textParts.push(text2);
      continue;
    }
    if (isReasoningPart(part)) {
      const text2 = part.text.trim();
      if (text2.length > 0) {
        lines.push(`Assistant Reasoning: ${text2}`);
      }
      continue;
    }
    if (part.type === "tool-call") {
      if (isToolCallPart(part)) {
        lines.push(`Tool Call (${part.toolName}): ${safeJsonStringify(part.input)}`);
      } else {
        warnings.push({
          type: "unsupported",
          feature: "prompt.assistant.tool-call.malformed",
          details: "Malformed assistant tool-call part; expected toolName, toolCallId, and input."
        });
      }
      continue;
    }
    if (isToolResultPart(part)) {
      const formatted = formatToolResultOutput(
        part.output
      );
      lines.push(`Tool Result (${part.toolName}): ${formatted.text}`);
      warnings.push(...formatted.warnings);
      continue;
    }
    if (isFilePart(part)) {
      lines.push(`[assistant-file: ${part.mediaType}]`);
      continue;
    }
    if (part.type === "custom") {
      const kind = "kind" in part && typeof part.kind === "string" ? part.kind : void 0;
      warnings.push({
        type: "unsupported",
        feature: "prompt.assistant.custom",
        details: `Custom content parts are not supported${kind ? ` (kind "${kind}")` : ""}.`
      });
      continue;
    }
    if (part.type === "reasoning-file") {
      warnings.push({
        type: "unsupported",
        feature: "prompt.assistant.reasoning-file",
        details: "Reasoning file parts are not supported."
      });
      continue;
    }
    warnings.push({
      type: "unsupported",
      feature: `prompt.assistant.${part.type}`,
      details: `Unsupported assistant content part "${part.type}".`
    });
  }
  const text = textParts.join("\n").trim();
  if (text.length > 0) {
    lines.unshift(`Assistant: ${text}`);
  }
  return { lines, warnings };
}
function formatToolRoleParts(parts) {
  const warnings = [];
  const lines = [];
  for (const part of parts) {
    if (isToolResultPart(part)) {
      const formatted = formatToolResultOutput(
        part.output
      );
      lines.push(`Tool Result (${part.toolName}): ${formatted.text}`);
      warnings.push(...formatted.warnings);
      continue;
    }
    if (isToolApprovalResponsePart(part)) {
      const decision = part.approved ? "approved" : "denied";
      const reason = part.reason ? ` (${part.reason})` : "";
      lines.push(`Tool Approval (${part.approvalId}): ${decision}${reason}`);
      continue;
    }
    warnings.push({
      type: "unsupported",
      feature: `prompt.tool.${part.type}`,
      details: `Unsupported tool message content part "${part.type}".`
    });
  }
  return { lines, warnings };
}
function formatStatelessTranscript(prompt, options) {
  const warnings = [];
  const lines = [];
  const localImages = [];
  const remoteImageUrls = [];
  const includeRemoteImagesInMarkers = options?.includeRemoteImagesInMarkers ?? true;
  for (const message of prompt) {
    if (message.role === "system") continue;
    if (message.role === "user") {
      const normalized = normalizeUserInputFromMessages([message]);
      warnings.push(...normalized.warnings);
      localImages.push(...normalized.localImages);
      remoteImageUrls.push(...normalized.remoteImageUrls);
      const markerImageCount = normalized.localImages.length + (includeRemoteImagesInMarkers ? normalized.remoteImageUrls.length : 0);
      const marker = normalized.sawImages && markerImageCount > 0 ? `[${markerImageCount} image${markerImageCount === 1 ? "" : "s"} attached]` : "";
      const text = [normalized.text, marker].filter((part) => part.length > 0).join("\n").trim();
      if (text.length > 0) {
        lines.push(`User: ${text}`);
      }
      continue;
    }
    if (message.role === "assistant") {
      const content = Array.isArray(message.content) ? message.content : [{ type: "text", text: String(message.content) }];
      const formatted = formatAssistantParts(content);
      lines.push(...formatted.lines);
      warnings.push(...formatted.warnings);
      continue;
    }
    if (message.role === "tool") {
      const content = Array.isArray(message.content) ? message.content : [{ type: "text", text: String(message.content) }];
      const formatted = formatToolRoleParts(content);
      lines.push(...formatted.lines);
      warnings.push(...formatted.warnings);
      continue;
    }
  }
  return {
    text: lines.join("\n\n").trim(),
    localImages,
    remoteImageUrls,
    warnings
  };
}
function selectLatestUserTurn(prompt) {
  const selected = [];
  let collecting = false;
  for (let i = prompt.length - 1; i >= 0; i -= 1) {
    const message = prompt[i];
    if (!message) continue;
    if (message.role === "system") {
      continue;
    }
    if (message.role === "user") {
      selected.push(message);
      collecting = true;
      continue;
    }
    if (collecting) {
      break;
    }
  }
  if (selected.length > 0) {
    return selected.reverse();
  }
  for (let i = prompt.length - 1; i >= 0; i -= 1) {
    const message = prompt[i];
    if (!message) continue;
    if (message.role === "user") {
      return [message];
    }
  }
  return [];
}
function formatPersistentInput(prompt) {
  const selectedUserTurn = selectLatestUserTurn(prompt);
  const normalized = normalizeUserInputFromMessages(selectedUserTurn);
  const nonSystemCount = prompt.filter((message) => message.role !== "system").length;
  const selectedCount = selectedUserTurn.length;
  const warnings = [...normalized.warnings];
  if (nonSystemCount > selectedCount) {
    warnings.push({
      type: "other",
      message: "Stateful mode ignores earlier prompt messages and only sends the latest user turn."
    });
  }
  return {
    text: normalized.text,
    localImages: normalized.localImages,
    remoteImageUrls: normalized.remoteImageUrls,
    warnings
  };
}
function convertPromptToCodexInput(args) {
  const systemInstruction = collectSystemInstruction(args.prompt);
  const converted = args.mode === "persistent" ? formatPersistentInput(args.prompt) : formatStatelessTranscript(args.prompt, {
    includeRemoteImagesInMarkers: args.includeRemoteImagesInMarkers
  });
  return {
    systemInstruction,
    text: converted.text,
    localImages: converted.localImages,
    remoteImageUrls: converted.remoteImageUrls,
    warnings: converted.warnings
  };
}

// src/message-mapper.ts
function mapMessagesToPrompt(prompt) {
  const converted = convertPromptToCodexInput({
    prompt,
    mode: "stateless",
    includeRemoteImagesInMarkers: false
  });
  const warnings = converted.warnings.map(
    (warning) => warning.type === "unsupported" ? {
      type: "unsupported",
      feature: warning.feature,
      details: warning.details
    } : {
      type: "other",
      message: warning.message
    }
  );
  if (converted.remoteImageUrls.length > 0) {
    warnings.push({
      type: "unsupported",
      feature: "prompt.user.image.remote-url.exec",
      details: "Unsupported image format in message (HTTP URLs not supported)"
    });
  }
  const promptParts = [];
  if (converted.systemInstruction) {
    promptParts.push(converted.systemInstruction);
  }
  if (converted.text) {
    promptParts.push(converted.text);
  }
  return {
    promptText: promptParts.join("\n\n"),
    images: converted.localImages,
    ...warnings.length > 0 ? { warnings } : {}
  };
}
var UnsupportedFeatureError = class extends UnsupportedFunctionalityError {
  feature;
  minCodexVersion;
  serverVersion;
  constructor({
    feature,
    minCodexVersion,
    serverVersion,
    message
  }) {
    const resolvedMessage = message ?? `Feature '${feature}' is not supported by this codex app-server` + (serverVersion ? ` (detected ${serverVersion})` : "") + (minCodexVersion ? `. Requires codex CLI >= ${minCodexVersion}.` : ".");
    super({ functionality: feature, message: resolvedMessage });
    this.name = "UnsupportedFeatureError";
    this.feature = feature;
    this.minCodexVersion = minCodexVersion;
    this.serverVersion = serverVersion;
  }
};
function createAPICallError({
  message,
  code,
  exitCode,
  stderr,
  promptExcerpt,
  provider = "exec",
  isRetryable = false
}) {
  const data = { code, exitCode, stderr, promptExcerpt };
  const url = provider === "app-server" ? "codex-cli://app-server" : "codex-cli://exec";
  return new APICallError({
    message,
    isRetryable,
    url,
    requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
    data
  });
}
function createAuthenticationError(message) {
  return new LoadAPIKeyError({
    message: message || "Authentication failed. Ensure Codex CLI is logged in (codex login)."
  });
}
function isAuthenticationError(err) {
  if (err instanceof LoadAPIKeyError) return true;
  if (err instanceof APICallError) {
    const data = err.data;
    if (data?.code !== void 0) {
      const normalized = String(data.code).trim().toLowerCase();
      if (normalized === "401" || normalized === "unauthorized" || normalized === "auth") {
        return true;
      }
    }
  }
  return false;
}
function isUnsupportedFeatureError(err) {
  return err instanceof UnsupportedFeatureError;
}

// src/exec-language-model.ts
function isAgentMessageItemType(itemType) {
  return itemType === "agent_message" || itemType === "assistant_message";
}
var codexReasoningEfforts = {
  none: true,
  minimal: true,
  low: true,
  medium: true,
  high: true,
  xhigh: true,
  max: true,
  ultra: true
};
var codexCliProviderOptionsSchema = z.object({
  reasoningEffort: reasoningEffortSchema.optional(),
  reasoningSummary: z.enum(["auto", "detailed"]).optional(),
  reasoningSummaryFormat: z.enum(["none", "experimental"]).optional(),
  textVerbosity: z.enum(["low", "medium", "high"]).optional(),
  serviceTier: serviceTierSchema.optional(),
  addDirs: z.array(z.string().min(1)).optional(),
  configOverrides: z.record(
    z.string().refine((key) => isValidConfigOverrideKey(key), {
      message: "configOverrides keys must match /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$/."
    }),
    z.union([
      z.string(),
      z.number(),
      z.boolean(),
      z.object({}).passthrough(),
      z.array(z.any())
    ])
  ).optional(),
  mcpServers: mcpServersSchema.optional(),
  rmcpClient: z.boolean().optional()
}).strict();
function resolveCodexPath(explicitPath, allowNpx) {
  if (explicitPath) {
    const lower = explicitPath.toLowerCase();
    if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
      return { cmd: "node", args: [explicitPath] };
    }
    return { cmd: explicitPath, args: [] };
  }
  try {
    const req = createRequire(import.meta.url);
    const pkgPath = req.resolve("@openai/codex/package.json");
    const root = pkgPath.replace(/package\.json$/, "");
    return { cmd: "node", args: [root + "bin/codex.js"] };
  } catch {
    if (allowNpx) return { cmd: "npx", args: ["-y", "@openai/codex"] };
    return { cmd: "codex", args: [] };
  }
}
var ExecLanguageModel = class {
  specificationVersion = "v4";
  provider = "codex-cli";
  supportedUrls = {};
  modelId;
  settings;
  logger;
  sessionId;
  deprecationWarningsEmitted = /* @__PURE__ */ new Set();
  constructor(options) {
    this.modelId = options.id;
    this.settings = options.settings ?? {};
    const baseLogger = getLogger(this.settings.logger);
    this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
    if (!this.modelId || this.modelId.trim() === "") {
      throw new NoSuchModelError({ modelId: this.modelId, modelType: "languageModel" });
    }
    const warn = validateModelId(this.modelId);
    if (warn) this.logger.warn(`Codex CLI model: ${warn}`);
  }
  warnDeprecatedOnce(key, message) {
    if (this.deprecationWarningsEmitted.has(key)) return;
    this.deprecationWarningsEmitted.add(key);
    this.logger.warn(`[codex-cli] ${message}`);
  }
  mergeSettings(providerOptions) {
    if (!providerOptions) return this.settings;
    const mergedConfigOverrides = providerOptions.configOverrides || this.settings.configOverrides ? {
      ...this.settings.configOverrides ?? {},
      ...providerOptions.configOverrides ?? {}
    } : void 0;
    const mergedAddDirs = providerOptions.addDirs || this.settings.addDirs ? [...this.settings.addDirs ?? [], ...providerOptions.addDirs ?? []] : void 0;
    const mergedMcpServers = mergeMcpServers(this.settings.mcpServers, providerOptions.mcpServers);
    return {
      ...this.settings,
      reasoningEffort: providerOptions.reasoningEffort ?? this.settings.reasoningEffort,
      reasoningSummary: providerOptions.reasoningSummary ?? this.settings.reasoningSummary,
      reasoningSummaryFormat: providerOptions.reasoningSummaryFormat ?? this.settings.reasoningSummaryFormat,
      modelVerbosity: providerOptions.textVerbosity ?? this.settings.modelVerbosity,
      serviceTier: providerOptions.serviceTier ?? this.settings.serviceTier,
      configOverrides: mergedConfigOverrides,
      addDirs: mergedAddDirs,
      mcpServers: mergedMcpServers,
      rmcpClient: providerOptions.rmcpClient ?? this.settings.rmcpClient
    };
  }
  // Codex JSONL items use `type` for the item discriminator, but some
  // earlier fixtures (and defensive parsing) might still surface `item_type`.
  // This helper returns whichever is present.
  getItemType(item) {
    if (!item) return void 0;
    const data = item;
    const legacy = typeof data.item_type === "string" ? data.item_type : void 0;
    const current = typeof data.type === "string" ? data.type : void 0;
    return legacy ?? current;
  }
  buildArgs(images = [], responseFormat, settings = this.settings) {
    const base = resolveCodexPath(settings.codexPath, settings.allowNpx);
    const args = [...base.args, "exec", "--experimental-json"];
    if (settings.dangerouslyBypassApprovalsAndSandbox && !settings.fullAuto) {
      args.push("--dangerously-bypass-approvals-and-sandbox");
    } else {
      const requestedApproval = settings.approvalMode ?? (settings.fullAuto ? "never" : "on-request");
      if (isDeprecatedApprovalPolicyAlias(requestedApproval)) {
        this.warnDeprecatedOnce(
          `approvalMode:${requestedApproval}`,
          `approvalMode '${requestedApproval}' is deprecated (retired by Codex CLI 0.143); sending 'on-request' instead.`
        );
      }
      const approval = normalizeApprovalPolicyAlias(requestedApproval);
      args.push("-c", `approval_policy=${approval}`);
      const sandbox = settings.sandboxMode ?? "workspace-write";
      args.push("-c", `sandbox_mode=${sandbox}`);
    }
    if (settings.skipGitRepoCheck !== false) {
      args.push("--skip-git-repo-check");
    }
    if (settings.reasoningEffort) {
      args.push("-c", `model_reasoning_effort=${settings.reasoningEffort}`);
    }
    if (settings.reasoningSummary) {
      args.push("-c", `model_reasoning_summary=${settings.reasoningSummary}`);
    }
    if (settings.reasoningSummaryFormat) {
      args.push("-c", `model_reasoning_summary_format=${settings.reasoningSummaryFormat}`);
    }
    if (settings.modelVerbosity) {
      args.push("-c", `model_verbosity=${settings.modelVerbosity}`);
    }
    if (settings.serviceTier) {
      args.push("-c", `service_tier=${JSON.stringify(settings.serviceTier)}`);
    }
    if (settings.profile) {
      args.push("--profile", settings.profile);
    }
    if (settings.oss) {
      args.push("--oss");
    }
    if (settings.webSearch) {
      args.push("-c", "tools.web_search=true");
    }
    const mcpSecretEnv = this.applyMcpSettings(args, settings);
    if (settings.color) {
      args.push("--color", settings.color);
    }
    if (this.modelId) {
      args.push("-m", this.modelId);
    }
    if (settings.addDirs?.length) {
      for (const dir of settings.addDirs) {
        if (typeof dir === "string" && dir.trim().length > 0) {
          args.push("--add-dir", dir);
        }
      }
    }
    if (settings.configOverrides) {
      for (const [key, value] of Object.entries(settings.configOverrides)) {
        this.addConfigOverride(args, key, value);
      }
    }
    let schemaPath;
    if (responseFormat?.type === "json" && responseFormat.schema) {
      const schema = typeof responseFormat.schema === "object" ? responseFormat.schema : {};
      const sanitizedSchema = sanitizeJsonSchema(schema);
      const hasProperties = Object.keys(sanitizedSchema).length > 0;
      if (hasProperties) {
        const dir = mkdtempSync(join(tmpdir(), "codex-schema-"));
        schemaPath = join(dir, "schema.json");
        const schemaWithAdditional = {
          ...sanitizedSchema,
          additionalProperties: false
        };
        writeFileSync(schemaPath, JSON.stringify(schemaWithAdditional, null, 2));
        args.push("--output-schema", schemaPath);
      }
    }
    const tempImagePaths = [];
    for (const img of images) {
      try {
        const tempPath = writeImageToTempFile(img);
        tempImagePaths.push(tempPath);
        args.push("--image", tempPath);
      } catch (e) {
        this.logger.warn(`[codex-cli] Failed to write image to temp file: ${String(e)}`);
      }
    }
    const env = {
      ...process.env,
      ...settings.env || {},
      ...mcpSecretEnv,
      RUST_LOG: process.env.RUST_LOG || "error"
    };
    let lastMessagePath = settings.outputLastMessageFile;
    let lastMessageIsTemp = false;
    if (!lastMessagePath) {
      const dir = mkdtempSync(join(tmpdir(), "codex-cli-"));
      lastMessagePath = join(dir, "last-message.txt");
      lastMessageIsTemp = true;
    }
    args.push("--output-last-message", lastMessagePath);
    if (tempImagePaths.length > 0) {
      args.push("--");
    }
    args.push("-");
    return {
      cmd: base.cmd,
      args,
      env,
      cwd: settings.cwd,
      lastMessagePath,
      lastMessageIsTemp,
      schemaPath,
      tempImagePaths: tempImagePaths.length > 0 ? tempImagePaths : void 0
    };
  }
  /**
   * Apply MCP server settings as `-c` config overrides.
   *
   * Returns environment variables that must be set on the spawned codex
   * process. Inline HTTP `bearerToken` secrets are routed through the child
   * environment (via `bearer_token_env_var`) instead of argv, because command
   * lines are readable by any local process (`ps`, /proc/<pid>/cmdline).
   */
  applyMcpSettings(args, settings) {
    if (settings.rmcpClient) {
      this.addConfigOverride(args, "features.rmcp_client", true);
    }
    if (!settings.mcpServers) return void 0;
    const secretEnv = {};
    for (const [rawName, server] of Object.entries(settings.mcpServers)) {
      const name = assertValidMcpServerName(rawName);
      const prefix = `mcp_servers.${name}`;
      if (server.enabled !== void 0) {
        this.addConfigOverride(args, `${prefix}.enabled`, server.enabled);
      }
      if (server.startupTimeoutSec !== void 0) {
        this.addConfigOverride(args, `${prefix}.startup_timeout_sec`, server.startupTimeoutSec);
      }
      if (server.toolTimeoutSec !== void 0) {
        this.addConfigOverride(args, `${prefix}.tool_timeout_sec`, server.toolTimeoutSec);
      }
      if (server.enabledTools !== void 0) {
        this.addConfigOverride(args, `${prefix}.enabled_tools`, server.enabledTools);
      }
      if (server.disabledTools !== void 0) {
        this.addConfigOverride(args, `${prefix}.disabled_tools`, server.disabledTools);
      }
      if (server.transport === "stdio") {
        this.addConfigOverride(args, `${prefix}.command`, server.command);
        if (server.args !== void 0) this.addConfigOverride(args, `${prefix}.args`, server.args);
        if (server.env !== void 0) this.addConfigOverride(args, `${prefix}.env`, server.env);
        if (server.cwd) this.addConfigOverride(args, `${prefix}.cwd`, server.cwd);
      } else {
        this.addConfigOverride(args, `${prefix}.url`, server.url);
        const hasExplicitAuthorizationHeader = Object.keys(server.httpHeaders ?? {}).some(
          (key) => key.toLowerCase() === "authorization"
        );
        if (server.bearerTokenEnvVar) {
          this.addConfigOverride(args, `${prefix}.bearer_token_env_var`, server.bearerTokenEnvVar);
          if (server.bearerToken !== void 0) {
            this.logger.warn(
              `[codex-cli] MCP server '${name}': both bearerToken and bearerTokenEnvVar are set; using bearerTokenEnvVar and ignoring the inline token.`
            );
          }
        } else if (server.bearerToken !== void 0 && !hasExplicitAuthorizationHeader) {
          const envVarName = this.allocateBearerTokenEnvVar(name, secretEnv);
          secretEnv[envVarName] = server.bearerToken;
          this.addConfigOverride(args, `${prefix}.bearer_token_env_var`, envVarName);
        }
        if (server.httpHeaders !== void 0)
          this.addConfigOverride(args, `${prefix}.http_headers`, server.httpHeaders);
        if (server.envHttpHeaders !== void 0)
          this.addConfigOverride(args, `${prefix}.env_http_headers`, server.envHttpHeaders);
      }
    }
    return Object.keys(secretEnv).length > 0 ? secretEnv : void 0;
  }
  /**
   * Pick a child-env variable name to carry an inline MCP bearer token,
   * avoiding collisions between server names that normalize identically
   * (e.g. 'foo-bar' and 'foo_bar').
   */
  allocateBearerTokenEnvVar(serverName, secretEnv) {
    const base = `CODEX_MCP_${serverName.toUpperCase().replace(/-/g, "_")}_BEARER_TOKEN`;
    let candidate = base;
    let suffix = 2;
    while (candidate in secretEnv) {
      candidate = `${base}_${suffix}`;
      suffix += 1;
    }
    return candidate;
  }
  addConfigOverride(args, key, value) {
    assertValidConfigOverrideKey(key);
    if (isPlainObject(value)) {
      const entries = Object.entries(value);
      if (entries.length === 0) {
        args.push("-c", `${key}={}`);
        return;
      }
      for (const [childKey, childValue] of entries) {
        this.addConfigOverride(
          args,
          `${key}.${childKey}`,
          childValue
        );
      }
      return;
    }
    const serialized = this.serializeConfigValue(value);
    args.push("-c", `${key}=${serialized}`);
  }
  /**
   * Serialize a config override value into a CLI-safe string.
   */
  serializeConfigValue(value) {
    if (typeof value === "string") return value;
    if (typeof value === "number" || typeof value === "boolean") return String(value);
    if (Array.isArray(value)) {
      try {
        return JSON.stringify(value);
      } catch {
        return String(value);
      }
    }
    if (value && typeof value === "object") {
      try {
        return JSON.stringify(value);
      } catch {
        return String(value);
      }
    }
    return String(value);
  }
  parseExperimentalJsonEvent(line) {
    try {
      return JSON.parse(line);
    } catch {
      return void 0;
    }
  }
  extractUsage(evt) {
    const reported = evt.usage;
    if (!reported) return void 0;
    const inputTotal = reported.input_tokens ?? 0;
    const outputTotal = reported.output_tokens ?? 0;
    const cachedInputTokens = reported.cached_input_tokens ?? 0;
    const cacheWrite = typeof reported.cache_write_input_tokens === "number" ? reported.cache_write_input_tokens : void 0;
    const reasoning = typeof reported.reasoning_output_tokens === "number" ? reported.reasoning_output_tokens : void 0;
    return {
      inputTokens: {
        total: inputTotal,
        noCache: Math.max(0, inputTotal - cachedInputTokens - (cacheWrite ?? 0)),
        cacheRead: cachedInputTokens,
        cacheWrite
      },
      outputTokens: {
        total: outputTotal,
        text: void 0,
        reasoning
      },
      raw: reported
    };
  }
  getToolName(item) {
    if (!item) return void 0;
    const itemType = this.getItemType(item);
    switch (itemType) {
      case "command_execution":
        return "exec";
      case "file_change":
        return "patch";
      case "mcp_tool_call": {
        const tool2 = item.tool;
        if (typeof tool2 === "string" && tool2.length > 0) return tool2;
        return "mcp_tool";
      }
      case "web_search":
        return "web_search";
      default:
        return void 0;
    }
  }
  buildToolInputPayload(item) {
    if (!item) return void 0;
    const data = item;
    switch (this.getItemType(item)) {
      case "command_execution": {
        const payload = {};
        if (typeof data.command === "string") payload.command = data.command;
        if (typeof data.status === "string") payload.status = data.status;
        if (typeof data.cwd === "string") payload.cwd = data.cwd;
        return Object.keys(payload).length ? payload : void 0;
      }
      case "file_change": {
        const payload = {};
        if (Array.isArray(data.changes)) payload.changes = data.changes;
        if (typeof data.status === "string") payload.status = data.status;
        return Object.keys(payload).length ? payload : void 0;
      }
      case "mcp_tool_call": {
        const payload = {};
        if (typeof data.server === "string") payload.server = data.server;
        if (typeof data.tool === "string") payload.tool = data.tool;
        if (typeof data.status === "string") payload.status = data.status;
        if (data.arguments !== void 0) payload.arguments = data.arguments;
        return Object.keys(payload).length ? payload : void 0;
      }
      case "web_search": {
        const payload = {};
        if (typeof data.query === "string") payload.query = data.query;
        return Object.keys(payload).length ? payload : void 0;
      }
      default:
        return void 0;
    }
  }
  buildToolResultPayload(item) {
    if (!item) return { result: {} };
    const data = item;
    const metadata = {};
    const itemType = this.getItemType(item);
    if (typeof itemType === "string") metadata.itemType = itemType;
    if (typeof item.id === "string") metadata.itemId = item.id;
    if (typeof data.status === "string") metadata.status = data.status;
    const buildResult = (result) => ({
      result,
      metadata: Object.keys(metadata).length ? metadata : void 0
    });
    switch (itemType) {
      case "command_execution": {
        const result = {};
        if (typeof data.command === "string") result.command = data.command;
        if (typeof data.aggregated_output === "string")
          result.aggregatedOutput = data.aggregated_output;
        if (typeof data.exit_code === "number") result.exitCode = data.exit_code;
        if (typeof data.status === "string") result.status = data.status;
        return buildResult(result);
      }
      case "file_change": {
        const result = {};
        if (Array.isArray(data.changes)) result.changes = data.changes;
        if (typeof data.status === "string") result.status = data.status;
        return buildResult(result);
      }
      case "mcp_tool_call": {
        const result = {};
        if (typeof data.server === "string") {
          result.server = data.server;
          metadata.server = data.server;
        }
        if (typeof data.tool === "string") result.tool = data.tool;
        if (typeof data.status === "string") result.status = data.status;
        if (data.result !== void 0) result.result = data.result;
        if (data.error !== void 0) result.error = data.error;
        return buildResult(result);
      }
      case "web_search": {
        const result = {};
        if (typeof data.query === "string") result.query = data.query;
        if (typeof data.status === "string") result.status = data.status;
        return buildResult(result);
      }
      default: {
        const result = { ...data };
        return buildResult(result);
      }
    }
  }
  emitToolInvocation(controller, toolCallId, toolName, inputPayload) {
    const inputString = safeStringify(inputPayload);
    controller.enqueue({
      type: "tool-input-start",
      id: toolCallId,
      toolName,
      providerExecuted: true,
      dynamic: true
    });
    if (inputString) {
      controller.enqueue({ type: "tool-input-delta", id: toolCallId, delta: inputString });
    }
    controller.enqueue({ type: "tool-input-end", id: toolCallId });
    controller.enqueue({
      type: "tool-call",
      toolCallId,
      toolName,
      input: inputString,
      providerExecuted: true,
      dynamic: true
    });
  }
  emitToolResult(controller, toolCallId, toolName, item, resultPayload, metadata) {
    const providerMetadataEntries = {
      ...metadata ?? {}
    };
    const itemType = this.getItemType(item);
    if (itemType && providerMetadataEntries.itemType === void 0) {
      providerMetadataEntries.itemType = itemType;
    }
    if (item.id && providerMetadataEntries.itemId === void 0) {
      providerMetadataEntries.itemId = item.id;
    }
    let isError;
    if (itemType === "command_execution") {
      const data = item;
      const exitCode = typeof data.exit_code === "number" ? data.exit_code : void 0;
      const status = typeof data.status === "string" ? data.status : void 0;
      if (exitCode !== void 0 && exitCode !== 0 || status === "failed") {
        isError = true;
      }
    }
    controller.enqueue({
      type: "tool-result",
      toolCallId,
      toolName,
      result: resultPayload ?? {},
      dynamic: true,
      ...isError ? { isError: true } : {},
      ...Object.keys(providerMetadataEntries).length ? { providerMetadata: { "codex-cli": providerMetadataEntries } } : {}
    });
  }
  // Converts a child-process spawn failure (e.g. ENOENT for a missing codex
  // binary) into the error the stream should be rejected with. Returns rather
  // than throws: it is called from inside the child's 'error' listener, where a
  // throw would escape as an uncaught exception instead of settling the request.
  handleSpawnError(err, promptExcerpt) {
    const e = err && typeof err === "object" ? err : void 0;
    const message = String((e?.message ?? err) || "Failed to run Codex CLI");
    if (/login|auth|unauthorized|not\s+logged/i.test(message)) {
      return createAuthenticationError(message);
    }
    return createAPICallError({
      message,
      code: typeof e?.code === "string" ? e.code : void 0,
      exitCode: typeof e?.exitCode === "number" ? e.exitCode : void 0,
      stderr: typeof e?.stderr === "string" ? e.stderr : void 0,
      promptExcerpt
    });
  }
  async doGenerate(options) {
    this.logger.debug(`[codex-cli] Starting doGenerate request with model: ${this.modelId}`);
    const { stream, request } = await this.doStream(
      options
    );
    const content = [];
    const textPartsById = /* @__PURE__ */ new Map();
    const reasoningPartsById = /* @__PURE__ */ new Map();
    let activeTextBlockId;
    let activeReasoningBlockId;
    let responseMetadata = {
      id: generateId(),
      timestamp: /* @__PURE__ */ new Date(),
      modelId: this.modelId
    };
    let usage = createEmptyCodexUsage();
    let finishReason = { unified: "other", raw: void 0 };
    let warnings = [];
    let providerMetadata;
    const ensureTextPart = (id, metadata) => {
      const existing = textPartsById.get(id);
      if (existing) {
        if (metadata) existing.providerMetadata = metadata;
        return existing;
      }
      const part = {
        type: "text",
        text: "",
        ...metadata ? { providerMetadata: metadata } : {}
      };
      textPartsById.set(id, part);
      content.push(part);
      return part;
    };
    const ensureReasoningPart = (id, metadata) => {
      const existing = reasoningPartsById.get(id);
      if (existing) {
        if (metadata) existing.providerMetadata = metadata;
        return existing;
      }
      const part = {
        type: "reasoning",
        text: "",
        ...metadata ? { providerMetadata: metadata } : {}
      };
      reasoningPartsById.set(id, part);
      content.push(part);
      return part;
    };
    const pushContentPart = (part) => {
      content.push(part);
    };
    for await (const part of stream) {
      if (part.type === "stream-start") {
        warnings = part.warnings;
        continue;
      }
      if (part.type === "response-metadata") {
        responseMetadata = {
          id: part.id,
          timestamp: part.timestamp,
          modelId: part.modelId
        };
        continue;
      }
      if (part.type === "text-start") {
        activeTextBlockId = part.id;
        ensureTextPart(part.id, part.providerMetadata);
        continue;
      }
      if (part.type === "text-delta") {
        const blockId = typeof part.id === "string" ? part.id : activeTextBlockId ?? "__default_text_block__";
        activeTextBlockId = blockId;
        const textPart = ensureTextPart(blockId, part.providerMetadata);
        textPart.text = `${textPart.text}${part.delta}`;
        continue;
      }
      if (part.type === "text-end") {
        const blockId = typeof part.id === "string" ? part.id : activeTextBlockId;
        if (blockId) {
          const textPart = ensureTextPart(blockId, part.providerMetadata);
          if (part.providerMetadata) {
            textPart.providerMetadata = part.providerMetadata;
          }
        }
        if (activeTextBlockId === blockId) {
          activeTextBlockId = void 0;
        }
        continue;
      }
      if (part.type === "reasoning-start") {
        activeReasoningBlockId = part.id;
        ensureReasoningPart(part.id, part.providerMetadata);
        continue;
      }
      if (part.type === "reasoning-delta") {
        const blockId = typeof part.id === "string" ? part.id : activeReasoningBlockId ?? "__default_reasoning_block__";
        activeReasoningBlockId = blockId;
        const reasoningPart = ensureReasoningPart(blockId, part.providerMetadata);
        reasoningPart.text = `${reasoningPart.text}${part.delta}`;
        continue;
      }
      if (part.type === "reasoning-end") {
        const blockId = typeof part.id === "string" ? part.id : activeReasoningBlockId;
        if (blockId) {
          const reasoningPart = ensureReasoningPart(blockId, part.providerMetadata);
          if (part.providerMetadata) {
            reasoningPart.providerMetadata = part.providerMetadata;
          }
        }
        if (activeReasoningBlockId === blockId) {
          activeReasoningBlockId = void 0;
        }
        continue;
      }
      if (part.type === "file") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "source") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-approval-request") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-call") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-result") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "finish") {
        usage = part.usage;
        finishReason = part.finishReason;
        providerMetadata = part.providerMetadata;
      }
    }
    const normalizedContent = content.filter((part) => {
      if (part.type === "text") return part.text.trim().length > 0;
      if (part.type === "reasoning") return part.text.trim().length > 0;
      return true;
    });
    const codexProviderMetadata = providerMetadata && typeof providerMetadata === "object" ? { ...providerMetadata } : {};
    if (this.sessionId) {
      const existing = codexProviderMetadata["codex-cli"];
      const existingObject = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
      codexProviderMetadata["codex-cli"] = {
        ...existingObject,
        sessionId: this.sessionId
      };
    }
    return {
      content: normalizedContent,
      usage,
      finishReason,
      warnings,
      response: responseMetadata,
      request,
      ...Object.keys(codexProviderMetadata).length > 0 ? { providerMetadata: codexProviderMetadata } : {}
    };
  }
  async doStream(options) {
    this.logger.debug(`[codex-cli] Starting doStream request with model: ${this.modelId}`);
    const { promptText, images, warnings: mappingWarnings } = mapMessagesToPrompt(options.prompt);
    const promptExcerpt = promptText.slice(0, 200);
    const warnings = [
      ...mapUnsupportedSettingsWarnings({
        temperature: options.temperature,
        topP: options.topP,
        topK: options.topK,
        maxOutputTokens: options.maxOutputTokens,
        presencePenalty: options.presencePenalty,
        frequencyPenalty: options.frequencyPenalty,
        stopSequences: options.stopSequences,
        seed: options.seed,
        tools: options.tools,
        toolChoice: options.toolChoice
      }),
      ...mappingWarnings ?? []
    ];
    this.logger.debug(
      `[codex-cli] Converted ${options.prompt.length} messages (${images.length} images) for streaming, response format: ${options.responseFormat?.type ?? "none"}`
    );
    const providerOptions = await parseProviderOptions({
      provider: this.provider,
      providerOptions: options.providerOptions,
      schema: codexCliProviderOptionsSchema
    });
    let effectiveSettings = this.mergeSettings(providerOptions);
    const topLevelReasoning = options.reasoning;
    if (topLevelReasoning !== void 0 && topLevelReasoning !== "provider-default" && providerOptions?.reasoningEffort === void 0) {
      if (Object.hasOwn(codexReasoningEfforts, topLevelReasoning)) {
        effectiveSettings = {
          ...effectiveSettings,
          reasoningEffort: topLevelReasoning
        };
      } else {
        warnings.push({
          type: "unsupported",
          feature: "reasoning",
          details: `Codex CLI does not support reasoning effort '${topLevelReasoning}'; it will be ignored.`
        });
      }
    }
    const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
    const { cmd, args, env, cwd, lastMessagePath, lastMessageIsTemp, schemaPath, tempImagePaths } = this.buildArgs(images, responseFormat, effectiveSettings);
    this.logger.debug(
      `[codex-cli] Executing Codex CLI for streaming: ${cmd} with ${args.length} arguments`
    );
    const stream = new ReadableStream({
      start: (controller) => {
        const startTime = Date.now();
        const child = spawn(cmd, args, { env, cwd, stdio: ["pipe", "pipe", "pipe"] });
        child.stdin.write(promptText);
        child.stdin.end();
        controller.enqueue({ type: "stream-start", warnings });
        let stderr = "";
        let accumulatedText = "";
        let stdoutBuffer = "";
        const activeTools = /* @__PURE__ */ new Map();
        let responseMetadataSent = false;
        let lastUsage;
        let turnFailureMessage;
        const cleanupOwnedLastMessage = () => {
          if (!lastMessageIsTemp || !lastMessagePath) return;
          try {
            rmSync(lastMessagePath, { force: true });
          } catch {
          }
          try {
            rmSync(dirname(lastMessagePath), { recursive: true, force: true });
          } catch {
          }
        };
        const cleanupTempFiles = () => {
          if (schemaPath) {
            try {
              const schemaDir = dirname(schemaPath);
              rmSync(schemaDir, { recursive: true, force: true });
            } catch {
            }
          }
          if (tempImagePaths?.length) {
            cleanupTempImages(tempImagePaths);
          }
        };
        const sendMetadata = (meta = {}) => {
          controller.enqueue({
            type: "response-metadata",
            id: randomUUID(),
            timestamp: /* @__PURE__ */ new Date(),
            modelId: this.modelId,
            ...Object.keys(meta).length ? { providerMetadata: { "codex-cli": meta } } : {}
          });
        };
        const handleItemEvent = (event) => {
          const item = event.item;
          if (!item) return;
          if (event.type === "item.completed" && isAgentMessageItemType(this.getItemType(item)) && typeof item.text === "string") {
            accumulatedText = item.text;
            this.logger.debug(
              `[codex-cli] Received assistant message, length: ${item.text.length}`
            );
            return;
          }
          const toolName = this.getToolName(item);
          if (!toolName) {
            return;
          }
          this.logger.debug(
            `[codex-cli] Tool detected: ${toolName}, item type: ${this.getItemType(item)}`
          );
          const mapKey = typeof item.id === "string" && item.id.length > 0 ? item.id : randomUUID();
          let toolState = activeTools.get(mapKey);
          const latestInput = this.buildToolInputPayload(item);
          if (!toolState) {
            toolState = {
              toolCallId: mapKey,
              toolName,
              inputPayload: latestInput,
              hasEmittedCall: false
            };
            activeTools.set(mapKey, toolState);
          } else {
            toolState.toolName = toolName;
            if (latestInput !== void 0) {
              toolState.inputPayload = latestInput;
            }
          }
          if (!toolState.hasEmittedCall) {
            this.logger.debug(`[codex-cli] Emitting tool invocation: ${toolState.toolName}`);
            this.emitToolInvocation(
              controller,
              toolState.toolCallId,
              toolState.toolName,
              toolState.inputPayload
            );
            toolState.hasEmittedCall = true;
          }
          if (event.type === "item.completed") {
            const { result, metadata } = this.buildToolResultPayload(item);
            this.logger.debug(`[codex-cli] Tool completed: ${toolState.toolName}`);
            this.emitToolResult(
              controller,
              toolState.toolCallId,
              toolState.toolName,
              item,
              result,
              metadata
            );
            activeTools.delete(mapKey);
          }
        };
        const onAbort = () => {
          child.kill("SIGTERM");
        };
        if (options.abortSignal) {
          if (options.abortSignal.aborted) {
            child.on("close", cleanupOwnedLastMessage);
            child.on("error", cleanupOwnedLastMessage);
            child.kill("SIGTERM");
            cleanupTempFiles();
            cleanupOwnedLastMessage();
            controller.error(options.abortSignal.reason ?? new Error("Request aborted"));
            return;
          }
          options.abortSignal.addEventListener("abort", onAbort, { once: true });
        }
        const finishStream = (code) => {
          const duration = Date.now() - startTime;
          if (code !== 0) {
            this.logger.error(
              `[codex-cli] Stream process exited with code ${code} after ${duration}ms`
            );
            controller.error(
              createAPICallError({
                message: `Codex CLI exited with code ${code}`,
                exitCode: code ?? void 0,
                stderr,
                promptExcerpt
              })
            );
            return;
          }
          if (turnFailureMessage) {
            this.logger.error(`[codex-cli] Stream failed: ${turnFailureMessage}`);
            controller.error(
              createAPICallError({
                message: turnFailureMessage,
                stderr,
                promptExcerpt
              })
            );
            return;
          }
          let finalText = accumulatedText;
          if (!finalText && lastMessagePath) {
            try {
              const fileText = readFileSync(lastMessagePath, "utf8");
              if (fileText) finalText = fileText.trim();
            } catch {
            }
          }
          if (finalText) {
            const textId = randomUUID();
            controller.enqueue({ type: "text-start", id: textId });
            controller.enqueue({ type: "text-delta", id: textId, delta: finalText });
            controller.enqueue({ type: "text-end", id: textId });
          }
          const usageSummary = lastUsage ?? createEmptyCodexUsage();
          const totalTokens = (usageSummary.inputTokens.total ?? 0) + (usageSummary.outputTokens.total ?? 0);
          this.logger.info(
            `[codex-cli] Stream completed - Session: ${this.sessionId ?? "N/A"}, Duration: ${duration}ms, Tokens: ${totalTokens}`
          );
          this.logger.debug(
            `[codex-cli] Token usage - Input: ${usageSummary.inputTokens.total ?? 0}, Output: ${usageSummary.outputTokens.total ?? 0}, Total: ${totalTokens}`
          );
          controller.enqueue({
            type: "finish",
            finishReason: mapCodexCliFinishReason(void 0),
            usage: usageSummary
          });
          controller.close();
        };
        const processLine = (line) => {
          const event = this.parseExperimentalJsonEvent(line);
          if (!event) return;
          this.logger.debug(`[codex-cli] Stream event: ${event.type ?? "unknown"}`);
          if (event.type === "thread.started" && typeof event.thread_id === "string") {
            this.sessionId = event.thread_id;
            this.logger.debug(`[codex-cli] Stream session started: ${this.sessionId}`);
            if (!responseMetadataSent) {
              responseMetadataSent = true;
              sendMetadata();
            }
            return;
          }
          if (event.type === "session.created" && typeof event.session_id === "string") {
            this.sessionId = event.session_id;
            this.logger.debug(`[codex-cli] Stream session created: ${this.sessionId}`);
            if (!responseMetadataSent) {
              responseMetadataSent = true;
              sendMetadata();
            }
            return;
          }
          if (event.type === "turn.completed") {
            const usageEvent = this.extractUsage(event);
            if (usageEvent) {
              lastUsage = usageEvent;
            }
            return;
          }
          if (event.type === "turn.failed") {
            const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
            turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
            this.logger.error(`[codex-cli] Stream turn failed: ${turnFailureMessage}`);
            sendMetadata({ error: turnFailureMessage });
            return;
          }
          if (event.type === "error") {
            const errorText = typeof event.message === "string" ? event.message : void 0;
            const effective = errorText ?? "Codex error";
            turnFailureMessage = turnFailureMessage ?? effective;
            this.logger.error(`[codex-cli] Stream error event: ${effective}`);
            sendMetadata({ error: effective });
            return;
          }
          if (event.type && event.type.startsWith("item.")) {
            handleItemEvent(event);
          }
        };
        child.stderr.on("data", (d) => stderr += String(d));
        child.stdout.setEncoding("utf8");
        child.stdout.on("data", (chunk) => {
          const pieces = (stdoutBuffer + chunk).split(/\r?\n/);
          stdoutBuffer = pieces.pop() ?? "";
          for (const line of pieces) {
            if (!line) continue;
            processLine(line);
          }
        });
        child.on("error", (e) => {
          this.logger.error(`[codex-cli] Stream spawn error: ${String(e)}`);
          if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
          cleanupTempFiles();
          cleanupOwnedLastMessage();
          controller.error(this.handleSpawnError(e, promptExcerpt));
        });
        child.on("close", (code) => {
          if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
          cleanupTempFiles();
          setImmediate(() => {
            try {
              if (stdoutBuffer) {
                processLine(stdoutBuffer);
                stdoutBuffer = "";
              }
              finishStream(code);
            } finally {
              cleanupOwnedLastMessage();
            }
          });
        });
      },
      cancel: () => {
      }
    });
    return { stream, request: { body: promptText } };
  }
};

// src/exec-provider.ts
function createCodexExec(options = {}) {
  const logger = getLogger(options.defaultSettings?.logger);
  if (options.defaultSettings) {
    const v = validateExecSettings(options.defaultSettings);
    if (!v.valid) {
      throw new Error(`Invalid default settings: ${v.errors.join(", ")}`);
    }
    for (const w of v.warnings) logger.warn(`Codex CLI Provider: ${w}`);
  }
  const createModel = (modelId, settings = {}) => {
    const merged = { ...options.defaultSettings, ...settings };
    const v = validateExecSettings(merged);
    if (!v.valid) throw new Error(`Invalid settings: ${v.errors.join(", ")}`);
    for (const w of v.warnings) logger.warn(`Codex CLI: ${w}`);
    return new ExecLanguageModel({ id: modelId, settings: merged });
  };
  const provider = Object.assign(
    function(modelId, settings) {
      if (new.target) throw new Error("The Codex CLI provider function cannot be called with new.");
      return createModel(modelId, settings);
    },
    { specificationVersion: "v4" }
  );
  provider.languageModel = createModel;
  provider.chat = createModel;
  provider.embeddingModel = ((modelId) => {
    throw new NoSuchModelError({ modelId, modelType: "embeddingModel" });
  });
  provider.imageModel = ((modelId) => {
    throw new NoSuchModelError({ modelId, modelType: "imageModel" });
  });
  return provider;
}
var codexExec = createCodexExec();

// src/app-server/session.ts
function toProtocolInput(input) {
  switch (input.type) {
    case "text":
      return { type: "text", text: input.text, text_elements: [] };
    case "image":
      return { type: "image", url: input.imageUrl, imageUrl: input.imageUrl };
    case "localImage":
      return { type: "localImage", path: input.path };
    default: {
      const neverInput = input;
      throw new Error(`Unsupported input type: ${String(neverInput)}`);
    }
  }
}
var AppServerSession = class {
  threadId;
  modelId;
  client;
  defaultTurnParams;
  requestHandlers;
  autoApprove;
  currentTurnId = null;
  active = false;
  constructor(options) {
    this.threadId = options.threadId;
    this.modelId = options.modelId;
    this.client = options.client;
    this.defaultTurnParams = options.defaultTurnParams ?? {};
    this.requestHandlers = options.requestHandlers ?? {};
    this.autoApprove = options.autoApprove;
  }
  get turnId() {
    return this.currentTurnId;
  }
  isTurnCompleted(turnId) {
    return this.client.hasTurnCompleted?.(turnId) ?? false;
  }
  refreshActiveState() {
    if (!this.active || !this.currentTurnId) {
      return;
    }
    if (this.isTurnCompleted(this.currentTurnId)) {
      this.active = false;
    }
  }
  isActive() {
    this.refreshActiveState();
    return this.active;
  }
  setTurnId(turnId) {
    this.currentTurnId = turnId;
    this.active = !this.isTurnCompleted(turnId);
  }
  setInactive(completedTurnId) {
    if (completedTurnId && this.currentTurnId && completedTurnId !== this.currentTurnId) {
      return;
    }
    this.active = false;
  }
  async injectMessage(content) {
    const inputs = typeof content === "string" ? [{ type: "text", text: content }] : content;
    if (inputs.length === 0) {
      return;
    }
    const protocolInputs = inputs.map(toProtocolInput);
    const result = await this.client.withThreadLock(this.threadId, async () => {
      const contextId = this.client.registerRequestContext(this.threadId, {
        handlers: this.requestHandlers,
        autoApprove: this.autoApprove
      });
      try {
        const turnStartResult = await this.client.turnStart({
          threadId: this.threadId,
          input: protocolInputs,
          model: this.modelId,
          ...this.defaultTurnParams
        });
        this.client.bindRequestContext(contextId, String(turnStartResult.turn.id));
        return turnStartResult;
      } catch (error) {
        this.client.clearRequestContext(contextId);
        throw error;
      }
    });
    const nextTurnId = String(result.turn.id);
    const alreadyCompleted = this.isTurnCompleted(nextTurnId);
    this.currentTurnId = nextTurnId;
    this.active = !alreadyCompleted;
  }
  async interrupt() {
    this.refreshActiveState();
    if (!this.active || !this.currentTurnId) {
      return;
    }
    const interruptedTurnId = this.currentTurnId;
    await this.client.turnInterrupt({
      threadId: this.threadId,
      turnId: interruptedTurnId
    });
    if (this.currentTurnId === interruptedTurnId) {
      this.active = false;
    }
  }
};

// src/app-server/stream/tool-tracker.ts
var ToolTracker = class {
  activeToolNames = /* @__PURE__ */ new Map();
  activeToolStarts = /* @__PURE__ */ new Map();
  stats = {
    totalCalls: 0,
    totalDurationMs: 0,
    byType: {
      exec: 0,
      patch: 0,
      mcp: 0,
      web_search: 0,
      other: 0
    }
  };
  start(toolCallId, tool2) {
    this.activeToolNames.set(toolCallId, tool2);
    this.activeToolStarts.set(toolCallId, Date.now());
  }
  get(toolCallId) {
    return this.activeToolNames.get(toolCallId);
  }
  resolve(toolCallId, fallback) {
    return this.activeToolNames.get(toolCallId) ?? fallback;
  }
  complete(toolCallId, fallback, durationMs) {
    const resolved = this.resolve(toolCallId, fallback);
    const startedAt = this.activeToolStarts.get(toolCallId);
    const fallbackDurationMs = startedAt !== void 0 ? Math.max(0, Date.now() - startedAt) : void 0;
    this.recordCompletion(resolved.toolName, durationMs ?? fallbackDurationMs);
    this.activeToolNames.delete(toolCallId);
    this.activeToolStarts.delete(toolCallId);
    return resolved;
  }
  getStats() {
    return {
      totalCalls: this.stats.totalCalls,
      totalDurationMs: this.stats.totalDurationMs,
      byType: { ...this.stats.byType }
    };
  }
  toolTypeFromName(toolName) {
    if (toolName === "exec") return "exec";
    if (toolName === "patch") return "patch";
    if (toolName === "web_search") return "web_search";
    if (toolName.startsWith("mcp__")) return "mcp";
    return "other";
  }
  recordCompletion(toolName, durationMs) {
    this.stats.totalCalls += 1;
    this.stats.byType[this.toolTypeFromName(toolName)] += 1;
    if (typeof durationMs === "number" && Number.isFinite(durationMs) && durationMs >= 0) {
      this.stats.totalDurationMs += durationMs;
    }
  }
};
function normalizeItemType(type) {
  return type.toLowerCase();
}
function mapTool(item) {
  const type = normalizeItemType(item.type);
  if (type === "commandexecution") {
    return { toolName: "exec", dynamic: true };
  }
  if (type === "filechange") {
    return { toolName: "patch", dynamic: true };
  }
  if (type === "mcptoolcall") {
    const server = typeof item.server === "string" ? item.server || "server" : "server";
    const tool2 = typeof item.tool === "string" ? item.tool || "tool" : "tool";
    return {
      toolName: `mcp__${server}__${tool2}`,
      dynamic: true
    };
  }
  if (type === "dynamictoolcall") {
    const tool2 = "tool" in item && typeof item.tool === "string" && item.tool ? item.tool : "tool";
    const namespace = "namespace" in item && typeof item.namespace === "string" && item.namespace ? item.namespace : void 0;
    return {
      toolName: namespace ? `${namespace}__${tool2}` : tool2,
      dynamic: true
    };
  }
  if (type === "websearch") {
    return { toolName: "web_search", dynamic: true };
  }
  return void 0;
}
function hasModelTokens(usage) {
  return usage.inputTokens > 0 || usage.outputTokens > 0;
}
function resolveTokenUsageIncrement(last, total, previousTotal) {
  if (!total || !previousTotal) {
    return hasModelTokens(last) ? last : void 0;
  }
  const delta = (current, previous) => Math.max(0, current - (previous ?? 0));
  const increment = {
    totalTokens: delta(total.totalTokens, previousTotal.totalTokens),
    inputTokens: delta(total.inputTokens, previousTotal.inputTokens),
    cachedInputTokens: delta(total.cachedInputTokens, previousTotal.cachedInputTokens),
    outputTokens: delta(total.outputTokens, previousTotal.outputTokens),
    reasoningOutputTokens: delta(total.reasoningOutputTokens, previousTotal.reasoningOutputTokens)
  };
  if (typeof total.cacheWriteInputTokens === "number") {
    increment.cacheWriteInputTokens = delta(
      total.cacheWriteInputTokens,
      previousTotal.cacheWriteInputTokens
    );
  }
  return hasModelTokens(increment) ? increment : void 0;
}
function createNotificationHandlers(context) {
  let previousTokenUsageTotal;
  let tokenUsageBaselineResolved = false;
  const handleReasoningDelta = (isSummary) => (params) => {
    if (!context.isSameTurn(params) || typeof params.delta !== "string") return;
    const itemId = typeof params.itemId === "string" ? params.itemId : generateId();
    context.reasoningItemIdsWithDelta.add(itemId);
    context.emitter.emitReasoningDelta(params.delta, isSummary, itemId);
  };
  const handleItemCompleted = (params) => {
    if (!context.isSameTurn(params)) return;
    if (!params.item || typeof params.item !== "object") return;
    const item = params.item;
    const type = normalizeItemType(item.type);
    if (type === "agentmessage") {
      const itemId = typeof item.id === "string" ? item.id : generateId();
      const text = item.text;
      if (!context.textItemIdsWithDelta.has(itemId) && typeof text === "string" && text.length > 0) {
        context.emitter.emitTextDelta(text, itemId);
      }
      return;
    }
    if (type === "reasoning") {
      const itemId = typeof item.id === "string" ? item.id : generateId();
      if (!context.reasoningItemIdsWithDelta.has(itemId)) {
        const summary = item.summary;
        const content = item.content;
        if (Array.isArray(summary) && summary.length > 0) {
          context.emitter.emitReasoningDelta(summary.join("\n"), true, itemId);
        }
        if (typeof summary === "string" && summary.length > 0) {
          context.emitter.emitReasoningDelta(summary, true, itemId);
        }
        if (Array.isArray(content) && content.length > 0) {
          context.emitter.emitReasoningDelta(content.join("\n"), false, itemId);
        }
        if (typeof content === "string" && content.length > 0) {
          context.emitter.emitReasoningDelta(content, false, itemId);
        }
      }
      return;
    }
    const tool2 = mapTool(item);
    if (!tool2) return;
    const toolCallId = typeof item.id === "string" ? item.id : generateId();
    const resolved = context.toolTracker.complete(
      toolCallId,
      tool2,
      typeof item.durationMs === "number" ? item.durationMs : void 0
    );
    context.emitter.emitToolResult(
      toolCallId,
      resolved.toolName,
      item,
      resolved.dynamic,
      item.status === "failed"
    );
  };
  const handleOutputDelta = (defaultToolName) => (params) => {
    if (!context.isSameTurn(params) || typeof params.delta !== "string") return;
    const itemId = typeof params.itemId === "string" ? params.itemId : generateId();
    const tracked = context.toolTracker.get(itemId);
    context.emitter.emitToolOutputDelta(
      itemId,
      tracked?.toolName ?? defaultToolName,
      params.delta,
      tracked?.dynamic ?? true
    );
  };
  return {
    "item/agentMessage/delta": (params) => {
      if (!context.isSameTurn(params) || typeof params.delta !== "string") return;
      const itemId = typeof params.itemId === "string" ? params.itemId : generateId();
      context.textItemIdsWithDelta.add(itemId);
      context.emitter.emitTextDelta(params.delta, itemId);
    },
    reasoningTextDelta: handleReasoningDelta(false),
    "item/reasoning/textDelta": handleReasoningDelta(false),
    reasoningSummaryTextDelta: handleReasoningDelta(true),
    "item/reasoning/summaryTextDelta": handleReasoningDelta(true),
    "item/started": (params) => {
      if (!context.isSameTurn(params)) return;
      if (!params.item || typeof params.item !== "object") return;
      const item = params.item;
      const tool2 = mapTool(item);
      if (!tool2) return;
      const toolCallId = typeof item.id === "string" ? item.id : generateId();
      context.toolTracker.start(toolCallId, tool2);
      context.emitter.emitToolCall(toolCallId, tool2.toolName, safeStringify(item), tool2.dynamic);
    },
    "item/completed": handleItemCompleted,
    "item/commandExecution/outputDelta": handleOutputDelta("exec"),
    "item/fileChange/outputDelta": handleOutputDelta("patch"),
    "thread/tokenUsage/updated": (params) => {
      if (!context.isSameTurn(params)) return;
      const event = params;
      const last = event.tokenUsage?.last;
      if (!last) return;
      const total = event.tokenUsage?.total;
      if (!tokenUsageBaselineResolved) {
        tokenUsageBaselineResolved = true;
        const turnId = context.getBoundTurnId() ?? event.turnId;
        previousTokenUsageTotal = typeof turnId === "string" ? context.getTokenUsageTotalBeforeTurn(turnId) : void 0;
      }
      const increment = resolveTokenUsageIncrement(last, total, previousTokenUsageTotal);
      if (total) previousTokenUsageTotal = total;
      if (!increment) return;
      const cacheWrite = typeof increment.cacheWriteInputTokens === "number" ? increment.cacheWriteInputTokens : void 0;
      context.onUsage({
        inputTokens: {
          total: increment.inputTokens,
          noCache: Math.max(
            0,
            increment.inputTokens - increment.cachedInputTokens - (cacheWrite ?? 0)
          ),
          cacheRead: increment.cachedInputTokens,
          cacheWrite
        },
        outputTokens: {
          total: increment.outputTokens,
          text: void 0,
          reasoning: increment.reasoningOutputTokens
        },
        raw: { ...increment }
      });
    },
    "turn/completed": (params) => {
      if (!params.turn || typeof params.turn !== "object") return;
      const turn = params.turn;
      const boundTurnId = context.getBoundTurnId();
      if (boundTurnId && turn.id !== boundTurnId) return;
      context.onTurnCompleted(turn);
    },
    error: (params) => {
      if (!context.isSameTurn(params)) return;
      if (params.willRetry === true) return;
      const nested = params.error;
      if (nested && typeof nested === "object" && typeof nested.message === "string") {
        context.onError(new Error(nested.message));
      }
    }
  };
}
function createServerRequestHandlers(context) {
  return {
    "item/commandExecution/requestApproval": (params) => {
      if (!context.isSameTurn(params)) return;
      const itemId = typeof params.itemId === "string" ? params.itemId : generateId();
      context.emitter.emitApprovalRequest(itemId);
    },
    "item/fileChange/requestApproval": (params) => {
      if (!context.isSameTurn(params)) return;
      const itemId = typeof params.itemId === "string" ? params.itemId : generateId();
      context.emitter.emitApprovalRequest(itemId);
    }
  };
}

// src/app-server/stream/router.ts
var AppServerNotificationRouter = class {
  client;
  emitter;
  threadId;
  onUsage;
  onThreadTurnCompleted;
  onTurnCompleted;
  onError;
  turnId;
  bufferedTurnScopedEvents = [];
  toolTracker = new ToolTracker();
  textItemIdsWithDelta = /* @__PURE__ */ new Set();
  reasoningItemIdsWithDelta = /* @__PURE__ */ new Set();
  notificationHandlers;
  serverRequestHandlers;
  notificationListener;
  serverRequestListener;
  constructor(options) {
    this.client = options.client;
    this.emitter = options.emitter;
    this.threadId = options.threadId;
    this.onUsage = options.onUsage;
    this.onThreadTurnCompleted = options.onThreadTurnCompleted;
    this.onTurnCompleted = options.onTurnCompleted;
    this.onError = options.onError;
    this.notificationHandlers = createNotificationHandlers({
      emitter: this.emitter,
      toolTracker: this.toolTracker,
      textItemIdsWithDelta: this.textItemIdsWithDelta,
      reasoningItemIdsWithDelta: this.reasoningItemIdsWithDelta,
      onUsage: this.onUsage,
      onTurnCompleted: this.onTurnCompleted,
      onError: this.onError,
      isSameTurn: (params) => this.isSameTurn(params),
      getBoundTurnId: () => this.turnId,
      getTokenUsageTotalBeforeTurn: (turnId) => (
        // Optional so lightweight client doubles without usage tracking still work.
        this.client.getTokenUsageTotalBeforeTurn?.(this.threadId, turnId)
      )
    });
    this.serverRequestHandlers = createServerRequestHandlers({
      emitter: this.emitter,
      isSameTurn: (params) => this.isSameTurn(params)
    });
  }
  setTurnId(turnId) {
    this.turnId = turnId;
    this.flushBufferedTurnScopedEvents();
  }
  getToolExecutionStats() {
    return this.toolTracker.getStats();
  }
  subscribe() {
    this.notificationListener = (method, params) => {
      if (!this.isSameThread(params)) return;
      if (method === "turn/completed" && params.turn && typeof params.turn === "object") {
        this.onThreadTurnCompleted?.(params.turn);
      }
      if (this.bufferTurnScopedEventBeforeBinding({ kind: "notification", method, params })) {
        return;
      }
      this.emitter.emitRaw(method, params);
      this.handleNotification(method, params);
    };
    this.client.on("notification", this.notificationListener);
    this.serverRequestListener = (method, params, id) => {
      if (!this.isSameThread(params)) return;
      if (this.bufferTurnScopedEventBeforeBinding({
        kind: "server-request",
        method,
        params,
        id
      })) {
        return;
      }
      this.emitter.emitRaw(method, params, id);
      this.handleServerRequest(method, params);
    };
    this.client.on("server-request", this.serverRequestListener);
    return () => this.unsubscribe();
  }
  unsubscribe() {
    if (this.notificationListener) {
      this.client.off("notification", this.notificationListener);
      this.notificationListener = void 0;
    }
    if (this.serverRequestListener) {
      this.client.off("server-request", this.serverRequestListener);
      this.serverRequestListener = void 0;
    }
    this.bufferedTurnScopedEvents = [];
  }
  isSameThread(params) {
    const notificationThreadId = typeof params.threadId === "string" ? params.threadId : void 0;
    return notificationThreadId === this.threadId;
  }
  getTurnIdFromParams(params) {
    const turnObject = params.turn && typeof params.turn === "object" ? params.turn : void 0;
    return typeof params.turnId === "string" ? params.turnId : typeof turnObject?.id === "string" ? turnObject.id : void 0;
  }
  bufferTurnScopedEventBeforeBinding(event) {
    if (this.turnId) return false;
    const turnIdInParams = this.getTurnIdFromParams(event.params);
    if (turnIdInParams === void 0) return false;
    this.bufferedTurnScopedEvents.push(event);
    return true;
  }
  flushBufferedTurnScopedEvents() {
    if (!this.turnId || this.bufferedTurnScopedEvents.length === 0) return;
    const buffered = this.bufferedTurnScopedEvents;
    this.bufferedTurnScopedEvents = [];
    for (const event of buffered) {
      if (!this.isSameThread(event.params)) {
        continue;
      }
      if (this.getTurnIdFromParams(event.params) !== this.turnId) {
        continue;
      }
      if (event.kind === "notification") {
        this.emitter.emitRaw(event.method, event.params);
        this.handleNotification(event.method, event.params);
      } else {
        this.emitter.emitRaw(event.method, event.params, event.id);
        this.handleServerRequest(event.method, event.params);
      }
    }
  }
  isSameTurn(params) {
    const turnIdInParams = this.getTurnIdFromParams(params);
    if (!this.turnId) {
      return turnIdInParams === void 0;
    }
    return turnIdInParams === void 0 || turnIdInParams === this.turnId;
  }
  handleNotification(method, params) {
    if (!this.isSameThread(params)) return;
    const handler = this.notificationHandlers[method];
    if (!handler) return;
    handler(params);
  }
  handleServerRequest(method, params) {
    if (!this.isSameThread(params)) return;
    const handler = this.serverRequestHandlers[method];
    if (!handler) return;
    handler(params);
  }
};
var AppServerStreamEmitter = class {
  constructor(controller, options) {
    this.controller = controller;
    this.options = options;
    this.jsonModeLastTextBlockOnly = Boolean(options.jsonModeLastTextBlockOnly);
  }
  controller;
  options;
  textId;
  reasoningId;
  jsonModeLastTextBlockOnly;
  bufferedCurrentJsonText = "";
  lastCompletedJsonTextId;
  lastCompletedJsonText = "";
  closed = false;
  safeEnqueue(part) {
    if (this.closed) return;
    try {
      this.controller.enqueue(part);
    } catch {
      this.closed = true;
    }
  }
  emitStreamStart(warnings) {
    this.safeEnqueue({ type: "stream-start", warnings });
  }
  emitResponseMetadata() {
    this.safeEnqueue({
      type: "response-metadata",
      id: generateId(),
      timestamp: /* @__PURE__ */ new Date(),
      modelId: this.options.modelId
    });
  }
  emitRaw(method, params, id) {
    if (!this.options.includeRawChunks) return;
    this.safeEnqueue({ type: "raw", rawValue: { method, params, id } });
  }
  emitTextDelta(delta, itemId) {
    if (this.jsonModeLastTextBlockOnly) {
      const nextTextId2 = itemId ?? this.textId ?? generateId();
      if (this.textId && this.textId !== nextTextId2) {
        this.lastCompletedJsonTextId = this.textId;
        this.lastCompletedJsonText = this.bufferedCurrentJsonText;
        this.textId = void 0;
        this.bufferedCurrentJsonText = "";
      }
      if (!this.textId) {
        this.textId = nextTextId2;
        this.bufferedCurrentJsonText = "";
      }
      this.bufferedCurrentJsonText = `${this.bufferedCurrentJsonText}${delta}`;
      return;
    }
    const nextTextId = itemId ?? this.textId ?? generateId();
    if (this.textId && this.textId !== nextTextId) {
      this.safeEnqueue({ type: "text-end", id: this.textId });
      this.textId = void 0;
    }
    if (!this.textId) {
      this.textId = nextTextId;
      this.safeEnqueue({ type: "text-start", id: this.textId });
    }
    this.safeEnqueue({ type: "text-delta", id: this.textId, delta });
  }
  emitReasoningDelta(delta, isSummary = false, itemId) {
    const nextReasoningId = itemId ?? this.reasoningId ?? generateId();
    if (this.reasoningId && this.reasoningId !== nextReasoningId) {
      this.safeEnqueue({ type: "reasoning-end", id: this.reasoningId });
      this.reasoningId = void 0;
    }
    if (!this.reasoningId) {
      this.reasoningId = nextReasoningId;
      this.safeEnqueue({ type: "reasoning-start", id: this.reasoningId });
    }
    this.safeEnqueue({
      type: "reasoning-delta",
      id: this.reasoningId,
      delta,
      ...isSummary ? {
        providerMetadata: {
          "codex-app-server": {
            isSummary: true
          }
        }
      } : {}
    });
  }
  emitToolCall(toolCallId, toolName, input, dynamic) {
    this.safeEnqueue({
      type: "tool-input-start",
      id: toolCallId,
      toolName,
      providerExecuted: true,
      ...dynamic ? { dynamic: true } : {}
    });
    if (input) {
      this.safeEnqueue({ type: "tool-input-delta", id: toolCallId, delta: input });
    }
    this.safeEnqueue({ type: "tool-input-end", id: toolCallId });
    this.safeEnqueue({
      type: "tool-call",
      toolCallId,
      toolName,
      input,
      providerExecuted: true,
      ...dynamic ? { dynamic: true } : {}
    });
  }
  emitToolOutputDelta(toolCallId, toolName, delta, dynamic) {
    this.safeEnqueue({
      type: "tool-result",
      toolCallId,
      toolName,
      preliminary: true,
      result: {
        type: "output-delta",
        delta
      },
      ...dynamic ? { dynamic: true } : {}
    });
  }
  emitToolResult(toolCallId, toolName, result, dynamic, isError) {
    this.safeEnqueue({
      type: "tool-result",
      toolCallId,
      toolName,
      result: result ?? {},
      ...dynamic ? { dynamic: true } : {},
      ...isError ? { isError: true } : {}
    });
  }
  emitApprovalRequest(approvalId) {
    this.safeEnqueue({
      type: "tool-approval-request",
      approvalId,
      toolCallId: approvalId
    });
  }
  emitFinish(finishReason, usage, providerMetadata) {
    if (this.jsonModeLastTextBlockOnly) {
      if (this.textId) {
        this.lastCompletedJsonTextId = this.textId;
        this.lastCompletedJsonText = this.bufferedCurrentJsonText;
        this.textId = void 0;
        this.bufferedCurrentJsonText = "";
      }
      const finalBlockId = this.lastCompletedJsonTextId;
      if (finalBlockId) {
        const finalText = this.lastCompletedJsonText;
        this.safeEnqueue({ type: "text-start", id: finalBlockId });
        if (finalText.length > 0) {
          this.safeEnqueue({ type: "text-delta", id: finalBlockId, delta: finalText });
        }
        this.safeEnqueue({ type: "text-end", id: finalBlockId });
      }
      this.lastCompletedJsonTextId = void 0;
      this.lastCompletedJsonText = "";
    } else if (this.textId) {
      this.safeEnqueue({ type: "text-end", id: this.textId });
    }
    if (this.reasoningId) {
      this.safeEnqueue({ type: "reasoning-end", id: this.reasoningId });
    }
    this.safeEnqueue({
      type: "finish",
      finishReason,
      usage,
      ...providerMetadata ? { providerMetadata } : {}
    });
  }
  close() {
    if (this.closed) return;
    this.closed = true;
    try {
      this.controller.close();
    } catch {
    }
  }
  error(error) {
    if (this.closed) return;
    this.closed = true;
    try {
      this.controller.error(error);
    } catch {
    }
  }
};

// src/app-server/stream/turn-stream-controller.ts
var INTERRUPT_COMPLETION_TIMEOUT_MS = 5e3;
function waitForPromiseOrTimeout(promise, timeoutMs) {
  return new Promise((resolve) => {
    let settled = false;
    const timer = setTimeout(() => {
      if (settled) return;
      settled = true;
      resolve(void 0);
    }, timeoutMs);
    promise.then((value) => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      resolve(value);
    }).catch(() => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      resolve(void 0);
    });
  });
}
function isThreadNotFoundError(error) {
  const message = String(error?.message ?? error);
  return /thread.*not found/i.test(message);
}
function createStaleThreadError(threadId) {
  return new Error(
    `Thread '${threadId}' not found after server restart. Create a new thread by omitting threadId.`
  );
}
function mapTurnStatusToFinishReason(turn) {
  switch (turn.status) {
    case "completed":
      return { unified: "stop", raw: "completed" };
    case "interrupted":
      return { unified: "stop", raw: "interrupted" };
    case "failed": {
      const errorInfo = turn.error?.codexErrorInfo;
      if (errorInfo === "contextWindowExceeded") {
        return { unified: "length", raw: "context_window_exceeded" };
      }
      if (errorInfo === "usageLimitExceeded") {
        return { unified: "length", raw: "usage_limit_exceeded" };
      }
      return { unified: "error", raw: turn.error?.message ?? "failed" };
    }
    default:
      return { unified: "other", raw: turn.status };
  }
}
var TurnStreamController = class {
  constructor(options) {
    this.options = options;
  }
  options;
  state = "created";
  usage = createEmptyCodexUsage();
  turnId;
  requestContextId;
  cleanedUp = false;
  pendingCancelReason;
  cancelBeforeTurnId = false;
  pendingAbortReason;
  interruptWaitPromise;
  cancelWaitPromise;
  settleTurn;
  turnCompletionPromise = new Promise(() => void 0);
  emitter;
  router;
  unsubscribeRouter;
  onAbort;
  isTerminalState() {
    return this.state === "closed" || this.state === "errored" || this.state === "finishing";
  }
  isClosedState() {
    return this.state === "closed";
  }
  async start(controller) {
    if (this.state !== "created") {
      return;
    }
    this.state = "starting";
    this.turnCompletionPromise = new Promise((resolve, reject) => {
      this.settleTurn = { resolve, reject };
    });
    this.emitter = new AppServerStreamEmitter(controller, {
      modelId: this.options.modelId,
      threadId: this.options.threadId,
      includeRawChunks: this.options.includeRawChunks,
      jsonModeLastTextBlockOnly: this.options.jsonModeLastTextBlockOnly
    });
    this.emitter.emitStreamStart(this.options.warnings);
    this.emitter.emitResponseMetadata();
    this.router = new AppServerNotificationRouter({
      client: this.options.client,
      emitter: this.emitter,
      threadId: this.options.threadId,
      // Codex emits one tokenUsage update per model response; a turn can make
      // many responses (tool calls, edits), so sum them for the whole call.
      // `tokenUsage.total` is thread-cumulative and would include prior turns.
      onUsage: (nextUsage) => {
        this.usage = addCodexUsage(this.usage, nextUsage);
      },
      onThreadTurnCompleted: (turn) => {
        this.options.session?.setInactive(turn.id);
      },
      onTurnCompleted: (turn) => {
        this.settleTurn?.resolve(turn);
      },
      onError: (error) => {
        this.settleTurn?.reject(error);
      }
    });
    this.unsubscribeRouter = this.router.subscribe();
    this.attachAbortSignal();
    if (this.pendingCancelReason !== void 0) {
      await this.requestCancel(this.pendingCancelReason);
      if (this.isClosedState()) return;
    }
    if (this.pendingAbortReason !== void 0) {
      await this.failWithError(this.pendingAbortReason);
      return;
    }
    try {
      this.state = "awaiting_turn_id";
      const turnResponse = this.options.shouldSerializeTurnStart ? await this.options.client.withThreadLock(
        this.options.threadId,
        async () => await this.startTurnWithContext()
      ) : await this.startTurnWithContext();
      this.turnId = turnResponse.turn.id;
      this.router.setTurnId(this.turnId);
      this.options.session?.setTurnId(this.turnId);
      if (this.isClosedState()) {
        if (this.pendingCancelReason !== void 0 && this.cancelBeforeTurnId) {
          await this.requestCancel(this.pendingCancelReason);
        }
        return;
      }
      this.state = "running";
      if (this.pendingCancelReason !== void 0) {
        await this.requestCancel(this.pendingCancelReason);
        return;
      }
      if (this.pendingAbortReason !== void 0) {
        await this.interruptAndAwaitCompletion();
        throw this.pendingAbortReason;
      }
      const turn = await this.turnCompletionPromise;
      if (this.pendingCancelReason !== void 0) {
        this.finishSilently();
        return;
      }
      if (this.pendingAbortReason !== void 0) {
        throw this.pendingAbortReason;
      }
      if (this.isTerminalState()) return;
      this.state = "finishing";
      const toolExecutionStats = this.router.getToolExecutionStats();
      this.emitter.emitFinish(mapTurnStatusToFinishReason(turn), this.usage, {
        "codex-app-server": {
          threadId: this.options.threadId,
          ...this.turnId ? { turnId: this.turnId } : {},
          toolExecutionStats
        }
      });
      this.emitter.close();
      this.cleanup();
      this.state = "closed";
    } catch (error) {
      if (this.pendingCancelReason !== void 0) {
        if (this.turnId) {
          await this.requestCancel(this.pendingCancelReason);
        } else {
          this.finishSilently();
        }
        return;
      }
      if (this.options.hadInitialThreadId && isThreadNotFoundError(error)) {
        if (this.options.threadResolution.persistent && !this.options.threadResolution.explicit) {
          this.options.clearPersistentThreadState(this.options.threadId);
        }
        await this.failWithError(createStaleThreadError(this.options.threadId));
        return;
      }
      if (this.pendingAbortReason !== void 0 && this.turnId) {
        await this.interruptAndAwaitCompletion();
      }
      await this.failWithError(error);
    }
  }
  async cancel(reason) {
    this.pendingCancelReason = reason ?? this.pendingCancelReason ?? new Error("Stream canceled");
    switch (this.state) {
      case "closed":
      case "errored":
      case "finishing":
        return;
      case "interrupting":
        await this.cancelWaitPromise;
        return;
      default:
        await this.requestCancel(reason);
    }
  }
  async startTurnWithContext() {
    this.requestContextId = this.options.client.registerRequestContext(this.options.threadId, {
      handlers: this.options.requestHandlers ?? {},
      autoApprove: this.options.autoApprove
    });
    try {
      const turnResponse = await this.options.client.turnStart(this.options.turnStartParams);
      this.options.client.bindRequestContext(this.requestContextId, turnResponse.turn.id);
      return turnResponse;
    } catch (error) {
      if (this.requestContextId) {
        this.options.client.clearRequestContext(this.requestContextId);
        this.requestContextId = void 0;
      }
      throw error;
    }
  }
  attachAbortSignal() {
    const signal = this.options.abortSignal;
    if (!signal) {
      return;
    }
    if (signal.aborted) {
      this.pendingAbortReason = signal.reason ?? new Error("Request aborted");
      return;
    }
    this.onAbort = () => {
      this.pendingAbortReason = signal.reason ?? new Error("Request aborted");
      if (!this.turnId) return;
      void (async () => {
        await this.interruptAndAwaitCompletion();
        await this.failWithError(this.pendingAbortReason);
      })();
    };
    signal.addEventListener("abort", this.onAbort, { once: true });
  }
  async requestCancel(reason) {
    this.pendingCancelReason = reason ?? this.pendingCancelReason ?? new Error("Stream canceled");
    if (!this.turnId) {
      this.cancelBeforeTurnId = this.state === "starting" || this.state === "awaiting_turn_id";
      this.finishSilently();
      return;
    }
    if (this.isTerminalState() && !this.cancelBeforeTurnId) return;
    if (!this.cancelWaitPromise) {
      this.cancelWaitPromise = (async () => {
        this.state = "interrupting";
        if (this.cancelBeforeTurnId) {
          await this.options.client.turnInterrupt({ threadId: this.options.threadId, turnId: this.turnId }).catch(() => void 0);
          this.cancelBeforeTurnId = false;
        } else {
          await this.interruptAndAwaitCompletion();
        }
        this.finishSilently();
      })();
    }
    await this.cancelWaitPromise;
  }
  async interruptAndAwaitCompletion() {
    if (!this.turnId) return;
    if (!this.interruptWaitPromise) {
      this.interruptWaitPromise = (async () => {
        await this.options.client.turnInterrupt({ threadId: this.options.threadId, turnId: this.turnId }).catch(() => void 0);
        await waitForPromiseOrTimeout(
          this.turnCompletionPromise.then(() => void 0),
          INTERRUPT_COMPLETION_TIMEOUT_MS
        );
      })();
    }
    await this.interruptWaitPromise;
  }
  async failWithError(error) {
    if (this.isTerminalState()) return;
    this.state = "errored";
    this.emitter?.error(error);
    this.cleanup();
  }
  finishSilently() {
    if (this.isTerminalState()) return;
    this.cleanup();
    this.state = "closed";
  }
  cleanup() {
    if (this.cleanedUp) return;
    this.cleanedUp = true;
    this.unsubscribeRouter?.();
    this.unsubscribeRouter = void 0;
    if (this.turnId) {
      this.options.client.clearRequestContextForTurn(this.turnId);
    }
    if (this.requestContextId) {
      this.options.client.clearRequestContext(this.requestContextId);
      this.requestContextId = void 0;
    }
    if (this.options.abortSignal && this.onAbort) {
      this.options.abortSignal.removeEventListener("abort", this.onAbort);
    }
    this.onAbort = void 0;
    this.options.releaseResources();
  }
};
function buildTurnStartParams(args) {
  return {
    threadId: args.threadId,
    input: args.input,
    cwd: args.settings.cwd,
    approvalPolicy: args.settings.approvalPolicy,
    sandboxPolicy: args.settings.sandboxPolicy,
    model: args.modelId,
    effort: args.settings.effort,
    summary: args.settings.summary,
    personality: args.settings.personality,
    serviceTier: args.settings.serviceTier,
    serviceTierForTurn: args.settings.serviceTierForTurn,
    ...args.responseFormat?.type === "json" && args.responseFormat.schema ? { outputSchema: sanitizeJsonSchema(args.responseFormat.schema) } : {}
  };
}
var MAX_REQUEST_BODY_BYTES = 1024 * 1024;
function normalizeHost(host) {
  const trimmed = host.trim();
  const unwrapped = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
  return unwrapped.toLowerCase();
}
function isLoopbackHost(host) {
  const normalized = normalizeHost(host);
  return normalized === "localhost" || normalized === "::1" || normalized === "0:0:0:0:0:0:0:1" || normalized.startsWith("127.");
}
function formatHttpUrlHost(host) {
  const normalized = normalizeHost(host);
  return normalized.includes(":") ? `[${normalized}]` : normalized;
}
function serializeToolResultToText(result) {
  if (typeof result === "string") {
    return result;
  }
  const json = JSON.stringify(result);
  if (json !== void 0) {
    return json;
  }
  return String(result);
}
async function createLocalMcpServer(options) {
  const { name, tools, port = 0, host = "127.0.0.1", allowNonLoopbackHost = false } = options;
  if (!isLoopbackHost(host) && !allowNonLoopbackHost) {
    throw new Error(
      `Refusing to bind local MCP server to non-loopback host '${host}'. Set allowNonLoopbackHost: true to override.`
    );
  }
  const toolMap = new Map(tools.map((tool2) => [tool2.name, tool2]));
  const bearerToken = randomBytes(32).toString("hex");
  const expectedAuthorizationHeader = `Bearer ${bearerToken}`;
  const handleRpcRequest = async (request) => {
    const id = request.id;
    if (request.method === "initialize") {
      return {
        jsonrpc: "2.0",
        id,
        result: {
          protocolVersion: "2024-11-05",
          capabilities: { tools: {} },
          serverInfo: { name, version: "1.0.0" }
        }
      };
    }
    if (request.method === "tools/list") {
      return {
        jsonrpc: "2.0",
        id,
        result: {
          tools: tools.map((tool2) => ({
            name: tool2.name,
            description: tool2.description,
            inputSchema: tool2.inputSchema
          }))
        }
      };
    }
    if (request.method === "tools/call") {
      const toolName = typeof request.params?.name === "string" ? request.params.name : void 0;
      const toolArgs = request.params?.arguments;
      if (!toolName || !toolMap.has(toolName)) {
        return {
          jsonrpc: "2.0",
          id,
          error: { code: -32602, message: `Unknown tool: ${String(toolName)}` }
        };
      }
      const tool2 = toolMap.get(toolName);
      try {
        const result = await tool2.execute(toolArgs);
        return {
          jsonrpc: "2.0",
          id,
          result: {
            content: [{ type: "text", text: serializeToolResultToText(result) }]
          }
        };
      } catch (error) {
        return {
          jsonrpc: "2.0",
          id,
          error: {
            code: -32603,
            message: error instanceof Error ? error.message : String(error)
          }
        };
      }
    }
    if (request.method === "notifications/initialized") {
      return {
        jsonrpc: "2.0",
        id,
        result: {}
      };
    }
    return {
      jsonrpc: "2.0",
      id,
      error: {
        code: -32601,
        message: `Method not found: ${request.method}`
      }
    };
  };
  const httpHandler = (req, res) => {
    void (async () => {
      if (req.method !== "POST") {
        res.writeHead(405, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ error: "Method not allowed" }));
        return;
      }
      const contentType = req.headers["content-type"];
      if (typeof contentType !== "string" || !contentType.includes("application/json")) {
        res.writeHead(415, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ error: "Unsupported media type" }));
        return;
      }
      const authorization = req.headers.authorization;
      if (authorization !== expectedAuthorizationHeader) {
        res.writeHead(401, {
          "Content-Type": "application/json",
          "WWW-Authenticate": "Bearer"
        });
        res.end(JSON.stringify({ error: "Unauthorized" }));
        return;
      }
      const chunks = [];
      let bodyBytes = 0;
      for await (const chunk of req) {
        const buffer = chunk;
        bodyBytes += buffer.length;
        if (bodyBytes > MAX_REQUEST_BODY_BYTES) {
          res.writeHead(413, { "Content-Type": "application/json" });
          res.end(JSON.stringify({ error: "Request body too large" }));
          return;
        }
        chunks.push(buffer);
      }
      let payload;
      try {
        payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
      } catch {
        res.writeHead(400, { "Content-Type": "application/json" });
        res.end(
          JSON.stringify({ jsonrpc: "2.0", error: { code: -32700, message: "Parse error" } })
        );
        return;
      }
      if (payload.id === void 0) {
        res.writeHead(202, { "Content-Type": "application/json" });
        res.end();
        return;
      }
      const rpcResponse = await handleRpcRequest(payload);
      res.writeHead(200, {
        "Content-Type": "application/json"
      });
      res.end(JSON.stringify(rpcResponse));
    })();
  };
  const server = createServer(httpHandler);
  await new Promise((resolve, reject) => {
    server.once("error", reject);
    server.listen(port, host, () => {
      server.off("error", reject);
      resolve();
    });
  });
  const address = server.address();
  if (!address || typeof address === "string") {
    throw new Error("Failed to resolve local MCP server address");
  }
  const actualPort = address.port;
  const url = `http://${formatHttpUrlHost(host)}:${actualPort}`;
  return {
    config: { transport: "http", url, bearerToken },
    url,
    port: actualPort,
    stop: async () => {
      await new Promise((resolve, reject) => {
        server.close((error) => {
          if (error) {
            reject(error);
            return;
          }
          resolve();
        });
      });
    }
  };
}

// src/tools/sdk-mcp-server.ts
var SDK_MCP_SERVER_MARKER = /* @__PURE__ */ Symbol.for("ai-sdk-provider-codex-cli.sdkMcpServer");
function createSdkMcpServer(options) {
  const { name, cacheKey, tools } = options;
  let server;
  let startPromise;
  let stopPromise;
  return {
    [SDK_MCP_SERVER_MARKER]: true,
    name,
    cacheKey: cacheKey?.trim() || void 0,
    tools,
    get _server() {
      return server;
    },
    set _server(nextServer) {
      server = nextServer;
    },
    async _start() {
      while (true) {
        if (server) {
          return server.config;
        }
        if (startPromise) {
          const started = await startPromise;
          return started.config;
        }
        if (stopPromise) {
          await stopPromise;
          continue;
        }
        const startup = (async () => {
          const created = await createLocalMcpServer({ name, tools });
          server = created;
          return created;
        })();
        startPromise = startup;
        try {
          const started = await startup;
          return started.config;
        } finally {
          if (startPromise === startup) {
            startPromise = void 0;
          }
        }
      }
    },
    async _stop() {
      if (stopPromise) {
        await stopPromise;
        return;
      }
      const stopping = (async () => {
        if (startPromise) {
          await startPromise.catch(() => void 0);
        }
        const serverToStop = server;
        server = void 0;
        if (serverToStop) {
          await serverToStop.stop();
        }
      })();
      stopPromise = stopping;
      try {
        await stopping;
      } finally {
        if (stopPromise === stopping) {
          stopPromise = void 0;
        }
        startPromise = void 0;
      }
    }
  };
}
function isSdkMcpServer(value) {
  const marker = typeof value === "object" && value !== null ? value[SDK_MCP_SERVER_MARKER] : void 0;
  return typeof value === "object" && value !== null && marker === true && typeof value._start === "function" && typeof value._stop === "function";
}

// src/app-server/language-model.ts
var CODEX_REASONING_EFFORTS2 = {
  none: true,
  minimal: true,
  low: true,
  medium: true,
  high: true,
  xhigh: true,
  max: true,
  ultra: true
};
function resolveReasoningEffort(args) {
  if (args.providerEffort !== void 0) {
    return { effort: args.providerEffort };
  }
  const { reasoning } = args;
  if (reasoning === void 0 || reasoning === "provider-default") {
    return { effort: args.defaultEffort };
  }
  if (Object.hasOwn(CODEX_REASONING_EFFORTS2, reasoning)) {
    return { effort: reasoning };
  }
  return {
    effort: args.defaultEffort,
    warning: {
      type: "unsupported",
      feature: "reasoning",
      details: `Codex app-server does not support reasoning effort '${reasoning}'; it will be ignored.`
    }
  };
}
function isThreadNotFoundError2(error) {
  const message = String(error?.message ?? error);
  return /thread.*not found/i.test(message);
}
function createStaleThreadError2(threadId) {
  return new Error(
    `Thread '${threadId}' not found after server restart. Create a new thread by omitting threadId.`
  );
}
function mapSandboxToThreadSandboxMode(settings) {
  const policy = settings.sandboxPolicy;
  if (!policy) return void 0;
  if (typeof policy === "string") return policy;
  if (policy.type === "readOnly") return "read-only";
  if (policy.type === "workspaceWrite") return "workspace-write";
  if (policy.type === "dangerFullAccess") return "danger-full-access";
  return void 0;
}
function mapSandboxToTurnSandboxPolicy(settings) {
  const policy = settings.sandboxPolicy;
  if (!policy) return void 0;
  if (typeof policy !== "string") return policy;
  if (policy === "read-only") return { type: "readOnly" };
  if (policy === "workspace-write") return { type: "workspaceWrite" };
  if (policy === "danger-full-access") return { type: "dangerFullAccess" };
  return void 0;
}
function mapApprovalPolicy(policy, warnDeprecated) {
  if (policy === void 0) return void 0;
  if (isDeprecatedApprovalPolicyAlias(policy)) {
    const normalized = normalizeApprovalPolicyAlias(policy);
    warnDeprecated?.(
      `approvalPolicy:${policy}`,
      `approvalPolicy '${policy}' is deprecated (retired by Codex CLI 0.143 and rejected by app-server >= 0.144); sending '${normalized}' instead.`
    );
    return normalized;
  }
  if (typeof policy === "object" && policy !== null && "reject" in policy) {
    const { reject } = policy;
    warnDeprecated?.(
      "approvalPolicy:reject",
      "approvalPolicy { reject } is deprecated (removed from the Codex app-server protocol); sending the equivalent { granular } policy instead."
    );
    return {
      granular: {
        sandbox_approval: !reject.sandbox_approval,
        rules: !reject.rules,
        mcp_elicitations: !reject.mcp_elicitations,
        skill_approval: true,
        request_permissions: true
      }
    };
  }
  return policy;
}
function mergeServerRequests(base, override) {
  if (!base && !override) return void 0;
  return {
    ...base ?? {},
    ...override ?? {}
  };
}
function mergeAppServerMcpServers(base, override) {
  if (!base && !override) return void 0;
  const merged = {};
  for (const [rawName, server] of Object.entries(base ?? {})) {
    const name = assertValidMcpServerName(rawName);
    merged[name] = server;
  }
  if (!override) return merged;
  for (const [rawName, incoming] of Object.entries(override)) {
    const name = assertValidMcpServerName(rawName);
    const existing = merged[name];
    if (!existing || isSdkMcpServer(existing) || isSdkMcpServer(incoming)) {
      merged[name] = incoming;
      continue;
    }
    if (existing.transport === incoming.transport) {
      merged[name] = mergeSingleMcpServer(existing, incoming);
    } else {
      merged[name] = incoming;
    }
  }
  return merged;
}
var AppServerLanguageModel = class {
  specificationVersion = "v4";
  provider = "codex-app-server";
  supportedUrls = {};
  modelId;
  settings;
  client;
  logger;
  onSdkMcpServerUsed;
  onSdkMcpServerReleased;
  persistentThreadId;
  persistentThreadRawEventsEnabled;
  rawEventsByThreadId = /* @__PURE__ */ new Map();
  persistentSession;
  persistentBootstrapLock = Promise.resolve();
  deprecationWarningsEmitted = /* @__PURE__ */ new Set();
  constructor(options) {
    this.modelId = options.id;
    this.settings = options.settings ?? {};
    this.client = options.client;
    this.onSdkMcpServerUsed = options.onSdkMcpServerUsed;
    this.onSdkMcpServerReleased = options.onSdkMcpServerReleased;
    const baseLogger = getLogger(this.settings.logger);
    this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
    if (!this.modelId || this.modelId.trim() === "") {
      throw new NoSuchModelError({ modelId: this.modelId, modelType: "languageModel" });
    }
  }
  warnDeprecatedOnce(key, message) {
    if (this.deprecationWarningsEmitted.has(key)) return;
    this.deprecationWarningsEmitted.add(key);
    this.logger.warn(`[codex-app-server] ${message}`);
  }
  mapApprovalPolicy(settings) {
    return mapApprovalPolicy(
      settings.approvalPolicy,
      (key, message) => this.warnDeprecatedOnce(key, message)
    );
  }
  mergeSettings(providerOptions) {
    if (!providerOptions) return this.settings;
    const merged = {
      ...this.settings,
      personality: providerOptions.personality ?? this.settings.personality,
      effort: providerOptions.effort ?? this.settings.effort,
      summary: providerOptions.summary ?? this.settings.summary,
      // serviceTier is deliberately not merged: the model-level tier is the thread
      // default, and a per-call providerOptions.serviceTier is sent only as
      // turn/start `serviceTierForTurn` so it never outlives the call.
      approvalPolicy: providerOptions.approvalPolicy ?? this.settings.approvalPolicy,
      sandboxPolicy: providerOptions.sandboxPolicy ?? this.settings.sandboxPolicy,
      baseInstructions: providerOptions.baseInstructions ?? this.settings.baseInstructions,
      developerInstructions: providerOptions.developerInstructions ?? this.settings.developerInstructions,
      autoApprove: providerOptions.autoApprove ?? this.settings.autoApprove,
      persistExtendedHistory: providerOptions.persistExtendedHistory ?? this.settings.persistExtendedHistory,
      threadMode: providerOptions.threadMode ?? this.settings.threadMode,
      resume: providerOptions.resume ?? this.settings.resume,
      includeRawChunks: providerOptions.includeRawChunks ?? this.settings.includeRawChunks,
      rmcpClient: providerOptions.rmcpClient ?? this.settings.rmcpClient,
      configOverrides: {
        ...this.settings.configOverrides ?? {},
        ...providerOptions.configOverrides ?? {}
      },
      serverRequests: mergeServerRequests(
        this.settings.serverRequests,
        providerOptions.serverRequests
      ),
      onSessionCreated: providerOptions.onSessionCreated ?? this.settings.onSessionCreated
    };
    merged.mcpServers = mergeAppServerMcpServers(
      this.settings.mcpServers,
      providerOptions.mcpServers
    );
    return merged;
  }
  resolveThreadMode(settings, providerOptions) {
    return providerOptions?.threadMode ?? settings.threadMode ?? "stateless";
  }
  resolveTargetThreadId(settings, providerOptions) {
    const mode = this.resolveThreadMode(settings, providerOptions);
    const explicit = providerOptions?.threadId ?? providerOptions?.resume ?? settings.resume;
    if (explicit) {
      return {
        threadId: explicit,
        explicit: true,
        persistent: mode === "persistent"
      };
    }
    if (mode === "persistent" && this.persistentThreadId) {
      return {
        threadId: this.persistentThreadId,
        explicit: false,
        persistent: true
      };
    }
    return {
      threadId: void 0,
      explicit: false,
      persistent: mode === "persistent"
    };
  }
  resolveIncludeRawChunks(optionsIncludeRawChunks, settings, providerOptions) {
    return (optionsIncludeRawChunks ?? providerOptions?.includeRawChunks ?? settings.includeRawChunks) === true;
  }
  async resolveConfig(settings, sdkServerLifecycle) {
    const resolvedMcpServers = {};
    const usedSdkMcpServers = [];
    try {
      for (const [name, server] of Object.entries(settings.mcpServers ?? {})) {
        if (isSdkMcpServer(server)) {
          const started = await server._start();
          this.onSdkMcpServerUsed?.(server, sdkServerLifecycle);
          usedSdkMcpServers.push(server);
          resolvedMcpServers[name] = started;
          continue;
        }
        resolvedMcpServers[name] = server;
      }
    } catch (error) {
      if (sdkServerLifecycle === "request") {
        for (const server of usedSdkMcpServers) {
          this.onSdkMcpServerReleased?.(server);
        }
      }
      throw error;
    }
    const mcpOverrides = mcpServersToConfigOverrides(
      Object.keys(resolvedMcpServers).length > 0 ? resolvedMcpServers : void 0,
      settings.rmcpClient
    );
    const configOverrides = {
      ...mcpOverrides,
      ...settings.configOverrides ?? {}
    };
    return {
      configOverrides: Object.keys(configOverrides).length > 0 ? configOverrides : void 0,
      usedSdkMcpServers
    };
  }
  async buildUserInput(text, images) {
    const input = [];
    const tempImagePaths = [];
    if (text.trim().length > 0) {
      input.push({ type: "text", text, text_elements: [] });
    }
    for (const image of images) {
      if (image.type === "remote") {
        input.push({ type: "image", url: image.url, imageUrl: image.url });
        continue;
      }
      try {
        const tempPath = writeImageToTempFile(image.data);
        tempImagePaths.push(tempPath);
        input.push({ type: "localImage", path: tempPath });
      } catch (error) {
        this.logger.warn(`[codex-app-server] Failed to write image to temp file: ${String(error)}`);
      }
    }
    return { input, tempImagePaths };
  }
  async startOrResumeThread(args) {
    const {
      settings,
      providerOptions,
      configOverrides,
      developerInstructionsOverride,
      systemInstruction,
      includeRawChunks
    } = args;
    const threadState = this.resolveTargetThreadId(settings, providerOptions);
    const startThread = async (ephemeral) => {
      const thread = await this.client.threadStart({
        model: this.modelId,
        cwd: settings.cwd,
        approvalPolicy: this.mapApprovalPolicy(settings),
        sandbox: mapSandboxToThreadSandboxMode(settings),
        config: configOverrides,
        baseInstructions: settings.baseInstructions,
        developerInstructions: developerInstructionsOverride ?? systemInstruction,
        personality: settings.personality,
        serviceTier: settings.serviceTier,
        ephemeral,
        experimentalRawEvents: includeRawChunks,
        persistExtendedHistory: settings.persistExtendedHistory ?? false
      });
      return thread.thread.id;
    };
    const resolveThread = async () => {
      const resumeThread = async (target) => {
        try {
          await this.client.threadResume({
            threadId: target.threadId,
            model: this.modelId,
            cwd: settings.cwd,
            approvalPolicy: this.mapApprovalPolicy(settings),
            sandbox: mapSandboxToThreadSandboxMode(settings),
            config: configOverrides,
            baseInstructions: settings.baseInstructions,
            developerInstructions: developerInstructionsOverride ?? systemInstruction,
            personality: settings.personality,
            serviceTier: settings.serviceTier,
            persistExtendedHistory: settings.persistExtendedHistory ?? false
          });
          const cachedRawEvents = this.rawEventsByThreadId.get(target.threadId);
          const knownRawEvents = cachedRawEvents ?? (target.persistent && this.persistentThreadId === target.threadId ? this.persistentThreadRawEventsEnabled : void 0);
          if (cachedRawEvents !== void 0) {
            this.rememberThreadRawEvents(target.threadId, cachedRawEvents);
          }
          if (target.persistent) {
            this.persistentThreadId = target.threadId;
            this.persistentThreadRawEventsEnabled = knownRawEvents;
          }
          return {
            threadId: target.threadId,
            persistent: target.persistent,
            explicit: target.explicit,
            resumed: true,
            rawEventsNegotiated: knownRawEvents
          };
        } catch (error) {
          if (!isThreadNotFoundError2(error)) {
            throw error;
          }
          if (target.persistent && !target.explicit) {
            this.clearPersistentThreadState(target.threadId);
          }
          throw createStaleThreadError2(target.threadId);
        }
      };
      if (!threadState.threadId && threadState.persistent) {
        if (this.persistentThreadId) {
          return await resumeThread({
            threadId: this.persistentThreadId,
            persistent: true,
            explicit: false
          });
        }
        const newThreadId = await startThread(false);
        this.rememberThreadRawEvents(newThreadId, includeRawChunks);
        this.persistentThreadId = newThreadId;
        this.persistentThreadRawEventsEnabled = includeRawChunks;
        return {
          threadId: newThreadId,
          persistent: true,
          explicit: false,
          resumed: false,
          rawEventsNegotiated: includeRawChunks
        };
      }
      if (!threadState.threadId) {
        const newThreadId = await startThread(!threadState.persistent);
        this.rememberThreadRawEvents(newThreadId, includeRawChunks);
        if (threadState.persistent) {
          this.persistentThreadId = newThreadId;
          this.persistentThreadRawEventsEnabled = includeRawChunks;
        }
        return {
          threadId: newThreadId,
          persistent: threadState.persistent,
          explicit: false,
          resumed: false,
          rawEventsNegotiated: includeRawChunks
        };
      }
      return await resumeThread({
        threadId: threadState.threadId,
        persistent: threadState.persistent,
        explicit: threadState.explicit
      });
    };
    if (threadState.persistent && !threadState.explicit) {
      return await this.withPersistentBootstrapLock(resolveThread);
    }
    return await resolveThread();
  }
  async withPersistentBootstrapLock(fn) {
    const previous = this.persistentBootstrapLock;
    let release;
    const gate = new Promise((resolve) => {
      release = resolve;
    });
    const chained = previous.then(() => gate);
    this.persistentBootstrapLock = chained;
    await previous;
    try {
      return await fn();
    } finally {
      release?.();
      if (this.persistentBootstrapLock === chained) {
        this.persistentBootstrapLock = Promise.resolve();
      }
    }
  }
  rememberThreadRawEvents(threadId, enabled) {
    this.rawEventsByThreadId.delete(threadId);
    this.rawEventsByThreadId.set(threadId, enabled);
    if (this.rawEventsByThreadId.size <= 256) {
      return;
    }
    const oldestThreadId = this.rawEventsByThreadId.keys().next().value;
    if (typeof oldestThreadId === "string") {
      this.rawEventsByThreadId.delete(oldestThreadId);
    }
  }
  clearPersistentThreadState(threadId) {
    if (threadId && this.persistentThreadId && this.persistentThreadId !== threadId) {
      return;
    }
    this.persistentThreadId = void 0;
    this.persistentThreadRawEventsEnabled = void 0;
    if (threadId) {
      this.rawEventsByThreadId.delete(threadId);
    }
    if (!threadId || this.persistentSession?.threadId === threadId) {
      this.persistentSession = void 0;
    }
  }
  async createOrReuseSession(args) {
    const { threadId, settings, providerOptions } = args;
    const onSessionCreated = providerOptions?.onSessionCreated ?? settings.onSessionCreated;
    if (!onSessionCreated) {
      return void 0;
    }
    const persistent = this.resolveThreadMode(settings, providerOptions) === "persistent";
    if (persistent && this.persistentSession && this.persistentSession.threadId === threadId) {
      return this.persistentSession;
    }
    const session = new AppServerSession({
      threadId,
      modelId: this.modelId,
      client: this.client,
      defaultTurnParams: {
        cwd: settings.cwd,
        approvalPolicy: this.mapApprovalPolicy(settings),
        sandboxPolicy: mapSandboxToTurnSandboxPolicy(settings),
        effort: settings.effort,
        summary: settings.summary,
        personality: settings.personality,
        serviceTier: settings.serviceTier
      },
      requestHandlers: settings.serverRequests ?? {},
      autoApprove: settings.autoApprove
    });
    await onSessionCreated(session);
    if (persistent) {
      this.persistentSession = session;
    }
    return session;
  }
  preparePrompt(prompt, hasExistingThreadContext) {
    const converted = convertPromptToCodexInput({
      prompt,
      mode: hasExistingThreadContext ? "persistent" : "stateless"
    });
    return {
      promptText: converted.text,
      images: [
        ...converted.localImages.map((image) => ({ type: "local", data: image })),
        ...converted.remoteImageUrls.map((url) => ({ type: "remote", url }))
      ],
      warnings: converted.warnings.map(
        (warning) => warning.type === "unsupported" ? {
          type: "unsupported",
          feature: warning.feature,
          details: warning.details
        } : {
          type: "other",
          message: warning.message
        }
      ),
      systemInstruction: converted.systemInstruction
    };
  }
  async doGenerate(options) {
    const { stream, request } = await this.doStream(
      options
    );
    const content = [];
    const textPartsById = /* @__PURE__ */ new Map();
    const reasoningPartsById = /* @__PURE__ */ new Map();
    let activeTextBlockId;
    let activeReasoningBlockId;
    let responseMetadata = {
      id: generateId(),
      timestamp: /* @__PURE__ */ new Date(),
      modelId: this.modelId
    };
    let usage = createEmptyCodexUsage();
    let finishReason = { unified: "other", raw: void 0 };
    let warnings = [];
    let providerMetadata;
    const ensureTextPart = (id, metadata) => {
      const existing = textPartsById.get(id);
      if (existing) {
        if (metadata) existing.providerMetadata = metadata;
        return existing;
      }
      const part = {
        type: "text",
        text: "",
        ...metadata ? { providerMetadata: metadata } : {}
      };
      textPartsById.set(id, part);
      content.push(part);
      return part;
    };
    const ensureReasoningPart = (id, metadata) => {
      const existing = reasoningPartsById.get(id);
      if (existing) {
        if (metadata) existing.providerMetadata = metadata;
        return existing;
      }
      const part = {
        type: "reasoning",
        text: "",
        ...metadata ? { providerMetadata: metadata } : {}
      };
      reasoningPartsById.set(id, part);
      content.push(part);
      return part;
    };
    const pushContentPart = (part) => {
      content.push(part);
    };
    for await (const part of stream) {
      if (part.type === "stream-start") {
        warnings = part.warnings;
        continue;
      }
      if (part.type === "response-metadata") {
        responseMetadata = {
          id: part.id,
          timestamp: part.timestamp,
          modelId: part.modelId
        };
        continue;
      }
      if (part.type === "text-start") {
        activeTextBlockId = part.id;
        ensureTextPart(part.id, part.providerMetadata);
        continue;
      }
      if (part.type === "text-delta") {
        const blockId = typeof part.id === "string" ? part.id : activeTextBlockId ?? "__default_text_block__";
        activeTextBlockId = blockId;
        const textPart = ensureTextPart(blockId, part.providerMetadata);
        textPart.text = `${textPart.text}${part.delta}`;
        continue;
      }
      if (part.type === "text-end") {
        const blockId = typeof part.id === "string" ? part.id : activeTextBlockId;
        if (blockId) {
          const textPart = ensureTextPart(blockId, part.providerMetadata);
          if (part.providerMetadata) {
            textPart.providerMetadata = part.providerMetadata;
          }
        }
        if (activeTextBlockId === blockId) {
          activeTextBlockId = void 0;
        }
        continue;
      }
      if (part.type === "reasoning-start") {
        activeReasoningBlockId = part.id;
        ensureReasoningPart(part.id, part.providerMetadata);
        continue;
      }
      if (part.type === "reasoning-delta") {
        const blockId = typeof part.id === "string" ? part.id : activeReasoningBlockId ?? "__default_reasoning_block__";
        activeReasoningBlockId = blockId;
        const reasoningPart = ensureReasoningPart(blockId, part.providerMetadata);
        reasoningPart.text = `${reasoningPart.text}${part.delta}`;
        continue;
      }
      if (part.type === "reasoning-end") {
        const blockId = typeof part.id === "string" ? part.id : activeReasoningBlockId;
        if (blockId) {
          const reasoningPart = ensureReasoningPart(blockId, part.providerMetadata);
          if (part.providerMetadata) {
            reasoningPart.providerMetadata = part.providerMetadata;
          }
        }
        if (activeReasoningBlockId === blockId) {
          activeReasoningBlockId = void 0;
        }
        continue;
      }
      if (part.type === "file") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "source") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-approval-request") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-call") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "tool-result") {
        pushContentPart(part);
        continue;
      }
      if (part.type === "finish") {
        usage = part.usage;
        finishReason = part.finishReason;
        providerMetadata = part.providerMetadata;
      }
    }
    const normalizedContent = content.filter((part) => {
      if (part.type === "text") return part.text.trim().length > 0;
      if (part.type === "reasoning") return part.text.trim().length > 0;
      return true;
    });
    return {
      content: normalizedContent,
      usage,
      finishReason,
      warnings,
      response: responseMetadata,
      request,
      ...providerMetadata ? { providerMetadata } : {}
    };
  }
  async doStream(options) {
    const providerOptions = await parseProviderOptions({
      provider: this.provider,
      providerOptions: options.providerOptions,
      schema: appServerProviderOptionsSchema
    });
    const mergedSettings = this.mergeSettings(providerOptions);
    const resolvedReasoning = resolveReasoningEffort({
      reasoning: options.reasoning,
      providerEffort: providerOptions?.effort,
      defaultEffort: mergedSettings.effort
    });
    const settings = resolvedReasoning.effort === mergedSettings.effort ? mergedSettings : { ...mergedSettings, effort: resolvedReasoning.effort };
    const sdkServerLifecycle = this.resolveThreadMode(settings, providerOptions) === "persistent" ? "provider" : "request";
    const includeRawChunks = this.resolveIncludeRawChunks(
      options.includeRawChunks,
      settings,
      providerOptions
    );
    const warnings = [
      ...mapUnsupportedSettingsWarnings({
        temperature: options.temperature,
        topP: options.topP,
        topK: options.topK,
        maxOutputTokens: options.maxOutputTokens,
        presencePenalty: options.presencePenalty,
        frequencyPenalty: options.frequencyPenalty,
        stopSequences: options.stopSequences,
        seed: options.seed,
        tools: options.tools,
        toolChoice: options.toolChoice
      })
    ];
    if (resolvedReasoning.warning) {
      warnings.push(resolvedReasoning.warning);
    }
    const developerInstructionsOverride = providerOptions?.developerInstructions ?? settings.developerInstructions;
    const systemInstruction = collectSystemInstruction(options.prompt);
    const resolvedConfig = await this.resolveConfig(settings, sdkServerLifecycle);
    let releasedSdkServers = false;
    const releaseUsedSdkMcpServers = () => {
      if (releasedSdkServers || sdkServerLifecycle !== "request") {
        return;
      }
      releasedSdkServers = true;
      for (const server of resolvedConfig.usedSdkMcpServers) {
        this.onSdkMcpServerReleased?.(server);
      }
    };
    let threadResolution;
    try {
      threadResolution = await this.startOrResumeThread({
        settings,
        providerOptions,
        configOverrides: resolvedConfig.configOverrides,
        developerInstructionsOverride,
        systemInstruction,
        includeRawChunks
      });
    } catch (error) {
      releaseUsedSdkMcpServers();
      throw error;
    }
    const threadId = threadResolution.threadId;
    if (includeRawChunks && threadResolution.resumed && threadResolution.rawEventsNegotiated !== true) {
      warnings.push({
        type: "other",
        message: "includeRawChunks was requested while resuming an existing thread that may not emit raw events. Start a new thread to guarantee raw chunk events."
      });
    }
    const prompt = this.preparePrompt(options.prompt, threadResolution.resumed);
    warnings.push(...prompt.warnings);
    let input = [];
    let tempImagePaths = [];
    let session;
    try {
      const builtInput = await this.buildUserInput(prompt.promptText, prompt.images);
      input = builtInput.input;
      tempImagePaths = builtInput.tempImagePaths;
      session = await this.createOrReuseSession({ threadId, settings, providerOptions });
    } catch (error) {
      cleanupTempImages(tempImagePaths);
      releaseUsedSdkMcpServers();
      throw error;
    }
    const turnStartParams = buildTurnStartParams({
      threadId,
      modelId: this.modelId,
      input,
      settings: {
        cwd: settings.cwd,
        approvalPolicy: this.mapApprovalPolicy(settings),
        sandboxPolicy: mapSandboxToTurnSandboxPolicy(settings),
        effort: settings.effort,
        summary: settings.summary,
        personality: settings.personality,
        serviceTier: settings.serviceTier,
        serviceTierForTurn: providerOptions?.serviceTier
      },
      responseFormat: options.responseFormat
    });
    const turnStreamController = new TurnStreamController({
      client: this.client,
      modelId: this.modelId,
      threadId,
      warnings,
      includeRawChunks,
      jsonModeLastTextBlockOnly: options.responseFormat?.type === "json",
      turnStartParams,
      requestHandlers: settings.serverRequests,
      autoApprove: settings.autoApprove,
      session,
      abortSignal: options.abortSignal,
      shouldSerializeTurnStart: threadResolution.persistent || threadResolution.explicit,
      hadInitialThreadId: threadResolution.resumed,
      threadResolution: {
        persistent: threadResolution.persistent,
        explicit: threadResolution.explicit
      },
      releaseResources: () => {
        cleanupTempImages(tempImagePaths);
        releaseUsedSdkMcpServers();
      },
      clearPersistentThreadState: (staleThreadId) => {
        this.clearPersistentThreadState(staleThreadId);
      }
    });
    const stream = new ReadableStream({
      start: async (controller) => {
        await turnStreamController.start(controller);
      },
      cancel: async (reason) => {
        await turnStreamController.cancel(reason);
      }
    });
    return {
      stream,
      request: { body: prompt.promptText }
    };
  }
};
var jsonRpcIdSchema = z.union([z.number(), z.string()]);
var jsonRpcRequestSchema = z.object({
  id: jsonRpcIdSchema,
  method: z.string().min(1),
  params: z.record(z.string(), z.unknown()).optional()
}).passthrough();
var jsonRpcResponseSchema = z.object({
  id: jsonRpcIdSchema,
  result: z.unknown()
}).refine((value) => Object.prototype.hasOwnProperty.call(value, "result"), {
  message: "result field is required"
}).passthrough();
var jsonRpcErrorResponseSchema = z.object({
  id: z.union([jsonRpcIdSchema, z.null()]),
  error: z.object({
    code: z.number(),
    message: z.string(),
    data: z.unknown().optional()
  }).passthrough()
}).passthrough();
var jsonRpcNotificationSchema = z.object({
  method: z.string().min(1),
  params: z.record(z.string(), z.unknown()).optional()
}).passthrough();
var userInputTextSchema = z.object({
  type: z.literal("text"),
  text: z.string(),
  text_elements: z.array(z.unknown())
}).passthrough();
var userInputImageSchema = z.object({
  type: z.literal("image"),
  url: z.string().optional(),
  imageUrl: z.string().optional()
}).refine((value) => typeof value.url === "string" || typeof value.imageUrl === "string", {
  message: "image input must provide url or imageUrl"
}).passthrough();
var userInputLocalImageSchema = z.object({
  type: z.literal("localImage"),
  path: z.string()
}).passthrough();
var userInputSkillSchema = z.object({
  type: z.literal("skill"),
  name: z.string(),
  path: z.string()
}).passthrough();
var userInputMentionSchema = z.object({
  type: z.literal("mention"),
  name: z.string(),
  path: z.string()
}).passthrough();
var userInputSchema = z.discriminatedUnion("type", [
  userInputTextSchema,
  userInputImageSchema,
  userInputLocalImageSchema,
  userInputSkillSchema,
  userInputMentionSchema
]);
var userMessageItemSchema = z.object({
  type: z.literal("userMessage"),
  id: z.string(),
  content: z.array(userInputSchema)
}).passthrough();
var agentMessageItemSchema = z.object({
  type: z.literal("agentMessage"),
  id: z.string(),
  text: z.string(),
  phase: z.string().nullable()
}).passthrough();
var planItemSchema = z.object({
  type: z.literal("plan"),
  id: z.string(),
  text: z.string()
}).passthrough();
var reasoningItemSchema = z.object({
  type: z.literal("reasoning"),
  id: z.string(),
  summary: z.array(z.string()),
  content: z.array(z.string())
}).passthrough();
var commandExecutionItemSchema = z.object({
  type: z.literal("commandExecution"),
  id: z.string(),
  command: z.string(),
  cwd: z.string(),
  processId: z.string().nullable(),
  status: z.string(),
  commandActions: z.array(z.unknown()),
  aggregatedOutput: z.string().nullable(),
  exitCode: z.number().nullable(),
  durationMs: z.number().nullable()
}).passthrough();
var fileChangeItemSchema = z.object({
  type: z.literal("fileChange"),
  id: z.string(),
  changes: z.array(z.unknown()),
  status: z.string()
}).passthrough();
var mcpToolCallItemSchema = z.object({
  type: z.literal("mcpToolCall"),
  id: z.string(),
  server: z.string(),
  tool: z.string(),
  status: z.string(),
  arguments: z.unknown(),
  result: z.unknown().nullable(),
  error: z.unknown().nullable(),
  durationMs: z.number().nullable()
}).passthrough();
var collabAgentToolCallItemSchema = z.object({
  type: z.literal("collabAgentToolCall"),
  id: z.string(),
  tool: z.string(),
  status: z.string(),
  senderThreadId: z.string(),
  receiverThreadIds: z.array(z.string()),
  prompt: z.string().nullable(),
  agentsStates: z.record(z.string(), z.unknown())
}).passthrough();
var webSearchItemSchema = z.object({
  type: z.literal("webSearch"),
  id: z.string(),
  query: z.string(),
  action: z.unknown().nullable()
}).passthrough();
var imageViewItemSchema = z.object({
  type: z.literal("imageView"),
  id: z.string(),
  path: z.string()
}).passthrough();
var enteredReviewModeItemSchema = z.object({
  type: z.literal("enteredReviewMode"),
  id: z.string(),
  review: z.string()
}).passthrough();
var exitedReviewModeItemSchema = z.object({
  type: z.literal("exitedReviewMode"),
  id: z.string(),
  review: z.string()
}).passthrough();
var contextCompactionItemSchema = z.object({
  type: z.literal("contextCompaction"),
  id: z.string()
}).passthrough();
var hookPromptItemSchema = z.object({
  type: z.literal("hookPrompt"),
  id: z.string(),
  fragments: z.array(z.unknown())
}).passthrough();
var dynamicToolCallItemSchema = z.object({
  type: z.literal("dynamicToolCall"),
  id: z.string(),
  namespace: z.string().nullable(),
  tool: z.string(),
  arguments: z.unknown(),
  status: z.string(),
  contentItems: z.array(z.unknown()).nullable(),
  success: z.boolean().nullable(),
  durationMs: z.number().nullable()
}).passthrough();
var subAgentActivityItemSchema = z.object({
  type: z.literal("subAgentActivity"),
  id: z.string(),
  kind: z.string(),
  agentThreadId: z.string(),
  agentPath: z.string()
}).passthrough();
var sleepItemSchema = z.object({
  type: z.literal("sleep"),
  id: z.string(),
  durationMs: z.number()
}).passthrough();
var imageGenerationItemSchema = z.object({
  type: z.literal("imageGeneration"),
  id: z.string(),
  status: z.string(),
  revisedPrompt: z.string().nullable(),
  result: z.string(),
  savedPath: z.string().optional()
}).passthrough();
var unknownThreadItemSchema = z.object({
  type: z.string()
}).passthrough();
var knownThreadItemSchema = z.discriminatedUnion("type", [
  userMessageItemSchema,
  agentMessageItemSchema,
  planItemSchema,
  reasoningItemSchema,
  commandExecutionItemSchema,
  fileChangeItemSchema,
  mcpToolCallItemSchema,
  dynamicToolCallItemSchema,
  collabAgentToolCallItemSchema,
  hookPromptItemSchema,
  subAgentActivityItemSchema,
  sleepItemSchema,
  imageGenerationItemSchema,
  webSearchItemSchema,
  imageViewItemSchema,
  enteredReviewModeItemSchema,
  exitedReviewModeItemSchema,
  contextCompactionItemSchema
]);
var threadItemSchema = knownThreadItemSchema.or(unknownThreadItemSchema);
var codexHttpStatusCodeSchema = z.object({
  httpStatusCode: z.number().nullable()
}).passthrough();
var codexErrorInfoSchema = z.union([
  z.enum([
    "contextWindowExceeded",
    "usageLimitExceeded",
    "rateLimitExceeded",
    "sessionBudgetExceeded",
    "misalignmentPolicyViolation",
    "serverOverloaded",
    "cyberPolicy",
    "internalServerError",
    "unauthorized",
    "badRequest",
    "threadRollbackFailed",
    "sandboxError",
    "other"
  ]),
  z.object({ httpConnectionFailed: codexHttpStatusCodeSchema }).passthrough(),
  z.object({ responseStreamConnectionFailed: codexHttpStatusCodeSchema }).passthrough(),
  z.object({ responseStreamDisconnected: codexHttpStatusCodeSchema }).passthrough(),
  z.object({ responseTooManyFailedAttempts: codexHttpStatusCodeSchema }).passthrough(),
  z.object({ activeTurnNotSteerable: z.object({ turnKind: z.string() }).passthrough() }).passthrough(),
  // Forward-compat catch-alls: consumers only compare against the known string
  // codes above (unknown codes fall back to the generic error path), so a new
  // errorInfo variant must never fail validation and drop the notification.
  z.string(),
  z.record(z.string(), z.unknown())
]);
var turnSchema = z.object({
  id: z.string(),
  items: z.array(threadItemSchema),
  status: z.string(),
  error: z.object({
    message: z.string(),
    codexErrorInfo: codexErrorInfoSchema.nullish(),
    additionalDetails: z.string().nullish()
  }).nullish()
}).passthrough();
var threadStartedNotificationSchema = z.object({
  thread: z.object({ id: z.string() }).passthrough()
}).passthrough();
var turnStartedNotificationSchema = z.object({
  threadId: z.string(),
  turn: turnSchema
}).passthrough();
var turnCompletedNotificationSchema = z.object({
  threadId: z.string(),
  turn: turnSchema
}).passthrough();
var itemStartedNotificationSchema = z.object({
  item: threadItemSchema,
  threadId: z.string(),
  turnId: z.string()
}).passthrough();
var itemCompletedNotificationSchema = z.object({
  item: threadItemSchema,
  threadId: z.string(),
  turnId: z.string()
}).passthrough();
var agentMessageDeltaNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  delta: z.string()
}).passthrough();
var reasoningTextDeltaNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  delta: z.string()
}).passthrough();
var reasoningSummaryTextDeltaNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  delta: z.string()
}).passthrough();
var commandExecutionOutputDeltaNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  delta: z.string()
}).passthrough();
var fileChangeOutputDeltaNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  delta: z.string()
}).passthrough();
var tokenUsageBreakdownSchema = z.object({
  totalTokens: z.number(),
  inputTokens: z.number(),
  cachedInputTokens: z.number(),
  cacheWriteInputTokens: z.number().optional(),
  outputTokens: z.number(),
  reasoningOutputTokens: z.number()
}).passthrough();
var threadTokenUsageUpdatedNotificationSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  tokenUsage: z.object({
    total: tokenUsageBreakdownSchema,
    last: tokenUsageBreakdownSchema,
    modelContextWindow: z.number().nullable()
  }).passthrough()
}).passthrough();
var errorNotificationSchema = z.object({
  error: z.object({
    message: z.string(),
    codexErrorInfo: codexErrorInfoSchema.nullable(),
    additionalDetails: z.string().nullable()
  }).passthrough(),
  willRetry: z.boolean(),
  threadId: z.string(),
  turnId: z.string()
}).passthrough();
var commandExecutionRequestApprovalParamsSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  approvalId: z.string().nullable().optional(),
  reason: z.string().nullable().optional(),
  networkApprovalContext: z.unknown().nullable().optional(),
  command: z.string().nullable().optional(),
  cwd: z.string().nullable().optional(),
  commandActions: z.array(z.unknown()).nullable().optional(),
  proposedExecpolicyAmendment: z.unknown().nullable().optional()
}).passthrough();
var fileChangeRequestApprovalParamsSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  reason: z.string().nullable().optional(),
  grantRoot: z.string().nullable().optional()
}).passthrough();
var toolRequestUserInputParamsSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  itemId: z.string(),
  questions: z.array(z.unknown())
}).passthrough();
var skillRequestApprovalParamsSchema = z.object({
  itemId: z.string(),
  skillName: z.string()
}).passthrough();
var mcpServerElicitationRequestParamsSchema = z.object({
  threadId: z.string(),
  turnId: z.string().nullable().optional(),
  serverName: z.string(),
  mode: z.string().optional(),
  message: z.string().optional(),
  requestedSchema: z.unknown().optional(),
  url: z.string().optional(),
  elicitationId: z.string().optional(),
  _meta: z.record(z.string(), z.unknown()).nullable().optional()
}).passthrough();
var dynamicToolCallParamsSchema = z.object({
  threadId: z.string(),
  turnId: z.string(),
  callId: z.string(),
  tool: z.string(),
  arguments: z.unknown()
}).passthrough();
var chatgptAuthTokensRefreshParamsSchema = z.object({
  reason: z.string(),
  previousAccountId: z.string().nullable().optional()
}).passthrough();
var serverRequestParamSchemas = {
  "item/commandExecution/requestApproval": commandExecutionRequestApprovalParamsSchema,
  "item/fileChange/requestApproval": fileChangeRequestApprovalParamsSchema,
  "item/tool/requestUserInput": toolRequestUserInputParamsSchema,
  "mcpServer/elicitation/request": mcpServerElicitationRequestParamsSchema,
  "skill/requestApproval": skillRequestApprovalParamsSchema,
  "item/tool/call": dynamicToolCallParamsSchema,
  "account/chatgptAuthTokens/refresh": chatgptAuthTokensRefreshParamsSchema
};
z.enum(
  Object.keys(serverRequestParamSchemas)
);
var serverRequestSchema = z.discriminatedUnion("method", [
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("item/commandExecution/requestApproval"),
    params: commandExecutionRequestApprovalParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("item/fileChange/requestApproval"),
    params: fileChangeRequestApprovalParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("item/tool/requestUserInput"),
    params: toolRequestUserInputParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("mcpServer/elicitation/request"),
    params: mcpServerElicitationRequestParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("skill/requestApproval"),
    params: skillRequestApprovalParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("item/tool/call"),
    params: dynamicToolCallParamsSchema
  }).passthrough(),
  z.object({
    id: jsonRpcIdSchema,
    method: z.literal("account/chatgptAuthTokens/refresh"),
    params: chatgptAuthTokensRefreshParamsSchema
  }).passthrough()
]);
var incomingNotificationSchemas = {
  "thread/started": threadStartedNotificationSchema,
  "turn/started": turnStartedNotificationSchema,
  "turn/completed": turnCompletedNotificationSchema,
  "item/started": itemStartedNotificationSchema,
  "item/completed": itemCompletedNotificationSchema,
  "item/agentMessage/delta": agentMessageDeltaNotificationSchema,
  reasoningTextDelta: reasoningTextDeltaNotificationSchema,
  reasoningSummaryTextDelta: reasoningSummaryTextDeltaNotificationSchema,
  "item/reasoning/textDelta": reasoningTextDeltaNotificationSchema,
  "item/reasoning/summaryTextDelta": reasoningSummaryTextDeltaNotificationSchema,
  "item/commandExecution/outputDelta": commandExecutionOutputDeltaNotificationSchema,
  "item/fileChange/outputDelta": fileChangeOutputDeltaNotificationSchema,
  "thread/tokenUsage/updated": threadTokenUsageUpdatedNotificationSchema,
  error: errorNotificationSchema
};

// src/app-server/rpc/client.ts
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
var MAX_COMPLETED_TURN_IDS = 1024;
var MAX_TOKEN_USAGE_THREADS = 256;
var MAX_TOKEN_USAGE_TURNS_PER_THREAD = 4;
var DEFAULT_MIN_CODEX_VERSION = "0.156.0";
function resolveCodexPath2(explicitPath) {
  if (explicitPath) {
    const lower = explicitPath.toLowerCase();
    if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
      return { cmd: "node", args: [explicitPath] };
    }
    return { cmd: explicitPath, args: [] };
  }
  try {
    const req = createRequire(import.meta.url);
    const pkgPath = req.resolve("@openai/codex/package.json");
    const root = path.dirname(pkgPath);
    return { cmd: "node", args: [path.join(root, "bin", "codex.js")] };
  } catch {
    return { cmd: "codex", args: [] };
  }
}
function parseVersionFromUserAgent(userAgent) {
  const match = userAgent.match(/(\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?)/);
  return match?.[1];
}
function parseSemver(input) {
  const match = input.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
  if (!match) return void 0;
  return {
    major: Number(match[1]),
    minor: Number(match[2]),
    patch: Number(match[3]),
    prerelease: match[4] ? match[4].split(".") : []
  };
}
function compareIdentifiers(a, b) {
  const aNum = /^\d+$/.test(a);
  const bNum = /^\d+$/.test(b);
  if (aNum && bNum) {
    const av = Number(a);
    const bv = Number(b);
    if (av > bv) return 1;
    if (av < bv) return -1;
    return 0;
  }
  if (aNum && !bNum) return -1;
  if (!aNum && bNum) return 1;
  return a.localeCompare(b);
}
function compareSemver(a, b) {
  const left = parseSemver(a);
  const right = parseSemver(b);
  if (!left || !right) return void 0;
  if (left.major !== right.major) return left.major > right.major ? 1 : -1;
  if (left.minor !== right.minor) return left.minor > right.minor ? 1 : -1;
  if (left.patch !== right.patch) return left.patch > right.patch ? 1 : -1;
  const aPre = left.prerelease;
  const bPre = right.prerelease;
  if (aPre.length === 0 && bPre.length === 0) return 0;
  if (aPre.length === 0) return 1;
  if (bPre.length === 0) return -1;
  const maxLen = Math.max(aPre.length, bPre.length);
  for (let i = 0; i < maxLen; i++) {
    const av = aPre[i];
    const bv = bPre[i];
    if (av === void 0) return -1;
    if (bv === void 0) return 1;
    const cmp = compareIdentifiers(av, bv);
    if (cmp !== 0) return cmp;
  }
  return 0;
}
var JsonRpcRequestError = class extends Error {
  code;
  constructor(code, message) {
    super(`JSON-RPC error ${code}: ${message}`);
    this.name = "JsonRpcRequestError";
    this.code = code;
  }
};
var AppServerRpcClient = class extends EventEmitter {
  settings;
  logger;
  requestTimeoutMs;
  clientVersion;
  child;
  stdoutReader;
  state = "idle";
  initPromise;
  nextId = 1;
  nextRequestContextId = 1;
  pending = /* @__PURE__ */ new Map();
  threadLocks = /* @__PURE__ */ new Map();
  pendingRequestContexts = /* @__PURE__ */ new Map();
  pendingRequestContextIdsByThread = /* @__PURE__ */ new Map();
  activeRequestContextsByTurn = /* @__PURE__ */ new Map();
  completedTurnIds = /* @__PURE__ */ new Set();
  /**
   * Latest cumulative `tokenUsage.total` per thread, keyed by the turn that
   * reported it (most recent last). Lets a turn measure its own usage against
   * the snapshot that preceded it, including the restored snapshot Codex
   * replays when a thread is resumed.
   */
  tokenUsageTotalsByThread = /* @__PURE__ */ new Map();
  lastStderr = "";
  lastCrashHadStderr = false;
  idleTimer;
  serverCapabilities;
  expectedExitSignal;
  writeQueue = Promise.resolve();
  serverVersion;
  constructor(options = {}) {
    super();
    this.settings = options.settings ?? {};
    this.logger = getLogger(options.logger ?? this.settings.logger);
    this.requestTimeoutMs = options.requestTimeoutMs ?? this.settings.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
    this.clientVersion = options.clientVersion ?? "0.0.0";
  }
  async ensureReady() {
    if (this.state === "ready") return;
    if (this.state === "closed") throw new Error("AppServerRpcClient is closed");
    if (this.initPromise) {
      await this.initPromise;
      return;
    }
    const shouldEmitReconnected = this.state === "error";
    this.state = "starting";
    this.initPromise = this.startAndInitialize().then(() => {
      this.state = "ready";
      if (shouldEmitReconnected) this.emit("reconnected");
    }).catch((error) => {
      this.cleanupAfterInitializationFailure(error);
      throw error;
    }).finally(() => {
      this.initPromise = void 0;
    });
    await this.initPromise;
    this.touchActivity();
  }
  async request(method, params, timeoutMs) {
    await this.ensureReady();
    this.touchActivity();
    return await this.requestInternal(method, params, timeoutMs);
  }
  async requestInternal(method, params, timeoutMs) {
    const id = this.nextId++;
    const request = {
      id,
      method,
      ...params ? { params } : {}
    };
    return await new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        this.pending.delete(id);
        reject(new Error(`Request timed out for method '${method}'`));
      }, timeoutMs ?? this.requestTimeoutMs);
      this.pending.set(id, {
        resolve: (value) => resolve(value),
        reject,
        timer
      });
      void this.writeMessage(request).catch((error) => {
        const pending = this.pending.get(id);
        if (!pending) {
          return;
        }
        clearTimeout(pending.timer);
        this.pending.delete(id);
        pending.reject(error);
      });
    });
  }
  notify(method, params) {
    if (this.state !== "ready") {
      throw new Error(`Cannot send notification '${method}' while client is not ready`);
    }
    void this.writeMessage({ method, ...params ? { params } : {} }).catch((error) => {
      this.logger.warn(
        `[codex-app-server] Failed to send notification '${method}': ${String(error)}`
      );
    });
  }
  async threadStart(params) {
    return await this.request("thread/start", params);
  }
  async threadResume(params) {
    return await this.request("thread/resume", params);
  }
  async turnStart(params) {
    return await this.request("turn/start", params);
  }
  async turnInterrupt(params) {
    return await this.request("turn/interrupt", params);
  }
  async modelList(params) {
    await this.ensureReady();
    this.touchActivity();
    if (this.serverCapabilities?.modelList === false) {
      throw new UnsupportedFeatureError({
        feature: "model/list",
        minCodexVersion: this.settings.minCodexVersion ?? DEFAULT_MIN_CODEX_VERSION,
        serverVersion: this.serverVersion
      });
    }
    try {
      return await this.requestInternal(
        "model/list",
        params
      );
    } catch (error) {
      if (error instanceof JsonRpcRequestError && error.code === -32601) {
        throw new UnsupportedFeatureError({
          feature: "model/list",
          minCodexVersion: this.settings.minCodexVersion ?? DEFAULT_MIN_CODEX_VERSION,
          serverVersion: this.serverVersion
        });
      }
      throw error;
    }
  }
  registerRequestContext(threadId, context) {
    const contextId = `ctx_${this.nextRequestContextId++}`;
    this.pendingRequestContexts.set(contextId, {
      threadId,
      context: {
        handlers: context.handlers,
        autoApprove: context.autoApprove
      }
    });
    const ids = this.pendingRequestContextIdsByThread.get(threadId) ?? /* @__PURE__ */ new Set();
    ids.add(contextId);
    this.pendingRequestContextIdsByThread.set(threadId, ids);
    return contextId;
  }
  bindRequestContext(contextId, turnId) {
    const pending = this.pendingRequestContexts.get(contextId);
    if (!pending) return;
    if (this.completedTurnIds.has(turnId)) {
      this.clearRequestContext(contextId);
      return;
    }
    this.activeRequestContextsByTurn.set(turnId, {
      handlers: pending.context.handlers,
      autoApprove: pending.context.autoApprove
    });
    this.clearRequestContext(contextId);
  }
  clearRequestContext(contextId) {
    const pending = this.pendingRequestContexts.get(contextId);
    if (!pending) return;
    this.pendingRequestContexts.delete(contextId);
    const ids = this.pendingRequestContextIdsByThread.get(pending.threadId);
    if (!ids) return;
    ids.delete(contextId);
    if (ids.size === 0) {
      this.pendingRequestContextIdsByThread.delete(pending.threadId);
    }
  }
  clearRequestContextForTurn(turnId) {
    this.activeRequestContextsByTurn.delete(turnId);
  }
  hasTurnCompleted(turnId) {
    return this.completedTurnIds.has(turnId);
  }
  async withThreadLock(threadId, fn) {
    const previous = this.threadLocks.get(threadId) ?? Promise.resolve();
    let release;
    const current = new Promise((resolve) => {
      release = resolve;
    });
    const chained = previous.then(() => current);
    this.threadLocks.set(threadId, chained);
    await previous;
    try {
      return await fn();
    } finally {
      release?.();
      if (this.threadLocks.get(threadId) === chained) {
        this.threadLocks.delete(threadId);
      }
    }
  }
  async close() {
    this.state = "closed";
    this.clearIdleTimer();
    this.stdoutReader?.close();
    this.stdoutReader = void 0;
    for (const [id, pending] of this.pending.entries()) {
      clearTimeout(pending.timer);
      pending.reject(new Error(`Client closed while request ${String(id)} was in flight`));
    }
    this.pending.clear();
    this.threadLocks.clear();
    this.pendingRequestContexts.clear();
    this.pendingRequestContextIdsByThread.clear();
    this.activeRequestContextsByTurn.clear();
    this.completedTurnIds.clear();
    this.tokenUsageTotalsByThread.clear();
    this.serverCapabilities = void 0;
    if (this.child) {
      this.expectedExitSignal = "SIGTERM";
      this.child.kill("SIGTERM");
      this.child = void 0;
    }
    this.writeQueue = Promise.resolve();
  }
  async dispose() {
    await this.close();
  }
  async startAndInitialize() {
    const base = resolveCodexPath2(this.settings.codexPath);
    const args = [...base.args, "app-server", "--listen", "stdio://"];
    this.lastStderr = "";
    this.lastCrashHadStderr = false;
    this.expectedExitSignal = void 0;
    this.child = spawn(base.cmd, args, {
      stdio: ["pipe", "pipe", "pipe"],
      env: {
        ...process.env,
        ...this.settings.env ?? {},
        RUST_LOG: process.env.RUST_LOG || "error"
      },
      cwd: this.settings.cwd
    });
    const child = this.child;
    let stderrBuf = "";
    child.stderr.setEncoding("utf8");
    child.stderr.on("data", (chunk) => {
      stderrBuf = (stderrBuf + String(chunk)).slice(-4e3);
      if (this.child === child) {
        this.lastStderr = stderrBuf;
      }
    });
    child.on("error", (error) => {
      this.logger.error(`[codex-app-server] process error: ${String(error)}`);
      const base2 = String(error);
      const message = this.withStderrTail(base2, stderrBuf);
      this.lastCrashHadStderr = message !== base2;
      this.handleCrash(new Error(message));
    });
    child.on("exit", (code, signal) => {
      const message = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
      const expected = this.state === "closed" || signal !== null && signal === this.expectedExitSignal;
      this.expectedExitSignal = void 0;
      if (expected) {
        this.logger.info(`[codex-app-server] ${message}`);
        return;
      }
      const crashedPending = this.markCrashed();
      if (!crashedPending) return;
      let settled = false;
      const finish = () => {
        if (settled) return;
        settled = true;
        child.off("close", finish);
        clearTimeout(timer);
        const base2 = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
        const crashMessage = this.withStderrTail(base2, stderrBuf);
        if (this.child === void 0) {
          this.lastStderr = stderrBuf;
          this.lastCrashHadStderr = crashMessage !== base2;
        }
        this.logger.warn(`[codex-app-server] ${crashMessage}`);
        this.rejectCrashedPending(crashedPending, new Error(crashMessage));
      };
      child.once("close", finish);
      const timer = setTimeout(finish, 120);
      timer.unref?.();
    });
    this.stdoutReader = readline.createInterface({
      input: child.stdout,
      crlfDelay: Infinity
    });
    this.stdoutReader.on("line", (line) => this.handleLine(line));
    const initializeParams = {
      clientInfo: {
        name: "ai-sdk-provider-codex-cli",
        version: this.clientVersion
      },
      capabilities: {
        experimentalApi: true,
        optOutNotificationMethods: null
      }
    };
    let initializeResult;
    try {
      initializeResult = await this.requestInternal(
        "initialize",
        initializeParams,
        this.settings.connectionTimeoutMs ?? this.requestTimeoutMs
      );
    } catch (error) {
      const raw = String(error?.message ?? error);
      if (raw.includes("unknown subcommand") || this.lastStderr.includes("unknown subcommand")) {
        throw new Error(
          this.withStderrTail(
            `codex app-server requires codex CLI >= ${this.settings.minCodexVersion ?? DEFAULT_MIN_CODEX_VERSION}. Run 'codex --version' to check.`
          )
        );
      }
      if (raw.includes("ENOENT") || this.lastStderr.includes("ENOENT")) {
        throw new Error(
          this.withStderrTail(
            "codex app-server failed to start: codex executable not found (ENOENT). Check that the codex CLI is installed and its native binary is intact \u2014 a corrupted @openai/codex npm install can cause this. Run 'codex --version' to verify."
          )
        );
      }
      const message = this.lastCrashHadStderr ? raw : this.withStderrTail(raw);
      throw createAPICallError({
        message: `Failed to initialize codex app-server: ${message}`,
        stderr: this.lastStderr,
        provider: "app-server"
      });
    }
    await this.writeMessage({ method: "initialized" });
    this.checkVersion(initializeResult.userAgent);
    this.serverCapabilities = initializeResult.capabilities ?? null;
  }
  checkVersion(userAgent) {
    const detected = parseVersionFromUserAgent(userAgent);
    if (!detected) {
      this.logger.warn(
        `[codex-app-server] Could not parse server version from userAgent: ${userAgent}`
      );
      return;
    }
    this.serverVersion = detected;
    const minVersion = this.settings.minCodexVersion ?? DEFAULT_MIN_CODEX_VERSION;
    const compared = compareSemver(detected, minVersion);
    if (compared === void 0) {
      this.logger.warn(
        `[codex-app-server] Could not semver-compare '${detected}' against '${minVersion}'.`
      );
      return;
    }
    if (compared < 0) {
      throw new Error(
        `codex app-server version '${detected}' is below required minimum '${minVersion}'.`
      );
    }
  }
  cleanupAfterInitializationFailure(error) {
    if (this.state === "closed") return;
    this.state = "error";
    this.clearIdleTimer();
    this.stdoutReader?.close();
    this.stdoutReader = void 0;
    for (const [id, pending] of this.pending.entries()) {
      clearTimeout(pending.timer);
      pending.reject(
        new Error(
          `Request ${String(id)} failed during app-server initialization: ${String(error)}`
        )
      );
    }
    this.pending.clear();
    this.threadLocks.clear();
    this.pendingRequestContexts.clear();
    this.pendingRequestContextIdsByThread.clear();
    this.activeRequestContextsByTurn.clear();
    this.completedTurnIds.clear();
    this.serverCapabilities = void 0;
    if (this.child) {
      this.expectedExitSignal = "SIGTERM";
      this.child.kill("SIGTERM");
      this.child = void 0;
    }
    this.writeQueue = Promise.resolve();
  }
  markCrashed() {
    if (this.state === "closed" || this.state === "error") return void 0;
    this.state = "error";
    this.clearIdleTimer();
    this.stdoutReader?.close();
    this.stdoutReader = void 0;
    if (this.child) {
      this.expectedExitSignal = "SIGTERM";
      this.child.kill("SIGTERM");
      this.child = void 0;
    }
    const pending = new Map(this.pending);
    for (const [, entry] of pending) {
      clearTimeout(entry.timer);
    }
    this.pending.clear();
    this.threadLocks.clear();
    this.pendingRequestContexts.clear();
    this.pendingRequestContextIdsByThread.clear();
    this.activeRequestContextsByTurn.clear();
    this.completedTurnIds.clear();
    this.serverCapabilities = void 0;
    this.writeQueue = Promise.resolve();
    return pending;
  }
  rejectCrashedPending(pending, error) {
    for (const [id, entry] of pending) {
      entry.reject(
        new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
      );
    }
  }
  handleCrash(error) {
    const pending = this.markCrashed();
    if (!pending) return;
    this.rejectCrashedPending(pending, error);
  }
  stderrExcerpt(raw) {
    if (!raw) return "";
    const escape = String.fromCharCode(27);
    const ansiEscapeSequence = new RegExp(
      `${escape}(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|${escape}\\\\))`,
      "g"
    );
    const withoutAnsi = raw.replace(ansiEscapeSequence, "");
    const withoutControls = Array.from(withoutAnsi).filter((character) => {
      const code = character.charCodeAt(0);
      return character === "\n" || code >= 32 && (code < 127 || code > 159);
    }).join("");
    const excerpt = withoutControls.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-5).join("; ");
    if (excerpt.length > 600) {
      return `\u2026${excerpt.slice(-600)}`;
    }
    return excerpt;
  }
  withStderrTail(message, raw = this.lastStderr) {
    const excerpt = this.stderrExcerpt(raw);
    return excerpt ? `${message} | stderr (tail): ${excerpt}` : message;
  }
  handleLine(line) {
    const trimmed = line.trim();
    if (!trimmed) return;
    this.touchActivity();
    let parsed;
    try {
      parsed = JSON.parse(trimmed);
    } catch {
      this.logger.warn(`[codex-app-server] Ignoring non-JSON line: ${trimmed.slice(0, 200)}`);
      return;
    }
    const response = jsonRpcResponseSchema.safeParse(parsed);
    if (response.success) {
      this.handleResponse(response.data);
      return;
    }
    const errorResponse = jsonRpcErrorResponseSchema.safeParse(parsed);
    if (errorResponse.success) {
      this.handleErrorResponse(errorResponse.data.id, errorResponse.data.error);
      return;
    }
    const request = jsonRpcRequestSchema.safeParse(parsed);
    if (request.success) {
      const data = request.data;
      void this.handleServerRequest(data.id, data.method, data.params ?? {}).catch((error) => {
        this.logger.warn(
          `[codex-app-server] Failed to handle server request '${data.method}': ${String(error)}`
        );
      });
      return;
    }
    const notification = jsonRpcNotificationSchema.safeParse(parsed);
    if (notification.success) {
      const data = notification.data;
      const schema = incomingNotificationSchemas[data.method];
      if (schema) {
        const notificationParsed = schema.safeParse(data.params ?? {});
        if (!notificationParsed.success) {
          this.logger.warn(
            `[codex-app-server] Notification '${data.method}' failed schema validation; dropping.`
          );
          return;
        }
      }
      if (data.method === "turn/completed") {
        const params = data.params;
        const turnId = typeof params?.turn?.id === "string" ? params.turn.id : void 0;
        if (turnId) {
          this.clearRequestContextForTurn(turnId);
          this.rememberCompletedTurn(turnId);
        }
      }
      if (data.method === "thread/tokenUsage/updated") {
        this.recordThreadTokenUsage(data.params);
      }
      this.emit("notification", data.method, data.params ?? {});
      return;
    }
    this.logger.warn("[codex-app-server] Received unrecognized JSON-RPC message");
  }
  handleResponse(response) {
    this.touchActivity();
    const pending = this.pending.get(response.id);
    if (!pending) return;
    clearTimeout(pending.timer);
    this.pending.delete(response.id);
    pending.resolve(response.result);
  }
  handleErrorResponse(id, error) {
    this.touchActivity();
    if (id === null) {
      this.logger.error(`[codex-app-server] JSON-RPC error: ${error.message}`);
      return;
    }
    const pending = this.pending.get(id);
    if (!pending) return;
    clearTimeout(pending.timer);
    this.pending.delete(id);
    pending.reject(new JsonRpcRequestError(error.code, error.message));
  }
  getThreadIdFromServerRequest(params) {
    return typeof params.threadId === "string" ? params.threadId : void 0;
  }
  getTurnIdFromServerRequest(params) {
    return typeof params.turnId === "string" ? params.turnId : void 0;
  }
  getPendingContextsForThread(threadId) {
    const ids = this.pendingRequestContextIdsByThread.get(threadId);
    if (!ids || ids.size === 0) return [];
    const contexts = [];
    for (const id of ids) {
      const pending = this.pendingRequestContexts.get(id);
      if (pending) contexts.push(pending.context);
    }
    return contexts;
  }
  getContextForRequest(params) {
    const turnId = this.getTurnIdFromServerRequest(params);
    if (turnId) {
      const active = this.activeRequestContextsByTurn.get(turnId);
      if (active) return active;
    }
    const threadId = this.getThreadIdFromServerRequest(params);
    if (threadId) {
      const pending = this.getPendingContextsForThread(threadId);
      if (pending.length === 1) return pending[0];
      if (pending.length > 1) {
        this.logger.debug(
          `[codex-app-server] Received server request for thread '${threadId}' before turn binding with multiple pending contexts; using settings-level handlers.`
        );
      }
      return void 0;
    }
    const totalActive = this.activeRequestContextsByTurn.size;
    const totalPending = this.pendingRequestContexts.size;
    if (totalActive + totalPending === 1) {
      if (totalActive === 1) {
        return this.activeRequestContextsByTurn.values().next().value;
      }
      return this.pendingRequestContexts.values().next().value?.context;
    }
    if (totalActive + totalPending > 1) {
      this.logger.debug(
        "[codex-app-server] Received threadless server request while multiple request contexts are active; using settings-level handlers."
      );
    }
    return void 0;
  }
  async handleServerRequest(id, method, params) {
    const parsed = serverRequestSchema.safeParse({ id, method, params });
    const normalized = parsed.success ? parsed.data : { id, method, params };
    const sendResult = async (result) => {
      try {
        await this.writeMessage({ id: normalized.id, result });
      } catch (error) {
        this.logger.warn(
          `[codex-app-server] Failed to send server request result for '${normalized.method}': ${String(error)}`
        );
      }
    };
    const sendError = async (code, message) => {
      try {
        await this.writeMessage({ id: normalized.id, error: { code, message } });
      } catch (error) {
        this.logger.warn(
          `[codex-app-server] Failed to send server request error for '${normalized.method}': ${String(error)}`
        );
      }
    };
    const activeContext = this.getContextForRequest(normalized.params);
    const handlers = activeContext?.handlers ?? this.settings.serverRequests ?? {};
    const autoApprove = activeContext?.autoApprove ?? this.settings.autoApprove ?? false;
    this.emit("server-request", normalized.method, normalized.params, normalized.id);
    const runHandler = async (handlerCall) => {
      if (!handlerCall) return void 0;
      try {
        const pending = handlerCall();
        if (!pending) return void 0;
        return await pending;
      } catch (error) {
        this.logger.warn(
          `[codex-app-server] request handler failed for '${normalized.method}': ${String(error)}`
        );
        return void 0;
      }
    };
    switch (normalized.method) {
      case "item/commandExecution/requestApproval": {
        const handled = await runHandler(
          () => handlers.onCommandExecutionApproval?.(
            normalized
          )
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendResult({ decision: autoApprove ? "accept" : "decline" });
        return;
      }
      case "item/fileChange/requestApproval": {
        const handled = await runHandler(
          () => handlers.onFileChangeApproval?.(
            normalized
          )
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendResult({ decision: autoApprove ? "accept" : "decline" });
        return;
      }
      case "skill/requestApproval": {
        const handled = await runHandler(
          () => handlers.onSkillApproval?.(normalized)
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendResult({ decision: autoApprove ? "approve" : "decline" });
        return;
      }
      case "mcpServer/elicitation/request": {
        const handled = await runHandler(
          () => handlers.onMcpElicitation?.(normalized)
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        const fallback = await runHandler(
          () => handlers.onUnhandled?.(normalized)
        );
        if (fallback !== void 0) {
          await sendResult(fallback);
          return;
        }
        const meta = normalized.params._meta;
        const isToolCallApproval = meta?.codex_approval_kind === "mcp_tool_call";
        if (isToolCallApproval && autoApprove) {
          await sendResult({ action: "accept", content: {} });
        } else {
          await sendResult({ action: "decline", content: null });
        }
        return;
      }
      case "item/tool/requestUserInput": {
        const handled = await runHandler(
          () => handlers.onToolRequestUserInput?.(
            normalized
          )
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendResult({ answers: {} });
        return;
      }
      case "item/tool/call": {
        const handled = await runHandler(
          () => handlers.onDynamicToolCall?.(normalized)
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendResult({ contentItems: [], success: false });
        return;
      }
      case "account/chatgptAuthTokens/refresh": {
        const handled = await runHandler(
          () => handlers.onAuthRefresh?.(normalized)
        );
        if (handled !== void 0) {
          await sendResult(handled);
          return;
        }
        await sendError(-32603, "Auth token refresh not supported by this client");
        return;
      }
      default:
        {
          const handled = await runHandler(
            () => handlers.onUnhandled?.(normalized)
          );
          if (handled !== void 0) {
            await sendResult(handled);
            return;
          }
        }
        await sendError(-32601, "Method not supported");
    }
  }
  async writeMessage(message) {
    const payload = `${JSON.stringify(message)}
`;
    const writeOperation = async () => {
      if (!this.child?.stdin.writable) {
        throw new Error("codex app-server stdin is not writable");
      }
      const stdin = this.child.stdin;
      const wrote = stdin.write(payload);
      if (!wrote) {
        await new Promise((resolve, reject) => {
          const onDrain = () => {
            cleanup();
            resolve();
          };
          const onError = (error) => {
            cleanup();
            reject(error);
          };
          const onClose = () => {
            cleanup();
            reject(new Error("codex app-server stdin closed before drain"));
          };
          const cleanup = () => {
            stdin.off("drain", onDrain);
            stdin.off("error", onError);
            stdin.off("close", onClose);
          };
          stdin.once("drain", onDrain);
          stdin.once("error", onError);
          stdin.once("close", onClose);
        });
      }
      this.touchActivity();
    };
    const queued = this.writeQueue.then(writeOperation, writeOperation);
    this.writeQueue = queued.catch(() => void 0);
    await queued;
  }
  /**
   * Returns the most recent cumulative token usage snapshot recorded for
   * `threadId` by a turn other than `turnId`, i.e. the thread's total before
   * `turnId` started, when known.
   */
  getTokenUsageTotalBeforeTurn(threadId, turnId) {
    const entries = this.tokenUsageTotalsByThread.get(threadId);
    if (!entries) return void 0;
    for (let index = entries.length - 1; index >= 0; index -= 1) {
      const entry = entries[index];
      if (entry && entry.turnId !== turnId) return entry.total;
    }
    return void 0;
  }
  recordThreadTokenUsage(params) {
    if (!params || typeof params !== "object") return;
    const { threadId, turnId, tokenUsage } = params;
    const total = tokenUsage?.total;
    if (typeof threadId !== "string" || !total || typeof total !== "object") return;
    const turnKey = typeof turnId === "string" ? turnId : void 0;
    const entries = (this.tokenUsageTotalsByThread.get(threadId) ?? []).filter(
      (entry) => entry.turnId !== turnKey
    );
    entries.push({ turnId: turnKey, total });
    if (entries.length > MAX_TOKEN_USAGE_TURNS_PER_THREAD) entries.shift();
    this.tokenUsageTotalsByThread.delete(threadId);
    this.tokenUsageTotalsByThread.set(threadId, entries);
    if (this.tokenUsageTotalsByThread.size > MAX_TOKEN_USAGE_THREADS) {
      const oldest = this.tokenUsageTotalsByThread.keys().next().value;
      if (typeof oldest === "string") this.tokenUsageTotalsByThread.delete(oldest);
    }
  }
  rememberCompletedTurn(turnId) {
    this.completedTurnIds.add(turnId);
    if (this.completedTurnIds.size <= MAX_COMPLETED_TURN_IDS) {
      return;
    }
    const oldest = this.completedTurnIds.values().next().value;
    if (typeof oldest === "string") {
      this.completedTurnIds.delete(oldest);
    }
  }
  clearIdleTimer() {
    if (this.idleTimer) {
      clearTimeout(this.idleTimer);
      this.idleTimer = void 0;
    }
  }
  touchActivity() {
    const idleTimeoutMs = this.settings.idleTimeoutMs;
    if (!idleTimeoutMs || idleTimeoutMs <= 0 || this.state !== "ready") {
      return;
    }
    this.clearIdleTimer();
    this.idleTimer = setTimeout(() => {
      if (this.state !== "ready") return;
      if (this.pending.size > 0 || this.activeRequestContextsByTurn.size > 0 || this.pendingRequestContexts.size > 0) {
        this.touchActivity();
        return;
      }
      if (this.child) {
        this.logger.info(
          `[codex-app-server] Closing idle app-server process after ${idleTimeoutMs}ms inactivity.`
        );
        this.expectedExitSignal = "SIGTERM";
        this.child.kill("SIGTERM");
        this.child = void 0;
      }
      this.stdoutReader?.close();
      this.stdoutReader = void 0;
      this.state = "idle";
      this.threadLocks.clear();
      this.pendingRequestContexts.clear();
      this.pendingRequestContextIdsByThread.clear();
      this.activeRequestContextsByTurn.clear();
      this.completedTurnIds.clear();
      this.serverCapabilities = void 0;
      this.writeQueue = Promise.resolve();
      this.emit("idle-timeout");
    }, idleTimeoutMs);
  }
};

// src/app-server/provider/client-pool.ts
function pickClientScopedSettings(settings) {
  return {
    codexPath: settings.codexPath,
    cwd: settings.cwd,
    env: settings.env,
    logger: settings.logger,
    connectionTimeoutMs: settings.connectionTimeoutMs,
    requestTimeoutMs: settings.requestTimeoutMs,
    idleTimeoutMs: settings.idleTimeoutMs,
    minCodexVersion: settings.minCodexVersion
  };
}
function createClientKey(settings, identityRegistry) {
  const envEntries = settings.env && Object.keys(settings.env).length > 0 ? Object.entries(settings.env).sort(([a], [b]) => a.localeCompare(b)) : void 0;
  return JSON.stringify({
    codexPath: settings.codexPath ?? null,
    cwd: settings.cwd ?? null,
    connectionTimeoutMs: settings.connectionTimeoutMs ?? null,
    requestTimeoutMs: settings.requestTimeoutMs ?? null,
    idleTimeoutMs: settings.idleTimeoutMs ?? null,
    minCodexVersion: settings.minCodexVersion ?? null,
    env: envEntries ?? null,
    logger: identityRegistry.loggerIdentity(settings.logger)
  });
}
function createAppServerClientPool(identityRegistry) {
  const sharedClients = /* @__PURE__ */ new Map();
  return {
    getOrCreate(settings) {
      const clientSettings = pickClientScopedSettings(settings);
      const key = createClientKey(clientSettings, identityRegistry);
      const existing = sharedClients.get(key);
      if (existing) {
        return existing;
      }
      const created = new AppServerRpcClient({
        settings: clientSettings,
        logger: clientSettings.logger
      });
      sharedClients.set(key, created);
      return created;
    },
    async closeAll() {
      await Promise.allSettled(
        Array.from(sharedClients.values()).map(async (client) => {
          await client.close();
        })
      );
      sharedClients.clear();
    }
  };
}

// src/app-server/provider/model-key-factory.ts
function isPlainObject2(value) {
  const prototype = Object.getPrototypeOf(value);
  return prototype === Object.prototype || prototype === null;
}
function createModelKeyFactory(identityRegistry) {
  const normalizeForModelKey = (value, seen = /* @__PURE__ */ new WeakSet()) => {
    if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
      return value;
    }
    if (typeof value === "bigint") {
      return { __bigint: value.toString() };
    }
    if (typeof value === "symbol") {
      return { __symbol: String(value) };
    }
    if (typeof value === "function") {
      return {
        __functionIdentity: identityRegistry.functionIdentity(
          value
        )
      };
    }
    if (Array.isArray(value)) {
      return value.map((item) => normalizeForModelKey(item, seen));
    }
    if (typeof value === "object") {
      if (seen.has(value)) {
        return { __objectRef: identityRegistry.objectIdentity(value) };
      }
      seen.add(value);
      if (isSdkMcpServer(value)) {
        if (value.cacheKey) {
          return {
            __sdkMcpServerCacheKey: value.cacheKey
          };
        }
        const tools = value.tools.map((tool2) => ({
          name: tool2.name,
          description: tool2.description,
          inputSchema: normalizeForModelKey(tool2.inputSchema, seen),
          // Function identity avoids conflating recreated tools whose source text is identical
          // but runtime closure state differs.
          execute: normalizeForModelKey(tool2.execute, seen)
        })).sort((left, right) => left.name.localeCompare(right.name));
        return {
          __sdkMcpServer: {
            name: value.name,
            tools
          }
        };
      }
      if (!isPlainObject2(value)) {
        return { __objectIdentity: identityRegistry.objectIdentity(value) };
      }
      const normalized = {};
      for (const key of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
        normalized[key] = normalizeForModelKey(value[key], seen);
      }
      return normalized;
    }
    return String(value);
  };
  return {
    createPersistentModelKey(modelId, settings) {
      const settingsForKey = {
        ...settings,
        logger: settings.logger === false ? false : settings.logger ? { __loggerIdentity: identityRegistry.loggerIdentity(settings.logger) } : void 0
      };
      return JSON.stringify({
        modelId,
        settings: normalizeForModelKey(settingsForKey)
      });
    }
  };
}

// src/app-server/provider/persistent-model-cache.ts
function createPersistentModelCache() {
  const cache = /* @__PURE__ */ new Map();
  return {
    get(key) {
      return cache.get(key);
    },
    set(key, model) {
      cache.set(key, model);
    },
    clear() {
      cache.clear();
    }
  };
}

// src/app-server/provider/sdk-mcp-lifecycle-manager.ts
function createSdkMcpLifecycleManager(logger) {
  const managedSdkServers = /* @__PURE__ */ new Set();
  const providerScopedSdkServers = /* @__PURE__ */ new Set();
  const requestScopedSdkServerRefCounts = /* @__PURE__ */ new Map();
  return {
    markUsed(server, lifecycle) {
      managedSdkServers.add(server);
      if (lifecycle === "provider") {
        providerScopedSdkServers.add(server);
        return;
      }
      const current = requestScopedSdkServerRefCounts.get(server) ?? 0;
      requestScopedSdkServerRefCounts.set(server, current + 1);
    },
    releaseRequestScoped(server) {
      const current = requestScopedSdkServerRefCounts.get(server);
      if (current === void 0) {
        return;
      }
      if (current > 1) {
        requestScopedSdkServerRefCounts.set(server, current - 1);
        return;
      }
      requestScopedSdkServerRefCounts.delete(server);
      if (providerScopedSdkServers.has(server)) {
        return;
      }
      void server._stop().then(() => {
        if (!providerScopedSdkServers.has(server) && !requestScopedSdkServerRefCounts.has(server)) {
          managedSdkServers.delete(server);
        }
      }).catch((error) => {
        logger.warn(
          `[codex-app-server] Failed to stop request-scoped SDK MCP server: ${String(error)}`
        );
      });
    },
    async closeAll() {
      await Promise.allSettled(
        Array.from(managedSdkServers).map(async (server) => {
          await server._stop();
        })
      );
      managedSdkServers.clear();
      providerScopedSdkServers.clear();
      requestScopedSdkServerRefCounts.clear();
    }
  };
}

// src/app-server/provider/value-identity-registry.ts
function createValueIdentityRegistry() {
  const loggerIdentityIds = /* @__PURE__ */ new WeakMap();
  const functionIdentityIds = /* @__PURE__ */ new WeakMap();
  const objectIdentityIds = /* @__PURE__ */ new WeakMap();
  let nextLoggerIdentityId = 1;
  let nextValueIdentityId = 1;
  return {
    loggerIdentity(value) {
      if (value === false) {
        return "logger:false";
      }
      if (!value) {
        return "logger:default";
      }
      const existing = loggerIdentityIds.get(value);
      if (existing !== void 0) {
        return `logger:${existing}`;
      }
      const id = nextLoggerIdentityId++;
      loggerIdentityIds.set(value, id);
      return `logger:${id}`;
    },
    functionIdentity(value) {
      const existing = functionIdentityIds.get(value);
      if (existing !== void 0) {
        return `fn:${existing}`;
      }
      const id = nextValueIdentityId++;
      functionIdentityIds.set(value, id);
      return `fn:${id}`;
    },
    objectIdentity(value) {
      const existing = objectIdentityIds.get(value);
      if (existing !== void 0) {
        return `obj:${existing}`;
      }
      const id = nextValueIdentityId++;
      objectIdentityIds.set(value, id);
      return `obj:${id}`;
    }
  };
}

// src/app-server/provider.ts
function createCodexAppServer(options = {}) {
  const logger = getLogger(options.defaultSettings?.logger);
  if (options.defaultSettings) {
    const validated = validateAppServerSettings(options.defaultSettings);
    if (!validated.valid) {
      throw new Error(`Invalid default settings: ${validated.errors.join(", ")}`);
    }
    for (const warning of validated.warnings) {
      logger.warn(`Codex App Server Provider: ${warning}`);
    }
  }
  const identityRegistry = createValueIdentityRegistry();
  const modelKeyFactory = createModelKeyFactory(identityRegistry);
  const clientPool = createAppServerClientPool(identityRegistry);
  const persistentModelCache = createPersistentModelCache();
  const sdkMcpLifecycle = createSdkMcpLifecycleManager(logger);
  const createModel = (modelId, settings = {}) => {
    const merged = {
      ...options.defaultSettings,
      ...settings,
      configOverrides: {
        ...options.defaultSettings?.configOverrides ?? {},
        ...settings.configOverrides ?? {}
      }
    };
    const validated = validateAppServerSettings(merged);
    if (!validated.valid) {
      throw new Error(`Invalid settings: ${validated.errors.join(", ")}`);
    }
    for (const warning of validated.warnings) {
      logger.warn(`Codex App Server: ${warning}`);
    }
    const buildModel = () => new AppServerLanguageModel({
      id: modelId,
      settings: merged,
      client: clientPool.getOrCreate(merged),
      onSdkMcpServerUsed: (server, lifecycle) => {
        sdkMcpLifecycle.markUsed(server, lifecycle);
      },
      onSdkMcpServerReleased: (server) => {
        sdkMcpLifecycle.releaseRequestScoped(server);
      }
    });
    if ((merged.threadMode ?? "stateless") !== "persistent") {
      return buildModel();
    }
    const persistentModelKey = modelKeyFactory.createPersistentModelKey(modelId, merged);
    const existingPersistentModel = persistentModelCache.get(persistentModelKey);
    if (existingPersistentModel) {
      return existingPersistentModel;
    }
    const createdPersistentModel = buildModel();
    persistentModelCache.set(persistentModelKey, createdPersistentModel);
    return createdPersistentModel;
  };
  const provider = Object.assign(
    function(modelId, settings) {
      if (new.target) {
        throw new Error("The Codex app-server provider function cannot be called with new.");
      }
      return createModel(modelId, settings);
    },
    { specificationVersion: "v4" }
  );
  provider.languageModel = createModel;
  provider.chat = createModel;
  provider.embeddingModel = ((modelId) => {
    throw new NoSuchModelError({ modelId, modelType: "embeddingModel" });
  });
  provider.imageModel = ((modelId) => {
    throw new NoSuchModelError({ modelId, modelType: "imageModel" });
  });
  provider.close = async () => {
    await sdkMcpLifecycle.closeAll();
    await clientPool.closeAll();
    persistentModelCache.clear();
  };
  provider.dispose = provider.close;
  provider.listModels = async (modelProviders) => {
    const client = clientPool.getOrCreate(options.defaultSettings ?? {});
    const response = await client.modelList({ modelProviders: modelProviders ?? null });
    const models = response.data ?? [];
    return {
      models,
      defaultModel: models.find((model) => model.isDefault === true),
      nextCursor: response.nextCursor
    };
  };
  return provider;
}
var codexAppServer = createCodexAppServer();

// src/app-server/list-models.ts
async function listModels(options = {}) {
  const client = new AppServerRpcClient({
    settings: {
      codexPath: options.codexPath,
      env: options.env,
      cwd: options.cwd,
      minCodexVersion: options.minCodexVersion,
      connectionTimeoutMs: options.connectionTimeoutMs,
      requestTimeoutMs: options.requestTimeoutMs,
      logger: false
    }
  });
  try {
    const result = await client.modelList({ modelProviders: options.modelProviders ?? null });
    const models = result.data ?? [];
    return {
      models,
      defaultModel: models.find((model) => model.isDefault === true),
      nextCursor: result.nextCursor
    };
  } finally {
    await client.dispose();
  }
}
function fromSchemaMethod(schema) {
  const withToJSON = schema;
  if (typeof withToJSON.toJSON !== "function") {
    return void 0;
  }
  try {
    return withToJSON.toJSON();
  } catch {
    return void 0;
  }
}
function fromZodToJsonSchema(schema) {
  const zodWithConverter = z;
  if (typeof zodWithConverter.toJSONSchema !== "function") {
    return void 0;
  }
  try {
    const converted = zodWithConverter.toJSONSchema(schema, {
      io: "input",
      target: "jsonSchema7"
    });
    if (converted && typeof converted === "object") {
      return converted;
    }
  } catch {
    return void 0;
  }
  return void 0;
}
function getDef(schema) {
  return schema._def ?? {};
}
function getTypeName(def) {
  const typeName = def.typeName;
  if (typeof typeName === "string") {
    return typeName;
  }
  const type = def.type;
  if (typeof type === "string") {
    return `Zod${type.charAt(0).toUpperCase()}${type.slice(1)}`;
  }
  return void 0;
}
function toJsonSchemaFallback(schema) {
  const def = getDef(schema);
  const typeName = getTypeName(def);
  if (typeName === "ZodString") return { type: "string" };
  if (typeName === "ZodNumber") return { type: "number" };
  if (typeName === "ZodBoolean") return { type: "boolean" };
  if (typeName === "ZodNull") return { type: "null" };
  if (typeName === "ZodAny" || typeName === "ZodUnknown") return {};
  if (typeName === "ZodLiteral") {
    const value = def.value;
    if (value === null) {
      return { type: "null", const: value };
    }
    return {
      type: typeof value,
      const: value
    };
  }
  if (typeName === "ZodEnum") {
    const values = def.values;
    if (Array.isArray(values)) {
      return {
        type: "string",
        enum: values
      };
    }
  }
  if (typeName === "ZodObject") {
    const rawShape = def.shape;
    const shape = typeof rawShape === "function" ? rawShape() : rawShape;
    const properties = {};
    const required = [];
    if (shape && typeof shape === "object") {
      for (const [key, value] of Object.entries(shape)) {
        if (!(value instanceof z.ZodType)) continue;
        properties[key] = toJsonSchema(value);
        const valueDef = getDef(value);
        const valueTypeName = getTypeName(valueDef);
        if (valueTypeName !== "ZodOptional" && valueTypeName !== "ZodDefault") {
          required.push(key);
        }
      }
    }
    const schemaObject = {
      type: "object",
      properties
    };
    if (required.length > 0) {
      schemaObject.required = required;
    }
    return schemaObject;
  }
  if (typeName === "ZodArray") {
    const itemType = def.type;
    if (itemType instanceof z.ZodType) {
      return {
        type: "array",
        items: toJsonSchema(itemType)
      };
    }
    return { type: "array", items: {} };
  }
  if (typeName === "ZodTuple") {
    const tupleItems = def.items;
    if (Array.isArray(tupleItems)) {
      const prefixItems = tupleItems.filter((item) => item instanceof z.ZodType).map((item) => toJsonSchema(item));
      return {
        type: "array",
        prefixItems,
        minItems: prefixItems.length,
        maxItems: prefixItems.length
      };
    }
    return { type: "array" };
  }
  if (typeName === "ZodRecord") {
    const valueType = def.valueType;
    if (valueType instanceof z.ZodType) {
      return {
        type: "object",
        additionalProperties: toJsonSchema(valueType)
      };
    }
    return {
      type: "object",
      additionalProperties: {}
    };
  }
  if (typeName === "ZodOptional" || typeName === "ZodDefault") {
    const inner = def.innerType;
    if (inner instanceof z.ZodType) {
      return toJsonSchema(inner);
    }
    return {};
  }
  if (typeName === "ZodNullable") {
    const innerType = def.innerType;
    if (innerType instanceof z.ZodType) {
      return {
        anyOf: [toJsonSchema(innerType), { type: "null" }]
      };
    }
    return { anyOf: [{}, { type: "null" }] };
  }
  if (typeName === "ZodUnion") {
    const options = def.options;
    if (Array.isArray(options)) {
      const converted = options.filter((option) => option instanceof z.ZodType).map((option) => toJsonSchema(option));
      return {
        anyOf: converted
      };
    }
  }
  if (typeName === "ZodDiscriminatedUnion") {
    const options = def.options;
    if (options && typeof options === "object") {
      const values = Array.from(options.values()).filter(
        (option) => option instanceof z.ZodType
      );
      return {
        anyOf: values.map((option) => toJsonSchema(option))
      };
    }
  }
  if (typeName === "ZodEffects" || typeName === "ZodPipeline") {
    const inner = def.schema ?? def.in ?? def.out;
    if (inner instanceof z.ZodType) {
      return toJsonSchema(inner);
    }
  }
  return { type: "object" };
}
function toJsonSchema(schema) {
  return fromSchemaMethod(schema) ?? fromZodToJsonSchema(schema) ?? toJsonSchemaFallback(schema);
}
function tool(definition) {
  const { name, description, parameters, execute } = definition;
  return {
    name,
    description,
    inputSchema: toJsonSchema(parameters),
    execute: async (params) => {
      const parsed = parameters.parse(params);
      return await execute(parsed);
    }
  };
}

export { CODEX_REASONING_EFFORTS, CODEX_SERVICE_TIERS, ExecLanguageModel as CodexCliLanguageModel, DEFAULT_MIN_CODEX_VERSION, ExecLanguageModel, UnsupportedFeatureError, codexAppServer, codexExec as codexCli, codexExec, createCodexAppServer, createCodexExec as createCodexCli, createCodexExec, createLocalMcpServer, createSdkMcpServer, isAuthenticationError, isUnsupportedFeatureError, listModels, tool };