@rallycry/conveyor-agent 8.5.0 → 8.5.1

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.
@@ -12,7 +12,7 @@ import {
12
12
  ensureClaudeCredentials,
13
13
  removeConveyorCredentials,
14
14
  sessionTranscriptPath
15
- } from "./chunk-63FTZOL5.js";
15
+ } from "./chunk-6B545CHM.js";
16
16
 
17
17
  // src/setup/bootstrap.ts
18
18
  var BOOTSTRAP_TIMEOUT_MS = 3e4;
@@ -134,11 +134,13 @@ function applyBootstrapToEnv(config) {
134
134
  import { io } from "socket.io-client";
135
135
  var EVENT_BATCH_MS = 500;
136
136
  var MAX_EVENT_BUFFER = 5e3;
137
+ var TOKEN_REFRESH_INTERVAL_MS = 6 * 60 * 60 * 1e3;
137
138
  var AgentConnection = class _AgentConnection {
138
139
  socket = null;
139
140
  config;
140
141
  eventBuffer = [];
141
142
  flushTimer = null;
143
+ tokenRefreshTimer = null;
142
144
  lastEmittedStatus = null;
143
145
  lastReportedStatus = null;
144
146
  droppedEventCount = 0;
@@ -202,6 +204,7 @@ var AgentConnection = class _AgentConnection {
202
204
  if (!this.config.apiUrl) {
203
205
  return Promise.reject(new Error("Cannot connect: apiUrl is empty"));
204
206
  }
207
+ this.startProactiveTokenRefresh();
205
208
  return new Promise((resolve2, reject) => {
206
209
  let settled = false;
207
210
  let attempts = 0;
@@ -316,6 +319,7 @@ var AgentConnection = class _AgentConnection {
316
319
  });
317
320
  }
318
321
  disconnect() {
322
+ this.stopProactiveTokenRefresh();
319
323
  void this.flushEvents();
320
324
  if (this.socket) {
321
325
  this.socket.io.reconnection(false);
@@ -399,7 +403,32 @@ var AgentConnection = class _AgentConnection {
399
403
  }
400
404
  }
401
405
  looksLikeAuthError(message) {
402
- return /unauthor|forbid|auth|token/i.test(message);
406
+ return /unauthor|forbid|auth|token|session (?:not found|expired|invalid)|invalid session/i.test(
407
+ message
408
+ );
409
+ }
410
+ // ── Proactive task-token refresh ────────────────────────────────────────
411
+ //
412
+ // Socket.IO only re-presents the taskToken on a (re)connect handshake, and
413
+ // the server only re-validates the JWT then. So a token that expires while
414
+ // the socket stays connected goes unnoticed until the next RPC fails. Re-mint
415
+ // periodically from the bootstrap endpoint — refreshFromBootstrap() updates
416
+ // both this.config.taskToken and socket.auth.taskToken, so any later
417
+ // reconnect carries a fresh token. No-ops for project mode / missing
418
+ // codespace env, and is rate-limited to once/60s inside refreshFromBootstrap.
419
+ startProactiveTokenRefresh() {
420
+ if (this.tokenRefreshTimer) return;
421
+ this.tokenRefreshTimer = setInterval(() => {
422
+ void this.refreshTaskTokenFromBootstrap().catch(() => {
423
+ });
424
+ }, TOKEN_REFRESH_INTERVAL_MS);
425
+ this.tokenRefreshTimer.unref?.();
426
+ }
427
+ stopProactiveTokenRefresh() {
428
+ if (this.tokenRefreshTimer) {
429
+ clearInterval(this.tokenRefreshTimer);
430
+ this.tokenRefreshTimer = null;
431
+ }
403
432
  }
404
433
  drainPendingMessages(messages) {
405
434
  for (const msg of messages) {
@@ -1936,6 +1965,11 @@ var StopProjectBuildRequestSchema = z2.object({
1936
1965
  taskId: z2.string(),
1937
1966
  requestingUserId: z2.string().optional()
1938
1967
  });
1968
+ var CreateProjectReleaseRequestSchema = z2.object({
1969
+ projectId: z2.string(),
1970
+ taskIds: z2.array(z2.string()).optional(),
1971
+ requestingUserId: z2.string().optional()
1972
+ });
1939
1973
  var ApproveProjectMergePRRequestSchema = z2.object({
1940
1974
  projectId: z2.string(),
1941
1975
  childTaskId: z2.string(),
@@ -2011,6 +2045,22 @@ var GetProjectAttachmentRequestSchema = z2.object({
2011
2045
  /** Max bytes of text content to return from `offset`. Server default applies. */
2012
2046
  maxBytes: z2.number().int().positive().optional()
2013
2047
  });
2048
+ var RequestProjectFileUploadRequestSchema = z2.object({
2049
+ projectId: z2.string(),
2050
+ taskId: z2.string(),
2051
+ fileName: z2.string().min(1).max(255),
2052
+ mimeType: z2.string().min(1).max(128),
2053
+ fileSize: z2.number().int().positive().max(MAX_FILE_SIZE_BYTES),
2054
+ requestingUserId: z2.string().optional()
2055
+ });
2056
+ var ConfirmProjectFileUploadRequestSchema = z2.object({
2057
+ projectId: z2.string(),
2058
+ taskId: z2.string(),
2059
+ fileId: z2.string(),
2060
+ /** When set, the attachment is also posted to the task chat with this text. */
2061
+ comment: z2.string().max(2e3).optional(),
2062
+ requestingUserId: z2.string().optional()
2063
+ });
2014
2064
  var CreateProjectPullRequestRequestSchema = z2.object({
2015
2065
  projectId: z2.string(),
2016
2066
  taskId: z2.string(),
@@ -2035,6 +2085,16 @@ var RemoveProjectTaskReviewerRequestSchema = z2.object({
2035
2085
  userId: z2.string(),
2036
2086
  requestingUserId: z2.string().optional()
2037
2087
  });
2088
+ var ListProjectManualTestsRequestSchema = z2.object({
2089
+ projectId: z2.string(),
2090
+ taskId: z2.string()
2091
+ });
2092
+ var SetProjectManualTestsRequestSchema = z2.object({
2093
+ projectId: z2.string(),
2094
+ taskId: z2.string(),
2095
+ items: z2.array(z2.object({ title: z2.string().min(1) })).min(1),
2096
+ requestingUserId: z2.string().optional()
2097
+ });
2038
2098
  var StartTaskAuditRequestSchema = z3.object({
2039
2099
  projectId: z3.string(),
2040
2100
  taskIds: z3.array(z3.string()).min(1)
@@ -8200,7 +8260,10 @@ var SessionRunner = class _SessionRunner {
8200
8260
  `);
8201
8261
  this.connection.sendEvent({ type: "shutdown", reason: finalState });
8202
8262
  this.lifecycle.destroy();
8203
- await this.setState(finalState);
8263
+ try {
8264
+ await this.setState(finalState);
8265
+ } catch {
8266
+ }
8204
8267
  this.connection.disconnect();
8205
8268
  this._finalState = finalState;
8206
8269
  }
@@ -8259,20 +8322,31 @@ var ProjectConnection = class {
8259
8322
  return this.socket?.connected ?? false;
8260
8323
  }
8261
8324
  // ── Typed service method call ──────────────────────────────────────────
8262
- call(method, payload) {
8325
+ call(method, payload, opts) {
8263
8326
  const socket = this.requireSocket();
8264
8327
  return new Promise((resolve2, reject) => {
8265
- socket.emit(
8266
- `agentSessionService:${String(method)}`,
8267
- payload,
8268
- (response) => {
8269
- if (response.success && response.data !== void 0) {
8270
- resolve2(response.data);
8271
- } else {
8272
- reject(new Error(response.error ?? `Service call failed: ${String(method)}`));
8273
- }
8328
+ const handleResponse = (response) => {
8329
+ if (response.success && response.data !== void 0) {
8330
+ resolve2(response.data);
8331
+ } else {
8332
+ reject(new Error(response.error ?? `Service call failed: ${String(method)}`));
8274
8333
  }
8275
- );
8334
+ };
8335
+ if (opts?.timeoutMs) {
8336
+ socket.timeout(opts.timeoutMs).emit(
8337
+ `agentSessionService:${String(method)}`,
8338
+ payload,
8339
+ (err, response) => {
8340
+ if (err) {
8341
+ reject(new Error(`Service call timed out: ${String(method)}`));
8342
+ } else {
8343
+ handleResponse(response);
8344
+ }
8345
+ }
8346
+ );
8347
+ return;
8348
+ }
8349
+ socket.emit(`agentSessionService:${String(method)}`, payload, handleResponse);
8276
8350
  });
8277
8351
  }
8278
8352
  // ── Connection lifecycle ───────────────────────────────────────────────
@@ -8330,8 +8404,13 @@ var ProjectConnection = class {
8330
8404
  this.flushTimer = null;
8331
8405
  }
8332
8406
  this.flushEvents();
8333
- this.socket?.disconnect();
8334
- this.socket = null;
8407
+ if (this.socket) {
8408
+ this.socket.io.reconnection(false);
8409
+ this.socket.removeAllListeners();
8410
+ this.socket.disconnect();
8411
+ this.socket = null;
8412
+ }
8413
+ this.reconnectCallbacks = [];
8335
8414
  }
8336
8415
  onReconnect(callback) {
8337
8416
  this.reconnectCallbacks.push(callback);
@@ -9928,6 +10007,10 @@ function spawnChildAgent(assignment, workDir) {
9928
10007
  CONVEYOR_MODE: mode,
9929
10008
  CONVEYOR_WORKSPACE: workDir,
9930
10009
  CONVEYOR_USE_WORKTREE: "false",
10010
+ // The project runner flushes git for every child working tree after the
10011
+ // kill (see ProjectRunner.stop), so the child skips its own shutdown
10012
+ // flush — keeps SIGTERM teardown well inside the SIGKILL window.
10013
+ CONVEYOR_SKIP_SHUTDOWN_GIT_FLUSH: "true",
9931
10014
  CONVEYOR_AGENT_MODE: effectiveAgentMode,
9932
10015
  CONVEYOR_IS_AUTO: isAuto ? "true" : "false",
9933
10016
  CONVEYOR_USE_SANDBOX: useSandbox === true ? "true" : "false",
@@ -10052,6 +10135,8 @@ function handleStopTask(taskId, activeAgents, projectDir) {
10052
10135
  // src/runner/project-runner.ts
10053
10136
  var logger10 = createServiceLogger("ProjectRunner");
10054
10137
  var HEARTBEAT_INTERVAL_MS = 3e4;
10138
+ var DISCONNECT_ACK_TIMEOUT_MS = 5e3;
10139
+ var SHUTDOWN_WATCHDOG_MS = 9e4;
10055
10140
  var ProjectRunner = class {
10056
10141
  connection;
10057
10142
  projectDir;
@@ -10135,11 +10220,17 @@ var ProjectRunner = class {
10135
10220
  }
10136
10221
  /** Best-effort WIP commit + push of every tracked working tree on shutdown.
10137
10222
  * Iterates the main project dir plus any active agent worktrees so nothing
10138
- * pending is lost when the pod is killed. Never throws. */
10139
- async flushGitOnShutdown() {
10140
- const dirs = /* @__PURE__ */ new Set([this.projectDir]);
10141
- for (const agent of this.activeAgents.values()) {
10142
- if (agent.worktreePath) dirs.add(agent.worktreePath);
10223
+ * pending is lost when the pod is killed. Never throws.
10224
+ *
10225
+ * Callers that kill child agents first must snapshot the worktree dirs
10226
+ * BEFORE the kill the child exit handler removes entries from
10227
+ * `activeAgents`, so reading it after the kill misses the worktrees. */
10228
+ async flushGitOnShutdown(dirs) {
10229
+ if (!dirs) {
10230
+ dirs = /* @__PURE__ */ new Set([this.projectDir]);
10231
+ for (const agent of this.activeAgents.values()) {
10232
+ if (agent.worktreePath) dirs.add(agent.worktreePath);
10233
+ }
10143
10234
  }
10144
10235
  for (const dir of dirs) {
10145
10236
  try {
@@ -10163,38 +10254,62 @@ var ProjectRunner = class {
10163
10254
  if (this.stopping) return;
10164
10255
  this.stopping = true;
10165
10256
  logger10.info("Shutting down");
10166
- this.commitWatcher.stop();
10167
- await killStartCommand(this.startCmd);
10168
- if (this.heartbeatTimer) {
10169
- clearInterval(this.heartbeatTimer);
10170
- this.heartbeatTimer = null;
10257
+ const watchdog = setTimeout(() => {
10258
+ logger10.warn("Shutdown watchdog fired, forcing lifecycle resolution");
10259
+ this.finishShutdown();
10260
+ }, SHUTDOWN_WATCHDOG_MS);
10261
+ watchdog.unref();
10262
+ const flushDirs = /* @__PURE__ */ new Set([this.projectDir]);
10263
+ for (const agent of this.activeAgents.values()) {
10264
+ if (agent.worktreePath) flushDirs.add(agent.worktreePath);
10171
10265
  }
10172
- const stopPromises = [...this.activeAgents.keys()].map(
10173
- (key) => new Promise((resolve2) => {
10174
- const agent = this.activeAgents.get(key);
10175
- if (!agent) {
10176
- resolve2();
10177
- return;
10178
- }
10179
- agent.process.on("exit", () => resolve2());
10180
- handleStopTask(key, this.activeAgents, this.projectDir);
10181
- })
10182
- );
10183
- await Promise.race([
10184
- Promise.all(stopPromises),
10185
- new Promise((resolve2) => {
10186
- setTimeout(resolve2, STOP_TIMEOUT_MS + 5e3);
10187
- })
10188
- ]);
10189
- await this.flushGitOnShutdown();
10190
10266
  try {
10191
- await this.connection.call("disconnectProjectRunner", {
10192
- projectId: this.connection.projectId
10193
- });
10267
+ this.commitWatcher.stop();
10268
+ await killStartCommand(this.startCmd);
10269
+ if (this.heartbeatTimer) {
10270
+ clearInterval(this.heartbeatTimer);
10271
+ this.heartbeatTimer = null;
10272
+ }
10273
+ const stopPromises = [...this.activeAgents.keys()].map(
10274
+ (key) => new Promise((resolve2) => {
10275
+ const agent = this.activeAgents.get(key);
10276
+ if (!agent) {
10277
+ resolve2();
10278
+ return;
10279
+ }
10280
+ agent.process.on("exit", () => resolve2());
10281
+ handleStopTask(key, this.activeAgents, this.projectDir);
10282
+ })
10283
+ );
10284
+ await Promise.race([
10285
+ Promise.all(stopPromises),
10286
+ new Promise((resolve2) => {
10287
+ setTimeout(resolve2, STOP_TIMEOUT_MS + 5e3);
10288
+ })
10289
+ ]);
10290
+ await this.flushGitOnShutdown(flushDirs);
10291
+ try {
10292
+ await this.connection.call(
10293
+ "disconnectProjectRunner",
10294
+ { projectId: this.connection.projectId },
10295
+ { timeoutMs: DISCONNECT_ACK_TIMEOUT_MS }
10296
+ );
10297
+ } catch {
10298
+ }
10299
+ logger10.info("Shutdown complete");
10300
+ } finally {
10301
+ clearTimeout(watchdog);
10302
+ this.finishShutdown();
10303
+ }
10304
+ }
10305
+ /** Idempotent final step: tear down the socket and resolve the lifecycle
10306
+ * promise so start() returns. Called from stop()'s finally and from the
10307
+ * shutdown watchdog. */
10308
+ finishShutdown() {
10309
+ try {
10310
+ this.connection.disconnect();
10194
10311
  } catch {
10195
10312
  }
10196
- this.connection.disconnect();
10197
- logger10.info("Shutdown complete");
10198
10313
  if (this.resolveLifecycle) {
10199
10314
  this.resolveLifecycle();
10200
10315
  this.resolveLifecycle = null;
@@ -10268,7 +10383,7 @@ var ProjectRunner = class {
10268
10383
  async handleAuditTags(request) {
10269
10384
  this.connection.emitStatus("busy");
10270
10385
  try {
10271
- const { handleTagAudit } = await import("./tag-audit-handler-PKYLDJHH.js");
10386
+ const { handleTagAudit } = await import("./tag-audit-handler-3IFB7YDV.js");
10272
10387
  await handleTagAudit(request, this.connection, this.projectDir);
10273
10388
  } catch (error) {
10274
10389
  const msg = parseErrorMessage(error);
@@ -10291,7 +10406,7 @@ var ProjectRunner = class {
10291
10406
  async handleAuditTasks(request) {
10292
10407
  this.connection.emitStatus("busy");
10293
10408
  try {
10294
- const { handleTaskAudit } = await import("./task-audit-handler-WINYLZMU.js");
10409
+ const { handleTaskAudit } = await import("./task-audit-handler-U5Q52YT2.js");
10295
10410
  await handleTaskAudit(request, this.connection, this.projectDir);
10296
10411
  } catch (error) {
10297
10412
  const msg = parseErrorMessage(error);
@@ -10432,4 +10547,4 @@ export {
10432
10547
  loadConveyorConfig,
10433
10548
  unshallowRepo
10434
10549
  };
10435
- //# sourceMappingURL=chunk-3T4SJEH6.js.map
10550
+ //# sourceMappingURL=chunk-WZCO64YR.js.map