@rallycry/conveyor-agent 10.13.3 → 10.13.9

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.
@@ -196,6 +196,8 @@ var AgentConnection = class _AgentConnection {
196
196
  earlySpawnReviews = [];
197
197
  spawnTuiCallback = null;
198
198
  earlySpawnTuis = [];
199
+ probeUsageCallback = null;
200
+ earlyProbeUsage = false;
199
201
  // PTY relay (S5 terminal). Single-slot callbacks, set per PtySession run.
200
202
  ptyInputCallback = null;
201
203
  ptyResizeCallback = null;
@@ -407,6 +409,10 @@ var AgentConnection = class _AgentConnection {
407
409
  if (this.spawnTuiCallback) this.spawnTuiCallback(data);
408
410
  else this.earlySpawnTuis.push(data);
409
411
  });
412
+ this.socket.on("session:probeUsage", () => {
413
+ if (this.probeUsageCallback) this.probeUsageCallback();
414
+ else this.earlyProbeUsage = true;
415
+ });
410
416
  this.socket.on("session:finalizeSnapshot", () => {
411
417
  this.finalizeSnapshotCallback?.();
412
418
  });
@@ -667,6 +673,15 @@ var AgentConnection = class _AgentConnection {
667
673
  for (const data of this.earlySpawnTuis) callback(data);
668
674
  this.earlySpawnTuis = [];
669
675
  }
676
+ /** Register the on-demand usage-refresh handler; drains an early-buffered
677
+ * `session:probeUsage` that arrived before the runner was ready. */
678
+ onProbeUsage(callback) {
679
+ this.probeUsageCallback = callback;
680
+ if (this.earlyProbeUsage) {
681
+ this.earlyProbeUsage = false;
682
+ callback();
683
+ }
684
+ }
670
685
  /**
671
686
  * Report that a same-pod TUI/shell child failed to spawn (fire-and-forget).
672
687
  * The server Ends the orphaned session — no fallback pod (unlike review).
@@ -766,12 +781,13 @@ var AgentConnection = class _AgentConnection {
766
781
  });
767
782
  }
768
783
  }
769
- postChatMessage(content) {
784
+ postChatMessage(content, milestone) {
770
785
  if (!this.socket) return;
771
786
  if (this.suppressIfDuplicate(content)) return;
772
787
  void this.call("postAgentMessage", {
773
788
  sessionId: this.config.sessionId,
774
- content
789
+ content,
790
+ milestone
775
791
  }).catch(() => {
776
792
  });
777
793
  }
@@ -779,13 +795,14 @@ var AgentConnection = class _AgentConnection {
779
795
  // the message is acknowledged by the server before proceeding (e.g. before
780
796
  // aborting the session). Dedup still applies; a suppressed message resolves
781
797
  // immediately without hitting the wire.
782
- async postChatMessageAwait(content) {
798
+ async postChatMessageAwait(content, milestone) {
783
799
  if (!this.socket) return;
784
800
  if (this.suppressIfDuplicate(content)) return;
785
801
  try {
786
802
  await this.call("postAgentMessage", {
787
803
  sessionId: this.config.sessionId,
788
- content
804
+ content,
805
+ milestone
789
806
  });
790
807
  } catch (err) {
791
808
  process.stderr.write(
@@ -990,6 +1007,12 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
990
1007
  triggerIdentification() {
991
1008
  return this.call("triggerIdentification", { sessionId: this.config.sessionId });
992
1009
  }
1010
+ handoffToImplementer(payload) {
1011
+ return this.call("handoffToImplementer", {
1012
+ sessionId: this.config.sessionId,
1013
+ ...payload
1014
+ });
1015
+ }
993
1016
  async refreshAuthToken() {
994
1017
  const result = await this.refreshFromBootstrap();
995
1018
  return result.refreshedClaude;
@@ -2108,7 +2131,8 @@ var CreatePRInputSchema = z3.object({
2108
2131
  });
2109
2132
  var PostToChatInputSchema = z3.object({
2110
2133
  message: z3.string().min(1),
2111
- type: z3.enum(["message", "question", "update"]).optional().default("message")
2134
+ type: z3.enum(["message", "question", "update"]).optional().default("message"),
2135
+ milestone: z3.enum(["plan_ready", "implementation_complete", "blocked"]).optional()
2112
2136
  });
2113
2137
  var GetTaskContextRequestSchema = z3.object({
2114
2138
  sessionId: z3.string(),
@@ -2294,7 +2318,10 @@ var UpdateTaskPropertiesRequestSchema = z3.object({
2294
2318
  tagIds: z3.array(z3.string()).optional(),
2295
2319
  tagNames: z3.array(z3.string()).optional(),
2296
2320
  githubPRUrl: z3.string().url().optional(),
2297
- githubBranch: z3.string().optional()
2321
+ githubBranch: z3.string().optional(),
2322
+ // Canonical risk level, or null to clear — same semantics as the headless
2323
+ // update_task boundary (resolved to the project's Risk row in the handler).
2324
+ risk: riskLevelSchema.nullable().optional()
2298
2325
  });
2299
2326
  var ListIconsRequestSchema = z3.object({
2300
2327
  sessionId: z3.string()
@@ -2343,6 +2370,15 @@ var VoteSuggestionRequestSchema = z3.object({
2343
2370
  var TriggerIdentificationRequestSchema = z3.object({
2344
2371
  sessionId: z3.string()
2345
2372
  });
2373
+ var HandoffToImplementerRequestSchema = z3.object({
2374
+ sessionId: z3.string(),
2375
+ // Optional difficulty sizing — sets the task's story points before resolving
2376
+ // the matched implementer agent. Omit to hand off using the task's current
2377
+ // story points (or the project's default task agent when unsized).
2378
+ storyPoints: z3.number().int().positive().optional(),
2379
+ // Optional kickoff note posted to the task chat alongside the handoff notice.
2380
+ message: z3.string().optional()
2381
+ });
2346
2382
  var SubmitCodeReviewResultRequestSchema = z3.object({
2347
2383
  sessionId: z3.string(),
2348
2384
  approved: z3.boolean(),
@@ -2556,6 +2592,10 @@ var ListAccessibleProjectsRequestSchema = z4.object({
2556
2592
  var ListProjectTasksRequestSchema = z4.object({
2557
2593
  projectId: z4.string(),
2558
2594
  status: z4.string().optional(),
2595
+ // Card types to include. Omitted/empty → defaults to ["task"] in the handler
2596
+ // (mirrors searchProjectTasks) so listing doesn't surface incidents/suggestions
2597
+ // unless asked. Enum validation lives at the MCP tool layer.
2598
+ typeFilters: z4.array(z4.string()).optional(),
2559
2599
  assigneeId: z4.string().optional(),
2560
2600
  unassigned: z4.boolean().optional(),
2561
2601
  // Scope to a sub-project board when provided. Unlike the board layer's `?? null`
@@ -2596,6 +2636,9 @@ var GetProjectSummaryRequestSchema = z4.object({
2596
2636
  var GetProjectOnboardingStatusRequestSchema = z4.object({
2597
2637
  projectId: z4.string()
2598
2638
  });
2639
+ var GetProjectConnectUrlsRequestSchema = z4.object({
2640
+ projectId: z4.string()
2641
+ });
2599
2642
  var CreateProjectTaskRequestSchema = z4.object({
2600
2643
  projectId: z4.string(),
2601
2644
  title: z4.string().min(1),
@@ -2610,7 +2653,13 @@ var UpdateProjectTaskRequestSchema = z4.object({
2610
2653
  projectId: z4.string(),
2611
2654
  taskId: z4.string(),
2612
2655
  title: z4.string().optional(),
2656
+ description: z4.string().optional(),
2613
2657
  plan: z4.string().optional(),
2658
+ // Enum validation lives at the MCP tool layer (mirrors createProjectTask);
2659
+ // the handler routes through the shared updateStatus core (InProgress
2660
+ // dependency check + cleanup/board/Slack side effects), not the stricter
2661
+ // card-type-validating path the Socket.IO updateTaskStatus mutation uses.
2662
+ status: z4.string().optional(),
2614
2663
  // Canonical risk level, or null to clear. Resolved to the project's
2615
2664
  // configured Risk row (by rank) in the handler.
2616
2665
  risk: riskLevelSchema.nullable().optional(),
@@ -2620,9 +2669,9 @@ var UpdateProjectTaskRequestSchema = z4.object({
2620
2669
  subProjectId: z4.string().nullable().optional(),
2621
2670
  requestingUserId: z4.string().optional()
2622
2671
  }).strict().refine(
2623
- (v) => v.title !== void 0 || v.plan !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
2672
+ (v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
2624
2673
  {
2625
- message: "update_task requires at least one field to change (title, plan, risk, assignedUserId, or subProjectId)"
2674
+ message: "update_task requires at least one field to change (title, description, plan, status, risk, assignedUserId, or subProjectId)"
2626
2675
  }
2627
2676
  );
2628
2677
  var TransitionProjectTaskStatusRequestSchema = z4.object({
@@ -2728,6 +2777,14 @@ var ResumeAdhocSessionRequestSchema = z4.object({
2728
2777
  workspaceId: z4.string(),
2729
2778
  requestingUserId: z4.string().optional()
2730
2779
  });
2780
+ var RefreshCodingAgentKeyUsageRequestSchema = z4.object({
2781
+ projectId: z4.string(),
2782
+ keyId: z4.string().optional(),
2783
+ requestingUserId: z4.string().optional()
2784
+ });
2785
+ var ListKeysToProbeRequestSchema = z4.object({
2786
+ sessionId: z4.string()
2787
+ });
2731
2788
  var CreateProjectReleaseRequestSchema = z4.object({
2732
2789
  projectId: z4.string(),
2733
2790
  taskIds: z4.array(z4.string()).optional(),
@@ -2766,6 +2823,9 @@ var UpdateProjectSubtaskRequestSchema = z4.object({
2766
2823
  ordinal: z4.number().int().nonnegative().optional(),
2767
2824
  storyPointValue: z4.number().int().positive().optional(),
2768
2825
  followParentStatus: z4.boolean().optional(),
2826
+ /** Replace-set of sibling subtask ids/slugs this subtask blocks on ([] clears).
2827
+ * Mirrors the in-pod updateSubtask semantics. */
2828
+ dependsOn: z4.array(z4.string().min(1)).max(32).optional(),
2769
2829
  requestingUserId: z4.string().optional()
2770
2830
  });
2771
2831
  var DeleteProjectSubtaskRequestSchema = z4.object({
@@ -3341,6 +3401,7 @@ var ClaudeCodeHarness = class {
3341
3401
  };
3342
3402
 
3343
3403
  // src/harness/pty/session.ts
3404
+ import { randomUUID as randomUUID2 } from "crypto";
3344
3405
  import { mkdtemp as mkdtemp2, mkdir as mkdir3, rm as rm5 } from "fs/promises";
3345
3406
  import { tmpdir as tmpdir3 } from "os";
3346
3407
  import { join as join4, dirname } from "path";
@@ -3739,11 +3800,131 @@ function matchUsageLimitBanner(text, now = Date.now()) {
3739
3800
  };
3740
3801
  }
3741
3802
 
3803
+ // src/harness/pty/pty-support.ts
3804
+ import { stat as stat2 } from "fs/promises";
3805
+ var MAX_DIAGNOSTIC_OUTPUT = 4e3;
3806
+ var MAX_BETWEEN_TURN_BUFFER = 500;
3807
+ var SUBMIT_SETTLE_MS = 300;
3808
+ var SUBMIT_NUDGE_INTERVAL_MS = 2e3;
3809
+ var SUBMIT_NUDGE_MAX_PRESSES = 5;
3810
+ var SUBMIT_NUDGE_SLOW_INTERVAL_MS = 5e3;
3811
+ var SUBMIT_NUDGE_WINDOW_MS = 9e4;
3812
+ var PLAN_DIALOG_FIRST_PRESS_MS = 700;
3813
+ var PLAN_DIALOG_INTERVAL_MS = 1500;
3814
+ var PLAN_DIALOG_SLOW_INTERVAL_MS = 5e3;
3815
+ var PLAN_DIALOG_FAST_WINDOW_MS = 1e4;
3816
+ var PLAN_DIALOG_WINDOW_MS = 9e4;
3817
+ function envMs(name, fallback) {
3818
+ const raw = Number(process.env[name]);
3819
+ return Number.isFinite(raw) && raw > 0 ? raw : fallback;
3820
+ }
3821
+ function resolveSubmitSettleMs() {
3822
+ return envMs("CONVEYOR_PTY_SUBMIT_SETTLE_MS", SUBMIT_SETTLE_MS);
3823
+ }
3824
+ function resolveSubmitNudgeTiming() {
3825
+ return {
3826
+ intervalMs: envMs("CONVEYOR_PTY_NUDGE_INTERVAL_MS", SUBMIT_NUDGE_INTERVAL_MS),
3827
+ slowIntervalMs: envMs("CONVEYOR_PTY_NUDGE_SLOW_INTERVAL_MS", SUBMIT_NUDGE_SLOW_INTERVAL_MS),
3828
+ maxPresses: SUBMIT_NUDGE_MAX_PRESSES,
3829
+ windowMs: envMs("CONVEYOR_PTY_NUDGE_WINDOW_MS", SUBMIT_NUDGE_WINDOW_MS)
3830
+ };
3831
+ }
3832
+ function resolvePlanDialogTiming() {
3833
+ return {
3834
+ firstPressMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FIRST_PRESS_MS", PLAN_DIALOG_FIRST_PRESS_MS),
3835
+ intervalMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_INTERVAL_MS", PLAN_DIALOG_INTERVAL_MS),
3836
+ slowIntervalMs: envMs(
3837
+ "CONVEYOR_PTY_PLAN_DIALOG_SLOW_INTERVAL_MS",
3838
+ PLAN_DIALOG_SLOW_INTERVAL_MS
3839
+ ),
3840
+ fastWindowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FAST_WINDOW_MS", PLAN_DIALOG_FAST_WINDOW_MS),
3841
+ windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
3842
+ };
3843
+ }
3844
+ function turnOptionsFrom(options) {
3845
+ return {
3846
+ canUseTool: options.canUseTool,
3847
+ promptDelivery: options.promptDelivery,
3848
+ planDialogAutoAccept: options.planDialogAutoAccept,
3849
+ abortController: options.abortController
3850
+ };
3851
+ }
3852
+ function isRecord2(value) {
3853
+ return typeof value === "object" && value !== null;
3854
+ }
3855
+ function extractSpawn(mod) {
3856
+ if (!isRecord2(mod)) return null;
3857
+ if (typeof mod.spawn === "function") return mod.spawn;
3858
+ const def = mod.default;
3859
+ if (isRecord2(def) && typeof def.spawn === "function") return def.spawn;
3860
+ return null;
3861
+ }
3862
+ async function loadPtySpawn() {
3863
+ const mod = await import("node-pty");
3864
+ const spawn2 = extractSpawn(mod);
3865
+ if (!spawn2) throw new Error("node-pty: spawn export not found");
3866
+ return spawn2;
3867
+ }
3868
+ function inheritedEnv(socketPath) {
3869
+ const env = {};
3870
+ for (const [key, value] of Object.entries(process.env)) {
3871
+ if (typeof value === "string") env[key] = value;
3872
+ }
3873
+ if (env.CLAUDE_CODE_OAUTH_TOKEN) {
3874
+ delete env.ANTHROPIC_API_KEY;
3875
+ }
3876
+ if (socketPath) {
3877
+ env.CONVEYOR_HOOK_SOCKET = socketPath;
3878
+ }
3879
+ env.MCP_TIMEOUT ??= "60000";
3880
+ env.MCP_TOOL_TIMEOUT ??= "180000";
3881
+ return env;
3882
+ }
3883
+ function buildPromptBytes(text) {
3884
+ return `\x1B[200~${text}\x1B[201~`;
3885
+ }
3886
+ function renderPromptContentText(content) {
3887
+ return content.map((block) => {
3888
+ const b = block;
3889
+ if (b?.type === "text" && typeof b.text === "string") return b.text;
3890
+ if (b?.type === "image") {
3891
+ return `[Image attachment \u2014 use list_task_files / get_attachment to view]`;
3892
+ }
3893
+ return JSON.stringify(block);
3894
+ }).join("\n\n");
3895
+ }
3896
+ async function transcriptSize(path4) {
3897
+ try {
3898
+ return (await stat2(path4)).size;
3899
+ } catch {
3900
+ return 0;
3901
+ }
3902
+ }
3903
+ function parseUserQuestions(input) {
3904
+ if (!Array.isArray(input.questions)) return [];
3905
+ const questions = [];
3906
+ for (const entry of input.questions) {
3907
+ if (!isRecord2(entry)) continue;
3908
+ if (typeof entry.question !== "string") continue;
3909
+ const options = Array.isArray(entry.options) ? entry.options.filter(isRecord2).filter((o) => typeof o.label === "string").map((o) => ({
3910
+ label: o.label,
3911
+ description: typeof o.description === "string" ? o.description : ""
3912
+ })) : [];
3913
+ questions.push({
3914
+ question: entry.question,
3915
+ header: typeof entry.header === "string" ? entry.header : "",
3916
+ options,
3917
+ ...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
3918
+ });
3919
+ }
3920
+ return questions;
3921
+ }
3922
+
3742
3923
  // src/harness/pty/chat-record-mapper.ts
3743
3924
  var TEXT_MAX = 16e3;
3744
3925
  var TOOL_INPUT_MAX = 1900;
3745
3926
  var TOOL_OUTPUT_MAX = 1900;
3746
- function isRecord2(value) {
3927
+ function isRecord3(value) {
3747
3928
  return typeof value === "object" && value !== null;
3748
3929
  }
3749
3930
  function isUnknownArray2(value) {
@@ -3759,6 +3940,27 @@ function stringField2(record, ...keys) {
3759
3940
  function truncate(text, max) {
3760
3941
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
3761
3942
  }
3943
+ function compactQuestionsJson(questions) {
3944
+ const serialize = (qs) => JSON.stringify({ questions: qs });
3945
+ const withDescriptions = (max) => questions.map((q) => ({
3946
+ ...q,
3947
+ options: q.options.map((o) => ({ ...o, description: truncate(o.description, max) }))
3948
+ }));
3949
+ const full = serialize(questions);
3950
+ if (full.length <= TOOL_INPUT_MAX) return full;
3951
+ const shortened = serialize(withDescriptions(80));
3952
+ if (shortened.length <= TOOL_INPUT_MAX) return shortened;
3953
+ const bare = serialize(withDescriptions(0));
3954
+ if (bare.length <= TOOL_INPUT_MAX) return bare;
3955
+ return bare.slice(0, TOOL_INPUT_MAX);
3956
+ }
3957
+ function compactToolInput(name, input) {
3958
+ if (name === "AskUserQuestion" && isRecord3(input)) {
3959
+ const questions = parseUserQuestions(input);
3960
+ if (questions.length > 0) return compactQuestionsJson(questions);
3961
+ }
3962
+ return JSON.stringify(input ?? {}).slice(0, TOOL_INPUT_MAX);
3963
+ }
3762
3964
  function isNonConversationText(text) {
3763
3965
  const trimmed = text.trimStart();
3764
3966
  return trimmed.startsWith("<command-name>") || trimmed.startsWith("<local-command-") || trimmed.startsWith("<task-notification>");
@@ -3778,11 +3980,11 @@ function mapSystem2(record) {
3778
3980
  }
3779
3981
  function mapAssistant2(record) {
3780
3982
  const message = record.message;
3781
- if (!isRecord2(message)) return [];
3983
+ if (!isRecord3(message)) return [];
3782
3984
  const content = isUnknownArray2(message.content) ? message.content : [];
3783
3985
  const events = [];
3784
3986
  for (const raw of content) {
3785
- if (!isRecord2(raw)) continue;
3987
+ if (!isRecord3(raw)) continue;
3786
3988
  if (raw.type === "text") {
3787
3989
  const text = stringField2(raw, "text");
3788
3990
  if (text && text.length > 0) {
@@ -3795,7 +3997,7 @@ function mapAssistant2(record) {
3795
3997
  const event = {
3796
3998
  kind: "tool_use",
3797
3999
  name: truncate(name, 200),
3798
- input: JSON.stringify(input ?? {}).slice(0, TOOL_INPUT_MAX)
4000
+ input: compactToolInput(name, input)
3799
4001
  };
3800
4002
  const id = stringField2(raw, "id");
3801
4003
  if (id !== void 0) event.id = id;
@@ -3809,14 +4011,14 @@ function toolResultText(block) {
3809
4011
  const content = block.content;
3810
4012
  if (typeof content === "string") return content;
3811
4013
  if (isUnknownArray2(content)) {
3812
- return content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
4014
+ return content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3813
4015
  }
3814
4016
  return "";
3815
4017
  }
3816
4018
  function mapToolResults(content) {
3817
4019
  const events = [];
3818
4020
  for (const raw of content) {
3819
- if (!isRecord2(raw) || raw.type !== "tool_result") continue;
4021
+ if (!isRecord3(raw) || raw.type !== "tool_result") continue;
3820
4022
  const event = {
3821
4023
  kind: "tool_result",
3822
4024
  output: truncate(toolResultText(raw), TOOL_OUTPUT_MAX),
@@ -3830,15 +4032,15 @@ function mapToolResults(content) {
3830
4032
  }
3831
4033
  function mapUser(record) {
3832
4034
  const message = record.message;
3833
- if (!isRecord2(message)) return [];
4035
+ if (!isRecord3(message)) return [];
3834
4036
  const content = message.content;
3835
4037
  let text;
3836
4038
  if (typeof content === "string") {
3837
4039
  text = content;
3838
4040
  } else if (isUnknownArray2(content)) {
3839
- const hasToolResult = content.some((b) => isRecord2(b) && b.type === "tool_result");
4041
+ const hasToolResult = content.some((b) => isRecord3(b) && b.type === "tool_result");
3840
4042
  if (hasToolResult) return mapToolResults(content);
3841
- text = content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
4043
+ text = content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3842
4044
  } else {
3843
4045
  return [];
3844
4046
  }
@@ -3847,7 +4049,7 @@ function mapUser(record) {
3847
4049
  return [{ kind: "user_text", text: truncate(trimmed, TEXT_MAX) }];
3848
4050
  }
3849
4051
  function mapChatRecords(raw) {
3850
- if (!isRecord2(raw)) return [];
4052
+ if (!isRecord3(raw)) return [];
3851
4053
  if (raw.isSidechain === true || raw.isMeta === true) return [];
3852
4054
  switch (raw.type) {
3853
4055
  case "system":
@@ -4751,126 +4953,6 @@ async function removeConveyorCredentials(env = process.env) {
4751
4953
  }
4752
4954
  }
4753
4955
 
4754
- // src/harness/pty/pty-support.ts
4755
- import { stat as stat2 } from "fs/promises";
4756
- var MAX_DIAGNOSTIC_OUTPUT = 4e3;
4757
- var MAX_BETWEEN_TURN_BUFFER = 500;
4758
- var SUBMIT_SETTLE_MS = 300;
4759
- var SUBMIT_NUDGE_INTERVAL_MS = 2e3;
4760
- var SUBMIT_NUDGE_MAX_PRESSES = 5;
4761
- var SUBMIT_NUDGE_SLOW_INTERVAL_MS = 5e3;
4762
- var SUBMIT_NUDGE_WINDOW_MS = 9e4;
4763
- var PLAN_DIALOG_FIRST_PRESS_MS = 700;
4764
- var PLAN_DIALOG_INTERVAL_MS = 1500;
4765
- var PLAN_DIALOG_SLOW_INTERVAL_MS = 5e3;
4766
- var PLAN_DIALOG_FAST_WINDOW_MS = 1e4;
4767
- var PLAN_DIALOG_WINDOW_MS = 9e4;
4768
- function envMs(name, fallback) {
4769
- const raw = Number(process.env[name]);
4770
- return Number.isFinite(raw) && raw > 0 ? raw : fallback;
4771
- }
4772
- function resolveSubmitSettleMs() {
4773
- return envMs("CONVEYOR_PTY_SUBMIT_SETTLE_MS", SUBMIT_SETTLE_MS);
4774
- }
4775
- function resolveSubmitNudgeTiming() {
4776
- return {
4777
- intervalMs: envMs("CONVEYOR_PTY_NUDGE_INTERVAL_MS", SUBMIT_NUDGE_INTERVAL_MS),
4778
- slowIntervalMs: envMs("CONVEYOR_PTY_NUDGE_SLOW_INTERVAL_MS", SUBMIT_NUDGE_SLOW_INTERVAL_MS),
4779
- maxPresses: SUBMIT_NUDGE_MAX_PRESSES,
4780
- windowMs: envMs("CONVEYOR_PTY_NUDGE_WINDOW_MS", SUBMIT_NUDGE_WINDOW_MS)
4781
- };
4782
- }
4783
- function resolvePlanDialogTiming() {
4784
- return {
4785
- firstPressMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FIRST_PRESS_MS", PLAN_DIALOG_FIRST_PRESS_MS),
4786
- intervalMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_INTERVAL_MS", PLAN_DIALOG_INTERVAL_MS),
4787
- slowIntervalMs: envMs(
4788
- "CONVEYOR_PTY_PLAN_DIALOG_SLOW_INTERVAL_MS",
4789
- PLAN_DIALOG_SLOW_INTERVAL_MS
4790
- ),
4791
- fastWindowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FAST_WINDOW_MS", PLAN_DIALOG_FAST_WINDOW_MS),
4792
- windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
4793
- };
4794
- }
4795
- function turnOptionsFrom(options) {
4796
- return {
4797
- canUseTool: options.canUseTool,
4798
- promptDelivery: options.promptDelivery,
4799
- planDialogAutoAccept: options.planDialogAutoAccept,
4800
- abortController: options.abortController
4801
- };
4802
- }
4803
- function isRecord3(value) {
4804
- return typeof value === "object" && value !== null;
4805
- }
4806
- function extractSpawn(mod) {
4807
- if (!isRecord3(mod)) return null;
4808
- if (typeof mod.spawn === "function") return mod.spawn;
4809
- const def = mod.default;
4810
- if (isRecord3(def) && typeof def.spawn === "function") return def.spawn;
4811
- return null;
4812
- }
4813
- async function loadPtySpawn() {
4814
- const mod = await import("node-pty");
4815
- const spawn2 = extractSpawn(mod);
4816
- if (!spawn2) throw new Error("node-pty: spawn export not found");
4817
- return spawn2;
4818
- }
4819
- function inheritedEnv(socketPath) {
4820
- const env = {};
4821
- for (const [key, value] of Object.entries(process.env)) {
4822
- if (typeof value === "string") env[key] = value;
4823
- }
4824
- if (env.CLAUDE_CODE_OAUTH_TOKEN) {
4825
- delete env.ANTHROPIC_API_KEY;
4826
- }
4827
- if (socketPath) {
4828
- env.CONVEYOR_HOOK_SOCKET = socketPath;
4829
- }
4830
- env.MCP_TIMEOUT ??= "60000";
4831
- env.MCP_TOOL_TIMEOUT ??= "180000";
4832
- return env;
4833
- }
4834
- function buildPromptBytes(text) {
4835
- return `\x1B[200~${text}\x1B[201~`;
4836
- }
4837
- function renderPromptContentText(content) {
4838
- return content.map((block) => {
4839
- const b = block;
4840
- if (b?.type === "text" && typeof b.text === "string") return b.text;
4841
- if (b?.type === "image") {
4842
- return `[Image attachment \u2014 use list_task_files / get_attachment to view]`;
4843
- }
4844
- return JSON.stringify(block);
4845
- }).join("\n\n");
4846
- }
4847
- async function transcriptSize(path4) {
4848
- try {
4849
- return (await stat2(path4)).size;
4850
- } catch {
4851
- return 0;
4852
- }
4853
- }
4854
- function parseUserQuestions(input) {
4855
- if (!Array.isArray(input.questions)) return [];
4856
- const questions = [];
4857
- for (const entry of input.questions) {
4858
- if (!isRecord3(entry)) continue;
4859
- if (typeof entry.question !== "string") continue;
4860
- const options = Array.isArray(entry.options) ? entry.options.filter(isRecord3).filter((o) => typeof o.label === "string").map((o) => ({
4861
- label: o.label,
4862
- description: typeof o.description === "string" ? o.description : ""
4863
- })) : [];
4864
- questions.push({
4865
- question: entry.question,
4866
- header: typeof entry.header === "string" ? entry.header : "",
4867
- options,
4868
- ...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
4869
- });
4870
- }
4871
- return questions;
4872
- }
4873
-
4874
4956
  // src/harness/pty/adapters/claude.ts
4875
4957
  var ClaudeTuiAdapter = class {
4876
4958
  id = "claude-code";
@@ -4981,6 +5063,18 @@ var PtySession = class {
4981
5063
  // Submit-nudge state (see armSubmitNudge).
4982
5064
  pendingSubmitNudge = false;
4983
5065
  submitNudgeTimer = null;
5066
+ // Synthetic AskUserQuestion chat-card state. The CLI does NOT flush the
5067
+ // assistant record holding a pending AskUserQuestion tool_use to the
5068
+ // transcript until the questionnaire resolves (verified live on CLI 2.1.209:
5069
+ // dialog parked on screen, transcript untouched) — so a transcript-derived
5070
+ // question card could only ever render AFTER the human answered in the raw
5071
+ // terminal. Instead the PreToolUse hook (which fires at ask time and carries
5072
+ // the full questions input) emits a synthetic `tool_use` chat event under an
5073
+ // `aq-…` id. When the real records eventually flush, the duplicate tool_use
5074
+ // is dropped (FIFO match below) and its tool_result is re-pointed at the
5075
+ // synthetic id so the card flips to answered.
5076
+ pendingSyntheticQuestionIds = [];
5077
+ questionResultRemap = /* @__PURE__ */ new Map();
4984
5078
  // Per-turn state: the prompt to feed and the per-turn options subset. Both
4985
5079
  // start from the constructor args (turn 1) and are replaced by beginTurn.
4986
5080
  turnPrompt;
@@ -5102,6 +5196,7 @@ var PtySession = class {
5102
5196
  this.passiveSignaled = false;
5103
5197
  this.disarmSubmitNudge();
5104
5198
  this.disarmPlanDialogAutoAccept();
5199
+ this.closeSyntheticQuestionCards();
5105
5200
  }
5106
5201
  /**
5107
5202
  * (Re)register the abort→teardown listener on the current turn's controller,
@@ -5133,6 +5228,7 @@ var PtySession = class {
5133
5228
  */
5134
5229
  endTurn(clean) {
5135
5230
  this.lastTurnCleanResult = clean;
5231
+ this.closeSyntheticQuestionCards();
5136
5232
  this.activeQueue?.close();
5137
5233
  this.activeQueue = null;
5138
5234
  if (this.abortHandler && this.turn.abortController) {
@@ -5207,17 +5303,72 @@ var PtySession = class {
5207
5303
  const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);
5208
5304
  await mkdir3(dirname(transcriptPath), { recursive: true });
5209
5305
  const startOffset = this.resume ? await transcriptSize(transcriptPath) : 0;
5210
- const sendChat = this.bridge?.sendChatEvent?.bind(this.bridge);
5211
5306
  this.tailer = new JsonlTailer(
5212
5307
  transcriptPath,
5213
5308
  (event) => this.handleTranscriptEvent(event),
5214
- sendChat ? (raw) => {
5215
- for (const chatEvent of mapChatRecords(raw)) sendChat(chatEvent);
5216
- } : void 0
5309
+ typeof this.bridge?.sendChatEvent === "function" ? (raw) => this.relayChatRecord(raw) : void 0
5217
5310
  );
5218
5311
  this.tailer.start(startOffset);
5219
5312
  return { settingsPath, socketPath };
5220
5313
  }
5314
+ /**
5315
+ * Project a tailed transcript record to chat events, reconciling them with
5316
+ * any synthetic question card already emitted at hook time: the flushed
5317
+ * AskUserQuestion `tool_use` duplicate is dropped (its real id remembered),
5318
+ * and the paired `tool_result` is re-pointed at the synthetic id so the
5319
+ * live-rendered card is the one that flips to answered.
5320
+ */
5321
+ relayChatRecord(raw) {
5322
+ for (const event of mapChatRecords(raw)) {
5323
+ if (event.kind === "tool_use" && event.name === "AskUserQuestion") {
5324
+ const syntheticId = this.pendingSyntheticQuestionIds.shift();
5325
+ if (syntheticId) {
5326
+ if (event.id) this.questionResultRemap.set(event.id, syntheticId);
5327
+ continue;
5328
+ }
5329
+ } else if (event.kind === "tool_result" && event.toolUseId) {
5330
+ const syntheticId = this.questionResultRemap.get(event.toolUseId);
5331
+ if (syntheticId) {
5332
+ this.questionResultRemap.delete(event.toolUseId);
5333
+ this.sendChatEvent({ ...event, toolUseId: syntheticId });
5334
+ continue;
5335
+ }
5336
+ }
5337
+ this.sendChatEvent(event);
5338
+ }
5339
+ }
5340
+ sendChatEvent(event) {
5341
+ this.bridge?.sendChatEvent?.(event);
5342
+ }
5343
+ /** Render the question card in the web chat NOW — at hook time — instead of
5344
+ * whenever the CLI flushes the transcript records (which is only after the
5345
+ * questionnaire resolves; see the field comment). */
5346
+ emitSyntheticQuestionCard(questions) {
5347
+ if (questions.length === 0 || typeof this.bridge?.sendChatEvent !== "function") return;
5348
+ const id = `aq-${randomUUID2()}`;
5349
+ this.pendingSyntheticQuestionIds.push(id);
5350
+ this.sendChatEvent({
5351
+ kind: "tool_use",
5352
+ name: "AskUserQuestion",
5353
+ input: compactQuestionsJson(questions),
5354
+ id
5355
+ });
5356
+ }
5357
+ /**
5358
+ * Close any still-open synthetic question cards. The questionnaire can only
5359
+ * outlive its card via a path that never flushes the paired records — Esc /
5360
+ * interrupt, a superseding turn, or process teardown — so an answering
5361
+ * tool_result will never arrive for these ids; emit one so the web card
5362
+ * stops soliciting input for a dialog that no longer exists.
5363
+ */
5364
+ closeSyntheticQuestionCards() {
5365
+ const orphaned = [...this.pendingSyntheticQuestionIds, ...this.questionResultRemap.values()];
5366
+ this.pendingSyntheticQuestionIds = [];
5367
+ this.questionResultRemap.clear();
5368
+ for (const toolUseId of orphaned) {
5369
+ this.sendChatEvent({ kind: "tool_result", toolUseId, output: "", isError: false });
5370
+ }
5371
+ }
5221
5372
  writeStdin(text) {
5222
5373
  this.pty?.write(text);
5223
5374
  }
@@ -5251,6 +5402,7 @@ var PtySession = class {
5251
5402
  this._toreDown = true;
5252
5403
  this.disarmPlanDialogAutoAccept();
5253
5404
  this.disarmSubmitNudge();
5405
+ this.closeSyntheticQuestionCards();
5254
5406
  this.unsubInput?.();
5255
5407
  this.unsubInput = null;
5256
5408
  this.unsubResize?.();
@@ -5421,10 +5573,9 @@ var PtySession = class {
5421
5573
  if (request.tool_name === "AskUserQuestion") {
5422
5574
  this.disarmSubmitNudge();
5423
5575
  this.disarmPlanDialogAutoAccept();
5424
- this.pushEvent({
5425
- type: "user_question",
5426
- questions: parseUserQuestions(request.tool_input)
5427
- });
5576
+ const questions = parseUserQuestions(request.tool_input);
5577
+ this.pushEvent({ type: "user_question", questions });
5578
+ this.emitSyntheticQuestionCard(questions);
5428
5579
  return { decision: "allow" };
5429
5580
  }
5430
5581
  const canUseTool = this.turn.canUseTool;
@@ -7493,9 +7644,12 @@ function buildPostToChatTool(connection) {
7493
7644
  "Post a message to the task chat for the team to see. Your turn output is NOT shown in chat, so this is the only way the team sees your status, summaries, and questions. Omit task_id to post to the current task's chat; pass a child's ID to message its chat.",
7494
7645
  {
7495
7646
  message: z9.string().describe("The message to post to the team"),
7496
- task_id: z9.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat.")
7647
+ task_id: z9.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat."),
7648
+ milestone: z9.enum(["plan_ready", "implementation_complete", "blocked"]).optional().describe(
7649
+ "Declare a narrative milestone instead of a routine update. Use SPARINGLY \u2014 only when the plan is ready, implementation is complete, or you are blocked. Milestones appear on the card's activity timeline and in Slack."
7650
+ )
7497
7651
  },
7498
- async ({ message, task_id }) => {
7652
+ async ({ message, task_id, milestone }) => {
7499
7653
  try {
7500
7654
  if (task_id) {
7501
7655
  await connection.call("postChildChatMessage", {
@@ -7516,7 +7670,7 @@ function buildPostToChatTool(connection) {
7516
7670
  })
7517
7671
  );
7518
7672
  }
7519
- await connection.call("postToChat", { message });
7673
+ await connection.call("postToChat", { message, milestone });
7520
7674
  return textResult(JSON.stringify({ posted: true }));
7521
7675
  } catch (error) {
7522
7676
  return textResult(
@@ -7531,7 +7685,16 @@ function buildForceUpdateTaskStatusTool(connection) {
7531
7685
  "force_update_task_status",
7532
7686
  "EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
7533
7687
  {
7534
- status: z9.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
7688
+ status: z9.enum([
7689
+ "Planning",
7690
+ "Open",
7691
+ "InProgress",
7692
+ "ReviewPR",
7693
+ "ReviewDev",
7694
+ "ReviewLive",
7695
+ "Complete",
7696
+ "Cancelled"
7697
+ ]).describe("The new status for the task"),
7535
7698
  task_id: z9.string().optional().describe("Child task ID to update. Omit to update the current task.")
7536
7699
  },
7537
7700
  async ({ status, task_id }) => {
@@ -8096,6 +8259,36 @@ function buildUpdateTaskTool(connection) {
8096
8259
  }
8097
8260
  );
8098
8261
  }
8262
+ function buildHandoffTool(connection) {
8263
+ return defineTool(
8264
+ "handoff_to_agent",
8265
+ "Hand this task off to an implementer agent for the build phase \u2014 mid-conversation, same session, no restart. Call this once the plan is compiled and saved (update_task_plan). The server swaps this task to the difficulty-sized implementer agent (which may run at a different model level), announces the handoff in the activity log + chat, and switches you into build mode to start implementing. Size the work with the storyPoints arg (or set it first via update_task_properties). Returns the implementer's name + model.",
8266
+ {
8267
+ storyPoints: z12.number().int().positive().optional().describe(
8268
+ "Difficulty sizing (1=Common, 2=Magic, 3=Rare, 5=Unique, 8=Pack) \u2014 picks which implementer agent takes over. Omit to use the task's current story points."
8269
+ ),
8270
+ message: z12.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
8271
+ },
8272
+ async ({ storyPoints, message }) => {
8273
+ try {
8274
+ const result = await connection.handoffToImplementer({
8275
+ ...storyPoints !== void 0 && { storyPoints },
8276
+ ...message !== void 0 && { message }
8277
+ });
8278
+ if (!result.handedOff) {
8279
+ return textResult(`Handoff did not complete: ${result.reason ?? "unknown reason"}`);
8280
+ }
8281
+ return textResult(
8282
+ `Handed off to ${result.agentName} (${result.model}). Now in build mode \u2014 start implementing the plan.`
8283
+ );
8284
+ } catch (error) {
8285
+ return textResult(
8286
+ `Failed to hand off: ${error instanceof Error ? error.message : "Unknown error"}`
8287
+ );
8288
+ }
8289
+ }
8290
+ );
8291
+ }
8099
8292
  function buildCreateSubtaskTool(connection) {
8100
8293
  return defineTool(
8101
8294
  "create_subtask",
@@ -8318,42 +8511,52 @@ function buildPmTools(connection, options) {
8318
8511
  // src/tools/discovery-tools.ts
8319
8512
  import { z as z13 } from "zod";
8320
8513
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
8321
- var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch";
8514
+ var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
8515
+ function describeUpdatedFields(p) {
8516
+ const fields = [];
8517
+ if (p.title !== void 0) fields.push(`title to "${p.title}"`);
8518
+ if (p.storyPointValue !== void 0) fields.push(`story points to ${p.storyPointValue}`);
8519
+ if (p.tagNames !== void 0) fields.push(`tags (${p.tagNames.length} tag(s))`);
8520
+ if (p.githubPRUrl !== void 0) fields.push(`PR link to "${p.githubPRUrl}"`);
8521
+ if (p.githubBranch !== void 0) fields.push(`branch to "${p.githubBranch}"`);
8522
+ if (p.risk !== void 0) fields.push(`risk to ${p.risk ?? "cleared"}`);
8523
+ return fields;
8524
+ }
8322
8525
  function buildDiscoveryTools(connection) {
8323
8526
  return [
8324
8527
  defineTool(
8325
8528
  "update_task_properties",
8326
- "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
8529
+ "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
8327
8530
  {
8328
8531
  title: z13.string().optional().describe("The new task title"),
8329
8532
  storyPointValue: z13.number().optional().describe(SP_DESCRIPTION2),
8330
8533
  tagNames: z13.array(z13.string()).optional().describe("Array of tag names to assign"),
8331
8534
  githubPRUrl: z13.string().url().optional().describe("GitHub pull request URL to link to this task"),
8332
- githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
8535
+ githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
8536
+ risk: z13.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
8537
+ "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
8538
+ )
8333
8539
  },
8334
- async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
8540
+ async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk }) => {
8335
8541
  try {
8336
- const nothingToUpdate = title === void 0 && storyPointValue === void 0 && tagNames === void 0 && githubPRUrl === void 0 && githubBranch === void 0;
8337
- if (nothingToUpdate) {
8542
+ const params = {
8543
+ title,
8544
+ storyPointValue,
8545
+ tagNames,
8546
+ githubPRUrl,
8547
+ githubBranch,
8548
+ risk
8549
+ };
8550
+ const updatedFields = describeUpdatedFields(params);
8551
+ if (updatedFields.length === 0) {
8338
8552
  return textResult(
8339
8553
  `No task properties were updated: none of the recognized keys were provided. Valid keys: ${VALID_PROPERTY_KEYS}. (Story points are set via 'storyPointValue', not 'storyPoints'.)`
8340
8554
  );
8341
8555
  }
8342
8556
  await connection.call("updateTaskProperties", {
8343
8557
  sessionId: connection.sessionId,
8344
- title,
8345
- storyPointValue,
8346
- tagNames,
8347
- githubPRUrl,
8348
- githubBranch
8558
+ ...params
8349
8559
  });
8350
- const updatedFields = [];
8351
- if (title !== void 0) updatedFields.push(`title to "${title}"`);
8352
- if (storyPointValue !== void 0)
8353
- updatedFields.push(`story points to ${storyPointValue}`);
8354
- if (tagNames !== void 0) updatedFields.push(`tags (${tagNames.length} tag(s))`);
8355
- if (githubPRUrl !== void 0) updatedFields.push(`PR link to "${githubPRUrl}"`);
8356
- if (githubBranch !== void 0) updatedFields.push(`branch to "${githubBranch}"`);
8357
8560
  return textResult(`Task properties updated: ${updatedFields.join(", ")}`);
8358
8561
  } catch (error) {
8359
8562
  return textResult(
@@ -8513,12 +8716,14 @@ function buildConveyorTools(connection, config, context, agentMode) {
8513
8716
  const modeTools = getModeTools(effectiveMode, connection, config, context);
8514
8717
  const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" || effectiveMode === "building" || effectiveMode === "chat" ? buildDiscoveryTools(connection) : [];
8515
8718
  const codeReviewTools = effectiveMode === "review" ? buildCodeReviewTools(connection) : [];
8719
+ const handoffTools = config.mode === "pm" && (effectiveMode === "discovery" || effectiveMode === "auto") ? [buildHandoffTool(connection)] : [];
8516
8720
  const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
8517
8721
  const tools = withAlwaysLoad([
8518
8722
  ...commonTools,
8519
8723
  ...modeTools,
8520
8724
  ...discoveryTools,
8521
8725
  ...codeReviewTools,
8726
+ ...handoffTools,
8522
8727
  ...emergencyTools
8523
8728
  ]);
8524
8729
  if (effectiveMode === "chat") {
@@ -10504,7 +10709,7 @@ async function runUsageProbe(deps = {}) {
10504
10709
  cols: 120,
10505
10710
  rows: 45,
10506
10711
  cwd,
10507
- env: buildProbeEnv()
10712
+ env: buildProbeEnv(deps.env)
10508
10713
  });
10509
10714
  } catch {
10510
10715
  resolve("");
@@ -12024,10 +12229,12 @@ export {
12024
12229
  DEFAULT_LIFECYCLE_CONFIG,
12025
12230
  Lifecycle,
12026
12231
  defineTool,
12027
- cleanTerminalOutput,
12028
12232
  loadPtySpawn,
12029
12233
  inheritedEnv,
12030
12234
  buildPromptBytes,
12235
+ cleanTerminalOutput,
12236
+ buildSynthesizedCredentials,
12237
+ claudeJsonPath,
12031
12238
  ClaudeTuiAdapter,
12032
12239
  createServiceLogger,
12033
12240
  PtyHarness,
@@ -12044,6 +12251,8 @@ export {
12044
12251
  findOnPath,
12045
12252
  resolvePlaywrightMcpServer,
12046
12253
  resolveSessionStart,
12254
+ parseUsageGauges,
12255
+ runUsageProbe,
12047
12256
  sampleKeyUsage,
12048
12257
  awaitGitReady,
12049
12258
  PortDiscovery,
@@ -12063,4 +12272,4 @@ export {
12063
12272
  runStartCommand,
12064
12273
  unshallowRepo
12065
12274
  };
12066
- //# sourceMappingURL=chunk-P4VWWR5O.js.map
12275
+ //# sourceMappingURL=chunk-PXQJ4NVO.js.map