doer-agent 0.2.5 → 0.2.7

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/agent.js +383 -388
  2. 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";
@@ -9,17 +9,13 @@ const AGENT_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
9
9
  const AGENT_PROJECT_DIR = path.join(AGENT_MODULE_DIR, "..");
10
10
  const AGENT_PACKAGE_JSON_PATH = path.join(AGENT_PROJECT_DIR, "package.json");
11
11
  let activeTaskLogContext = null;
12
- const activeTaskCancelRequests = new Map();
13
12
  let workspaceRootOverride = null;
14
13
  const fsRpcCodec = StringCodec();
15
- const shellRpcCodec = StringCodec();
16
14
  const runRpcCodec = StringCodec();
17
15
  const sessionRpcCodec = StringCodec();
18
- const codexRpcCodec = StringCodec();
19
16
  const codexAuthRpcCodec = StringCodec();
20
17
  const settingsRpcCodec = StringCodec();
21
18
  const gitRpcCodec = StringCodec();
22
- const activeRuns = new Map();
23
19
  const retainedRuns = new Map();
24
20
  const activeSessionWatchers = new Map();
25
21
  const sessionLineIndexCache = new Map();
@@ -35,9 +31,6 @@ function buildAgentRunRpcSubject(userId, agentId) {
35
31
  function buildAgentSessionRpcSubject(userId, agentId) {
36
32
  return `doer.agent.session.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
37
33
  }
38
- function buildAgentCodexRpcSubject(userId, agentId) {
39
- return `doer.agent.codex.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
40
- }
41
34
  function buildAgentCodexAuthRpcSubject(userId, agentId) {
42
35
  return `doer.agent.codex.auth.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
43
36
  }
@@ -421,9 +414,11 @@ function normalizeRunRpcRequest(args) {
421
414
  throw new Error("missing responseSubject");
422
415
  }
423
416
  const runId = typeof args.request.runId === "string" && args.request.runId.trim() ? args.request.runId.trim() : null;
424
- const command = typeof args.request.command === "string" && args.request.command.trim() ? args.request.command.trim() : null;
425
- if (action === "start" && !command) {
426
- throw new Error("missing command");
417
+ const prompt = typeof args.request.prompt === "string" && args.request.prompt.trim() ? args.request.prompt.trim() : null;
418
+ const sessionId = typeof args.request.sessionId === "string" && args.request.sessionId.trim() ? args.request.sessionId.trim() : null;
419
+ const model = normalizeCodexModel(args.request.model);
420
+ if (action === "start" && !prompt) {
421
+ throw new Error("missing prompt");
427
422
  }
428
423
  if ((action === "get" || action === "cancel") && !runId) {
429
424
  throw new Error("missing runId");
@@ -437,7 +432,9 @@ function normalizeRunRpcRequest(args) {
437
432
  requestId,
438
433
  action,
439
434
  runId,
440
- command,
435
+ prompt,
436
+ sessionId,
437
+ model,
441
438
  cwd,
442
439
  responseSubject,
443
440
  sinceSeq,
@@ -466,10 +463,13 @@ async function persistRunTask(task) {
466
463
  runId: task.id,
467
464
  agentId: task.agentId,
468
465
  userId: task.userId,
466
+ processPid: task.processPid,
469
467
  sessionId: task.sessionId,
470
468
  sessionFilePath: task.sessionFilePath,
471
469
  status: task.status,
472
470
  cancelRequested: task.cancelRequested,
471
+ resultExitCode: task.resultExitCode,
472
+ resultSignal: task.resultSignal,
473
473
  createdAt: task.createdAt,
474
474
  updatedAt: task.updatedAt,
475
475
  startedAt: task.startedAt,
@@ -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,14 +1009,82 @@ 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));
921
1022
  }
922
- function getStoredRun(runId) {
923
- const active = activeRuns.get(runId);
924
- if (active) {
925
- return active.task;
1023
+ function normalizePersistedRunTask(value) {
1024
+ if (!value || typeof value !== "object") {
1025
+ return null;
1026
+ }
1027
+ const record = value;
1028
+ const id = typeof record.runId === "string" && record.runId.trim()
1029
+ ? record.runId.trim()
1030
+ : typeof record.id === "string" && record.id.trim()
1031
+ ? record.id.trim()
1032
+ : "";
1033
+ const userId = typeof record.userId === "string" ? record.userId : "";
1034
+ const agentId = typeof record.agentId === "string" ? record.agentId : "";
1035
+ const status = record.status;
1036
+ if (!id || !userId || !agentId || !["queued", "running", "completed", "failed", "canceled"].includes(String(status))) {
1037
+ return null;
1038
+ }
1039
+ return {
1040
+ id,
1041
+ userId,
1042
+ agentId,
1043
+ processPid: typeof record.processPid === "number" ? record.processPid : null,
1044
+ sessionId: typeof record.sessionId === "string" && record.sessionId.trim() ? record.sessionId.trim() : null,
1045
+ sessionFilePath: typeof record.sessionFilePath === "string" && record.sessionFilePath.trim() ? record.sessionFilePath.trim() : null,
1046
+ status: status,
1047
+ cancelRequested: Boolean(record.cancelRequested),
1048
+ resultExitCode: typeof record.resultExitCode === "number" ? record.resultExitCode : null,
1049
+ resultSignal: typeof record.resultSignal === "string" && record.resultSignal.trim() ? record.resultSignal.trim() : null,
1050
+ error: typeof record.error === "string" && record.error.trim() ? record.error : null,
1051
+ createdAt: typeof record.createdAt === "string" ? record.createdAt : "",
1052
+ updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : "",
1053
+ startedAt: typeof record.startedAt === "string" && record.startedAt.trim() ? record.startedAt : null,
1054
+ finishedAt: typeof record.finishedAt === "string" && record.finishedAt.trim() ? record.finishedAt : null,
1055
+ };
1056
+ }
1057
+ async function listPersistedRunTasks() {
1058
+ const dir = await resolveRunsDir();
1059
+ const names = await readdir(dir).catch(() => []);
1060
+ const tasks = await Promise.all(names
1061
+ .filter((name) => name.endsWith(".json"))
1062
+ .map(async (name) => {
1063
+ const raw = await readFile(path.join(dir, name), "utf8").catch(() => null);
1064
+ if (!raw) {
1065
+ return null;
1066
+ }
1067
+ try {
1068
+ return normalizePersistedRunTask(JSON.parse(raw));
1069
+ }
1070
+ catch {
1071
+ return null;
1072
+ }
1073
+ }));
1074
+ return tasks.filter((task) => task !== null);
1075
+ }
1076
+ async function getStoredRun(runId) {
1077
+ const persisted = await readFile(path.join(await resolveRunsDir(), `${runId}.json`), "utf8").catch(() => null);
1078
+ if (persisted) {
1079
+ try {
1080
+ const parsed = normalizePersistedRunTask(JSON.parse(persisted));
1081
+ if (parsed) {
1082
+ return parsed;
1083
+ }
1084
+ }
1085
+ catch {
1086
+ // Ignore malformed persisted state and fall back to retained memory.
1087
+ }
926
1088
  }
927
1089
  return retainedRuns.get(runId) ?? null;
928
1090
  }
@@ -933,11 +1095,8 @@ async function startManagedRun(args) {
933
1095
  taskId: args.runId,
934
1096
  codexAuthBundle: args.codexAuthBundle,
935
1097
  });
936
- const child = spawnPreparedCommand({
937
- kind: "shell",
938
- command: args.command,
939
- patch: null,
940
- shellPath: prepared.shellPath,
1098
+ const child = spawnManagedCodexCommand({
1099
+ codexArgs: args.codexArgs,
941
1100
  taskWorkspace: prepared.taskWorkspace,
942
1101
  env: prepared.env,
943
1102
  agentToken: args.agentToken,
@@ -947,6 +1106,7 @@ async function startManagedRun(args) {
947
1106
  id: args.runId,
948
1107
  userId: args.userId,
949
1108
  agentId: args.agentId,
1109
+ processPid: typeof child.pid === "number" ? child.pid : null,
950
1110
  sessionId: typeof args.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : null,
951
1111
  sessionFilePath: null,
952
1112
  status: "running",
@@ -959,17 +1119,6 @@ async function startManagedRun(args) {
959
1119
  startedAt: now,
960
1120
  finishedAt: null,
961
1121
  };
962
- const cancellation = createManagedCancellation(child);
963
- const requestCancel = () => {
964
- if (task.status === "completed" || task.status === "failed" || task.status === "canceled") {
965
- return;
966
- }
967
- task.cancelRequested = true;
968
- task.updatedAt = formatLocalTimestamp();
969
- void persistRunTask(task).catch(() => undefined);
970
- writeRunStatus(task.id, "cancel requested");
971
- cancellation.requestCancel();
972
- };
973
1122
  let stdoutBuffer = "";
974
1123
  const recordChunk = (stream, chunk) => {
975
1124
  writeRunStream(task.id, stream, chunk);
@@ -998,31 +1147,33 @@ async function startManagedRun(args) {
998
1147
  task.error = message;
999
1148
  task.finishedAt = formatLocalTimestamp();
1000
1149
  persistRetainedRun(task);
1001
- activeRuns.delete(task.id);
1002
1150
  void removeRunTask(task.id).catch(() => undefined);
1151
+ void releaseRunStartSlot({ runId: task.id, sessionId: task.sessionId }).catch(() => undefined);
1003
1152
  void prepared.codexAuthCleanup().catch(() => undefined);
1004
1153
  writeRunStatus(task.id, `failed error=${message}`);
1005
1154
  });
1006
- child.once("close", (code, signal) => {
1007
- cancellation.clear();
1155
+ child.once("close", async (code, signal) => {
1008
1156
  if (stdoutBuffer.trim() && (!task.sessionId || !task.sessionFilePath)) {
1009
1157
  const metadata = extractCodexSessionMetadata(stdoutBuffer.trim());
1010
1158
  if (metadata.sessionId || metadata.sessionFilePath) {
1011
1159
  void updateRunSessionMetadata(task, metadata);
1012
1160
  }
1013
1161
  }
1162
+ const latest = await getStoredRun(task.id).catch(() => null);
1163
+ if (latest?.cancelRequested) {
1164
+ task.cancelRequested = true;
1165
+ }
1014
1166
  task.resultExitCode = typeof code === "number" ? code : null;
1015
1167
  task.resultSignal = signal;
1016
1168
  task.finishedAt = formatLocalTimestamp();
1017
1169
  task.status = task.cancelRequested ? "canceled" : (task.resultExitCode ?? 1) === 0 ? "completed" : "failed";
1018
1170
  task.error = task.status === "failed" ? `Command exited with code ${task.resultExitCode ?? "null"}` : null;
1019
1171
  persistRetainedRun(task);
1020
- activeRuns.delete(task.id);
1021
1172
  void removeRunTask(task.id).catch(() => undefined);
1173
+ void releaseRunStartSlot({ runId: task.id, sessionId: task.sessionId }).catch(() => undefined);
1022
1174
  void prepared.codexAuthCleanup().catch(() => undefined);
1023
1175
  writeRunStatus(task.id, `completed status=${task.status} exitCode=${task.resultExitCode ?? "null"} signal=${task.resultSignal ?? "null"}`);
1024
1176
  });
1025
- activeRuns.set(task.id, { task, child, requestCancel });
1026
1177
  persistRetainedRun(task);
1027
1178
  void persistRunTask(task).catch(() => undefined);
1028
1179
  writeRunStatus(task.id, `started requestId=${args.requestId} cwd=${prepared.taskWorkspace}`);
@@ -1038,45 +1189,17 @@ function normalizeCodexModel(value) {
1038
1189
  const normalized = typeof value === "string" ? value.trim() : "";
1039
1190
  return normalized || "gpt-5.4";
1040
1191
  }
1041
- function normalizeCodexRpcRequest(args) {
1042
- const requestId = typeof args.request.requestId === "string" ? args.request.requestId.trim() : "";
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) {
1192
+ function buildManagedCodexArgs(args) {
1193
+ const promptArgs = ["--", args.prompt];
1063
1194
  const fixedArgs = ["--dangerously-bypass-approvals-and-sandbox"];
1064
- const codexArgs = args.sessionId
1065
- ? ["exec", "resume", "--json", args.sessionId, args.prompt]
1066
- : ["exec", "--json", args.prompt];
1067
- const commandArgs = [...fixedArgs, "--model", args.model, ...codexArgs].map(shellSingleQuote).join(" ");
1068
- const direct = `exec codex ${commandArgs}`;
1069
- const fallback = `exec npm exec --yes --package doer-agent -- codex ${commandArgs}`;
1070
- const script = [
1071
- "if command -v codex >/dev/null 2>&1; then",
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)));
1195
+ return [
1196
+ ...fixedArgs,
1197
+ "--model",
1198
+ args.model,
1199
+ ...(args.sessionId
1200
+ ? ["exec", "resume", "--json", args.sessionId, ...promptArgs]
1201
+ : ["exec", "--json", ...promptArgs]),
1202
+ ];
1080
1203
  }
1081
1204
  function buildLocalCodexCliCommand(args) {
1082
1205
  const quotedArgs = args.map(shellSingleQuote).join(" ");
@@ -1090,6 +1213,34 @@ function buildLocalCodexCliCommand(args) {
1090
1213
  ].join("\n");
1091
1214
  return `bash -lc ${shellSingleQuote(script)}`;
1092
1215
  }
1216
+ function hasDirectCodexBinary() {
1217
+ const result = spawnSync("bash", ["-lc", "command -v codex >/dev/null 2>&1"], {
1218
+ stdio: "ignore",
1219
+ });
1220
+ return result.status === 0;
1221
+ }
1222
+ function spawnManagedCodexCommand(args) {
1223
+ const env = {
1224
+ ...args.env,
1225
+ DOER_AGENT_TOKEN: args.agentToken,
1226
+ };
1227
+ const child = hasDirectCodexBinary()
1228
+ ? spawn("codex", args.codexArgs, {
1229
+ cwd: args.taskWorkspace,
1230
+ detached: process.platform !== "win32",
1231
+ env,
1232
+ stdio: ["ignore", "pipe", "pipe"],
1233
+ })
1234
+ : spawn("npm", ["exec", "--yes", "--package", "doer-agent", "--", "codex", ...args.codexArgs], {
1235
+ cwd: args.taskWorkspace,
1236
+ detached: process.platform !== "win32",
1237
+ env,
1238
+ stdio: ["ignore", "pipe", "pipe"],
1239
+ });
1240
+ child.stdout?.setEncoding("utf8");
1241
+ child.stderr?.setEncoding("utf8");
1242
+ return child;
1243
+ }
1093
1244
  async function runLocalCodexCli(args, timeoutMs) {
1094
1245
  const command = buildLocalCodexCliCommand(args);
1095
1246
  const workspaceRoot = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
@@ -1482,70 +1633,6 @@ function subscribeToCodexAuthRpc(args) {
1482
1633
  });
1483
1634
  writeAgentInfo(`codex auth rpc subscribed subject=${subject}`);
1484
1635
  }
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
1636
  function runLocalCommand(command, args, cwd) {
1550
1637
  return new Promise((resolve, reject) => {
1551
1638
  const child = spawn(command, args, {
@@ -1870,39 +1957,66 @@ async function handleRunRpcMessage(args) {
1870
1957
  requestId = request.requestId;
1871
1958
  responseSubject = request.responseSubject;
1872
1959
  if (request.action === "start") {
1873
- const task = await startManagedRun({
1874
- requestId,
1875
- runId: request.runId ?? requestId,
1876
- serverBaseUrl: args.serverBaseUrl,
1877
- userId: args.userId,
1878
- agentId: args.agentId,
1879
- sessionId: null,
1880
- command: request.command ?? "",
1881
- cwd: request.cwd,
1882
- runtimeEnvPatch: request.runtimeEnvPatch,
1883
- codexAuthBundle: request.codexAuthBundle,
1884
- agentToken: args.agentToken,
1885
- });
1886
- publishRunRpcResponse({ nc: args.jetstream.nc, responseSubject, payload: { requestId, ok: true, task } });
1960
+ const runId = request.runId ?? requestId;
1961
+ await claimRunStartSlot({ runId, sessionId: request.sessionId });
1962
+ try {
1963
+ const task = await startManagedRun({
1964
+ requestId,
1965
+ runId,
1966
+ serverBaseUrl: args.serverBaseUrl,
1967
+ userId: args.userId,
1968
+ agentId: args.agentId,
1969
+ sessionId: request.sessionId,
1970
+ codexArgs: buildManagedCodexArgs({
1971
+ prompt: request.prompt ?? "",
1972
+ sessionId: request.sessionId,
1973
+ model: request.model,
1974
+ }),
1975
+ cwd: request.cwd,
1976
+ runtimeEnvPatch: request.runtimeEnvPatch,
1977
+ codexAuthBundle: request.codexAuthBundle,
1978
+ agentToken: args.agentToken,
1979
+ });
1980
+ publishRunRpcResponse({ nc: args.jetstream.nc, responseSubject, payload: { requestId, ok: true, task } });
1981
+ }
1982
+ catch (error) {
1983
+ await releaseRunStartSlot({ runId, sessionId: request.sessionId }).catch(() => undefined);
1984
+ throw error;
1985
+ }
1887
1986
  return;
1888
1987
  }
1889
1988
  if (request.action === "list") {
1890
- const tasks = [...activeRuns.values()].map((entry) => cloneRunTask(entry.task));
1891
- const retained = [...retainedRuns.values()].filter((task) => !activeRuns.has(task.id)).map((task) => cloneRunTask(task));
1892
- const merged = [...tasks, ...retained]
1989
+ const persisted = await listPersistedRunTasks();
1990
+ const mergedById = new Map();
1991
+ for (const task of persisted) {
1992
+ mergedById.set(task.id, cloneRunTask(task));
1993
+ }
1994
+ for (const task of retainedRuns.values()) {
1995
+ if (!mergedById.has(task.id)) {
1996
+ mergedById.set(task.id, cloneRunTask(task));
1997
+ }
1998
+ }
1999
+ const merged = [...mergedById.values()]
1893
2000
  .sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))
1894
2001
  .slice(0, request.limit);
1895
2002
  publishRunRpcResponse({ nc: args.jetstream.nc, responseSubject, payload: { requestId, ok: true, tasks: merged } });
1896
2003
  return;
1897
2004
  }
1898
- const stored = request.runId ? getStoredRun(request.runId) : null;
2005
+ const stored = request.runId ? await getStoredRun(request.runId) : null;
1899
2006
  if (!stored || stored.agentId !== args.agentId || stored.userId !== args.userId) {
1900
2007
  throw new Error("Run not found");
1901
2008
  }
1902
2009
  if (request.action === "cancel") {
1903
- const active = activeRuns.get(stored.id);
1904
- active?.requestCancel();
1905
- const task = cloneRunTask(active?.task ?? stored);
2010
+ const target = stored;
2011
+ if (target.processPid === null) {
2012
+ throw new Error("Run pid not found");
2013
+ }
2014
+ target.cancelRequested = true;
2015
+ target.updatedAt = formatLocalTimestamp();
2016
+ await persistRunTask(target);
2017
+ writeRunStatus(target.id, `cancel requested pid=${target.processPid}`);
2018
+ sendSignalToPid(target.processPid, "SIGINT");
2019
+ const task = cloneRunTask(target);
1906
2020
  publishRunRpcResponse({ nc: args.jetstream.nc, responseSubject, payload: { requestId, ok: true, task } });
1907
2021
  return;
1908
2022
  }
@@ -1978,21 +2092,17 @@ function sendSignalToTaskProcess(child, signal) {
1978
2092
  // noop
1979
2093
  }
1980
2094
  }
1981
- function requestTaskCancellation(taskId, reason) {
1982
- const requestCancel = activeTaskCancelRequests.get(taskId);
1983
- if (!requestCancel) {
1984
- return false;
1985
- }
1986
- try {
1987
- requestCancel();
1988
- writeAgentInfo(`task cancel requested taskId=${taskId} via=${reason}`);
1989
- return true;
1990
- }
1991
- catch (error) {
1992
- const message = error instanceof Error ? error.message : String(error);
1993
- writeAgentError(`task cancel request failed taskId=${taskId} via=${reason}: ${message}`);
1994
- return false;
2095
+ function sendSignalToPid(pid, signal) {
2096
+ if (process.platform !== "win32") {
2097
+ try {
2098
+ process.kill(-pid, signal);
2099
+ return;
2100
+ }
2101
+ catch {
2102
+ // Fall back to direct pid signaling.
2103
+ }
1995
2104
  }
2105
+ process.kill(pid, signal);
1996
2106
  }
1997
2107
  function resolveLogTimeZone() {
1998
2108
  const configured = process.env.DOER_AGENT_LOG_TIMEZONE?.trim() || process.env.TZ?.trim();
@@ -2124,9 +2234,6 @@ function resolveTaskWorkspace(rawCwd) {
2124
2234
  function buildAgentFsRpcSubject(userId, agentId) {
2125
2235
  return `doer.agent.fs.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
2126
2236
  }
2127
- function buildAgentShellRpcSubject(userId, agentId) {
2128
- return `doer.agent.shell.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
2129
- }
2130
2237
  function normalizeFsRpcPath(rawPath) {
2131
2238
  const root = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
2132
2239
  const raw = typeof rawPath === "string" && rawPath.trim() ? rawPath.trim() : ".";
@@ -2458,6 +2565,71 @@ function resolveSessionFilePath(filePath) {
2458
2565
  function isObjectRecord(value) {
2459
2566
  return !!value && typeof value === "object" && !Array.isArray(value);
2460
2567
  }
2568
+ const SESSION_RPC_BLOB_KEYS = new Set([
2569
+ "image_url",
2570
+ "image_base64",
2571
+ "content_base64",
2572
+ "file_data",
2573
+ "bytes",
2574
+ "data",
2575
+ ]);
2576
+ function isInlineBlobString(value) {
2577
+ const trimmed = value.trim();
2578
+ if (!trimmed) {
2579
+ return false;
2580
+ }
2581
+ return trimmed.startsWith("data:") || trimmed.includes(";base64,");
2582
+ }
2583
+ function buildInlineBlobMarker(value) {
2584
+ const trimmed = value.trim();
2585
+ if (trimmed.startsWith("data:")) {
2586
+ const mimeEnd = trimmed.indexOf(";");
2587
+ const mimeType = mimeEnd > 5 ? trimmed.slice(5, mimeEnd) : "";
2588
+ if (mimeType) {
2589
+ return `[inline blob omitted: ${mimeType}]`;
2590
+ }
2591
+ }
2592
+ return "[inline blob omitted]";
2593
+ }
2594
+ function sanitizeSessionRpcPayload(value) {
2595
+ if (typeof value === "string") {
2596
+ return value;
2597
+ }
2598
+ if (Array.isArray(value)) {
2599
+ return value.map((entry) => sanitizeSessionRpcPayload(entry));
2600
+ }
2601
+ if (!isObjectRecord(value)) {
2602
+ return value;
2603
+ }
2604
+ const sanitized = {};
2605
+ for (const [key, entry] of Object.entries(value)) {
2606
+ if (SESSION_RPC_BLOB_KEYS.has(key) && typeof entry === "string" && isInlineBlobString(entry)) {
2607
+ sanitized[key] = buildInlineBlobMarker(entry);
2608
+ continue;
2609
+ }
2610
+ sanitized[key] = sanitizeSessionRpcPayload(entry);
2611
+ }
2612
+ return sanitized;
2613
+ }
2614
+ function sanitizeSessionRpcRawLine(line) {
2615
+ const trimmed = line.trim();
2616
+ if (!trimmed.startsWith("{")) {
2617
+ return line;
2618
+ }
2619
+ try {
2620
+ const parsed = JSON.parse(line);
2621
+ if (!isObjectRecord(parsed) || !isObjectRecord(parsed.payload) || parsed.type !== "response_item") {
2622
+ return line;
2623
+ }
2624
+ return JSON.stringify({
2625
+ ...parsed,
2626
+ payload: sanitizeSessionRpcPayload(parsed.payload),
2627
+ });
2628
+ }
2629
+ catch {
2630
+ return line;
2631
+ }
2632
+ }
2461
2633
  function toTrimmedStringOrNull(value) {
2462
2634
  if (typeof value !== "string") {
2463
2635
  return null;
@@ -2734,7 +2906,9 @@ async function getAgentSessionRawRows(args) {
2734
2906
  const sinceLine = Math.max(0, Math.floor(args.sinceLine));
2735
2907
  const beforeRowId = args.beforeRowId && args.beforeRowId > 0 ? Math.floor(args.beforeRowId) : null;
2736
2908
  const maxRawRows = 200;
2737
- const maxRawBytes = 120_000;
2909
+ const maxSelectionBytes = 120_000;
2910
+ const maxLineSelectionBytes = 4_096;
2911
+ const maxReadBytes = 2_000_000;
2738
2912
  if (totalLines === 0) {
2739
2913
  return {
2740
2914
  rawRows: [],
@@ -2752,46 +2926,64 @@ async function getAgentSessionRawRows(args) {
2752
2926
  endLineIndex = Math.max(0, Math.min(totalLines, beforeRowId - 1));
2753
2927
  startLineIndex = endLineIndex;
2754
2928
  let collectedRows = 0;
2755
- let collectedBytes = 0;
2929
+ let collectedSelectionBytes = 0;
2930
+ let collectedReadBytes = 0;
2756
2931
  while (startLineIndex > 0 && collectedRows < maxRawRows) {
2757
2932
  const nextIndex = startLineIndex - 1;
2758
- const nextBytes = getLineSpanBytes(nextIndex);
2759
- if (collectedRows > 0 && collectedBytes + nextBytes > maxRawBytes) {
2933
+ const nextReadBytes = getLineSpanBytes(nextIndex);
2934
+ const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
2935
+ if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
2936
+ break;
2937
+ }
2938
+ if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
2760
2939
  break;
2761
2940
  }
2762
2941
  startLineIndex = nextIndex;
2763
2942
  collectedRows += 1;
2764
- collectedBytes += nextBytes;
2943
+ collectedSelectionBytes += nextSelectionBytes;
2944
+ collectedReadBytes += nextReadBytes;
2765
2945
  }
2766
2946
  }
2767
2947
  else if (sinceLine > 0) {
2768
2948
  startLineIndex = Math.min(totalLines, sinceLine);
2769
2949
  endLineIndex = startLineIndex;
2770
2950
  let collectedRows = 0;
2771
- let collectedBytes = 0;
2951
+ let collectedSelectionBytes = 0;
2952
+ let collectedReadBytes = 0;
2772
2953
  while (endLineIndex < totalLines && collectedRows < maxRawRows) {
2773
- const nextBytes = getLineSpanBytes(endLineIndex);
2774
- if (collectedRows > 0 && collectedBytes + nextBytes > maxRawBytes) {
2954
+ const nextReadBytes = getLineSpanBytes(endLineIndex);
2955
+ const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
2956
+ if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
2957
+ break;
2958
+ }
2959
+ if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
2775
2960
  break;
2776
2961
  }
2777
2962
  endLineIndex += 1;
2778
2963
  collectedRows += 1;
2779
- collectedBytes += nextBytes;
2964
+ collectedSelectionBytes += nextSelectionBytes;
2965
+ collectedReadBytes += nextReadBytes;
2780
2966
  }
2781
2967
  }
2782
2968
  else {
2783
2969
  startLineIndex = totalLines;
2784
2970
  let collectedRows = 0;
2785
- let collectedBytes = 0;
2971
+ let collectedSelectionBytes = 0;
2972
+ let collectedReadBytes = 0;
2786
2973
  while (startLineIndex > 0 && collectedRows < maxRawRows) {
2787
2974
  const nextIndex = startLineIndex - 1;
2788
- const nextBytes = getLineSpanBytes(nextIndex);
2789
- if (collectedRows > 0 && collectedBytes + nextBytes > maxRawBytes) {
2975
+ const nextReadBytes = getLineSpanBytes(nextIndex);
2976
+ const nextSelectionBytes = Math.min(nextReadBytes, maxLineSelectionBytes);
2977
+ if (collectedRows > 0 && collectedSelectionBytes + nextSelectionBytes > maxSelectionBytes) {
2978
+ break;
2979
+ }
2980
+ if (collectedRows > 0 && collectedReadBytes + nextReadBytes > maxReadBytes) {
2790
2981
  break;
2791
2982
  }
2792
2983
  startLineIndex = nextIndex;
2793
2984
  collectedRows += 1;
2794
- collectedBytes += nextBytes;
2985
+ collectedSelectionBytes += nextSelectionBytes;
2986
+ collectedReadBytes += nextReadBytes;
2795
2987
  }
2796
2988
  }
2797
2989
  if (startLineIndex >= endLineIndex) {
@@ -2824,7 +3016,7 @@ async function getAgentSessionRawRows(args) {
2824
3016
  if (line.trim()) {
2825
3017
  rawRows.push({
2826
3018
  id: lineNumber,
2827
- raw: line,
3019
+ raw: sanitizeSessionRpcRawLine(line),
2828
3020
  });
2829
3021
  }
2830
3022
  lineNumber += 1;
@@ -3055,46 +3247,6 @@ function subscribeToFsRpc(args) {
3055
3247
  });
3056
3248
  writeAgentInfo(`fs rpc subscribed subject=${subject}`);
3057
3249
  }
3058
- function normalizeShellRpcRequest(args) {
3059
- const requestId = typeof args.request.requestId === "string" ? args.request.requestId.trim() : "";
3060
- if (!requestId) {
3061
- throw new Error("missing requestId");
3062
- }
3063
- const requestAgentId = typeof args.request.agentId === "string" ? args.request.agentId.trim() : "";
3064
- if (!requestAgentId) {
3065
- throw new Error("missing agentId");
3066
- }
3067
- if (requestAgentId !== args.agentId) {
3068
- throw new Error("agent id mismatch");
3069
- }
3070
- const kind = args.request.kind === "apply_patch" ? "apply_patch" : "shell";
3071
- const command = typeof args.request.command === "string" ? args.request.command.trim() : "";
3072
- const patch = typeof args.request.patch === "string" ? args.request.patch : "";
3073
- if (kind === "shell" && !command) {
3074
- throw new Error("missing command");
3075
- }
3076
- if (kind === "apply_patch" && !patch.trim()) {
3077
- throw new Error("missing patch");
3078
- }
3079
- const responseSubject = typeof args.request.responseSubject === "string" ? args.request.responseSubject.trim() : "";
3080
- if (!responseSubject) {
3081
- throw new Error("missing responseSubject");
3082
- }
3083
- const cwd = typeof args.request.cwd === "string" && args.request.cwd.trim() ? args.request.cwd.trim() : null;
3084
- const timeoutRaw = Number(args.request.timeoutMs);
3085
- const timeoutMs = Number.isFinite(timeoutRaw) ? Math.max(1000, Math.min(Math.floor(timeoutRaw), 300000)) : 30000;
3086
- return {
3087
- kind,
3088
- requestId,
3089
- command: kind === "shell" ? command : null,
3090
- patch: kind === "apply_patch" ? patch : null,
3091
- cwd,
3092
- timeoutMs,
3093
- responseSubject,
3094
- runtimeEnvPatch: normalizeEnvPatch(args.request.runtimeEnvPatch),
3095
- codexAuthBundle: normalizeShellRpcCodexAuthBundle(args.request.codexAuth),
3096
- };
3097
- }
3098
3250
  function normalizeShellRpcCodexAuthBundle(value) {
3099
3251
  if (!value || typeof value !== "object" || Array.isArray(value)) {
3100
3252
  return null;
@@ -3113,118 +3265,6 @@ function normalizeShellRpcCodexAuthBundle(value) {
3113
3265
  apiKey: typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined,
3114
3266
  };
3115
3267
  }
3116
- function publishShellRpcResponse(args) {
3117
- args.nc.publish(args.responseSubject, shellRpcCodec.encode(JSON.stringify(args.payload)));
3118
- }
3119
- async function handleShellRpcMessage(args) {
3120
- let requestId = "unknown";
3121
- let responseSubject = "";
3122
- let stdout = "";
3123
- let stderr = "";
3124
- try {
3125
- const payload = JSON.parse(shellRpcCodec.decode(args.msg.data));
3126
- const request = normalizeShellRpcRequest({ request: payload, agentId: args.agentId });
3127
- requestId = request.requestId;
3128
- responseSubject = request.responseSubject;
3129
- const startedAtMs = Date.now();
3130
- const prepared = await prepareCommandExecution({
3131
- cwd: request.cwd,
3132
- userId: args.userId,
3133
- taskId: request.requestId,
3134
- codexAuthBundle: request.codexAuthBundle,
3135
- });
3136
- const child = spawnPreparedCommand({
3137
- kind: request.kind,
3138
- command: request.command,
3139
- patch: request.patch,
3140
- shellPath: prepared.shellPath,
3141
- taskWorkspace: prepared.taskWorkspace,
3142
- env: prepared.env,
3143
- agentToken: args.agentToken,
3144
- });
3145
- writeRpcStatus(requestId, `started kind=${request.kind} cwd=${prepared.taskWorkspace} shell=${request.kind === "shell" ? prepared.shellPath : "apply_patch"}`);
3146
- child.stdout.on("data", (chunk) => {
3147
- stdout += chunk;
3148
- writeRpcStream(requestId, "stdout", chunk);
3149
- });
3150
- child.stderr.on("data", (chunk) => {
3151
- stderr += chunk;
3152
- writeRpcStream(requestId, "stderr", chunk);
3153
- });
3154
- let timedOut = false;
3155
- const timeout = setTimeout(() => {
3156
- timedOut = true;
3157
- sendSignalToTaskProcess(child, "SIGTERM");
3158
- setTimeout(() => {
3159
- sendSignalToTaskProcess(child, "SIGKILL");
3160
- }, 1000).unref?.();
3161
- }, request.timeoutMs);
3162
- timeout.unref?.();
3163
- const result = await new Promise((resolve, reject) => {
3164
- child.once("error", reject);
3165
- child.once("close", (code, signal) => {
3166
- resolve({ exitCode: typeof code === "number" ? code : null, signal });
3167
- });
3168
- }).finally(() => {
3169
- clearTimeout(timeout);
3170
- });
3171
- await prepared.codexAuthCleanup().catch(() => undefined);
3172
- publishShellRpcResponse({
3173
- nc: args.jetstream.nc,
3174
- responseSubject,
3175
- payload: {
3176
- requestId,
3177
- ok: !timedOut,
3178
- exitCode: result.exitCode,
3179
- signal: result.signal,
3180
- stdout,
3181
- stderr,
3182
- ...(timedOut ? { error: `Command timed out after ${request.timeoutMs}ms` } : {}),
3183
- },
3184
- });
3185
- writeRpcStatus(requestId, `${timedOut ? "timed_out" : "completed"} exitCode=${result.exitCode ?? "null"} signal=${result.signal ?? "null"} durationMs=${Date.now() - startedAtMs}`);
3186
- }
3187
- catch (error) {
3188
- const message = error instanceof Error ? error.message : String(error);
3189
- if (responseSubject) {
3190
- publishShellRpcResponse({
3191
- nc: args.jetstream.nc,
3192
- responseSubject,
3193
- payload: {
3194
- requestId,
3195
- ok: false,
3196
- exitCode: null,
3197
- signal: null,
3198
- stdout,
3199
- stderr,
3200
- error: message,
3201
- },
3202
- });
3203
- }
3204
- writeRpcStatus(requestId, `failed error=${message}`);
3205
- writeAgentError(`shell rpc failed requestId=${requestId} error=${message}`);
3206
- }
3207
- }
3208
- function subscribeToShellRpc(args) {
3209
- const subject = buildAgentShellRpcSubject(args.userId, args.agentId);
3210
- args.jetstream.nc.subscribe(subject, {
3211
- callback: (error, msg) => {
3212
- if (error) {
3213
- const message = error instanceof Error ? error.message : String(error);
3214
- writeAgentError(`shell rpc subscription error: ${message}`);
3215
- return;
3216
- }
3217
- void handleShellRpcMessage({
3218
- msg,
3219
- jetstream: args.jetstream,
3220
- userId: args.userId,
3221
- agentId: args.agentId,
3222
- agentToken: args.agentToken,
3223
- });
3224
- },
3225
- });
3226
- writeAgentInfo(`shell rpc subscribed subject=${subject}`);
3227
- }
3228
3268
  async function postJson(url, body) {
3229
3269
  const res = await fetch(url, {
3230
3270
  method: "POST",
@@ -3472,36 +3512,6 @@ function spawnPreparedCommand(args) {
3472
3512
  child.stderr.setEncoding("utf8");
3473
3513
  return child;
3474
3514
  }
3475
- function createManagedCancellation(child) {
3476
- let cancelStage1Timer = null;
3477
- let cancelStage2Timer = null;
3478
- let cancelSignalSent = false;
3479
- return {
3480
- requestCancel: () => {
3481
- if (cancelSignalSent) {
3482
- return;
3483
- }
3484
- cancelSignalSent = true;
3485
- sendSignalToTaskProcess(child, "SIGINT");
3486
- cancelStage1Timer = setTimeout(() => {
3487
- sendSignalToTaskProcess(child, "SIGTERM");
3488
- }, 1200);
3489
- cancelStage1Timer.unref?.();
3490
- cancelStage2Timer = setTimeout(() => {
3491
- sendSignalToTaskProcess(child, "SIGKILL");
3492
- }, 3500);
3493
- cancelStage2Timer.unref?.();
3494
- },
3495
- clear: () => {
3496
- if (cancelStage1Timer) {
3497
- clearTimeout(cancelStage1Timer);
3498
- }
3499
- if (cancelStage2Timer) {
3500
- clearTimeout(cancelStage2Timer);
3501
- }
3502
- },
3503
- };
3504
- }
3505
3515
  async function runTask(args) {
3506
3516
  activeTaskLogContext = {
3507
3517
  jetstream: args.jetstream,
@@ -3595,7 +3605,6 @@ async function runTask(args) {
3595
3605
  }, 3500);
3596
3606
  cancelStage2Timer.unref?.();
3597
3607
  };
3598
- activeTaskCancelRequests.set(args.taskId, requestCancel);
3599
3608
  child.stdout.on("data", (chunk) => {
3600
3609
  writeTaskStream(args.taskId, "stdout", chunk);
3601
3610
  const seq = reserveNextEventSeq(args.taskId);
@@ -3690,7 +3699,6 @@ async function runTask(args) {
3690
3699
  writeAgentInfo(`task=${args.taskId} status=${status} exitCode=${typeof result.code === "number" ? result.code : "null"} signal=${result.signal ?? "null"}`);
3691
3700
  }
3692
3701
  finally {
3693
- activeTaskCancelRequests.delete(args.taskId);
3694
3702
  activeTaskLogContext = null;
3695
3703
  await codexAuth?.cleanup().catch(() => undefined);
3696
3704
  }
@@ -3794,24 +3802,11 @@ async function main() {
3794
3802
  agentId: initialAgentId,
3795
3803
  agentToken,
3796
3804
  });
3797
- subscribeToShellRpc({
3798
- jetstream,
3799
- userId,
3800
- agentId: initialAgentId,
3801
- agentToken,
3802
- });
3803
3805
  subscribeToSessionRpc({
3804
3806
  jetstream,
3805
3807
  userId,
3806
3808
  agentId: initialAgentId,
3807
3809
  });
3808
- subscribeToCodexRpc({
3809
- jetstream,
3810
- serverBaseUrl,
3811
- userId,
3812
- agentId: initialAgentId,
3813
- agentToken,
3814
- });
3815
3810
  subscribeToCodexAuthRpc({
3816
3811
  jetstream,
3817
3812
  userId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",