@indigoai-us/hq-cli 5.275.0 → 5.277.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.
Files changed (120) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +12 -0
  3. package/assets/bot-workers/setup/context/USER-GUIDE.md +4 -4
  4. package/assets/scaffold/core/scripts/hq-delegate-pickup.sh +99 -0
  5. package/assets/scaffold/core/scripts/hq-delegate-repo.sh +158 -0
  6. package/assets/scaffold/core/scripts/hq-delegate-resolve.sh +145 -0
  7. package/assets/scaffold/core/scripts/hq-delegate-secrets.sh +129 -0
  8. package/assets/scaffold/core/scripts/hq-delegate-transfer.sh +175 -0
  9. package/assets/scaffold/core/scripts/lib/secret-patterns.sh +58 -0
  10. package/dist/command-catalog.generated.d.ts +239 -1
  11. package/dist/command-catalog.generated.js +302 -1
  12. package/dist/commands/core-checkpoint.d.ts +4 -2
  13. package/dist/commands/core-checkpoint.js +34 -3
  14. package/dist/commands/core-command-catalog.d.ts +4 -2
  15. package/dist/commands/core-command-catalog.js +5 -2
  16. package/dist/commands/core-delegate.d.ts +6 -0
  17. package/dist/commands/core-delegate.js +987 -0
  18. package/dist/commands/core-ontology.js +3 -3
  19. package/dist/commands/core.js +11 -1
  20. package/dist/commands/daemon.d.ts +4 -0
  21. package/dist/commands/daemon.js +175 -2
  22. package/dist/commands/dm.d.ts +2 -0
  23. package/dist/commands/dm.js +11 -4
  24. package/dist/commands/feedback.d.ts +6 -0
  25. package/dist/commands/feedback.js +86 -37
  26. package/dist/commands/lanes-accounts.js +44 -2
  27. package/dist/commands/lanes-message.js +69 -12
  28. package/dist/commands/lanes-resume.js +2 -0
  29. package/dist/commands/lanes-shared.d.ts +3 -0
  30. package/dist/commands/lanes-shared.js +12 -2
  31. package/dist/commands/lanes.d.ts +5 -1
  32. package/dist/commands/lanes.js +402 -49
  33. package/dist/commands/run-feature-telemetry.d.ts +3 -0
  34. package/dist/commands/run-feature-telemetry.js +6 -0
  35. package/dist/commands/run.js +3 -2
  36. package/dist/commands/secrets.d.ts +16 -1
  37. package/dist/commands/secrets.js +80 -45
  38. package/dist/commands/sync-mode.d.ts +11 -0
  39. package/dist/commands/sync-mode.js +30 -0
  40. package/dist/commands/whoami.d.ts +4 -6
  41. package/dist/commands/whoami.js +39 -8
  42. package/dist/fast-core.d.ts +6 -7
  43. package/dist/fast-core.js +22 -8
  44. package/dist/fast-scaffold.d.ts +33 -7
  45. package/dist/fast-scaffold.js +104 -8
  46. package/dist/index.js +32 -1
  47. package/dist/lanes-drain-hook.js +154 -7
  48. package/dist/lib/agent-kit/run/inbox.js +3 -2
  49. package/dist/lib/billing/plan-lock.d.ts +3 -1
  50. package/dist/lib/billing/plan-lock.js +7 -1
  51. package/dist/lib/checkpoint-ontology/apply.d.ts +6 -0
  52. package/dist/lib/checkpoint-ontology/apply.js +60 -3
  53. package/dist/lib/checkpoint-ontology/company.d.ts +8 -4
  54. package/dist/lib/checkpoint-ontology/company.js +15 -7
  55. package/dist/lib/checkpoint-ontology/plan.d.ts +36 -0
  56. package/dist/lib/checkpoint-ontology/plan.js +66 -4
  57. package/dist/lib/checkpoint-ontology/queued-touches.d.ts +32 -0
  58. package/dist/lib/checkpoint-ontology/queued-touches.js +193 -0
  59. package/dist/lib/checkpoint-ontology/tree.d.ts +2 -0
  60. package/dist/lib/checkpoint-ontology/tree.js +34 -0
  61. package/dist/lib/core-utils/jq-json-serializer.d.ts +5 -0
  62. package/dist/lib/core-utils/jq-json-serializer.js +10 -0
  63. package/dist/lib/core-utils/secret-patterns.d.ts +41 -0
  64. package/dist/lib/core-utils/secret-patterns.js +33 -0
  65. package/dist/lib/core-utils/timeout-guard-native.d.ts +7 -0
  66. package/dist/lib/core-utils/timeout-guard-native.js +40 -0
  67. package/dist/lib/core-utils/timeout-guard.d.ts +3 -3
  68. package/dist/lib/core-utils/timeout-guard.js +28 -13
  69. package/dist/lib/daemon/catalog.d.ts +18 -2
  70. package/dist/lib/daemon/catalog.js +18 -2
  71. package/dist/lib/daemon/config.d.ts +6 -0
  72. package/dist/lib/daemon/config.js +15 -2
  73. package/dist/lib/daemon/requests.d.ts +4 -0
  74. package/dist/lib/daemon/requests.js +2 -0
  75. package/dist/lib/daemon/run.js +94 -8
  76. package/dist/lib/daemon/supervisor.d.ts +8 -0
  77. package/dist/lib/daemon/supervisor.js +101 -1
  78. package/dist/lib/daemon/sync-runner.d.ts +58 -0
  79. package/dist/lib/daemon/sync-runner.js +277 -0
  80. package/dist/lib/doctor/__testing__/run-doctor.d.ts +8 -0
  81. package/dist/lib/doctor/__testing__/run-doctor.js +52 -0
  82. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  83. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  84. package/dist/lib/flag-registry.d.ts +12 -7
  85. package/dist/lib/flag-registry.js +66 -75
  86. package/dist/lib/flag-snapshot-file.d.ts +20 -0
  87. package/dist/lib/flag-snapshot-file.js +91 -0
  88. package/dist/lib/lanes/claude-balance.d.ts +6 -1
  89. package/dist/lib/lanes/claude-balance.js +46 -8
  90. package/dist/lib/lanes/config.js +15 -0
  91. package/dist/lib/lanes/hire-grant-dispatch.d.ts +2 -0
  92. package/dist/lib/lanes/hire-grant-dispatch.js +41 -0
  93. package/dist/lib/lanes/index.d.ts +1 -1
  94. package/dist/lib/lanes/keep-alive.d.ts +3 -0
  95. package/dist/lib/lanes/keep-alive.js +3 -0
  96. package/dist/lib/lanes/outpost-dispatch.d.ts +4 -0
  97. package/dist/lib/lanes/outpost-dispatch.js +11 -2
  98. package/dist/lib/lanes/reconcile.js +20 -4
  99. package/dist/lib/lanes/spawn.js +17 -10
  100. package/dist/lib/lanes/store.js +45 -1
  101. package/dist/lib/lanes/types.d.ts +30 -1
  102. package/dist/lib/lanes/types.js +2 -2
  103. package/dist/lib/lanes/watcher.js +3 -2
  104. package/dist/lib/process-flag-company-context.d.ts +8 -0
  105. package/dist/lib/process-flag-company-context.js +67 -0
  106. package/dist/main.js +3 -1
  107. package/dist/native-fast-route.d.ts +3 -0
  108. package/dist/native-fast-route.js +39 -0
  109. package/dist/secrets-env-fast-path.d.ts +5 -0
  110. package/dist/secrets-env-fast-path.js +16 -0
  111. package/dist/secrets-env-fast-route.d.ts +18 -0
  112. package/dist/secrets-env-fast-route.js +54 -0
  113. package/dist/utils/cli-telemetry.d.ts +26 -2
  114. package/dist/utils/cli-telemetry.js +178 -3
  115. package/dist/utils/hq-root-resolver.d.ts +13 -0
  116. package/dist/utils/hq-root-resolver.js +103 -0
  117. package/dist/utils/scaffold-fast-import-guard.js +6 -1
  118. package/dist/utils/vault-api.d.ts +8 -0
  119. package/dist/utils/vault-api.js +13 -1
  120. package/package.json +2 -2
@@ -130,6 +130,10 @@ export function createLaneAccountsHandlers(deps) {
130
130
  return {
131
131
  ...status,
132
132
  effective_home: status.home,
133
+ ...(status.auth_state === "home_missing" ? {
134
+ error: "home_missing",
135
+ fix_command: `hq lanes accounts set-home ${provider} ${status.id} <host-home>`,
136
+ } : {}),
133
137
  model: accountCfg?.model ?? null,
134
138
  effort: accountCfg?.effort ?? null,
135
139
  effective_model: model ?? null,
@@ -165,6 +169,7 @@ export function createLaneAccountsHandlers(deps) {
165
169
  // Retain the established response field for clients; balancing is now
166
170
  // unconditional, so this compatibility value is always true.
167
171
  const balanceGateEnabled = true;
172
+ const recentUsageEnabled = await deps.readClaudeAccountBalanceSignalsEnabled(ctx);
168
173
  const loads = accountLoadsForReport({ hosts: loaded.snapshots, config });
169
174
  const suggestions = planClaudeBalanceMoves({ hosts: loaded.snapshots, config });
170
175
  const selections = loaded.snapshots.map((host) => {
@@ -173,6 +178,8 @@ export function createLaneAccountsHandlers(deps) {
173
178
  hosts: loaded.snapshots,
174
179
  targetHost: host.capacity.host,
175
180
  config,
181
+ recentUsageEnabled,
182
+ now: ctx.now(),
176
183
  unavailableHosts: loaded.unavailableHosts,
177
184
  });
178
185
  return {
@@ -192,11 +199,19 @@ export function createLaneAccountsHandlers(deps) {
192
199
  });
193
200
  const accounts = Object.keys(config.providers.claude.accounts).sort().map((account) => {
194
201
  const load = loads.find((row) => row.account === account);
202
+ const currentStatus = loaded.snapshots[0]?.capacity.providers.claude.accounts[account];
203
+ const currentHomeMissing = config.providers.claude.accounts[account]?.enabled === true &&
204
+ currentStatus?.auth_state === "home_missing";
195
205
  return {
196
206
  account,
197
207
  enabled: config.providers.claude.accounts[account]?.enabled === true,
198
208
  live_lanes: load?.live_lanes ?? 0,
199
209
  counts_complete: load?.counts_complete ?? false,
210
+ ...(load ? { recent_usage_at: load.recent_usage_at } : {}),
211
+ ...(currentHomeMissing ? {
212
+ error: "home_missing",
213
+ fix_command: `hq lanes accounts set-home claude ${account} <host-home>`,
214
+ } : {}),
200
215
  hosts: loaded.snapshots.map((host) => {
201
216
  const accountStatus = host.capacity.providers.claude.accounts[account];
202
217
  const accountLoad = load?.hosts.find((row) => row.host.kind === host.capacity.host.kind && row.host.id === host.capacity.host.id);
@@ -204,6 +219,11 @@ export function createLaneAccountsHandlers(deps) {
204
219
  host: host.capacity.host,
205
220
  enabled: accountStatus?.enabled ?? false,
206
221
  logged_in: accountStatus?.logged_in ?? null,
222
+ ...(accountStatus?.auth_state !== undefined ? { auth_state: accountStatus.auth_state } : {}),
223
+ ...(accountStatus?.auth_state === "home_missing" ? {
224
+ error: "home_missing",
225
+ fix_command: `hq lanes accounts set-home claude ${account} <host-home>`,
226
+ } : {}),
207
227
  live_lanes: accountLoad?.live_lanes ?? null,
208
228
  lane_counts: accountLoad?.lane_counts ?? { running: null, queued: null, awaiting_input: null },
209
229
  count_source: accountLoad?.count_source ?? "unavailable",
@@ -251,6 +271,10 @@ export function createLaneAccountsHandlers(deps) {
251
271
  suggestions,
252
272
  ...(opts.apply ? { moved, skipped } : {}),
253
273
  });
274
+ const currentHostMissing = Object.entries(config.providers.claude.accounts).some(([account, accountConfig]) => accountConfig.enabled &&
275
+ loaded.snapshots[0]?.capacity.providers.claude.accounts[account]?.auth_state === "home_missing");
276
+ if (currentHostMissing)
277
+ process.exitCode = 1;
254
278
  }
255
279
  async function runAccountsLogin(ctx, providerRaw, accountId, opts) {
256
280
  const callerCwd = ctx.callerCwd();
@@ -276,6 +300,12 @@ export function createLaneAccountsHandlers(deps) {
276
300
  if (opts.company !== undefined) {
277
301
  await resolveSelectedAccountHomeCompany(ctx, opts.company);
278
302
  }
303
+ if (process.platform === "linux" &&
304
+ requestedHome === undefined &&
305
+ registeredAccount !== undefined &&
306
+ effectiveAccountHome(registeredAccount, hostname).source !== "host") {
307
+ throw new LanesError("account_host_home_required", `Account ${id} for ${provider} has no home for this Linux host; pass --home <host-home> to store a host-specific home`, { provider, account: id, hostname });
308
+ }
279
309
  const homeRaw = requestedHome ??
280
310
  (registeredAccount !== undefined
281
311
  ? effectiveAccountHome(registeredAccount, hostname).home
@@ -291,10 +321,22 @@ export function createLaneAccountsHandlers(deps) {
291
321
  existsSync: (p) => fs.existsSync(p),
292
322
  });
293
323
  const nextAccount = registeredAccount === undefined
294
- ? { home, enabled: true }
324
+ ? process.platform === "linux"
325
+ ? {
326
+ home: id === "a" ? DEFAULT_ACCOUNT_HOMES[provider] : `~/.${provider}-${id}`,
327
+ hosts: { [hostname]: { home } },
328
+ enabled: true,
329
+ }
330
+ : { home, enabled: true }
295
331
  : { ...registeredAccount, enabled: true };
296
332
  if (registeredAccount !== undefined && requestedHome !== undefined) {
297
- if (home === registeredAccount.home) {
333
+ if (process.platform === "linux") {
334
+ nextAccount.hosts = {
335
+ ...(registeredAccount.hosts ?? {}),
336
+ [hostname]: { home },
337
+ };
338
+ }
339
+ else if (home === registeredAccount.home) {
298
340
  if (nextAccount.hosts !== undefined) {
299
341
  delete nextAccount.hosts[hostname];
300
342
  if (Object.keys(nextAccount.hosts).length === 0)
@@ -9,7 +9,7 @@ import { dropboxInboxHasMessages, drainDropbox, formatDropboxMessages, formatMon
9
9
  import { isPidAlive, runWatcherLoop, WATCHER_OUT_MAX_BYTES, watcherOutLogPath, writeWatcherDecision } from "../lib/lanes/watcher.js";
10
10
  import { laneStories, laneStory, storyWarning } from "../lib/lanes/stories.js";
11
11
  import { laneRunDir, probePidRecord, readPidRecord, readProcStarttime, spawnLaneWorker } from "../lib/lanes/spawn.js";
12
- import { keepAliveExclusion, keepAliveSupersededBy, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds } from "../lib/lanes/keep-alive.js";
12
+ import { keepAliveExclusion, keepAliveSupersededBy, LANE_RUNNING_SUPERSEDED_MESSAGE_DELIVERY_FLAG, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds } from "../lib/lanes/keep-alive.js";
13
13
  import { managerStatusGateEnabledForLane } from "../lib/lanes/manager-status-gate.js";
14
14
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
15
15
  import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
@@ -90,20 +90,20 @@ export function createLaneMessageHandlers(deps) {
90
90
  company: companyForLane(lane),
91
91
  env: ctx.env,
92
92
  });
93
- const currentLane = readLane(ctx.hqRoot(), lane.lane_id);
93
+ let currentLane = readLane(ctx.hqRoot(), lane.lane_id);
94
94
  if (currentLane.state === "killed" || currentLane.state === "cancelled") {
95
95
  return { lane: currentLane, exclusion: currentLane.state };
96
96
  }
97
97
  const localLanes = loadAllLanes(ctx.hqRoot());
98
98
  let lanes = localLanes;
99
- const retiredLaneIds = loadRetiredLaneIds(ctx.hqRoot());
99
+ let retiredLaneIds = loadRetiredLaneIds(ctx.hqRoot());
100
100
  const dispatchedCallerId = grantContext?.superseded_message_caller_id;
101
101
  const dispatchedSessionId = grantContext?.superseded_message_session_id;
102
- const dispatchedContextBound = grantContext !== undefined &&
102
+ let dispatchedContextBound = grantContext !== undefined &&
103
103
  ctx.isSsmOwnerDispatch() &&
104
104
  sameHireGrantSenior(grantContext.expected_senior, currentLane.senior);
105
- const dispatchedSessionContext = dispatchedContextBound && currentLane.senior.kind === "session";
106
- const dispatchedContextAuthorized = dispatchedContextBound && (dispatchedCallerId !== undefined ||
105
+ let dispatchedSessionContext = dispatchedContextBound && currentLane.senior.kind === "session";
106
+ let dispatchedContextAuthorized = dispatchedContextBound && (dispatchedCallerId !== undefined ||
107
107
  (dispatchedSessionId !== undefined && dispatchedSessionId === currentLane.senior.id));
108
108
  if (dispatchedContextBound && grantContext &&
109
109
  (dispatchedCallerId !== undefined || dispatchedSessionContext)) {
@@ -116,7 +116,61 @@ export function createLaneMessageHandlers(deps) {
116
116
  merged.set(currentLane.lane_id, currentLane);
117
117
  lanes = [...merged.values()];
118
118
  }
119
- const exclusion = keepAliveExclusion(currentLane, lanes, retiredLaneIds, lineageAware, companyForLane);
119
+ let exclusion = keepAliveExclusion(currentLane, lanes, retiredLaneIds, lineageAware, companyForLane);
120
+ if (exclusion === "superseded" && currentLane.state !== "done" && currentLane.state !== "interrupted") {
121
+ const runningSupersededMessageDeliveryEnabled = await readLanesRegistryFlag(LANE_RUNNING_SUPERSEDED_MESSAGE_DELIVERY_FLAG, { company: companyForLane(currentLane), env: ctx.env });
122
+ if (runningSupersededMessageDeliveryEnabled) {
123
+ const latestLane = readLane(ctx.hqRoot(), currentLane.lane_id);
124
+ if (latestLane.state === "killed" || latestLane.state === "cancelled") {
125
+ return { lane: latestLane, exclusion: latestLane.state };
126
+ }
127
+ const latestLocalLanes = loadAllLanes(ctx.hqRoot());
128
+ currentLane = latestLane;
129
+ let latestLanes = latestLocalLanes;
130
+ dispatchedContextBound =
131
+ grantContext !== undefined &&
132
+ ctx.isSsmOwnerDispatch() &&
133
+ sameHireGrantSenior(grantContext.expected_senior, currentLane.senior);
134
+ dispatchedSessionContext = dispatchedContextBound && currentLane.senior.kind === "session";
135
+ dispatchedContextAuthorized = dispatchedContextBound && (dispatchedCallerId !== undefined ||
136
+ (dispatchedSessionId !== undefined && dispatchedSessionId === currentLane.senior.id));
137
+ if (dispatchedContextBound && grantContext &&
138
+ (dispatchedCallerId !== undefined || dispatchedSessionContext)) {
139
+ const merged = new Map(latestLocalLanes.map((record) => [record.lane_id, record]));
140
+ for (const snapshot of grantContext.hierarchy) {
141
+ const contextLane = keepAliveLaneFromSnapshot(snapshot);
142
+ if (contextLane)
143
+ merged.set(contextLane.lane_id, contextLane);
144
+ }
145
+ merged.set(currentLane.lane_id, currentLane);
146
+ latestLanes = [...merged.values()];
147
+ }
148
+ lanes = latestLanes;
149
+ retiredLaneIds = loadRetiredLaneIds(ctx.hqRoot());
150
+ exclusion = keepAliveExclusion(currentLane, lanes, retiredLaneIds, lineageAware, companyForLane);
151
+ if (exclusion === "superseded" && currentLane.state !== "done" && currentLane.state !== "interrupted") {
152
+ const callerLaneId = dispatchedContextBound ? dispatchedCallerId : undefined;
153
+ const callerSessionId = dispatchedContextBound
154
+ ? (dispatchedSessionId === undefined ? null : dispatchedSessionId)
155
+ : undefined;
156
+ const callerMayMessage = callerMayMessageSupersededLane(ctx, currentLane, lanes, lineageAware, companyForLane, callerLaneId, callerSessionId);
157
+ return {
158
+ lane: currentLane,
159
+ exclusion: undefined,
160
+ ...(!dispatchedContextAuthorized && callerMayMessage && ctx.env.HQ_LANE_ID?.trim()
161
+ ? { supersededMessageCallerId: ctx.env.HQ_LANE_ID.trim() }
162
+ : {}),
163
+ ...(callerMayMessage && !dispatchedContextBound && currentLane.senior.kind === "session" &&
164
+ typeof currentLane.senior.id === "string" &&
165
+ currentLane.senior.id.length > 0 &&
166
+ callerMatchesSessionSenior(ctx, currentLane.senior.id)
167
+ ? { supersededMessageSessionId: currentLane.senior.id }
168
+ : {}),
169
+ supersededMessageWarning: `A newer lead supersedes this ${currentLane.state} lane; message delivery is allowed while the lane remains active.`,
170
+ };
171
+ }
172
+ }
173
+ }
120
174
  const callerLaneId = dispatchedContextBound ? dispatchedCallerId : undefined;
121
175
  const callerSessionId = dispatchedContextBound
122
176
  ? (dispatchedSessionId === undefined ? null : dispatchedSessionId)
@@ -552,11 +606,14 @@ export function createLaneMessageHandlers(deps) {
552
606
  : mechanism === "dropbox"
553
607
  ? "awaiting_next_tool_call"
554
608
  : "worker_spawned";
555
- const warning = mechanism === "dropbox" && drainHook !== null && !drainHook.registered
556
- ? `The drain hook is not registered in ${drainHook.path}; the running worker will not receive this message. It stays in the inbox and is delivered on the lane's next start.`
557
- : mechanism === "dropbox" && next.stalled === true
558
- ? `The worker has made no tool call since ${next.stalled_at ?? "an unknown time"} (${next.stalled_reason ?? "stalled"}); delivery waits on its next tool call.`
559
- : null;
609
+ const warning = [
610
+ eligibility.supersededMessageWarning,
611
+ mechanism === "dropbox" && drainHook !== null && !drainHook.registered
612
+ ? `The drain hook is not registered in ${drainHook.path}; the running worker will not receive this message. It stays in the inbox and is delivered on the lane's next start.`
613
+ : mechanism === "dropbox" && next.stalled === true
614
+ ? `The worker has made no tool call since ${next.stalled_at ?? "an unknown time"} (${next.stalled_reason ?? "stalled"}); delivery waits on its next tool call.`
615
+ : null,
616
+ ].filter((message) => message !== undefined && message !== null).join(" ") || null;
560
617
  if (!opts.silent)
561
618
  printJson({
562
619
  ok: true,
@@ -223,6 +223,7 @@ export function createLaneResumeHandlers(deps) {
223
223
  ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
224
224
  ...(profileRepin !== undefined ? { profileRepin } : {}),
225
225
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
226
+ ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
226
227
  pending: {
227
228
  trigger: "resume",
228
229
  resumed_from: resumedFrom,
@@ -252,6 +253,7 @@ export function createLaneResumeHandlers(deps) {
252
253
  ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
253
254
  ...(profileRepin !== undefined ? { profileRepin } : {}),
254
255
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
256
+ ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
255
257
  pending: {
256
258
  trigger: "resume",
257
259
  resumed_from: resumedFrom,
@@ -245,9 +245,11 @@ export interface Ctx {
245
245
  mergeReviewEnabled?: (company: LaneCompanyRef) => boolean | Promise<boolean>;
246
246
  }
247
247
  export declare const LANE_OUTPOST_LOAD_BALANCING_FLAG = "lanes.outpost-load-balancing";
248
+ export declare const CLAUDE_ACCOUNT_BALANCE_SIGNALS_FLAG = "lanes.claude-account-balance-signals";
248
249
  export declare function defaultLanePlacementEnabled(company: LaneCompanyRef | undefined): Promise<boolean>;
249
250
  export declare function readLanePlacementEnabled(ctx: Ctx, company: LaneCompanyRef | undefined): Promise<boolean>;
250
251
  export declare function processLanePlacementCompany(ctx: Ctx): LaneCompanyRef | undefined;
252
+ export declare function readClaudeAccountBalanceSignalsEnabled(ctx: Ctx, company?: LaneCompanyRef): Promise<boolean>;
251
253
  export declare function parsePlacementPolicy(raw: string): LanePlacementPolicy;
252
254
  export declare function assertPlacementEnabled(ctx: Ctx, company: LaneCompanyRef | undefined, readPlacementEnabled?: () => Promise<boolean>): Promise<void>;
253
255
  /** Match a direct session-senior caller while refusing lane-worker identities. */
@@ -349,6 +351,7 @@ export interface LaneAccountsHandlerDeps {
349
351
  to: string;
350
352
  }>;
351
353
  printJson(value: unknown): void;
354
+ readClaudeAccountBalanceSignalsEnabled(ctx: Ctx): Promise<boolean>;
352
355
  reconcileHostFromCtx(ctx: Ctx): ReconcileHost;
353
356
  resolveCallerPath(callerCwd: string, filePath: string): string;
354
357
  }
@@ -72,9 +72,9 @@ export function resolveResumeEffort(ctx, lane, input) {
72
72
  ? undefined
73
73
  : providerConfig.accounts[accountId];
74
74
  const profileDefaultsApply = input.profile?.profile.meta.provider === lane.provider;
75
- const pinnedModel = lane.model_source === undefined || lane.model_source === "cli" || lane.model_source === "preserved"
75
+ const pinnedModel = lane.pending_settings?.model ?? (lane.model_source === undefined || lane.model_source === "cli" || lane.model_source === "preserved"
76
76
  ? lane.model
77
- : undefined;
77
+ : undefined);
78
78
  const model = resolveLaneModel({
79
79
  cliModel: input.model?.trim() || pinnedModel,
80
80
  profileModel: profileDefaultsApply ? input.profile?.profile.meta.model : undefined,
@@ -90,12 +90,16 @@ export function resolveResumeEffort(ctx, lane, input) {
90
90
  });
91
91
  }
92
92
  export function recordedResumeEffort(lane) {
93
+ if (lane.pending_settings?.effort !== undefined) {
94
+ return { effort: lane.pending_settings.effort, source: "cli" };
95
+ }
93
96
  return {
94
97
  ...(lane.effort !== undefined ? { effort: lane.effort } : {}),
95
98
  source: lane.effort_source ?? "none",
96
99
  };
97
100
  }
98
101
  export const LANE_OUTPOST_LOAD_BALANCING_FLAG = "lanes.outpost-load-balancing";
102
+ export const CLAUDE_ACCOUNT_BALANCE_SIGNALS_FLAG = "lanes.claude-account-balance-signals";
99
103
  function lanePlacementFlagErrorCode(error) {
100
104
  if (error && typeof error === "object") {
101
105
  for (const key of ["code", "error", "name"]) {
@@ -135,6 +139,12 @@ export function processLanePlacementCompany(ctx) {
135
139
  return undefined;
136
140
  return { slug: context.companySlug, uid: context.companyUid ?? "" };
137
141
  }
142
+ export function readClaudeAccountBalanceSignalsEnabled(ctx, company) {
143
+ return readLanesRegistryFlag(CLAUDE_ACCOUNT_BALANCE_SIGNALS_FLAG, {
144
+ company: company ?? processLanePlacementCompany(ctx),
145
+ env: ctx.env,
146
+ });
147
+ }
138
148
  export function parsePlacementPolicy(raw) {
139
149
  const value = raw.trim();
140
150
  if (value === "local")
@@ -8,7 +8,7 @@
8
8
  import { Command } from "commander";
9
9
  import { type LanesMesh, type SpawnLoginResult } from "./lanes-shared.js";
10
10
  export type { LanesMesh, SpawnLoginResult } from "./lanes-shared.js";
11
- export { LANE_OUTPOST_LOAD_BALANCING_FLAG } from "./lanes-shared.js";
11
+ export { CLAUDE_ACCOUNT_BALANCE_SIGNALS_FLAG, LANE_OUTPOST_LOAD_BALANCING_FLAG } from "./lanes-shared.js";
12
12
  export { resolveAnswerChoice } from "./lanes-questions.js";
13
13
  import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
14
14
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
@@ -139,5 +139,9 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
139
139
  export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
140
140
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
141
141
  export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
142
+ /** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
143
+ export declare function stopTerminalWarning(laneId: string): string;
144
+ /** Stderr notice when an interrupted lane has no session to resume from. */
145
+ export declare function interruptNotResumableWarning(laneId: string): string;
142
146
  export declare function registerLanesCommand(parent: Command, deps?: LanesCommandDeps): Command;
143
147
  //# sourceMappingURL=lanes.d.ts.map