ai-sdk-provider-codex-cli 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'fs';
6
6
  import { tmpdir } from 'os';
7
7
  import path, { join, dirname } from 'path';
8
8
  import { z } from 'zod';
9
- import { generateId, parseProviderOptions } from '@ai-sdk/provider-utils';
9
+ import { generateId, parseProviderOptions, getTopLevelMediaType, isFullMediaType, detectMediaType } from '@ai-sdk/provider-utils';
10
10
  import { createServer } from 'http';
11
11
  import { EventEmitter } from 'events';
12
12
  import readline from 'readline';
@@ -324,75 +324,41 @@ function validateModelId(modelId) {
324
324
  if (!modelId || modelId.trim() === "") return "Model ID cannot be empty";
325
325
  return void 0;
326
326
  }
327
-
328
- // src/converters/tool-result-converter.ts
329
- function safeJsonStringify(value) {
330
- if (value === void 0) return "";
331
- if (typeof value === "string") return value;
332
- try {
333
- return JSON.stringify(value);
334
- } catch {
335
- return "[unserializable]";
336
- }
327
+ function isTaggedFileData(value) {
328
+ if (typeof value !== "object" || value === null || !("type" in value)) return false;
329
+ return value.type === "data" || value.type === "url" || value.type === "reference" || value.type === "text";
337
330
  }
338
- function formatToolResultOutput(output) {
339
- switch (output.type) {
340
- case "text":
341
- return { text: output.value, warnings: [] };
342
- case "json":
343
- return { text: safeJsonStringify(output.value), warnings: [] };
344
- case "execution-denied":
345
- return {
346
- text: output.reason ? `Execution denied: ${output.reason}` : "Execution denied",
347
- warnings: []
348
- };
349
- case "error-text":
350
- return { text: `Tool error: ${output.value}`, warnings: [] };
351
- case "error-json":
352
- return { text: `Tool error: ${safeJsonStringify(output.value)}`, warnings: [] };
353
- case "content": {
354
- const warnings = [];
355
- const parts = output.value.map((part) => {
356
- if (part.type === "text") return part.text;
357
- if (part.type === "file-data") {
358
- return `[file-data: ${part.mediaType}${part.filename ? `, ${part.filename}` : ""}]`;
359
- }
360
- if (part.type === "file-url") return `[file-url: ${part.url}]`;
361
- if (part.type === "file-id") return "[file-id]";
362
- if (part.type === "image-data") return `[image-data: ${part.mediaType}]`;
363
- if (part.type === "image-url") return `[image-url: ${part.url}]`;
364
- if (part.type === "image-file-id") return "[image-file-id]";
365
- warnings.push({
366
- type: "unsupported",
367
- feature: `tool-result.content.${String(part.type)}`,
368
- details: `Unsupported tool content part "${String(part.type)}".`
369
- });
370
- return "[unsupported-tool-content-part]";
371
- }).filter((part) => part.length > 0);
372
- return { text: parts.join("\n"), warnings };
373
- }
374
- default:
375
- return {
376
- text: "[unsupported-tool-result-output]",
377
- warnings: [
378
- {
379
- type: "unsupported",
380
- feature: `tool-result.output.${String(output.type)}`,
381
- details: `Unsupported tool result output type "${String(output.type)}".`
382
- }
383
- ]
384
- };
331
+ function resolveImageMimeType(declaredType, data) {
332
+ if (isFullMediaType(declaredType)) return declaredType;
333
+ const bytes = typeof data === "string" || data instanceof Uint8Array ? data : data instanceof ArrayBuffer ? new Uint8Array(data) : void 0;
334
+ if (bytes !== void 0) {
335
+ const detected = detectMediaType({
336
+ data: bytes,
337
+ topLevelType: getTopLevelMediaType(declaredType)
338
+ });
339
+ if (detected) return detected;
385
340
  }
341
+ return "image/png";
386
342
  }
387
343
  function extractImageData(part) {
388
344
  if (typeof part !== "object" || part === null) return null;
389
345
  const p = part;
390
346
  const isFilePart2 = p.type === "file";
391
- const mimeType = isFilePart2 ? p.mediaType || "image/png" : p.mimeType || "image/png";
392
- if (isFilePart2 && !mimeType.toLowerCase().startsWith("image/")) {
347
+ const declaredType = isFilePart2 ? p.mediaType || "image/png" : p.mimeType || "image/png";
348
+ if (isFilePart2 && getTopLevelMediaType(declaredType).toLowerCase() !== "image") {
393
349
  return null;
394
350
  }
395
- const primaryInput = isFilePart2 ? p.data : p.image;
351
+ let primaryInput = isFilePart2 ? p.data : p.image;
352
+ if (isTaggedFileData(primaryInput)) {
353
+ if (primaryInput.type === "data") {
354
+ primaryInput = primaryInput.data;
355
+ } else if (primaryInput.type === "url") {
356
+ primaryInput = primaryInput.url;
357
+ } else {
358
+ return null;
359
+ }
360
+ }
361
+ const mimeType = resolveImageMimeType(declaredType, primaryInput);
396
362
  if (typeof primaryInput === "string") {
397
363
  return extractFromString(primaryInput, mimeType);
398
364
  }
@@ -538,14 +504,18 @@ function asRemoteUrl(value) {
538
504
  return void 0;
539
505
  }
540
506
  function isImageMediaType(mediaType) {
541
- return mediaType.toLowerCase().startsWith("image/");
507
+ return getTopLevelMediaType(mediaType).toLowerCase() === "image";
542
508
  }
543
509
  function isFilePart(part) {
544
- return part.type === "file" && typeof part.mediaType === "string";
510
+ return part.type === "file" && "mediaType" in part && typeof part.mediaType === "string";
545
511
  }
546
512
  function isCompatImagePart(part) {
547
513
  return part.type === "image";
548
514
  }
515
+ function isTaggedFileData2(value) {
516
+ if (typeof value !== "object" || value === null || !("type" in value)) return false;
517
+ return value.type === "data" || value.type === "url" || value.type === "reference" || value.type === "text";
518
+ }
549
519
  function toImageReference(part) {
550
520
  if (isFilePart(part)) {
551
521
  if (!isImageMediaType(part.mediaType)) {
@@ -554,7 +524,42 @@ function toImageReference(part) {
554
524
  warning: `Unsupported file mediaType "${part.mediaType}"; only image/* is supported.`
555
525
  };
556
526
  }
557
- const remote = asRemoteUrl(part.data);
527
+ const data = part.data;
528
+ if (isTaggedFileData2(data)) {
529
+ if (data.type === "reference") {
530
+ return {
531
+ kind: "unsupported",
532
+ warning: "File reference data is not supported for images."
533
+ };
534
+ }
535
+ if (data.type === "text") {
536
+ return {
537
+ kind: "unsupported",
538
+ warning: "Inline text file data is not supported for images."
539
+ };
540
+ }
541
+ if (data.type === "url") {
542
+ const remote2 = asRemoteUrl(data.url);
543
+ if (remote2) {
544
+ return { kind: "remote", url: remote2 };
545
+ }
546
+ if (isFileUrlValue(data.url)) {
547
+ return {
548
+ kind: "unsupported",
549
+ warning: "file:// image URLs are not supported."
550
+ };
551
+ }
552
+ }
553
+ const image2 = extractImageData(part);
554
+ if (image2) {
555
+ return { kind: "local", image: image2 };
556
+ }
557
+ return {
558
+ kind: "unsupported",
559
+ warning: "Unsupported image format in message."
560
+ };
561
+ }
562
+ const remote = asRemoteUrl(data);
558
563
  if (remote) {
559
564
  return { kind: "remote", url: remote };
560
565
  }
@@ -562,7 +567,7 @@ function toImageReference(part) {
562
567
  if (image) {
563
568
  return { kind: "local", image };
564
569
  }
565
- if (isFileUrlValue(part.data) || isFileUrlValue(part.url)) {
570
+ if (isFileUrlValue(data) || "url" in part && isFileUrlValue(part.url)) {
566
571
  return {
567
572
  kind: "unsupported",
568
573
  warning: "file:// image URLs are not supported."
@@ -596,6 +601,90 @@ function toImageReference(part) {
596
601
  return void 0;
597
602
  }
598
603
 
604
+ // src/converters/tool-result-converter.ts
605
+ function safeJsonStringify(value) {
606
+ if (value === void 0) return "";
607
+ if (typeof value === "string") return value;
608
+ try {
609
+ return JSON.stringify(value);
610
+ } catch {
611
+ return "[unserializable]";
612
+ }
613
+ }
614
+ function describeTypeTag(value) {
615
+ if (typeof value === "object" && value !== null && "type" in value) {
616
+ return String(value.type);
617
+ }
618
+ return "unknown";
619
+ }
620
+ function formatToolResultOutput(output) {
621
+ switch (output.type) {
622
+ case "text":
623
+ return { text: output.value, warnings: [] };
624
+ case "json":
625
+ return { text: safeJsonStringify(output.value), warnings: [] };
626
+ case "execution-denied":
627
+ return {
628
+ text: output.reason ? `Execution denied: ${output.reason}` : "Execution denied",
629
+ warnings: []
630
+ };
631
+ case "error-text":
632
+ return { text: `Tool error: ${output.value}`, warnings: [] };
633
+ case "error-json":
634
+ return { text: `Tool error: ${safeJsonStringify(output.value)}`, warnings: [] };
635
+ case "content": {
636
+ const warnings = [];
637
+ const parts = output.value.map((part) => {
638
+ if (part.type === "text") return part.text;
639
+ if (part.type === "file") {
640
+ const prefix = isImageMediaType(part.mediaType) ? "image" : "file";
641
+ const data = part.data;
642
+ if (data.type === "data") {
643
+ return prefix === "image" ? `[image-data: ${part.mediaType}]` : `[file-data: ${part.mediaType}${part.filename ? `, ${part.filename}` : ""}]`;
644
+ }
645
+ if (data.type === "url") return `[${prefix}-url: ${data.url}]`;
646
+ if (data.type === "reference") {
647
+ return prefix === "image" ? "[image-file-id]" : "[file-id]";
648
+ }
649
+ if (data.type === "text") return data.text;
650
+ warnings.push({
651
+ type: "unsupported",
652
+ feature: "tool-result.content.file",
653
+ details: `Unsupported tool file content data type "${describeTypeTag(data)}".`
654
+ });
655
+ return "[unsupported-tool-content-part]";
656
+ }
657
+ if (part.type === "custom") {
658
+ warnings.push({
659
+ type: "unsupported",
660
+ feature: "tool-result.content.custom",
661
+ details: "Custom tool content parts are not supported."
662
+ });
663
+ return "";
664
+ }
665
+ warnings.push({
666
+ type: "unsupported",
667
+ feature: `tool-result.content.${describeTypeTag(part)}`,
668
+ details: `Unsupported tool content part "${describeTypeTag(part)}".`
669
+ });
670
+ return "[unsupported-tool-content-part]";
671
+ }).filter((part) => part.length > 0);
672
+ return { text: parts.join("\n"), warnings };
673
+ }
674
+ default:
675
+ return {
676
+ text: "[unsupported-tool-result-output]",
677
+ warnings: [
678
+ {
679
+ type: "unsupported",
680
+ feature: `tool-result.output.${describeTypeTag(output)}`,
681
+ details: `Unsupported tool result output type "${describeTypeTag(output)}".`
682
+ }
683
+ ]
684
+ };
685
+ }
686
+ }
687
+
599
688
  // src/converters/prompt-converter.ts
600
689
  function isTextPart(part) {
601
690
  return part.type === "text" && typeof part.text === "string";
@@ -730,6 +819,23 @@ function formatAssistantParts(parts) {
730
819
  lines.push(`[assistant-file: ${part.mediaType}]`);
731
820
  continue;
732
821
  }
822
+ if (part.type === "custom") {
823
+ const kind = "kind" in part && typeof part.kind === "string" ? part.kind : void 0;
824
+ warnings.push({
825
+ type: "unsupported",
826
+ feature: "prompt.assistant.custom",
827
+ details: `Custom content parts are not supported${kind ? ` (kind "${kind}")` : ""}.`
828
+ });
829
+ continue;
830
+ }
831
+ if (part.type === "reasoning-file") {
832
+ warnings.push({
833
+ type: "unsupported",
834
+ feature: "prompt.assistant.reasoning-file",
835
+ details: "Reasoning file parts are not supported."
836
+ });
837
+ continue;
838
+ }
733
839
  warnings.push({
734
840
  type: "unsupported",
735
841
  feature: `prompt.assistant.${part.type}`,
@@ -1146,7 +1252,11 @@ function mapUnsupportedSettingsWarnings(options) {
1146
1252
  add(options.stopSequences?.length ? options.stopSequences : void 0, "stopSequences");
1147
1253
  add(options.seed, "seed");
1148
1254
  add(options.tools, "tools");
1149
- add(options.toolChoice, "toolChoice");
1255
+ const toolChoice = options.toolChoice;
1256
+ add(
1257
+ toolChoice !== void 0 && toolChoice.type === "auto" ? void 0 : toolChoice,
1258
+ "toolChoice"
1259
+ );
1150
1260
  return unsupported;
1151
1261
  }
1152
1262
  function mcpServersToConfigOverrides(mcpServers, rmcpClient) {
@@ -1200,6 +1310,14 @@ function mcpServersToConfigOverrides(mcpServers, rmcpClient) {
1200
1310
  }
1201
1311
 
1202
1312
  // src/exec-language-model.ts
1313
+ var codexReasoningEfforts = {
1314
+ none: true,
1315
+ minimal: true,
1316
+ low: true,
1317
+ medium: true,
1318
+ high: true,
1319
+ xhigh: true
1320
+ };
1203
1321
  var codexCliProviderOptionsSchema = z.object({
1204
1322
  reasoningEffort: z.enum(["none", "minimal", "low", "medium", "high", "xhigh"]).optional(),
1205
1323
  reasoningSummary: z.enum(["auto", "detailed"]).optional(),
@@ -1240,12 +1358,9 @@ function resolveCodexPath(explicitPath, allowNpx) {
1240
1358
  }
1241
1359
  }
1242
1360
  var ExecLanguageModel = class {
1243
- specificationVersion = "v3";
1361
+ specificationVersion = "v4";
1244
1362
  provider = "codex-cli";
1245
- defaultObjectGenerationMode = "json";
1246
- supportsImageUrls = false;
1247
1363
  supportedUrls = {};
1248
- supportsStructuredOutputs = true;
1249
1364
  modelId;
1250
1365
  settings;
1251
1366
  logger;
@@ -1874,7 +1989,22 @@ var ExecLanguageModel = class {
1874
1989
  providerOptions: options.providerOptions,
1875
1990
  schema: codexCliProviderOptionsSchema
1876
1991
  });
1877
- const effectiveSettings = this.mergeSettings(providerOptions);
1992
+ let effectiveSettings = this.mergeSettings(providerOptions);
1993
+ const topLevelReasoning = options.reasoning;
1994
+ if (topLevelReasoning !== void 0 && topLevelReasoning !== "provider-default" && providerOptions?.reasoningEffort === void 0) {
1995
+ if (Object.hasOwn(codexReasoningEfforts, topLevelReasoning)) {
1996
+ effectiveSettings = {
1997
+ ...effectiveSettings,
1998
+ reasoningEffort: topLevelReasoning
1999
+ };
2000
+ } else {
2001
+ warnings.push({
2002
+ type: "unsupported",
2003
+ feature: "reasoning",
2004
+ details: `Codex CLI does not support reasoning effort '${topLevelReasoning}'; it will be ignored.`
2005
+ });
2006
+ }
2007
+ }
1878
2008
  const responseFormat = options.responseFormat?.type === "json" ? { type: "json", schema: options.responseFormat.schema } : void 0;
1879
2009
  const { cmd, args, env, cwd, lastMessagePath, lastMessageIsTemp, schemaPath, tempImagePaths } = this.buildArgs(images, responseFormat, effectiveSettings);
1880
2010
  this.logger.debug(
@@ -1889,6 +2019,7 @@ var ExecLanguageModel = class {
1889
2019
  controller.enqueue({ type: "stream-start", warnings });
1890
2020
  let stderr = "";
1891
2021
  let accumulatedText = "";
2022
+ let stdoutBuffer = "";
1892
2023
  const activeTools = /* @__PURE__ */ new Map();
1893
2024
  let responseMetadataSent = false;
1894
2025
  let lastUsage;
@@ -2046,57 +2177,62 @@ var ExecLanguageModel = class {
2046
2177
  });
2047
2178
  controller.close();
2048
2179
  };
2049
- child.stderr.on("data", (d) => stderr += String(d));
2050
- child.stdout.setEncoding("utf8");
2051
- child.stdout.on("data", (chunk) => {
2052
- const lines = chunk.split(/\r?\n/).filter(Boolean);
2053
- for (const line of lines) {
2054
- const event = this.parseExperimentalJsonEvent(line);
2055
- if (!event) continue;
2056
- this.logger.debug(`[codex-cli] Stream event: ${event.type ?? "unknown"}`);
2057
- if (event.type === "thread.started" && typeof event.thread_id === "string") {
2058
- this.sessionId = event.thread_id;
2059
- this.logger.debug(`[codex-cli] Stream session started: ${this.sessionId}`);
2060
- if (!responseMetadataSent) {
2061
- responseMetadataSent = true;
2062
- sendMetadata();
2063
- }
2064
- continue;
2180
+ const processLine = (line) => {
2181
+ const event = this.parseExperimentalJsonEvent(line);
2182
+ if (!event) return;
2183
+ this.logger.debug(`[codex-cli] Stream event: ${event.type ?? "unknown"}`);
2184
+ if (event.type === "thread.started" && typeof event.thread_id === "string") {
2185
+ this.sessionId = event.thread_id;
2186
+ this.logger.debug(`[codex-cli] Stream session started: ${this.sessionId}`);
2187
+ if (!responseMetadataSent) {
2188
+ responseMetadataSent = true;
2189
+ sendMetadata();
2065
2190
  }
2066
- if (event.type === "session.created" && typeof event.session_id === "string") {
2067
- this.sessionId = event.session_id;
2068
- this.logger.debug(`[codex-cli] Stream session created: ${this.sessionId}`);
2069
- if (!responseMetadataSent) {
2070
- responseMetadataSent = true;
2071
- sendMetadata();
2072
- }
2073
- continue;
2074
- }
2075
- if (event.type === "turn.completed") {
2076
- const usageEvent = this.extractUsage(event);
2077
- if (usageEvent) {
2078
- lastUsage = usageEvent;
2079
- }
2080
- continue;
2081
- }
2082
- if (event.type === "turn.failed") {
2083
- const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
2084
- turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
2085
- this.logger.error(`[codex-cli] Stream turn failed: ${turnFailureMessage}`);
2086
- sendMetadata({ error: turnFailureMessage });
2087
- continue;
2088
- }
2089
- if (event.type === "error") {
2090
- const errorText = typeof event.message === "string" ? event.message : void 0;
2091
- const effective = errorText ?? "Codex error";
2092
- turnFailureMessage = turnFailureMessage ?? effective;
2093
- this.logger.error(`[codex-cli] Stream error event: ${effective}`);
2094
- sendMetadata({ error: effective });
2095
- continue;
2191
+ return;
2192
+ }
2193
+ if (event.type === "session.created" && typeof event.session_id === "string") {
2194
+ this.sessionId = event.session_id;
2195
+ this.logger.debug(`[codex-cli] Stream session created: ${this.sessionId}`);
2196
+ if (!responseMetadataSent) {
2197
+ responseMetadataSent = true;
2198
+ sendMetadata();
2096
2199
  }
2097
- if (event.type && event.type.startsWith("item.")) {
2098
- handleItemEvent(event);
2200
+ return;
2201
+ }
2202
+ if (event.type === "turn.completed") {
2203
+ const usageEvent = this.extractUsage(event);
2204
+ if (usageEvent) {
2205
+ lastUsage = usageEvent;
2099
2206
  }
2207
+ return;
2208
+ }
2209
+ if (event.type === "turn.failed") {
2210
+ const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
2211
+ turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
2212
+ this.logger.error(`[codex-cli] Stream turn failed: ${turnFailureMessage}`);
2213
+ sendMetadata({ error: turnFailureMessage });
2214
+ return;
2215
+ }
2216
+ if (event.type === "error") {
2217
+ const errorText = typeof event.message === "string" ? event.message : void 0;
2218
+ const effective = errorText ?? "Codex error";
2219
+ turnFailureMessage = turnFailureMessage ?? effective;
2220
+ this.logger.error(`[codex-cli] Stream error event: ${effective}`);
2221
+ sendMetadata({ error: effective });
2222
+ return;
2223
+ }
2224
+ if (event.type && event.type.startsWith("item.")) {
2225
+ handleItemEvent(event);
2226
+ }
2227
+ };
2228
+ child.stderr.on("data", (d) => stderr += String(d));
2229
+ child.stdout.setEncoding("utf8");
2230
+ child.stdout.on("data", (chunk) => {
2231
+ const pieces = (stdoutBuffer + chunk).split(/\r?\n/);
2232
+ stdoutBuffer = pieces.pop() ?? "";
2233
+ for (const line of pieces) {
2234
+ if (!line) continue;
2235
+ processLine(line);
2100
2236
  }
2101
2237
  });
2102
2238
  child.on("error", (e) => {
@@ -2108,7 +2244,13 @@ var ExecLanguageModel = class {
2108
2244
  child.on("close", (code) => {
2109
2245
  if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
2110
2246
  cleanupTempFiles();
2111
- setImmediate(() => finishStream(code));
2247
+ setImmediate(() => {
2248
+ if (stdoutBuffer) {
2249
+ processLine(stdoutBuffer);
2250
+ stdoutBuffer = "";
2251
+ }
2252
+ finishStream(code);
2253
+ });
2112
2254
  });
2113
2255
  },
2114
2256
  cancel: () => {
@@ -2140,7 +2282,7 @@ function createCodexExec(options = {}) {
2140
2282
  if (new.target) throw new Error("The Codex CLI provider function cannot be called with new.");
2141
2283
  return createModel(modelId, settings);
2142
2284
  },
2143
- { specificationVersion: "v3" }
2285
+ { specificationVersion: "v4" }
2144
2286
  );
2145
2287
  provider.languageModel = createModel;
2146
2288
  provider.chat = createModel;
@@ -2335,6 +2477,14 @@ function mapTool(item) {
2335
2477
  dynamic: true
2336
2478
  };
2337
2479
  }
2480
+ if (type === "dynamictoolcall") {
2481
+ const tool2 = "tool" in item && typeof item.tool === "string" && item.tool ? item.tool : "tool";
2482
+ const namespace = "namespace" in item && typeof item.namespace === "string" && item.namespace ? item.namespace : void 0;
2483
+ return {
2484
+ toolName: namespace ? `${namespace}__${tool2}` : tool2,
2485
+ dynamic: true
2486
+ };
2487
+ }
2338
2488
  if (type === "websearch") {
2339
2489
  return { toolName: "web_search", dynamic: true };
2340
2490
  }
@@ -3409,6 +3559,34 @@ function isSdkMcpServer(value) {
3409
3559
  }
3410
3560
 
3411
3561
  // src/app-server/language-model.ts
3562
+ var CODEX_REASONING_EFFORTS = {
3563
+ none: true,
3564
+ minimal: true,
3565
+ low: true,
3566
+ medium: true,
3567
+ high: true,
3568
+ xhigh: true
3569
+ };
3570
+ function resolveReasoningEffort(args) {
3571
+ if (args.providerEffort !== void 0) {
3572
+ return { effort: args.providerEffort };
3573
+ }
3574
+ const { reasoning } = args;
3575
+ if (reasoning === void 0 || reasoning === "provider-default") {
3576
+ return { effort: args.defaultEffort };
3577
+ }
3578
+ if (Object.hasOwn(CODEX_REASONING_EFFORTS, reasoning)) {
3579
+ return { effort: reasoning };
3580
+ }
3581
+ return {
3582
+ effort: args.defaultEffort,
3583
+ warning: {
3584
+ type: "unsupported",
3585
+ feature: "reasoning",
3586
+ details: `Codex app-server does not support reasoning effort '${reasoning}'; it will be ignored.`
3587
+ }
3588
+ };
3589
+ }
3412
3590
  function isThreadNotFoundError2(error) {
3413
3591
  const message = String(error?.message ?? error);
3414
3592
  return /thread.*not found/i.test(message);
@@ -3470,12 +3648,9 @@ function mergeAppServerMcpServers(base, override) {
3470
3648
  return merged;
3471
3649
  }
3472
3650
  var AppServerLanguageModel = class {
3473
- specificationVersion = "v3";
3651
+ specificationVersion = "v4";
3474
3652
  provider = "codex-app-server";
3475
- defaultObjectGenerationMode = "json";
3476
- supportsImageUrls = true;
3477
3653
  supportedUrls = {};
3478
- supportsStructuredOutputs = true;
3479
3654
  modelId;
3480
3655
  settings;
3481
3656
  client;
@@ -3484,6 +3659,7 @@ var AppServerLanguageModel = class {
3484
3659
  onSdkMcpServerReleased;
3485
3660
  persistentThreadId;
3486
3661
  persistentThreadRawEventsEnabled;
3662
+ rawEventsByThreadId = /* @__PURE__ */ new Map();
3487
3663
  persistentSession;
3488
3664
  persistentBootstrapLock = Promise.resolve();
3489
3665
  constructor(options) {
@@ -3617,7 +3793,14 @@ var AppServerLanguageModel = class {
3617
3793
  return { input, tempImagePaths };
3618
3794
  }
3619
3795
  async startOrResumeThread(args) {
3620
- const { settings, providerOptions, configOverrides, developerInstructions, includeRawChunks } = args;
3796
+ const {
3797
+ settings,
3798
+ providerOptions,
3799
+ configOverrides,
3800
+ developerInstructionsOverride,
3801
+ systemInstruction,
3802
+ includeRawChunks
3803
+ } = args;
3621
3804
  const threadState = this.resolveTargetThreadId(settings, providerOptions);
3622
3805
  const startThread = async (ephemeral) => {
3623
3806
  const thread = await this.client.threadStart({
@@ -3627,7 +3810,7 @@ var AppServerLanguageModel = class {
3627
3810
  sandbox: mapSandboxToThreadSandboxMode(settings),
3628
3811
  config: configOverrides,
3629
3812
  baseInstructions: settings.baseInstructions,
3630
- developerInstructions,
3813
+ developerInstructions: developerInstructionsOverride ?? systemInstruction,
3631
3814
  personality: settings.personality,
3632
3815
  ephemeral,
3633
3816
  experimentalRawEvents: includeRawChunks,
@@ -3646,16 +3829,18 @@ var AppServerLanguageModel = class {
3646
3829
  sandbox: mapSandboxToThreadSandboxMode(settings),
3647
3830
  config: configOverrides,
3648
3831
  baseInstructions: settings.baseInstructions,
3649
- developerInstructions,
3832
+ developerInstructions: developerInstructionsOverride ?? systemInstruction,
3650
3833
  personality: settings.personality,
3651
3834
  persistExtendedHistory: settings.persistExtendedHistory ?? false
3652
3835
  });
3653
- const knownRawEvents = target.persistent && this.persistentThreadId === target.threadId ? this.persistentThreadRawEventsEnabled : void 0;
3836
+ const cachedRawEvents = this.rawEventsByThreadId.get(target.threadId);
3837
+ const knownRawEvents = cachedRawEvents ?? (target.persistent && this.persistentThreadId === target.threadId ? this.persistentThreadRawEventsEnabled : void 0);
3838
+ if (cachedRawEvents !== void 0) {
3839
+ this.rememberThreadRawEvents(target.threadId, cachedRawEvents);
3840
+ }
3654
3841
  if (target.persistent) {
3655
3842
  this.persistentThreadId = target.threadId;
3656
- if (target.explicit) {
3657
- this.persistentThreadRawEventsEnabled = void 0;
3658
- }
3843
+ this.persistentThreadRawEventsEnabled = knownRawEvents;
3659
3844
  }
3660
3845
  return {
3661
3846
  threadId: target.threadId,
@@ -3683,6 +3868,7 @@ var AppServerLanguageModel = class {
3683
3868
  });
3684
3869
  }
3685
3870
  const newThreadId = await startThread(false);
3871
+ this.rememberThreadRawEvents(newThreadId, includeRawChunks);
3686
3872
  this.persistentThreadId = newThreadId;
3687
3873
  this.persistentThreadRawEventsEnabled = includeRawChunks;
3688
3874
  return {
@@ -3695,6 +3881,7 @@ var AppServerLanguageModel = class {
3695
3881
  }
3696
3882
  if (!threadState.threadId) {
3697
3883
  const newThreadId = await startThread(!threadState.persistent);
3884
+ this.rememberThreadRawEvents(newThreadId, includeRawChunks);
3698
3885
  if (threadState.persistent) {
3699
3886
  this.persistentThreadId = newThreadId;
3700
3887
  this.persistentThreadRawEventsEnabled = includeRawChunks;
@@ -3736,12 +3923,26 @@ var AppServerLanguageModel = class {
3736
3923
  }
3737
3924
  }
3738
3925
  }
3926
+ rememberThreadRawEvents(threadId, enabled) {
3927
+ this.rawEventsByThreadId.delete(threadId);
3928
+ this.rawEventsByThreadId.set(threadId, enabled);
3929
+ if (this.rawEventsByThreadId.size <= 256) {
3930
+ return;
3931
+ }
3932
+ const oldestThreadId = this.rawEventsByThreadId.keys().next().value;
3933
+ if (typeof oldestThreadId === "string") {
3934
+ this.rawEventsByThreadId.delete(oldestThreadId);
3935
+ }
3936
+ }
3739
3937
  clearPersistentThreadState(threadId) {
3740
3938
  if (threadId && this.persistentThreadId && this.persistentThreadId !== threadId) {
3741
3939
  return;
3742
3940
  }
3743
3941
  this.persistentThreadId = void 0;
3744
3942
  this.persistentThreadRawEventsEnabled = void 0;
3943
+ if (threadId) {
3944
+ this.rawEventsByThreadId.delete(threadId);
3945
+ }
3745
3946
  if (!threadId || this.persistentSession?.threadId === threadId) {
3746
3947
  this.persistentSession = void 0;
3747
3948
  }
@@ -3962,7 +4163,13 @@ var AppServerLanguageModel = class {
3962
4163
  providerOptions: options.providerOptions,
3963
4164
  schema: appServerProviderOptionsSchema
3964
4165
  });
3965
- const settings = this.mergeSettings(providerOptions);
4166
+ const mergedSettings = this.mergeSettings(providerOptions);
4167
+ const resolvedReasoning = resolveReasoningEffort({
4168
+ reasoning: options.reasoning,
4169
+ providerEffort: providerOptions?.effort,
4170
+ defaultEffort: mergedSettings.effort
4171
+ });
4172
+ const settings = resolvedReasoning.effort === mergedSettings.effort ? mergedSettings : { ...mergedSettings, effort: resolvedReasoning.effort };
3966
4173
  const sdkServerLifecycle = this.resolveThreadMode(settings, providerOptions) === "persistent" ? "provider" : "request";
3967
4174
  const includeRawChunks = this.resolveIncludeRawChunks(
3968
4175
  options.includeRawChunks,
@@ -3983,11 +4190,11 @@ var AppServerLanguageModel = class {
3983
4190
  toolChoice: options.toolChoice
3984
4191
  })
3985
4192
  ];
4193
+ if (resolvedReasoning.warning) {
4194
+ warnings.push(resolvedReasoning.warning);
4195
+ }
3986
4196
  const developerInstructionsOverride = providerOptions?.developerInstructions ?? settings.developerInstructions;
3987
- const threadState = this.resolveTargetThreadId(settings, providerOptions);
3988
- const prompt = this.preparePrompt(options.prompt, Boolean(threadState.threadId));
3989
- warnings.push(...prompt.warnings);
3990
- const effectiveDeveloperInstructions = developerInstructionsOverride ?? (!threadState.threadId ? prompt.systemInstruction : void 0);
4197
+ const systemInstruction = collectSystemInstruction(options.prompt);
3991
4198
  const resolvedConfig = await this.resolveConfig(settings, sdkServerLifecycle);
3992
4199
  let releasedSdkServers = false;
3993
4200
  const releaseUsedSdkMcpServers = () => {
@@ -4005,7 +4212,8 @@ var AppServerLanguageModel = class {
4005
4212
  settings,
4006
4213
  providerOptions,
4007
4214
  configOverrides: resolvedConfig.configOverrides,
4008
- developerInstructions: effectiveDeveloperInstructions,
4215
+ developerInstructionsOverride,
4216
+ systemInstruction,
4009
4217
  includeRawChunks
4010
4218
  });
4011
4219
  } catch (error) {
@@ -4019,6 +4227,8 @@ var AppServerLanguageModel = class {
4019
4227
  message: "includeRawChunks was requested while resuming an existing thread that may not emit raw events. Start a new thread to guarantee raw chunk events."
4020
4228
  });
4021
4229
  }
4230
+ const prompt = this.preparePrompt(options.prompt, threadResolution.resumed);
4231
+ warnings.push(...prompt.warnings);
4022
4232
  let input = [];
4023
4233
  let tempImagePaths = [];
4024
4234
  let session;
@@ -4059,7 +4269,7 @@ var AppServerLanguageModel = class {
4059
4269
  session,
4060
4270
  abortSignal: options.abortSignal,
4061
4271
  shouldSerializeTurnStart: threadResolution.persistent || threadResolution.explicit,
4062
- hadInitialThreadId: Boolean(threadState.threadId),
4272
+ hadInitialThreadId: threadResolution.resumed,
4063
4273
  threadResolution: {
4064
4274
  persistent: threadResolution.persistent,
4065
4275
  explicit: threadResolution.explicit
@@ -4229,7 +4439,46 @@ var contextCompactionItemSchema = z.object({
4229
4439
  type: z.literal("contextCompaction"),
4230
4440
  id: z.string()
4231
4441
  }).passthrough();
4232
- var threadItemSchema = z.discriminatedUnion("type", [
4442
+ var hookPromptItemSchema = z.object({
4443
+ type: z.literal("hookPrompt"),
4444
+ id: z.string(),
4445
+ fragments: z.array(z.unknown())
4446
+ }).passthrough();
4447
+ var dynamicToolCallItemSchema = z.object({
4448
+ type: z.literal("dynamicToolCall"),
4449
+ id: z.string(),
4450
+ namespace: z.string().nullable(),
4451
+ tool: z.string(),
4452
+ arguments: z.unknown(),
4453
+ status: z.string(),
4454
+ contentItems: z.array(z.unknown()).nullable(),
4455
+ success: z.boolean().nullable(),
4456
+ durationMs: z.number().nullable()
4457
+ }).passthrough();
4458
+ var subAgentActivityItemSchema = z.object({
4459
+ type: z.literal("subAgentActivity"),
4460
+ id: z.string(),
4461
+ kind: z.string(),
4462
+ agentThreadId: z.string(),
4463
+ agentPath: z.string()
4464
+ }).passthrough();
4465
+ var sleepItemSchema = z.object({
4466
+ type: z.literal("sleep"),
4467
+ id: z.string(),
4468
+ durationMs: z.number()
4469
+ }).passthrough();
4470
+ var imageGenerationItemSchema = z.object({
4471
+ type: z.literal("imageGeneration"),
4472
+ id: z.string(),
4473
+ status: z.string(),
4474
+ revisedPrompt: z.string().nullable(),
4475
+ result: z.string(),
4476
+ savedPath: z.string().optional()
4477
+ }).passthrough();
4478
+ var unknownThreadItemSchema = z.object({
4479
+ type: z.string()
4480
+ }).passthrough();
4481
+ var knownThreadItemSchema = z.discriminatedUnion("type", [
4233
4482
  userMessageItemSchema,
4234
4483
  agentMessageItemSchema,
4235
4484
  planItemSchema,
@@ -4237,13 +4486,19 @@ var threadItemSchema = z.discriminatedUnion("type", [
4237
4486
  commandExecutionItemSchema,
4238
4487
  fileChangeItemSchema,
4239
4488
  mcpToolCallItemSchema,
4489
+ dynamicToolCallItemSchema,
4240
4490
  collabAgentToolCallItemSchema,
4491
+ hookPromptItemSchema,
4492
+ subAgentActivityItemSchema,
4493
+ sleepItemSchema,
4494
+ imageGenerationItemSchema,
4241
4495
  webSearchItemSchema,
4242
4496
  imageViewItemSchema,
4243
4497
  enteredReviewModeItemSchema,
4244
4498
  exitedReviewModeItemSchema,
4245
4499
  contextCompactionItemSchema
4246
4500
  ]);
4501
+ var threadItemSchema = knownThreadItemSchema.or(unknownThreadItemSchema);
4247
4502
  var codexHttpStatusCodeSchema = z.object({
4248
4503
  httpStatusCode: z.number().nullable()
4249
4504
  }).passthrough();
@@ -4252,6 +4507,7 @@ var codexErrorInfoSchema = z.union([
4252
4507
  "contextWindowExceeded",
4253
4508
  "usageLimitExceeded",
4254
4509
  "serverOverloaded",
4510
+ "cyberPolicy",
4255
4511
  "internalServerError",
4256
4512
  "unauthorized",
4257
4513
  "badRequest",
@@ -4262,17 +4518,23 @@ var codexErrorInfoSchema = z.union([
4262
4518
  z.object({ httpConnectionFailed: codexHttpStatusCodeSchema }).passthrough(),
4263
4519
  z.object({ responseStreamConnectionFailed: codexHttpStatusCodeSchema }).passthrough(),
4264
4520
  z.object({ responseStreamDisconnected: codexHttpStatusCodeSchema }).passthrough(),
4265
- z.object({ responseTooManyFailedAttempts: codexHttpStatusCodeSchema }).passthrough()
4521
+ z.object({ responseTooManyFailedAttempts: codexHttpStatusCodeSchema }).passthrough(),
4522
+ z.object({ activeTurnNotSteerable: z.object({ turnKind: z.string() }).passthrough() }).passthrough(),
4523
+ // Forward-compat catch-alls: consumers only compare against the known string
4524
+ // codes above (unknown codes fall back to the generic error path), so a new
4525
+ // errorInfo variant must never fail validation and drop the notification.
4526
+ z.string(),
4527
+ z.record(z.string(), z.unknown())
4266
4528
  ]);
4267
4529
  var turnSchema = z.object({
4268
4530
  id: z.string(),
4269
4531
  items: z.array(threadItemSchema),
4270
- status: z.enum(["completed", "interrupted", "failed", "inProgress"]),
4532
+ status: z.string(),
4271
4533
  error: z.object({
4272
4534
  message: z.string(),
4273
- codexErrorInfo: codexErrorInfoSchema.nullable(),
4274
- additionalDetails: z.string().nullable()
4275
- }).nullable()
4535
+ codexErrorInfo: codexErrorInfoSchema.nullish(),
4536
+ additionalDetails: z.string().nullish()
4537
+ }).nullish()
4276
4538
  }).passthrough();
4277
4539
  var threadStartedNotificationSchema = z.object({
4278
4540
  thread: z.object({ id: z.string() }).passthrough()
@@ -4565,7 +4827,6 @@ var AppServerRpcClient = class extends EventEmitter {
4565
4827
  activeRequestContextsByTurn = /* @__PURE__ */ new Map();
4566
4828
  completedTurnIds = /* @__PURE__ */ new Set();
4567
4829
  lastStderr = "";
4568
- lastCrashHadStderr = false;
4569
4830
  idleTimer;
4570
4831
  serverCapabilities;
4571
4832
  expectedExitSignal;
@@ -4660,7 +4921,7 @@ var AppServerRpcClient = class extends EventEmitter {
4660
4921
  if (this.serverCapabilities?.modelList === false) {
4661
4922
  throw new UnsupportedFeatureError({
4662
4923
  feature: "model/list",
4663
- minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4924
+ minCodexVersion: this.settings.minCodexVersion ?? "0.142.5",
4664
4925
  serverVersion: this.serverVersion
4665
4926
  });
4666
4927
  }
@@ -4673,7 +4934,7 @@ var AppServerRpcClient = class extends EventEmitter {
4673
4934
  if (error instanceof JsonRpcRequestError && error.code === -32601) {
4674
4935
  throw new UnsupportedFeatureError({
4675
4936
  feature: "model/list",
4676
- minCodexVersion: this.settings.minCodexVersion ?? "0.144.0",
4937
+ minCodexVersion: this.settings.minCodexVersion ?? "0.142.5",
4677
4938
  serverVersion: this.serverVersion
4678
4939
  });
4679
4940
  }
@@ -4772,7 +5033,6 @@ var AppServerRpcClient = class extends EventEmitter {
4772
5033
  const base = resolveCodexPath2(this.settings.codexPath);
4773
5034
  const args = [...base.args, "app-server", "--listen", "stdio://"];
4774
5035
  this.lastStderr = "";
4775
- this.lastCrashHadStderr = false;
4776
5036
  this.expectedExitSignal = void 0;
4777
5037
  this.child = spawn(base.cmd, args, {
4778
5038
  stdio: ["pipe", "pipe", "pipe"],
@@ -4783,23 +5043,18 @@ var AppServerRpcClient = class extends EventEmitter {
4783
5043
  },
4784
5044
  cwd: this.settings.cwd
4785
5045
  });
4786
- const child = this.child;
4787
- let stderrBuf = "";
4788
- child.stderr.setEncoding("utf8");
4789
- child.stderr.on("data", (chunk) => {
4790
- stderrBuf = (stderrBuf + String(chunk)).slice(-4e3);
4791
- if (this.child === child) {
4792
- this.lastStderr = stderrBuf;
5046
+ this.child.stderr.setEncoding("utf8");
5047
+ this.child.stderr.on("data", (chunk) => {
5048
+ this.lastStderr += String(chunk);
5049
+ if (this.lastStderr.length > 4e3) {
5050
+ this.lastStderr = this.lastStderr.slice(-4e3);
4793
5051
  }
4794
5052
  });
4795
- child.on("error", (error) => {
5053
+ this.child.on("error", (error) => {
4796
5054
  this.logger.error(`[codex-app-server] process error: ${String(error)}`);
4797
- const base2 = String(error);
4798
- const message = this.withStderrTail(base2, stderrBuf);
4799
- this.lastCrashHadStderr = message !== base2;
4800
- this.handleCrash(new Error(message));
5055
+ this.handleCrash(error);
4801
5056
  });
4802
- child.on("exit", (code, signal) => {
5057
+ this.child.on("exit", (code, signal) => {
4803
5058
  const message = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4804
5059
  const expected = this.state === "closed" || signal !== null && signal === this.expectedExitSignal;
4805
5060
  this.expectedExitSignal = void 0;
@@ -4807,29 +5062,11 @@ var AppServerRpcClient = class extends EventEmitter {
4807
5062
  this.logger.info(`[codex-app-server] ${message}`);
4808
5063
  return;
4809
5064
  }
4810
- const crashedPending = this.markCrashed();
4811
- if (!crashedPending) return;
4812
- let settled = false;
4813
- const finish = () => {
4814
- if (settled) return;
4815
- settled = true;
4816
- child.off("close", finish);
4817
- clearTimeout(timer);
4818
- const base2 = `codex app-server exited (code=${String(code)}, signal=${String(signal)})`;
4819
- const crashMessage = this.withStderrTail(base2, stderrBuf);
4820
- if (this.child === void 0) {
4821
- this.lastStderr = stderrBuf;
4822
- this.lastCrashHadStderr = crashMessage !== base2;
4823
- }
4824
- this.logger.warn(`[codex-app-server] ${crashMessage}`);
4825
- this.rejectCrashedPending(crashedPending, new Error(crashMessage));
4826
- };
4827
- child.once("close", finish);
4828
- const timer = setTimeout(finish, 120);
4829
- timer.unref?.();
5065
+ this.logger.warn(`[codex-app-server] ${message}`);
5066
+ this.handleCrash(new Error(message));
4830
5067
  });
4831
5068
  this.stdoutReader = readline.createInterface({
4832
- input: child.stdout,
5069
+ input: this.child.stdout,
4833
5070
  crlfDelay: Infinity
4834
5071
  });
4835
5072
  this.stdoutReader.on("line", (line) => this.handleLine(line));
@@ -4851,22 +5088,12 @@ var AppServerRpcClient = class extends EventEmitter {
4851
5088
  this.settings.connectionTimeoutMs ?? this.requestTimeoutMs
4852
5089
  );
4853
5090
  } catch (error) {
4854
- const raw = String(error?.message ?? error);
4855
- if (raw.includes("unknown subcommand") || this.lastStderr.includes("unknown subcommand")) {
5091
+ const message = String(error?.message ?? error);
5092
+ if (message.includes("ENOENT") || message.includes("unknown subcommand")) {
4856
5093
  throw new Error(
4857
- this.withStderrTail(
4858
- "codex app-server requires codex CLI >= 0.144.0. Run 'codex --version' to check."
4859
- )
5094
+ "codex app-server requires codex CLI >= 0.142.5. Run 'codex --version' to check."
4860
5095
  );
4861
5096
  }
4862
- if (raw.includes("ENOENT") || this.lastStderr.includes("ENOENT")) {
4863
- throw new Error(
4864
- this.withStderrTail(
4865
- "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."
4866
- )
4867
- );
4868
- }
4869
- const message = this.lastCrashHadStderr ? raw : this.withStderrTail(raw);
4870
5097
  throw createAPICallError({
4871
5098
  message: `Failed to initialize codex app-server: ${message}`,
4872
5099
  stderr: this.lastStderr,
@@ -4886,7 +5113,7 @@ var AppServerRpcClient = class extends EventEmitter {
4886
5113
  return;
4887
5114
  }
4888
5115
  this.serverVersion = detected;
4889
- const minVersion = this.settings.minCodexVersion ?? "0.144.0";
5116
+ const minVersion = this.settings.minCodexVersion ?? "0.142.5";
4890
5117
  const compared = compareSemver(detected, minVersion);
4891
5118
  if (compared === void 0) {
4892
5119
  this.logger.warn(
@@ -4928,8 +5155,8 @@ var AppServerRpcClient = class extends EventEmitter {
4928
5155
  }
4929
5156
  this.writeQueue = Promise.resolve();
4930
5157
  }
4931
- markCrashed() {
4932
- if (this.state === "closed" || this.state === "error") return void 0;
5158
+ handleCrash(error) {
5159
+ if (this.state === "closed") return;
4933
5160
  this.state = "error";
4934
5161
  this.clearIdleTimer();
4935
5162
  this.stdoutReader?.close();
@@ -4939,9 +5166,11 @@ var AppServerRpcClient = class extends EventEmitter {
4939
5166
  this.child.kill("SIGTERM");
4940
5167
  this.child = void 0;
4941
5168
  }
4942
- const pending = new Map(this.pending);
4943
- for (const [, entry] of pending) {
4944
- clearTimeout(entry.timer);
5169
+ for (const [id, pending] of this.pending.entries()) {
5170
+ clearTimeout(pending.timer);
5171
+ pending.reject(
5172
+ new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
5173
+ );
4945
5174
  }
4946
5175
  this.pending.clear();
4947
5176
  this.threadLocks.clear();
@@ -4951,41 +5180,6 @@ var AppServerRpcClient = class extends EventEmitter {
4951
5180
  this.completedTurnIds.clear();
4952
5181
  this.serverCapabilities = void 0;
4953
5182
  this.writeQueue = Promise.resolve();
4954
- return pending;
4955
- }
4956
- rejectCrashedPending(pending, error) {
4957
- for (const [id, entry] of pending) {
4958
- entry.reject(
4959
- new Error(`Request ${String(id)} failed after app-server crash: ${String(error)}`)
4960
- );
4961
- }
4962
- }
4963
- handleCrash(error) {
4964
- const pending = this.markCrashed();
4965
- if (!pending) return;
4966
- this.rejectCrashedPending(pending, error);
4967
- }
4968
- stderrExcerpt(raw) {
4969
- if (!raw) return "";
4970
- const escape = String.fromCharCode(27);
4971
- const ansiEscapeSequence = new RegExp(
4972
- `${escape}(?:\\[[0-?]*[ -/]*[@-~]|\\][^\\u0007]*(?:\\u0007|${escape}\\\\))`,
4973
- "g"
4974
- );
4975
- const withoutAnsi = raw.replace(ansiEscapeSequence, "");
4976
- const withoutControls = Array.from(withoutAnsi).filter((character) => {
4977
- const code = character.charCodeAt(0);
4978
- return character === "\n" || code >= 32 && (code < 127 || code > 159);
4979
- }).join("");
4980
- const excerpt = withoutControls.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-5).join("; ");
4981
- if (excerpt.length > 600) {
4982
- return `\u2026${excerpt.slice(-600)}`;
4983
- }
4984
- return excerpt;
4985
- }
4986
- withStderrTail(message, raw = this.lastStderr) {
4987
- const excerpt = this.stderrExcerpt(raw);
4988
- return excerpt ? `${message} | stderr (tail): ${excerpt}` : message;
4989
5183
  }
4990
5184
  handleLine(line) {
4991
5185
  const trimmed = line.trim();
@@ -5655,7 +5849,7 @@ function createCodexAppServer(options = {}) {
5655
5849
  }
5656
5850
  return createModel(modelId, settings);
5657
5851
  },
5658
- { specificationVersion: "v3" }
5852
+ { specificationVersion: "v4" }
5659
5853
  );
5660
5854
  provider.languageModel = createModel;
5661
5855
  provider.chat = createModel;