@ycodium-ai/agent-codex 0.2.2634 → 0.2.2664

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.
Files changed (2) hide show
  1. package/dist/index.js +217 -31
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -424,7 +424,10 @@ var probeCodexAccount = (accountPath) => {
424
424
 
425
425
  // src/codexInstance.ts
426
426
  import * as NodeProcess from "node:process";
427
- import { ExecutorSpawnRefusedError } from "@ycodium-ai/plugin-api/executor";
427
+ import {
428
+ describeExecutorSpawnFailure,
429
+ ExecutorSpawnRefusedError
430
+ } from "@ycodium-ai/plugin-api/executor";
428
431
 
429
432
  // src/config.ts
430
433
  import { modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
@@ -782,7 +785,8 @@ function codexCollaborationMode(input) {
782
785
  mode: input.interactionMode === "plan" ? "plan" : "default",
783
786
  settings: {
784
787
  model: input.model,
785
- ...effort ? { reasoning_effort: effort } : {}
788
+ ...effort ? { reasoning_effort: effort } : {},
789
+ ...input.developerInstructions ? { developer_instructions: input.developerInstructions } : {}
786
790
  }
787
791
  };
788
792
  }
@@ -868,6 +872,8 @@ var CHILD_AGENT_EVENT_METHODS = /* @__PURE__ */ new Set([
868
872
  "turn/completed",
869
873
  "thread/status/changed",
870
874
  "thread/tokenUsage/updated",
875
+ "thread/settings/updated",
876
+ "model/rerouted",
871
877
  "item/started",
872
878
  "item/completed",
873
879
  "thread/closed",
@@ -885,7 +891,6 @@ var CHILD_CHATTER_METHODS = /* @__PURE__ */ new Set([
885
891
  "turn/plan/updated",
886
892
  "turn/diff/updated",
887
893
  "thread/name/updated",
888
- "thread/settings/updated",
889
894
  "rawResponseItem/completed",
890
895
  // 子线程自己的生命周期通知不能改父线程状态(旧宿主 runtime 同名单)。
891
896
  "thread/archived",
@@ -1672,9 +1677,13 @@ function mapCollabAgentEvent(input) {
1672
1677
  const role = asString(input.params["agentRole"]) ?? asString(input.params["role"]) ?? pathLeaf(agentPath) ?? "general-purpose";
1673
1678
  const knownName = nickname ?? pathLeaf(agentPath);
1674
1679
  const title = knownName ?? agentThreadId;
1680
+ const model = trimText(input.params["model"]);
1681
+ const effort = trimText(input.params["effort"]);
1675
1682
  const linkage = {
1676
1683
  role,
1677
1684
  ...knownName ? { title: knownName } : {},
1685
+ ...model ? { model } : {},
1686
+ ...effort ? { effort } : {},
1678
1687
  ...agentPath ? { agentPath } : {},
1679
1688
  timelineBypass: true,
1680
1689
  rosterKind: "subagent"
@@ -1735,6 +1744,17 @@ function mapCollabAgentEvent(input) {
1735
1744
  })
1736
1745
  ];
1737
1746
  }
1747
+ if (method === "collabAgent/metadataUpdated") {
1748
+ return [
1749
+ baseEvent({
1750
+ type: "task.updated",
1751
+ threadId,
1752
+ payload: { taskId: agentThreadId, ...linkage },
1753
+ method,
1754
+ providerInstanceId: input.providerInstanceId
1755
+ })
1756
+ ];
1757
+ }
1738
1758
  if (method === "collabAgent/metadata") {
1739
1759
  const status = asCollabChildStatus(input.params["status"]);
1740
1760
  if (status === void 0) return [];
@@ -1810,6 +1830,42 @@ function describeFileChangeApproval(params) {
1810
1830
  fileChanges: toApplyPatchFileChanges(record)
1811
1831
  };
1812
1832
  }
1833
+ function describePermissionsApproval(params) {
1834
+ const record = isRecord3(params) ? params : {};
1835
+ const permissions = isRecord3(record["permissions"]) ? record["permissions"] : {};
1836
+ const fileSystem = isRecord3(permissions["fileSystem"]) ? permissions["fileSystem"] : {};
1837
+ const network = isRecord3(permissions["network"]) ? permissions["network"] : {};
1838
+ const pathsOf = (value) => Array.isArray(value) ? value.flatMap((entry) => typeof entry === "string" && entry.length > 0 ? [entry] : []) : [];
1839
+ const read = pathsOf(fileSystem["read"]);
1840
+ const write = pathsOf(fileSystem["write"]);
1841
+ const entries = Array.isArray(fileSystem["entries"]) ? fileSystem["entries"] : [];
1842
+ for (const entry of entries) {
1843
+ if (!isRecord3(entry) || !isRecord3(entry["path"])) continue;
1844
+ const target = entry["path"];
1845
+ const path = target["type"] === "path" ? asString(target["path"]) : target["type"] === "glob_pattern" ? asString(target["pattern"]) : void 0;
1846
+ if (!path) continue;
1847
+ if (entry["access"] === "read") read.push(path);
1848
+ if (entry["access"] === "write") write.push(path);
1849
+ }
1850
+ const reason = trimText(record["reason"]);
1851
+ return {
1852
+ ...reason ? { reason } : {},
1853
+ read: [...new Set(read)],
1854
+ write: [...new Set(write)],
1855
+ network: network["enabled"] === true
1856
+ };
1857
+ }
1858
+ function toPermissionsResponse(decision, params, persistent) {
1859
+ if (decision !== "allow") return { permissions: {} };
1860
+ const requested = isRecord3(params) && isRecord3(params["permissions"]) ? params["permissions"] : {};
1861
+ return {
1862
+ permissions: {
1863
+ ...requested["fileSystem"] !== void 0 ? { fileSystem: requested["fileSystem"] } : {},
1864
+ ...requested["network"] !== void 0 ? { network: requested["network"] } : {}
1865
+ },
1866
+ scope: persistent ? "session" : "turn"
1867
+ };
1868
+ }
1813
1869
  function describeMcpElicitation(params) {
1814
1870
  const record = isRecord3(params) ? params : {};
1815
1871
  const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
@@ -2409,6 +2465,8 @@ function toRequestTypeFromKind(kind) {
2409
2465
  return "apply_patch_approval";
2410
2466
  case "mcp-elicitation":
2411
2467
  return "mcp_elicitation_approval";
2468
+ // 契约没有单独的权限审批类型;宿主把 unknown 当 tool 类审批卡片渲染。
2469
+ case "permission":
2412
2470
  default:
2413
2471
  return "unknown";
2414
2472
  }
@@ -2431,6 +2489,7 @@ function toRequestTypeFromMethod(method) {
2431
2489
  return "tool_user_input";
2432
2490
  case "item/tool/call":
2433
2491
  return "dynamic_tool_call";
2492
+ case "item/permissions/requestApproval":
2434
2493
  default:
2435
2494
  return "unknown";
2436
2495
  }
@@ -2475,6 +2534,17 @@ function mapServerRequestToEvents(input) {
2475
2534
  if (appName) payload["appName"] = appName;
2476
2535
  if (described.message) payload["detail"] = described.message;
2477
2536
  payload["args"] = input.params;
2537
+ } else if (input.method === "item/permissions/requestApproval") {
2538
+ const described = describePermissionsApproval(input.params);
2539
+ const paths = [...described.read, ...described.write];
2540
+ const detail = described.reason ?? (paths.length > 0 ? paths.join(", ") : void 0);
2541
+ if (detail) payload["detail"] = detail;
2542
+ payload["args"] = {
2543
+ ...described.reason ? { reason: described.reason } : {},
2544
+ ...described.read.length > 0 ? { read: described.read.join(", ") } : {},
2545
+ ...described.write.length > 0 ? { write: described.write.join(", ") } : {},
2546
+ ...described.network ? { network: "true" } : {}
2547
+ };
2478
2548
  } else if (input.method === "item/fileChange/requestApproval" || input.method.includes("applyPatch")) {
2479
2549
  const described = describeFileChangeApproval(input.params);
2480
2550
  if (described.detail) payload["detail"] = described.detail;
@@ -2508,7 +2578,13 @@ function mapServerRequestToEvents(input) {
2508
2578
  }
2509
2579
 
2510
2580
  // src/sessionRuntime.ts
2581
+ var COLLAB_META_LOOKUP_TIMEOUT_MS = 5e3;
2582
+ function metaValue(value) {
2583
+ const text = asString(value)?.trim();
2584
+ return text === void 0 || text.length === 0 ? void 0 : text;
2585
+ }
2511
2586
  function cancelWireFor(pending) {
2587
+ if (pending.kind === "permission") return toPermissionsResponse("cancel", pending.params, false);
2512
2588
  return pending.kind === "mcp-elicitation" ? toMcpElicitationResponse("cancel", pending.params) : { decision: "cancel" };
2513
2589
  }
2514
2590
  var sleep = (ms) => new Promise((resolve2) => {
@@ -2558,6 +2634,7 @@ var CodexSessionRuntime = class {
2558
2634
  grants;
2559
2635
  declineRelay;
2560
2636
  collabChildren = /* @__PURE__ */ new Map();
2637
+ collabChildMeta = /* @__PURE__ */ new Map();
2561
2638
  liveForeignTurns = /* @__PURE__ */ new Map();
2562
2639
  collabReceiverTurns = /* @__PURE__ */ new Map();
2563
2640
  memoryThreadIds = /* @__PURE__ */ new Set();
@@ -2766,14 +2843,28 @@ var CodexSessionRuntime = class {
2766
2843
  }
2767
2844
  this.upsertCollabChild(agentThreadId, { agentPath: activity.agentPath });
2768
2845
  }
2846
+ if (method === "thread/settings/updated" || method === "model/rerouted") {
2847
+ const settings = isRecord3(params) && isRecord3(params["threadSettings"]) ? params["threadSettings"] : {};
2848
+ const reroute = method === "model/rerouted";
2849
+ const model = metaValue(
2850
+ reroute ? isRecord3(params) ? params["toModel"] : void 0 : settings["model"]
2851
+ );
2852
+ const effort = reroute ? void 0 : metaValue(settings["effort"]);
2853
+ this.updateCollabChildMeta(conversationId, { model, effort }, true);
2854
+ return;
2855
+ }
2769
2856
  const child = this.collabChildren.get(conversationId);
2770
2857
  if (method === "turn/started") {
2858
+ this.reopenCollabChildMeta(conversationId);
2771
2859
  const turnId = isRecord3(params) ? asString(params["turnId"]) ?? (isRecord3(params["turn"]) ? asString(params["turn"]["id"]) : null) : null;
2772
2860
  if (turnId) this.liveForeignTurns.set(conversationId, turnId);
2773
2861
  }
2774
2862
  if (method === "turn/completed" || method === "thread/closed") {
2775
2863
  this.liveForeignTurns.delete(conversationId);
2776
2864
  }
2865
+ if (method === "thread/closed" && this.collabChildren.has(conversationId)) {
2866
+ this.metaOf(conversationId).closed = true;
2867
+ }
2777
2868
  if (!child) {
2778
2869
  if (CHILD_CHATTER_METHODS.has(method) || !CHILD_AGENT_EVENT_METHODS.has(method)) return;
2779
2870
  return;
@@ -2834,6 +2925,7 @@ var CodexSessionRuntime = class {
2834
2925
  };
2835
2926
  this.collabChildren.set(agentThreadId, child);
2836
2927
  this.emitCollab("collabAgent/started", child);
2928
+ this.startCollabChildMetaLookup(agentThreadId);
2837
2929
  return;
2838
2930
  }
2839
2931
  if (agentPath === existing.agentPath && role === existing.role && nickname === existing.nickname) {
@@ -2848,12 +2940,64 @@ var CodexSessionRuntime = class {
2848
2940
  this.collabChildren.set(agentThreadId, merged);
2849
2941
  this.emitCollab("collabAgent/metadata", merged, { status: merged.status });
2850
2942
  }
2943
+ metaOf(agentThreadId) {
2944
+ let meta = this.collabChildMeta.get(agentThreadId);
2945
+ if (meta === void 0) {
2946
+ meta = { lookupStarted: false, closed: false };
2947
+ this.collabChildMeta.set(agentThreadId, meta);
2948
+ }
2949
+ return meta;
2950
+ }
2951
+ reopenCollabChildMeta(agentThreadId) {
2952
+ const meta = this.collabChildMeta.get(agentThreadId);
2953
+ if (meta !== void 0) meta.closed = false;
2954
+ }
2955
+ /**
2956
+ * 合并子线程的型号与推理档;变了且子代理已登记就补发一条不带状态的更新。
2957
+ * 通知报的是当前值(overwrite),回读只补缺:回读的回包可能比通知晚到,不能盖掉更新的值。
2958
+ */
2959
+ updateCollabChildMeta(agentThreadId, update, overwrite) {
2960
+ const meta = this.metaOf(agentThreadId);
2961
+ const model = update.model !== void 0 && (overwrite || !meta.model) ? update.model : meta.model;
2962
+ const effort = update.effort !== void 0 && (overwrite || !meta.effort) ? update.effort : meta.effort;
2963
+ if (model === meta.model && effort === meta.effort) return;
2964
+ if (model !== void 0) meta.model = model;
2965
+ if (effort !== void 0) meta.effort = effort;
2966
+ const child = this.collabChildren.get(agentThreadId);
2967
+ if (child !== void 0 && !meta.closed) this.emitCollab("collabAgent/metadataUpdated", child);
2968
+ }
2969
+ /**
2970
+ * 子线程已经在 CLI 里加载着:thread/resume 只是重新接上它,不开新一轮;
2971
+ * excludeTurns 避免把它的历史再读一遍。回包带当前的型号与推理档。
2972
+ */
2973
+ startCollabChildMetaLookup(agentThreadId) {
2974
+ const meta = this.metaOf(agentThreadId);
2975
+ if (meta.lookupStarted || meta.closed) return;
2976
+ meta.lookupStarted = true;
2977
+ void this.rpc.request(
2978
+ "thread/resume",
2979
+ { threadId: agentThreadId, excludeTurns: true },
2980
+ COLLAB_META_LOOKUP_TIMEOUT_MS
2981
+ ).then((response) => {
2982
+ if (this.closed || meta.closed || !isRecord3(response)) return;
2983
+ const thread = response["thread"];
2984
+ if (!isRecord3(thread) || thread["id"] !== agentThreadId) return;
2985
+ this.updateCollabChildMeta(
2986
+ agentThreadId,
2987
+ { model: metaValue(response["model"]), effort: metaValue(response["reasoningEffort"]) },
2988
+ false
2989
+ );
2990
+ }).catch(() => void 0);
2991
+ }
2851
2992
  emitCollab(method, child, extra) {
2993
+ const meta = this.collabChildMeta.get(child.agentThreadId);
2852
2994
  const params = {
2853
2995
  agentThreadId: child.agentThreadId,
2854
2996
  agentPath: child.agentPath,
2855
2997
  agentRole: child.role ?? pathLeaf2(child.agentPath) ?? "general-purpose",
2856
2998
  agentNickname: child.nickname,
2999
+ ...meta?.model ? { model: meta.model } : {},
3000
+ ...meta?.effort ? { effort: meta.effort } : {},
2857
3001
  ...isRecord3(extra) ? extra : {}
2858
3002
  };
2859
3003
  for (const event of mapNotificationToEvents({
@@ -2892,6 +3036,16 @@ var CodexSessionRuntime = class {
2892
3036
  await this.handleApproval(id, method, params, "mcp-elicitation", described.identity);
2893
3037
  return;
2894
3038
  }
3039
+ if (method === "item/permissions/requestApproval") {
3040
+ await this.handleApproval(
3041
+ id,
3042
+ method,
3043
+ params,
3044
+ "permission",
3045
+ isRecord3(params) ? asString(params["itemId"]) ?? "permission" : "permission"
3046
+ );
3047
+ return;
3048
+ }
2895
3049
  if (method === "item/tool/requestUserInput") {
2896
3050
  const requestId = this.requestIdFor(id);
2897
3051
  const events = mapServerRequestToEvents({
@@ -2920,7 +3074,7 @@ var CodexSessionRuntime = class {
2920
3074
  await this.rpc.respondError(id, -32601, `Method not found: ${method}`);
2921
3075
  }
2922
3076
  async handleApproval(id, method, params, kind, identity) {
2923
- const grant = this.grants.lookup(codexApprovalGrantKey(kind, identity));
3077
+ const grant = kind === "permission" ? void 0 : this.grants.lookup(codexApprovalGrantKey(kind, identity));
2924
3078
  if (grant) {
2925
3079
  const decision = wireDecisionFromPersist(kind, grant);
2926
3080
  await this.rpc.respond(id, { decision });
@@ -2962,7 +3116,9 @@ var CodexSessionRuntime = class {
2962
3116
  if (decision === "allow" && pending.kind === "mcp-elicitation") {
2963
3117
  wire = toMcpElicitationResponse("allow", pending.params);
2964
3118
  }
2965
- if (decision === "allow" && persist) {
3119
+ if (pending.kind === "permission") {
3120
+ wire = toPermissionsResponse(decision, pending.params, persist !== void 0);
3121
+ } else if (decision === "allow" && persist) {
2966
3122
  this.grants.remember(codexApprovalGrantKey(pending.kind, pending.identity), persist);
2967
3123
  if (pending.kind !== "mcp-elicitation") {
2968
3124
  wire = { decision: wireDecisionFromPersist(pending.kind, persist) };
@@ -3095,10 +3251,13 @@ var CodexSessionRuntime = class {
3095
3251
  });
3096
3252
  return this.session;
3097
3253
  }
3098
- developerInstructions(input) {
3099
- const mode = typeof input.interactionMode === "string" ? input.interactionMode : void 0;
3254
+ /** 宿主没给模式时沿用上一轮的模式;从没给过按 default。指令每轮都要带上。 */
3255
+ turnInteractionMode(input) {
3256
+ const mode = typeof input.interactionMode === "string" ? input.interactionMode : this.interactionMode ?? "default";
3100
3257
  this.interactionMode = mode;
3101
- if (mode === void 0) return void 0;
3258
+ return mode;
3259
+ }
3260
+ developerInstructions(input, mode) {
3102
3261
  const tools = hasConfiguredMcpServer(this.appServerArgs);
3103
3262
  const model = this.model ?? DEFAULT_MODEL;
3104
3263
  const effort = modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? DEFAULT_TEXT_GENERATION_REASONING_EFFORT;
@@ -3146,7 +3305,8 @@ var CodexSessionRuntime = class {
3146
3305
  }
3147
3306
  const sandboxPolicy = this.#turnSandboxPolicy();
3148
3307
  const config = this.#threadConfig();
3149
- const developerInstructions = this.developerInstructions(input);
3308
+ const interactionMode = this.turnInteractionMode(input);
3309
+ const developerInstructions = this.developerInstructions(input, interactionMode);
3150
3310
  const requestedModel = modelIdFromSelection(input.modelSelection) ?? this.model;
3151
3311
  const serviceTier = modelSelectionStringOption(input.modelSelection, "serviceTier");
3152
3312
  const params = {
@@ -3157,16 +3317,14 @@ var CodexSessionRuntime = class {
3157
3317
  sandboxPolicy,
3158
3318
  ...requestedModel ? { model: requestedModel } : {},
3159
3319
  ...serviceTier ? { serviceTier } : {},
3160
- ...developerInstructions ? { developerInstructions } : {}
3161
- };
3162
- const interactionMode = typeof input.interactionMode === "string" ? input.interactionMode : void 0;
3163
- if (interactionMode !== void 0) {
3164
- params["collaborationMode"] = codexCollaborationMode({
3320
+ // 指令只能走这里:turn 顶层的 developerInstructions 会被 CLI 静默丢掉(见 codexCollaborationMode)。
3321
+ collaborationMode: codexCollaborationMode({
3165
3322
  interactionMode,
3166
3323
  model: requestedModel ?? DEFAULT_MODEL,
3167
- reasoningEffort: modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? void 0
3168
- });
3169
- }
3324
+ reasoningEffort: modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? void 0,
3325
+ developerInstructions
3326
+ })
3327
+ };
3170
3328
  const isSteer = input.expectedTurnId !== void 0;
3171
3329
  const result = await markProviderRejection(
3172
3330
  () => isSteer ? this.rpc.request(
@@ -3265,9 +3423,10 @@ var CodexSessionRuntime = class {
3265
3423
  }
3266
3424
  return { threadId: this.threadId, turns: this.turns };
3267
3425
  }
3268
- settlePendingApprovals(decision) {
3426
+ /** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
3427
+ settlePendingApprovals() {
3269
3428
  for (const [requestId, pending] of this.pendingApprovals) {
3270
- pending.settle({ decision });
3429
+ pending.settle(cancelWireFor(pending));
3271
3430
  this.pendingApprovals.delete(requestId);
3272
3431
  }
3273
3432
  }
@@ -3282,7 +3441,7 @@ var CodexSessionRuntime = class {
3282
3441
  this.closed = true;
3283
3442
  this.requestKinds.clear();
3284
3443
  this.failPendingNativeWrites("session closed before write confirmation");
3285
- this.settlePendingApprovals("cancel");
3444
+ this.settlePendingApprovals();
3286
3445
  this.settlePendingUserInputs({});
3287
3446
  this.declineRelay.clear(this.threadId);
3288
3447
  this.grants.clear();
@@ -3871,7 +4030,7 @@ async function verifyCodexSignIn(input) {
3871
4030
  return {
3872
4031
  installed: false,
3873
4032
  version: null,
3874
- readiness: "error",
4033
+ readiness: "not-installed",
3875
4034
  auth: { status: "unknown" },
3876
4035
  detail: "Codex program path is not configured"
3877
4036
  };
@@ -4381,6 +4540,10 @@ ${extra}` : ""}`;
4381
4540
  }
4382
4541
 
4383
4542
  // src/codexInstance.ts
4543
+ function missingBinaryDetail(binaryPath) {
4544
+ const relyOnPath = !/[\\/]/.test(binaryPath);
4545
+ return `Could not start Codex (${binaryPath}). Check the "Program path" setting of this provider on this machine.` + (relyOnPath ? " The Codex desktop app may not add codex to PATH; enter the full path to the codex executable there." : " Make sure the configured file exists and can be run.");
4546
+ }
4384
4547
  var yieldTick = () => new Promise((resolve2) => {
4385
4548
  setImmediate(resolve2);
4386
4549
  });
@@ -4462,12 +4625,26 @@ var createCodexInstance = (config, host, attachments) => {
4462
4625
  let rpc;
4463
4626
  let version = null;
4464
4627
  try {
4465
- handle = await host.process.spawn({
4466
- program: recipe.binaryPath,
4467
- args: [...codexSpawnArgs(recipe, layout)],
4468
- owner: { bindingId: host.bindingId },
4469
- ...Object.keys(env).length > 0 ? { env } : {}
4470
- });
4628
+ try {
4629
+ handle = await host.process.spawn({
4630
+ program: recipe.binaryPath,
4631
+ args: [...codexSpawnArgs(recipe, layout)],
4632
+ owner: { bindingId: host.bindingId },
4633
+ ...Object.keys(env).length > 0 ? { env } : {}
4634
+ });
4635
+ } catch (error) {
4636
+ if (error instanceof ExecutorSpawnRefusedError) throw error;
4637
+ host.log.warn("codex binary could not be started", {
4638
+ program: recipe.binaryPath,
4639
+ error: error instanceof Error ? error.message : String(error)
4640
+ });
4641
+ return {
4642
+ version: null,
4643
+ models: void 0,
4644
+ skills: void 0,
4645
+ spawnFailed: describeExecutorSpawnFailure(error)
4646
+ };
4647
+ }
4471
4648
  trackHandle(handle, `${recipe.binaryPath} (probe)`);
4472
4649
  rpc = await createCodexRpcClient(handle, {
4473
4650
  onNotification: () => void 0,
@@ -4479,10 +4656,10 @@ var createCodexInstance = (config, host, attachments) => {
4479
4656
  );
4480
4657
  await rpc.notify("initialized");
4481
4658
  const models = input.listModels ? await listCodexModels(rpc) : void 0;
4482
- return { version, models, skills: await listCodexSkills(rpc) };
4659
+ return { version, models, skills: await listCodexSkills(rpc), spawnFailed: null };
4483
4660
  } catch (error) {
4484
4661
  if (error instanceof ExecutorSpawnRefusedError) throw error;
4485
- return { version, models: void 0, skills: void 0 };
4662
+ return { version, models: void 0, skills: void 0, spawnFailed: null };
4486
4663
  } finally {
4487
4664
  await rpc?.close().catch(() => void 0);
4488
4665
  await handle?.kill("SIGTERM").catch(() => void 0);
@@ -4503,15 +4680,24 @@ var createCodexInstance = (config, host, attachments) => {
4503
4680
  return {
4504
4681
  installed: false,
4505
4682
  version: null,
4506
- readiness: "error",
4683
+ readiness: "not-installed",
4507
4684
  auth: { status: "unknown" },
4508
4685
  detail: "Codex program path is not configured"
4509
4686
  };
4510
4687
  }
4511
4688
  const connectionError = codexApiConnectionError(recipe, recipe.environment);
4512
- const { version, models, skills } = await probeAppServer({
4689
+ const { version, models, skills, spawnFailed } = await probeAppServer({
4513
4690
  listModels: connectionError === null
4514
4691
  });
4692
+ if (spawnFailed !== null) {
4693
+ return {
4694
+ installed: false,
4695
+ version: null,
4696
+ readiness: spawnFailed.readiness,
4697
+ auth: { status: "unknown" },
4698
+ detail: spawnFailed.readiness === "not-installed" ? missingBinaryDetail(recipe.binaryPath) : spawnFailed.detail
4699
+ };
4700
+ }
4515
4701
  if (connectionError) {
4516
4702
  return {
4517
4703
  installed: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-codex",
3
- "version": "0.2.2634",
3
+ "version": "0.2.2664",
4
4
  "description": "Codex executor: Codex CLI over the app-server JSON-RPC protocol.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -29,7 +29,7 @@
29
29
  "tailwindcss": "^4.0.0",
30
30
  "typescript": "~6.0.3",
31
31
  "vite-plus": "0.2.2",
32
- "@ycodium-ai/plugin-api": "0.2.2634",
32
+ "@ycodium-ai/plugin-api": "0.2.2664",
33
33
  "ycodium-executor-testing": "0.0.0"
34
34
  },
35
35
  "ycodium": {