@ycodium-ai/agent-codex 0.2.2130 → 0.2.2143
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 +70 -18
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -92,6 +92,7 @@ var CODEX_CARD = {
|
|
|
92
92
|
|
|
93
93
|
// src/codexInstance.ts
|
|
94
94
|
import * as NodeProcess from "node:process";
|
|
95
|
+
import { ExecutorSpawnRefusedError } from "@ycodium-ai/plugin-api/executor";
|
|
95
96
|
|
|
96
97
|
// src/codexAccount.ts
|
|
97
98
|
import * as NodeFS from "node:fs";
|
|
@@ -755,6 +756,10 @@ function asString(value) {
|
|
|
755
756
|
function asNumber(value) {
|
|
756
757
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
757
758
|
}
|
|
759
|
+
function codexVersionFromInitialize(initializeResult) {
|
|
760
|
+
const userAgent = isRecord3(initializeResult) ? asString(initializeResult["userAgent"]) : null;
|
|
761
|
+
return userAgent?.match(/\/(\S+)/)?.[1] ?? null;
|
|
762
|
+
}
|
|
758
763
|
function decodeThreadOpen(result) {
|
|
759
764
|
const thread = isRecord3(result) ? isRecord3(result["thread"]) ? result["thread"] : result : null;
|
|
760
765
|
const id = thread ? asString(thread["id"]) : null;
|
|
@@ -992,7 +997,15 @@ async function createCodexRpcClient(handle, handlers) {
|
|
|
992
997
|
const settled = new Promise((resolve2, reject) => {
|
|
993
998
|
pending.set(id, { resolve: resolve2, reject, method });
|
|
994
999
|
});
|
|
995
|
-
|
|
1000
|
+
try {
|
|
1001
|
+
await Promise.race([
|
|
1002
|
+
writeLine({ id, method, ...params === void 0 ? {} : { params } }),
|
|
1003
|
+
settled
|
|
1004
|
+
]);
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
pending.delete(id);
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
996
1009
|
const limit = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
997
1010
|
if (limit <= 0) return await settled;
|
|
998
1011
|
let timer;
|
|
@@ -1033,7 +1046,15 @@ async function createCodexRpcClient(handle, handlers) {
|
|
|
1033
1046
|
failAll("the client was closed");
|
|
1034
1047
|
await Promise.all([pumpStdout, pumpStderr]);
|
|
1035
1048
|
};
|
|
1036
|
-
return {
|
|
1049
|
+
return {
|
|
1050
|
+
request,
|
|
1051
|
+
notify,
|
|
1052
|
+
respond,
|
|
1053
|
+
respondError,
|
|
1054
|
+
close,
|
|
1055
|
+
pid: handle.pid,
|
|
1056
|
+
pendingCountForTests: () => pending.size
|
|
1057
|
+
};
|
|
1037
1058
|
}
|
|
1038
1059
|
|
|
1039
1060
|
// src/approvalGrant.ts
|
|
@@ -2341,6 +2362,11 @@ function pathLeaf2(value) {
|
|
|
2341
2362
|
const parts = value.split("/").filter((part) => part.length > 0);
|
|
2342
2363
|
return parts.at(-1);
|
|
2343
2364
|
}
|
|
2365
|
+
function undeliveredAnswerError(requestId, delivery) {
|
|
2366
|
+
return new Error(
|
|
2367
|
+
delivery === "closed" ? `codex did not receive the answer to request ${requestId}: the session closed before the answer was written` : `codex did not receive the answer to request ${requestId}: writing it to the CLI failed`
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2344
2370
|
var CodexSessionRuntime = class {
|
|
2345
2371
|
threadId;
|
|
2346
2372
|
cwd;
|
|
@@ -2379,6 +2405,15 @@ var CodexSessionRuntime = class {
|
|
|
2379
2405
|
codexNativeModeFor(this.runtimeMode, this.nativeModeId)
|
|
2380
2406
|
);
|
|
2381
2407
|
}
|
|
2408
|
+
/** 给 CLI 的应答没写进去:带上线程与方法名记一行 warn。 */
|
|
2409
|
+
#warnAnswerUndelivered(method, jsonRpcId, error) {
|
|
2410
|
+
this.host.log.warn("codex did not receive an answer", {
|
|
2411
|
+
threadId: this.threadId,
|
|
2412
|
+
method,
|
|
2413
|
+
jsonRpcId,
|
|
2414
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2382
2417
|
constructor(host, rpc, emit, input, cwd, appServerArgs, grants, declineRelay) {
|
|
2383
2418
|
this.host = host;
|
|
2384
2419
|
this.rpc = rpc;
|
|
@@ -2648,7 +2683,7 @@ var CodexSessionRuntime = class {
|
|
|
2648
2683
|
jsonRpcId: id,
|
|
2649
2684
|
settle: (result) => {
|
|
2650
2685
|
this.pendingUserInputs.delete(requestId);
|
|
2651
|
-
void this.rpc.respond(id, result).finally(resolve2);
|
|
2686
|
+
void this.rpc.respond(id, result).catch((error) => this.#warnAnswerUndelivered(method, id, error)).finally(resolve2);
|
|
2652
2687
|
}
|
|
2653
2688
|
});
|
|
2654
2689
|
for (const event of events) this.emit(event);
|
|
@@ -2682,7 +2717,7 @@ var CodexSessionRuntime = class {
|
|
|
2682
2717
|
identity,
|
|
2683
2718
|
settle: (decision) => {
|
|
2684
2719
|
this.pendingApprovals.delete(requestId);
|
|
2685
|
-
void this.rpc.respond(id, decision).finally(resolve2);
|
|
2720
|
+
void this.rpc.respond(id, decision).catch((error) => this.#warnAnswerUndelivered(method, id, error)).finally(resolve2);
|
|
2686
2721
|
}
|
|
2687
2722
|
});
|
|
2688
2723
|
for (const event of events) this.emit(event);
|
|
@@ -2704,7 +2739,7 @@ var CodexSessionRuntime = class {
|
|
|
2704
2739
|
}
|
|
2705
2740
|
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
2706
2741
|
pending.settle(wire);
|
|
2707
|
-
await ack.catch(() => "closed");
|
|
2742
|
+
const delivery = await ack.catch(() => "closed");
|
|
2708
2743
|
this.emit({
|
|
2709
2744
|
eventId: `codex-req-${requestId}`,
|
|
2710
2745
|
provider: CODEX_DRIVER_KIND,
|
|
@@ -2719,6 +2754,7 @@ var CodexSessionRuntime = class {
|
|
|
2719
2754
|
...reason ? { reason } : {}
|
|
2720
2755
|
}
|
|
2721
2756
|
});
|
|
2757
|
+
if (delivery !== "written") throw undeliveredAnswerError(requestId, delivery);
|
|
2722
2758
|
}
|
|
2723
2759
|
async respondToUserInput(requestId, answers) {
|
|
2724
2760
|
const pending = this.pendingUserInputs.get(requestId);
|
|
@@ -2727,7 +2763,9 @@ var CodexSessionRuntime = class {
|
|
|
2727
2763
|
if (mapped === void 0) {
|
|
2728
2764
|
throw new Error(`invalid user input answers for request ${requestId}`);
|
|
2729
2765
|
}
|
|
2766
|
+
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
2730
2767
|
pending.settle({ answers: mapped });
|
|
2768
|
+
const delivery = await ack.catch(() => "closed");
|
|
2731
2769
|
this.emit({
|
|
2732
2770
|
eventId: `codex-ui-${requestId}`,
|
|
2733
2771
|
provider: CODEX_DRIVER_KIND,
|
|
@@ -2738,6 +2776,7 @@ var CodexSessionRuntime = class {
|
|
|
2738
2776
|
// 契约的 answers 是 questionId → 答案 的 record,不是数组。
|
|
2739
2777
|
payload: { answers: toCanonicalUserInputAnswers(mapped) }
|
|
2740
2778
|
});
|
|
2779
|
+
if (delivery !== "written") throw undeliveredAnswerError(requestId, delivery);
|
|
2741
2780
|
}
|
|
2742
2781
|
async withActiveWriterRecovery(run, allowUnsubscribe) {
|
|
2743
2782
|
let lastError;
|
|
@@ -3539,15 +3578,18 @@ async function verifyCodexSignIn(input) {
|
|
|
3539
3578
|
onServerRequest: () => void 0,
|
|
3540
3579
|
onStderrLine: () => void 0
|
|
3541
3580
|
});
|
|
3581
|
+
let version = null;
|
|
3542
3582
|
try {
|
|
3543
|
-
|
|
3583
|
+
version = codexVersionFromInitialize(
|
|
3584
|
+
await rpc.request("initialize", CODEX_INITIALIZE_PARAMS, 15e3)
|
|
3585
|
+
);
|
|
3544
3586
|
await rpc.notify("initialized");
|
|
3545
3587
|
const account = await rpc.request("account/read", { refreshToken: true }, 15e3);
|
|
3546
3588
|
const type = isRecord3(account) ? asString(account["type"]) : null;
|
|
3547
3589
|
if (type !== "chatgpt") {
|
|
3548
3590
|
return {
|
|
3549
3591
|
installed: true,
|
|
3550
|
-
version
|
|
3592
|
+
version,
|
|
3551
3593
|
readiness: "unchecked",
|
|
3552
3594
|
auth: { status: "unauthenticated" },
|
|
3553
3595
|
...CODEX_NATIVE_MODE_REPORT
|
|
@@ -3556,7 +3598,7 @@ async function verifyCodexSignIn(input) {
|
|
|
3556
3598
|
const models = await listCodexModels(rpc);
|
|
3557
3599
|
return {
|
|
3558
3600
|
installed: true,
|
|
3559
|
-
version
|
|
3601
|
+
version,
|
|
3560
3602
|
readiness: "ready",
|
|
3561
3603
|
auth,
|
|
3562
3604
|
...models === void 0 ? {} : { models },
|
|
@@ -3565,7 +3607,7 @@ async function verifyCodexSignIn(input) {
|
|
|
3565
3607
|
} catch {
|
|
3566
3608
|
return {
|
|
3567
3609
|
installed: true,
|
|
3568
|
-
version
|
|
3610
|
+
version,
|
|
3569
3611
|
readiness: "unchecked",
|
|
3570
3612
|
auth: { status: "unknown" },
|
|
3571
3613
|
...CODEX_NATIVE_MODE_REPORT
|
|
@@ -4101,7 +4143,7 @@ var createCodexInstance = (config, host) => {
|
|
|
4101
4143
|
return booked;
|
|
4102
4144
|
};
|
|
4103
4145
|
const probeAuth = () => accountToAuth2(readCodexAccount(accountHomeFromLayout(layoutFor(recipe))));
|
|
4104
|
-
const
|
|
4146
|
+
const probeAppServer = async (input) => {
|
|
4105
4147
|
const layout = layoutFor(recipe);
|
|
4106
4148
|
const env = buildCodexSpawnEnv({
|
|
4107
4149
|
recipe,
|
|
@@ -4110,6 +4152,7 @@ var createCodexInstance = (config, host) => {
|
|
|
4110
4152
|
});
|
|
4111
4153
|
let handle;
|
|
4112
4154
|
let rpc;
|
|
4155
|
+
let version = null;
|
|
4113
4156
|
try {
|
|
4114
4157
|
handle = await host.process.spawn({
|
|
4115
4158
|
program: recipe.binaryPath,
|
|
@@ -4123,11 +4166,14 @@ var createCodexInstance = (config, host) => {
|
|
|
4123
4166
|
onServerRequest: () => void 0,
|
|
4124
4167
|
onStderrLine: () => void 0
|
|
4125
4168
|
});
|
|
4126
|
-
|
|
4169
|
+
version = codexVersionFromInitialize(
|
|
4170
|
+
await rpc.request("initialize", CODEX_INITIALIZE_PARAMS)
|
|
4171
|
+
);
|
|
4127
4172
|
await rpc.notify("initialized");
|
|
4128
|
-
return await listCodexModels(rpc);
|
|
4129
|
-
} catch {
|
|
4130
|
-
|
|
4173
|
+
return { version, models: input.listModels ? await listCodexModels(rpc) : void 0 };
|
|
4174
|
+
} catch (error) {
|
|
4175
|
+
if (error instanceof ExecutorSpawnRefusedError) throw error;
|
|
4176
|
+
return { version, models: void 0 };
|
|
4131
4177
|
} finally {
|
|
4132
4178
|
await rpc?.close().catch(() => void 0);
|
|
4133
4179
|
await handle?.kill("SIGTERM").catch(() => void 0);
|
|
@@ -4154,20 +4200,20 @@ var createCodexInstance = (config, host) => {
|
|
|
4154
4200
|
};
|
|
4155
4201
|
}
|
|
4156
4202
|
const connectionError = codexApiConnectionError(recipe, recipe.environment);
|
|
4203
|
+
const { version, models } = await probeAppServer({ listModels: connectionError === null });
|
|
4157
4204
|
if (connectionError) {
|
|
4158
4205
|
return {
|
|
4159
4206
|
installed: true,
|
|
4160
|
-
version
|
|
4207
|
+
version,
|
|
4161
4208
|
readiness: "error",
|
|
4162
4209
|
auth: probeAuth(),
|
|
4163
4210
|
detail: connectionError,
|
|
4164
4211
|
...CODEX_NATIVE_MODE_REPORT
|
|
4165
4212
|
};
|
|
4166
4213
|
}
|
|
4167
|
-
const models = await probeModels();
|
|
4168
4214
|
return {
|
|
4169
4215
|
installed: true,
|
|
4170
|
-
version
|
|
4216
|
+
version,
|
|
4171
4217
|
readiness: models === void 0 ? "unchecked" : "ready",
|
|
4172
4218
|
auth: probeAuth(),
|
|
4173
4219
|
...models === void 0 ? {} : { models },
|
|
@@ -4219,7 +4265,13 @@ var createCodexInstance = (config, host) => {
|
|
|
4219
4265
|
runtime?.handleNotification(notification.method, notification.params);
|
|
4220
4266
|
},
|
|
4221
4267
|
onServerRequest: (request) => {
|
|
4222
|
-
void runtime?.handleServerRequest(request.id, request.method, request.params)
|
|
4268
|
+
void runtime?.handleServerRequest(request.id, request.method, request.params).catch((error) => {
|
|
4269
|
+
host.log.warn("codex server request was not answered", {
|
|
4270
|
+
threadId: input.threadId,
|
|
4271
|
+
method: request.method,
|
|
4272
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4273
|
+
});
|
|
4274
|
+
});
|
|
4223
4275
|
},
|
|
4224
4276
|
onStderrLine: (line) => {
|
|
4225
4277
|
runtime?.handleStderrLine(line);
|
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.2143",
|
|
4
4
|
"description": "Codex executor: Codex CLI over the app-server JSON-RPC protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ycodium",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@types/node": "24.12.4",
|
|
21
21
|
"esbuild": "0.28.1",
|
|
22
22
|
"vite-plus": "0.2.2",
|
|
23
|
-
"@ycodium-ai/plugin-api": "0.2.
|
|
23
|
+
"@ycodium-ai/plugin-api": "0.2.2143"
|
|
24
24
|
},
|
|
25
25
|
"ycodium": {
|
|
26
26
|
"plugin": "./dist/index.js",
|