@rallycry/conveyor-agent 10.4.0 → 10.5.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.
@@ -197,6 +197,8 @@ var AgentConnection = class _AgentConnection {
197
197
  earlyPullBranches = [];
198
198
  spawnReviewCallback = null;
199
199
  earlySpawnReviews = [];
200
+ spawnTuiCallback = null;
201
+ earlySpawnTuis = [];
200
202
  // PTY relay (S5 terminal). Single-slot callbacks, set per PtySession run.
201
203
  ptyInputCallback = null;
202
204
  ptyResizeCallback = null;
@@ -404,6 +406,10 @@ var AgentConnection = class _AgentConnection {
404
406
  if (this.spawnReviewCallback) this.spawnReviewCallback(data);
405
407
  else this.earlySpawnReviews.push(data);
406
408
  });
409
+ this.socket.on("session:spawnTui", (data) => {
410
+ if (this.spawnTuiCallback) this.spawnTuiCallback(data);
411
+ else this.earlySpawnTuis.push(data);
412
+ });
407
413
  this.socket.on("session:finalizeSnapshot", () => {
408
414
  this.finalizeSnapshotCallback?.();
409
415
  });
@@ -659,6 +665,25 @@ var AgentConnection = class _AgentConnection {
659
665
  }).catch(() => {
660
666
  });
661
667
  }
668
+ onSpawnTui(callback) {
669
+ this.spawnTuiCallback = callback;
670
+ for (const data of this.earlySpawnTuis) callback(data);
671
+ this.earlySpawnTuis = [];
672
+ }
673
+ /**
674
+ * Report that a same-pod TUI/shell child failed to spawn (fire-and-forget).
675
+ * The server Ends the orphaned session — no fallback pod (unlike review).
676
+ * sessionId is OUR (builder) session — the SEC-9 guard runs on it.
677
+ */
678
+ reportSessionSpawnFailure(spawnedSessionId, error) {
679
+ if (!this.socket) return;
680
+ void this.call("reportSessionSpawnFailure", {
681
+ sessionId: this.config.sessionId,
682
+ spawnedSessionId,
683
+ ...error ? { error: error.slice(0, 2e3) } : {}
684
+ }).catch(() => {
685
+ });
686
+ }
662
687
  onFinalizeSnapshot(callback) {
663
688
  this.finalizeSnapshotCallback = callback;
664
689
  }
@@ -719,13 +744,16 @@ var AgentConnection = class _AgentConnection {
719
744
  };
720
745
  }
721
746
  // ── Convenience methods (thin wrappers around call / emit) ─────────
722
- async emitStatus(status, reason) {
747
+ async emitStatus(status, reason, questionText) {
723
748
  this.lastEmittedStatus = status;
724
749
  await this.flushEvents();
725
750
  const payload = {
726
751
  sessionId: this.config.sessionId,
727
752
  status,
728
- ...reason ? { reason } : {}
753
+ ...reason ? { reason } : {},
754
+ // Only sent with a pending TUI questionnaire (reason "user_question") so
755
+ // the server can surface the real question text in the notification.
756
+ ...questionText ? { questionText } : {}
729
757
  };
730
758
  const AWAIT_STATUSES = ["idle", "waiting_for_input", "connected"];
731
759
  if (AWAIT_STATUSES.includes(status)) {
@@ -2046,7 +2074,14 @@ var ReportAgentStatusRequestSchema = z.object({
2046
2074
  sessionId: z.string(),
2047
2075
  status: z.string(),
2048
2076
  /** Why the agent reports this status (e.g. "user_question" while an AskUserQuestion questionnaire is pending in the TUI). */
2049
- reason: z.string().optional()
2077
+ reason: z.string().optional(),
2078
+ /**
2079
+ * The pending question text, sent only alongside `reason: "user_question"`
2080
+ * so the server can surface it in the user-question notification body (and
2081
+ * thus the Attention feed) instead of a generic string. Optional: older
2082
+ * agents omit it and the server falls back to the generic wording.
2083
+ */
2084
+ questionText: z.string().optional()
2050
2085
  });
2051
2086
  var NotifyAgentVersionRequestSchema = z.object({
2052
2087
  sessionId: z.string(),
@@ -2069,7 +2104,10 @@ var CreateSubtaskRequestSchema = z.object({
2069
2104
  plan: z.string().optional(),
2070
2105
  storyPointValue: z.number().int().positive().optional(),
2071
2106
  ordinal: z.number().int().nonnegative().optional(),
2072
- followParentStatus: z.boolean().optional()
2107
+ followParentStatus: z.boolean().optional(),
2108
+ /** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
2109
+ * metadata — preferred over encoding order in plan text / ordinal). */
2110
+ dependsOn: z.array(z.string().min(1)).max(32).optional()
2073
2111
  });
2074
2112
  var UpdateSubtaskRequestSchema = z.object({
2075
2113
  sessionId: z.string(),
@@ -2079,7 +2117,10 @@ var UpdateSubtaskRequestSchema = z.object({
2079
2117
  plan: z.string().optional(),
2080
2118
  status: z.string().optional(),
2081
2119
  storyPointValue: z.number().int().positive().optional(),
2082
- followParentStatus: z.boolean().optional()
2120
+ followParentStatus: z.boolean().optional(),
2121
+ /** Replace this subtask's dependency edges with these sibling ids/slugs.
2122
+ * Empty array clears all. Omit to leave dependencies unchanged. */
2123
+ dependsOn: z.array(z.string().min(1)).max(32).optional()
2083
2124
  });
2084
2125
  var DeleteSubtaskRequestSchema = z.object({
2085
2126
  sessionId: z.string(),
@@ -2263,6 +2304,15 @@ var ReportReviewSpawnFailureRequestSchema = z.object({
2263
2304
  reviewSessionId: z.string(),
2264
2305
  error: z.string().max(2e3).optional()
2265
2306
  });
2307
+ var SpawnTaskSessionRequestSchema = z.object({
2308
+ taskId: z.string(),
2309
+ kind: z.enum(["tui", "shell"])
2310
+ });
2311
+ var ReportSessionSpawnFailureRequestSchema = z.object({
2312
+ sessionId: z.string(),
2313
+ spawnedSessionId: z.string(),
2314
+ error: z.string().max(2e3).optional()
2315
+ });
2266
2316
  var RefreshGithubTokenResponseSchema = z.object({
2267
2317
  token: z.string()
2268
2318
  });
@@ -2500,6 +2550,9 @@ var CreateProjectSubtaskRequestSchema = z2.object({
2500
2550
  ordinal: z2.number().int().nonnegative().optional(),
2501
2551
  storyPointValue: z2.number().int().positive().optional(),
2502
2552
  followParentStatus: z2.boolean().optional(),
2553
+ /** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
2554
+ * metadata — preferred over encoding order in plan text / ordinal). */
2555
+ dependsOn: z2.array(z2.string().min(1)).max(32).optional(),
2503
2556
  requestingUserId: z2.string().optional()
2504
2557
  });
2505
2558
  var UpdateProjectSubtaskRequestSchema = z2.object({
@@ -3950,6 +4003,9 @@ async function startToolServers(mcpServers, tempDir) {
3950
4003
  }
3951
4004
 
3952
4005
  // src/harness/pty/spawn-args.ts
4006
+ function resolveClaudeBinary() {
4007
+ return process.env.CONVEYOR_CLAUDE_BIN ?? "claude";
4008
+ }
3953
4009
  function buildSpawnArgs(input) {
3954
4010
  const args = [];
3955
4011
  if (input.resume) {
@@ -4239,9 +4295,9 @@ function extractSpawn(mod) {
4239
4295
  }
4240
4296
  async function loadPtySpawn() {
4241
4297
  const mod = await import("node-pty");
4242
- const spawn2 = extractSpawn(mod);
4243
- if (!spawn2) throw new Error("node-pty: spawn export not found");
4244
- return spawn2;
4298
+ const spawn3 = extractSpawn(mod);
4299
+ if (!spawn3) throw new Error("node-pty: spawn export not found");
4300
+ return spawn3;
4245
4301
  }
4246
4302
  function inheritedEnv(socketPath) {
4247
4303
  const env = {};
@@ -4740,8 +4796,8 @@ var PtySession = class {
4740
4796
  // server doesn't load).
4741
4797
  ...this.mcpConfigPath ? { mcpConfigPath: this.mcpConfigPath } : {}
4742
4798
  });
4743
- const spawn2 = await loadPtySpawn();
4744
- const pty = spawn2(spec.file, spec.args, {
4799
+ const spawn3 = await loadPtySpawn();
4800
+ const pty = spawn3(spec.file, spec.args, {
4745
4801
  name: "xterm-color",
4746
4802
  cols: this.cols,
4747
4803
  rows: this.rows,
@@ -5195,7 +5251,7 @@ function buildPackRunnerSystemPrompt(context, config, setupLog) {
5195
5251
  ``,
5196
5252
  `## Important Rules`,
5197
5253
  `- When dependencies are set on children, use them to determine execution order. Fire all ready tasks in parallel (up to the PACK_CHILD_LIMIT backpressure \u2014 see the loop above).`,
5198
- `- When NO dependencies are set on any children, fall back to ordinal order (one at a time). This preserves backward compatibility.`,
5254
+ `- Dependencies are explicit card metadata (set at creation via create_subtask's dependsOn, or rewired with update_subtask). Prefer them over reading order out of plan text. When NO dependencies are set on any children, fall back to ordinal order (one at a time) \u2014 this is legacy behavior; set dependsOn when a child truly blocks on another.`,
5199
5255
  `- After firing builds OR when waiting on CI, explicitly state you are going idle. Go idle when waiting \u2014 the system wakes you (or relaunches this environment) when a child changes status.`,
5200
5256
  `- Do NOT attempt to write code yourself. Your role is coordination only.`,
5201
5257
  `- If a child is stuck in "InProgress" for an unusually long time, use get_execution_logs(childTaskId) to check its logs and escalate to the team if it appears stuck.`,
@@ -5816,7 +5872,8 @@ function buildDiscoveryPrompt(context, runnerMode) {
5816
5872
  `### Parent Task Coordination`,
5817
5873
  `You are a parent task with child tasks. Focus on breaking work into child tasks with detailed plans, not planning implementation for yourself.`,
5818
5874
  `- Use \`list_subtasks\` to review existing children. Create or update child tasks using \`create_subtask\` / \`update_subtask\`.`,
5819
- `- Each child task should be a self-contained unit of work with a clear plan.`
5875
+ `- Each child task should be a self-contained unit of work with a clear plan.`,
5876
+ `- Set ordering as explicit **card metadata**, not prose: pass \`dependsOn\` (sibling ids/slugs) on \`create_subtask\` for any child that blocks on another. Leave independent children with no dependencies so the pack runner fans them out in parallel. Do NOT encode "do X after Y" only in the plan text \u2014 the runner schedules off dependsOn, not narrative.`
5820
5877
  ] : [
5821
5878
  `### Self-Update vs Subtasks`,
5822
5879
  `- If the work fits in a single task (1-3 SP), update YOUR OWN plan and properties \u2014 do not create subtasks`,
@@ -5830,6 +5887,7 @@ function buildDiscoveryPrompt(context, runnerMode) {
5830
5887
  `- Reference existing implementations when relevant (e.g., "follow the pattern in src/services/foo.ts")`,
5831
5888
  `- Include testing requirements and acceptance criteria`,
5832
5889
  `- Set \`storyPointValue\` based on estimated complexity`,
5890
+ `- Express cross-child ordering as \`dependsOn\` (sibling ids/slugs) on \`create_subtask\` \u2014 explicit dependency metadata, not "after task 2" phrasing in the plan. Independent children get no dependencies so they run in parallel.`,
5833
5891
  ``,
5834
5892
  `### Plan Verification Requirements`,
5835
5893
  `Every plan MUST include a **Testing / Verification** section so the build agent has a clear definition of "done". Enumerate:`,
@@ -5876,6 +5934,7 @@ function buildAutoPrompt(context, runnerMode) {
5876
5934
  `- Reference existing implementations when relevant`,
5877
5935
  `- Include testing requirements and acceptance criteria`,
5878
5936
  `- Set \`storyPointValue\` based on estimated complexity`,
5937
+ `- Express cross-child ordering as \`dependsOn\` (sibling ids/slugs) on \`create_subtask\` \u2014 explicit dependency metadata, not "after task 2" phrasing in the plan. Independent children get no dependencies so they run in parallel.`,
5879
5938
  ``,
5880
5939
  ...buildPlanCitationFormat(),
5881
5940
  ``,
@@ -5884,7 +5943,8 @@ function buildAutoPrompt(context, runnerMode) {
5884
5943
  `### Parent Task Guidance`,
5885
5944
  `You are a parent task. Your plan should define child tasks with detailed plans, not direct implementation steps.`,
5886
5945
  `After ExitPlanMode, you'll transition to Review mode to coordinate child task execution.`,
5887
- `Child task status lifecycle: Open \u2192 InProgress \u2192 ReviewPR \u2192 ReviewDev \u2192 Complete.`
5946
+ `Child task status lifecycle: Open \u2192 InProgress \u2192 ReviewPR \u2192 ReviewDev \u2192 Complete.`,
5947
+ `Set child ordering with \`dependsOn\` (sibling ids/slugs) on \`create_subtask\` \u2014 explicit metadata the pack runner schedules off, not order described in plan text. Independent children get none and run in parallel.`
5888
5948
  ] : [],
5889
5949
  ``,
5890
5950
  `### Autonomous Guidelines:`,
@@ -7359,6 +7419,7 @@ function buildCommonTools(connection, config) {
7359
7419
  import { z as z9 } from "zod";
7360
7420
  var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
7361
7421
  var FOLLOW_PARENT_STATUS_DESCRIPTION = "Child mirrors the parent task's status automatically \u2014 for subtasks that ship on the parent's branch/PR with no build or PR of their own. Manual status writes on a follower stick only until the parent's next transition.";
7422
+ var DEPENDS_ON_DESCRIPTION = "Sibling subtask ids or slugs this subtask blocks on (it won't start until they merge to dev). Set explicit dependency metadata here instead of describing order in the plan text \u2014 the pack runner schedules children off these edges. Omit / leave empty for independent children so they run in parallel.";
7362
7423
  function buildUpdateTaskTool(connection) {
7363
7424
  return defineTool(
7364
7425
  "update_task_plan",
@@ -7391,9 +7452,18 @@ function buildCreateSubtaskTool(connection) {
7391
7452
  plan: z9.string().optional().describe("Implementation plan in markdown"),
7392
7453
  ordinal: z9.number().optional().describe("Step/order number (0-based)"),
7393
7454
  storyPointValue: z9.number().optional().describe(SP_DESCRIPTION),
7394
- followParentStatus: z9.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION)
7455
+ followParentStatus: z9.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
7456
+ dependsOn: z9.array(z9.string()).optional().describe(DEPENDS_ON_DESCRIPTION)
7395
7457
  },
7396
- async ({ title, description, plan, ordinal, storyPointValue, followParentStatus }) => {
7458
+ async ({
7459
+ title,
7460
+ description,
7461
+ plan,
7462
+ ordinal,
7463
+ storyPointValue,
7464
+ followParentStatus,
7465
+ dependsOn
7466
+ }) => {
7397
7467
  try {
7398
7468
  const result = await connection.call("createSubtask", {
7399
7469
  sessionId: connection.sessionId,
@@ -7402,9 +7472,10 @@ function buildCreateSubtaskTool(connection) {
7402
7472
  ...plan !== void 0 && { plan },
7403
7473
  ...storyPointValue !== void 0 && { storyPointValue },
7404
7474
  ...ordinal !== void 0 && { ordinal },
7405
- ...followParentStatus !== void 0 && { followParentStatus }
7475
+ ...followParentStatus !== void 0 && { followParentStatus },
7476
+ ...dependsOn !== void 0 && { dependsOn }
7406
7477
  });
7407
- return textResult(`Subtask created with ID: ${result.id}`);
7478
+ return textResult(`Subtask created with ID: ${result.id} (slug: ${result.slug})`);
7408
7479
  } catch (error) {
7409
7480
  return textResult(
7410
7481
  `Failed to create subtask: ${error instanceof Error ? error.message : "Unknown error"}`
@@ -7416,7 +7487,7 @@ function buildCreateSubtaskTool(connection) {
7416
7487
  function buildUpdateSubtaskTool(connection) {
7417
7488
  return defineTool(
7418
7489
  "update_subtask",
7419
- "Update an existing subtask's fields (title, description, plan, ordinal, storyPointValue). Use when refining a child's plan or reordering. For the current task use update_task_plan.",
7490
+ "Update an existing subtask's fields (title, description, plan, ordinal, storyPointValue, dependsOn). Use when refining a child's plan, reordering, or rewiring dependencies. For the current task use update_task_plan.",
7420
7491
  {
7421
7492
  subtaskId: z9.string().describe("The subtask ID to update"),
7422
7493
  title: z9.string().optional(),
@@ -7424,9 +7495,20 @@ function buildUpdateSubtaskTool(connection) {
7424
7495
  plan: z9.string().optional(),
7425
7496
  ordinal: z9.number().optional(),
7426
7497
  storyPointValue: z9.number().optional().describe(SP_DESCRIPTION),
7427
- followParentStatus: z9.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION)
7498
+ followParentStatus: z9.boolean().optional().describe(FOLLOW_PARENT_STATUS_DESCRIPTION),
7499
+ dependsOn: z9.array(z9.string()).optional().describe(
7500
+ `${DEPENDS_ON_DESCRIPTION} Replaces the full dependency set \u2014 pass [] to clear all, omit to leave unchanged.`
7501
+ )
7428
7502
  },
7429
- async ({ subtaskId, title, description, plan, storyPointValue, followParentStatus }) => {
7503
+ async ({
7504
+ subtaskId,
7505
+ title,
7506
+ description,
7507
+ plan,
7508
+ storyPointValue,
7509
+ followParentStatus,
7510
+ dependsOn
7511
+ }) => {
7430
7512
  try {
7431
7513
  await connection.call("updateSubtask", {
7432
7514
  sessionId: connection.sessionId,
@@ -7435,7 +7517,8 @@ function buildUpdateSubtaskTool(connection) {
7435
7517
  ...description !== void 0 && { description },
7436
7518
  ...plan !== void 0 && { plan },
7437
7519
  ...storyPointValue !== void 0 && { storyPointValue },
7438
- ...followParentStatus !== void 0 && { followParentStatus }
7520
+ ...followParentStatus !== void 0 && { followParentStatus },
7521
+ ...dependsOn !== void 0 && { dependsOn }
7439
7522
  });
7440
7523
  return textResult("Subtask updated.");
7441
7524
  } catch (error) {
@@ -8040,9 +8123,17 @@ async function handleResultCase(event, host, context, startTime, isTyping, lastA
8040
8123
  function hasAskUserQuestionBlock(event) {
8041
8124
  return event.message.content.some((b) => b.type === "tool_use" && b.name === "AskUserQuestion");
8042
8125
  }
8043
- async function armQuestionPending(host, state) {
8126
+ function questionTextFromEvent(event) {
8127
+ const text = event.questions.map((q) => q.question.trim()).filter((q) => q.length > 0).join("\n");
8128
+ return text.length > 0 ? text : void 0;
8129
+ }
8130
+ async function armQuestionPending(host, state, event) {
8044
8131
  state.questionPending = true;
8045
- await host.connection.emitStatus("waiting_for_input", AGENT_STATUS_REASON_USER_QUESTION);
8132
+ await host.connection.emitStatus(
8133
+ "waiting_for_input",
8134
+ AGENT_STATUS_REASON_USER_QUESTION,
8135
+ questionTextFromEvent(event)
8136
+ );
8046
8137
  await host.callbacks.onStatusChange("waiting_for_input");
8047
8138
  }
8048
8139
  async function clearQuestionPending(host, state, options) {
@@ -8056,7 +8147,7 @@ async function clearQuestionPending(host, state, options) {
8056
8147
  async function applyQuestionTransitions(event, host, state) {
8057
8148
  switch (event.type) {
8058
8149
  case "user_question":
8059
- await armQuestionPending(host, state);
8150
+ await armQuestionPending(host, state, event);
8060
8151
  return;
8061
8152
  case "assistant":
8062
8153
  if (!hasAskUserQuestionBlock(event)) {
@@ -9493,74 +9584,77 @@ function readAgentVersion() {
9493
9584
  return null;
9494
9585
  }
9495
9586
 
9496
- // src/execution/usage-sampler.ts
9497
- var logger4 = createServiceLogger("usage-sampler");
9498
- var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
9499
- var OAUTH_USAGE_BETA = "oauth-2025-04-20";
9500
- var KNOWN_RATE_LIMIT_TYPES = [
9501
- "five_hour",
9502
- "seven_day",
9503
- "seven_day_opus",
9504
- "seven_day_sonnet"
9505
- ];
9506
- function normalizeUtilization(raw) {
9507
- if (typeof raw !== "number" || !Number.isFinite(raw)) return null;
9508
- const fraction = raw > 1 ? raw / 100 : raw;
9509
- return Math.max(0, Math.min(1, fraction));
9510
- }
9511
- function extractBucket(value) {
9512
- if (typeof value === "number") {
9513
- const utilization = normalizeUtilization(value);
9514
- return utilization === null ? null : { utilization, status: "allowed" };
9515
- }
9516
- if (value && typeof value === "object") {
9517
- const obj = value;
9518
- const utilization = normalizeUtilization(
9519
- obj.utilization ?? obj.used ?? obj.percent ?? obj.percentage
9520
- );
9521
- if (utilization === null) return null;
9522
- const status = typeof obj.status === "string" ? obj.status : "allowed";
9523
- return { utilization, status };
9524
- }
9525
- return null;
9587
+ // src/usage/parse-usage.ts
9588
+ function parseUsageGauges(stdout) {
9589
+ const session = stdout.match(/Current session:\s*(\d+(?:\.\d+)?)%\s*used/i);
9590
+ const weekly = [...stdout.matchAll(/Current week[^:\n]*:\s*(\d+(?:\.\d+)?)%\s*used/gi)];
9591
+ return {
9592
+ sessionUsage: session ? Number(session[1]) / 100 : null,
9593
+ weeklyUsage: weekly.length ? Math.max(...weekly.map((m) => Number(m[1]))) / 100 : null
9594
+ };
9526
9595
  }
9527
- function mapUsageResponse(data) {
9528
- if (!data || typeof data !== "object") return [];
9529
- const root = data;
9530
- const candidates = [root];
9531
- for (const key of ["usage", "rate_limits", "rateLimits"]) {
9532
- const nested = root[key];
9533
- if (nested && typeof nested === "object") {
9534
- candidates.push(nested);
9535
- }
9536
- }
9537
- for (const container of candidates) {
9538
- const samples = [];
9539
- for (const type of KNOWN_RATE_LIMIT_TYPES) {
9540
- const bucket = extractBucket(container[type]);
9541
- if (bucket) samples.push({ rateLimitType: type, ...bucket });
9596
+
9597
+ // src/usage/run-probe.ts
9598
+ import { spawn } from "child_process";
9599
+ var PROBE_TIMEOUT_MS = 15e3;
9600
+ function runUsageProbe(binary = resolveClaudeBinary(), args = ["-p", "/usage"]) {
9601
+ return new Promise((resolve) => {
9602
+ let stdout = "";
9603
+ let settled = false;
9604
+ const finish = (out) => {
9605
+ if (settled) return;
9606
+ settled = true;
9607
+ resolve(out);
9608
+ };
9609
+ let child;
9610
+ try {
9611
+ child = spawn(binary, args, { stdio: ["ignore", "pipe", "ignore"] });
9612
+ } catch {
9613
+ finish("");
9614
+ return;
9542
9615
  }
9543
- if (samples.length > 0) return samples;
9544
- }
9545
- return [];
9616
+ const timer = setTimeout(() => {
9617
+ try {
9618
+ child.kill("SIGKILL");
9619
+ } catch {
9620
+ }
9621
+ finish("");
9622
+ }, PROBE_TIMEOUT_MS);
9623
+ timer.unref?.();
9624
+ child.stdout?.on("data", (d) => {
9625
+ stdout += d.toString();
9626
+ });
9627
+ child.on("error", () => {
9628
+ clearTimeout(timer);
9629
+ finish("");
9630
+ });
9631
+ child.on("close", (code) => {
9632
+ clearTimeout(timer);
9633
+ finish(code === 0 ? stdout : "");
9634
+ });
9635
+ });
9546
9636
  }
9547
- async function sampleKeyUsage(token) {
9637
+
9638
+ // src/execution/usage-sampler.ts
9639
+ var logger4 = createServiceLogger("usage-sampler");
9640
+ async function sampleKeyUsage(token, probe = runUsageProbe) {
9548
9641
  if (!token) return [];
9549
9642
  try {
9550
- const res = await fetch(OAUTH_USAGE_URL, {
9551
- headers: {
9552
- Authorization: `Bearer ${token}`,
9553
- "anthropic-beta": OAUTH_USAGE_BETA
9554
- }
9555
- });
9556
- if (!res.ok) {
9557
- logger4.info("OAuth usage endpoint returned non-200", { status: res.status });
9558
- return [];
9643
+ const stdout = await probe();
9644
+ const { sessionUsage, weeklyUsage } = parseUsageGauges(stdout);
9645
+ const samples = [];
9646
+ if (sessionUsage !== null) {
9647
+ samples.push({ rateLimitType: "five_hour", utilization: sessionUsage, status: "allowed" });
9559
9648
  }
9560
- const data = await res.json();
9561
- return mapUsageResponse(data);
9649
+ if (weeklyUsage !== null) {
9650
+ samples.push({ rateLimitType: "seven_day", utilization: weeklyUsage, status: "allowed" });
9651
+ }
9652
+ if (samples.length === 0) {
9653
+ logger4.info("usage sample produced no gauges", { stdoutLength: stdout.length });
9654
+ }
9655
+ return samples;
9562
9656
  } catch (error) {
9563
- logger4.info("OAuth usage sample failed", {
9657
+ logger4.info("usage sample failed", {
9564
9658
  error: error instanceof Error ? error.message : String(error)
9565
9659
  });
9566
9660
  return [];
@@ -10080,7 +10174,11 @@ var SessionRunner = class _SessionRunner {
10080
10174
  const staleBatch = [...this.pendingMessages];
10081
10175
  const didExecuteInitialQuery = await this.executeInitialMode();
10082
10176
  if (staleBatch.length > 0 && didExecuteInitialQuery) {
10177
+ const historyContents = new Set(
10178
+ (this.fullContext?.chatHistory ?? []).filter((m) => m.role !== "assistant" && m.content.trim()).map((m) => m.content.trim())
10179
+ );
10083
10180
  for (const stale of staleBatch) {
10181
+ if (stale.content.trim() && !historyContents.has(stale.content.trim())) continue;
10084
10182
  const idx = this.pendingMessages.indexOf(stale);
10085
10183
  if (idx !== -1) this.pendingMessages.splice(idx, 1);
10086
10184
  }
@@ -10437,8 +10535,8 @@ var SessionRunner = class _SessionRunner {
10437
10535
  this.periodicFlushInFlight = false;
10438
10536
  }
10439
10537
  }
10440
- /** Sample the running Claude subscription key's rate-limit utilization from the
10441
- * Anthropic OAuth usage endpoint and report it as `rate_limit_update` events.
10538
+ /** Sample the running Claude subscription key's rate-limit utilization from
10539
+ * the Claude CLI `/usage` command and report it as `rate_limit_update` events.
10442
10540
  * The API (`persistRateLimitSnapshot`) attributes them to the key this session
10443
10541
  * launched under, keeping User Settings + the PtY-tab usage widget fresh and
10444
10542
  * `selectBestKey` rotation honest. Best-effort — never throws, no-op when the
@@ -10933,10 +11031,10 @@ function loadConveyorConfig() {
10933
11031
  }
10934
11032
 
10935
11033
  // src/setup/commands.ts
10936
- import { spawn, execSync } from "child_process";
11034
+ import { spawn as spawn2, execSync } from "child_process";
10937
11035
  function runSetupCommand(cmd, cwd, onOutput) {
10938
11036
  return new Promise((resolve, reject) => {
10939
- const child = spawn("sh", ["-c", cmd], {
11037
+ const child = spawn2("sh", ["-c", cmd], {
10940
11038
  cwd,
10941
11039
  stdio: ["ignore", "pipe", "pipe"],
10942
11040
  env: { ...process.env }
@@ -10975,7 +11073,7 @@ function runAuthTokenCommand(cmd, userEmail, cwd) {
10975
11073
  }
10976
11074
  }
10977
11075
  function runStartCommand(cmd, cwd, onOutput) {
10978
- const child = spawn("sh", ["-c", cmd], {
11076
+ const child = spawn2("sh", ["-c", cmd], {
10979
11077
  cwd,
10980
11078
  stdio: ["ignore", "pipe", "pipe"],
10981
11079
  detached: true,
@@ -11013,6 +11111,7 @@ export {
11013
11111
  DEFAULT_LIFECYCLE_CONFIG,
11014
11112
  Lifecycle,
11015
11113
  cleanTerminalOutput,
11114
+ loadPtySpawn,
11016
11115
  inheritedEnv,
11017
11116
  buildPromptBytes,
11018
11117
  ClaudeTuiAdapter,
@@ -11046,4 +11145,4 @@ export {
11046
11145
  runStartCommand,
11047
11146
  unshallowRepo
11048
11147
  };
11049
- //# sourceMappingURL=chunk-TZYEU4QE.js.map
11148
+ //# sourceMappingURL=chunk-MSDYMAG3.js.map