@indigoai-us/hq-cli 5.345.155 → 5.345.159

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 (169) hide show
  1. package/CHANGELOG.md +289 -0
  2. package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +110 -2
  3. package/assets/scaffold/core/scripts/rebuild-projects-index.sh +101 -5
  4. package/assets/scaffold/core/scripts/worktree.sh +4 -0
  5. package/dist/command-catalog.generated.d.ts +536 -7
  6. package/dist/command-catalog.generated.js +676 -24
  7. package/dist/command-registration-plan.d.ts +18 -0
  8. package/dist/command-registration-plan.js +4 -0
  9. package/dist/commands/_patterns.d.ts +17 -0
  10. package/dist/commands/_patterns.js +22 -0
  11. package/dist/commands/agent-inbox.js +2 -2
  12. package/dist/commands/agent-kit.js +8 -6
  13. package/dist/commands/bot.js +7 -4
  14. package/dist/commands/channel-suggest.d.ts +56 -0
  15. package/dist/commands/channel-suggest.js +170 -0
  16. package/dist/commands/channels.js +7 -1
  17. package/dist/commands/cloud.d.ts +45 -0
  18. package/dist/commands/cloud.js +150 -10
  19. package/dist/commands/company.js +4 -3
  20. package/dist/commands/core-checkpoint-native.d.ts +18 -0
  21. package/dist/commands/core-checkpoint-native.js +67 -2
  22. package/dist/commands/core-pipeline-driver-native.d.ts +12 -1
  23. package/dist/commands/core-pipeline-driver-native.js +131 -5
  24. package/dist/commands/core.js +13 -1
  25. package/dist/commands/crm.js +83 -6
  26. package/dist/commands/deploy-access.d.ts +33 -0
  27. package/dist/commands/deploy-access.js +322 -0
  28. package/dist/commands/dm-attach.d.ts +106 -0
  29. package/dist/commands/dm-attach.js +184 -0
  30. package/dist/commands/dm.d.ts +37 -0
  31. package/dist/commands/dm.js +172 -10
  32. package/dist/commands/feedback-show.d.ts +40 -0
  33. package/dist/commands/feedback-show.js +261 -0
  34. package/dist/commands/feedback.d.ts +21 -0
  35. package/dist/commands/feedback.js +152 -2
  36. package/dist/commands/files-browse.d.ts +16 -0
  37. package/dist/commands/files-browse.js +52 -1
  38. package/dist/commands/files.d.ts +24 -0
  39. package/dist/commands/files.js +52 -1
  40. package/dist/commands/integrations-connect.js +7 -3
  41. package/dist/commands/integrations-core.d.ts +13 -1
  42. package/dist/commands/integrations-core.js +49 -4
  43. package/dist/commands/integrations.js +4 -2
  44. package/dist/commands/lanes-accounts.js +16 -1
  45. package/dist/commands/lanes-loop.js +14 -4
  46. package/dist/commands/lanes-message.js +6 -4
  47. package/dist/commands/lanes-resume.js +36 -13
  48. package/dist/commands/lanes-shared.d.ts +5 -0
  49. package/dist/commands/lanes-shared.js +13 -3
  50. package/dist/commands/lanes-watch.js +1 -0
  51. package/dist/commands/lanes.d.ts +20 -1
  52. package/dist/commands/lanes.js +201 -51
  53. package/dist/commands/mesh.js +149 -1
  54. package/dist/commands/monitor.d.ts +1 -0
  55. package/dist/commands/monitor.js +4 -1
  56. package/dist/commands/repo.d.ts +41 -0
  57. package/dist/commands/repo.js +331 -0
  58. package/dist/commands/scan.d.ts +3 -1
  59. package/dist/commands/scan.js +7 -3
  60. package/dist/commands/secrets.d.ts +37 -0
  61. package/dist/commands/secrets.js +185 -16
  62. package/dist/commands/signals.js +17 -5
  63. package/dist/commands/staff/listings.d.ts +41 -0
  64. package/dist/commands/staff/listings.js +160 -0
  65. package/dist/commands/staff/reset-test-account.d.ts +3 -2
  66. package/dist/commands/staff/reset-test-account.js +4 -3
  67. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2629 -1036
  68. package/dist/hq-cloud-sync-runner/package.json +1 -1
  69. package/dist/lib/agent-kit/inbox-state.d.ts +7 -0
  70. package/dist/lib/agent-kit/inbox-state.js +5 -1
  71. package/dist/lib/agent-kit/mcp/tools.js +2 -1
  72. package/dist/lib/agent-kit/run/inbox.js +2 -1
  73. package/dist/lib/agent-kit/wake-secrets.d.ts +7 -0
  74. package/dist/lib/agent-kit/wake-secrets.js +32 -0
  75. package/dist/lib/agent-kit/wake.d.ts +4 -0
  76. package/dist/lib/agent-kit/wake.js +49 -3
  77. package/dist/lib/bot/inflight.d.ts +7 -1
  78. package/dist/lib/bot/inflight.js +10 -2
  79. package/dist/lib/bot/prompt.js +3 -0
  80. package/dist/lib/bot/run.js +72 -20
  81. package/dist/lib/bot/session.d.ts +14 -0
  82. package/dist/lib/bot/session.js +70 -0
  83. package/dist/lib/capacity/collectors/claude-oauth.d.ts +6 -0
  84. package/dist/lib/capacity/collectors/claude-oauth.js +12 -0
  85. package/dist/lib/capacity/lane-login.d.ts +6 -0
  86. package/dist/lib/capacity/lane-login.js +5 -0
  87. package/dist/lib/core-utils/worktree.js +2 -0
  88. package/dist/lib/daemon/exit-cause.d.ts +43 -0
  89. package/dist/lib/daemon/exit-cause.js +88 -0
  90. package/dist/lib/daemon/handoff.d.ts +2 -0
  91. package/dist/lib/daemon/handoff.js +6 -1
  92. package/dist/lib/daemon/hq-child-bootstrap.mjs +20 -3
  93. package/dist/lib/daemon/paths.d.ts +2 -0
  94. package/dist/lib/daemon/paths.js +1 -0
  95. package/dist/lib/daemon/run.js +13 -0
  96. package/dist/lib/daemon/supervisor.d.ts +54 -1
  97. package/dist/lib/daemon/supervisor.js +297 -21
  98. package/dist/lib/daemon/unit-holds.d.ts +22 -0
  99. package/dist/lib/daemon/unit-holds.js +86 -0
  100. package/dist/lib/doctor/checks/sync-health.d.ts +9 -0
  101. package/dist/lib/doctor/checks/sync-health.js +66 -1
  102. package/dist/lib/doctor/company-resolution-state.d.ts +30 -0
  103. package/dist/lib/doctor/company-resolution-state.js +89 -0
  104. package/dist/lib/doctor/fix/apply.d.ts +36 -6
  105. package/dist/lib/doctor/fix/apply.js +128 -12
  106. package/dist/lib/doctor/types.d.ts +1 -1
  107. package/dist/lib/doctor/types.js +1 -0
  108. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  109. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  110. package/dist/lib/hook-engine/resolve.js +6 -2
  111. package/dist/lib/index-render/company-knowledge.d.ts +1 -1
  112. package/dist/lib/index-render/company-knowledge.js +4 -5
  113. package/dist/lib/index-render/index.js +44 -3
  114. package/dist/lib/index-render/projects.d.ts +1 -1
  115. package/dist/lib/index-render/projects.js +5 -5
  116. package/dist/lib/lanes/account-company.d.ts +28 -0
  117. package/dist/lib/lanes/account-company.js +53 -0
  118. package/dist/lib/lanes/accounts.d.ts +22 -3
  119. package/dist/lib/lanes/accounts.js +38 -2
  120. package/dist/lib/lanes/claude-balance.d.ts +6 -1
  121. package/dist/lib/lanes/claude-balance.js +6 -1
  122. package/dist/lib/lanes/claude-credential.d.ts +78 -0
  123. package/dist/lib/lanes/claude-credential.js +163 -0
  124. package/dist/lib/lanes/compaction.d.ts +2 -0
  125. package/dist/lib/lanes/compaction.js +9 -0
  126. package/dist/lib/lanes/config.js +6 -0
  127. package/dist/lib/lanes/envelope-repair-dispatch.js +2 -1
  128. package/dist/lib/lanes/hooks-install.js +2 -1
  129. package/dist/lib/lanes/host-capacity.d.ts +2 -0
  130. package/dist/lib/lanes/host-capacity.js +1 -0
  131. package/dist/lib/lanes/keep-alive.d.ts +1 -1
  132. package/dist/lib/lanes/keep-alive.js +3 -1
  133. package/dist/lib/lanes/links.js +10 -4
  134. package/dist/lib/lanes/loop.js +16 -1
  135. package/dist/lib/lanes/node-binary.d.ts +17 -0
  136. package/dist/lib/lanes/node-binary.js +30 -0
  137. package/dist/lib/lanes/outpost-dispatch.d.ts +2 -1
  138. package/dist/lib/lanes/outpost-dispatch.js +1 -0
  139. package/dist/lib/lanes/reconcile.js +4 -0
  140. package/dist/lib/lanes/remote-control-lane.js +2 -0
  141. package/dist/lib/lanes/spawn.d.ts +3 -0
  142. package/dist/lib/lanes/spawn.js +119 -9
  143. package/dist/lib/lanes/store.d.ts +6 -0
  144. package/dist/lib/lanes/store.js +107 -18
  145. package/dist/lib/lanes/types.d.ts +2 -0
  146. package/dist/lib/lanes/watch.js +28 -5
  147. package/dist/lib/lanes/watcher-main.js +4 -3
  148. package/dist/lib/lanes/watcher.d.ts +4 -0
  149. package/dist/lib/lanes/watcher.js +110 -21
  150. package/dist/lib/mesh/api.d.ts +32 -0
  151. package/dist/lib/mesh/api.js +75 -0
  152. package/dist/lib/secret-scan/detect.js +9 -0
  153. package/dist/lib/secret-scan/repo-config.d.ts +41 -0
  154. package/dist/lib/secret-scan/repo-config.js +237 -0
  155. package/dist/lib/secret-scan/rules.d.ts +7 -1
  156. package/dist/lib/secret-scan/rules.js +3 -1
  157. package/dist/lib/secret-scan/staged.d.ts +5 -0
  158. package/dist/lib/secret-scan/staged.js +19 -2
  159. package/dist/lib/secret-scan/types.d.ts +2 -0
  160. package/dist/lib/sync/server-owned-local-only.d.ts +29 -0
  161. package/dist/lib/sync/server-owned-local-only.js +75 -0
  162. package/dist/sentry-before-send.js +1 -0
  163. package/dist/utils/access-denied-hint.js +6 -0
  164. package/dist/utils/client-health.d.ts +34 -0
  165. package/dist/utils/client-health.js +96 -3
  166. package/dist/utils/package-use-lease.js +27 -9
  167. package/dist/utils/version-gate.d.ts +7 -0
  168. package/dist/utils/version-gate.js +66 -0
  169. package/package.json +8 -8
@@ -101,7 +101,16 @@ export async function runLaneLoopController(ctx, laneId, deps) {
101
101
  execute: async ({ envelope, signal, fresh, sessionId, repairError }) => {
102
102
  let current = readLane(hqRoot, laneId);
103
103
  const data = envelope;
104
- const storyId = typeof data.story_id === "string" && data.story_id.trim() ? data.story_id.trim() : current.story_id;
104
+ const envelopeStory = typeof data.story_id === "string" && data.story_id.trim() ? data.story_id.trim() : undefined;
105
+ // A lane created with --no-story keeps no_story_reason and an empty
106
+ // story_id; the record schema rejects both together. Ignore an
107
+ // envelope story on such a lane instead of writing a record that can
108
+ // no longer be loaded or cancelled.
109
+ const ignoreEnvelopeStory = envelopeStory !== undefined && !current.story_id && !!current.no_story_reason;
110
+ if (ignoreEnvelopeStory) {
111
+ process.stderr.write(`hq lanes: ignoring envelope story_id ${envelopeStory} for no-story lane ${laneId}\n`);
112
+ }
113
+ const storyId = envelopeStory !== undefined && !ignoreEnvelopeStory ? envelopeStory : current.story_id;
105
114
  if (storyId !== current.story_id) {
106
115
  current = retryUpdateLane(hqRoot, laneId, "command", (record) => {
107
116
  const stories = laneStories(record);
@@ -153,8 +162,8 @@ export async function runLaneLoopController(ctx, laneId, deps) {
153
162
  fs.writeFileSync(briefPath, [
154
163
  "Follow the lane worker instructions and complete this single bounded phase.",
155
164
  handoffRepairEnabled
156
- ? "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts. The lane envelope decision uses done/ask/blocked; handoff status uses passed/failed/blocked. Allowed handoff status values are passed, failed, blocked. Allowed back_pressure values for tests, lint, typecheck, and build are pass, fail, skip. Include story_id, phase, worker_id, summary, files_changed, commits, back_pressure, and context_for_next."
157
- : "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts; it must identify the requested story_id and phase and include worker_id, status, summary, files_changed, commits, back_pressure (tests/lint/typecheck/build), and context_for_next.",
165
+ ? "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts. The lane envelope decision uses done/ask/blocked; handoff status uses passed/failed/blocked. Allowed handoff status values are passed, failed, blocked. Allowed back_pressure values for tests, lint, typecheck, and build are pass, fail, skip. Include story_id, phase, worker_id, summary, files_changed, commits, back_pressure, and context_for_next. summary, context_for_next, and each ac_evidence evidence are single strings, never arrays. Copy each acceptance criterion verbatim into ac_evidence[].criterion; keep its 0-based index and do not shorten or paraphrase the text."
166
+ : "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts; it must identify the requested story_id and phase and include worker_id, status, summary, files_changed, commits, back_pressure (tests/lint/typecheck/build), and context_for_next. summary, context_for_next, and each ac_evidence evidence are single strings, never arrays. Copy each acceptance criterion verbatim into ac_evidence[].criterion; keep its 0-based index and do not shorten or paraphrase the text.",
158
167
  repairInstructions,
159
168
  "",
160
169
  prompt,
@@ -173,6 +182,7 @@ export async function runLaneLoopController(ctx, laneId, deps) {
173
182
  }
174
183
  await spawnLaneWorker(hqRoot, current, {
175
184
  env: ctx.env,
185
+ readClaudeCredential: ctx.readClaudeCredential,
176
186
  modelDeps: { readFileSync: ctx.readFileSync, probe: ctx.probeProvider, env: ctx.env },
177
187
  existsSync: ctx.existsSync,
178
188
  spawnProcess: ctx.spawnProcess,
@@ -201,7 +211,7 @@ export async function runLaneLoopController(ctx, laneId, deps) {
201
211
  await deps.resumeLane(laneId, {
202
212
  text: `Read ${briefPath} and follow it as the complete instruction for this phase.`,
203
213
  project: current.project_id,
204
- story: storyId,
214
+ ...(storyId ? { story: storyId } : {}),
205
215
  freshThread: fresh,
206
216
  ...(sessionId ? { loopSessionId: sessionId } : {}),
207
217
  ...(repairError !== undefined ? { loopHandoffRepair: true } : {}),
@@ -560,16 +560,17 @@ export function createLaneMessageHandlers(deps) {
560
560
  ? err.details.account_reasons.filter((reason) => reason !== null && typeof reason === "object" && !Array.isArray(reason))
561
561
  : [];
562
562
  const held = reasons.filter((reason) => reason.reason === "quota_held");
563
- if (held.length === 0)
563
+ const companyUnavailable = reasons.some((reason) => reason.reason === "company_mismatch");
564
+ if (held.length === 0 && !companyUnavailable)
564
565
  throw err;
565
566
  const resetTimes = held
566
567
  .map((reason) => reason.quota_reset_at)
567
568
  .filter((value) => typeof value === "string" && Number.isFinite(Date.parse(value)))
568
569
  .sort((a, b) => Date.parse(a) - Date.parse(b));
569
- if (resetTimes.length === 0)
570
+ if (resetTimes.length === 0 && !companyUnavailable)
570
571
  throw err;
571
572
  quotaHoldDeferred = {
572
- quota_reset_at: resetTimes[0],
573
+ ...(resetTimes[0] !== undefined ? { quota_reset_at: resetTimes[0] } : {}),
573
574
  ...(lane.model !== undefined ? { model: lane.model } : {}),
574
575
  };
575
576
  automaticAccountSelection = undefined;
@@ -911,6 +912,7 @@ export function createLaneMessageHandlers(deps) {
911
912
  }
912
913
  const started = spawnLaneWorker(hqRoot, lane, {
913
914
  env: ctx.env,
915
+ readClaudeCredential: ctx.readClaudeCredential,
914
916
  modelDeps: {
915
917
  readFileSync: ctx.readFileSync,
916
918
  probe: ctx.probeProvider,
@@ -974,7 +976,7 @@ export function createLaneMessageHandlers(deps) {
974
976
  const deliveryStatus = move?.queued
975
977
  ? "queued_on_target_account"
976
978
  : queuedOnQuotaHold
977
- ? "queued_on_quota_hold"
979
+ ? quotaHoldDeferred?.quota_reset_at === undefined ? "queued_on_account" : "queued_on_quota_hold"
978
980
  : queuedOnAdmission
979
981
  ? "queued_on_admission"
980
982
  : mechanism === "dropbox"
@@ -98,6 +98,8 @@ export function createLaneResumeHandlers(deps) {
98
98
  const hqRoot = ctx.hqRoot();
99
99
  const resolution = await deps.resolveCommandLane(ctx, laneId);
100
100
  let lane = resolution.lane;
101
+ // Use the resolved identity for every later exact-path write and audit.
102
+ laneId = lane.lane_id;
101
103
  if (opts.freshThread === true && lane.loop_mode !== true) {
102
104
  throw new LanesError("fresh_thread_loop_only", "--fresh-thread is reserved for loop-lane phases", { lane_id: laneId });
103
105
  }
@@ -333,9 +335,10 @@ export function createLaneResumeHandlers(deps) {
333
335
  .map((reason) => reason.quota_reset_at)
334
336
  .filter((value) => typeof value === "string" && Number.isFinite(Date.parse(value)))
335
337
  .sort((a, b) => Date.parse(a) - Date.parse(b));
336
- if (resets.length === 0)
338
+ const companyUnavailable = reasons.some((reason) => reason.reason === "company_mismatch");
339
+ if (resets.length === 0 && !companyUnavailable)
337
340
  throw err;
338
- quotaHoldDeferred = { quota_reset_at: resets[0], account_reasons: reasons };
341
+ quotaHoldDeferred = { ...(resets[0] !== undefined ? { quota_reset_at: resets[0] } : {}), account_reasons: reasons };
339
342
  }
340
343
  }
341
344
  const profileRefreshAccount = opts.account === undefined && refreshProfileSettings && resumeProfile !== undefined &&
@@ -555,12 +558,24 @@ export function createLaneResumeHandlers(deps) {
555
558
  ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
556
559
  at: quotaHoldAt,
557
560
  };
558
- resumed = markSpawnFailure(hqRoot, laneId, "account_quota_exhausted", {
559
- provider: lane.provider,
560
- account: lane.account ?? null,
561
- quota_reset_at: quotaHoldDeferred.quota_reset_at,
562
- account_reasons: quotaHoldDeferred.account_reasons,
563
- }, { resumeTrigger: "resume", pendingResume, at: quotaHoldAt });
561
+ resumed = quotaHoldDeferred.quota_reset_at === undefined
562
+ ? casUpdateLane(hqRoot, laneId, (current) => ({
563
+ ...current,
564
+ state: "queued",
565
+ queued_reason: "account_retry",
566
+ queued_details: undefined,
567
+ queued_at: quotaHoldAt,
568
+ admission_reserved_at: undefined,
569
+ terminal_reason: undefined,
570
+ terminal_details: undefined,
571
+ pending_resume: pendingResume,
572
+ }))
573
+ : markSpawnFailure(hqRoot, laneId, "account_quota_exhausted", {
574
+ provider: lane.provider,
575
+ account: lane.account ?? null,
576
+ quota_reset_at: quotaHoldDeferred.quota_reset_at,
577
+ account_reasons: quotaHoldDeferred.account_reasons,
578
+ }, { resumeTrigger: "resume", pendingResume, at: quotaHoldAt });
564
579
  queuedOnQuotaHold = true;
565
580
  }
566
581
  else if (move?.queued) {
@@ -617,6 +632,7 @@ export function createLaneResumeHandlers(deps) {
617
632
  }
618
633
  const started = spawnLaneWorker(hqRoot, lane, {
619
634
  env: ctx.env,
635
+ readClaudeCredential: ctx.readClaudeCredential,
620
636
  modelDeps: {
621
637
  readFileSync: ctx.readFileSync,
622
638
  probe: ctx.probeProvider,
@@ -624,6 +640,7 @@ export function createLaneResumeHandlers(deps) {
624
640
  },
625
641
  existsSync: ctx.existsSync,
626
642
  spawnProcess: ctx.spawnProcess,
643
+ statfs: ctx.statfs,
627
644
  proveDetached: ctx.proveDetached,
628
645
  killProcess: ctx.killProcess,
629
646
  now: ctx.now,
@@ -647,8 +664,14 @@ export function createLaneResumeHandlers(deps) {
647
664
  compact: opts.compact !== false,
648
665
  effort: resumeEffort ?? recordedResumeEffort(lane),
649
666
  });
650
- storySwitchWorkerStarted = true;
651
- deps.onLaneWorkerStarted?.(laneId, "resume");
667
+ if (started.state === "running") {
668
+ storySwitchWorkerStarted = true;
669
+ deps.onLaneWorkerStarted?.(laneId, "resume");
670
+ }
671
+ else if (started.state === "queued") {
672
+ // The spawn preflight can hold on disk even after admission passed.
673
+ queuedOnAdmission = true;
674
+ }
652
675
  return started;
653
676
  }, hireGrantContext);
654
677
  }, ctx.sleep);
@@ -658,7 +681,7 @@ export function createLaneResumeHandlers(deps) {
658
681
  : null;
659
682
  if (deliveryText !== undefined && messageId !== undefined) {
660
683
  // Same audit copy a message resume leaves; nothing drains it. Archive only
661
- // after a successful spawn so a concurrent resume cannot leave a phantom.
684
+ // after the launch starts or durably queues its resume instruction.
662
685
  archiveResumeMessage(hqRoot, laneId, {
663
686
  at,
664
687
  message_id: messageId,
@@ -703,10 +726,10 @@ export function createLaneResumeHandlers(deps) {
703
726
  delivery_status: move?.queued
704
727
  ? "queued_on_target_account"
705
728
  : queuedOnQuotaHold
706
- ? "queued_on_quota_hold"
729
+ ? quotaHoldDeferred?.quota_reset_at === undefined ? "queued_on_account" : "queued_on_quota_hold"
707
730
  : queuedOnAdmission
708
731
  ? "queued_on_admission"
709
- : "worker_spawned",
732
+ : next.state === "running" ? "worker_spawned" : "not_started",
710
733
  account_move: move ? accountMoveSummary(move) : null,
711
734
  resumed_session_id: resumed.last_resume?.session_id ?? null,
712
735
  last_resume: next.last_resume ?? null,
@@ -1,8 +1,10 @@
1
+ import type { StatFs } from "../lib/lanes/disk-guard.js";
1
2
  import type { LaneCompanyRef, LanePlacementPolicy, LaneRecord, LanesConfig, QuestionAnswer, QuestionBody, QuestionRecord, SeniorRef, AccountHistoryEntry, LaneAccountSelection, PendingResume, QueuedReason, ResolvedLaneWorker, LivenessDeps, MachineIdentity, MonitorEvidenceReader, ReconcileHost, RunCommand, ThrottleDeps, withLaneAdmissionLock, withLaneHireLocks } from "../lib/lanes/index.js";
2
3
  import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
3
4
  import type { OutpostDispatchResult } from "../lib/lanes/outpost-dispatch.js";
4
5
  import type { CpuBusySource, MemoryHold } from "../lib/lanes/throttle.js";
5
6
  import type { ProbeCommand } from "../lib/lanes/models.js";
7
+ import type { ClaudeCredentialReader } from "../lib/lanes/claude-credential.js";
6
8
  import type { FocusGuardReason } from "../lib/lanes/focus-guard.js";
7
9
  import type { KillProcessFn, ProveDetachedFn, SpawnProcessFn } from "../lib/lanes/spawn.js";
8
10
  import type { WatcherLoopDeps } from "../lib/lanes/watcher.js";
@@ -284,6 +286,7 @@ export interface Ctx {
284
286
  mesh: LanesMesh;
285
287
  sleep: (ms: number) => Promise<void>;
286
288
  spawnProcess?: SpawnProcessFn;
289
+ statfs?: StatFs;
287
290
  proveDetached?: ProveDetachedFn;
288
291
  killProcess?: KillProcessFn;
289
292
  isAlive?: (pid: number) => boolean;
@@ -296,6 +299,8 @@ export interface Ctx {
296
299
  runSystemctl?: RunCommand;
297
300
  /** Runs a provider CLI probe (`grok models`, `claude --help`) for --model validation. */
298
301
  probeProvider?: ProbeCommand;
302
+ /** Claude access-token expiry check for account status. Absent in tests that fake the probe. */
303
+ readClaudeCredential?: ClaudeCredentialReader;
299
304
  resolveMachineIdentity: () => MachineIdentity;
300
305
  runCompaction?: RunCompactionFn;
301
306
  locateTranscript?: (home: string, sessionId: string) => string | null;
@@ -25,13 +25,23 @@ export function assertCurrentResumeAccountAvailable(ctx, lane) {
25
25
  ? `Lane ${lane.lane_id} records account ${JSON.stringify(lane.account)} which is not configured for ${lane.provider}`
26
26
  : `Account ${JSON.stringify(lane.account)} is disabled${account.disabled_reason ? `: ${account.disabled_reason}` : ""}`, { provider: lane.provider, account: lane.account ?? "", ...(account?.disabled_reason !== undefined ? { reason: account.disabled_reason } : {}) });
27
27
  }
28
- if (accountStatus(lane.provider, lane.account, config, {
28
+ const status = accountStatus(lane.provider, lane.account, config, {
29
29
  existsSync: ctx.existsSync,
30
30
  hostname: ctx.hostname(),
31
31
  probe: ctx.probeProvider,
32
+ readClaudeCredential: ctx.readClaudeCredential,
32
33
  env: ctx.env,
33
- }).logged_in !== true) {
34
- throw new LanesError("account_not_logged_in", `Account ${lane.account} for ${lane.provider} is not logged in (no credential file)`, { provider: lane.provider, account: lane.account, lane_id: lane.lane_id });
34
+ });
35
+ if (status.logged_in !== true) {
36
+ const expired = status.sign_in_state === "expired";
37
+ throw new LanesError("account_not_logged_in", expired
38
+ ? `Account ${lane.account} for ${lane.provider} has an expired Claude sign-in; open Claude Code on this account and run /login`
39
+ : `Account ${lane.account} for ${lane.provider} is not logged in (no credential file)`, {
40
+ provider: lane.provider,
41
+ account: lane.account,
42
+ lane_id: lane.lane_id,
43
+ ...(expired ? { sign_in_state: "expired", sign_in_expires_at: status.sign_in_expires_at ?? null } : {}),
44
+ });
35
45
  }
36
46
  }
37
47
  export function accountMoveSummary(move) {
@@ -80,6 +80,7 @@ export function createLaneWatchHandlers(deps) {
80
80
  localStatusCache.set(key, accountStatus(lane.provider, lane.account, config, {
81
81
  existsSync: ctx.existsSync,
82
82
  probe: ctx.probeProvider,
83
+ readClaudeCredential: ctx.readClaudeCredential,
83
84
  env: ctx.env,
84
85
  hostname: currentHostname,
85
86
  }).logged_in);
@@ -7,11 +7,12 @@
7
7
  */
8
8
  import { Command } from "commander";
9
9
  import type { MonitorLaneMessagePayload, MonitorLaneMessageResult } from "./lanes-message.js";
10
- import { type Ctx, type LanesMesh, type OutpostRouteIdentity, type SpawnLoginResult } from "./lanes-shared.js";
10
+ import { type Ctx, type LanesMesh, type OutpostRouteIdentity, type ResolvedResumeProfile, type ResumeProfileRepinAuthorization, type SpawnLoginResult } from "./lanes-shared.js";
11
11
  export type { LanesMesh, SpawnLoginResult } from "./lanes-shared.js";
12
12
  export { CLAUDE_ACCOUNT_BALANCE_SIGNALS_FLAG, LANE_OUTPOST_LOAD_BALANCING_FLAG } from "./lanes-shared.js";
13
13
  export { resolveAnswerChoice } from "./lanes-questions.js";
14
14
  import { type FocusGuardPullRequest } from "../lib/lanes/focus-guard.js";
15
+ import { type StatFs } from "../lib/lanes/disk-guard.js";
15
16
  import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
16
17
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
17
18
  export type { OutpostIdentity } from "../lib/lanes/admission-host.js";
@@ -20,8 +21,10 @@ import { emitLaneDecisionEvents } from "../lib/lanes/mesh-events.js";
20
21
  import { MAX_LANE_DEPTH, withLaneAdmissionLock, withLaneHireLocks, type LaneCompanyRef, type QuestionBody, type LaneRecord, type QuestionRecord, type ResolvedLaneWorker, type SeniorRef, type LivenessDeps, type MachineIdentity, type MonitorEvidenceReader, type RunCommand, type ThrottleDeps } from "../lib/lanes/index.js";
21
22
  import { questionBodyAbsPath } from "../lib/lanes/questions.js";
22
23
  import { type RunCompactionFn } from "../lib/lanes/compaction.js";
24
+ import { type ResolvedEffort } from "../lib/lanes/effort.js";
23
25
  import type { WatcherLoopDeps } from "../lib/lanes/watcher.js";
24
26
  import { type KnownModelsDeps, type ProbeCommand } from "../lib/lanes/models.js";
27
+ import { type ClaudeCredentialReader } from "../lib/lanes/claude-credential.js";
25
28
  import { spawnLaneWorker, type KillProcessFn, type ProveDetachedFn, type SpawnProcessFn } from "../lib/lanes/spawn.js";
26
29
  export { spawnLaneWorker };
27
30
  export declare function openPullRequestsForProject(hqRoot: string, metadata: Record<string, unknown>): FocusGuardPullRequest[];
@@ -67,6 +70,8 @@ export interface LanesCommandDeps {
67
70
  mesh?: LanesMesh;
68
71
  sleep?: (ms: number) => Promise<void>;
69
72
  spawnProcess?: SpawnProcessFn;
73
+ /** Inject the worker disk preflight without depending on host free space. */
74
+ statfs?: StatFs;
70
75
  proveDetached?: ProveDetachedFn;
71
76
  killProcess?: KillProcessFn;
72
77
  isAlive?: (pid: number) => boolean;
@@ -81,6 +86,11 @@ export interface LanesCommandDeps {
81
86
  runSystemctl?: RunCommand;
82
87
  /** Provider CLI probe for --model validation; tests inject a fake. */
83
88
  probeProvider?: ProbeCommand;
89
+ /**
90
+ * Claude access-token expiry check. Defaults to the real Keychain/file
91
+ * read when the real probe is used; a test that fakes the probe passes its own.
92
+ */
93
+ readClaudeCredential?: ClaudeCredentialReader;
84
94
  resolveMachineIdentity?: () => MachineIdentity;
85
95
  stageOutpostPayload?: (outpostId: string, bytes: Uint8Array) => Promise<{
86
96
  getUrl: string;
@@ -157,6 +167,15 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
157
167
  */
158
168
  export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
159
169
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
170
+ /** @internal Exported for regression tests of the profile-refresh company check. */
171
+ export declare function applyResumeProfileAndEffort(hqRoot: string, current: LaneRecord, next: LaneRecord, input: {
172
+ profile?: ResolvedResumeProfile;
173
+ profileRepin?: ResumeProfileRepinAuthorization;
174
+ effort?: ResolvedEffort;
175
+ keepAlive?: boolean;
176
+ at?: string;
177
+ refreshProfileSettings?: boolean;
178
+ }): void;
160
179
  export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
161
180
  export declare function readLaneReapStopEnabled(hqRoot: string, lane: LaneRecord, env: NodeJS.ProcessEnv): Promise<boolean>;
162
181
  export declare function registerLanesCommand(parent: Command, deps?: LanesCommandDeps): Command;