@rallycry/conveyor-agent 10.13.72 → 11.0.0

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/cli.js CHANGED
@@ -7,10 +7,11 @@ import {
7
7
  WorkspaceCommandSupervisor,
8
8
  startWorkspaceCommandsAfterConnect,
9
9
  stopWorkspaceCommands
10
- } from "./chunk-2L5THOWD.js";
10
+ } from "./chunk-JQVAWRVL.js";
11
11
  import {
12
12
  DEFAULT_SONNET_MODEL,
13
13
  PtyHarness,
14
+ RUNNER_MODES,
14
15
  SessionRunner,
15
16
  buildProjectTools,
16
17
  buildRateLimitEvents,
@@ -18,6 +19,7 @@ import {
18
19
  buildUnmeasurableEvent,
19
20
  claudeJsonPath,
20
21
  isPermissionDeniedError,
22
+ parsePackExecution,
21
23
  parseUsageGauges,
22
24
  resolvePlaywrightMcpServer,
23
25
  resolveSessionStart,
@@ -25,7 +27,7 @@ import {
25
27
  resolveTuiKindFromEnv,
26
28
  runUsageProbe,
27
29
  sampleKeyUsage
28
- } from "./chunk-2SN32LM6.js";
30
+ } from "./chunk-PEEGCZAR.js";
29
31
  import "./chunk-XORJ6SII.js";
30
32
  import {
31
33
  AgentConnection,
@@ -37,8 +39,8 @@ import {
37
39
  createServiceLogger,
38
40
  fetchBootstrap,
39
41
  loadConveyorConfig
40
- } from "./chunk-LSZ2KLJY.js";
41
- import "./chunk-WMMBAKPE.js";
42
+ } from "./chunk-N4WSUTGV.js";
43
+ import "./chunk-GL2DIQEQ.js";
42
44
  import "./chunk-IA45XHOA.js";
43
45
  import "./chunk-EXQ6AHOY.js";
44
46
  import "./chunk-KMB3BU4S.js";
@@ -57,22 +59,22 @@ import { fileURLToPath } from "url";
57
59
 
58
60
  // src/utils/session-identity.ts
59
61
  async function checkSessionTaskIdentity(params) {
60
- const { sessionId, taskId, fetchSessionTaskId, logger: logger6 } = params;
62
+ const { sessionId, taskId, fetchSessionTaskId, logger: logger7 } = params;
61
63
  if (!sessionId || sessionId === taskId) return "skipped";
62
64
  let sessionTaskId;
63
65
  try {
64
66
  sessionTaskId = await fetchSessionTaskId(sessionId);
65
67
  } catch (err) {
66
- const message = err instanceof Error ? err.message : String(err);
67
- logger6.warn("Could not verify session/task identity \u2014 continuing (defense-in-depth only)", {
68
+ const message2 = err instanceof Error ? err.message : String(err);
69
+ logger7.warn("Could not verify session/task identity \u2014 continuing (defense-in-depth only)", {
68
70
  sessionId,
69
71
  taskId,
70
- error: message
72
+ error: message2
71
73
  });
72
74
  return "error";
73
75
  }
74
76
  if (sessionTaskId !== taskId) {
75
- logger6.warn(
77
+ logger7.warn(
76
78
  "!!! CONVEYOR_SESSION_ID is bound to a different task than CONVEYOR_TASK_ID \u2014 server-side guards should still block mutations, but this indicates a misconfiguration or replayed token.",
77
79
  { sessionId, envTaskId: taskId, sessionTaskId }
78
80
  );
@@ -188,10 +190,10 @@ var ProjectSessionRunner = class {
188
190
  await this.waitUntilStopped();
189
191
  this._finalState = "finished";
190
192
  } catch (error) {
191
- const message = error instanceof Error ? error.message : String(error);
192
- process.stderr.write(`[conveyor-agent] Project runner failed: ${message}
193
+ const message2 = error instanceof Error ? error.message : String(error);
194
+ process.stderr.write(`[conveyor-agent] Project runner failed: ${message2}
193
195
  `);
194
- this.connection.sendEvent({ type: "error", message });
196
+ this.connection.sendEvent({ type: "error", message: message2 });
195
197
  this._finalState = "error";
196
198
  } finally {
197
199
  this.shutdown();
@@ -463,10 +465,10 @@ var AdhocSessionRunner = class {
463
465
  await this.runInteractiveTui();
464
466
  this._finalState = "finished";
465
467
  } catch (error) {
466
- const message = error instanceof Error ? error.message : String(error);
467
- process.stderr.write(`[conveyor-agent] Ad-hoc runner failed: ${message}
468
+ const message2 = error instanceof Error ? error.message : String(error);
469
+ process.stderr.write(`[conveyor-agent] Ad-hoc runner failed: ${message2}
468
470
  `);
469
- this.connection.sendEvent({ type: "error", message });
471
+ this.connection.sendEvent({ type: "error", message: message2 });
470
472
  this._finalState = "error";
471
473
  } finally {
472
474
  await this.shutdown();
@@ -620,6 +622,10 @@ var ReviewChildSupervisor = class {
620
622
  spawnFn;
621
623
  child = null;
622
624
  childSessionId = null;
625
+ /** The review session this supervisor currently hosts, if any. */
626
+ get activeSessionId() {
627
+ return this.childSessionId;
628
+ }
623
629
  /** Spawn (or replace) the review child for a `session:spawnReview` push. */
624
630
  async spawn(data) {
625
631
  logger2.info("Spawning same-pod review child", {
@@ -653,10 +659,10 @@ var ReviewChildSupervisor = class {
653
659
  this.childSessionId = data.sessionId;
654
660
  const spawnedAt = Date.now();
655
661
  let reported = false;
656
- const reportOnce = (message) => {
662
+ const reportOnce = (message2) => {
657
663
  if (reported) return;
658
664
  reported = true;
659
- this.connection.reportReviewSpawnFailure(data.sessionId, message);
665
+ this.connection.reportReviewSpawnFailure(data.sessionId, message2);
660
666
  };
661
667
  child.stdout?.on("data", (chunk) => {
662
668
  process.stdout.write(`[review-child] ${chunk.toString()}`);
@@ -776,6 +782,10 @@ var SessionChildSupervisor = class {
776
782
  get size() {
777
783
  return this.children.size;
778
784
  }
785
+ /** Sessions this supervisor currently hosts. Drives the shutdown deferral. */
786
+ get activeSessionIds() {
787
+ return [...this.children.keys()];
788
+ }
779
789
  /** Spawn a TUI/shell child for a `session:spawnTui` push. Additive per session. */
780
790
  async spawn(data) {
781
791
  logger3.info("Spawning same-pod session child", {
@@ -806,10 +816,10 @@ var SessionChildSupervisor = class {
806
816
  this.children.set(data.sessionId, child);
807
817
  const spawnedAt = Date.now();
808
818
  let reported = false;
809
- const reportOnce = (message) => {
819
+ const reportOnce = (message2) => {
810
820
  if (reported) return;
811
821
  reported = true;
812
- this.connection.reportSessionSpawnFailure(data.sessionId, message);
822
+ this.connection.reportSessionSpawnFailure(data.sessionId, message2);
813
823
  };
814
824
  const prefix = `[${data.mode}-child]`;
815
825
  child.stdout?.on("data", (chunk) => {
@@ -936,10 +946,10 @@ var ShellSessionRunner = class {
936
946
  await this.runShell();
937
947
  this._finalState = "finished";
938
948
  } catch (error) {
939
- const message = error instanceof Error ? error.message : String(error);
940
- process.stderr.write(`[conveyor-agent] Shell runner failed: ${message}
949
+ const message2 = error instanceof Error ? error.message : String(error);
950
+ process.stderr.write(`[conveyor-agent] Shell runner failed: ${message2}
941
951
  `);
942
- this.connection.sendEvent({ type: "error", message });
952
+ this.connection.sendEvent({ type: "error", message: message2 });
943
953
  this._finalState = "error";
944
954
  } finally {
945
955
  this.shutdown();
@@ -1033,14 +1043,158 @@ function createSpawnedChildRunner(inputs) {
1033
1043
  );
1034
1044
  }
1035
1045
 
1046
+ // src/runner/builder-child.ts
1047
+ import { spawn as nodeSpawn3 } from "child_process";
1048
+ var logger5 = createServiceLogger("BuilderChild");
1049
+ function buildBuilderChildEnv(baseEnv, data) {
1050
+ const env = { ...baseEnv };
1051
+ env.CONVEYOR_TASK_TOKEN = data.sessionJwt;
1052
+ env.CONVEYOR_SESSION_ID = data.sessionId;
1053
+ env.CONVEYOR_MODE = data.mode ?? "task";
1054
+ env.CONVEYOR_WORKBENCH_TOKEN ??= baseEnv.POD_BOOTSTRAP_TOKEN;
1055
+ delete env.POD_BOOTSTRAP_TOKEN;
1056
+ delete env.CONVEYOR_BOOTSTRAP_TOKEN;
1057
+ delete env.CONVEYOR_START_COMMAND;
1058
+ delete env.CONVEYOR_GIT_READY;
1059
+ delete env.CONVEYOR_GIT_READY_MARKER;
1060
+ delete env.CONVEYOR_AGENT_MODE;
1061
+ delete env.CONVEYOR_IS_AUTO;
1062
+ return env;
1063
+ }
1064
+ var BuilderChildSupervisor = class {
1065
+ constructor(connection, workspaceDir, spawnFn = nodeSpawn3) {
1066
+ this.connection = connection;
1067
+ this.workspaceDir = workspaceDir;
1068
+ this.spawnFn = spawnFn;
1069
+ }
1070
+ connection;
1071
+ workspaceDir;
1072
+ spawnFn;
1073
+ child = null;
1074
+ childSessionId = null;
1075
+ /** The build session this supervisor currently hosts, if any. */
1076
+ get activeSessionId() {
1077
+ return this.childSessionId;
1078
+ }
1079
+ /** Spawn (or replace) the builder child for a `session:spawnBuilder` push. */
1080
+ async spawn(data) {
1081
+ if (this.childSessionId === data.sessionId && this.child && this.child.exitCode === null) {
1082
+ logger5.info("Builder child already running for this session; ignoring re-push", {
1083
+ buildSessionId: data.sessionId
1084
+ });
1085
+ return;
1086
+ }
1087
+ logger5.info("Spawning same-pod builder child", {
1088
+ buildSessionId: data.sessionId,
1089
+ branch: data.branch ?? void 0,
1090
+ agentName: data.agentName ?? void 0
1091
+ });
1092
+ await this.stopCurrent("superseded by new builder spawn");
1093
+ const cliPath = process.argv[1];
1094
+ if (!cliPath) {
1095
+ this.connection.reportBuilderSpawnFailure(data.sessionId, "cannot resolve CLI entry path");
1096
+ return;
1097
+ }
1098
+ let child;
1099
+ try {
1100
+ child = this.spawnFn(process.execPath, [cliPath], {
1101
+ cwd: this.workspaceDir,
1102
+ env: buildBuilderChildEnv(process.env, data),
1103
+ stdio: ["ignore", "pipe", "pipe"]
1104
+ });
1105
+ } catch (err) {
1106
+ this.connection.reportBuilderSpawnFailure(
1107
+ data.sessionId,
1108
+ err instanceof Error ? err.message : String(err)
1109
+ );
1110
+ return;
1111
+ }
1112
+ this.child = child;
1113
+ this.childSessionId = data.sessionId;
1114
+ this.watchForEarlyExit(child, data.sessionId);
1115
+ }
1116
+ /**
1117
+ * A non-zero exit inside the grace window is a spawn failure, not a finished
1118
+ * build — the child never got far enough to connect. Reporting it lets the
1119
+ * server End the orphaned session and fall back to a cold boot instead of
1120
+ * leaving the card parked with a Builder tab that never appears.
1121
+ */
1122
+ watchForEarlyExit(child, sessionId) {
1123
+ const spawnedAt = Date.now();
1124
+ child.on("exit", (code) => {
1125
+ if (this.childSessionId === sessionId) {
1126
+ this.child = null;
1127
+ this.childSessionId = null;
1128
+ }
1129
+ const early = Date.now() - spawnedAt < SPAWN_FAILURE_GRACE_MS;
1130
+ if (early && code !== 0) {
1131
+ logger5.warn("Builder child exited inside the spawn grace window", { sessionId, code });
1132
+ this.connection.reportBuilderSpawnFailure(sessionId, `child exited with code ${code}`);
1133
+ }
1134
+ });
1135
+ }
1136
+ /** SIGTERM the current child, escalating to SIGKILL after KILL_WAIT_MS. */
1137
+ async stopCurrent(reason) {
1138
+ const child = this.child;
1139
+ if (!child || child.exitCode !== null) {
1140
+ this.child = null;
1141
+ this.childSessionId = null;
1142
+ return;
1143
+ }
1144
+ logger5.info("Stopping builder child", { reason, buildSessionId: this.childSessionId });
1145
+ child.kill("SIGTERM");
1146
+ await new Promise((resolve) => {
1147
+ const timer = setTimeout(() => {
1148
+ if (child.exitCode === null) child.kill("SIGKILL");
1149
+ resolve();
1150
+ }, KILL_WAIT_MS);
1151
+ child.once("exit", () => {
1152
+ clearTimeout(timer);
1153
+ resolve();
1154
+ });
1155
+ });
1156
+ this.child = null;
1157
+ this.childSessionId = null;
1158
+ }
1159
+ };
1160
+
1036
1161
  // src/runner/host-modes.ts
1037
1162
  function hostsSpawnedChildren(mode) {
1038
- return mode === "task" || mode === "pack";
1163
+ return mode === "task" || mode === "plan" || mode === "pack";
1164
+ }
1165
+
1166
+ // src/runner/wire-spawn-children.ts
1167
+ var message = (err) => err instanceof Error ? err.message : String(err);
1168
+ function wireSpawnChildren(mode, connection, supervisors, logger7) {
1169
+ if (hostsSpawnedChildren(mode)) {
1170
+ connection.onSpawnReview((data) => {
1171
+ void supervisors.review.spawn(data).catch((err) => {
1172
+ logger7.error("Review child spawn threw", { error: message(err) });
1173
+ connection.reportReviewSpawnFailure(data.sessionId, message(err));
1174
+ });
1175
+ });
1176
+ }
1177
+ if (mode === "plan") {
1178
+ connection.onSpawnBuilder((data) => {
1179
+ void supervisors.builder.spawn(data).catch((err) => {
1180
+ logger7.error("Builder child spawn threw", { error: message(err) });
1181
+ connection.reportBuilderSpawnFailure(data.sessionId, message(err));
1182
+ });
1183
+ });
1184
+ }
1185
+ if (hostsSpawnedChildren(mode)) {
1186
+ connection.onSpawnTui((data) => {
1187
+ void supervisors.session.spawn(data).catch((err) => {
1188
+ logger7.error("Session child spawn threw", { error: message(err) });
1189
+ connection.reportSessionSpawnFailure(data.sessionId, message(err));
1190
+ });
1191
+ });
1192
+ }
1039
1193
  }
1040
1194
 
1041
1195
  // src/cli.ts
1042
1196
  if (process.argv[2] === "boot") {
1043
- const { runBoot } = await import("./boot-ZNL7X5LQ.js");
1197
+ const { runBoot } = await import("./boot-PKQ2I66D.js");
1044
1198
  process.exit(await runBoot(process.argv.slice(3)));
1045
1199
  }
1046
1200
  if (isLegacyEntrypointLaunch(process.env)) {
@@ -1055,7 +1209,7 @@ if (process.argv.includes("--version")) {
1055
1209
  process.stdout.write(pkg.version + "\n");
1056
1210
  process.exit(0);
1057
1211
  }
1058
- var logger5 = createServiceLogger("CLI");
1212
+ var logger6 = createServiceLogger("CLI");
1059
1213
  var exitContext = {};
1060
1214
  var exitLogged = false;
1061
1215
  var shuttingDown = false;
@@ -1070,12 +1224,12 @@ function logAgentExit(reason, opts) {
1070
1224
  uptimeSec: process.uptime(),
1071
1225
  identity: exitContext
1072
1226
  });
1073
- logger5[exitLogLevel(reason)]("agent_exit", payload);
1227
+ logger6[exitLogLevel(reason)]("agent_exit", payload);
1074
1228
  }
1075
1229
  async function bootstrapFromCodespace(apiUrl, instanceName) {
1076
1230
  const bootstrapToken = process.env.CONVEYOR_BOOTSTRAP_TOKEN;
1077
1231
  const apiUrlFromEnv = Boolean(process.env.CONVEYOR_API_URL);
1078
- logger5.info("Bootstrapping from codespace", {
1232
+ logger6.info("Bootstrapping from codespace", {
1079
1233
  codespace: instanceName,
1080
1234
  apiUrl,
1081
1235
  apiUrlFromEnv,
@@ -1087,7 +1241,7 @@ async function bootstrapFromCodespace(apiUrl, instanceName) {
1087
1241
  bootstrapToken
1088
1242
  });
1089
1243
  if (!result.ok) {
1090
- logger5.error("Bootstrap failed after retries", {
1244
+ logger6.error("Bootstrap failed after retries", {
1091
1245
  reason: result.reason,
1092
1246
  attempts: result.attempts,
1093
1247
  status: result.status,
@@ -1100,20 +1254,20 @@ async function bootstrapFromCodespace(apiUrl, instanceName) {
1100
1254
  process.exit(1);
1101
1255
  }
1102
1256
  applyBootstrapToEnv(result.config);
1103
- logger5.info("Bootstrap complete", {
1257
+ logger6.info("Bootstrap complete", {
1104
1258
  taskId: result.config.taskId,
1105
1259
  attempts: result.attempts
1106
1260
  });
1107
1261
  }
1108
1262
  function isExpectedAbortError(error) {
1109
- const message = error.message || "";
1110
- const hasAbortMessage = /operation aborted/i.test(message) || /abort/i.test(message);
1263
+ const message2 = error.message || "";
1264
+ const hasAbortMessage = /operation aborted/i.test(message2) || /abort/i.test(message2);
1111
1265
  const hasAbortCode = !("code" in error) || error.code === void 0 || error.code === "ABORT_ERR";
1112
1266
  return hasAbortMessage && hasAbortCode;
1113
1267
  }
1114
1268
  function exitOnPermissionDenial(err) {
1115
1269
  if (!isPermissionDeniedError(err)) return false;
1116
- logger5.error("Not authorized for this project \u2014 parking instead of crash-looping", {
1270
+ logger6.error("Not authorized for this project \u2014 parking instead of crash-looping", {
1117
1271
  error: err.message
1118
1272
  });
1119
1273
  logAgentExit("unauthorized", { exitCode: 0 });
@@ -1122,22 +1276,22 @@ function exitOnPermissionDenial(err) {
1122
1276
  process.on("uncaughtException", (err) => {
1123
1277
  if (err.code === "EPIPE") return;
1124
1278
  if (shuttingDown && isExpectedAbortError(err)) {
1125
- logger5.info("Ignored expected abort after shutdown", { error: err.message, code: err.code });
1279
+ logger6.info("Ignored expected abort after shutdown", { error: err.message, code: err.code });
1126
1280
  return;
1127
1281
  }
1128
1282
  if (exitOnPermissionDenial(err)) return;
1129
- logger5.error("Uncaught exception", { error: err.message, code: err.code });
1283
+ logger6.error("Uncaught exception", { error: err.message, code: err.code });
1130
1284
  logAgentExit("uncaught_exception", { exitCode: 1 });
1131
1285
  process.exit(1);
1132
1286
  });
1133
1287
  process.on("unhandledRejection", (reason) => {
1134
1288
  const err = reason instanceof Error ? reason : new Error(String(reason));
1135
1289
  if (shuttingDown && isExpectedAbortError(err)) {
1136
- logger5.info("Ignored expected abort rejection after shutdown", { error: err.message });
1290
+ logger6.info("Ignored expected abort rejection after shutdown", { error: err.message });
1137
1291
  return;
1138
1292
  }
1139
1293
  if (exitOnPermissionDenial(err)) return;
1140
- logger5.error("Unhandled rejection", { error: err.message });
1294
+ logger6.error("Unhandled rejection", { error: err.message });
1141
1295
  logAgentExit("unhandled_rejection", { exitCode: 1 });
1142
1296
  process.exit(1);
1143
1297
  });
@@ -1148,7 +1302,7 @@ if (process.env.CONVEYOR_MODE === "workbench") {
1148
1302
  const port = Number(process.env.CONVEYOR_WORKBENCH_PORT) || DEFAULT_WORKBENCH_PORT;
1149
1303
  const token = process.env.CONVEYOR_WORKBENCH_TOKEN ?? process.env.POD_BOOTSTRAP_TOKEN ?? "";
1150
1304
  if (!token) {
1151
- logger5.error("workbench mode requires POD_BOOTSTRAP_TOKEN (or CONVEYOR_WORKBENCH_TOKEN)");
1305
+ logger6.error("workbench mode requires POD_BOOTSTRAP_TOKEN (or CONVEYOR_WORKBENCH_TOKEN)");
1152
1306
  process.exit(1);
1153
1307
  }
1154
1308
  const pkgDir = dirname(fileURLToPath(import.meta.url));
@@ -1158,12 +1312,12 @@ if (process.env.CONVEYOR_MODE === "workbench") {
1158
1312
  token,
1159
1313
  version: pkg.version ?? "unknown",
1160
1314
  oomWatchdog: oomWatchdogOptionsFromEnv({
1161
- info: (m) => logger5.info(m),
1162
- warn: (m) => logger5.warn(m),
1163
- error: (m) => logger5.error(m)
1315
+ info: (m) => logger6.info(m),
1316
+ warn: (m) => logger6.warn(m),
1317
+ error: (m) => logger6.error(m)
1164
1318
  })
1165
1319
  });
1166
- logger5.info(`Workbench launcher listening on 127.0.0.1:${handle.port}`);
1320
+ logger6.info(`Workbench launcher listening on 127.0.0.1:${handle.port}`);
1167
1321
  process.on("SIGTERM", () => process.exit(0));
1168
1322
  await new Promise(() => {
1169
1323
  });
@@ -1173,7 +1327,7 @@ var conveyorApiUrl = process.env.CONVEYOR_API_URL || DEFAULT_CONVEYOR_API_URL;
1173
1327
  var INSTANCE_NAME = process.env.CODESPACE_NAME || process.env.CLAUDESPACE_NAME;
1174
1328
  if (INSTANCE_NAME && !process.env.CONVEYOR_TASK_TOKEN) {
1175
1329
  if (!conveyorApiUrl) {
1176
- logger5.error("Could not resolve CONVEYOR_API_URL for codespace bootstrap");
1330
+ logger6.error("Could not resolve CONVEYOR_API_URL for codespace bootstrap");
1177
1331
  process.exit(1);
1178
1332
  }
1179
1333
  await bootstrapFromCodespace(conveyorApiUrl, INSTANCE_NAME);
@@ -1191,7 +1345,7 @@ exitContext.runnerMode = CONVEYOR_MODE;
1191
1345
  exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? CONVEYOR_TASK_ID;
1192
1346
  var projectIdentity = resolveProjectRunnerIdentity(process.env);
1193
1347
  if (!CONVEYOR_TASK_ID && projectIdentity && CONVEYOR_MODE === "adhoc") {
1194
- logger5.info("Starting ad-hoc agent", { projectId: projectIdentity.projectId });
1348
+ logger6.info("Starting ad-hoc agent", { projectId: projectIdentity.projectId });
1195
1349
  const adhocRunner = new AdhocSessionRunner(
1196
1350
  {
1197
1351
  connection: {
@@ -1210,7 +1364,7 @@ if (!CONVEYOR_TASK_ID && projectIdentity && CONVEYOR_MODE === "adhoc") {
1210
1364
  },
1211
1365
  {
1212
1366
  onEvent: (event) => {
1213
- logger5.info("Ad-hoc runner event", { eventType: event.type });
1367
+ logger6.info("Ad-hoc runner event", { eventType: event.type });
1214
1368
  }
1215
1369
  }
1216
1370
  );
@@ -1234,7 +1388,7 @@ if (!CONVEYOR_TASK_ID && projectIdentity && CONVEYOR_MODE === "adhoc") {
1234
1388
  process.exit(adhocError ? 1 : 0);
1235
1389
  }
1236
1390
  if (!CONVEYOR_TASK_ID && projectIdentity) {
1237
- logger5.info("Starting project agent", { projectId: projectIdentity.projectId });
1391
+ logger6.info("Starting project agent", { projectId: projectIdentity.projectId });
1238
1392
  const projectRunner = new ProjectSessionRunner(
1239
1393
  {
1240
1394
  connection: {
@@ -1249,7 +1403,7 @@ if (!CONVEYOR_TASK_ID && projectIdentity) {
1249
1403
  },
1250
1404
  {
1251
1405
  onEvent: (event) => {
1252
- logger5.info("Project runner event", { eventType: event.type });
1406
+ logger6.info("Project runner event", { eventType: event.type });
1253
1407
  }
1254
1408
  }
1255
1409
  );
@@ -1273,29 +1427,26 @@ if (!CONVEYOR_TASK_ID && projectIdentity) {
1273
1427
  process.exit(projectError ? 1 : 0);
1274
1428
  }
1275
1429
  if (!CONVEYOR_TASK_TOKEN || !CONVEYOR_TASK_ID) {
1276
- logger5.error("Missing required environment variables");
1277
- logger5.error(" CONVEYOR_TASK_TOKEN - JWT token for task authentication");
1278
- logger5.error(" CONVEYOR_TASK_ID - ID of the task to execute");
1279
- logger5.error("");
1280
- logger5.error("CONVEYOR_API_URL is provided via codespace secret or bootstrap.");
1281
- logger5.error("");
1282
- logger5.error("Optional:");
1283
- logger5.error(" CONVEYOR_MODE - Runner mode: 'task' (default), 'pack', or 'pm'");
1284
- logger5.error(" CONVEYOR_WORKSPACE - Working directory (defaults to cwd)");
1285
- logger5.error(
1430
+ logger6.error("Missing required environment variables");
1431
+ logger6.error(" CONVEYOR_TASK_TOKEN - JWT token for task authentication");
1432
+ logger6.error(" CONVEYOR_TASK_ID - ID of the task to execute");
1433
+ logger6.error("");
1434
+ logger6.error("CONVEYOR_API_URL is provided via codespace secret or bootstrap.");
1435
+ logger6.error("");
1436
+ logger6.error("Optional:");
1437
+ logger6.error(" CONVEYOR_MODE - Runner mode: 'task' (default), 'pack', or 'pm'");
1438
+ logger6.error(" CONVEYOR_WORKSPACE - Working directory (defaults to cwd)");
1439
+ logger6.error(
1286
1440
  " Project pods instead require CONVEYOR_PROJECT_ID + CONVEYOR_SESSION_ID + CONVEYOR_MODE=pm"
1287
1441
  );
1288
1442
  process.exit(1);
1289
1443
  }
1290
- if (CONVEYOR_MODE !== "task" && CONVEYOR_MODE !== "pm" && CONVEYOR_MODE !== "code-review" && CONVEYOR_MODE !== "adhoc" && CONVEYOR_MODE !== "pack" && CONVEYOR_MODE !== "shell" && CONVEYOR_MODE !== "serving") {
1291
- logger5.error("Invalid CONVEYOR_MODE", {
1292
- mode: CONVEYOR_MODE,
1293
- expected: ["task", "pm", "code-review", "adhoc", "pack", "shell", "serving"]
1294
- });
1444
+ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
1445
+ logger6.error("Invalid CONVEYOR_MODE", { mode: CONVEYOR_MODE, expected: RUNNER_MODES });
1295
1446
  process.exit(1);
1296
1447
  }
1297
1448
  if (CONVEYOR_MODE === "serving") {
1298
- const { runServingSession } = await import("./serve-boot-4N3FRXVQ.js");
1449
+ const { runServingSession } = await import("./serve-boot-YPAUKENG.js");
1299
1450
  exitContext.runnerMode = "serving";
1300
1451
  exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
1301
1452
  const outcome = await runServingSession({
@@ -1315,7 +1466,7 @@ if (CONVEYOR_MODE === "serving") {
1315
1466
  if (CONVEYOR_MODE === "shell" || CONVEYOR_MODE === "adhoc") {
1316
1467
  const spawnedSessionId = process.env.CONVEYOR_SESSION_ID;
1317
1468
  if (!spawnedSessionId) {
1318
- logger5.error("Spawned child requires CONVEYOR_SESSION_ID", { mode: CONVEYOR_MODE });
1469
+ logger6.error("Spawned child requires CONVEYOR_SESSION_ID", { mode: CONVEYOR_MODE });
1319
1470
  process.exit(1);
1320
1471
  }
1321
1472
  exitContext.sessionId = spawnedSessionId;
@@ -1328,7 +1479,7 @@ if (CONVEYOR_MODE === "shell" || CONVEYOR_MODE === "adhoc") {
1328
1479
  workspaceDir: CONVEYOR_WORKSPACE,
1329
1480
  projectId: process.env.CONVEYOR_PROJECT_ID ?? ""
1330
1481
  });
1331
- logger5.info("Starting spawned session child", {
1482
+ logger6.info("Starting spawned session child", {
1332
1483
  mode: CONVEYOR_MODE,
1333
1484
  sessionId: spawnedSessionId
1334
1485
  });
@@ -1348,7 +1499,7 @@ if (CONVEYOR_MODE === "shell" || CONVEYOR_MODE === "adhoc") {
1348
1499
  });
1349
1500
  process.exit(childError ? 1 : 0);
1350
1501
  }
1351
- logger5.info("Starting agent", { mode: CONVEYOR_MODE });
1502
+ logger6.info("Starting agent", { mode: CONVEYOR_MODE });
1352
1503
  var lifecycleOverrides = process.env.CLAUDESPACE_NAME ? { idleTimeoutMs: 60 * 60 * 1e3 } : { gitFlushIntervalMs: 0 };
1353
1504
  var runner = new SessionRunner(
1354
1505
  {
@@ -1361,6 +1512,10 @@ var runner = new SessionRunner(
1361
1512
  runnerMode: CONVEYOR_MODE
1362
1513
  },
1363
1514
  runnerMode: CONVEYOR_MODE,
1515
+ // Only read when the mode is "pack". Parsed rather than cast so an unset or
1516
+ // malformed value falls back to single-pod instead of selecting a third,
1517
+ // non-existent behavior.
1518
+ packExecution: parsePackExecution(process.env.CONVEYOR_PACK_EXECUTION),
1364
1519
  isAuto: CONVEYOR_IS_AUTO,
1365
1520
  workspaceDir: CONVEYOR_WORKSPACE,
1366
1521
  agentMode: CONVEYOR_AGENT_MODE,
@@ -1368,29 +1523,51 @@ var runner = new SessionRunner(
1368
1523
  },
1369
1524
  {
1370
1525
  onStatusChange: (status) => {
1371
- logger5.info("Status changed", { status });
1526
+ logger6.info("Status changed", { status });
1372
1527
  },
1373
1528
  onEvent: (event) => {
1374
1529
  const detail = event.message ?? event.content ?? event.summary ?? "";
1375
1530
  if (detail) {
1376
- logger5.info(detail, { eventType: event.type });
1531
+ logger6.info(detail, { eventType: event.type });
1377
1532
  }
1378
1533
  }
1379
1534
  }
1380
1535
  );
1381
1536
  var reviewChildren = new ReviewChildSupervisor(runner.connection, CONVEYOR_WORKSPACE);
1382
1537
  var sessionChildren = new SessionChildSupervisor(runner.connection, CONVEYOR_WORKSPACE);
1538
+ var builderChildren = new BuilderChildSupervisor(runner.connection, CONVEYOR_WORKSPACE);
1539
+ runner.setLiveChildSources([
1540
+ {
1541
+ kind: "builder",
1542
+ activeSessionIds: () => {
1543
+ const id = builderChildren.activeSessionId;
1544
+ return id ? [id] : [];
1545
+ }
1546
+ },
1547
+ {
1548
+ kind: "review",
1549
+ activeSessionIds: () => {
1550
+ const id = reviewChildren.activeSessionId;
1551
+ return id ? [id] : [];
1552
+ }
1553
+ },
1554
+ { kind: "tui", activeSessionIds: () => sessionChildren.activeSessionIds }
1555
+ ]);
1383
1556
  var shutdownSignal;
1384
1557
  var workspaceCommandSupervisor = null;
1385
1558
  var shutdownCompletion = null;
1386
1559
  var shutdownAgent = (signal) => {
1387
1560
  shuttingDown = true;
1388
- logger5.info(`Received ${signal}, flushing git and stopping agent`);
1561
+ logger6.info(`Received ${signal}, flushing git and stopping agent`);
1389
1562
  const commandShutdown = stopWorkspaceCommands(workspaceCommandSupervisor);
1390
1563
  shutdownSignal = signal;
1391
1564
  shutdownCompletion ??= (async () => {
1392
1565
  try {
1393
- await Promise.all([reviewChildren.stopAll(), sessionChildren.stopAll()]);
1566
+ await Promise.all([
1567
+ reviewChildren.stopAll(),
1568
+ sessionChildren.stopAll(),
1569
+ builderChildren.stopCurrent("agent shutdown")
1570
+ ]);
1394
1571
  } catch {
1395
1572
  }
1396
1573
  try {
@@ -1402,8 +1579,8 @@ var shutdownAgent = (signal) => {
1402
1579
  })();
1403
1580
  void shutdownCompletion;
1404
1581
  setTimeout(() => {
1405
- logger5.warn(`Forcing exit after ${signal} timeout`);
1406
- logger5.warn(
1582
+ logger6.warn(`Forcing exit after ${signal} timeout`);
1583
+ logger6.warn(
1407
1584
  "agent_exit",
1408
1585
  buildAgentExitLog({
1409
1586
  reason: "force_timeout",
@@ -1429,7 +1606,7 @@ workspaceCommandSupervisor = await startWorkspaceCommandsAfterConnect({
1429
1606
  })
1430
1607
  });
1431
1608
  if (!workspaceCommandSupervisor) {
1432
- logger5.info("Agent stopped before workspace commands were started");
1609
+ logger6.info("Agent stopped before workspace commands were started");
1433
1610
  if (shutdownCompletion) await shutdownCompletion;
1434
1611
  logAgentExit(shutdownSignal ? "signal" : "clean", {
1435
1612
  exitCode: 0,
@@ -1449,34 +1626,14 @@ void checkSessionTaskIdentity({
1449
1626
  });
1450
1627
  return ctx.id;
1451
1628
  },
1452
- logger: logger5
1629
+ logger: logger6
1453
1630
  });
1454
- if (hostsSpawnedChildren(CONVEYOR_MODE)) {
1455
- runner.connection.onSpawnReview((data) => {
1456
- void reviewChildren.spawn(data).catch((err) => {
1457
- logger5.error("Review child spawn threw", {
1458
- error: err instanceof Error ? err.message : String(err)
1459
- });
1460
- runner.connection.reportReviewSpawnFailure(
1461
- data.sessionId,
1462
- err instanceof Error ? err.message : String(err)
1463
- );
1464
- });
1465
- });
1466
- }
1467
- if (hostsSpawnedChildren(CONVEYOR_MODE)) {
1468
- runner.connection.onSpawnTui((data) => {
1469
- void sessionChildren.spawn(data).catch((err) => {
1470
- logger5.error("Session child spawn threw", {
1471
- error: err instanceof Error ? err.message : String(err)
1472
- });
1473
- runner.connection.reportSessionSpawnFailure(
1474
- data.sessionId,
1475
- err instanceof Error ? err.message : String(err)
1476
- );
1477
- });
1478
- });
1479
- }
1631
+ wireSpawnChildren(
1632
+ CONVEYOR_MODE,
1633
+ runner.connection,
1634
+ { review: reviewChildren, builder: builderChildren, session: sessionChildren },
1635
+ logger6
1636
+ );
1480
1637
  runner.run().then(async () => {
1481
1638
  await stopWorkspaceCommands(workspaceCommandSupervisor);
1482
1639
  const errored = runner.finalState === "error";
@@ -1490,7 +1647,7 @@ runner.run().then(async () => {
1490
1647
  }).catch(async (error) => {
1491
1648
  await stopWorkspaceCommands(workspaceCommandSupervisor);
1492
1649
  const msg = error instanceof Error ? error.message : String(error);
1493
- logger5.error("Agent runner failed", { error: msg });
1650
+ logger6.error("Agent runner failed", { error: msg });
1494
1651
  logAgentExit("error", { exitCode: 1, finalState: runner.finalState ?? void 0 });
1495
1652
  process.exit(1);
1496
1653
  });