@ycodium-ai/agent-codex 0.2.2126 → 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 +82 -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";
|
|
@@ -500,6 +501,13 @@ function resolveCodexHomeLayout(input) {
|
|
|
500
501
|
continuationKey: `codex:home:${sharedHomePath}`
|
|
501
502
|
};
|
|
502
503
|
}
|
|
504
|
+
function codexContinuationKey(input) {
|
|
505
|
+
return resolveCodexHomeLayout({
|
|
506
|
+
homePath: input.homePath,
|
|
507
|
+
shadowHomePath: "",
|
|
508
|
+
environmentHome: input.instanceEnvironment["CODEX_HOME"] ?? input.inheritedEnvironment["CODEX_HOME"]
|
|
509
|
+
}).continuationKey;
|
|
510
|
+
}
|
|
503
511
|
async function readLinkState(linkPath) {
|
|
504
512
|
try {
|
|
505
513
|
const target = await NodeFSP.readlink(linkPath);
|
|
@@ -748,6 +756,10 @@ function asString(value) {
|
|
|
748
756
|
function asNumber(value) {
|
|
749
757
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
750
758
|
}
|
|
759
|
+
function codexVersionFromInitialize(initializeResult) {
|
|
760
|
+
const userAgent = isRecord3(initializeResult) ? asString(initializeResult["userAgent"]) : null;
|
|
761
|
+
return userAgent?.match(/\/(\S+)/)?.[1] ?? null;
|
|
762
|
+
}
|
|
751
763
|
function decodeThreadOpen(result) {
|
|
752
764
|
const thread = isRecord3(result) ? isRecord3(result["thread"]) ? result["thread"] : result : null;
|
|
753
765
|
const id = thread ? asString(thread["id"]) : null;
|
|
@@ -985,7 +997,15 @@ async function createCodexRpcClient(handle, handlers) {
|
|
|
985
997
|
const settled = new Promise((resolve2, reject) => {
|
|
986
998
|
pending.set(id, { resolve: resolve2, reject, method });
|
|
987
999
|
});
|
|
988
|
-
|
|
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
|
+
}
|
|
989
1009
|
const limit = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
990
1010
|
if (limit <= 0) return await settled;
|
|
991
1011
|
let timer;
|
|
@@ -1026,7 +1046,15 @@ async function createCodexRpcClient(handle, handlers) {
|
|
|
1026
1046
|
failAll("the client was closed");
|
|
1027
1047
|
await Promise.all([pumpStdout, pumpStderr]);
|
|
1028
1048
|
};
|
|
1029
|
-
return {
|
|
1049
|
+
return {
|
|
1050
|
+
request,
|
|
1051
|
+
notify,
|
|
1052
|
+
respond,
|
|
1053
|
+
respondError,
|
|
1054
|
+
close,
|
|
1055
|
+
pid: handle.pid,
|
|
1056
|
+
pendingCountForTests: () => pending.size
|
|
1057
|
+
};
|
|
1030
1058
|
}
|
|
1031
1059
|
|
|
1032
1060
|
// src/approvalGrant.ts
|
|
@@ -2334,6 +2362,11 @@ function pathLeaf2(value) {
|
|
|
2334
2362
|
const parts = value.split("/").filter((part) => part.length > 0);
|
|
2335
2363
|
return parts.at(-1);
|
|
2336
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
|
+
}
|
|
2337
2370
|
var CodexSessionRuntime = class {
|
|
2338
2371
|
threadId;
|
|
2339
2372
|
cwd;
|
|
@@ -2372,6 +2405,15 @@ var CodexSessionRuntime = class {
|
|
|
2372
2405
|
codexNativeModeFor(this.runtimeMode, this.nativeModeId)
|
|
2373
2406
|
);
|
|
2374
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
|
+
}
|
|
2375
2417
|
constructor(host, rpc, emit, input, cwd, appServerArgs, grants, declineRelay) {
|
|
2376
2418
|
this.host = host;
|
|
2377
2419
|
this.rpc = rpc;
|
|
@@ -2641,7 +2683,7 @@ var CodexSessionRuntime = class {
|
|
|
2641
2683
|
jsonRpcId: id,
|
|
2642
2684
|
settle: (result) => {
|
|
2643
2685
|
this.pendingUserInputs.delete(requestId);
|
|
2644
|
-
void this.rpc.respond(id, result).finally(resolve2);
|
|
2686
|
+
void this.rpc.respond(id, result).catch((error) => this.#warnAnswerUndelivered(method, id, error)).finally(resolve2);
|
|
2645
2687
|
}
|
|
2646
2688
|
});
|
|
2647
2689
|
for (const event of events) this.emit(event);
|
|
@@ -2675,7 +2717,7 @@ var CodexSessionRuntime = class {
|
|
|
2675
2717
|
identity,
|
|
2676
2718
|
settle: (decision) => {
|
|
2677
2719
|
this.pendingApprovals.delete(requestId);
|
|
2678
|
-
void this.rpc.respond(id, decision).finally(resolve2);
|
|
2720
|
+
void this.rpc.respond(id, decision).catch((error) => this.#warnAnswerUndelivered(method, id, error)).finally(resolve2);
|
|
2679
2721
|
}
|
|
2680
2722
|
});
|
|
2681
2723
|
for (const event of events) this.emit(event);
|
|
@@ -2697,7 +2739,7 @@ var CodexSessionRuntime = class {
|
|
|
2697
2739
|
}
|
|
2698
2740
|
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
2699
2741
|
pending.settle(wire);
|
|
2700
|
-
await ack.catch(() => "closed");
|
|
2742
|
+
const delivery = await ack.catch(() => "closed");
|
|
2701
2743
|
this.emit({
|
|
2702
2744
|
eventId: `codex-req-${requestId}`,
|
|
2703
2745
|
provider: CODEX_DRIVER_KIND,
|
|
@@ -2712,6 +2754,7 @@ var CodexSessionRuntime = class {
|
|
|
2712
2754
|
...reason ? { reason } : {}
|
|
2713
2755
|
}
|
|
2714
2756
|
});
|
|
2757
|
+
if (delivery !== "written") throw undeliveredAnswerError(requestId, delivery);
|
|
2715
2758
|
}
|
|
2716
2759
|
async respondToUserInput(requestId, answers) {
|
|
2717
2760
|
const pending = this.pendingUserInputs.get(requestId);
|
|
@@ -2720,7 +2763,9 @@ var CodexSessionRuntime = class {
|
|
|
2720
2763
|
if (mapped === void 0) {
|
|
2721
2764
|
throw new Error(`invalid user input answers for request ${requestId}`);
|
|
2722
2765
|
}
|
|
2766
|
+
const ack = this.registerWriteAck(pending.jsonRpcId);
|
|
2723
2767
|
pending.settle({ answers: mapped });
|
|
2768
|
+
const delivery = await ack.catch(() => "closed");
|
|
2724
2769
|
this.emit({
|
|
2725
2770
|
eventId: `codex-ui-${requestId}`,
|
|
2726
2771
|
provider: CODEX_DRIVER_KIND,
|
|
@@ -2731,6 +2776,7 @@ var CodexSessionRuntime = class {
|
|
|
2731
2776
|
// 契约的 answers 是 questionId → 答案 的 record,不是数组。
|
|
2732
2777
|
payload: { answers: toCanonicalUserInputAnswers(mapped) }
|
|
2733
2778
|
});
|
|
2779
|
+
if (delivery !== "written") throw undeliveredAnswerError(requestId, delivery);
|
|
2734
2780
|
}
|
|
2735
2781
|
async withActiveWriterRecovery(run, allowUnsubscribe) {
|
|
2736
2782
|
let lastError;
|
|
@@ -3532,15 +3578,18 @@ async function verifyCodexSignIn(input) {
|
|
|
3532
3578
|
onServerRequest: () => void 0,
|
|
3533
3579
|
onStderrLine: () => void 0
|
|
3534
3580
|
});
|
|
3581
|
+
let version = null;
|
|
3535
3582
|
try {
|
|
3536
|
-
|
|
3583
|
+
version = codexVersionFromInitialize(
|
|
3584
|
+
await rpc.request("initialize", CODEX_INITIALIZE_PARAMS, 15e3)
|
|
3585
|
+
);
|
|
3537
3586
|
await rpc.notify("initialized");
|
|
3538
3587
|
const account = await rpc.request("account/read", { refreshToken: true }, 15e3);
|
|
3539
3588
|
const type = isRecord3(account) ? asString(account["type"]) : null;
|
|
3540
3589
|
if (type !== "chatgpt") {
|
|
3541
3590
|
return {
|
|
3542
3591
|
installed: true,
|
|
3543
|
-
version
|
|
3592
|
+
version,
|
|
3544
3593
|
readiness: "unchecked",
|
|
3545
3594
|
auth: { status: "unauthenticated" },
|
|
3546
3595
|
...CODEX_NATIVE_MODE_REPORT
|
|
@@ -3549,7 +3598,7 @@ async function verifyCodexSignIn(input) {
|
|
|
3549
3598
|
const models = await listCodexModels(rpc);
|
|
3550
3599
|
return {
|
|
3551
3600
|
installed: true,
|
|
3552
|
-
version
|
|
3601
|
+
version,
|
|
3553
3602
|
readiness: "ready",
|
|
3554
3603
|
auth,
|
|
3555
3604
|
...models === void 0 ? {} : { models },
|
|
@@ -3558,7 +3607,7 @@ async function verifyCodexSignIn(input) {
|
|
|
3558
3607
|
} catch {
|
|
3559
3608
|
return {
|
|
3560
3609
|
installed: true,
|
|
3561
|
-
version
|
|
3610
|
+
version,
|
|
3562
3611
|
readiness: "unchecked",
|
|
3563
3612
|
auth: { status: "unknown" },
|
|
3564
3613
|
...CODEX_NATIVE_MODE_REPORT
|
|
@@ -4094,7 +4143,7 @@ var createCodexInstance = (config, host) => {
|
|
|
4094
4143
|
return booked;
|
|
4095
4144
|
};
|
|
4096
4145
|
const probeAuth = () => accountToAuth2(readCodexAccount(accountHomeFromLayout(layoutFor(recipe))));
|
|
4097
|
-
const
|
|
4146
|
+
const probeAppServer = async (input) => {
|
|
4098
4147
|
const layout = layoutFor(recipe);
|
|
4099
4148
|
const env = buildCodexSpawnEnv({
|
|
4100
4149
|
recipe,
|
|
@@ -4103,6 +4152,7 @@ var createCodexInstance = (config, host) => {
|
|
|
4103
4152
|
});
|
|
4104
4153
|
let handle;
|
|
4105
4154
|
let rpc;
|
|
4155
|
+
let version = null;
|
|
4106
4156
|
try {
|
|
4107
4157
|
handle = await host.process.spawn({
|
|
4108
4158
|
program: recipe.binaryPath,
|
|
@@ -4116,11 +4166,14 @@ var createCodexInstance = (config, host) => {
|
|
|
4116
4166
|
onServerRequest: () => void 0,
|
|
4117
4167
|
onStderrLine: () => void 0
|
|
4118
4168
|
});
|
|
4119
|
-
|
|
4169
|
+
version = codexVersionFromInitialize(
|
|
4170
|
+
await rpc.request("initialize", CODEX_INITIALIZE_PARAMS)
|
|
4171
|
+
);
|
|
4120
4172
|
await rpc.notify("initialized");
|
|
4121
|
-
return await listCodexModels(rpc);
|
|
4122
|
-
} catch {
|
|
4123
|
-
|
|
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 };
|
|
4124
4177
|
} finally {
|
|
4125
4178
|
await rpc?.close().catch(() => void 0);
|
|
4126
4179
|
await handle?.kill("SIGTERM").catch(() => void 0);
|
|
@@ -4147,20 +4200,20 @@ var createCodexInstance = (config, host) => {
|
|
|
4147
4200
|
};
|
|
4148
4201
|
}
|
|
4149
4202
|
const connectionError = codexApiConnectionError(recipe, recipe.environment);
|
|
4203
|
+
const { version, models } = await probeAppServer({ listModels: connectionError === null });
|
|
4150
4204
|
if (connectionError) {
|
|
4151
4205
|
return {
|
|
4152
4206
|
installed: true,
|
|
4153
|
-
version
|
|
4207
|
+
version,
|
|
4154
4208
|
readiness: "error",
|
|
4155
4209
|
auth: probeAuth(),
|
|
4156
4210
|
detail: connectionError,
|
|
4157
4211
|
...CODEX_NATIVE_MODE_REPORT
|
|
4158
4212
|
};
|
|
4159
4213
|
}
|
|
4160
|
-
const models = await probeModels();
|
|
4161
4214
|
return {
|
|
4162
4215
|
installed: true,
|
|
4163
|
-
version
|
|
4216
|
+
version,
|
|
4164
4217
|
readiness: models === void 0 ? "unchecked" : "ready",
|
|
4165
4218
|
auth: probeAuth(),
|
|
4166
4219
|
...models === void 0 ? {} : { models },
|
|
@@ -4212,7 +4265,13 @@ var createCodexInstance = (config, host) => {
|
|
|
4212
4265
|
runtime?.handleNotification(notification.method, notification.params);
|
|
4213
4266
|
},
|
|
4214
4267
|
onServerRequest: (request) => {
|
|
4215
|
-
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
|
+
});
|
|
4216
4275
|
},
|
|
4217
4276
|
onStderrLine: (line) => {
|
|
4218
4277
|
runtime?.handleStderrLine(line);
|
|
@@ -4317,6 +4376,11 @@ var createCodexInstance = (config, host) => {
|
|
|
4317
4376
|
recipe,
|
|
4318
4377
|
layout: layoutFor(recipe)
|
|
4319
4378
|
}),
|
|
4379
|
+
continuationKey: codexContinuationKey({
|
|
4380
|
+
homePath: recipe.homePath,
|
|
4381
|
+
instanceEnvironment: recipe.environment,
|
|
4382
|
+
inheritedEnvironment: process.env
|
|
4383
|
+
}),
|
|
4320
4384
|
sessions: {
|
|
4321
4385
|
scan: async (input) => {
|
|
4322
4386
|
if (recipe.binaryPath.length === 0) {
|
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",
|