@rallycry/conveyor-agent 5.12.0 → 6.0.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.
@@ -427,23 +427,6 @@ var ConveyorConnection = class _ConveyorConnection {
427
427
  triggerIdentification() {
428
428
  return triggerIdentification(this.socket);
429
429
  }
430
- async refreshAuthToken() {
431
- const codespaceName = process.env.CODESPACE_NAME;
432
- const apiUrl = process.env.CONVEYOR_API_URL ?? this.config.conveyorApiUrl;
433
- if (!codespaceName || !apiUrl) return false;
434
- try {
435
- const response = await fetch(`${apiUrl}/api/codespace/bootstrap/${codespaceName}`);
436
- if (!response.ok) return false;
437
- const config = await response.json();
438
- if (config.envVars?.CLAUDE_CODE_OAUTH_TOKEN) {
439
- process.env.CLAUDE_CODE_OAUTH_TOKEN = config.envVars.CLAUDE_CODE_OAUTH_TOKEN;
440
- return true;
441
- }
442
- return false;
443
- } catch {
444
- return false;
445
- }
446
- }
447
430
  emitModeTransition(payload) {
448
431
  if (!this.socket) return;
449
432
  this.socket.emit("agentRunner:modeTransition", payload);
@@ -471,16 +454,9 @@ var ProjectConnection = class {
471
454
  shutdownCallback = null;
472
455
  chatMessageCallback = null;
473
456
  earlyChatMessages = [];
474
- auditRequestCallback = null;
475
- // Branch switching callbacks
476
- onSwitchBranch = null;
477
- onSyncEnvironment = null;
478
- onGetEnvStatus = null;
479
- onRestartStartCommand = null;
480
457
  constructor(config) {
481
458
  this.config = config;
482
459
  }
483
- // oxlint-disable-next-line max-lines-per-function -- socket event registration requires co-located handlers
484
460
  connect() {
485
461
  return new Promise((resolve2, reject) => {
486
462
  let settled = false;
@@ -520,37 +496,6 @@ var ProjectConnection = class {
520
496
  this.earlyChatMessages.push(msg);
521
497
  }
522
498
  });
523
- this.socket.on("projectRunner:auditTags", (data) => {
524
- if (this.auditRequestCallback) {
525
- this.auditRequestCallback(data);
526
- }
527
- });
528
- this.socket.on(
529
- "projectRunner:switchBranch",
530
- (data, cb) => {
531
- if (this.onSwitchBranch) this.onSwitchBranch(data, cb);
532
- else cb({ ok: false, error: "switchBranch handler not registered" });
533
- }
534
- );
535
- this.socket.on("projectRunner:syncEnvironment", (cb) => {
536
- if (this.onSyncEnvironment) this.onSyncEnvironment(cb);
537
- else cb({ ok: false, error: "syncEnvironment handler not registered" });
538
- });
539
- this.socket.on("projectRunner:getEnvStatus", (cb) => {
540
- if (this.onGetEnvStatus) this.onGetEnvStatus(cb);
541
- else cb({ ok: false, data: void 0 });
542
- });
543
- this.socket.on(
544
- "projectRunner:restartStartCommand",
545
- (_data, cb) => {
546
- if (this.onRestartStartCommand) this.onRestartStartCommand(cb);
547
- else cb({ ok: false, error: "restartStartCommand handler not registered" });
548
- }
549
- );
550
- this.socket.on(
551
- "projectRunner:runAuthTokenCommand",
552
- (data, cb) => this.handleRunAuthTokenCommand(data.userEmail, cb)
553
- );
554
499
  this.socket.on("connect", () => {
555
500
  if (!settled) {
556
501
  settled = true;
@@ -582,17 +527,6 @@ var ProjectConnection = class {
582
527
  }
583
528
  this.earlyChatMessages = [];
584
529
  }
585
- onAuditRequest(callback) {
586
- this.auditRequestCallback = callback;
587
- }
588
- emitAuditResult(data) {
589
- if (!this.socket) return;
590
- this.socket.emit("conveyor:tagAuditResult", data);
591
- }
592
- emitAuditProgress(data) {
593
- if (!this.socket) return;
594
- this.socket.emit("conveyor:tagAuditProgress", data);
595
- }
596
530
  sendHeartbeat() {
597
531
  if (!this.socket) return;
598
532
  this.socket.emit("projectRunner:heartbeat", {});
@@ -640,13 +574,13 @@ var ProjectConnection = class {
640
574
  );
641
575
  });
642
576
  }
643
- fetchChatHistory(limit, chatId) {
577
+ fetchChatHistory(limit) {
644
578
  const socket = this.socket;
645
579
  if (!socket) return Promise.reject(new Error("Not connected"));
646
580
  return new Promise((resolve2, reject) => {
647
581
  socket.emit(
648
582
  "projectRunner:getChatHistory",
649
- { limit, chatId },
583
+ { limit },
650
584
  (response) => {
651
585
  if (response.success && response.data) resolve2(response.data);
652
586
  else reject(new Error(response.error ?? "Failed to fetch chat history"));
@@ -654,84 +588,39 @@ var ProjectConnection = class {
654
588
  );
655
589
  });
656
590
  }
657
- // ── Project MCP tool request methods ──
658
- requestListTasks(params) {
659
- return this.requestWithCallback("projectRunner:listTasks", params);
660
- }
661
- requestGetTask(taskId) {
662
- return this.requestWithCallback("projectRunner:getTask", { taskId });
663
- }
664
- requestCreateTask(params) {
665
- return this.requestWithCallback("projectRunner:createTask", params);
666
- }
667
- requestUpdateTask(params) {
668
- return this.requestWithCallback("projectRunner:updateTask", params);
669
- }
670
- requestSearchTasks(params) {
671
- return this.requestWithCallback("projectRunner:searchTasks", params);
672
- }
673
- requestListTags() {
674
- return this.requestWithCallback("projectRunner:listTags", {});
675
- }
676
- requestGetProjectSummary() {
677
- return this.requestWithCallback("projectRunner:getProjectSummary", {});
678
- }
679
- requestWithCallback(event, data) {
680
- const socket = this.socket;
681
- if (!socket) return Promise.reject(new Error("Not connected"));
682
- return new Promise((resolve2, reject) => {
683
- socket.emit(event, data, (response) => {
684
- if (response.success) resolve2(response.data);
685
- else reject(new Error(response.error ?? `${event} failed`));
686
- });
687
- });
688
- }
689
- emitNewCommitsDetected(data) {
690
- if (!this.socket) return;
691
- this.socket.emit("projectRunner:newCommitsDetected", data);
692
- }
693
- emitEnvironmentReady(data) {
694
- if (!this.socket) return;
695
- this.socket.emit("projectRunner:environmentReady", data);
696
- }
697
- emitEnvSwitchProgress(data) {
698
- if (!this.socket) return;
699
- this.socket.emit("projectRunner:envSwitchProgress", data);
700
- }
701
- handleRunAuthTokenCommand(userEmail, cb) {
702
- try {
703
- if (process.env.CODESPACES !== "true") {
704
- cb({ ok: false, error: "Auth token command only available in codespace environments" });
705
- return;
706
- }
707
- const authCmd = process.env.CONVEYOR_AUTH_TOKEN_COMMAND;
708
- if (!authCmd) {
709
- cb({ ok: false, error: "CONVEYOR_AUTH_TOKEN_COMMAND not configured" });
710
- return;
711
- }
712
- const cwd = this.config.projectDir ?? process.cwd();
713
- const token = runAuthTokenCommand(authCmd, userEmail, cwd);
714
- if (!token) {
715
- cb({
716
- ok: false,
717
- error: `Auth token command returned empty output. Command: ${authCmd}`
718
- });
719
- return;
720
- }
721
- cb({ ok: true, token });
722
- } catch (error) {
723
- cb({
724
- ok: false,
725
- error: error instanceof Error ? error.message : "Auth token command failed"
726
- });
727
- }
728
- }
729
591
  disconnect() {
730
592
  this.socket?.disconnect();
731
593
  this.socket = null;
732
594
  }
733
595
  };
734
596
 
597
+ // src/utils/logger.ts
598
+ import winston from "winston";
599
+ var { combine, timestamp, printf, colorize } = winston.format;
600
+ var customFormat = printf(({ level, message, timestamp: ts, service, ...meta }) => {
601
+ const svc = service ? `[${service}] ` : "";
602
+ const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
603
+ return `${ts} [${level}]: ${svc}${message}${metaStr}`;
604
+ });
605
+ var logger = winston.createLogger({
606
+ level: process.env.LOG_LEVEL ?? "info",
607
+ format: combine(timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), combine(colorize(), customFormat)),
608
+ transports: [new winston.transports.Console()]
609
+ });
610
+ function createServiceLogger(serviceName) {
611
+ return logger.child({ service: serviceName });
612
+ }
613
+ function errorMeta(error) {
614
+ if (error instanceof Error) {
615
+ return {
616
+ message: error.message,
617
+ stack: error.stack,
618
+ ..."code" in error && typeof error.code === "string" ? { code: error.code } : {}
619
+ };
620
+ }
621
+ return { message: String(error) };
622
+ }
623
+
735
624
  // src/runner/worktree.ts
736
625
  import { execSync as execSync2 } from "child_process";
737
626
  import { existsSync } from "fs";
@@ -813,46 +702,11 @@ function unshallowRepo(workspaceDir) {
813
702
  }
814
703
  }
815
704
 
816
- // src/utils/logger.ts
817
- import winston from "winston";
818
- var { combine, timestamp, printf, colorize } = winston.format;
819
- var customFormat = printf(({ level, message, timestamp: ts, service, ...meta }) => {
820
- const svc = service ? `[${service}] ` : "";
821
- const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
822
- return `${ts} [${level}]: ${svc}${message}${metaStr}`;
823
- });
824
- var logger = winston.createLogger({
825
- level: process.env.LOG_LEVEL ?? "info",
826
- format: combine(timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), combine(colorize(), customFormat)),
827
- transports: [new winston.transports.Console()]
828
- });
829
- function createServiceLogger(serviceName) {
830
- return logger.child({ service: serviceName });
831
- }
832
- function errorMeta(error) {
833
- if (error instanceof Error) {
834
- return {
835
- message: error.message,
836
- stack: error.stack,
837
- ..."code" in error && typeof error.code === "string" ? { code: error.code } : {}
838
- };
839
- }
840
- return { message: String(error) };
841
- }
842
-
843
705
  // src/runner/agent-runner.ts
844
706
  import { randomUUID as randomUUID2 } from "crypto";
845
707
  import { execSync as execSync4 } from "child_process";
846
708
 
847
709
  // src/execution/event-handlers.ts
848
- function safeVoid(promise, context) {
849
- if (promise && typeof promise.catch === "function") {
850
- promise.catch((err) => {
851
- process.stderr.write(`[safeVoid] ${context}: ${err}
852
- `);
853
- });
854
- }
855
- }
856
710
  function epochSecondsToISO(value) {
857
711
  if (typeof value === "string") return value;
858
712
  if (typeof value !== "number" || value <= 0) return void 0;
@@ -891,10 +745,6 @@ async function processAssistantEvent(event, host, turnToolCalls) {
891
745
  }
892
746
  var API_ERROR_PATTERN = /API Error: [45]\d\d/;
893
747
  var IMAGE_ERROR_PATTERN = /Could not process image/i;
894
- var AUTH_ERROR_PATTERN = /Not logged in|Please run \/login|authentication failed|invalid.*token|unauthorized/i;
895
- function isAuthError(msg) {
896
- return AUTH_ERROR_PATTERN.test(msg);
897
- }
898
748
  function isRetriableMessage(msg) {
899
749
  if (IMAGE_ERROR_PATTERN.test(msg)) return true;
900
750
  if (API_ERROR_PATTERN.test(msg)) return true;
@@ -975,10 +825,6 @@ function handleErrorResult(event, host) {
975
825
  if (isStaleSession) {
976
826
  return { retriable: false, staleSession: true };
977
827
  }
978
- if (isAuthError(errorMsg)) {
979
- host.connection.sendEvent({ type: "error", message: errorMsg });
980
- return { retriable: false, authError: true };
981
- }
982
828
  const retriable = isRetriableMessage(errorMsg);
983
829
  host.connection.sendEvent({ type: "error", message: errorMsg });
984
830
  return { retriable };
@@ -1018,8 +864,7 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
1018
864
  return {
1019
865
  retriable: result.retriable,
1020
866
  resultSummary: result.resultSummary,
1021
- staleSession: result.staleSession,
1022
- authError: result.authError
867
+ staleSession: result.staleSession
1023
868
  };
1024
869
  }
1025
870
  function handleRateLimitEvent(event, host) {
@@ -1038,13 +883,13 @@ function handleRateLimitEvent(event, host) {
1038
883
  const resetsAtDisplay = resetsAt ?? "unknown";
1039
884
  const message = `Rate limit rejected (type: ${rate_limit_info.rateLimitType ?? "unknown"}, resets at: ${resetsAtDisplay})`;
1040
885
  host.connection.sendEvent({ type: "error", message });
1041
- safeVoid(host.callbacks.onEvent({ type: "error", message }), "rateLimitRejected");
886
+ void host.callbacks.onEvent({ type: "error", message });
1042
887
  return resetsAt;
1043
888
  } else if (status === "allowed_warning") {
1044
889
  const utilization = rate_limit_info.utilization ? `${Math.round(rate_limit_info.utilization * 100)}%` : "high";
1045
890
  const message = `Rate limit warning: ${utilization} utilization (type: ${rate_limit_info.rateLimitType ?? "unknown"})`;
1046
891
  host.connection.sendEvent({ type: "thinking", message });
1047
- safeVoid(host.callbacks.onEvent({ type: "thinking", message }), "rateLimitWarning");
892
+ void host.callbacks.onEvent({ type: "thinking", message });
1048
893
  }
1049
894
  return void 0;
1050
895
  }
@@ -1062,46 +907,34 @@ async function handleSystemEvent(event, host, context, sessionIdStored) {
1062
907
  }
1063
908
  function handleSystemSubevents(systemEvent, host) {
1064
909
  if (systemEvent.subtype === "compact_boundary") {
1065
- safeVoid(
1066
- host.callbacks.onEvent({
1067
- type: "context_compacted",
1068
- trigger: systemEvent.compact_metadata.trigger,
1069
- preTokens: systemEvent.compact_metadata.pre_tokens
1070
- }),
1071
- "compactBoundary"
1072
- );
910
+ void host.callbacks.onEvent({
911
+ type: "context_compacted",
912
+ trigger: systemEvent.compact_metadata.trigger,
913
+ preTokens: systemEvent.compact_metadata.pre_tokens
914
+ });
1073
915
  } else if (systemEvent.subtype === "task_started") {
1074
- safeVoid(
1075
- host.callbacks.onEvent({
1076
- type: "subagent_started",
1077
- sdkTaskId: systemEvent.task_id,
1078
- description: systemEvent.description
1079
- }),
1080
- "taskStarted"
1081
- );
916
+ void host.callbacks.onEvent({
917
+ type: "subagent_started",
918
+ sdkTaskId: systemEvent.task_id,
919
+ description: systemEvent.description
920
+ });
1082
921
  } else if (systemEvent.subtype === "task_progress") {
1083
- safeVoid(
1084
- host.callbacks.onEvent({
1085
- type: "subagent_progress",
1086
- sdkTaskId: systemEvent.task_id,
1087
- description: systemEvent.description,
1088
- toolUses: systemEvent.usage?.tool_uses ?? 0,
1089
- durationMs: systemEvent.usage?.duration_ms ?? 0
1090
- }),
1091
- "taskProgress"
1092
- );
922
+ void host.callbacks.onEvent({
923
+ type: "subagent_progress",
924
+ sdkTaskId: systemEvent.task_id,
925
+ description: systemEvent.description,
926
+ toolUses: systemEvent.usage?.tool_uses ?? 0,
927
+ durationMs: systemEvent.usage?.duration_ms ?? 0
928
+ });
1093
929
  }
1094
930
  }
1095
931
  function handleToolProgressEvent(event, host) {
1096
932
  const msg = event;
1097
- safeVoid(
1098
- host.callbacks.onEvent({
1099
- type: "tool_progress",
1100
- toolName: msg.tool_name ?? "",
1101
- elapsedSeconds: msg.elapsed_time_seconds ?? 0
1102
- }),
1103
- "toolProgress"
1104
- );
933
+ void host.callbacks.onEvent({
934
+ type: "tool_progress",
935
+ toolName: msg.tool_name ?? "",
936
+ elapsedSeconds: msg.elapsed_time_seconds ?? 0
937
+ });
1105
938
  }
1106
939
  async function handleAssistantCase(event, host, turnToolCalls) {
1107
940
  await processAssistantEvent(event, host, turnToolCalls);
@@ -1119,13 +952,11 @@ async function handleResultCase(event, host, context, startTime, isTyping, lastA
1119
952
  retriable: resultInfo.retriable,
1120
953
  resultSummary: resultInfo.resultSummary,
1121
954
  staleSession: resultInfo.staleSession,
1122
- authError: resultInfo.authError,
1123
955
  stoppedTyping
1124
956
  };
1125
957
  }
1126
958
 
1127
959
  // src/execution/event-processor.ts
1128
- var API_ERROR_PATTERN2 = /API Error: [45]\d\d/;
1129
960
  function stopTypingIfNeeded(host, isTyping) {
1130
961
  if (isTyping) host.connection.sendTypingStop();
1131
962
  }
@@ -1147,12 +978,6 @@ async function processAssistantCase(event, host, state) {
1147
978
  }
1148
979
  const usage = await handleAssistantCase(event, host, state.turnToolCalls);
1149
980
  if (usage) state.lastAssistantUsage = usage;
1150
- if (!state.sawApiError) {
1151
- const fullText = event.message.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
1152
- if (API_ERROR_PATTERN2.test(fullText)) {
1153
- state.sawApiError = true;
1154
- }
1155
- }
1156
981
  }
1157
982
  async function processResultCase(event, host, context, startTime, state) {
1158
983
  const info = await handleResultCase(
@@ -1167,7 +992,6 @@ async function processResultCase(event, host, context, startTime, state) {
1167
992
  state.retriable = info.retriable;
1168
993
  state.resultSummary = info.resultSummary;
1169
994
  if (info.staleSession) state.staleSession = true;
1170
- if (info.authError) state.authError = true;
1171
995
  }
1172
996
  async function processEvents(events, context, host) {
1173
997
  const startTime = Date.now();
@@ -1177,11 +1001,9 @@ async function processEvents(events, context, host) {
1177
1001
  sessionIdStored: false,
1178
1002
  isTyping: false,
1179
1003
  retriable: false,
1180
- sawApiError: false,
1181
1004
  resultSummary: void 0,
1182
1005
  rateLimitResetsAt: void 0,
1183
1006
  staleSession: void 0,
1184
- authError: void 0,
1185
1007
  lastAssistantUsage: void 0,
1186
1008
  turnToolCalls: []
1187
1009
  };
@@ -1218,11 +1040,10 @@ async function processEvents(events, context, host) {
1218
1040
  }
1219
1041
  stopTypingIfNeeded(host, state.isTyping);
1220
1042
  return {
1221
- retriable: state.retriable || state.sawApiError,
1043
+ retriable: state.retriable,
1222
1044
  resultSummary: state.resultSummary,
1223
1045
  rateLimitResetsAt: state.rateLimitResetsAt,
1224
- ...state.staleSession && { staleSession: state.staleSession },
1225
- ...state.authError && { authError: state.authError }
1046
+ ...state.staleSession && { staleSession: state.staleSession }
1226
1047
  };
1227
1048
  }
1228
1049
 
@@ -1580,8 +1401,6 @@ function buildDiscoveryPrompt(context) {
1580
1401
  `You are in Discovery mode \u2014 helping plan and scope this task.`,
1581
1402
  `- You have read-only codebase access (can read files, run git commands, search code)`,
1582
1403
  `- You can write plan files in .claude/plans/ only \u2014 no other file writes`,
1583
- `- Do NOT attempt to edit, write, or modify source code files \u2014 these operations will be denied`,
1584
- `- If you identify code changes needed, describe them in the plan instead of implementing them`,
1585
1404
  `- You can create and manage subtasks`,
1586
1405
  `- Goal: collaborate with the user to create a clear plan`,
1587
1406
  `- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
@@ -1716,14 +1535,6 @@ Project Agents:`);
1716
1535
  parts.push(formatProjectAgentLine(pa));
1717
1536
  }
1718
1537
  }
1719
- if (context.projectObjectives && context.projectObjectives.length > 0) {
1720
- parts.push(`
1721
- Project Objectives:`);
1722
- for (const obj of context.projectObjectives) {
1723
- const dates = `${obj.startDate.split("T")[0]} to ${obj.endDate.split("T")[0]}`;
1724
- parts.push(`- **${obj.name}** (${dates})${obj.description ? ": " + obj.description : ""}`);
1725
- }
1726
- }
1727
1538
  return parts;
1728
1539
  }
1729
1540
  function buildActivePreamble(context, workspaceDir) {
@@ -1846,7 +1657,7 @@ function detectRelaunchScenario(context, trustChatHistory = false) {
1846
1657
  const hasNewUserMessages = messagesAfterAgent.some((m) => m.role === "user");
1847
1658
  return hasNewUserMessages ? "feedback_relaunch" : "idle_relaunch";
1848
1659
  }
1849
- function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
1660
+ function buildRelaunchWithSession(mode, context, agentMode) {
1850
1661
  const scenario = detectRelaunchScenario(context);
1851
1662
  if (!context.claudeSessionId || scenario === "fresh") return null;
1852
1663
  const parts = [];
@@ -1894,7 +1705,7 @@ Address the requested changes. Do NOT re-investigate the codebase from scratch o
1894
1705
  `Run \`git log --oneline -10\` to review what you already committed.`,
1895
1706
  `Review the current state of the codebase and verify everything is working correctly.`
1896
1707
  );
1897
- if (agentMode === "auto" || agentMode === "building" && isAuto) {
1708
+ if (agentMode === "auto" || agentMode === "building") {
1898
1709
  parts.push(
1899
1710
  `If work is incomplete, continue implementing the plan. When finished, commit, push, and use mcp__conveyor__create_pull_request to open a PR.`,
1900
1711
  `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
@@ -2100,7 +1911,7 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
2100
1911
  }
2101
1912
  return parts;
2102
1913
  }
2103
- function buildInstructions(mode, context, scenario, agentMode, isAuto) {
1914
+ function buildInstructions(mode, context, scenario, agentMode) {
2104
1915
  const parts = [`
2105
1916
  ## Instructions`];
2106
1917
  const isPm = mode === "pm";
@@ -2132,7 +1943,7 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2132
1943
  `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
2133
1944
  `Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`
2134
1945
  );
2135
- if (agentMode === "auto" || agentMode === "building" && isAuto) {
1946
+ if (agentMode === "auto" || agentMode === "building") {
2136
1947
  parts.push(
2137
1948
  `If work is incomplete, continue implementing the plan. When finished, commit, push, and use mcp__conveyor__create_pull_request to open a PR.`,
2138
1949
  `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
@@ -2155,13 +1966,13 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2155
1966
  async function buildInitialPrompt(mode, context, isAuto, agentMode) {
2156
1967
  const isPackRunner = mode === "pm" && !!isAuto && !!context.isParentTask;
2157
1968
  if (!isPackRunner) {
2158
- const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode, isAuto);
1969
+ const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode);
2159
1970
  if (sessionRelaunch) return sessionRelaunch;
2160
1971
  }
2161
1972
  const isPm = mode === "pm";
2162
1973
  const scenario = detectRelaunchScenario(context, isPm);
2163
1974
  const body = await buildTaskBody(context);
2164
- const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode, isAuto);
1975
+ const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode);
2165
1976
  return [...body, ...instructions].join("\n");
2166
1977
  }
2167
1978
 
@@ -2423,14 +2234,11 @@ function buildCreatePullRequestTool(connection) {
2423
2234
  "Create a GitHub pull request for this task. Use this instead of gh CLI or git commands to create PRs.",
2424
2235
  {
2425
2236
  title: z.string().describe("The PR title"),
2426
- body: z.string().describe("The PR description/body in markdown"),
2427
- branch: z.string().optional().describe(
2428
- "The head branch name for the PR. If the task doesn't have a branch set, this will be used. Defaults to the task's existing branch."
2429
- )
2237
+ body: z.string().describe("The PR description/body in markdown")
2430
2238
  },
2431
- async ({ title, body, branch }) => {
2239
+ async ({ title, body }) => {
2432
2240
  try {
2433
- const result = await connection.createPR({ title, body, branch });
2241
+ const result = await connection.createPR({ title, body });
2434
2242
  connection.sendEvent({
2435
2243
  type: "pr_created",
2436
2244
  url: result.url,
@@ -2918,9 +2726,7 @@ async function handleAskUserQuestion(host, input) {
2918
2726
  }
2919
2727
  return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
2920
2728
  }
2921
- var DENIAL_WARNING_THRESHOLD = 3;
2922
2729
  function buildCanUseTool(host) {
2923
- let consecutiveDenials = 0;
2924
2730
  return async (toolName, input) => {
2925
2731
  if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
2926
2732
  return await handleExitPlanMode(host, input);
@@ -2928,39 +2734,24 @@ function buildCanUseTool(host) {
2928
2734
  if (toolName === "AskUserQuestion") {
2929
2735
  return await handleAskUserQuestion(host, input);
2930
2736
  }
2931
- let result;
2932
2737
  switch (host.agentMode) {
2933
2738
  case "discovery":
2934
- result = handleDiscoveryToolAccess(toolName, input);
2935
- break;
2739
+ return handleDiscoveryToolAccess(toolName, input);
2936
2740
  case "building":
2937
- result = handleBuildingToolAccess(toolName, input);
2938
- break;
2741
+ return handleBuildingToolAccess(toolName, input);
2939
2742
  case "review":
2940
- result = handleReviewToolAccess(toolName, input, host.isParentTask);
2941
- break;
2743
+ return handleReviewToolAccess(toolName, input, host.isParentTask);
2942
2744
  case "auto":
2943
- result = handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
2944
- break;
2745
+ return handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
2945
2746
  default:
2946
- result = { behavior: "allow", updatedInput: input };
2747
+ return { behavior: "allow", updatedInput: input };
2947
2748
  }
2948
- if (result.behavior === "deny") {
2949
- consecutiveDenials++;
2950
- if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
2951
- host.connection.postChatMessage(
2952
- `\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ are not permitted. Focus on creating a plan instead of implementing code changes.`
2953
- );
2954
- }
2955
- } else {
2956
- consecutiveDenials = 0;
2957
- }
2958
- return result;
2959
2749
  };
2960
2750
  }
2961
2751
 
2962
2752
  // src/execution/query-executor.ts
2963
- var API_ERROR_PATTERN3 = /API Error: [45]\d\d/;
2753
+ var logger2 = createServiceLogger("QueryExecutor");
2754
+ var API_ERROR_PATTERN2 = /API Error: [45]\d\d/;
2964
2755
  var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
2965
2756
  var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
2966
2757
  function buildHooks(host) {
@@ -3048,7 +2839,10 @@ function buildQueryOptions(host, context) {
3048
2839
  betas: settings.betas,
3049
2840
  maxBudgetUsd: settings.maxBudgetUsd ?? 50,
3050
2841
  disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
3051
- enableFileCheckpointing: settings.enableFileCheckpointing
2842
+ enableFileCheckpointing: settings.enableFileCheckpointing,
2843
+ stderr: (data) => {
2844
+ logger2.warn("Claude Code stderr", { data: data.trimEnd() });
2845
+ }
3052
2846
  };
3053
2847
  if (isCloud && isReadOnly) {
3054
2848
  baseOptions.sandbox = buildSandboxConfig(host);
@@ -3177,29 +2971,6 @@ async function buildRetryQuery(host, context, options, lastErrorWasImage) {
3177
2971
  options: { ...options, resume: void 0 }
3178
2972
  });
3179
2973
  }
3180
- async function handleAuthError(context, host, options) {
3181
- host.connection.postChatMessage("Authentication expired. Re-bootstrapping credentials...");
3182
- const refreshed = await host.connection.refreshAuthToken();
3183
- if (!refreshed) {
3184
- host.connection.postChatMessage("Failed to refresh authentication. Agent will restart.");
3185
- host.connection.sendEvent({
3186
- type: "error",
3187
- message: "Auth re-bootstrap failed, exiting for restart"
3188
- });
3189
- process.exit(1);
3190
- }
3191
- context.claudeSessionId = null;
3192
- host.connection.storeSessionId("");
3193
- const freshPrompt = buildMultimodalPrompt(
3194
- await buildInitialPrompt(host.config.mode, context, host.config.isAuto, host.agentMode),
3195
- context
3196
- );
3197
- const freshQuery = query({
3198
- prompt: host.createInputStream(freshPrompt),
3199
- options: { ...options, resume: void 0 }
3200
- });
3201
- return runWithRetry(freshQuery, context, host, options);
3202
- }
3203
2974
  async function handleStaleSession(context, host, options) {
3204
2975
  context.claudeSessionId = null;
3205
2976
  host.connection.storeSessionId("");
@@ -3231,17 +3002,12 @@ function isStaleOrExitedSession(error, context) {
3231
3002
  if (error.message.includes("No conversation found with session ID")) return true;
3232
3003
  return !!context.claudeSessionId && error.message.includes("process exited");
3233
3004
  }
3234
- function getErrorMessage(error) {
3235
- if (error instanceof Error) return error.message;
3236
- if (typeof error === "string") return error;
3237
- return String(error);
3238
- }
3239
3005
  function isRetriableError(error) {
3240
- const message = getErrorMessage(error);
3241
- return API_ERROR_PATTERN3.test(message) || IMAGE_ERROR_PATTERN2.test(message);
3006
+ if (!(error instanceof Error)) return false;
3007
+ return API_ERROR_PATTERN2.test(error.message) || IMAGE_ERROR_PATTERN2.test(error.message);
3242
3008
  }
3243
3009
  function classifyImageError(error) {
3244
- return IMAGE_ERROR_PATTERN2.test(getErrorMessage(error));
3010
+ return error instanceof Error && IMAGE_ERROR_PATTERN2.test(error.message);
3245
3011
  }
3246
3012
  async function emitRetryStatus(host, attempt, delayMs) {
3247
3013
  const delayMin = Math.round(delayMs / 6e4);
@@ -3268,41 +3034,26 @@ function handleRetryError(error, context, host, options, prevImageError) {
3268
3034
  if (isStaleOrExitedSession(error, context) && context.claudeSessionId) {
3269
3035
  return handleStaleSession(context, host, options);
3270
3036
  }
3271
- if (isAuthError(getErrorMessage(error))) {
3272
- return handleAuthError(context, host, options);
3273
- }
3274
3037
  if (!isRetriableError(error)) throw error;
3275
3038
  return { action: "continue", lastErrorWasImage: classifyImageError(error) || prevImageError };
3276
3039
  }
3277
- function handleProcessResult(result, context, host, options) {
3278
- if (result.modeRestart || host.isStopped()) return { action: "return" };
3279
- if (result.rateLimitResetsAt) {
3280
- handleRateLimitPause(host, result.rateLimitResetsAt);
3281
- return { action: "return" };
3282
- }
3283
- if (result.staleSession && context.claudeSessionId) {
3284
- return { action: "return_promise", promise: handleStaleSession(context, host, options) };
3285
- }
3286
- if (result.authError) {
3287
- return { action: "return_promise", promise: handleAuthError(context, host, options) };
3288
- }
3289
- if (!result.retriable) return { action: "return" };
3290
- return {
3291
- action: "continue",
3292
- lastErrorWasImage: IMAGE_ERROR_PATTERN2.test(result.resultSummary ?? "")
3293
- };
3294
- }
3295
3040
  async function runWithRetry(initialQuery, context, host, options) {
3296
3041
  let lastErrorWasImage = false;
3297
3042
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
3298
3043
  if (host.isStopped()) return;
3299
3044
  const agentQuery = attempt === 0 ? initialQuery : await buildRetryQuery(host, context, options, lastErrorWasImage);
3300
3045
  try {
3301
- const result = await processEvents(agentQuery, context, host);
3302
- const outcome = handleProcessResult(result, context, host, options);
3303
- if (outcome.action === "return") return;
3304
- if (outcome.action === "return_promise") return outcome.promise;
3305
- lastErrorWasImage = outcome.lastErrorWasImage;
3046
+ const { retriable, resultSummary, modeRestart, rateLimitResetsAt, staleSession } = await processEvents(agentQuery, context, host);
3047
+ if (modeRestart || host.isStopped()) return;
3048
+ if (rateLimitResetsAt) {
3049
+ handleRateLimitPause(host, rateLimitResetsAt);
3050
+ return;
3051
+ }
3052
+ if (staleSession && context.claudeSessionId) {
3053
+ return handleStaleSession(context, host, options);
3054
+ }
3055
+ if (!retriable) return;
3056
+ lastErrorWasImage = IMAGE_ERROR_PATTERN2.test(resultSummary ?? "");
3306
3057
  } catch (error) {
3307
3058
  const outcome = handleRetryError(error, context, host, options, lastErrorWasImage);
3308
3059
  if (outcome instanceof Promise) return outcome;
@@ -3682,7 +3433,7 @@ function buildQueryHost(deps) {
3682
3433
  }
3683
3434
 
3684
3435
  // src/runner/agent-runner.ts
3685
- var logger2 = createServiceLogger("AgentRunner");
3436
+ var logger3 = createServiceLogger("AgentRunner");
3686
3437
  var HEARTBEAT_INTERVAL_MS = 3e4;
3687
3438
  var IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
3688
3439
  var AgentRunner = class {
@@ -3971,7 +3722,7 @@ var AgentRunner = class {
3971
3722
  const s = this.taskContext.agentSettings ?? this.config.agentSettings ?? {};
3972
3723
  const model = this.taskContext.model || this.config.model;
3973
3724
  const thinking = formatThinkingSetting(s.thinking);
3974
- logger2.info("Effective agent settings", {
3725
+ logger3.info("Effective agent settings", {
3975
3726
  model,
3976
3727
  mode: this.config.mode ?? "task",
3977
3728
  effort: s.effort ?? "default",
@@ -4015,7 +3766,7 @@ var AgentRunner = class {
4015
3766
  this.idleTimer = setTimeout(() => {
4016
3767
  this.clearIdleTimers();
4017
3768
  this.inputResolver = null;
4018
- logger2.info("Idle timeout reached, shutting down", {
3769
+ logger3.info("Idle timeout reached, shutting down", {
4019
3770
  idleMinutes: IDLE_TIMEOUT_MS / 6e4
4020
3771
  });
4021
3772
  this.connection.postChatMessage(
@@ -4132,269 +3883,12 @@ var AgentRunner = class {
4132
3883
 
4133
3884
  // src/runner/project-runner.ts
4134
3885
  import { fork } from "child_process";
4135
- import { execSync as execSync6 } from "child_process";
3886
+ import { execSync as execSync5 } from "child_process";
4136
3887
  import * as path from "path";
4137
3888
  import { fileURLToPath } from "url";
4138
3889
 
4139
- // src/runner/commit-watcher.ts
4140
- import { execSync as execSync5 } from "child_process";
4141
- var logger3 = createServiceLogger("CommitWatcher");
4142
- var CommitWatcher = class {
4143
- constructor(config, callbacks) {
4144
- this.config = config;
4145
- this.callbacks = callbacks;
4146
- }
4147
- interval = null;
4148
- lastKnownRemoteSha = null;
4149
- branch = null;
4150
- debounceTimer = null;
4151
- isSyncing = false;
4152
- start(branch) {
4153
- this.stop();
4154
- this.branch = branch;
4155
- this.lastKnownRemoteSha = this.getLocalHeadSha();
4156
- this.interval = setInterval(() => void this.poll(), this.config.pollIntervalMs);
4157
- logger3.info("Commit watcher started", {
4158
- branch,
4159
- baseSha: this.lastKnownRemoteSha?.slice(0, 8)
4160
- });
4161
- }
4162
- stop() {
4163
- if (this.interval) clearInterval(this.interval);
4164
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
4165
- this.interval = null;
4166
- this.debounceTimer = null;
4167
- this.branch = null;
4168
- this.lastKnownRemoteSha = null;
4169
- this.isSyncing = false;
4170
- }
4171
- getLocalHeadSha() {
4172
- return execSync5("git rev-parse HEAD", {
4173
- cwd: this.config.projectDir,
4174
- stdio: ["ignore", "pipe", "ignore"]
4175
- }).toString().trim();
4176
- }
4177
- poll() {
4178
- if (!this.branch || this.isSyncing) return;
4179
- try {
4180
- execSync5(`git fetch origin ${this.branch} --quiet`, {
4181
- cwd: this.config.projectDir,
4182
- stdio: "ignore",
4183
- timeout: 3e4
4184
- });
4185
- const remoteSha = execSync5(`git rev-parse origin/${this.branch}`, {
4186
- cwd: this.config.projectDir,
4187
- stdio: ["ignore", "pipe", "ignore"]
4188
- }).toString().trim();
4189
- if (remoteSha !== this.lastKnownRemoteSha) {
4190
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
4191
- this.debounceTimer = setTimeout(
4192
- () => void this.handleNewCommits(remoteSha),
4193
- this.config.debounceMs
4194
- );
4195
- }
4196
- } catch {
4197
- }
4198
- }
4199
- async handleNewCommits(remoteSha) {
4200
- if (!this.branch) return;
4201
- const previousSha = this.lastKnownRemoteSha ?? "HEAD";
4202
- let commitCount = 1;
4203
- let latestMessage = "";
4204
- let latestAuthor = "";
4205
- try {
4206
- const countOutput = execSync5(`git rev-list --count ${previousSha}..origin/${this.branch}`, {
4207
- cwd: this.config.projectDir,
4208
- stdio: ["ignore", "pipe", "ignore"]
4209
- }).toString().trim();
4210
- commitCount = parseInt(countOutput, 10) || 1;
4211
- const logOutput = execSync5(`git log -1 --format="%s|||%an" origin/${this.branch}`, {
4212
- cwd: this.config.projectDir,
4213
- stdio: ["ignore", "pipe", "ignore"]
4214
- }).toString().trim();
4215
- const parts = logOutput.split("|||");
4216
- latestMessage = parts[0] ?? "";
4217
- latestAuthor = parts[1] ?? "";
4218
- } catch {
4219
- }
4220
- this.lastKnownRemoteSha = remoteSha;
4221
- this.isSyncing = true;
4222
- logger3.info("New commits detected", {
4223
- branch: this.branch,
4224
- commitCount,
4225
- sha: remoteSha.slice(0, 8)
4226
- });
4227
- try {
4228
- await this.callbacks.onNewCommits({
4229
- branch: this.branch,
4230
- previousSha,
4231
- newCommitSha: remoteSha,
4232
- commitCount,
4233
- latestMessage,
4234
- latestAuthor
4235
- });
4236
- } catch (err) {
4237
- logger3.error("Error handling new commits", errorMeta(err));
4238
- } finally {
4239
- this.isSyncing = false;
4240
- }
4241
- }
4242
- };
4243
-
4244
- // src/runner/project-chat-handler.ts
4245
- import {
4246
- query as query2,
4247
- createSdkMcpServer as createSdkMcpServer2
4248
- } from "@anthropic-ai/claude-agent-sdk";
4249
-
4250
- // src/tools/project-tools.ts
4251
- import { tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
4252
- import { z as z4 } from "zod";
4253
- function buildReadTools(connection) {
4254
- return [
4255
- tool4(
4256
- "list_tasks",
4257
- "List tasks in the project. Optionally filter by status or assignee.",
4258
- {
4259
- status: z4.string().optional().describe("Filter by task status (e.g. Planning, Open, InProgress, ReviewPR, Complete)"),
4260
- assigneeId: z4.string().optional().describe("Filter by assigned user ID"),
4261
- limit: z4.number().optional().describe("Max number of tasks to return (default 50)")
4262
- },
4263
- async (params) => {
4264
- try {
4265
- const tasks = await connection.requestListTasks(params);
4266
- return textResult(JSON.stringify(tasks, null, 2));
4267
- } catch (error) {
4268
- return textResult(
4269
- `Failed to list tasks: ${error instanceof Error ? error.message : "Unknown error"}`
4270
- );
4271
- }
4272
- },
4273
- { annotations: { readOnlyHint: true } }
4274
- ),
4275
- tool4(
4276
- "get_task",
4277
- "Get detailed information about a task including its chat messages, child tasks, and codespace status.",
4278
- { task_id: z4.string().describe("The task ID to look up") },
4279
- async ({ task_id }) => {
4280
- try {
4281
- const task = await connection.requestGetTask(task_id);
4282
- return textResult(JSON.stringify(task, null, 2));
4283
- } catch (error) {
4284
- return textResult(
4285
- `Failed to get task: ${error instanceof Error ? error.message : "Unknown error"}`
4286
- );
4287
- }
4288
- },
4289
- { annotations: { readOnlyHint: true } }
4290
- ),
4291
- tool4(
4292
- "search_tasks",
4293
- "Search tasks by tags, text query, or status filters.",
4294
- {
4295
- tagNames: z4.array(z4.string()).optional().describe("Filter by tag names"),
4296
- searchQuery: z4.string().optional().describe("Text search in title/description"),
4297
- statusFilters: z4.array(z4.string()).optional().describe("Filter by statuses"),
4298
- limit: z4.number().optional().describe("Max results (default 20)")
4299
- },
4300
- async (params) => {
4301
- try {
4302
- const tasks = await connection.requestSearchTasks(params);
4303
- return textResult(JSON.stringify(tasks, null, 2));
4304
- } catch (error) {
4305
- return textResult(
4306
- `Failed to search tasks: ${error instanceof Error ? error.message : "Unknown error"}`
4307
- );
4308
- }
4309
- },
4310
- { annotations: { readOnlyHint: true } }
4311
- ),
4312
- tool4(
4313
- "list_tags",
4314
- "List all tags available in the project.",
4315
- {},
4316
- async () => {
4317
- try {
4318
- const tags = await connection.requestListTags();
4319
- return textResult(JSON.stringify(tags, null, 2));
4320
- } catch (error) {
4321
- return textResult(
4322
- `Failed to list tags: ${error instanceof Error ? error.message : "Unknown error"}`
4323
- );
4324
- }
4325
- },
4326
- { annotations: { readOnlyHint: true } }
4327
- ),
4328
- tool4(
4329
- "get_project_summary",
4330
- "Get a summary of the project including task counts by status and active builds.",
4331
- {},
4332
- async () => {
4333
- try {
4334
- const summary = await connection.requestGetProjectSummary();
4335
- return textResult(JSON.stringify(summary, null, 2));
4336
- } catch (error) {
4337
- return textResult(
4338
- `Failed to get project summary: ${error instanceof Error ? error.message : "Unknown error"}`
4339
- );
4340
- }
4341
- },
4342
- { annotations: { readOnlyHint: true } }
4343
- )
4344
- ];
4345
- }
4346
- function buildMutationTools(connection) {
4347
- return [
4348
- tool4(
4349
- "create_task",
4350
- "Create a new task in the project.",
4351
- {
4352
- title: z4.string().describe("Task title"),
4353
- description: z4.string().optional().describe("Task description"),
4354
- plan: z4.string().optional().describe("Implementation plan in markdown"),
4355
- status: z4.string().optional().describe("Initial status (default: Planning)"),
4356
- isBug: z4.boolean().optional().describe("Whether this is a bug report")
4357
- },
4358
- async (params) => {
4359
- try {
4360
- const result = await connection.requestCreateTask(params);
4361
- return textResult(`Task created: ${result.slug} (ID: ${result.id})`);
4362
- } catch (error) {
4363
- return textResult(
4364
- `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`
4365
- );
4366
- }
4367
- }
4368
- ),
4369
- tool4(
4370
- "update_task",
4371
- "Update an existing task's title, description, plan, status, or assignee.",
4372
- {
4373
- task_id: z4.string().describe("The task ID to update"),
4374
- title: z4.string().optional().describe("New title"),
4375
- description: z4.string().optional().describe("New description"),
4376
- plan: z4.string().optional().describe("New plan in markdown"),
4377
- status: z4.string().optional().describe("New status"),
4378
- assignedUserId: z4.string().nullable().optional().describe("Assign to user ID, or null to unassign")
4379
- },
4380
- async ({ task_id, ...fields }) => {
4381
- try {
4382
- await connection.requestUpdateTask({ taskId: task_id, ...fields });
4383
- return textResult("Task updated successfully.");
4384
- } catch (error) {
4385
- return textResult(
4386
- `Failed to update task: ${error instanceof Error ? error.message : "Unknown error"}`
4387
- );
4388
- }
4389
- }
4390
- )
4391
- ];
4392
- }
4393
- function buildProjectTools(connection) {
4394
- return [...buildReadTools(connection), ...buildMutationTools(connection)];
4395
- }
4396
-
4397
3890
  // src/runner/project-chat-handler.ts
3891
+ import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
4398
3892
  var logger4 = createServiceLogger("ProjectChat");
4399
3893
  var FALLBACK_MODEL = "claude-sonnet-4-20250514";
4400
3894
  function buildSystemPrompt2(projectDir, agentCtx) {
@@ -4451,7 +3945,7 @@ function processContentBlock(block, responseParts, turnToolCalls) {
4451
3945
  logger4.debug("Tool use", { tool: block.name });
4452
3946
  }
4453
3947
  }
4454
- async function fetchContext(connection, chatId) {
3948
+ async function fetchContext(connection) {
4455
3949
  let agentCtx = null;
4456
3950
  try {
4457
3951
  agentCtx = await connection.fetchAgentContext();
@@ -4460,19 +3954,15 @@ async function fetchContext(connection, chatId) {
4460
3954
  }
4461
3955
  let chatHistory = [];
4462
3956
  try {
4463
- chatHistory = await connection.fetchChatHistory(30, chatId);
3957
+ chatHistory = await connection.fetchChatHistory(30);
4464
3958
  } catch {
4465
3959
  logger4.warn("Could not fetch chat history, proceeding without it");
4466
3960
  }
4467
3961
  return { agentCtx, chatHistory };
4468
3962
  }
4469
- function buildChatQueryOptions(agentCtx, projectDir, connection) {
3963
+ function buildChatQueryOptions(agentCtx, projectDir) {
4470
3964
  const model = agentCtx?.model || FALLBACK_MODEL;
4471
3965
  const settings = agentCtx?.agentSettings ?? {};
4472
- const mcpServer = createSdkMcpServer2({
4473
- name: "conveyor",
4474
- tools: buildProjectTools(connection)
4475
- });
4476
3966
  return {
4477
3967
  model,
4478
3968
  systemPrompt: {
@@ -4484,48 +3974,12 @@ function buildChatQueryOptions(agentCtx, projectDir, connection) {
4484
3974
  permissionMode: "bypassPermissions",
4485
3975
  allowDangerouslySkipPermissions: true,
4486
3976
  tools: { type: "preset", preset: "claude_code" },
4487
- mcpServers: { conveyor: mcpServer },
4488
- maxTurns: settings.maxTurns ?? 30,
4489
- maxBudgetUsd: settings.maxBudgetUsd ?? 50,
3977
+ maxTurns: settings.maxTurns ?? 15,
3978
+ maxBudgetUsd: settings.maxBudgetUsd ?? 5,
4490
3979
  effort: settings.effort,
4491
3980
  thinking: settings.thinking
4492
3981
  };
4493
3982
  }
4494
- function emitResultCostAndContext(event, connection) {
4495
- const resultEvent = event;
4496
- if (resultEvent.total_cost_usd !== void 0 && resultEvent.total_cost_usd > 0) {
4497
- connection.emitEvent({
4498
- type: "cost_update",
4499
- costUsd: resultEvent.total_cost_usd
4500
- });
4501
- }
4502
- if (resultEvent.modelUsage && typeof resultEvent.modelUsage === "object") {
4503
- const modelUsage = resultEvent.modelUsage;
4504
- let contextWindow = 0;
4505
- let totalInputTokens = 0;
4506
- let totalCacheRead = 0;
4507
- let totalCacheCreation = 0;
4508
- for (const data of Object.values(modelUsage)) {
4509
- const d = data;
4510
- totalInputTokens += d.inputTokens ?? 0;
4511
- totalCacheRead += d.cacheReadInputTokens ?? 0;
4512
- totalCacheCreation += d.cacheCreationInputTokens ?? 0;
4513
- const cw = d.contextWindow ?? 0;
4514
- if (cw > contextWindow) contextWindow = cw;
4515
- }
4516
- if (contextWindow > 0) {
4517
- const queryInputTokens = totalInputTokens + totalCacheRead + totalCacheCreation;
4518
- connection.emitEvent({
4519
- type: "context_update",
4520
- contextTokens: queryInputTokens,
4521
- contextWindow,
4522
- inputTokens: totalInputTokens,
4523
- cacheReadInputTokens: totalCacheRead,
4524
- cacheCreationInputTokens: totalCacheCreation
4525
- });
4526
- }
4527
- }
4528
- }
4529
3983
  function processEventStream(event, connection, responseParts, turnToolCalls, isTyping) {
4530
3984
  if (event.type === "assistant") {
4531
3985
  if (!isTyping.value) {
@@ -4547,30 +4001,19 @@ function processEventStream(event, connection, responseParts, turnToolCalls, isT
4547
4001
  connection.emitEvent({ type: "agent_typing_stop" });
4548
4002
  isTyping.value = false;
4549
4003
  }
4550
- emitResultCostAndContext(event, connection);
4551
4004
  return true;
4552
4005
  }
4553
4006
  return false;
4554
4007
  }
4555
- async function runChatQuery(message, connection, projectDir, sessionId) {
4556
- const { agentCtx, chatHistory } = await fetchContext(connection, message.chatId);
4557
- const options = buildChatQueryOptions(agentCtx, projectDir, connection);
4008
+ async function runChatQuery(message, connection, projectDir) {
4009
+ const { agentCtx, chatHistory } = await fetchContext(connection);
4010
+ const options = buildChatQueryOptions(agentCtx, projectDir);
4558
4011
  const prompt = buildPrompt(message, chatHistory);
4559
- connection.emitAgentStatus("running");
4560
- const events = query2({
4561
- prompt,
4562
- options,
4563
- ...sessionId ? { resume: sessionId } : {}
4564
- });
4012
+ const events = query2({ prompt, options });
4565
4013
  const responseParts = [];
4566
4014
  const turnToolCalls = [];
4567
4015
  const isTyping = { value: false };
4568
- let resultSessionId;
4569
4016
  for await (const event of events) {
4570
- if (event.type === "result") {
4571
- const resultEvent = event;
4572
- resultSessionId = resultEvent.sessionId;
4573
- }
4574
4017
  const done = processEventStream(event, connection, responseParts, turnToolCalls, isTyping);
4575
4018
  if (done) break;
4576
4019
  }
@@ -4581,416 +4024,26 @@ async function runChatQuery(message, connection, projectDir, sessionId) {
4581
4024
  if (responseText) {
4582
4025
  await connection.emitChatMessage(responseText);
4583
4026
  }
4584
- return resultSessionId;
4585
4027
  }
4586
- async function handleProjectChatMessage(message, connection, projectDir, sessionId) {
4587
- connection.emitAgentStatus("fetching_context");
4028
+ async function handleProjectChatMessage(message, connection, projectDir) {
4029
+ connection.emitAgentStatus("busy");
4588
4030
  try {
4589
- return await runChatQuery(message, connection, projectDir, sessionId);
4031
+ await runChatQuery(message, connection, projectDir);
4590
4032
  } catch (error) {
4591
4033
  logger4.error("Failed to handle message", errorMeta(error));
4592
- connection.emitAgentStatus("error");
4593
4034
  try {
4594
4035
  await connection.emitChatMessage(
4595
4036
  "I encountered an error processing your message. Please try again."
4596
4037
  );
4597
4038
  } catch {
4598
4039
  }
4599
- return void 0;
4600
- } finally {
4601
- connection.emitAgentStatus("idle");
4602
- }
4603
- }
4604
-
4605
- // src/runner/project-audit-handler.ts
4606
- import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
4607
-
4608
- // src/tools/audit-tools.ts
4609
- import { randomUUID as randomUUID3 } from "crypto";
4610
- import { tool as tool5, createSdkMcpServer as createSdkMcpServer3 } from "@anthropic-ai/claude-agent-sdk";
4611
- import { z as z5 } from "zod";
4612
- function mapCreateTag(input) {
4613
- return {
4614
- type: "create_tag",
4615
- tagName: input.name,
4616
- suggestion: `Create new tag "${input.name}"${input.description ? `: ${input.description}` : ""}`,
4617
- reasoning: input.reasoning,
4618
- payload: { name: input.name, color: input.color ?? "#6B7280", description: input.description }
4619
- };
4620
- }
4621
- function mapUpdateDescription(input) {
4622
- return {
4623
- type: "update_description",
4624
- tagId: input.tagId,
4625
- tagName: input.tagName,
4626
- suggestion: `Update description for "${input.tagName}"`,
4627
- reasoning: input.reasoning,
4628
- payload: { description: input.description }
4629
- };
4630
- }
4631
- function mapContextLink(input) {
4632
- return {
4633
- type: "add_context_link",
4634
- tagId: input.tagId,
4635
- tagName: input.tagName,
4636
- suggestion: `Link ${input.linkType}:${input.path} to "${input.tagName}"`,
4637
- reasoning: input.reasoning,
4638
- payload: { contextLink: { type: input.linkType, path: input.path, label: input.label } }
4639
- };
4640
- }
4641
- function mapDocGap(input) {
4642
- return {
4643
- type: "documentation_gap",
4644
- tagId: input.tagId,
4645
- tagName: input.tagName,
4646
- suggestion: `Documentation gap: ${input.filePath} (${input.readCount} reads)`,
4647
- reasoning: input.reasoning,
4648
- payload: {
4649
- filePath: input.filePath,
4650
- readCount: input.readCount,
4651
- suggestedAction: input.suggestedAction
4652
- }
4653
- };
4654
- }
4655
- function mapMergeTags(input) {
4656
- return {
4657
- type: "merge_tags",
4658
- tagId: input.tagId,
4659
- tagName: input.tagName,
4660
- suggestion: `Merge "${input.tagName}" into "${input.mergeIntoTagName}"`,
4661
- reasoning: input.reasoning,
4662
- payload: { mergeIntoTagId: input.mergeIntoTagId }
4663
- };
4664
- }
4665
- function mapRenameTag(input) {
4666
- return {
4667
- type: "rename_tag",
4668
- tagId: input.tagId,
4669
- tagName: input.tagName,
4670
- suggestion: `Rename "${input.tagName}" to "${input.newName}"`,
4671
- reasoning: input.reasoning,
4672
- payload: { newName: input.newName }
4673
- };
4674
- }
4675
- var TOOL_MAPPERS = {
4676
- recommend_create_tag: mapCreateTag,
4677
- recommend_update_description: mapUpdateDescription,
4678
- recommend_context_link: mapContextLink,
4679
- flag_documentation_gap: mapDocGap,
4680
- recommend_merge_tags: mapMergeTags,
4681
- recommend_rename_tag: mapRenameTag
4682
- };
4683
- function collectRecommendation(toolName, input, collector, onRecommendation) {
4684
- const mapper = TOOL_MAPPERS[toolName];
4685
- if (!mapper) return JSON.stringify({ error: `Unknown tool: ${toolName}` });
4686
- const rec = { id: randomUUID3(), ...mapper(input) };
4687
- collector.recommendations.push(rec);
4688
- onRecommendation?.({ tagName: rec.tagName ?? rec.type, type: rec.type });
4689
- return JSON.stringify({ success: true, recommendationId: rec.id });
4690
- }
4691
- function createAuditMcpServer(collector, onRecommendation) {
4692
- const auditTools = [
4693
- tool5(
4694
- "recommend_create_tag",
4695
- "Recommend creating a new tag for an uncovered subsystem or area",
4696
- {
4697
- name: z5.string().describe("Proposed tag name (lowercase, hyphenated)"),
4698
- color: z5.string().optional().describe("Hex color code"),
4699
- description: z5.string().describe("What this tag covers"),
4700
- reasoning: z5.string().describe("Why this tag should be created")
4701
- },
4702
- async (args) => {
4703
- const result = collectRecommendation(
4704
- "recommend_create_tag",
4705
- args,
4706
- collector,
4707
- onRecommendation
4708
- );
4709
- return { content: [{ type: "text", text: result }] };
4710
- }
4711
- ),
4712
- tool5(
4713
- "recommend_update_description",
4714
- "Recommend updating a tag's description to better reflect its scope",
4715
- {
4716
- tagId: z5.string(),
4717
- tagName: z5.string(),
4718
- description: z5.string().describe("Proposed new description"),
4719
- reasoning: z5.string()
4720
- },
4721
- async (args) => {
4722
- const result = collectRecommendation(
4723
- "recommend_update_description",
4724
- args,
4725
- collector,
4726
- onRecommendation
4727
- );
4728
- return { content: [{ type: "text", text: result }] };
4729
- }
4730
- ),
4731
- tool5(
4732
- "recommend_context_link",
4733
- "Recommend linking a doc, rule, file, or folder to a tag's contextPaths",
4734
- {
4735
- tagId: z5.string(),
4736
- tagName: z5.string(),
4737
- linkType: z5.enum(["rule", "doc", "file", "folder"]),
4738
- path: z5.string(),
4739
- label: z5.string().optional(),
4740
- reasoning: z5.string()
4741
- },
4742
- async (args) => {
4743
- const result = collectRecommendation(
4744
- "recommend_context_link",
4745
- args,
4746
- collector,
4747
- onRecommendation
4748
- );
4749
- return { content: [{ type: "text", text: result }] };
4750
- }
4751
- ),
4752
- tool5(
4753
- "flag_documentation_gap",
4754
- "Flag a file that agents read heavily but has no tag documentation linked",
4755
- {
4756
- tagName: z5.string().describe("Tag whose agents read this file"),
4757
- tagId: z5.string().optional(),
4758
- filePath: z5.string(),
4759
- readCount: z5.number(),
4760
- suggestedAction: z5.string().describe("What doc or rule should be created"),
4761
- reasoning: z5.string()
4762
- },
4763
- async (args) => {
4764
- const result = collectRecommendation(
4765
- "flag_documentation_gap",
4766
- args,
4767
- collector,
4768
- onRecommendation
4769
- );
4770
- return { content: [{ type: "text", text: result }] };
4771
- }
4772
- ),
4773
- tool5(
4774
- "recommend_merge_tags",
4775
- "Recommend merging one tag into another",
4776
- {
4777
- tagId: z5.string().describe("Tag ID to be merged (removed after merge)"),
4778
- tagName: z5.string().describe("Name of the tag to be merged"),
4779
- mergeIntoTagId: z5.string().describe("Tag ID to merge into (kept)"),
4780
- mergeIntoTagName: z5.string(),
4781
- reasoning: z5.string()
4782
- },
4783
- async (args) => {
4784
- const result = collectRecommendation(
4785
- "recommend_merge_tags",
4786
- args,
4787
- collector,
4788
- onRecommendation
4789
- );
4790
- return { content: [{ type: "text", text: result }] };
4791
- }
4792
- ),
4793
- tool5(
4794
- "recommend_rename_tag",
4795
- "Recommend renaming a tag",
4796
- {
4797
- tagId: z5.string(),
4798
- tagName: z5.string().describe("Current tag name"),
4799
- newName: z5.string().describe("Proposed new name"),
4800
- reasoning: z5.string()
4801
- },
4802
- async (args) => {
4803
- const result = collectRecommendation(
4804
- "recommend_rename_tag",
4805
- args,
4806
- collector,
4807
- onRecommendation
4808
- );
4809
- return { content: [{ type: "text", text: result }] };
4810
- }
4811
- ),
4812
- tool5(
4813
- "complete_audit",
4814
- "Signal that the audit is complete with a summary of all findings",
4815
- { summary: z5.string().describe("Brief overview of all findings") },
4816
- async (args) => {
4817
- collector.complete = true;
4818
- collector.summary = args.summary ?? "Audit completed.";
4819
- return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
4820
- }
4821
- )
4822
- ];
4823
- return createSdkMcpServer3({
4824
- name: "tag-audit",
4825
- tools: auditTools
4826
- });
4827
- }
4828
-
4829
- // src/runner/project-audit-handler.ts
4830
- var logger5 = createServiceLogger("ProjectAudit");
4831
- var FALLBACK_MODEL2 = "claude-sonnet-4-20250514";
4832
- function buildTagSection(tags) {
4833
- if (tags.length === 0) return "No tags configured yet.";
4834
- return tags.map((t) => {
4835
- const paths = t.contextPaths ?? [];
4836
- const pathStr = paths.length > 0 ? `
4837
- Context links: ${paths.map((p) => `${p.type}:${p.path}`).join(", ")}` : "";
4838
- return ` - ${t.name} (id: ${t.id})${t.description ? `: ${t.description}` : " [no description]"}${pathStr}
4839
- Active tasks: ${t.activeTaskCount}`;
4840
- }).join("\n");
4841
- }
4842
- function buildHeatmapSection(entries) {
4843
- if (entries.length === 0) return "No file read analytics data available.";
4844
- return entries.slice(0, 50).map((e) => {
4845
- const tagBreakdown = Object.entries(e.byTag).sort(([, a], [, b]) => b - a).map(([tag, count]) => `${tag}:${count}`).join(", ");
4846
- return ` ${e.filePath} \u2014 ${e.totalReads} reads${tagBreakdown ? ` (${tagBreakdown})` : ""}`;
4847
- }).join("\n");
4848
- }
4849
- function buildAuditSystemPrompt(projectName, tags, heatmapData, projectDir) {
4850
- return [
4851
- "You are a project organization expert analyzing tag taxonomy for a software project.",
4852
- "Tags are used to categorize tasks and link relevant documentation/rules/files to subsystems.",
4853
- "",
4854
- `PROJECT: ${projectName}`,
4855
- "",
4856
- `EXISTING TAGS (${tags.length}):`,
4857
- buildTagSection(tags),
4858
- "",
4859
- "FILE READ ANALYTICS (what agents actually read, by tag):",
4860
- buildHeatmapSection(heatmapData),
4861
- "",
4862
- `You have full access to the codebase at: ${projectDir}`,
4863
- "Use your file reading and searching tools to understand the codebase structure,",
4864
- "module boundaries, and architectural patterns before making recommendations.",
4865
- "",
4866
- "ANALYSIS TASKS:",
4867
- "1. Read actual source files to understand code areas and module boundaries",
4868
- "2. Search for imports, class definitions, and architectural patterns",
4869
- "3. Coverage: Are all major subsystems/services represented by tags?",
4870
- "4. Descriptions: Do all tags have clear, useful descriptions?",
4871
- "5. Context Links: Are relevant rules/docs/folders linked to tags via contextPaths?",
4872
- "6. Documentation Gaps: Which high-read files lack linked documentation?",
4873
- "7. Cleanup: Any tags that should be merged, renamed, or removed?",
4874
- "",
4875
- "Use the tag-audit MCP tools to submit each recommendation.",
4876
- "Call complete_audit when you are done with a thorough summary.",
4877
- "Be comprehensive \u2014 recommend all improvements your analysis supports.",
4878
- "Analyze actual file contents, not just file names."
4879
- ].join("\n");
4880
- }
4881
- function emitToolCallProgress(event, request, connection) {
4882
- if (event.type !== "assistant") return;
4883
- const assistantEvent = event;
4884
- for (const block of assistantEvent.message.content) {
4885
- if (block.type === "tool_use" && block.name) {
4886
- const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input);
4887
- connection.emitAuditProgress({
4888
- requestId: request.requestId,
4889
- activity: {
4890
- tool: block.name,
4891
- input: inputStr.slice(0, 500),
4892
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
4893
- }
4894
- });
4895
- }
4896
- }
4897
- }
4898
- async function runAuditQuery(request, connection, projectDir) {
4899
- connection.emitAgentStatus("fetching_context");
4900
- let agentCtx = null;
4901
- try {
4902
- agentCtx = await connection.fetchAgentContext();
4903
- } catch {
4904
- logger5.warn("Could not fetch agent context for audit, using defaults");
4905
- }
4906
- connection.emitAgentStatus("running");
4907
- const model = agentCtx?.model || FALLBACK_MODEL2;
4908
- const settings = agentCtx?.agentSettings ?? {};
4909
- const collector = {
4910
- recommendations: [],
4911
- summary: "Audit completed.",
4912
- complete: false
4913
- };
4914
- const onRecommendation = (rec) => {
4915
- connection.emitEvent({
4916
- type: "audit_recommendation",
4917
- tagName: rec.tagName,
4918
- recommendationType: rec.type
4919
- });
4920
- };
4921
- const systemPrompt = buildAuditSystemPrompt(
4922
- request.projectName,
4923
- request.tags,
4924
- request.fileHeatmap,
4925
- projectDir
4926
- );
4927
- const userPrompt = [
4928
- "Analyze the project's tag taxonomy and submit recommendations using the tag-audit MCP tools.",
4929
- `There are currently ${request.tags.length} tags configured.`,
4930
- request.fileHeatmap.length > 0 ? `File analytics show ${request.fileHeatmap.length} files with read activity.` : "No file read analytics available.",
4931
- "",
4932
- "Start by exploring the codebase structure, then analyze each tag for accuracy and completeness.",
4933
- "Call complete_audit when done."
4934
- ].join("\n");
4935
- const events = query3({
4936
- prompt: userPrompt,
4937
- options: {
4938
- model,
4939
- systemPrompt: { type: "preset", preset: "claude_code", append: systemPrompt },
4940
- cwd: projectDir,
4941
- permissionMode: "bypassPermissions",
4942
- allowDangerouslySkipPermissions: true,
4943
- tools: { type: "preset", preset: "claude_code" },
4944
- mcpServers: { "tag-audit": createAuditMcpServer(collector, onRecommendation) },
4945
- maxTurns: settings.maxTurns ?? 75,
4946
- maxBudgetUsd: settings.maxBudgetUsd ?? 5,
4947
- effort: settings.effort,
4948
- thinking: settings.thinking
4949
- }
4950
- });
4951
- const responseParts = [];
4952
- const turnToolCalls = [];
4953
- const isTyping = { value: false };
4954
- for await (const event of events) {
4955
- emitToolCallProgress(event, request, connection);
4956
- const done = processEventStream(event, connection, responseParts, turnToolCalls, isTyping);
4957
- if (done) break;
4958
- }
4959
- if (isTyping.value) {
4960
- connection.emitEvent({ type: "agent_typing_stop" });
4961
- }
4962
- return collector;
4963
- }
4964
- async function handleProjectAuditRequest(request, connection, projectDir) {
4965
- connection.emitAgentStatus("running");
4966
- try {
4967
- const collector = await runAuditQuery(request, connection, projectDir);
4968
- const result = {
4969
- recommendations: collector.recommendations,
4970
- summary: collector.summary,
4971
- analyzedAt: (/* @__PURE__ */ new Date()).toISOString()
4972
- };
4973
- logger5.info("Tag audit completed", {
4974
- requestId: request.requestId,
4975
- recommendationCount: result.recommendations.length
4976
- });
4977
- connection.emitAuditResult({ requestId: request.requestId, result });
4978
- } catch (error) {
4979
- logger5.error("Tag audit failed", {
4980
- requestId: request.requestId,
4981
- ...errorMeta(error)
4982
- });
4983
- connection.emitAuditResult({
4984
- requestId: request.requestId,
4985
- error: error instanceof Error ? error.message : "Tag audit failed"
4986
- });
4987
4040
  } finally {
4988
4041
  connection.emitAgentStatus("idle");
4989
4042
  }
4990
4043
  }
4991
4044
 
4992
4045
  // src/runner/project-runner.ts
4993
- var logger6 = createServiceLogger("ProjectRunner");
4046
+ var logger5 = createServiceLogger("ProjectRunner");
4994
4047
  var __filename = fileURLToPath(import.meta.url);
4995
4048
  var __dirname = path.dirname(__filename);
4996
4049
  var HEARTBEAT_INTERVAL_MS2 = 3e4;
@@ -5009,12 +4062,12 @@ function setupWorkDir(projectDir, assignment) {
5009
4062
  }
5010
4063
  if (branch && branch !== devBranch) {
5011
4064
  try {
5012
- execSync6(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
4065
+ execSync5(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
5013
4066
  } catch {
5014
4067
  try {
5015
- execSync6(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
4068
+ execSync5(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
5016
4069
  } catch {
5017
- logger6.warn("Could not checkout branch", { taskId: shortId, branch });
4070
+ logger5.warn("Could not checkout branch", { taskId: shortId, branch });
5018
4071
  }
5019
4072
  }
5020
4073
  }
@@ -5053,13 +4106,13 @@ function spawnChildAgent(assignment, workDir) {
5053
4106
  child.stdout?.on("data", (data) => {
5054
4107
  const lines = data.toString().trimEnd().split("\n");
5055
4108
  for (const line of lines) {
5056
- logger6.info(line, { taskId: shortId });
4109
+ logger5.info(line, { taskId: shortId });
5057
4110
  }
5058
4111
  });
5059
4112
  child.stderr?.on("data", (data) => {
5060
4113
  const lines = data.toString().trimEnd().split("\n");
5061
4114
  for (const line of lines) {
5062
- logger6.error(line, { taskId: shortId });
4115
+ logger5.error(line, { taskId: shortId });
5063
4116
  }
5064
4117
  });
5065
4118
  return child;
@@ -5071,60 +4124,27 @@ var ProjectRunner = class {
5071
4124
  heartbeatTimer = null;
5072
4125
  stopping = false;
5073
4126
  resolveLifecycle = null;
5074
- chatSessionIds = /* @__PURE__ */ new Map();
5075
4127
  // Start command process management
5076
4128
  startCommandChild = null;
5077
4129
  startCommandRunning = false;
5078
4130
  setupComplete = false;
5079
- branchSwitchCommand;
5080
- commitWatcher;
5081
4131
  constructor(config) {
5082
4132
  this.projectDir = config.projectDir;
5083
4133
  this.connection = new ProjectConnection({
5084
4134
  apiUrl: config.conveyorApiUrl,
5085
4135
  projectToken: config.projectToken,
5086
- projectId: config.projectId,
5087
- projectDir: config.projectDir
4136
+ projectId: config.projectId
5088
4137
  });
5089
- this.commitWatcher = new CommitWatcher(
5090
- {
5091
- projectDir: this.projectDir,
5092
- pollIntervalMs: Number(process.env.CONVEYOR_COMMIT_POLL_INTERVAL) || 1e4,
5093
- debounceMs: 3e3
5094
- },
5095
- {
5096
- onNewCommits: async (data) => {
5097
- this.connection.emitNewCommitsDetected({
5098
- branch: data.branch,
5099
- commitCount: data.commitCount,
5100
- latestCommit: {
5101
- sha: data.newCommitSha,
5102
- message: data.latestMessage,
5103
- author: data.latestAuthor
5104
- },
5105
- autoSyncing: true
5106
- });
5107
- const startTime = Date.now();
5108
- const stepsRun = await this.smartSync(data.previousSha, data.newCommitSha, data.branch);
5109
- this.connection.emitEnvironmentReady({
5110
- branch: data.branch,
5111
- commitsSynced: data.commitCount,
5112
- syncDurationMs: Date.now() - startTime,
5113
- stepsRun
5114
- });
5115
- }
5116
- }
5117
- );
5118
4138
  }
5119
4139
  checkoutWorkspaceBranch() {
5120
4140
  const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
5121
4141
  if (!workspaceBranch) return;
5122
4142
  try {
5123
- execSync6(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
5124
- execSync6(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
5125
- logger6.info("Checked out workspace branch", { workspaceBranch });
4143
+ execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
4144
+ execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
4145
+ logger5.info("Checked out workspace branch", { workspaceBranch });
5126
4146
  } catch (err) {
5127
- logger6.warn("Failed to checkout workspace branch, continuing on current branch", {
4147
+ logger5.warn("Failed to checkout workspace branch, continuing on current branch", {
5128
4148
  workspaceBranch,
5129
4149
  ...errorMeta(err)
5130
4150
  });
@@ -5133,15 +4153,15 @@ var ProjectRunner = class {
5133
4153
  async executeSetupCommand() {
5134
4154
  const cmd = process.env.CONVEYOR_SETUP_COMMAND;
5135
4155
  if (!cmd) return;
5136
- logger6.info("Running setup command", { command: cmd });
4156
+ logger5.info("Running setup command", { command: cmd });
5137
4157
  try {
5138
4158
  await runSetupCommand(cmd, this.projectDir, (stream, data) => {
5139
4159
  this.connection.emitEvent({ type: "setup_output", stream, data });
5140
4160
  (stream === "stderr" ? process.stderr : process.stdout).write(data);
5141
4161
  });
5142
- logger6.info("Setup command completed");
4162
+ logger5.info("Setup command completed");
5143
4163
  } catch (error) {
5144
- logger6.error("Setup command failed", errorMeta(error));
4164
+ logger5.error("Setup command failed", errorMeta(error));
5145
4165
  this.connection.emitEvent({
5146
4166
  type: "setup_error",
5147
4167
  message: error instanceof Error ? error.message : "Setup command failed"
@@ -5152,7 +4172,7 @@ var ProjectRunner = class {
5152
4172
  executeStartCommand() {
5153
4173
  const cmd = process.env.CONVEYOR_START_COMMAND;
5154
4174
  if (!cmd) return;
5155
- logger6.info("Running start command", { command: cmd });
4175
+ logger5.info("Running start command", { command: cmd });
5156
4176
  const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
5157
4177
  this.connection.emitEvent({ type: "start_command_output", stream, data });
5158
4178
  (stream === "stderr" ? process.stderr : process.stdout).write(data);
@@ -5162,7 +4182,7 @@ var ProjectRunner = class {
5162
4182
  child.on("exit", (code, signal) => {
5163
4183
  this.startCommandRunning = false;
5164
4184
  this.startCommandChild = null;
5165
- logger6.info("Start command exited", { code, signal });
4185
+ logger5.info("Start command exited", { code, signal });
5166
4186
  this.connection.emitEvent({
5167
4187
  type: "start_command_exited",
5168
4188
  code,
@@ -5173,13 +4193,13 @@ var ProjectRunner = class {
5173
4193
  child.on("error", (err) => {
5174
4194
  this.startCommandRunning = false;
5175
4195
  this.startCommandChild = null;
5176
- logger6.error("Start command error", errorMeta(err));
4196
+ logger5.error("Start command error", errorMeta(err));
5177
4197
  });
5178
4198
  }
5179
4199
  async killStartCommand() {
5180
4200
  const child = this.startCommandChild;
5181
4201
  if (!child || !this.startCommandRunning) return;
5182
- logger6.info("Killing start command");
4202
+ logger5.info("Killing start command");
5183
4203
  try {
5184
4204
  if (child.pid) process.kill(-child.pid, "SIGTERM");
5185
4205
  } catch {
@@ -5211,7 +4231,7 @@ var ProjectRunner = class {
5211
4231
  getEnvironmentStatus() {
5212
4232
  let currentBranch = "unknown";
5213
4233
  try {
5214
- currentBranch = execSync6("git branch --show-current", {
4234
+ currentBranch = execSync5("git branch --show-current", {
5215
4235
  cwd: this.projectDir,
5216
4236
  stdio: ["ignore", "pipe", "ignore"]
5217
4237
  }).toString().trim();
@@ -5224,180 +4244,6 @@ var ProjectRunner = class {
5224
4244
  previewPort: Number(process.env.CONVEYOR_PREVIEW_PORT) || null
5225
4245
  };
5226
4246
  }
5227
- getCurrentBranch() {
5228
- try {
5229
- return execSync6("git branch --show-current", {
5230
- cwd: this.projectDir,
5231
- stdio: ["ignore", "pipe", "ignore"]
5232
- }).toString().trim() || null;
5233
- } catch {
5234
- return null;
5235
- }
5236
- }
5237
- // oxlint-disable-next-line max-lines-per-function, complexity -- sequential sync steps with per-step error handling
5238
- async smartSync(previousSha, newSha, branch) {
5239
- const stepsRun = [];
5240
- const status = execSync6("git status --porcelain", {
5241
- cwd: this.projectDir,
5242
- stdio: ["ignore", "pipe", "ignore"]
5243
- }).toString().trim();
5244
- if (status) {
5245
- this.connection.emitEvent({
5246
- type: "commit_watch_warning",
5247
- message: "Working tree has uncommitted changes. Auto-pull skipped."
5248
- });
5249
- return ["skipped:dirty_tree"];
5250
- }
5251
- await this.killStartCommand();
5252
- this.connection.emitEnvSwitchProgress({ step: "pull", status: "running" });
5253
- try {
5254
- execSync6(`git pull origin ${branch}`, {
5255
- cwd: this.projectDir,
5256
- stdio: "pipe",
5257
- timeout: 6e4
5258
- });
5259
- stepsRun.push("pull");
5260
- this.connection.emitEnvSwitchProgress({ step: "pull", status: "success" });
5261
- } catch (err) {
5262
- const message = err instanceof Error ? err.message : "Pull failed";
5263
- this.connection.emitEnvSwitchProgress({ step: "pull", status: "error", message });
5264
- logger6.error("Git pull failed during commit sync", errorMeta(err));
5265
- this.executeStartCommand();
5266
- return ["error:pull"];
5267
- }
5268
- let changedFiles = [];
5269
- try {
5270
- changedFiles = execSync6(`git diff --name-only ${previousSha}..${newSha}`, {
5271
- cwd: this.projectDir,
5272
- stdio: ["ignore", "pipe", "ignore"]
5273
- }).toString().trim().split("\n").filter(Boolean);
5274
- } catch {
5275
- }
5276
- const needsInstall = changedFiles.some(
5277
- (f) => f === "package.json" || f === "bun.lockb" || f === "bunfig.toml" || f.endsWith("/package.json") || f.endsWith("/bun.lockb")
5278
- );
5279
- const needsPrisma = changedFiles.some(
5280
- (f) => f.includes("prisma/schema.prisma") || f.includes("prisma/migrations/")
5281
- );
5282
- const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
5283
- if (cmd && (needsInstall || needsPrisma)) {
5284
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "running" });
5285
- try {
5286
- await runSetupCommand(cmd, this.projectDir, (stream, data) => {
5287
- this.connection.emitEvent({ type: "sync_output", stream, data });
5288
- });
5289
- stepsRun.push("branchSwitchCommand");
5290
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "success" });
5291
- } catch (err) {
5292
- const message = err instanceof Error ? err.message : "Sync command failed";
5293
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "error", message });
5294
- logger6.error("Branch switch command failed during commit sync", errorMeta(err));
5295
- }
5296
- } else if (!cmd) {
5297
- if (needsInstall) {
5298
- this.connection.emitEnvSwitchProgress({ step: "install", status: "running" });
5299
- try {
5300
- execSync6("bun install", { cwd: this.projectDir, timeout: 12e4, stdio: "pipe" });
5301
- stepsRun.push("install");
5302
- this.connection.emitEnvSwitchProgress({ step: "install", status: "success" });
5303
- } catch (err) {
5304
- const message = err instanceof Error ? err.message : "Install failed";
5305
- this.connection.emitEnvSwitchProgress({ step: "install", status: "error", message });
5306
- logger6.error("bun install failed during commit sync", errorMeta(err));
5307
- }
5308
- }
5309
- if (needsPrisma) {
5310
- this.connection.emitEnvSwitchProgress({ step: "prisma", status: "running" });
5311
- try {
5312
- execSync6("bunx prisma generate", {
5313
- cwd: this.projectDir,
5314
- timeout: 6e4,
5315
- stdio: "pipe"
5316
- });
5317
- execSync6("bunx prisma db push --accept-data-loss", {
5318
- cwd: this.projectDir,
5319
- timeout: 6e4,
5320
- stdio: "pipe"
5321
- });
5322
- stepsRun.push("prisma");
5323
- this.connection.emitEnvSwitchProgress({ step: "prisma", status: "success" });
5324
- } catch (err) {
5325
- const message = err instanceof Error ? err.message : "Prisma sync failed";
5326
- this.connection.emitEnvSwitchProgress({ step: "prisma", status: "error", message });
5327
- logger6.error("Prisma sync failed during commit sync", errorMeta(err));
5328
- }
5329
- }
5330
- }
5331
- this.executeStartCommand();
5332
- stepsRun.push("startCommand");
5333
- return stepsRun;
5334
- }
5335
- async handleSwitchBranch(data, callback) {
5336
- const { branch, syncAfter } = data;
5337
- try {
5338
- this.connection.emitEnvSwitchProgress({ step: "fetch", status: "running" });
5339
- try {
5340
- execSync6("git fetch origin", { cwd: this.projectDir, stdio: "pipe" });
5341
- } catch {
5342
- logger6.warn("Git fetch failed during branch switch");
5343
- }
5344
- this.connection.emitEnvSwitchProgress({ step: "fetch", status: "success" });
5345
- this.connection.emitEnvSwitchProgress({ step: "checkout", status: "running" });
5346
- try {
5347
- execSync6(`git checkout ${branch}`, { cwd: this.projectDir, stdio: "pipe" });
5348
- } catch (err) {
5349
- const message = err instanceof Error ? err.message : "Checkout failed";
5350
- this.connection.emitEnvSwitchProgress({ step: "checkout", status: "error", message });
5351
- callback({ ok: false, error: `Failed to checkout branch: ${message}` });
5352
- return;
5353
- }
5354
- try {
5355
- execSync6(`git pull origin ${branch}`, { cwd: this.projectDir, stdio: "pipe" });
5356
- } catch {
5357
- logger6.warn("Git pull failed during branch switch", { branch });
5358
- }
5359
- this.connection.emitEnvSwitchProgress({ step: "checkout", status: "success" });
5360
- if (syncAfter !== false) {
5361
- await this.handleSyncEnvironment();
5362
- }
5363
- this.commitWatcher.start(branch);
5364
- callback({ ok: true, data: this.getEnvironmentStatus() });
5365
- } catch (err) {
5366
- const message = err instanceof Error ? err.message : "Branch switch failed";
5367
- logger6.error("Branch switch failed", errorMeta(err));
5368
- callback({ ok: false, error: message });
5369
- }
5370
- }
5371
- async handleSyncEnvironment(callback) {
5372
- try {
5373
- await this.killStartCommand();
5374
- const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
5375
- if (cmd) {
5376
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "running" });
5377
- try {
5378
- await runSetupCommand(cmd, this.projectDir, (stream, data) => {
5379
- this.connection.emitEvent({ type: "sync_output", stream, data });
5380
- (stream === "stderr" ? process.stderr : process.stdout).write(data);
5381
- });
5382
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "success" });
5383
- } catch (err) {
5384
- const message = err instanceof Error ? err.message : "Sync command failed";
5385
- this.connection.emitEnvSwitchProgress({ step: "sync", status: "error", message });
5386
- logger6.error("Branch switch sync command failed", errorMeta(err));
5387
- }
5388
- }
5389
- this.executeStartCommand();
5390
- this.connection.emitEnvSwitchProgress({ step: "startCommand", status: "success" });
5391
- callback?.({ ok: true, data: this.getEnvironmentStatus() });
5392
- } catch (err) {
5393
- const message = err instanceof Error ? err.message : "Sync failed";
5394
- logger6.error("Environment sync failed", errorMeta(err));
5395
- callback?.({ ok: false, error: message });
5396
- }
5397
- }
5398
- handleGetEnvStatus(callback) {
5399
- callback({ ok: true, data: this.getEnvironmentStatus() });
5400
- }
5401
4247
  async start() {
5402
4248
  this.checkoutWorkspaceBranch();
5403
4249
  await this.connection.connect();
@@ -5411,7 +4257,7 @@ var ProjectRunner = class {
5411
4257
  startCommandRunning: this.startCommandRunning
5412
4258
  });
5413
4259
  } catch (error) {
5414
- logger6.error("Environment setup failed", errorMeta(error));
4260
+ logger5.error("Environment setup failed", errorMeta(error));
5415
4261
  this.setupComplete = false;
5416
4262
  }
5417
4263
  this.connection.onTaskAssignment((assignment) => {
@@ -5421,53 +4267,17 @@ var ProjectRunner = class {
5421
4267
  this.handleStopTask(data.taskId);
5422
4268
  });
5423
4269
  this.connection.onShutdown(() => {
5424
- logger6.info("Received shutdown signal from server");
4270
+ logger5.info("Received shutdown signal from server");
5425
4271
  void this.stop();
5426
4272
  });
5427
4273
  this.connection.onChatMessage((msg) => {
5428
- logger6.debug("Received project chat message");
5429
- const chatId = msg.chatId ?? "default";
5430
- const existingSessionId = this.chatSessionIds.get(chatId);
5431
- void handleProjectChatMessage(msg, this.connection, this.projectDir, existingSessionId).then(
5432
- (newSessionId) => {
5433
- if (newSessionId) {
5434
- this.chatSessionIds.set(chatId, newSessionId);
5435
- }
5436
- }
5437
- );
5438
- });
5439
- this.connection.onAuditRequest((request) => {
5440
- logger6.debug("Received tag audit request", { requestId: request.requestId });
5441
- void handleProjectAuditRequest(request, this.connection, this.projectDir);
4274
+ logger5.debug("Received project chat message");
4275
+ void handleProjectChatMessage(msg, this.connection, this.projectDir);
5442
4276
  });
5443
- this.connection.onSwitchBranch = (data, cb) => {
5444
- void this.handleSwitchBranch(data, cb);
5445
- };
5446
- this.connection.onSyncEnvironment = (cb) => {
5447
- void this.handleSyncEnvironment(cb);
5448
- };
5449
- this.connection.onGetEnvStatus = (cb) => {
5450
- this.handleGetEnvStatus(cb);
5451
- };
5452
- this.connection.onRestartStartCommand = (cb) => {
5453
- void this.restartStartCommand().then(() => cb({ ok: true })).catch(
5454
- (err) => cb({ ok: false, error: err instanceof Error ? err.message : "Restart failed" })
5455
- );
5456
- };
5457
- try {
5458
- const context = await this.connection.fetchAgentContext();
5459
- this.branchSwitchCommand = context?.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
5460
- } catch {
5461
- this.branchSwitchCommand = process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
5462
- }
5463
4277
  this.heartbeatTimer = setInterval(() => {
5464
4278
  this.connection.sendHeartbeat();
5465
4279
  }, HEARTBEAT_INTERVAL_MS2);
5466
- const currentBranch = this.getCurrentBranch();
5467
- if (currentBranch) {
5468
- this.commitWatcher.start(currentBranch);
5469
- }
5470
- logger6.info("Connected, waiting for task assignments");
4280
+ logger5.info("Connected, waiting for task assignments");
5471
4281
  await new Promise((resolve2) => {
5472
4282
  this.resolveLifecycle = resolve2;
5473
4283
  process.on("SIGTERM", () => void this.stop());
@@ -5478,11 +4288,11 @@ var ProjectRunner = class {
5478
4288
  const { taskId, mode } = assignment;
5479
4289
  const shortId = taskId.slice(0, 8);
5480
4290
  if (this.activeAgents.has(taskId)) {
5481
- logger6.info("Task already running, skipping", { taskId: shortId });
4291
+ logger5.info("Task already running, skipping", { taskId: shortId });
5482
4292
  return;
5483
4293
  }
5484
4294
  if (this.activeAgents.size >= MAX_CONCURRENT) {
5485
- logger6.warn("Max concurrent agents reached, rejecting task", {
4295
+ logger5.warn("Max concurrent agents reached, rejecting task", {
5486
4296
  maxConcurrent: MAX_CONCURRENT,
5487
4297
  taskId: shortId
5488
4298
  });
@@ -5491,9 +4301,9 @@ var ProjectRunner = class {
5491
4301
  }
5492
4302
  try {
5493
4303
  try {
5494
- execSync6("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
4304
+ execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
5495
4305
  } catch {
5496
- logger6.warn("Git fetch failed", { taskId: shortId });
4306
+ logger5.warn("Git fetch failed", { taskId: shortId });
5497
4307
  }
5498
4308
  const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
5499
4309
  const child = spawnChildAgent(assignment, workDir);
@@ -5504,12 +4314,12 @@ var ProjectRunner = class {
5504
4314
  usesWorktree
5505
4315
  });
5506
4316
  this.connection.emitTaskStarted(taskId);
5507
- logger6.info("Started task", { taskId: shortId, mode, workDir });
4317
+ logger5.info("Started task", { taskId: shortId, mode, workDir });
5508
4318
  child.on("exit", (code) => {
5509
4319
  this.activeAgents.delete(taskId);
5510
4320
  const reason = code === 0 ? "completed" : `exited with code ${code}`;
5511
4321
  this.connection.emitTaskStopped(taskId, reason);
5512
- logger6.info("Task exited", { taskId: shortId, reason });
4322
+ logger5.info("Task exited", { taskId: shortId, reason });
5513
4323
  if (code === 0 && usesWorktree) {
5514
4324
  try {
5515
4325
  removeWorktree(this.projectDir, taskId);
@@ -5518,7 +4328,7 @@ var ProjectRunner = class {
5518
4328
  }
5519
4329
  });
5520
4330
  } catch (error) {
5521
- logger6.error("Failed to start task", {
4331
+ logger5.error("Failed to start task", {
5522
4332
  taskId: shortId,
5523
4333
  ...errorMeta(error)
5524
4334
  });
@@ -5532,7 +4342,7 @@ var ProjectRunner = class {
5532
4342
  const agent = this.activeAgents.get(taskId);
5533
4343
  if (!agent) return;
5534
4344
  const shortId = taskId.slice(0, 8);
5535
- logger6.info("Stopping task", { taskId: shortId });
4345
+ logger5.info("Stopping task", { taskId: shortId });
5536
4346
  agent.process.kill("SIGTERM");
5537
4347
  const timer = setTimeout(() => {
5538
4348
  if (this.activeAgents.has(taskId)) {
@@ -5552,8 +4362,7 @@ var ProjectRunner = class {
5552
4362
  async stop() {
5553
4363
  if (this.stopping) return;
5554
4364
  this.stopping = true;
5555
- logger6.info("Shutting down");
5556
- this.commitWatcher.stop();
4365
+ logger5.info("Shutting down");
5557
4366
  await this.killStartCommand();
5558
4367
  if (this.heartbeatTimer) {
5559
4368
  clearInterval(this.heartbeatTimer);
@@ -5579,7 +4388,7 @@ var ProjectRunner = class {
5579
4388
  })
5580
4389
  ]);
5581
4390
  this.connection.disconnect();
5582
- logger6.info("Shutdown complete");
4391
+ logger5.info("Shutdown complete");
5583
4392
  if (this.resolveLifecycle) {
5584
4393
  this.resolveLifecycle();
5585
4394
  this.resolveLifecycle = null;
@@ -5656,13 +4465,13 @@ export {
5656
4465
  runStartCommand,
5657
4466
  ConveyorConnection,
5658
4467
  ProjectConnection,
4468
+ createServiceLogger,
4469
+ errorMeta,
5659
4470
  ensureWorktree,
5660
4471
  removeWorktree,
5661
4472
  loadConveyorConfig,
5662
- createServiceLogger,
5663
- errorMeta,
5664
4473
  AgentRunner,
5665
4474
  ProjectRunner,
5666
4475
  FileCache
5667
4476
  };
5668
- //# sourceMappingURL=chunk-SQJJL2PU.js.map
4477
+ //# sourceMappingURL=chunk-HYWZJYPW.js.map