@rallycry/conveyor-agent 5.10.2 → 5.11.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.
@@ -459,6 +459,7 @@ var ProjectConnection = class {
459
459
  onSwitchBranch = null;
460
460
  onSyncEnvironment = null;
461
461
  onGetEnvStatus = null;
462
+ onRestartStartCommand = null;
462
463
  constructor(config) {
463
464
  this.config = config;
464
465
  }
@@ -522,6 +523,13 @@ var ProjectConnection = class {
522
523
  if (this.onGetEnvStatus) this.onGetEnvStatus(cb);
523
524
  else cb({ ok: false, data: void 0 });
524
525
  });
526
+ this.socket.on(
527
+ "projectRunner:restartStartCommand",
528
+ (_data, cb) => {
529
+ if (this.onRestartStartCommand) this.onRestartStartCommand(cb);
530
+ else cb({ ok: false, error: "restartStartCommand handler not registered" });
531
+ }
532
+ );
525
533
  this.socket.on(
526
534
  "projectRunner:runAuthTokenCommand",
527
535
  (data, cb) => this.handleRunAuthTokenCommand(data.userEmail, cb)
@@ -564,6 +572,10 @@ var ProjectConnection = class {
564
572
  if (!this.socket) return;
565
573
  this.socket.emit("conveyor:tagAuditResult", data);
566
574
  }
575
+ emitAuditProgress(data) {
576
+ if (!this.socket) return;
577
+ this.socket.emit("conveyor:tagAuditProgress", data);
578
+ }
567
579
  sendHeartbeat() {
568
580
  if (!this.socket) return;
569
581
  this.socket.emit("projectRunner:heartbeat", {});
@@ -784,6 +796,14 @@ import { randomUUID as randomUUID2 } from "crypto";
784
796
  import { execSync as execSync4 } from "child_process";
785
797
 
786
798
  // src/execution/event-handlers.ts
799
+ function safeVoid(promise, context) {
800
+ if (promise && typeof promise.catch === "function") {
801
+ promise.catch((err) => {
802
+ process.stderr.write(`[safeVoid] ${context}: ${err}
803
+ `);
804
+ });
805
+ }
806
+ }
787
807
  function epochSecondsToISO(value) {
788
808
  if (typeof value === "string") return value;
789
809
  if (typeof value !== "number" || value <= 0) return void 0;
@@ -960,13 +980,13 @@ function handleRateLimitEvent(event, host) {
960
980
  const resetsAtDisplay = resetsAt ?? "unknown";
961
981
  const message = `Rate limit rejected (type: ${rate_limit_info.rateLimitType ?? "unknown"}, resets at: ${resetsAtDisplay})`;
962
982
  host.connection.sendEvent({ type: "error", message });
963
- void host.callbacks.onEvent({ type: "error", message });
983
+ safeVoid(host.callbacks.onEvent({ type: "error", message }), "rateLimitRejected");
964
984
  return resetsAt;
965
985
  } else if (status === "allowed_warning") {
966
986
  const utilization = rate_limit_info.utilization ? `${Math.round(rate_limit_info.utilization * 100)}%` : "high";
967
987
  const message = `Rate limit warning: ${utilization} utilization (type: ${rate_limit_info.rateLimitType ?? "unknown"})`;
968
988
  host.connection.sendEvent({ type: "thinking", message });
969
- void host.callbacks.onEvent({ type: "thinking", message });
989
+ safeVoid(host.callbacks.onEvent({ type: "thinking", message }), "rateLimitWarning");
970
990
  }
971
991
  return void 0;
972
992
  }
@@ -984,34 +1004,46 @@ async function handleSystemEvent(event, host, context, sessionIdStored) {
984
1004
  }
985
1005
  function handleSystemSubevents(systemEvent, host) {
986
1006
  if (systemEvent.subtype === "compact_boundary") {
987
- void host.callbacks.onEvent({
988
- type: "context_compacted",
989
- trigger: systemEvent.compact_metadata.trigger,
990
- preTokens: systemEvent.compact_metadata.pre_tokens
991
- });
1007
+ safeVoid(
1008
+ host.callbacks.onEvent({
1009
+ type: "context_compacted",
1010
+ trigger: systemEvent.compact_metadata.trigger,
1011
+ preTokens: systemEvent.compact_metadata.pre_tokens
1012
+ }),
1013
+ "compactBoundary"
1014
+ );
992
1015
  } else if (systemEvent.subtype === "task_started") {
993
- void host.callbacks.onEvent({
994
- type: "subagent_started",
995
- sdkTaskId: systemEvent.task_id,
996
- description: systemEvent.description
997
- });
1016
+ safeVoid(
1017
+ host.callbacks.onEvent({
1018
+ type: "subagent_started",
1019
+ sdkTaskId: systemEvent.task_id,
1020
+ description: systemEvent.description
1021
+ }),
1022
+ "taskStarted"
1023
+ );
998
1024
  } else if (systemEvent.subtype === "task_progress") {
999
- void host.callbacks.onEvent({
1000
- type: "subagent_progress",
1001
- sdkTaskId: systemEvent.task_id,
1002
- description: systemEvent.description,
1003
- toolUses: systemEvent.usage?.tool_uses ?? 0,
1004
- durationMs: systemEvent.usage?.duration_ms ?? 0
1005
- });
1025
+ safeVoid(
1026
+ host.callbacks.onEvent({
1027
+ type: "subagent_progress",
1028
+ sdkTaskId: systemEvent.task_id,
1029
+ description: systemEvent.description,
1030
+ toolUses: systemEvent.usage?.tool_uses ?? 0,
1031
+ durationMs: systemEvent.usage?.duration_ms ?? 0
1032
+ }),
1033
+ "taskProgress"
1034
+ );
1006
1035
  }
1007
1036
  }
1008
1037
  function handleToolProgressEvent(event, host) {
1009
1038
  const msg = event;
1010
- void host.callbacks.onEvent({
1011
- type: "tool_progress",
1012
- toolName: msg.tool_name ?? "",
1013
- elapsedSeconds: msg.elapsed_time_seconds ?? 0
1014
- });
1039
+ safeVoid(
1040
+ host.callbacks.onEvent({
1041
+ type: "tool_progress",
1042
+ toolName: msg.tool_name ?? "",
1043
+ elapsedSeconds: msg.elapsed_time_seconds ?? 0
1044
+ }),
1045
+ "toolProgress"
1046
+ );
1015
1047
  }
1016
1048
  async function handleAssistantCase(event, host, turnToolCalls) {
1017
1049
  await processAssistantEvent(event, host, turnToolCalls);
@@ -1034,6 +1066,7 @@ async function handleResultCase(event, host, context, startTime, isTyping, lastA
1034
1066
  }
1035
1067
 
1036
1068
  // src/execution/event-processor.ts
1069
+ var API_ERROR_PATTERN2 = /API Error: [45]\d\d/;
1037
1070
  function stopTypingIfNeeded(host, isTyping) {
1038
1071
  if (isTyping) host.connection.sendTypingStop();
1039
1072
  }
@@ -1055,6 +1088,12 @@ async function processAssistantCase(event, host, state) {
1055
1088
  }
1056
1089
  const usage = await handleAssistantCase(event, host, state.turnToolCalls);
1057
1090
  if (usage) state.lastAssistantUsage = usage;
1091
+ if (!state.sawApiError) {
1092
+ const fullText = event.message.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
1093
+ if (API_ERROR_PATTERN2.test(fullText)) {
1094
+ state.sawApiError = true;
1095
+ }
1096
+ }
1058
1097
  }
1059
1098
  async function processResultCase(event, host, context, startTime, state) {
1060
1099
  const info = await handleResultCase(
@@ -1078,6 +1117,7 @@ async function processEvents(events, context, host) {
1078
1117
  sessionIdStored: false,
1079
1118
  isTyping: false,
1080
1119
  retriable: false,
1120
+ sawApiError: false,
1081
1121
  resultSummary: void 0,
1082
1122
  rateLimitResetsAt: void 0,
1083
1123
  staleSession: void 0,
@@ -1117,7 +1157,7 @@ async function processEvents(events, context, host) {
1117
1157
  }
1118
1158
  stopTypingIfNeeded(host, state.isTyping);
1119
1159
  return {
1120
- retriable: state.retriable,
1160
+ retriable: state.retriable || state.sawApiError,
1121
1161
  resultSummary: state.resultSummary,
1122
1162
  rateLimitResetsAt: state.rateLimitResetsAt,
1123
1163
  ...state.staleSession && { staleSession: state.staleSession }
@@ -1478,6 +1518,8 @@ function buildDiscoveryPrompt(context) {
1478
1518
  `You are in Discovery mode \u2014 helping plan and scope this task.`,
1479
1519
  `- You have read-only codebase access (can read files, run git commands, search code)`,
1480
1520
  `- You can write plan files in .claude/plans/ only \u2014 no other file writes`,
1521
+ `- Do NOT attempt to edit, write, or modify source code files \u2014 these operations will be denied`,
1522
+ `- If you identify code changes needed, describe them in the plan instead of implementing them`,
1481
1523
  `- You can create and manage subtasks`,
1482
1524
  `- Goal: collaborate with the user to create a clear plan`,
1483
1525
  `- Proactively fill task properties (SP, tags, icon) as the plan takes shape`,
@@ -1742,7 +1784,7 @@ function detectRelaunchScenario(context, trustChatHistory = false) {
1742
1784
  const hasNewUserMessages = messagesAfterAgent.some((m) => m.role === "user");
1743
1785
  return hasNewUserMessages ? "feedback_relaunch" : "idle_relaunch";
1744
1786
  }
1745
- function buildRelaunchWithSession(mode, context, agentMode) {
1787
+ function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
1746
1788
  const scenario = detectRelaunchScenario(context);
1747
1789
  if (!context.claudeSessionId || scenario === "fresh") return null;
1748
1790
  const parts = [];
@@ -1790,7 +1832,7 @@ Address the requested changes. Do NOT re-investigate the codebase from scratch o
1790
1832
  `Run \`git log --oneline -10\` to review what you already committed.`,
1791
1833
  `Review the current state of the codebase and verify everything is working correctly.`
1792
1834
  );
1793
- if (agentMode === "auto" || agentMode === "building") {
1835
+ if (agentMode === "auto" || agentMode === "building" && isAuto) {
1794
1836
  parts.push(
1795
1837
  `If work is incomplete, continue implementing the plan. When finished, commit, push, and use mcp__conveyor__create_pull_request to open a PR.`,
1796
1838
  `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
@@ -1996,7 +2038,7 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
1996
2038
  }
1997
2039
  return parts;
1998
2040
  }
1999
- function buildInstructions(mode, context, scenario, agentMode) {
2041
+ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2000
2042
  const parts = [`
2001
2043
  ## Instructions`];
2002
2044
  const isPm = mode === "pm";
@@ -2028,7 +2070,7 @@ function buildInstructions(mode, context, scenario, agentMode) {
2028
2070
  `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
2029
2071
  `Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`
2030
2072
  );
2031
- if (agentMode === "auto" || agentMode === "building") {
2073
+ if (agentMode === "auto" || agentMode === "building" && isAuto) {
2032
2074
  parts.push(
2033
2075
  `If work is incomplete, continue implementing the plan. When finished, commit, push, and use mcp__conveyor__create_pull_request to open a PR.`,
2034
2076
  `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
@@ -2051,13 +2093,13 @@ function buildInstructions(mode, context, scenario, agentMode) {
2051
2093
  async function buildInitialPrompt(mode, context, isAuto, agentMode) {
2052
2094
  const isPackRunner = mode === "pm" && !!isAuto && !!context.isParentTask;
2053
2095
  if (!isPackRunner) {
2054
- const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode);
2096
+ const sessionRelaunch = buildRelaunchWithSession(mode, context, agentMode, isAuto);
2055
2097
  if (sessionRelaunch) return sessionRelaunch;
2056
2098
  }
2057
2099
  const isPm = mode === "pm";
2058
2100
  const scenario = detectRelaunchScenario(context, isPm);
2059
2101
  const body = await buildTaskBody(context);
2060
- const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode);
2102
+ const instructions = isPackRunner ? buildPackRunnerInstructions(context, scenario) : buildInstructions(mode, context, scenario, agentMode, isAuto);
2061
2103
  return [...body, ...instructions].join("\n");
2062
2104
  }
2063
2105
 
@@ -2319,11 +2361,14 @@ function buildCreatePullRequestTool(connection) {
2319
2361
  "Create a GitHub pull request for this task. Use this instead of gh CLI or git commands to create PRs.",
2320
2362
  {
2321
2363
  title: z.string().describe("The PR title"),
2322
- body: z.string().describe("The PR description/body in markdown")
2364
+ body: z.string().describe("The PR description/body in markdown"),
2365
+ branch: z.string().optional().describe(
2366
+ "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."
2367
+ )
2323
2368
  },
2324
- async ({ title, body }) => {
2369
+ async ({ title, body, branch }) => {
2325
2370
  try {
2326
- const result = await connection.createPR({ title, body });
2371
+ const result = await connection.createPR({ title, body, branch });
2327
2372
  connection.sendEvent({
2328
2373
  type: "pr_created",
2329
2374
  url: result.url,
@@ -2811,7 +2856,9 @@ async function handleAskUserQuestion(host, input) {
2811
2856
  }
2812
2857
  return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
2813
2858
  }
2859
+ var DENIAL_WARNING_THRESHOLD = 3;
2814
2860
  function buildCanUseTool(host) {
2861
+ let consecutiveDenials = 0;
2815
2862
  return async (toolName, input) => {
2816
2863
  if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
2817
2864
  return await handleExitPlanMode(host, input);
@@ -2819,23 +2866,39 @@ function buildCanUseTool(host) {
2819
2866
  if (toolName === "AskUserQuestion") {
2820
2867
  return await handleAskUserQuestion(host, input);
2821
2868
  }
2869
+ let result;
2822
2870
  switch (host.agentMode) {
2823
2871
  case "discovery":
2824
- return handleDiscoveryToolAccess(toolName, input);
2872
+ result = handleDiscoveryToolAccess(toolName, input);
2873
+ break;
2825
2874
  case "building":
2826
- return handleBuildingToolAccess(toolName, input);
2875
+ result = handleBuildingToolAccess(toolName, input);
2876
+ break;
2827
2877
  case "review":
2828
- return handleReviewToolAccess(toolName, input, host.isParentTask);
2878
+ result = handleReviewToolAccess(toolName, input, host.isParentTask);
2879
+ break;
2829
2880
  case "auto":
2830
- return handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
2881
+ result = handleAutoToolAccess(toolName, input, host.hasExitedPlanMode, host.isParentTask);
2882
+ break;
2831
2883
  default:
2832
- return { behavior: "allow", updatedInput: input };
2884
+ result = { behavior: "allow", updatedInput: input };
2885
+ }
2886
+ if (result.behavior === "deny") {
2887
+ consecutiveDenials++;
2888
+ if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
2889
+ host.connection.postChatMessage(
2890
+ `\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.`
2891
+ );
2892
+ }
2893
+ } else {
2894
+ consecutiveDenials = 0;
2833
2895
  }
2896
+ return result;
2834
2897
  };
2835
2898
  }
2836
2899
 
2837
2900
  // src/execution/query-executor.ts
2838
- var API_ERROR_PATTERN2 = /API Error: [45]\d\d/;
2901
+ var API_ERROR_PATTERN3 = /API Error: [45]\d\d/;
2839
2902
  var IMAGE_ERROR_PATTERN2 = /Could not process image/i;
2840
2903
  var RETRY_DELAYS_MS = [6e4, 12e4, 18e4, 3e5];
2841
2904
  function buildHooks(host) {
@@ -3083,12 +3146,17 @@ function isStaleOrExitedSession(error, context) {
3083
3146
  if (error.message.includes("No conversation found with session ID")) return true;
3084
3147
  return !!context.claudeSessionId && error.message.includes("process exited");
3085
3148
  }
3149
+ function getErrorMessage(error) {
3150
+ if (error instanceof Error) return error.message;
3151
+ if (typeof error === "string") return error;
3152
+ return String(error);
3153
+ }
3086
3154
  function isRetriableError(error) {
3087
- if (!(error instanceof Error)) return false;
3088
- return API_ERROR_PATTERN2.test(error.message) || IMAGE_ERROR_PATTERN2.test(error.message);
3155
+ const message = getErrorMessage(error);
3156
+ return API_ERROR_PATTERN3.test(message) || IMAGE_ERROR_PATTERN2.test(message);
3089
3157
  }
3090
3158
  function classifyImageError(error) {
3091
- return error instanceof Error && IMAGE_ERROR_PATTERN2.test(error.message);
3159
+ return IMAGE_ERROR_PATTERN2.test(getErrorMessage(error));
3092
3160
  }
3093
3161
  async function emitRetryStatus(host, attempt, delayMs) {
3094
3162
  const delayMin = Math.round(delayMs / 6e4);
@@ -4344,14 +4412,15 @@ var TOOL_MAPPERS = {
4344
4412
  recommend_merge_tags: mapMergeTags,
4345
4413
  recommend_rename_tag: mapRenameTag
4346
4414
  };
4347
- function collectRecommendation(toolName, input, collector) {
4415
+ function collectRecommendation(toolName, input, collector, onRecommendation) {
4348
4416
  const mapper = TOOL_MAPPERS[toolName];
4349
4417
  if (!mapper) return JSON.stringify({ error: `Unknown tool: ${toolName}` });
4350
4418
  const rec = { id: randomUUID3(), ...mapper(input) };
4351
4419
  collector.recommendations.push(rec);
4420
+ onRecommendation?.({ tagName: rec.tagName ?? rec.type, type: rec.type });
4352
4421
  return JSON.stringify({ success: true, recommendationId: rec.id });
4353
4422
  }
4354
- function createAuditMcpServer(collector) {
4423
+ function createAuditMcpServer(collector, onRecommendation) {
4355
4424
  const auditTools = [
4356
4425
  tool4(
4357
4426
  "recommend_create_tag",
@@ -4366,7 +4435,8 @@ function createAuditMcpServer(collector) {
4366
4435
  const result = collectRecommendation(
4367
4436
  "recommend_create_tag",
4368
4437
  args,
4369
- collector
4438
+ collector,
4439
+ onRecommendation
4370
4440
  );
4371
4441
  return { content: [{ type: "text", text: result }] };
4372
4442
  }
@@ -4384,7 +4454,8 @@ function createAuditMcpServer(collector) {
4384
4454
  const result = collectRecommendation(
4385
4455
  "recommend_update_description",
4386
4456
  args,
4387
- collector
4457
+ collector,
4458
+ onRecommendation
4388
4459
  );
4389
4460
  return { content: [{ type: "text", text: result }] };
4390
4461
  }
@@ -4404,7 +4475,8 @@ function createAuditMcpServer(collector) {
4404
4475
  const result = collectRecommendation(
4405
4476
  "recommend_context_link",
4406
4477
  args,
4407
- collector
4478
+ collector,
4479
+ onRecommendation
4408
4480
  );
4409
4481
  return { content: [{ type: "text", text: result }] };
4410
4482
  }
@@ -4424,7 +4496,8 @@ function createAuditMcpServer(collector) {
4424
4496
  const result = collectRecommendation(
4425
4497
  "flag_documentation_gap",
4426
4498
  args,
4427
- collector
4499
+ collector,
4500
+ onRecommendation
4428
4501
  );
4429
4502
  return { content: [{ type: "text", text: result }] };
4430
4503
  }
@@ -4443,7 +4516,8 @@ function createAuditMcpServer(collector) {
4443
4516
  const result = collectRecommendation(
4444
4517
  "recommend_merge_tags",
4445
4518
  args,
4446
- collector
4519
+ collector,
4520
+ onRecommendation
4447
4521
  );
4448
4522
  return { content: [{ type: "text", text: result }] };
4449
4523
  }
@@ -4461,7 +4535,8 @@ function createAuditMcpServer(collector) {
4461
4535
  const result = collectRecommendation(
4462
4536
  "recommend_rename_tag",
4463
4537
  args,
4464
- collector
4538
+ collector,
4539
+ onRecommendation
4465
4540
  );
4466
4541
  return { content: [{ type: "text", text: result }] };
4467
4542
  }
@@ -4535,13 +4610,32 @@ function buildAuditSystemPrompt(projectName, tags, heatmapData, projectDir) {
4535
4610
  "Analyze actual file contents, not just file names."
4536
4611
  ].join("\n");
4537
4612
  }
4613
+ function emitToolCallProgress(event, request, connection) {
4614
+ if (event.type !== "assistant") return;
4615
+ const assistantEvent = event;
4616
+ for (const block of assistantEvent.message.content) {
4617
+ if (block.type === "tool_use" && block.name) {
4618
+ const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input);
4619
+ connection.emitAuditProgress({
4620
+ requestId: request.requestId,
4621
+ activity: {
4622
+ tool: block.name,
4623
+ input: inputStr.slice(0, 500),
4624
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4625
+ }
4626
+ });
4627
+ }
4628
+ }
4629
+ }
4538
4630
  async function runAuditQuery(request, connection, projectDir) {
4631
+ connection.emitAgentStatus("fetching_context");
4539
4632
  let agentCtx = null;
4540
4633
  try {
4541
4634
  agentCtx = await connection.fetchAgentContext();
4542
4635
  } catch {
4543
4636
  logger5.warn("Could not fetch agent context for audit, using defaults");
4544
4637
  }
4638
+ connection.emitAgentStatus("running");
4545
4639
  const model = agentCtx?.model || FALLBACK_MODEL2;
4546
4640
  const settings = agentCtx?.agentSettings ?? {};
4547
4641
  const collector = {
@@ -4549,6 +4643,13 @@ async function runAuditQuery(request, connection, projectDir) {
4549
4643
  summary: "Audit completed.",
4550
4644
  complete: false
4551
4645
  };
4646
+ const onRecommendation = (rec) => {
4647
+ connection.emitEvent({
4648
+ type: "audit_recommendation",
4649
+ tagName: rec.tagName,
4650
+ recommendationType: rec.type
4651
+ });
4652
+ };
4552
4653
  const systemPrompt = buildAuditSystemPrompt(
4553
4654
  request.projectName,
4554
4655
  request.tags,
@@ -4572,20 +4673,28 @@ async function runAuditQuery(request, connection, projectDir) {
4572
4673
  permissionMode: "bypassPermissions",
4573
4674
  allowDangerouslySkipPermissions: true,
4574
4675
  tools: { type: "preset", preset: "claude_code" },
4575
- mcpServers: { "tag-audit": createAuditMcpServer(collector) },
4676
+ mcpServers: { "tag-audit": createAuditMcpServer(collector, onRecommendation) },
4576
4677
  maxTurns: settings.maxTurns ?? 30,
4577
4678
  maxBudgetUsd: settings.maxBudgetUsd ?? 5,
4578
4679
  effort: settings.effort,
4579
4680
  thinking: settings.thinking
4580
4681
  }
4581
4682
  });
4683
+ const responseParts = [];
4684
+ const turnToolCalls = [];
4685
+ const isTyping = { value: false };
4582
4686
  for await (const event of events) {
4583
- if (event.type === "result") break;
4687
+ emitToolCallProgress(event, request, connection);
4688
+ const done = processEventStream(event, connection, responseParts, turnToolCalls, isTyping);
4689
+ if (done) break;
4690
+ }
4691
+ if (isTyping.value) {
4692
+ connection.emitEvent({ type: "agent_typing_stop" });
4584
4693
  }
4585
4694
  return collector;
4586
4695
  }
4587
4696
  async function handleProjectAuditRequest(request, connection, projectDir) {
4588
- connection.emitAgentStatus("busy");
4697
+ connection.emitAgentStatus("running");
4589
4698
  try {
4590
4699
  const collector = await runAuditQuery(request, connection, projectDir);
4591
4700
  const result = {
@@ -5063,6 +5172,11 @@ var ProjectRunner = class {
5063
5172
  this.connection.onGetEnvStatus = (cb) => {
5064
5173
  this.handleGetEnvStatus(cb);
5065
5174
  };
5175
+ this.connection.onRestartStartCommand = (cb) => {
5176
+ void this.restartStartCommand().then(() => cb({ ok: true })).catch(
5177
+ (err) => cb({ ok: false, error: err instanceof Error ? err.message : "Restart failed" })
5178
+ );
5179
+ };
5066
5180
  try {
5067
5181
  const context = await this.connection.fetchAgentContext();
5068
5182
  this.branchSwitchCommand = context?.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
@@ -5274,4 +5388,4 @@ export {
5274
5388
  ProjectRunner,
5275
5389
  FileCache
5276
5390
  };
5277
- //# sourceMappingURL=chunk-WPQXAPVA.js.map
5391
+ //# sourceMappingURL=chunk-LBEAK2VJ.js.map