@rallycry/conveyor-agent 5.12.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,78 +588,6 @@ 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;
@@ -845,14 +707,6 @@ 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,23 @@ 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 API_ERROR_PATTERN2 = /API Error: [45]\d\d/;
2964
2754
  var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
2965
2755
  var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
2966
2756
  function buildHooks(host) {
@@ -3177,29 +2967,6 @@ async function buildRetryQuery(host, context, options, lastErrorWasImage) {
3177
2967
  options: { ...options, resume: void 0 }
3178
2968
  });
3179
2969
  }
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
2970
  async function handleStaleSession(context, host, options) {
3204
2971
  context.claudeSessionId = null;
3205
2972
  host.connection.storeSessionId("");
@@ -3231,17 +2998,12 @@ function isStaleOrExitedSession(error, context) {
3231
2998
  if (error.message.includes("No conversation found with session ID")) return true;
3232
2999
  return !!context.claudeSessionId && error.message.includes("process exited");
3233
3000
  }
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
3001
  function isRetriableError(error) {
3240
- const message = getErrorMessage(error);
3241
- return API_ERROR_PATTERN3.test(message) || IMAGE_ERROR_PATTERN2.test(message);
3002
+ if (!(error instanceof Error)) return false;
3003
+ return API_ERROR_PATTERN2.test(error.message) || IMAGE_ERROR_PATTERN2.test(error.message);
3242
3004
  }
3243
3005
  function classifyImageError(error) {
3244
- return IMAGE_ERROR_PATTERN2.test(getErrorMessage(error));
3006
+ return error instanceof Error && IMAGE_ERROR_PATTERN2.test(error.message);
3245
3007
  }
3246
3008
  async function emitRetryStatus(host, attempt, delayMs) {
3247
3009
  const delayMin = Math.round(delayMs / 6e4);
@@ -3268,41 +3030,26 @@ function handleRetryError(error, context, host, options, prevImageError) {
3268
3030
  if (isStaleOrExitedSession(error, context) && context.claudeSessionId) {
3269
3031
  return handleStaleSession(context, host, options);
3270
3032
  }
3271
- if (isAuthError(getErrorMessage(error))) {
3272
- return handleAuthError(context, host, options);
3273
- }
3274
3033
  if (!isRetriableError(error)) throw error;
3275
3034
  return { action: "continue", lastErrorWasImage: classifyImageError(error) || prevImageError };
3276
3035
  }
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
3036
  async function runWithRetry(initialQuery, context, host, options) {
3296
3037
  let lastErrorWasImage = false;
3297
3038
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
3298
3039
  if (host.isStopped()) return;
3299
3040
  const agentQuery = attempt === 0 ? initialQuery : await buildRetryQuery(host, context, options, lastErrorWasImage);
3300
3041
  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;
3042
+ const { retriable, resultSummary, modeRestart, rateLimitResetsAt, staleSession } = await processEvents(agentQuery, context, host);
3043
+ if (modeRestart || host.isStopped()) return;
3044
+ if (rateLimitResetsAt) {
3045
+ handleRateLimitPause(host, rateLimitResetsAt);
3046
+ return;
3047
+ }
3048
+ if (staleSession && context.claudeSessionId) {
3049
+ return handleStaleSession(context, host, options);
3050
+ }
3051
+ if (!retriable) return;
3052
+ lastErrorWasImage = IMAGE_ERROR_PATTERN2.test(resultSummary ?? "");
3306
3053
  } catch (error) {
3307
3054
  const outcome = handleRetryError(error, context, host, options, lastErrorWasImage);
3308
3055
  if (outcome instanceof Promise) return outcome;
@@ -4132,270 +3879,13 @@ var AgentRunner = class {
4132
3879
 
4133
3880
  // src/runner/project-runner.ts
4134
3881
  import { fork } from "child_process";
4135
- import { execSync as execSync6 } from "child_process";
3882
+ import { execSync as execSync5 } from "child_process";
4136
3883
  import * as path from "path";
4137
3884
  import { fileURLToPath } from "url";
4138
3885
 
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
3886
  // 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
- // src/runner/project-chat-handler.ts
4398
- var logger4 = createServiceLogger("ProjectChat");
3887
+ import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
3888
+ var logger3 = createServiceLogger("ProjectChat");
4399
3889
  var FALLBACK_MODEL = "claude-sonnet-4-20250514";
4400
3890
  function buildSystemPrompt2(projectDir, agentCtx) {
4401
3891
  const parts = [];
@@ -4448,31 +3938,27 @@ function processContentBlock(block, responseParts, turnToolCalls) {
4448
3938
  input: inputStr.slice(0, 1e4),
4449
3939
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4450
3940
  });
4451
- logger4.debug("Tool use", { tool: block.name });
3941
+ logger3.debug("Tool use", { tool: block.name });
4452
3942
  }
4453
3943
  }
4454
- async function fetchContext(connection, chatId) {
3944
+ async function fetchContext(connection) {
4455
3945
  let agentCtx = null;
4456
3946
  try {
4457
3947
  agentCtx = await connection.fetchAgentContext();
4458
3948
  } catch {
4459
- logger4.warn("Could not fetch agent context, using defaults");
3949
+ logger3.warn("Could not fetch agent context, using defaults");
4460
3950
  }
4461
3951
  let chatHistory = [];
4462
3952
  try {
4463
- chatHistory = await connection.fetchChatHistory(30, chatId);
3953
+ chatHistory = await connection.fetchChatHistory(30);
4464
3954
  } catch {
4465
- logger4.warn("Could not fetch chat history, proceeding without it");
3955
+ logger3.warn("Could not fetch chat history, proceeding without it");
4466
3956
  }
4467
3957
  return { agentCtx, chatHistory };
4468
3958
  }
4469
- function buildChatQueryOptions(agentCtx, projectDir, connection) {
3959
+ function buildChatQueryOptions(agentCtx, projectDir) {
4470
3960
  const model = agentCtx?.model || FALLBACK_MODEL;
4471
3961
  const settings = agentCtx?.agentSettings ?? {};
4472
- const mcpServer = createSdkMcpServer2({
4473
- name: "conveyor",
4474
- tools: buildProjectTools(connection)
4475
- });
4476
3962
  return {
4477
3963
  model,
4478
3964
  systemPrompt: {
@@ -4484,48 +3970,12 @@ function buildChatQueryOptions(agentCtx, projectDir, connection) {
4484
3970
  permissionMode: "bypassPermissions",
4485
3971
  allowDangerouslySkipPermissions: true,
4486
3972
  tools: { type: "preset", preset: "claude_code" },
4487
- mcpServers: { conveyor: mcpServer },
4488
- maxTurns: settings.maxTurns ?? 30,
4489
- maxBudgetUsd: settings.maxBudgetUsd ?? 50,
3973
+ maxTurns: settings.maxTurns ?? 15,
3974
+ maxBudgetUsd: settings.maxBudgetUsd ?? 5,
4490
3975
  effort: settings.effort,
4491
3976
  thinking: settings.thinking
4492
3977
  };
4493
3978
  }
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
3979
  function processEventStream(event, connection, responseParts, turnToolCalls, isTyping) {
4530
3980
  if (event.type === "assistant") {
4531
3981
  if (!isTyping.value) {
@@ -4547,30 +3997,19 @@ function processEventStream(event, connection, responseParts, turnToolCalls, isT
4547
3997
  connection.emitEvent({ type: "agent_typing_stop" });
4548
3998
  isTyping.value = false;
4549
3999
  }
4550
- emitResultCostAndContext(event, connection);
4551
4000
  return true;
4552
4001
  }
4553
4002
  return false;
4554
4003
  }
4555
- async function runChatQuery(message, connection, projectDir, sessionId) {
4556
- const { agentCtx, chatHistory } = await fetchContext(connection, message.chatId);
4557
- const options = buildChatQueryOptions(agentCtx, projectDir, connection);
4004
+ async function runChatQuery(message, connection, projectDir) {
4005
+ const { agentCtx, chatHistory } = await fetchContext(connection);
4006
+ const options = buildChatQueryOptions(agentCtx, projectDir);
4558
4007
  const prompt = buildPrompt(message, chatHistory);
4559
- connection.emitAgentStatus("running");
4560
- const events = query2({
4561
- prompt,
4562
- options,
4563
- ...sessionId ? { resume: sessionId } : {}
4564
- });
4008
+ const events = query2({ prompt, options });
4565
4009
  const responseParts = [];
4566
4010
  const turnToolCalls = [];
4567
4011
  const isTyping = { value: false };
4568
- let resultSessionId;
4569
4012
  for await (const event of events) {
4570
- if (event.type === "result") {
4571
- const resultEvent = event;
4572
- resultSessionId = resultEvent.sessionId;
4573
- }
4574
4013
  const done = processEventStream(event, connection, responseParts, turnToolCalls, isTyping);
4575
4014
  if (done) break;
4576
4015
  }
@@ -4581,416 +4020,26 @@ async function runChatQuery(message, connection, projectDir, sessionId) {
4581
4020
  if (responseText) {
4582
4021
  await connection.emitChatMessage(responseText);
4583
4022
  }
4584
- return resultSessionId;
4585
4023
  }
4586
- async function handleProjectChatMessage(message, connection, projectDir, sessionId) {
4587
- connection.emitAgentStatus("fetching_context");
4024
+ async function handleProjectChatMessage(message, connection, projectDir) {
4025
+ connection.emitAgentStatus("busy");
4588
4026
  try {
4589
- return await runChatQuery(message, connection, projectDir, sessionId);
4027
+ await runChatQuery(message, connection, projectDir);
4590
4028
  } catch (error) {
4591
- logger4.error("Failed to handle message", errorMeta(error));
4592
- connection.emitAgentStatus("error");
4029
+ logger3.error("Failed to handle message", errorMeta(error));
4593
4030
  try {
4594
4031
  await connection.emitChatMessage(
4595
4032
  "I encountered an error processing your message. Please try again."
4596
4033
  );
4597
4034
  } catch {
4598
4035
  }
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
4036
  } finally {
4988
4037
  connection.emitAgentStatus("idle");
4989
4038
  }
4990
4039
  }
4991
4040
 
4992
4041
  // src/runner/project-runner.ts
4993
- var logger6 = createServiceLogger("ProjectRunner");
4042
+ var logger4 = createServiceLogger("ProjectRunner");
4994
4043
  var __filename = fileURLToPath(import.meta.url);
4995
4044
  var __dirname = path.dirname(__filename);
4996
4045
  var HEARTBEAT_INTERVAL_MS2 = 3e4;
@@ -5009,12 +4058,12 @@ function setupWorkDir(projectDir, assignment) {
5009
4058
  }
5010
4059
  if (branch && branch !== devBranch) {
5011
4060
  try {
5012
- execSync6(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
4061
+ execSync5(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
5013
4062
  } catch {
5014
4063
  try {
5015
- execSync6(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
4064
+ execSync5(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
5016
4065
  } catch {
5017
- logger6.warn("Could not checkout branch", { taskId: shortId, branch });
4066
+ logger4.warn("Could not checkout branch", { taskId: shortId, branch });
5018
4067
  }
5019
4068
  }
5020
4069
  }
@@ -5053,13 +4102,13 @@ function spawnChildAgent(assignment, workDir) {
5053
4102
  child.stdout?.on("data", (data) => {
5054
4103
  const lines = data.toString().trimEnd().split("\n");
5055
4104
  for (const line of lines) {
5056
- logger6.info(line, { taskId: shortId });
4105
+ logger4.info(line, { taskId: shortId });
5057
4106
  }
5058
4107
  });
5059
4108
  child.stderr?.on("data", (data) => {
5060
4109
  const lines = data.toString().trimEnd().split("\n");
5061
4110
  for (const line of lines) {
5062
- logger6.error(line, { taskId: shortId });
4111
+ logger4.error(line, { taskId: shortId });
5063
4112
  }
5064
4113
  });
5065
4114
  return child;
@@ -5071,60 +4120,27 @@ var ProjectRunner = class {
5071
4120
  heartbeatTimer = null;
5072
4121
  stopping = false;
5073
4122
  resolveLifecycle = null;
5074
- chatSessionIds = /* @__PURE__ */ new Map();
5075
4123
  // Start command process management
5076
4124
  startCommandChild = null;
5077
4125
  startCommandRunning = false;
5078
4126
  setupComplete = false;
5079
- branchSwitchCommand;
5080
- commitWatcher;
5081
4127
  constructor(config) {
5082
4128
  this.projectDir = config.projectDir;
5083
4129
  this.connection = new ProjectConnection({
5084
4130
  apiUrl: config.conveyorApiUrl,
5085
4131
  projectToken: config.projectToken,
5086
- projectId: config.projectId,
5087
- projectDir: config.projectDir
4132
+ projectId: config.projectId
5088
4133
  });
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
4134
  }
5119
4135
  checkoutWorkspaceBranch() {
5120
4136
  const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
5121
4137
  if (!workspaceBranch) return;
5122
4138
  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 });
4139
+ execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
4140
+ execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
4141
+ logger4.info("Checked out workspace branch", { workspaceBranch });
5126
4142
  } catch (err) {
5127
- logger6.warn("Failed to checkout workspace branch, continuing on current branch", {
4143
+ logger4.warn("Failed to checkout workspace branch, continuing on current branch", {
5128
4144
  workspaceBranch,
5129
4145
  ...errorMeta(err)
5130
4146
  });
@@ -5133,15 +4149,15 @@ var ProjectRunner = class {
5133
4149
  async executeSetupCommand() {
5134
4150
  const cmd = process.env.CONVEYOR_SETUP_COMMAND;
5135
4151
  if (!cmd) return;
5136
- logger6.info("Running setup command", { command: cmd });
4152
+ logger4.info("Running setup command", { command: cmd });
5137
4153
  try {
5138
4154
  await runSetupCommand(cmd, this.projectDir, (stream, data) => {
5139
4155
  this.connection.emitEvent({ type: "setup_output", stream, data });
5140
4156
  (stream === "stderr" ? process.stderr : process.stdout).write(data);
5141
4157
  });
5142
- logger6.info("Setup command completed");
4158
+ logger4.info("Setup command completed");
5143
4159
  } catch (error) {
5144
- logger6.error("Setup command failed", errorMeta(error));
4160
+ logger4.error("Setup command failed", errorMeta(error));
5145
4161
  this.connection.emitEvent({
5146
4162
  type: "setup_error",
5147
4163
  message: error instanceof Error ? error.message : "Setup command failed"
@@ -5152,7 +4168,7 @@ var ProjectRunner = class {
5152
4168
  executeStartCommand() {
5153
4169
  const cmd = process.env.CONVEYOR_START_COMMAND;
5154
4170
  if (!cmd) return;
5155
- logger6.info("Running start command", { command: cmd });
4171
+ logger4.info("Running start command", { command: cmd });
5156
4172
  const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
5157
4173
  this.connection.emitEvent({ type: "start_command_output", stream, data });
5158
4174
  (stream === "stderr" ? process.stderr : process.stdout).write(data);
@@ -5162,7 +4178,7 @@ var ProjectRunner = class {
5162
4178
  child.on("exit", (code, signal) => {
5163
4179
  this.startCommandRunning = false;
5164
4180
  this.startCommandChild = null;
5165
- logger6.info("Start command exited", { code, signal });
4181
+ logger4.info("Start command exited", { code, signal });
5166
4182
  this.connection.emitEvent({
5167
4183
  type: "start_command_exited",
5168
4184
  code,
@@ -5173,13 +4189,13 @@ var ProjectRunner = class {
5173
4189
  child.on("error", (err) => {
5174
4190
  this.startCommandRunning = false;
5175
4191
  this.startCommandChild = null;
5176
- logger6.error("Start command error", errorMeta(err));
4192
+ logger4.error("Start command error", errorMeta(err));
5177
4193
  });
5178
4194
  }
5179
4195
  async killStartCommand() {
5180
4196
  const child = this.startCommandChild;
5181
4197
  if (!child || !this.startCommandRunning) return;
5182
- logger6.info("Killing start command");
4198
+ logger4.info("Killing start command");
5183
4199
  try {
5184
4200
  if (child.pid) process.kill(-child.pid, "SIGTERM");
5185
4201
  } catch {
@@ -5211,7 +4227,7 @@ var ProjectRunner = class {
5211
4227
  getEnvironmentStatus() {
5212
4228
  let currentBranch = "unknown";
5213
4229
  try {
5214
- currentBranch = execSync6("git branch --show-current", {
4230
+ currentBranch = execSync5("git branch --show-current", {
5215
4231
  cwd: this.projectDir,
5216
4232
  stdio: ["ignore", "pipe", "ignore"]
5217
4233
  }).toString().trim();
@@ -5224,180 +4240,6 @@ var ProjectRunner = class {
5224
4240
  previewPort: Number(process.env.CONVEYOR_PREVIEW_PORT) || null
5225
4241
  };
5226
4242
  }
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
4243
  async start() {
5402
4244
  this.checkoutWorkspaceBranch();
5403
4245
  await this.connection.connect();
@@ -5411,7 +4253,7 @@ var ProjectRunner = class {
5411
4253
  startCommandRunning: this.startCommandRunning
5412
4254
  });
5413
4255
  } catch (error) {
5414
- logger6.error("Environment setup failed", errorMeta(error));
4256
+ logger4.error("Environment setup failed", errorMeta(error));
5415
4257
  this.setupComplete = false;
5416
4258
  }
5417
4259
  this.connection.onTaskAssignment((assignment) => {
@@ -5421,53 +4263,17 @@ var ProjectRunner = class {
5421
4263
  this.handleStopTask(data.taskId);
5422
4264
  });
5423
4265
  this.connection.onShutdown(() => {
5424
- logger6.info("Received shutdown signal from server");
4266
+ logger4.info("Received shutdown signal from server");
5425
4267
  void this.stop();
5426
4268
  });
5427
4269
  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);
4270
+ logger4.debug("Received project chat message");
4271
+ void handleProjectChatMessage(msg, this.connection, this.projectDir);
5442
4272
  });
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
4273
  this.heartbeatTimer = setInterval(() => {
5464
4274
  this.connection.sendHeartbeat();
5465
4275
  }, 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");
4276
+ logger4.info("Connected, waiting for task assignments");
5471
4277
  await new Promise((resolve2) => {
5472
4278
  this.resolveLifecycle = resolve2;
5473
4279
  process.on("SIGTERM", () => void this.stop());
@@ -5478,11 +4284,11 @@ var ProjectRunner = class {
5478
4284
  const { taskId, mode } = assignment;
5479
4285
  const shortId = taskId.slice(0, 8);
5480
4286
  if (this.activeAgents.has(taskId)) {
5481
- logger6.info("Task already running, skipping", { taskId: shortId });
4287
+ logger4.info("Task already running, skipping", { taskId: shortId });
5482
4288
  return;
5483
4289
  }
5484
4290
  if (this.activeAgents.size >= MAX_CONCURRENT) {
5485
- logger6.warn("Max concurrent agents reached, rejecting task", {
4291
+ logger4.warn("Max concurrent agents reached, rejecting task", {
5486
4292
  maxConcurrent: MAX_CONCURRENT,
5487
4293
  taskId: shortId
5488
4294
  });
@@ -5491,9 +4297,9 @@ var ProjectRunner = class {
5491
4297
  }
5492
4298
  try {
5493
4299
  try {
5494
- execSync6("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
4300
+ execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
5495
4301
  } catch {
5496
- logger6.warn("Git fetch failed", { taskId: shortId });
4302
+ logger4.warn("Git fetch failed", { taskId: shortId });
5497
4303
  }
5498
4304
  const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
5499
4305
  const child = spawnChildAgent(assignment, workDir);
@@ -5504,12 +4310,12 @@ var ProjectRunner = class {
5504
4310
  usesWorktree
5505
4311
  });
5506
4312
  this.connection.emitTaskStarted(taskId);
5507
- logger6.info("Started task", { taskId: shortId, mode, workDir });
4313
+ logger4.info("Started task", { taskId: shortId, mode, workDir });
5508
4314
  child.on("exit", (code) => {
5509
4315
  this.activeAgents.delete(taskId);
5510
4316
  const reason = code === 0 ? "completed" : `exited with code ${code}`;
5511
4317
  this.connection.emitTaskStopped(taskId, reason);
5512
- logger6.info("Task exited", { taskId: shortId, reason });
4318
+ logger4.info("Task exited", { taskId: shortId, reason });
5513
4319
  if (code === 0 && usesWorktree) {
5514
4320
  try {
5515
4321
  removeWorktree(this.projectDir, taskId);
@@ -5518,7 +4324,7 @@ var ProjectRunner = class {
5518
4324
  }
5519
4325
  });
5520
4326
  } catch (error) {
5521
- logger6.error("Failed to start task", {
4327
+ logger4.error("Failed to start task", {
5522
4328
  taskId: shortId,
5523
4329
  ...errorMeta(error)
5524
4330
  });
@@ -5532,7 +4338,7 @@ var ProjectRunner = class {
5532
4338
  const agent = this.activeAgents.get(taskId);
5533
4339
  if (!agent) return;
5534
4340
  const shortId = taskId.slice(0, 8);
5535
- logger6.info("Stopping task", { taskId: shortId });
4341
+ logger4.info("Stopping task", { taskId: shortId });
5536
4342
  agent.process.kill("SIGTERM");
5537
4343
  const timer = setTimeout(() => {
5538
4344
  if (this.activeAgents.has(taskId)) {
@@ -5552,8 +4358,7 @@ var ProjectRunner = class {
5552
4358
  async stop() {
5553
4359
  if (this.stopping) return;
5554
4360
  this.stopping = true;
5555
- logger6.info("Shutting down");
5556
- this.commitWatcher.stop();
4361
+ logger4.info("Shutting down");
5557
4362
  await this.killStartCommand();
5558
4363
  if (this.heartbeatTimer) {
5559
4364
  clearInterval(this.heartbeatTimer);
@@ -5579,7 +4384,7 @@ var ProjectRunner = class {
5579
4384
  })
5580
4385
  ]);
5581
4386
  this.connection.disconnect();
5582
- logger6.info("Shutdown complete");
4387
+ logger4.info("Shutdown complete");
5583
4388
  if (this.resolveLifecycle) {
5584
4389
  this.resolveLifecycle();
5585
4390
  this.resolveLifecycle = null;
@@ -5665,4 +4470,4 @@ export {
5665
4470
  ProjectRunner,
5666
4471
  FileCache
5667
4472
  };
5668
- //# sourceMappingURL=chunk-SQJJL2PU.js.map
4473
+ //# sourceMappingURL=chunk-JFIWJVOH.js.map