@ycodium-ai/agent-codex 0.2.2596 → 0.2.2658
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 +260 -40
- 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 {
|
|
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",
|
|
@@ -1584,6 +1589,28 @@ function toUserInputQuestions(params) {
|
|
|
1584
1589
|
}
|
|
1585
1590
|
return mapped.length > 0 ? mapped : void 0;
|
|
1586
1591
|
}
|
|
1592
|
+
function toAsyncQuestions(params) {
|
|
1593
|
+
if (!isRecord3(params)) return void 0;
|
|
1594
|
+
const item = isRecord3(params["item"]) ? params["item"] : params;
|
|
1595
|
+
if (item["delivery"] !== "async" || !Array.isArray(item["questions"])) return void 0;
|
|
1596
|
+
const mapped = [];
|
|
1597
|
+
item["questions"].forEach((question, index) => {
|
|
1598
|
+
if (!isRecord3(question)) return;
|
|
1599
|
+
const title = trimText(question["title"]);
|
|
1600
|
+
if (!title) return;
|
|
1601
|
+
const labels = Array.isArray(question["options"]) ? question["options"] : [];
|
|
1602
|
+
const options = labels.map((label) => trimText(label)).filter((label) => label !== void 0).map((label) => ({ label, description: "" }));
|
|
1603
|
+
mapped.push({
|
|
1604
|
+
id: `q${index}`,
|
|
1605
|
+
header: title,
|
|
1606
|
+
question: title,
|
|
1607
|
+
options,
|
|
1608
|
+
allowFreeform: true,
|
|
1609
|
+
multiSelect: false
|
|
1610
|
+
});
|
|
1611
|
+
});
|
|
1612
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
1613
|
+
}
|
|
1587
1614
|
function toCodexUserInputAnswers(answers) {
|
|
1588
1615
|
if (!isRecord3(answers)) return void 0;
|
|
1589
1616
|
const out = {};
|
|
@@ -1650,9 +1677,13 @@ function mapCollabAgentEvent(input) {
|
|
|
1650
1677
|
const role = asString(input.params["agentRole"]) ?? asString(input.params["role"]) ?? pathLeaf(agentPath) ?? "general-purpose";
|
|
1651
1678
|
const knownName = nickname ?? pathLeaf(agentPath);
|
|
1652
1679
|
const title = knownName ?? agentThreadId;
|
|
1680
|
+
const model = trimText(input.params["model"]);
|
|
1681
|
+
const effort = trimText(input.params["effort"]);
|
|
1653
1682
|
const linkage = {
|
|
1654
1683
|
role,
|
|
1655
1684
|
...knownName ? { title: knownName } : {},
|
|
1685
|
+
...model ? { model } : {},
|
|
1686
|
+
...effort ? { effort } : {},
|
|
1656
1687
|
...agentPath ? { agentPath } : {},
|
|
1657
1688
|
timelineBypass: true,
|
|
1658
1689
|
rosterKind: "subagent"
|
|
@@ -1713,6 +1744,17 @@ function mapCollabAgentEvent(input) {
|
|
|
1713
1744
|
})
|
|
1714
1745
|
];
|
|
1715
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
|
+
}
|
|
1716
1758
|
if (method === "collabAgent/metadata") {
|
|
1717
1759
|
const status = asCollabChildStatus(input.params["status"]);
|
|
1718
1760
|
if (status === void 0) return [];
|
|
@@ -1788,6 +1830,42 @@ function describeFileChangeApproval(params) {
|
|
|
1788
1830
|
fileChanges: toApplyPatchFileChanges(record)
|
|
1789
1831
|
};
|
|
1790
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
|
+
}
|
|
1791
1869
|
function describeMcpElicitation(params) {
|
|
1792
1870
|
const record = isRecord3(params) ? params : {};
|
|
1793
1871
|
const appName = asString(record["appName"]) ?? asString(record["serverName"]) ?? void 0;
|
|
@@ -2171,19 +2249,31 @@ function mapNotificationToEvents(input) {
|
|
|
2171
2249
|
if (canonical === "unknown") return [];
|
|
2172
2250
|
const rawStatus = itemStatusOf(params);
|
|
2173
2251
|
const status = rawStatus === "failed" || rawStatus === "declined" ? rawStatus : "completed";
|
|
2252
|
+
const completed = baseEvent({
|
|
2253
|
+
type: "item.completed",
|
|
2254
|
+
threadId,
|
|
2255
|
+
turnId,
|
|
2256
|
+
itemId,
|
|
2257
|
+
payload: {
|
|
2258
|
+
itemType: canonical,
|
|
2259
|
+
status,
|
|
2260
|
+
...itemTitle(params) ? { title: itemTitle(params) } : {},
|
|
2261
|
+
...itemDetail(params) ? { detail: itemDetail(params) } : {},
|
|
2262
|
+
...params !== void 0 ? { data: params } : {}
|
|
2263
|
+
},
|
|
2264
|
+
method,
|
|
2265
|
+
providerInstanceId
|
|
2266
|
+
});
|
|
2267
|
+
const asyncQuestions = canonical === "assistant_message" && itemId ? toAsyncQuestions(params) : void 0;
|
|
2268
|
+
if (!asyncQuestions) return [completed];
|
|
2174
2269
|
return [
|
|
2270
|
+
completed,
|
|
2175
2271
|
baseEvent({
|
|
2176
|
-
type: "
|
|
2272
|
+
type: "user-input.requested",
|
|
2177
2273
|
threadId,
|
|
2178
2274
|
turnId,
|
|
2179
|
-
itemId
|
|
2180
|
-
payload: {
|
|
2181
|
-
itemType: canonical,
|
|
2182
|
-
status,
|
|
2183
|
-
...itemTitle(params) ? { title: itemTitle(params) } : {},
|
|
2184
|
-
...itemDetail(params) ? { detail: itemDetail(params) } : {},
|
|
2185
|
-
...params !== void 0 ? { data: params } : {}
|
|
2186
|
-
},
|
|
2275
|
+
requestId: `codex-async:${itemId}`,
|
|
2276
|
+
payload: { questions: asyncQuestions, responseMode: "message" },
|
|
2187
2277
|
method,
|
|
2188
2278
|
providerInstanceId
|
|
2189
2279
|
})
|
|
@@ -2375,6 +2465,8 @@ function toRequestTypeFromKind(kind) {
|
|
|
2375
2465
|
return "apply_patch_approval";
|
|
2376
2466
|
case "mcp-elicitation":
|
|
2377
2467
|
return "mcp_elicitation_approval";
|
|
2468
|
+
// 契约没有单独的权限审批类型;宿主把 unknown 当 tool 类审批卡片渲染。
|
|
2469
|
+
case "permission":
|
|
2378
2470
|
default:
|
|
2379
2471
|
return "unknown";
|
|
2380
2472
|
}
|
|
@@ -2397,6 +2489,7 @@ function toRequestTypeFromMethod(method) {
|
|
|
2397
2489
|
return "tool_user_input";
|
|
2398
2490
|
case "item/tool/call":
|
|
2399
2491
|
return "dynamic_tool_call";
|
|
2492
|
+
case "item/permissions/requestApproval":
|
|
2400
2493
|
default:
|
|
2401
2494
|
return "unknown";
|
|
2402
2495
|
}
|
|
@@ -2441,6 +2534,17 @@ function mapServerRequestToEvents(input) {
|
|
|
2441
2534
|
if (appName) payload["appName"] = appName;
|
|
2442
2535
|
if (described.message) payload["detail"] = described.message;
|
|
2443
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
|
+
};
|
|
2444
2548
|
} else if (input.method === "item/fileChange/requestApproval" || input.method.includes("applyPatch")) {
|
|
2445
2549
|
const described = describeFileChangeApproval(input.params);
|
|
2446
2550
|
if (described.detail) payload["detail"] = described.detail;
|
|
@@ -2474,7 +2578,13 @@ function mapServerRequestToEvents(input) {
|
|
|
2474
2578
|
}
|
|
2475
2579
|
|
|
2476
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
|
+
}
|
|
2477
2586
|
function cancelWireFor(pending) {
|
|
2587
|
+
if (pending.kind === "permission") return toPermissionsResponse("cancel", pending.params, false);
|
|
2478
2588
|
return pending.kind === "mcp-elicitation" ? toMcpElicitationResponse("cancel", pending.params) : { decision: "cancel" };
|
|
2479
2589
|
}
|
|
2480
2590
|
var sleep = (ms) => new Promise((resolve2) => {
|
|
@@ -2524,6 +2634,7 @@ var CodexSessionRuntime = class {
|
|
|
2524
2634
|
grants;
|
|
2525
2635
|
declineRelay;
|
|
2526
2636
|
collabChildren = /* @__PURE__ */ new Map();
|
|
2637
|
+
collabChildMeta = /* @__PURE__ */ new Map();
|
|
2527
2638
|
liveForeignTurns = /* @__PURE__ */ new Map();
|
|
2528
2639
|
collabReceiverTurns = /* @__PURE__ */ new Map();
|
|
2529
2640
|
memoryThreadIds = /* @__PURE__ */ new Set();
|
|
@@ -2732,14 +2843,28 @@ var CodexSessionRuntime = class {
|
|
|
2732
2843
|
}
|
|
2733
2844
|
this.upsertCollabChild(agentThreadId, { agentPath: activity.agentPath });
|
|
2734
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
|
+
}
|
|
2735
2856
|
const child = this.collabChildren.get(conversationId);
|
|
2736
2857
|
if (method === "turn/started") {
|
|
2858
|
+
this.reopenCollabChildMeta(conversationId);
|
|
2737
2859
|
const turnId = isRecord3(params) ? asString(params["turnId"]) ?? (isRecord3(params["turn"]) ? asString(params["turn"]["id"]) : null) : null;
|
|
2738
2860
|
if (turnId) this.liveForeignTurns.set(conversationId, turnId);
|
|
2739
2861
|
}
|
|
2740
2862
|
if (method === "turn/completed" || method === "thread/closed") {
|
|
2741
2863
|
this.liveForeignTurns.delete(conversationId);
|
|
2742
2864
|
}
|
|
2865
|
+
if (method === "thread/closed" && this.collabChildren.has(conversationId)) {
|
|
2866
|
+
this.metaOf(conversationId).closed = true;
|
|
2867
|
+
}
|
|
2743
2868
|
if (!child) {
|
|
2744
2869
|
if (CHILD_CHATTER_METHODS.has(method) || !CHILD_AGENT_EVENT_METHODS.has(method)) return;
|
|
2745
2870
|
return;
|
|
@@ -2800,6 +2925,7 @@ var CodexSessionRuntime = class {
|
|
|
2800
2925
|
};
|
|
2801
2926
|
this.collabChildren.set(agentThreadId, child);
|
|
2802
2927
|
this.emitCollab("collabAgent/started", child);
|
|
2928
|
+
this.startCollabChildMetaLookup(agentThreadId);
|
|
2803
2929
|
return;
|
|
2804
2930
|
}
|
|
2805
2931
|
if (agentPath === existing.agentPath && role === existing.role && nickname === existing.nickname) {
|
|
@@ -2814,12 +2940,64 @@ var CodexSessionRuntime = class {
|
|
|
2814
2940
|
this.collabChildren.set(agentThreadId, merged);
|
|
2815
2941
|
this.emitCollab("collabAgent/metadata", merged, { status: merged.status });
|
|
2816
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
|
+
}
|
|
2817
2992
|
emitCollab(method, child, extra) {
|
|
2993
|
+
const meta = this.collabChildMeta.get(child.agentThreadId);
|
|
2818
2994
|
const params = {
|
|
2819
2995
|
agentThreadId: child.agentThreadId,
|
|
2820
2996
|
agentPath: child.agentPath,
|
|
2821
2997
|
agentRole: child.role ?? pathLeaf2(child.agentPath) ?? "general-purpose",
|
|
2822
2998
|
agentNickname: child.nickname,
|
|
2999
|
+
...meta?.model ? { model: meta.model } : {},
|
|
3000
|
+
...meta?.effort ? { effort: meta.effort } : {},
|
|
2823
3001
|
...isRecord3(extra) ? extra : {}
|
|
2824
3002
|
};
|
|
2825
3003
|
for (const event of mapNotificationToEvents({
|
|
@@ -2858,6 +3036,16 @@ var CodexSessionRuntime = class {
|
|
|
2858
3036
|
await this.handleApproval(id, method, params, "mcp-elicitation", described.identity);
|
|
2859
3037
|
return;
|
|
2860
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
|
+
}
|
|
2861
3049
|
if (method === "item/tool/requestUserInput") {
|
|
2862
3050
|
const requestId = this.requestIdFor(id);
|
|
2863
3051
|
const events = mapServerRequestToEvents({
|
|
@@ -2886,7 +3074,7 @@ var CodexSessionRuntime = class {
|
|
|
2886
3074
|
await this.rpc.respondError(id, -32601, `Method not found: ${method}`);
|
|
2887
3075
|
}
|
|
2888
3076
|
async handleApproval(id, method, params, kind, identity) {
|
|
2889
|
-
const grant = this.grants.lookup(codexApprovalGrantKey(kind, identity));
|
|
3077
|
+
const grant = kind === "permission" ? void 0 : this.grants.lookup(codexApprovalGrantKey(kind, identity));
|
|
2890
3078
|
if (grant) {
|
|
2891
3079
|
const decision = wireDecisionFromPersist(kind, grant);
|
|
2892
3080
|
await this.rpc.respond(id, { decision });
|
|
@@ -2928,7 +3116,9 @@ var CodexSessionRuntime = class {
|
|
|
2928
3116
|
if (decision === "allow" && pending.kind === "mcp-elicitation") {
|
|
2929
3117
|
wire = toMcpElicitationResponse("allow", pending.params);
|
|
2930
3118
|
}
|
|
2931
|
-
if (
|
|
3119
|
+
if (pending.kind === "permission") {
|
|
3120
|
+
wire = toPermissionsResponse(decision, pending.params, persist !== void 0);
|
|
3121
|
+
} else if (decision === "allow" && persist) {
|
|
2932
3122
|
this.grants.remember(codexApprovalGrantKey(pending.kind, pending.identity), persist);
|
|
2933
3123
|
if (pending.kind !== "mcp-elicitation") {
|
|
2934
3124
|
wire = { decision: wireDecisionFromPersist(pending.kind, persist) };
|
|
@@ -3061,10 +3251,13 @@ var CodexSessionRuntime = class {
|
|
|
3061
3251
|
});
|
|
3062
3252
|
return this.session;
|
|
3063
3253
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3254
|
+
/** 宿主没给模式时沿用上一轮的模式;从没给过按 default。指令每轮都要带上。 */
|
|
3255
|
+
turnInteractionMode(input) {
|
|
3256
|
+
const mode = typeof input.interactionMode === "string" ? input.interactionMode : this.interactionMode ?? "default";
|
|
3066
3257
|
this.interactionMode = mode;
|
|
3067
|
-
|
|
3258
|
+
return mode;
|
|
3259
|
+
}
|
|
3260
|
+
developerInstructions(input, mode) {
|
|
3068
3261
|
const tools = hasConfiguredMcpServer(this.appServerArgs);
|
|
3069
3262
|
const model = this.model ?? DEFAULT_MODEL;
|
|
3070
3263
|
const effort = modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? DEFAULT_TEXT_GENERATION_REASONING_EFFORT;
|
|
@@ -3112,7 +3305,8 @@ var CodexSessionRuntime = class {
|
|
|
3112
3305
|
}
|
|
3113
3306
|
const sandboxPolicy = this.#turnSandboxPolicy();
|
|
3114
3307
|
const config = this.#threadConfig();
|
|
3115
|
-
const
|
|
3308
|
+
const interactionMode = this.turnInteractionMode(input);
|
|
3309
|
+
const developerInstructions = this.developerInstructions(input, interactionMode);
|
|
3116
3310
|
const requestedModel = modelIdFromSelection(input.modelSelection) ?? this.model;
|
|
3117
3311
|
const serviceTier = modelSelectionStringOption(input.modelSelection, "serviceTier");
|
|
3118
3312
|
const params = {
|
|
@@ -3123,16 +3317,14 @@ var CodexSessionRuntime = class {
|
|
|
3123
3317
|
sandboxPolicy,
|
|
3124
3318
|
...requestedModel ? { model: requestedModel } : {},
|
|
3125
3319
|
...serviceTier ? { serviceTier } : {},
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
const interactionMode = typeof input.interactionMode === "string" ? input.interactionMode : void 0;
|
|
3129
|
-
if (interactionMode !== void 0) {
|
|
3130
|
-
params["collaborationMode"] = codexCollaborationMode({
|
|
3320
|
+
// 指令只能走这里:turn 顶层的 developerInstructions 会被 CLI 静默丢掉(见 codexCollaborationMode)。
|
|
3321
|
+
collaborationMode: codexCollaborationMode({
|
|
3131
3322
|
interactionMode,
|
|
3132
3323
|
model: requestedModel ?? DEFAULT_MODEL,
|
|
3133
|
-
reasoningEffort: modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? void 0
|
|
3134
|
-
|
|
3135
|
-
|
|
3324
|
+
reasoningEffort: modelSelectionStringOption(input.modelSelection, "reasoningEffort") ?? void 0,
|
|
3325
|
+
developerInstructions
|
|
3326
|
+
})
|
|
3327
|
+
};
|
|
3136
3328
|
const isSteer = input.expectedTurnId !== void 0;
|
|
3137
3329
|
const result = await markProviderRejection(
|
|
3138
3330
|
() => isSteer ? this.rpc.request(
|
|
@@ -3231,9 +3423,10 @@ var CodexSessionRuntime = class {
|
|
|
3231
3423
|
}
|
|
3232
3424
|
return { threadId: this.threadId, turns: this.turns };
|
|
3233
3425
|
}
|
|
3234
|
-
|
|
3426
|
+
/** 会话收口时把还挂着的审批答成「取消」:应答形状按审批种类,权限审批不认 decision。 */
|
|
3427
|
+
settlePendingApprovals() {
|
|
3235
3428
|
for (const [requestId, pending] of this.pendingApprovals) {
|
|
3236
|
-
pending.settle(
|
|
3429
|
+
pending.settle(cancelWireFor(pending));
|
|
3237
3430
|
this.pendingApprovals.delete(requestId);
|
|
3238
3431
|
}
|
|
3239
3432
|
}
|
|
@@ -3248,7 +3441,7 @@ var CodexSessionRuntime = class {
|
|
|
3248
3441
|
this.closed = true;
|
|
3249
3442
|
this.requestKinds.clear();
|
|
3250
3443
|
this.failPendingNativeWrites("session closed before write confirmation");
|
|
3251
|
-
this.settlePendingApprovals(
|
|
3444
|
+
this.settlePendingApprovals();
|
|
3252
3445
|
this.settlePendingUserInputs({});
|
|
3253
3446
|
this.declineRelay.clear(this.threadId);
|
|
3254
3447
|
this.grants.clear();
|
|
@@ -3837,7 +4030,7 @@ async function verifyCodexSignIn(input) {
|
|
|
3837
4030
|
return {
|
|
3838
4031
|
installed: false,
|
|
3839
4032
|
version: null,
|
|
3840
|
-
readiness: "
|
|
4033
|
+
readiness: "not-installed",
|
|
3841
4034
|
auth: { status: "unknown" },
|
|
3842
4035
|
detail: "Codex program path is not configured"
|
|
3843
4036
|
};
|
|
@@ -4347,6 +4540,10 @@ ${extra}` : ""}`;
|
|
|
4347
4540
|
}
|
|
4348
4541
|
|
|
4349
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
|
+
}
|
|
4350
4547
|
var yieldTick = () => new Promise((resolve2) => {
|
|
4351
4548
|
setImmediate(resolve2);
|
|
4352
4549
|
});
|
|
@@ -4428,12 +4625,26 @@ var createCodexInstance = (config, host, attachments) => {
|
|
|
4428
4625
|
let rpc;
|
|
4429
4626
|
let version = null;
|
|
4430
4627
|
try {
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
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
|
+
}
|
|
4437
4648
|
trackHandle(handle, `${recipe.binaryPath} (probe)`);
|
|
4438
4649
|
rpc = await createCodexRpcClient(handle, {
|
|
4439
4650
|
onNotification: () => void 0,
|
|
@@ -4445,10 +4656,10 @@ var createCodexInstance = (config, host, attachments) => {
|
|
|
4445
4656
|
);
|
|
4446
4657
|
await rpc.notify("initialized");
|
|
4447
4658
|
const models = input.listModels ? await listCodexModels(rpc) : void 0;
|
|
4448
|
-
return { version, models, skills: await listCodexSkills(rpc) };
|
|
4659
|
+
return { version, models, skills: await listCodexSkills(rpc), spawnFailed: null };
|
|
4449
4660
|
} catch (error) {
|
|
4450
4661
|
if (error instanceof ExecutorSpawnRefusedError) throw error;
|
|
4451
|
-
return { version, models: void 0, skills: void 0 };
|
|
4662
|
+
return { version, models: void 0, skills: void 0, spawnFailed: null };
|
|
4452
4663
|
} finally {
|
|
4453
4664
|
await rpc?.close().catch(() => void 0);
|
|
4454
4665
|
await handle?.kill("SIGTERM").catch(() => void 0);
|
|
@@ -4469,15 +4680,24 @@ var createCodexInstance = (config, host, attachments) => {
|
|
|
4469
4680
|
return {
|
|
4470
4681
|
installed: false,
|
|
4471
4682
|
version: null,
|
|
4472
|
-
readiness: "
|
|
4683
|
+
readiness: "not-installed",
|
|
4473
4684
|
auth: { status: "unknown" },
|
|
4474
4685
|
detail: "Codex program path is not configured"
|
|
4475
4686
|
};
|
|
4476
4687
|
}
|
|
4477
4688
|
const connectionError = codexApiConnectionError(recipe, recipe.environment);
|
|
4478
|
-
const { version, models, skills } = await probeAppServer({
|
|
4689
|
+
const { version, models, skills, spawnFailed } = await probeAppServer({
|
|
4479
4690
|
listModels: connectionError === null
|
|
4480
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
|
+
}
|
|
4481
4701
|
if (connectionError) {
|
|
4482
4702
|
return {
|
|
4483
4703
|
installed: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/agent-codex",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2658",
|
|
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.
|
|
32
|
+
"@ycodium-ai/plugin-api": "0.2.2658",
|
|
33
33
|
"ycodium-executor-testing": "0.0.0"
|
|
34
34
|
},
|
|
35
35
|
"ycodium": {
|