@rallycry/conveyor-agent 9.1.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -378,6 +378,14 @@ var AgentConnection = class _AgentConnection {
378
378
  static RECONNECT_MAX_DELAY_MS = 6e4;
379
379
  static RECONNECT_STATUS_EVERY_N = 3;
380
380
  isReconnecting = false;
381
+ /**
382
+ * Invoked after every successful session reconnect (the `connectAgent` RPC
383
+ * re-established the session room). The runner uses this to force a TUI
384
+ * repaint: the reconnect may have landed on a different/restarted API
385
+ * process whose PTY scrollback ring is empty, and a quiet terminal would
386
+ * otherwise never re-seed it.
387
+ */
388
+ onReconnected;
381
389
  async reconnectToSession() {
382
390
  if (this.isReconnecting) return;
383
391
  this.isReconnecting = true;
@@ -409,6 +417,10 @@ var AgentConnection = class _AgentConnection {
409
417
  reason: "reconnected",
410
418
  attempts: attempt
411
419
  });
420
+ try {
421
+ this.onReconnected?.();
422
+ } catch {
423
+ }
412
424
  return;
413
425
  } catch (err) {
414
426
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -667,17 +679,6 @@ var AgentConnection = class _AgentConnection {
667
679
  this.sendEvent({ type: "agent_typing_stop" });
668
680
  }
669
681
  // ── RPC convenience wrappers (v6 compat, will migrate to call()) ───
670
- trackSpending(params) {
671
- if (!this.socket) return;
672
- void this.call("trackSpending", {
673
- sessionId: this.config.sessionId,
674
- inputTokens: params.modelUsage?.reduce((s, m) => s + (m.inputTokens ?? 0), 0) ?? 0,
675
- outputTokens: params.modelUsage?.reduce((s, m) => s + (m.outputTokens ?? 0), 0) ?? 0,
676
- costUsd: params.totalCostUsd,
677
- model: params.modelUsage?.[0]?.model ?? "unknown"
678
- }).catch(() => {
679
- });
680
- }
681
682
  emitRateLimitPause(resetsAt) {
682
683
  this.sendEvent({ type: "rate_limit_update", resetsAt });
683
684
  }
@@ -722,9 +723,6 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
722
723
  triggerIdentification() {
723
724
  return this.call("triggerIdentification", { sessionId: this.config.sessionId });
724
725
  }
725
- getCumulativeSpending() {
726
- return this.call("getCumulativeSpending", { sessionId: this.config.sessionId });
727
- }
728
726
  async refreshAuthToken() {
729
727
  const result = await this.refreshFromBootstrap();
730
728
  return result.refreshedClaude;
@@ -3274,6 +3272,21 @@ var PtySession = class {
3274
3272
  } catch {
3275
3273
  }
3276
3274
  }
3275
+ /**
3276
+ * Force the CLI to repaint its full screen by wiggling the pty size
3277
+ * (SIGWINCH). Used after a socket reconnect: the server's scrollback ring is
3278
+ * per-process and starts empty on a new instance, so without a repaint a
3279
+ * quiet TUI would leave the Connected-TUI terminal blank (and hidden) until
3280
+ * the CLI next draws on its own.
3281
+ */
3282
+ forceRepaint() {
3283
+ if (!this.pty) return;
3284
+ try {
3285
+ this.pty.resize(this.cols, Math.max(1, this.rows - 1));
3286
+ this.pty.resize(this.cols, this.rows);
3287
+ } catch {
3288
+ }
3289
+ }
3277
3290
  async teardown() {
3278
3291
  if (this._toreDown) return;
3279
3292
  this._toreDown = true;
@@ -3693,6 +3706,17 @@ var PtyHarness = class {
3693
3706
  this.bridge = bridge;
3694
3707
  }
3695
3708
  bridge;
3709
+ /** The session currently streaming events, if any — repaint target. */
3710
+ activeSession = null;
3711
+ /**
3712
+ * Wiggle the live pty's size so the CLI repaints its whole screen. No-op
3713
+ * between queries or on the SDK harness. Called after a socket reconnect so
3714
+ * the server's (per-process, possibly brand-new) scrollback ring re-seeds
3715
+ * and the Connected-TUI terminal reappears.
3716
+ */
3717
+ forceRepaint() {
3718
+ this.activeSession?.forceRepaint();
3719
+ }
3696
3720
  async *executeQuery(opts) {
3697
3721
  const session = new PtySession(
3698
3722
  opts.prompt,
@@ -3703,11 +3727,13 @@ var PtyHarness = class {
3703
3727
  await ensureClaudeCredentials();
3704
3728
  await ensureClaudeOnboarding(process.env, opts.options.cwd);
3705
3729
  await session.start();
3730
+ this.activeSession = session;
3706
3731
  try {
3707
3732
  for await (const event of session.events()) {
3708
3733
  yield event;
3709
3734
  }
3710
3735
  } finally {
3736
+ if (this.activeSession === session) this.activeSession = null;
3711
3737
  await session.teardown();
3712
3738
  }
3713
3739
  }
@@ -5062,7 +5088,7 @@ var cliEventFormatters = {
5062
5088
  tool_result: (e) => `${e.tool} \u2192 ${e.output?.slice(0, 500) ?? ""}${e.isError ? " [ERROR]" : ""}`,
5063
5089
  message: (e) => e.content ?? "",
5064
5090
  error: (e) => `ERROR: ${e.message ?? ""}`,
5065
- completed: (e) => `Completed: ${e.summary ?? ""} (cost: $${e.costUsd ?? "?"}, duration: ${e.durationMs ?? "?"}ms)`,
5091
+ completed: (e) => `Completed: ${e.summary ?? ""} (duration: ${e.durationMs ?? "?"}ms)`,
5066
5092
  setup_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
5067
5093
  start_command_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
5068
5094
  turn_end: (e) => `Turn complete (${e.toolCalls?.length ?? 0} tool calls)`
@@ -6326,35 +6352,16 @@ function emitContextUpdate(modelUsage, host, context, lastAssistantUsage) {
6326
6352
  });
6327
6353
  }
6328
6354
  }
6329
- function trackCostSpending(host, context, cumulativeTotal) {
6330
- if (cumulativeTotal > 0 && context.agentId && context._runnerSessionId) {
6331
- const breakdown = host.costTracker.modelBreakdown;
6332
- host.connection.trackSpending({
6333
- agentId: context.agentId,
6334
- sessionId: context._runnerSessionId,
6335
- totalCostUsd: cumulativeTotal,
6336
- onSubscription: host.config.mode === "pm" || !!process.env.CLAUDE_CODE_OAUTH_TOKEN,
6337
- modelUsage: breakdown.length > 0 ? breakdown : void 0
6338
- });
6339
- }
6340
- }
6341
6355
  function handleSuccessResult(event, host, context, startTime, lastAssistantUsage) {
6342
6356
  const durationMs = Date.now() - startTime;
6343
6357
  const summary = event.result || "Task completed.";
6344
6358
  const retriable = isRetriableMessage(summary);
6345
- const cumulativeTotal = host.costTracker.addQueryCost(event.total_cost_usd);
6359
+ host.connection.sendEvent({ type: "completed", summary, durationMs });
6346
6360
  const { modelUsage } = event;
6347
- if (modelUsage && typeof modelUsage === "object") {
6348
- host.costTracker.addModelUsage(
6349
- modelUsage
6350
- );
6351
- }
6352
- host.connection.sendEvent({ type: "completed", summary, costUsd: cumulativeTotal, durationMs });
6353
6361
  if (modelUsage && typeof modelUsage === "object") {
6354
6362
  emitContextUpdate(modelUsage, host, context, lastAssistantUsage);
6355
6363
  }
6356
- trackCostSpending(host, context, cumulativeTotal);
6357
- return { totalCostUsd: cumulativeTotal, retriable };
6364
+ return { retriable };
6358
6365
  }
6359
6366
  function handleErrorResult(event, host) {
6360
6367
  const errorMsg = event.errors.length > 0 ? event.errors.join(", ") : `Agent stopped: ${event.subtype}`;
@@ -6383,7 +6390,7 @@ function handleResultEvent(event, host, context, startTime, lastAssistantUsage)
6383
6390
  return { ...result2, resultSummary };
6384
6391
  }
6385
6392
  const result = handleErrorResult(event, host);
6386
- return { totalCostUsd: 0, ...result, resultSummary };
6393
+ return { ...result, resultSummary };
6387
6394
  }
6388
6395
  async function emitResultEvent(event, host, context, startTime, lastAssistantUsage) {
6389
6396
  const result = handleResultEvent(event, host, context, startTime, lastAssistantUsage);
@@ -6394,7 +6401,6 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
6394
6401
  await host.callbacks.onEvent({
6395
6402
  type: "completed",
6396
6403
  summary,
6397
- costUsd: result.totalCostUsd,
6398
6404
  durationMs
6399
6405
  });
6400
6406
  } else if (!result.staleSession) {
@@ -7149,7 +7155,6 @@ function buildQueryOptions(host, context) {
7149
7155
  effort: settings.effort,
7150
7156
  thinking: settings.thinking,
7151
7157
  betas: settings.betas,
7152
- maxBudgetUsd: settings.maxBudgetUsd ?? 50,
7153
7158
  abortController: host.abortController ?? void 0,
7154
7159
  disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
7155
7160
  enableFileCheckpointing: settings.enableFileCheckpointing,
@@ -7548,58 +7553,6 @@ async function runWithRetry(initialQuery, context, host, options) {
7548
7553
  }
7549
7554
  }
7550
7555
 
7551
- // src/execution/cost-tracker.ts
7552
- var CostTracker = class {
7553
- cumulativeCostUsd = 0;
7554
- modelUsage = /* @__PURE__ */ new Map();
7555
- seeded = false;
7556
- /**
7557
- * Rehydrate cumulative spend from the server so `maxBudgetUsd` enforcement
7558
- * accounts for costs incurred by prior agent runs on the same task. Must be
7559
- * called before any `addQueryCost` / `addModelUsage` — re-seeding after
7560
- * costs have accumulated would clobber in-process totals.
7561
- */
7562
- seed(totalCostUsd, modelUsage) {
7563
- if (this.seeded) return;
7564
- if (this.cumulativeCostUsd > 0 || this.modelUsage.size > 0) return;
7565
- this.cumulativeCostUsd = totalCostUsd;
7566
- for (const entry of modelUsage) {
7567
- this.modelUsage.set(entry.model, { ...entry });
7568
- }
7569
- this.seeded = true;
7570
- }
7571
- /** Add cost from a completed query and return the running total */
7572
- addQueryCost(queryCostUsd) {
7573
- this.cumulativeCostUsd += queryCostUsd;
7574
- return this.cumulativeCostUsd;
7575
- }
7576
- /** Merge per-model usage from a completed query */
7577
- addModelUsage(usage) {
7578
- for (const [model, data] of Object.entries(usage)) {
7579
- const existing = this.modelUsage.get(model) ?? {
7580
- model,
7581
- inputTokens: 0,
7582
- outputTokens: 0,
7583
- cacheReadInputTokens: 0,
7584
- cacheCreationInputTokens: 0,
7585
- costUSD: 0
7586
- };
7587
- existing.inputTokens += data.inputTokens ?? 0;
7588
- existing.outputTokens += data.outputTokens ?? 0;
7589
- existing.cacheReadInputTokens += data.cacheReadInputTokens ?? 0;
7590
- existing.cacheCreationInputTokens += data.cacheCreationInputTokens ?? 0;
7591
- existing.costUSD += data.costUSD ?? 0;
7592
- this.modelUsage.set(model, existing);
7593
- }
7594
- }
7595
- get totalCostUsd() {
7596
- return this.cumulativeCostUsd;
7597
- }
7598
- get modelBreakdown() {
7599
- return [...this.modelUsage.values()];
7600
- }
7601
- };
7602
-
7603
7556
  // src/execution/exploration-tracker.ts
7604
7557
  var FILE_REREAD_NUDGE_L1 = 3;
7605
7558
  var FILE_REREAD_NUDGE_L2 = 5;
@@ -7726,7 +7679,6 @@ var QueryBridge = class {
7726
7679
  harnessKind,
7727
7680
  harnessKind === "pty" ? buildPtyBridge(connection) : void 0
7728
7681
  );
7729
- this.costTracker = new CostTracker();
7730
7682
  this.planSync = new PlanSync(workspaceDir, connection);
7731
7683
  }
7732
7684
  connection;
@@ -7737,7 +7689,6 @@ var QueryBridge = class {
7737
7689
  /** Which harness drives this bridge ("pty" task chat; "sdk" only under the
7738
7690
  * CONVEYOR_FORCE_SDK_CARDS maintainer override). */
7739
7691
  harnessKind;
7740
- costTracker;
7741
7692
  planSync;
7742
7693
  sessionIds = /* @__PURE__ */ new Map();
7743
7694
  activeQuery = null;
@@ -7769,6 +7720,13 @@ var QueryBridge = class {
7769
7720
  get wasRateLimited() {
7770
7721
  return this._wasRateLimited;
7771
7722
  }
7723
+ /**
7724
+ * Ask the live terminal (PTY harness only) to redraw its full screen.
7725
+ * Safe no-op on the SDK harness or between queries.
7726
+ */
7727
+ forceRepaint() {
7728
+ this.harness.forceRepaint?.();
7729
+ }
7772
7730
  stop() {
7773
7731
  this._stopped = true;
7774
7732
  this._abortController?.abort();
@@ -7776,10 +7734,6 @@ var QueryBridge = class {
7776
7734
  resume() {
7777
7735
  this._stopped = false;
7778
7736
  }
7779
- /** Rehydrate CostTracker from server-side cumulative spend on agent boot. */
7780
- seedCostTracker(totalCostUsd, modelUsage) {
7781
- this.costTracker.seed(totalCostUsd, modelUsage);
7782
- }
7783
7737
  /**
7784
7738
  * How the INITIAL instructions for this task would be delivered — "prefill"
7785
7739
  * means the TUI input is pre-filled but unsubmitted, awaiting the human.
@@ -7838,7 +7792,6 @@ var QueryBridge = class {
7838
7792
  harness: this.harness,
7839
7793
  harnessKind: this.harnessKind,
7840
7794
  setupLog: [],
7841
- costTracker: this.costTracker,
7842
7795
  explorationTracker: bridge.mode.effectiveMode === "discovery" || bridge.mode.effectiveMode === "auto" && !bridge.mode.hasExitedPlanMode ? new ExplorationTracker(bridge._isParentTask) : null,
7843
7796
  sessionIds: this.sessionIds,
7844
7797
  pendingToolOutputs: this.pendingToolOutputs,
@@ -8148,7 +8101,6 @@ var SessionRunner = class _SessionRunner {
8148
8101
  });
8149
8102
  }
8150
8103
  this.queryBridge = this.createQueryBridge();
8151
- await this.seedCostTrackerFromServer();
8152
8104
  this.logInitialization();
8153
8105
  const staleBatch = [...this.pendingMessages];
8154
8106
  const didExecuteInitialQuery = await this.executeInitialMode();
@@ -8700,6 +8652,7 @@ var SessionRunner = class _SessionRunner {
8700
8652
  this.connection.onMessage((msg) => this.injectMessage(msg));
8701
8653
  this.connection.onStop(() => this.stop());
8702
8654
  this.connection.onSoftStop(() => this.softStop());
8655
+ this.connection.onReconnected = () => this.queryBridge?.forceRepaint();
8703
8656
  this.connection.onModeChange((data) => {
8704
8657
  const action = this.mode.handleModeChange(data.agentMode);
8705
8658
  if (action.type === "start_auto") {
@@ -8732,36 +8685,6 @@ var SessionRunner = class _SessionRunner {
8732
8685
  handlePullBranch(this.config.workspaceDir, branch);
8733
8686
  });
8734
8687
  }
8735
- /**
8736
- * Rehydrate CostTracker from server. Caps at 3s so a slow API call never
8737
- * blocks agent startup — on timeout/error we fall through with a $0 tracker
8738
- * (the existing behavior before this rehydration path was added).
8739
- */
8740
- async seedCostTrackerFromServer() {
8741
- if (!this.queryBridge) return;
8742
- const TIMEOUT_MS = 3e3;
8743
- try {
8744
- const timeout = new Promise((resolve) => {
8745
- setTimeout(() => resolve(null), TIMEOUT_MS);
8746
- });
8747
- const fetched = await Promise.race([this.connection.getCumulativeSpending(), timeout]);
8748
- if (fetched) {
8749
- this.queryBridge.seedCostTracker(fetched.totalCostUsd, fetched.modelUsage);
8750
- process.stderr.write(
8751
- `[conveyor-agent] CostTracker seeded: $${fetched.totalCostUsd.toFixed(4)} across ${fetched.modelUsage.length} model(s)
8752
- `
8753
- );
8754
- } else {
8755
- process.stderr.write(
8756
- "[conveyor-agent] CostTracker seed timed out after 3s \u2014 starting at $0\n"
8757
- );
8758
- }
8759
- } catch (err) {
8760
- const msg = err instanceof Error ? err.message : String(err);
8761
- process.stderr.write(`[conveyor-agent] CostTracker seed failed: ${msg} \u2014 starting at $0
8762
- `);
8763
- }
8764
- }
8765
8688
  /** Proactively refresh the GitHub token before the 1-hour expiry. */
8766
8689
  async refreshGithubToken() {
8767
8690
  try {
@@ -8991,4 +8914,4 @@ export {
8991
8914
  runStartCommand,
8992
8915
  unshallowRepo
8993
8916
  };
8994
- //# sourceMappingURL=chunk-P3QCTUHC.js.map
8917
+ //# sourceMappingURL=chunk-34NA4BCK.js.map