doer-agent 0.2.5 → 0.2.6
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/agent.js +277 -151
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, statSync, watch } from "node:fs";
|
|
3
|
-
import { chmod, mkdir, open, readFile, readdir, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { chmod, mkdir, open, readFile, readdir, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { AckPolicy, connect, DeliverPolicy, JSONCodec, RetentionPolicy, StorageType, StringCodec } from "nats";
|
|
@@ -15,7 +15,6 @@ const fsRpcCodec = StringCodec();
|
|
|
15
15
|
const shellRpcCodec = StringCodec();
|
|
16
16
|
const runRpcCodec = StringCodec();
|
|
17
17
|
const sessionRpcCodec = StringCodec();
|
|
18
|
-
const codexRpcCodec = StringCodec();
|
|
19
18
|
const codexAuthRpcCodec = StringCodec();
|
|
20
19
|
const settingsRpcCodec = StringCodec();
|
|
21
20
|
const gitRpcCodec = StringCodec();
|
|
@@ -35,9 +34,6 @@ function buildAgentRunRpcSubject(userId, agentId) {
|
|
|
35
34
|
function buildAgentSessionRpcSubject(userId, agentId) {
|
|
36
35
|
return `doer.agent.session.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
37
36
|
}
|
|
38
|
-
function buildAgentCodexRpcSubject(userId, agentId) {
|
|
39
|
-
return `doer.agent.codex.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
40
|
-
}
|
|
41
37
|
function buildAgentCodexAuthRpcSubject(userId, agentId) {
|
|
42
38
|
return `doer.agent.codex.auth.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
43
39
|
}
|
|
@@ -421,9 +417,11 @@ function normalizeRunRpcRequest(args) {
|
|
|
421
417
|
throw new Error("missing responseSubject");
|
|
422
418
|
}
|
|
423
419
|
const runId = typeof args.request.runId === "string" && args.request.runId.trim() ? args.request.runId.trim() : null;
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
420
|
+
const prompt = typeof args.request.prompt === "string" && args.request.prompt.trim() ? args.request.prompt.trim() : null;
|
|
421
|
+
const sessionId = typeof args.request.sessionId === "string" && args.request.sessionId.trim() ? args.request.sessionId.trim() : null;
|
|
422
|
+
const model = normalizeCodexModel(args.request.model);
|
|
423
|
+
if (action === "start" && !prompt) {
|
|
424
|
+
throw new Error("missing prompt");
|
|
427
425
|
}
|
|
428
426
|
if ((action === "get" || action === "cancel") && !runId) {
|
|
429
427
|
throw new Error("missing runId");
|
|
@@ -437,7 +435,9 @@ function normalizeRunRpcRequest(args) {
|
|
|
437
435
|
requestId,
|
|
438
436
|
action,
|
|
439
437
|
runId,
|
|
440
|
-
|
|
438
|
+
prompt,
|
|
439
|
+
sessionId,
|
|
440
|
+
model,
|
|
441
441
|
cwd,
|
|
442
442
|
responseSubject,
|
|
443
443
|
sinceSeq,
|
|
@@ -482,6 +482,99 @@ async function removeRunTask(runId) {
|
|
|
482
482
|
const dir = await resolveRunsDir();
|
|
483
483
|
await unlink(path.join(dir, `${runId}.json`)).catch(() => undefined);
|
|
484
484
|
}
|
|
485
|
+
function sanitizeRunLockSegment(value) {
|
|
486
|
+
return value.trim().replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 160) || "lock";
|
|
487
|
+
}
|
|
488
|
+
async function resolveRunLocksDir() {
|
|
489
|
+
const dir = path.join(await resolveRunsDir(), "locks");
|
|
490
|
+
await mkdir(dir, { recursive: true });
|
|
491
|
+
return dir;
|
|
492
|
+
}
|
|
493
|
+
async function resolveRunStartLockPath(args) {
|
|
494
|
+
const dir = await resolveRunLocksDir();
|
|
495
|
+
if (typeof args.sessionId === "string" && args.sessionId.trim()) {
|
|
496
|
+
return path.join(dir, `session__${sanitizeRunLockSegment(args.sessionId)}.lock`);
|
|
497
|
+
}
|
|
498
|
+
return path.join(dir, `run__${sanitizeRunLockSegment(args.runId)}.lock`);
|
|
499
|
+
}
|
|
500
|
+
async function claimRunStartSlot(args) {
|
|
501
|
+
const lockPath = await resolveRunStartLockPath(args);
|
|
502
|
+
try {
|
|
503
|
+
const handle = await open(lockPath, "wx");
|
|
504
|
+
try {
|
|
505
|
+
const payload = {
|
|
506
|
+
runId: args.runId,
|
|
507
|
+
sessionId: typeof args.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : null,
|
|
508
|
+
pid: process.pid,
|
|
509
|
+
createdAt: formatLocalTimestamp(),
|
|
510
|
+
};
|
|
511
|
+
await handle.writeFile(`${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
512
|
+
}
|
|
513
|
+
finally {
|
|
514
|
+
await handle.close().catch(() => undefined);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
if (error?.code === "EEXIST") {
|
|
519
|
+
const lockContents = await readFile(lockPath, "utf8").catch(() => "");
|
|
520
|
+
const existingRunId = (() => {
|
|
521
|
+
try {
|
|
522
|
+
const parsed = JSON.parse(lockContents);
|
|
523
|
+
return typeof parsed.runId === "string" && parsed.runId.trim() ? parsed.runId.trim() : null;
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
})();
|
|
529
|
+
throw new Error(existingRunId ? `Another run is already active: ${existingRunId}` : "Another run is already active");
|
|
530
|
+
}
|
|
531
|
+
throw error;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function updateRunStartSlotSession(args) {
|
|
535
|
+
const nextSessionId = args.sessionId.trim();
|
|
536
|
+
if (!nextSessionId) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const previousSessionId = typeof args.previousSessionId === "string" && args.previousSessionId.trim() ? args.previousSessionId.trim() : null;
|
|
540
|
+
if (previousSessionId === nextSessionId) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const currentPath = await resolveRunStartLockPath({ runId: args.runId, sessionId: previousSessionId });
|
|
544
|
+
const nextPath = await resolveRunStartLockPath({ runId: args.runId, sessionId: nextSessionId });
|
|
545
|
+
if (currentPath === nextPath) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
await rename(currentPath, nextPath);
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
const code = error?.code;
|
|
553
|
+
if (code === "ENOENT") {
|
|
554
|
+
// Lock may already be released; nothing to migrate.
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (code === "EEXIST") {
|
|
558
|
+
throw new Error(`Another run is already active for session: ${nextSessionId}`);
|
|
559
|
+
}
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
const payload = {
|
|
563
|
+
runId: args.runId,
|
|
564
|
+
sessionId: nextSessionId,
|
|
565
|
+
pid: process.pid,
|
|
566
|
+
createdAt: formatLocalTimestamp(),
|
|
567
|
+
};
|
|
568
|
+
await writeFile(nextPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
569
|
+
}
|
|
570
|
+
async function releaseRunStartSlot(args) {
|
|
571
|
+
const paths = new Set();
|
|
572
|
+
paths.add(await resolveRunStartLockPath({ runId: args.runId, sessionId: args.sessionId ?? null }));
|
|
573
|
+
paths.add(await resolveRunStartLockPath({ runId: args.runId, sessionId: null }));
|
|
574
|
+
for (const lockPath of paths) {
|
|
575
|
+
await unlink(lockPath).catch(() => undefined);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
485
578
|
function resolveAgentSettingsDir() {
|
|
486
579
|
const workspaceRoot = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
|
|
487
580
|
return path.join(workspaceRoot, ".doer-agent");
|
|
@@ -902,6 +995,7 @@ function extractCodexSessionMetadata(value) {
|
|
|
902
995
|
}
|
|
903
996
|
async function updateRunSessionMetadata(task, metadata) {
|
|
904
997
|
let changed = false;
|
|
998
|
+
const previousSessionId = task.sessionId;
|
|
905
999
|
if (!task.sessionId && typeof metadata.sessionId === "string" && metadata.sessionId.trim()) {
|
|
906
1000
|
task.sessionId = metadata.sessionId.trim();
|
|
907
1001
|
changed = true;
|
|
@@ -915,6 +1009,13 @@ async function updateRunSessionMetadata(task, metadata) {
|
|
|
915
1009
|
}
|
|
916
1010
|
task.updatedAt = formatLocalTimestamp();
|
|
917
1011
|
await persistRunTask(task).catch(() => undefined);
|
|
1012
|
+
if (!previousSessionId && task.sessionId) {
|
|
1013
|
+
await updateRunStartSlotSession({
|
|
1014
|
+
runId: task.id,
|
|
1015
|
+
previousSessionId,
|
|
1016
|
+
sessionId: task.sessionId,
|
|
1017
|
+
}).catch(() => undefined);
|
|
1018
|
+
}
|
|
918
1019
|
}
|
|
919
1020
|
function persistRetainedRun(task) {
|
|
920
1021
|
retainedRuns.set(task.id, cloneRunTask(task));
|
|
@@ -933,11 +1034,8 @@ async function startManagedRun(args) {
|
|
|
933
1034
|
taskId: args.runId,
|
|
934
1035
|
codexAuthBundle: args.codexAuthBundle,
|
|
935
1036
|
});
|
|
936
|
-
const child =
|
|
937
|
-
|
|
938
|
-
command: args.command,
|
|
939
|
-
patch: null,
|
|
940
|
-
shellPath: prepared.shellPath,
|
|
1037
|
+
const child = spawnManagedCodexCommand({
|
|
1038
|
+
codexArgs: args.codexArgs,
|
|
941
1039
|
taskWorkspace: prepared.taskWorkspace,
|
|
942
1040
|
env: prepared.env,
|
|
943
1041
|
agentToken: args.agentToken,
|
|
@@ -1000,6 +1098,7 @@ async function startManagedRun(args) {
|
|
|
1000
1098
|
persistRetainedRun(task);
|
|
1001
1099
|
activeRuns.delete(task.id);
|
|
1002
1100
|
void removeRunTask(task.id).catch(() => undefined);
|
|
1101
|
+
void releaseRunStartSlot({ runId: task.id, sessionId: task.sessionId }).catch(() => undefined);
|
|
1003
1102
|
void prepared.codexAuthCleanup().catch(() => undefined);
|
|
1004
1103
|
writeRunStatus(task.id, `failed error=${message}`);
|
|
1005
1104
|
});
|
|
@@ -1019,6 +1118,7 @@ async function startManagedRun(args) {
|
|
|
1019
1118
|
persistRetainedRun(task);
|
|
1020
1119
|
activeRuns.delete(task.id);
|
|
1021
1120
|
void removeRunTask(task.id).catch(() => undefined);
|
|
1121
|
+
void releaseRunStartSlot({ runId: task.id, sessionId: task.sessionId }).catch(() => undefined);
|
|
1022
1122
|
void prepared.codexAuthCleanup().catch(() => undefined);
|
|
1023
1123
|
writeRunStatus(task.id, `completed status=${task.status} exitCode=${task.resultExitCode ?? "null"} signal=${task.resultSignal ?? "null"}`);
|
|
1024
1124
|
});
|
|
@@ -1038,45 +1138,17 @@ function normalizeCodexModel(value) {
|
|
|
1038
1138
|
const normalized = typeof value === "string" ? value.trim() : "";
|
|
1039
1139
|
return normalized || "gpt-5.4";
|
|
1040
1140
|
}
|
|
1041
|
-
function
|
|
1042
|
-
const
|
|
1043
|
-
const responseSubject = typeof args.request.responseSubject === "string" ? args.request.responseSubject.trim() : "";
|
|
1044
|
-
const requestAgentId = typeof args.request.agentId === "string" ? args.request.agentId.trim() : "";
|
|
1045
|
-
const runId = typeof args.request.runId === "string" ? args.request.runId.trim() : "";
|
|
1046
|
-
const prompt = typeof args.request.prompt === "string" ? args.request.prompt.trim() : "";
|
|
1047
|
-
if (!requestId || !responseSubject || !requestAgentId || requestAgentId !== args.agentId || !runId || !prompt) {
|
|
1048
|
-
throw new Error("invalid codex rpc request");
|
|
1049
|
-
}
|
|
1050
|
-
return {
|
|
1051
|
-
requestId,
|
|
1052
|
-
responseSubject,
|
|
1053
|
-
runId,
|
|
1054
|
-
prompt,
|
|
1055
|
-
sessionId: typeof args.request.sessionId === "string" && args.request.sessionId.trim() ? args.request.sessionId.trim() : null,
|
|
1056
|
-
cwd: typeof args.request.cwd === "string" && args.request.cwd.trim() ? args.request.cwd.trim() : null,
|
|
1057
|
-
model: normalizeCodexModel(args.request.model),
|
|
1058
|
-
runtimeEnvPatch: normalizeEnvPatch(args.request.runtimeEnvPatch),
|
|
1059
|
-
codexAuthBundle: normalizeShellRpcCodexAuthBundle(args.request.codexAuth),
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
function buildManagedCodexCommand(args) {
|
|
1141
|
+
function buildManagedCodexArgs(args) {
|
|
1142
|
+
const promptArgs = ["--", args.prompt];
|
|
1063
1143
|
const fixedArgs = ["--dangerously-bypass-approvals-and-sandbox"];
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
` ${direct}`,
|
|
1073
|
-
"fi",
|
|
1074
|
-
fallback,
|
|
1075
|
-
].join("\n");
|
|
1076
|
-
return `bash -lc ${shellSingleQuote(script)}`;
|
|
1077
|
-
}
|
|
1078
|
-
function publishCodexRpcResponse(args) {
|
|
1079
|
-
args.nc.publish(args.responseSubject, codexRpcCodec.encode(JSON.stringify(args.payload)));
|
|
1144
|
+
return [
|
|
1145
|
+
...fixedArgs,
|
|
1146
|
+
"--model",
|
|
1147
|
+
args.model,
|
|
1148
|
+
...(args.sessionId
|
|
1149
|
+
? ["exec", "resume", "--json", args.sessionId, ...promptArgs]
|
|
1150
|
+
: ["exec", "--json", ...promptArgs]),
|
|
1151
|
+
];
|
|
1080
1152
|
}
|
|
1081
1153
|
function buildLocalCodexCliCommand(args) {
|
|
1082
1154
|
const quotedArgs = args.map(shellSingleQuote).join(" ");
|
|
@@ -1090,6 +1162,34 @@ function buildLocalCodexCliCommand(args) {
|
|
|
1090
1162
|
].join("\n");
|
|
1091
1163
|
return `bash -lc ${shellSingleQuote(script)}`;
|
|
1092
1164
|
}
|
|
1165
|
+
function hasDirectCodexBinary() {
|
|
1166
|
+
const result = spawnSync("bash", ["-lc", "command -v codex >/dev/null 2>&1"], {
|
|
1167
|
+
stdio: "ignore",
|
|
1168
|
+
});
|
|
1169
|
+
return result.status === 0;
|
|
1170
|
+
}
|
|
1171
|
+
function spawnManagedCodexCommand(args) {
|
|
1172
|
+
const env = {
|
|
1173
|
+
...args.env,
|
|
1174
|
+
DOER_AGENT_TOKEN: args.agentToken,
|
|
1175
|
+
};
|
|
1176
|
+
const child = hasDirectCodexBinary()
|
|
1177
|
+
? spawn("codex", args.codexArgs, {
|
|
1178
|
+
cwd: args.taskWorkspace,
|
|
1179
|
+
detached: process.platform !== "win32",
|
|
1180
|
+
env,
|
|
1181
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1182
|
+
})
|
|
1183
|
+
: spawn("npm", ["exec", "--yes", "--package", "doer-agent", "--", "codex", ...args.codexArgs], {
|
|
1184
|
+
cwd: args.taskWorkspace,
|
|
1185
|
+
detached: process.platform !== "win32",
|
|
1186
|
+
env,
|
|
1187
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1188
|
+
});
|
|
1189
|
+
child.stdout?.setEncoding("utf8");
|
|
1190
|
+
child.stderr?.setEncoding("utf8");
|
|
1191
|
+
return child;
|
|
1192
|
+
}
|
|
1093
1193
|
async function runLocalCodexCli(args, timeoutMs) {
|
|
1094
1194
|
const command = buildLocalCodexCliCommand(args);
|
|
1095
1195
|
const workspaceRoot = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
|
|
@@ -1482,70 +1582,6 @@ function subscribeToCodexAuthRpc(args) {
|
|
|
1482
1582
|
});
|
|
1483
1583
|
writeAgentInfo(`codex auth rpc subscribed subject=${subject}`);
|
|
1484
1584
|
}
|
|
1485
|
-
async function handleCodexRpcMessage(args) {
|
|
1486
|
-
let requestId = "unknown";
|
|
1487
|
-
let responseSubject = "";
|
|
1488
|
-
try {
|
|
1489
|
-
const payload = JSON.parse(codexRpcCodec.decode(args.msg.data));
|
|
1490
|
-
const request = normalizeCodexRpcRequest({ request: payload, agentId: args.agentId });
|
|
1491
|
-
requestId = request.requestId;
|
|
1492
|
-
responseSubject = request.responseSubject;
|
|
1493
|
-
const task = await startManagedRun({
|
|
1494
|
-
requestId,
|
|
1495
|
-
runId: request.runId,
|
|
1496
|
-
serverBaseUrl: args.serverBaseUrl,
|
|
1497
|
-
userId: args.userId,
|
|
1498
|
-
agentId: args.agentId,
|
|
1499
|
-
sessionId: request.sessionId,
|
|
1500
|
-
command: buildManagedCodexCommand({
|
|
1501
|
-
prompt: request.prompt,
|
|
1502
|
-
sessionId: request.sessionId,
|
|
1503
|
-
model: request.model,
|
|
1504
|
-
}),
|
|
1505
|
-
cwd: request.cwd,
|
|
1506
|
-
runtimeEnvPatch: request.runtimeEnvPatch,
|
|
1507
|
-
codexAuthBundle: request.codexAuthBundle,
|
|
1508
|
-
agentToken: args.agentToken,
|
|
1509
|
-
});
|
|
1510
|
-
publishCodexRpcResponse({
|
|
1511
|
-
nc: args.jetstream.nc,
|
|
1512
|
-
responseSubject,
|
|
1513
|
-
payload: { requestId, ok: true, task },
|
|
1514
|
-
});
|
|
1515
|
-
}
|
|
1516
|
-
catch (error) {
|
|
1517
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1518
|
-
if (responseSubject) {
|
|
1519
|
-
publishCodexRpcResponse({
|
|
1520
|
-
nc: args.jetstream.nc,
|
|
1521
|
-
responseSubject,
|
|
1522
|
-
payload: { requestId, ok: false, error: message },
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
writeAgentError(`codex rpc failed requestId=${requestId} error=${message}`);
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
function subscribeToCodexRpc(args) {
|
|
1529
|
-
const subject = buildAgentCodexRpcSubject(args.userId, args.agentId);
|
|
1530
|
-
args.jetstream.nc.subscribe(subject, {
|
|
1531
|
-
callback: (error, msg) => {
|
|
1532
|
-
if (error) {
|
|
1533
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1534
|
-
writeAgentError(`codex rpc subscription error: ${message}`);
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
void handleCodexRpcMessage({
|
|
1538
|
-
msg,
|
|
1539
|
-
jetstream: args.jetstream,
|
|
1540
|
-
serverBaseUrl: args.serverBaseUrl,
|
|
1541
|
-
userId: args.userId,
|
|
1542
|
-
agentId: args.agentId,
|
|
1543
|
-
agentToken: args.agentToken,
|
|
1544
|
-
});
|
|
1545
|
-
},
|
|
1546
|
-
});
|
|
1547
|
-
writeAgentInfo(`codex rpc subscribed subject=${subject}`);
|
|
1548
|
-
}
|
|
1549
1585
|
function runLocalCommand(command, args, cwd) {
|
|
1550
1586
|
return new Promise((resolve, reject) => {
|
|
1551
1587
|
const child = spawn(command, args, {
|
|
@@ -1870,20 +1906,32 @@ async function handleRunRpcMessage(args) {
|
|
|
1870
1906
|
requestId = request.requestId;
|
|
1871
1907
|
responseSubject = request.responseSubject;
|
|
1872
1908
|
if (request.action === "start") {
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1909
|
+
const runId = request.runId ?? requestId;
|
|
1910
|
+
await claimRunStartSlot({ runId, sessionId: request.sessionId });
|
|
1911
|
+
try {
|
|
1912
|
+
const task = await startManagedRun({
|
|
1913
|
+
requestId,
|
|
1914
|
+
runId,
|
|
1915
|
+
serverBaseUrl: args.serverBaseUrl,
|
|
1916
|
+
userId: args.userId,
|
|
1917
|
+
agentId: args.agentId,
|
|
1918
|
+
sessionId: request.sessionId,
|
|
1919
|
+
codexArgs: buildManagedCodexArgs({
|
|
1920
|
+
prompt: request.prompt ?? "",
|
|
1921
|
+
sessionId: request.sessionId,
|
|
1922
|
+
model: request.model,
|
|
1923
|
+
}),
|
|
1924
|
+
cwd: request.cwd,
|
|
1925
|
+
runtimeEnvPatch: request.runtimeEnvPatch,
|
|
1926
|
+
codexAuthBundle: request.codexAuthBundle,
|
|
1927
|
+
agentToken: args.agentToken,
|
|
1928
|
+
});
|
|
1929
|
+
publishRunRpcResponse({ nc: args.jetstream.nc, responseSubject, payload: { requestId, ok: true, task } });
|
|
1930
|
+
}
|
|
1931
|
+
catch (error) {
|
|
1932
|
+
await releaseRunStartSlot({ runId, sessionId: request.sessionId }).catch(() => undefined);
|
|
1933
|
+
throw error;
|
|
1934
|
+
}
|
|
1887
1935
|
return;
|
|
1888
1936
|
}
|
|
1889
1937
|
if (request.action === "list") {
|
|
@@ -2458,6 +2506,71 @@ function resolveSessionFilePath(filePath) {
|
|
|
2458
2506
|
function isObjectRecord(value) {
|
|
2459
2507
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2460
2508
|
}
|
|
2509
|
+
const SESSION_RPC_BLOB_KEYS = new Set([
|
|
2510
|
+
"image_url",
|
|
2511
|
+
"image_base64",
|
|
2512
|
+
"content_base64",
|
|
2513
|
+
"file_data",
|
|
2514
|
+
"bytes",
|
|
2515
|
+
"data",
|
|
2516
|
+
]);
|
|
2517
|
+
function isInlineBlobString(value) {
|
|
2518
|
+
const trimmed = value.trim();
|
|
2519
|
+
if (!trimmed) {
|
|
2520
|
+
return false;
|
|
2521
|
+
}
|
|
2522
|
+
return trimmed.startsWith("data:") || trimmed.includes(";base64,");
|
|
2523
|
+
}
|
|
2524
|
+
function buildInlineBlobMarker(value) {
|
|
2525
|
+
const trimmed = value.trim();
|
|
2526
|
+
if (trimmed.startsWith("data:")) {
|
|
2527
|
+
const mimeEnd = trimmed.indexOf(";");
|
|
2528
|
+
const mimeType = mimeEnd > 5 ? trimmed.slice(5, mimeEnd) : "";
|
|
2529
|
+
if (mimeType) {
|
|
2530
|
+
return `[inline blob omitted: ${mimeType}]`;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
return "[inline blob omitted]";
|
|
2534
|
+
}
|
|
2535
|
+
function sanitizeSessionRpcPayload(value) {
|
|
2536
|
+
if (typeof value === "string") {
|
|
2537
|
+
return value;
|
|
2538
|
+
}
|
|
2539
|
+
if (Array.isArray(value)) {
|
|
2540
|
+
return value.map((entry) => sanitizeSessionRpcPayload(entry));
|
|
2541
|
+
}
|
|
2542
|
+
if (!isObjectRecord(value)) {
|
|
2543
|
+
return value;
|
|
2544
|
+
}
|
|
2545
|
+
const sanitized = {};
|
|
2546
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2547
|
+
if (SESSION_RPC_BLOB_KEYS.has(key) && typeof entry === "string" && isInlineBlobString(entry)) {
|
|
2548
|
+
sanitized[key] = buildInlineBlobMarker(entry);
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
sanitized[key] = sanitizeSessionRpcPayload(entry);
|
|
2552
|
+
}
|
|
2553
|
+
return sanitized;
|
|
2554
|
+
}
|
|
2555
|
+
function sanitizeSessionRpcRawLine(line) {
|
|
2556
|
+
const trimmed = line.trim();
|
|
2557
|
+
if (!trimmed.startsWith("{")) {
|
|
2558
|
+
return line;
|
|
2559
|
+
}
|
|
2560
|
+
try {
|
|
2561
|
+
const parsed = JSON.parse(line);
|
|
2562
|
+
if (!isObjectRecord(parsed) || !isObjectRecord(parsed.payload) || parsed.type !== "response_item") {
|
|
2563
|
+
return line;
|
|
2564
|
+
}
|
|
2565
|
+
return JSON.stringify({
|
|
2566
|
+
...parsed,
|
|
2567
|
+
payload: sanitizeSessionRpcPayload(parsed.payload),
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
catch {
|
|
2571
|
+
return line;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2461
2574
|
function toTrimmedStringOrNull(value) {
|
|
2462
2575
|
if (typeof value !== "string") {
|
|
2463
2576
|
return null;
|
|
@@ -2734,7 +2847,9 @@ async function getAgentSessionRawRows(args) {
|
|
|
2734
2847
|
const sinceLine = Math.max(0, Math.floor(args.sinceLine));
|
|
2735
2848
|
const beforeRowId = args.beforeRowId && args.beforeRowId > 0 ? Math.floor(args.beforeRowId) : null;
|
|
2736
2849
|
const maxRawRows = 200;
|
|
2737
|
-
const
|
|
2850
|
+
const maxSelectionBytes = 120_000;
|
|
2851
|
+
const maxLineSelectionBytes = 4_096;
|
|
2852
|
+
const maxReadBytes = 2_000_000;
|
|
2738
2853
|
if (totalLines === 0) {
|
|
2739
2854
|
return {
|
|
2740
2855
|
rawRows: [],
|
|
@@ -2752,46 +2867,64 @@ async function getAgentSessionRawRows(args) {
|
|
|
2752
2867
|
endLineIndex = Math.max(0, Math.min(totalLines, beforeRowId - 1));
|
|
2753
2868
|
startLineIndex = endLineIndex;
|
|
2754
2869
|
let collectedRows = 0;
|
|
2755
|
-
let
|
|
2870
|
+
let collectedSelectionBytes = 0;
|
|
2871
|
+
let collectedReadBytes = 0;
|
|
2756
2872
|
while (startLineIndex > 0 && collectedRows < maxRawRows) {
|
|
2757
2873
|
const nextIndex = startLineIndex - 1;
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2874
|
+
const nextReadBytes = getLineSpanBytes(nextIndex);
|
|
2875
|
+
const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
|
|
2876
|
+
if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
|
|
2877
|
+
break;
|
|
2878
|
+
}
|
|
2879
|
+
if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
|
|
2760
2880
|
break;
|
|
2761
2881
|
}
|
|
2762
2882
|
startLineIndex = nextIndex;
|
|
2763
2883
|
collectedRows += 1;
|
|
2764
|
-
|
|
2884
|
+
collectedSelectionBytes += nextSelectionBytes;
|
|
2885
|
+
collectedReadBytes += nextReadBytes;
|
|
2765
2886
|
}
|
|
2766
2887
|
}
|
|
2767
2888
|
else if (sinceLine > 0) {
|
|
2768
2889
|
startLineIndex = Math.min(totalLines, sinceLine);
|
|
2769
2890
|
endLineIndex = startLineIndex;
|
|
2770
2891
|
let collectedRows = 0;
|
|
2771
|
-
let
|
|
2892
|
+
let collectedSelectionBytes = 0;
|
|
2893
|
+
let collectedReadBytes = 0;
|
|
2772
2894
|
while (endLineIndex < totalLines && collectedRows < maxRawRows) {
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2895
|
+
const nextReadBytes = getLineSpanBytes(endLineIndex);
|
|
2896
|
+
const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
|
|
2897
|
+
if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
|
|
2898
|
+
break;
|
|
2899
|
+
}
|
|
2900
|
+
if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
|
|
2775
2901
|
break;
|
|
2776
2902
|
}
|
|
2777
2903
|
endLineIndex += 1;
|
|
2778
2904
|
collectedRows += 1;
|
|
2779
|
-
|
|
2905
|
+
collectedSelectionBytes += nextSelectionBytes;
|
|
2906
|
+
collectedReadBytes += nextReadBytes;
|
|
2780
2907
|
}
|
|
2781
2908
|
}
|
|
2782
2909
|
else {
|
|
2783
2910
|
startLineIndex = totalLines;
|
|
2784
2911
|
let collectedRows = 0;
|
|
2785
|
-
let
|
|
2912
|
+
let collectedSelectionBytes = 0;
|
|
2913
|
+
let collectedReadBytes = 0;
|
|
2786
2914
|
while (startLineIndex > 0 && collectedRows < maxRawRows) {
|
|
2787
2915
|
const nextIndex = startLineIndex - 1;
|
|
2788
|
-
const
|
|
2789
|
-
|
|
2916
|
+
const nextReadBytes = getLineSpanBytes(nextIndex);
|
|
2917
|
+
const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
|
|
2918
|
+
if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
|
|
2919
|
+
break;
|
|
2920
|
+
}
|
|
2921
|
+
if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
|
|
2790
2922
|
break;
|
|
2791
2923
|
}
|
|
2792
2924
|
startLineIndex = nextIndex;
|
|
2793
2925
|
collectedRows += 1;
|
|
2794
|
-
|
|
2926
|
+
collectedSelectionBytes += nextSelectionBytes;
|
|
2927
|
+
collectedReadBytes += nextReadBytes;
|
|
2795
2928
|
}
|
|
2796
2929
|
}
|
|
2797
2930
|
if (startLineIndex >= endLineIndex) {
|
|
@@ -2824,7 +2957,7 @@ async function getAgentSessionRawRows(args) {
|
|
|
2824
2957
|
if (line.trim()) {
|
|
2825
2958
|
rawRows.push({
|
|
2826
2959
|
id: lineNumber,
|
|
2827
|
-
raw: line,
|
|
2960
|
+
raw: sanitizeSessionRpcRawLine(line),
|
|
2828
2961
|
});
|
|
2829
2962
|
}
|
|
2830
2963
|
lineNumber += 1;
|
|
@@ -3805,13 +3938,6 @@ async function main() {
|
|
|
3805
3938
|
userId,
|
|
3806
3939
|
agentId: initialAgentId,
|
|
3807
3940
|
});
|
|
3808
|
-
subscribeToCodexRpc({
|
|
3809
|
-
jetstream,
|
|
3810
|
-
serverBaseUrl,
|
|
3811
|
-
userId,
|
|
3812
|
-
agentId: initialAgentId,
|
|
3813
|
-
agentToken,
|
|
3814
|
-
});
|
|
3815
3941
|
subscribeToCodexAuthRpc({
|
|
3816
3942
|
jetstream,
|
|
3817
3943
|
userId,
|