@indigoai-us/hq-cli 5.246.0 → 5.247.1

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.247.1] — 2026-09-27
6
+
7
+ ### Fixed
8
+
9
+ - `hq bot create setup` stops before creating a bot when the reserved setup slug exists but its record cannot be verified.
10
+
11
+ ## [5.247.0] — 2026-09-27
12
+ ### Changed
13
+
14
+ - The default-off `lanes.supersession-lineage-aware` flag lets a worker's lane
15
+ keep-alive and message delivery continue when its newer lane senior or a
16
+ same-profile sibling under that senior would otherwise look like a replacement.
17
+ - Keep-alive setup reads lineage settings before provider locks, and message
18
+ eligibility rechecks lane and retirement state after that read.
19
+
20
+ ### Changed
21
+
22
+ - `hq agent kit install` no longer implies the `@reboot` crontab entry is
23
+ enough on its own. The entry is installed and does restart the kit on an
24
+ ordinary host, but on a container whose root filesystem is rebuilt each boot
25
+ both cron and the CLI are erased along with it. The line now says so and
26
+ says what to do instead.
27
+
5
28
  ## [5.246.0] — 2026-09-27
6
29
 
7
30
  ### Fixed
@@ -279,6 +279,10 @@ export function registerAgentKitCommand(agent) {
279
279
  console.log(` kit log: ${result.fallback.logPath}`);
280
280
  if (result.fallback.reboot.installed) {
281
281
  console.log(chalk.green(" reboot: a @reboot crontab entry was installed, so the kit restarts with the host"));
282
+ console.log(chalk.dim(" That holds only if cron and this CLI survive the restart. On a container " +
283
+ "whose root filesystem is rebuilt each boot, both are erased and the entry goes with " +
284
+ "them — reinstall the kit from your persistent storage after a restart, or have your " +
285
+ "platform start `hq agent kit run all` for you."));
282
286
  }
283
287
  else {
284
288
  console.log(chalk.red(` reboot: NOT protected — ${result.fallback.reboot.reason}. Until this is fixed the kit ` +
@@ -521,11 +521,17 @@ export async function runBotCreate(nameArg, opts) {
521
521
  if (matches.length > 1) {
522
522
  throw Object.assign(new Error(`You own more than one bot called "${name}": ${matches.map((bot) => bot.agentUid).join(", ")}. Name the one you mean by its id.`), { expected: true });
523
523
  }
524
- if (matches.length === 0)
525
- return false;
526
- const remote = matches[0];
527
- if (!isReservedSetupBotRecord(remote, owner))
524
+ const setupSlug = `${RESERVED_SETUP_BOT_NAME}-${owner.slice(-8).toLowerCase()}`;
525
+ const slugMatches = mine.filter((bot) => (bot.slug ?? "").trim().toLowerCase() === setupSlug);
526
+ if (slugMatches.length > 1) {
527
+ throw Object.assign(new Error(`HQ returned more than one agent with the setup slug "${setupSlug}". The CLI cannot safely choose one; ask your HQ admin to inspect the duplicate records.`), { expected: true });
528
+ }
529
+ if (slugMatches.length === 0)
528
530
  return false;
531
+ const remote = slugMatches[0];
532
+ if (!isReservedSetupBotRecord(remote, owner)) {
533
+ throw Object.assign(new Error(`HQ lists an agent with the setup slug "${setupSlug}", but its record does not identify it as your setup bot. The CLI cannot safely adopt it; check it with your HQ admin before retrying.`), { expected: true });
534
+ }
529
535
  const { result, config } = await adoptRemoteBot({
530
536
  bot: remote,
531
537
  name,
@@ -46,7 +46,7 @@ import { effectiveAccountHome } from "../lib/lanes/account-home.js";
46
46
  import { AccountResolutionError, compareLaneQueueOrder, coveredLaneIds, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, MONITOR_ENGINES, MONITOR_MAX_TIMEOUT_MS, MONITOR_UNCOVERED_EXIT_CODE, MAX_WATCHED_LANES, monitorReminderText, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readLane, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, runLaneWatch, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, appendDropboxMessage, deliveryRefusalReason, resumeCommandRefusal, dropboxDir, dropboxInboxPath, laneAcceptsOperatorMessage, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, withLaneHireLocks, operatorMessageRefusalReason, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
47
47
  import { activeLaneStories, laneStories, laneStory, storyWarning, } from "../lib/lanes/stories.js";
48
48
  import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
49
- import { keepAliveExclusion, LEAD_LANE_KEEP_ALIVE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
49
+ import { keepAliveExclusion, LEAD_LANE_KEEP_ALIVE_FLAG, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
50
50
  import { questionBodyAbsPath, resolveQuestionCompanyWithFallback, resolveProjectQuestionCompany, } from "../lib/lanes/questions.js";
51
51
  import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
52
52
  import { compactionAfterUsage, } from "../lib/lanes/compaction.js";
@@ -4891,21 +4891,22 @@ async function runKeepAlive(ctx, laneId, opts) {
4891
4891
  throw new LanesError("keep_alive_mode_invalid", "Pass exactly one of --on or --off", { lane_id: laneId });
4892
4892
  }
4893
4893
  const hqRoot = ctx.hqRoot();
4894
+ let lineageAware = false;
4895
+ if (opts.on) {
4896
+ const initialLane = readLane(hqRoot, laneId);
4897
+ assertKeepAliveChangeAllowed(ctx, initialLane, laneId);
4898
+ lineageAware = await readLanesRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, {
4899
+ company: initialLane.company,
4900
+ env: ctx.env,
4901
+ });
4902
+ }
4894
4903
  const updated = await withAllLaneAdmissionLocks(hqRoot, async () => {
4895
4904
  const lane = readLane(hqRoot, laneId);
4896
- if (!callerMatchesSenior(ctx, lane.senior)) {
4897
- throw new LanesError("keep_alive_unauthorized", `Only senior ${seniorKey(lane.senior)} may change lane ${laneId}'s keep-alive setting`, { lane_id: laneId, senior: lane.senior });
4898
- }
4899
- if (!laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
4900
- throw new LanesError("keep_alive_owner_host_required", `Run hq lanes keep-alive ${laneId} on its owner host ${lane.host.kind}:${lane.host.id}`, { lane_id: laneId, host: lane.host });
4901
- }
4902
- if (lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") {
4903
- throw new LanesError("keep_alive_terminal_lane", `Cannot change keep-alive for lane ${laneId} in terminal state ${lane.state}`, { lane_id: laneId, state: lane.state });
4904
- }
4905
+ assertKeepAliveChangeAllowed(ctx, lane, laneId);
4905
4906
  let optedInAt;
4906
4907
  if (opts.on) {
4907
4908
  const retiredLaneIds = loadRetiredLaneIds(hqRoot);
4908
- const exclusion = keepAliveExclusion(lane, loadAllLanes(hqRoot), retiredLaneIds);
4909
+ const exclusion = keepAliveExclusion(lane, loadAllLanes(hqRoot), retiredLaneIds, lineageAware);
4909
4910
  if (exclusion !== undefined) {
4910
4911
  throw new LanesError("keep_alive_lane_excluded", `Cannot opt in lane ${laneId}: it is ${exclusion}`, { lane_id: laneId, blocked_reason: exclusion });
4911
4912
  }
@@ -4926,6 +4927,17 @@ async function runKeepAlive(ctx, laneId, opts) {
4926
4927
  lane: updated,
4927
4928
  });
4928
4929
  }
4930
+ function assertKeepAliveChangeAllowed(ctx, lane, laneId) {
4931
+ if (!callerMatchesSenior(ctx, lane.senior)) {
4932
+ throw new LanesError("keep_alive_unauthorized", "Only senior " + seniorKey(lane.senior) + " may change lane " + laneId + "'s keep-alive setting", { lane_id: laneId, senior: lane.senior });
4933
+ }
4934
+ if (!laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
4935
+ throw new LanesError("keep_alive_owner_host_required", "Run hq lanes keep-alive " + laneId + " on its owner host " + lane.host.kind + ":" + lane.host.id, { lane_id: laneId, host: lane.host });
4936
+ }
4937
+ if (lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") {
4938
+ throw new LanesError("keep_alive_terminal_lane", "Cannot change keep-alive for lane " + laneId + " in terminal state " + lane.state, { lane_id: laneId, state: lane.state });
4939
+ }
4940
+ }
4929
4941
  async function runGrant(ctx, laneId, opts) {
4930
4942
  if (opts.hire === undefined) {
4931
4943
  throw new LanesError("missing_hire_grant", "--hire is required", {});
@@ -5808,7 +5820,9 @@ async function runMessage(ctx, laneId, opts) {
5808
5820
  }
5809
5821
  }
5810
5822
  try {
5811
- const exclusion = messageLaneExclusion(ctx, lane);
5823
+ const eligibility = await messageLaneExclusion(ctx, lane);
5824
+ lane = eligibility.lane;
5825
+ const exclusion = eligibility.exclusion;
5812
5826
  if (exclusion !== undefined) {
5813
5827
  let messageCount;
5814
5828
  if (dropboxInboxHasMessages(hqRoot, laneId)) {
@@ -6183,10 +6197,24 @@ async function runMessage(ctx, laneId, opts) {
6183
6197
  throw err;
6184
6198
  }
6185
6199
  }
6186
- function messageLaneExclusion(ctx, lane) {
6187
- if (lane.state === "killed" || lane.state === "cancelled")
6188
- return lane.state;
6189
- return keepAliveExclusion(lane, loadAllLanes(ctx.hqRoot()), loadRetiredLaneIds(ctx.hqRoot()));
6200
+ async function messageLaneExclusion(ctx, lane) {
6201
+ if (lane.state === "killed" || lane.state === "cancelled") {
6202
+ return { lane, exclusion: lane.state };
6203
+ }
6204
+ const lineageAware = await readLanesRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, {
6205
+ company: lane.company,
6206
+ env: ctx.env,
6207
+ });
6208
+ const currentLane = readLane(ctx.hqRoot(), lane.lane_id);
6209
+ if (currentLane.state === "killed" || currentLane.state === "cancelled") {
6210
+ return { lane: currentLane, exclusion: currentLane.state };
6211
+ }
6212
+ const lanes = loadAllLanes(ctx.hqRoot());
6213
+ const retiredLaneIds = loadRetiredLaneIds(ctx.hqRoot());
6214
+ return {
6215
+ lane: currentLane,
6216
+ exclusion: keepAliveExclusion(currentLane, lanes, retiredLaneIds, lineageAware),
6217
+ };
6190
6218
  }
6191
6219
  function recordMessageUndeliverable(ctx, lane, reason, messageCount) {
6192
6220
  writeWatcherDecision(watcherOutLogPath(ctx.hqRoot(), lane.lane_id), {
@@ -6206,10 +6234,12 @@ function recordMessageUndeliverable(ctx, lane, reason, messageCount) {
6206
6234
  */
6207
6235
  async function recoverQueuedMessages(ctx, laneId) {
6208
6236
  const hqRoot = ctx.hqRoot();
6209
- const lane = readLane(hqRoot, laneId);
6237
+ let lane = readLane(hqRoot, laneId);
6210
6238
  if (!dropboxInboxHasMessages(hqRoot, laneId))
6211
6239
  return { action: "none", message_count: 0 };
6212
- const exclusion = messageLaneExclusion(ctx, lane);
6240
+ const eligibility = await messageLaneExclusion(ctx, lane);
6241
+ lane = eligibility.lane;
6242
+ const exclusion = eligibility.exclusion;
6213
6243
  const refusal = exclusion ?? (lane.state === "done"
6214
6244
  ? "done_before_envelope"
6215
6245
  : lane.state === "awaiting_input"
@@ -1,6 +1,9 @@
1
1
  import type { LaneRecord, QuestionRecord } from "./types.js";
2
2
  export declare const LEAD_LANE_KEEP_ALIVE_FLAG = "lanes.lead-keep-alive";
3
3
  export declare const LEAD_LANE_KEEP_ALIVE_DEFAULT_ENABLED = false;
4
+ /** Missing or unreadable registry values retain the legacy supersession behavior. */
5
+ export declare const LANE_SUPERSESSION_LINEAGE_AWARE_FLAG = "lanes.supersession-lineage-aware";
6
+ export declare const LANE_SUPERSESSION_LINEAGE_AWARE_DEFAULT_ENABLED = false;
4
7
  export declare const LEAD_LANE_KEEP_ALIVE_MAX_RESUMES = 24;
5
8
  export declare const LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS: number;
6
9
  export declare const LEAD_LANE_KEEP_ALIVE_MAX_BACKOFF_MS: number;
@@ -27,7 +30,7 @@ export declare function retiredLaneRegistryPath(hqRoot: string): string;
27
30
  export declare function loadRetiredLaneIds(hqRoot: string): ReadonlySet<string>;
28
31
  /** IDs may be stored bare or with an owner-host suffix after an underscore. */
29
32
  export declare function isRetiredLaneId(laneId: string, retiredLaneIds: ReadonlySet<string>): boolean;
30
- export declare function keepAliveSupersededBy(lane: LaneRecord, lanes: readonly LaneRecord[]): LaneRecord | undefined;
31
- export declare function keepAliveExclusion(lane: LaneRecord, lanes: readonly LaneRecord[], retiredLaneIds: ReadonlySet<string>): "retired" | "superseded" | undefined;
33
+ export declare function keepAliveSupersededBy(lane: LaneRecord, lanes: readonly LaneRecord[], lineageAware?: boolean): LaneRecord | undefined;
34
+ export declare function keepAliveExclusion(lane: LaneRecord, lanes: readonly LaneRecord[], retiredLaneIds: ReadonlySet<string>, lineageAware?: boolean): "retired" | "superseded" | undefined;
32
35
  export declare function leadLaneKeepAliveBackoffMs(attempt: number): number;
33
36
  //# sourceMappingURL=keep-alive.d.ts.map
@@ -5,6 +5,9 @@ import { dropboxInboxHasMessages } from "./dropbox.js";
5
5
  import { listQuestionIds, readQuestion } from "./store.js";
6
6
  export const LEAD_LANE_KEEP_ALIVE_FLAG = "lanes.lead-keep-alive";
7
7
  export const LEAD_LANE_KEEP_ALIVE_DEFAULT_ENABLED = false;
8
+ /** Missing or unreadable registry values retain the legacy supersession behavior. */
9
+ export const LANE_SUPERSESSION_LINEAGE_AWARE_FLAG = "lanes.supersession-lineage-aware";
10
+ export const LANE_SUPERSESSION_LINEAGE_AWARE_DEFAULT_ENABLED = false;
8
11
  export const LEAD_LANE_KEEP_ALIVE_MAX_RESUMES = 24;
9
12
  export const LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS = 5 * 60 * 1000;
10
13
  export const LEAD_LANE_KEEP_ALIVE_MAX_BACKOFF_MS = 60 * 60 * 1000;
@@ -119,11 +122,22 @@ export function isRetiredLaneId(laneId, retiredLaneIds) {
119
122
  }
120
123
  return false;
121
124
  }
122
- export function keepAliveSupersededBy(lane, lanes) {
125
+ export function keepAliveSupersededBy(lane, lanes, lineageAware = LANE_SUPERSESSION_LINEAGE_AWARE_DEFAULT_ENABLED) {
123
126
  const createdAt = Date.parse(lane.created_at);
124
127
  if (!Number.isFinite(createdAt))
125
128
  return undefined;
126
129
  const lanesById = new Map(lanes.map((candidate) => [candidate.lane_id, candidate]));
130
+ const targetAncestorIds = new Set();
131
+ if (lineageAware) {
132
+ let parentLaneId = lane.senior.kind === "lane" ? lane.senior.id : undefined;
133
+ while (parentLaneId) {
134
+ if (targetAncestorIds.has(parentLaneId))
135
+ break;
136
+ targetAncestorIds.add(parentLaneId);
137
+ const parent = lanesById.get(parentLaneId);
138
+ parentLaneId = parent?.senior.kind === "lane" ? parent.senior.id : undefined;
139
+ }
140
+ }
127
141
  return lanes.find((candidate) => {
128
142
  if (candidate.lane_id === lane.lane_id || Date.parse(candidate.created_at) <= createdAt)
129
143
  return false;
@@ -132,6 +146,8 @@ export function keepAliveSupersededBy(lane, lanes) {
132
146
  : candidate.company?.uid === lane.company.uid;
133
147
  if (!sameCompany)
134
148
  return false;
149
+ if (lineageAware && targetAncestorIds.has(candidate.lane_id))
150
+ return false;
135
151
  // Any descendant is part of this lead's work tree, not a replacement lead.
136
152
  let parentLaneId = candidate.senior.kind === "lane" ? candidate.senior.id : undefined;
137
153
  const visitedLaneIds = new Set();
@@ -144,18 +160,22 @@ export function keepAliveSupersededBy(lane, lanes) {
144
160
  const parent = lanesById.get(parentLaneId);
145
161
  parentLaneId = parent?.senior.kind === "lane" ? parent.senior.id : undefined;
146
162
  }
163
+ const sameLaneSenior = lane.senior.kind === "lane" &&
164
+ candidate.senior.kind === "lane" &&
165
+ candidate.senior.id === lane.senior.id;
147
166
  const sameProfile = lane.worker_profile !== undefined &&
148
- candidate.worker_profile === lane.worker_profile;
167
+ candidate.worker_profile === lane.worker_profile &&
168
+ !(lineageAware && sameLaneSenior);
149
169
  const projectLead = lane.project_id !== undefined &&
150
170
  candidate.project_id === lane.project_id &&
151
171
  (candidate.hire_grant ?? 0) > 0;
152
172
  return sameProfile || projectLead;
153
173
  });
154
174
  }
155
- export function keepAliveExclusion(lane, lanes, retiredLaneIds) {
175
+ export function keepAliveExclusion(lane, lanes, retiredLaneIds, lineageAware = LANE_SUPERSESSION_LINEAGE_AWARE_DEFAULT_ENABLED) {
156
176
  if (isRetiredLaneId(lane.lane_id, retiredLaneIds))
157
177
  return "retired";
158
- return keepAliveSupersededBy(lane, lanes) === undefined ? undefined : "superseded";
178
+ return keepAliveSupersededBy(lane, lanes, lineageAware) === undefined ? undefined : "superseded";
159
179
  }
160
180
  export function leadLaneKeepAliveBackoffMs(attempt) {
161
181
  if (!Number.isSafeInteger(attempt) || attempt < 1) {
@@ -22,7 +22,7 @@ import { ANSWERED_QUESTIONS_FILE, armLaneWatcher, envelopeRepairMode, isPidRecor
22
22
  import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, updateLane, updateQuestion, } from "./store.js";
23
23
  import { applyThrottleDecision, decideAdmission, defaultThrottleDeps, emptyThrottleState, evaluateThrottle, isCpuCheckEnabled, readAvailableMemMb, readThrottleState, resolveCpuBusyFraction, throttleStatePath, writeThrottleState, } from "./throttle.js";
24
24
  import { readLanesRegistryFlag } from "./registry-flag.js";
25
- import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT, LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, LEAD_LANE_KEEP_ALIVE_RESUME_TEXT, canResumeDoneKeepAliveLane, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
25
+ import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT, LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, LEAD_LANE_KEEP_ALIVE_RESUME_TEXT, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, canResumeDoneKeepAliveLane, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
26
26
  import { providerSupportsSessionResume } from "./delivery.js";
27
27
  import { applyDefaultQuotaHold, readQuotaSignal, recordAccountQuota } from "./quota.js";
28
28
  import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
@@ -821,17 +821,24 @@ export async function runPassBody(hqRoot, deps) {
821
821
  keepAliveFlagCompanies.set(key, lane.company);
822
822
  }
823
823
  const keepAliveFlagByCompany = new Map();
824
+ const supersessionLineageAwareByCompany = new Map();
824
825
  const env = deps.env ?? process.env;
825
826
  await Promise.all([...keepAliveFlagCompanies.entries()].map(async ([key, company]) => {
826
- keepAliveFlagByCompany.set(key, await readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, {
827
+ const options = {
827
828
  company: company ?? undefined,
828
829
  env,
829
- }));
830
+ };
831
+ const [keepAliveEnabled, lineageAware] = await Promise.all([
832
+ readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, options),
833
+ readRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, options),
834
+ ]);
835
+ keepAliveFlagByCompany.set(key, keepAliveEnabled);
836
+ supersessionLineageAwareByCompany.set(key, lineageAware);
830
837
  }));
831
838
  await withAllLaneAdmissionLocks(hqRoot, async () => {
832
839
  const freshLanes = loadLaneRecords(hqRoot, summary);
833
840
  const freshQuestions = loadQuestionRecords(hqRoot, summary);
834
- resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, deps, now, keepAliveFlagByCompany);
841
+ resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, deps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany);
835
842
  const drainLanes = loadLaneRecords(hqRoot, summary);
836
843
  await admitQueued(hqRoot, drainLanes, summary, deps, now);
837
844
  await resumeAwaiting(hqRoot, drainLanes, freshQuestions, summary, deps);
@@ -1546,7 +1553,7 @@ function latestEnvelopeMtimeMs(hqRoot, lane, ref) {
1546
1553
  throw new LanesError("keep_alive_envelope_timestamp_unavailable", `Cannot stat latest envelope for lane ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: lane.lane_id, path: filePath });
1547
1554
  }
1548
1555
  }
1549
- function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany) {
1556
+ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany) {
1550
1557
  const nowMs = now.getTime();
1551
1558
  const nowIso = now.toISOString();
1552
1559
  const config = loadLanesConfig(hqRoot);
@@ -1702,7 +1709,7 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
1702
1709
  recordKeepAliveSkip(hqRoot, lane, "retired_list_invalid", summary, deps);
1703
1710
  continue;
1704
1711
  }
1705
- const exclusion = keepAliveExclusion(lane, lanes, retiredLaneIds);
1712
+ const exclusion = keepAliveExclusion(lane, lanes, retiredLaneIds, supersessionLineageAwareByCompany.get(keepAliveCompanyKey(lane.company)) === true);
1706
1713
  if (exclusion !== undefined) {
1707
1714
  recordKeepAliveSkip(hqRoot, lane, exclusion, summary, deps);
1708
1715
  continue;
@@ -1790,7 +1797,7 @@ function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCom
1790
1797
  continue;
1791
1798
  }
1792
1799
  const currentLanes = loadLaneRecords(hqRoot, summary);
1793
- const currentExclusion = keepAliveExclusion(current, currentLanes, retiredLaneIds);
1800
+ const currentExclusion = keepAliveExclusion(current, currentLanes, retiredLaneIds, supersessionLineageAwareByCompany.get(keepAliveCompanyKey(current.company)) === true);
1794
1801
  if (currentExclusion !== undefined) {
1795
1802
  recordKeepAliveSkip(hqRoot, current, currentExclusion, summary, deps);
1796
1803
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.246.0",
3
+ "version": "5.247.1",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {