@indigoai-us/hq-cli 5.291.0 → 5.293.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 (64) hide show
  1. package/CHANGELOG.md +53 -8
  2. package/assets/scaffold/core/scripts/handoff-finalize.sh +18 -0
  3. package/assets/scaffold/core/scripts/handoff-post.sh +71 -6
  4. package/assets/scaffold/core/scripts/hq-delegate-bundle.sh +307 -0
  5. package/assets/scaffold/core/scripts/hq-delegate-grant.sh +226 -0
  6. package/assets/scaffold/core/scripts/hq-delegate-publish.sh +91 -0
  7. package/assets/scaffold/core/scripts/hq-delegate-send.sh +259 -0
  8. package/assets/scaffold/core/scripts/hq-delegate-verify.sh +213 -0
  9. package/assets/scaffold/core/scripts/hq-job-notify.sh +389 -0
  10. package/assets/scaffold/core/scripts/hq-job-probe.sh +835 -0
  11. package/assets/scaffold/core/scripts/hq-job-remediate.sh +359 -0
  12. package/assets/scaffold/core/scripts/hq-job-run.sh +1025 -0
  13. package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +830 -0
  14. package/dist/command-catalog.generated.d.ts +109 -1
  15. package/dist/command-catalog.generated.js +136 -1
  16. package/dist/commands/core-delegate-w1b.d.ts +8 -0
  17. package/dist/commands/core-delegate-w1b.js +1049 -0
  18. package/dist/commands/core-delegate.d.ts +28 -0
  19. package/dist/commands/core-delegate.js +15 -13
  20. package/dist/commands/core-handoff.js +152 -23
  21. package/dist/commands/core-jobs-native.d.ts +15 -0
  22. package/dist/commands/core-jobs-native.js +709 -2
  23. package/dist/commands/core-ontology.d.ts +22 -4
  24. package/dist/commands/core-ontology.js +90 -8
  25. package/dist/commands/core.js +10 -0
  26. package/dist/commands/lanes-move.js +54 -1
  27. package/dist/commands/lanes-questions.js +4 -0
  28. package/dist/commands/lanes.d.ts +2 -0
  29. package/dist/commands/lanes.js +119 -5
  30. package/dist/lib/checkpoint-ontology/drop-aliases.d.ts +41 -0
  31. package/dist/lib/checkpoint-ontology/drop-aliases.js +76 -0
  32. package/dist/lib/checkpoint-ontology/entity-file.d.ts +6 -3
  33. package/dist/lib/checkpoint-ontology/entity-file.js +12 -6
  34. package/dist/lib/checkpoint-ontology/merge-copies.d.ts +27 -8
  35. package/dist/lib/checkpoint-ontology/merge-copies.js +64 -97
  36. package/dist/lib/core-utils/sentry-report.js +2 -2
  37. package/dist/lib/daemon/flag.d.ts +2 -0
  38. package/dist/lib/daemon/flag.js +2 -0
  39. package/dist/lib/daemon/run.d.ts +11 -0
  40. package/dist/lib/daemon/run.js +17 -1
  41. package/dist/lib/lanes/autopilot.d.ts +13 -1
  42. package/dist/lib/lanes/autopilot.js +271 -28
  43. package/dist/lib/lanes/grants.d.ts +1 -1
  44. package/dist/lib/lanes/grants.js +7 -5
  45. package/dist/lib/lanes/index.d.ts +1 -1
  46. package/dist/lib/lanes/keep-alive.d.ts +5 -0
  47. package/dist/lib/lanes/keep-alive.js +9 -4
  48. package/dist/lib/lanes/questions.d.ts +2 -1
  49. package/dist/lib/lanes/questions.js +2 -0
  50. package/dist/lib/lanes/reconcile.js +186 -9
  51. package/dist/lib/lanes/spawn.js +4 -0
  52. package/dist/lib/lanes/store.js +21 -1
  53. package/dist/lib/lanes/types.d.ts +13 -1
  54. package/dist/lib/lanes/types.js +2 -2
  55. package/dist/lib/lanes/watcher.js +3 -1
  56. package/dist/utils/scaffold-sibling-references.js +4 -4
  57. package/dist/utils/self-update.d.ts +13 -0
  58. package/dist/utils/self-update.js +33 -7
  59. package/dist/utils/staged-install.d.ts +34 -0
  60. package/dist/utils/staged-install.js +296 -0
  61. package/dist/utils/version-gate.d.ts +15 -0
  62. package/dist/utils/version-gate.js +46 -2
  63. package/package.json +1 -1
  64. package/scripts/mesh-daemon-if-stale.mjs +4 -0
@@ -19,19 +19,19 @@ import { loadWorkerProfile } from "./profile.js";
19
19
  import { terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
20
20
  import { classifySessionLiveness, defaultLivenessDeps, loadLivenessIndex, readDaemonPresenceConnected, unindexedClaudeSessionLiveness, } from "./liveness.js";
21
21
  import { ANSWERED_QUESTIONS_FILE, armLaneWatcher, envelopeRepairMode, isPidRecordAlive, laneRunDir, readPidRecord, readProcStarttime, readSpawnManifest, markSpawnFailure, spawnEnvelopeRepair, spawnLaneWorker, retryLaneOnAlternateAccount, } from "./spawn.js";
22
- import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, updateLane, updateQuestion, } from "./store.js";
22
+ import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, listQuestionIds, updateLane, updateQuestion, } from "./store.js";
23
23
  import { applyThrottleDecision, decideAdmission, defaultThrottleDeps, emptyThrottleState, evaluateThrottle, isCpuCheckEnabled, readAvailableMemMb, readThrottleState, resolveCpuBusyFraction, sampleCpuBusyObservation, recordCpuBusyObservation, throttleStatePath, writeThrottleState, } from "./throttle.js";
24
24
  import { readLanesRegistryFlag } from "./registry-flag.js";
25
25
  import { resolveLaneCompany } from "./lane-company.js";
26
26
  import { appendDropboxMessage } from "./dropbox.js";
27
27
  import { DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG, listHostProcesses, runRunawayToolGuard, RUNAWAY_TOOL_GUARD_FLAG, } from "./runaway-tool-guard.js";
28
- 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, doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
28
+ import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS, LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, queueLaneTerminalResume, LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT, LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, LEAD_LANE_KEEP_ALIVE_RESUME_TEXT, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
29
29
  import { providerSupportsSessionResume } from "./delivery.js";
30
30
  import { applyDefaultQuotaHold, readQuotaSignal, recordAccountQuota } from "./quota.js";
31
31
  import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
32
32
  import { SILENCE_MS, appendWatcherEvent, classifyLaneExit, isPidAlive, missingExpectedEnvelopeFile, readLatestEnvelopeFile, readLatestEnvelopeRecord, watcherEventsPath, watcherOutLogPath, writeWatcherDecision, } from "./watcher.js";
33
33
  import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshCompany, } from "./mesh-events.js";
34
- import { laneStories, storyStatusForEnvelope } from "./stories.js";
34
+ import { activeLaneStories, laneStories, storyStatusForEnvelope } from "./stories.js";
35
35
  import { resolveManagerStatusGateForLane } from "./manager-status-gate.js";
36
36
  import { decideEnvelopeRepair, envelopeQuestionIds, routeProviderFailure } from "./watcher.js";
37
37
  import { PROVIDER_REJECTED_REASON, providerRejectionDetails, readProviderRejection, } from "./provider-rejection.js";
@@ -962,8 +962,9 @@ export async function runPassBody(hqRoot, deps) {
962
962
  const readRegistryFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
963
963
  const keepAliveFlagCompanies = new Map();
964
964
  for (const lane of lanes) {
965
- if (!(lane.state === "done" || lane.state === "interrupted" || lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") ||
965
+ if (!(lane.state === "done" || lane.state === "interrupted" || lane.state === "blocked" || lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") ||
966
966
  (lane.keep_alive !== true && lane.worker_profile === undefined) ||
967
+ (lane.state === "blocked" && lane.keep_alive !== true) ||
967
968
  !laneRunsHere(lane, deps.host) ||
968
969
  (lane.state === "done" && (lane.terminal_reason === PROVIDER_REJECTED_REASON ||
969
970
  lane.terminal_reason === "account_not_logged_in" ||
@@ -985,6 +986,7 @@ export async function runPassBody(hqRoot, deps) {
985
986
  keepAliveFlagCompanies.set(key, company);
986
987
  }
987
988
  const keepAliveFlagByCompany = new Map();
989
+ const blockedResumeFlagByCompany = new Map();
988
990
  const supersessionLineageAwareByCompany = new Map();
989
991
  const env = deps.env ?? process.env;
990
992
  await Promise.all([...keepAliveFlagCompanies.entries()].map(async ([key, company]) => {
@@ -992,11 +994,19 @@ export async function runPassBody(hqRoot, deps) {
992
994
  company: company ?? undefined,
993
995
  env,
994
996
  };
995
- const [keepAliveEnabled, lineageAware] = await Promise.all([
997
+ const hasBlockedKeepAliveLane = lanes.some((lane) => lane.state === "blocked" &&
998
+ lane.keep_alive === true &&
999
+ laneRunsHere(lane, startDeps.host) &&
1000
+ keepAliveCompanyKey(resolveLaneCompany(hqRoot, lane).company) === key);
1001
+ const [keepAliveEnabled, lineageAware, blockedResumeEnabled] = await Promise.all([
996
1002
  readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, options),
997
1003
  readRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, options),
1004
+ hasBlockedKeepAliveLane
1005
+ ? readRegistryFlag(LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, options)
1006
+ : Promise.resolve(false),
998
1007
  ]);
999
1008
  keepAliveFlagByCompany.set(key, keepAliveEnabled);
1009
+ blockedResumeFlagByCompany.set(key, blockedResumeEnabled);
1000
1010
  supersessionLineageAwareByCompany.set(key, lineageAware);
1001
1011
  }));
1002
1012
  const admissionThrottleDeps = startDeps.throttleDeps ?? defaultThrottleDeps(startDeps.env ?? process.env);
@@ -1016,7 +1026,7 @@ export async function runPassBody(hqRoot, deps) {
1016
1026
  await withAllLaneAdmissionLocks(hqRoot, async () => {
1017
1027
  const freshLanes = loadLaneRecords(hqRoot, summary);
1018
1028
  const freshQuestions = loadQuestionRecords(hqRoot, summary);
1019
- await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany);
1029
+ await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany);
1020
1030
  const drainLanes = loadLaneRecords(hqRoot, summary);
1021
1031
  await admitQueued(hqRoot, drainLanes, summary, admissionDeps, now);
1022
1032
  await resumeAwaiting(hqRoot, drainLanes, freshQuestions, summary, admissionDeps);
@@ -1666,7 +1676,8 @@ function keepAliveProgressResetByHuman(lane) {
1666
1676
  return true;
1667
1677
  const messageMs = Date.parse(lane.last_message_at ?? "");
1668
1678
  const resumeMs = lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_RESUME_TEXT ||
1669
- lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT
1679
+ lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT ||
1680
+ lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT
1670
1681
  ? Number.NaN
1671
1682
  : Date.parse(lane.last_resume?.at ?? "");
1672
1683
  return Number.isFinite(lastAutoMs) &&
@@ -1779,7 +1790,169 @@ function latestEnvelopeMtimeMs(hqRoot, lane, ref) {
1779
1790
  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 });
1780
1791
  }
1781
1792
  }
1782
- async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany) {
1793
+ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, now, keepAliveFlags, blockedFlags, companyForLane, lanes, retiredLaneIds, lineageAwareByCompany) {
1794
+ const company = companyForLane(lane);
1795
+ if (company === null) {
1796
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_company_unresolved" });
1797
+ return;
1798
+ }
1799
+ const key = keepAliveCompanyKey(company);
1800
+ if (keepAliveFlags.get(key) !== true || blockedFlags.get(key) !== true) {
1801
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked" });
1802
+ return;
1803
+ }
1804
+ if (lane.terminal_reason === "approval_gate_violated" || lane.last_envelope?.blocked_reason === "approval_gate_violated") {
1805
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_approval_gate_violated" });
1806
+ return;
1807
+ }
1808
+ if (lane.last_envelope?.decision !== "blocked") {
1809
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_missing_blocked_envelope" });
1810
+ return;
1811
+ }
1812
+ if (retiredLaneIds === null) {
1813
+ recordKeepAliveSkip(hqRoot, lane, "retired_list_invalid", summary, deps);
1814
+ return;
1815
+ }
1816
+ const exclusion = keepAliveExclusion(lane, lanes, retiredLaneIds, lineageAwareByCompany.get(key) === true, companyForLane);
1817
+ if (exclusion !== undefined) {
1818
+ recordKeepAliveSkip(hqRoot, lane, exclusion, summary, deps);
1819
+ return;
1820
+ }
1821
+ const terminalAt = lane.last_envelope.at;
1822
+ const terminalMs = Date.parse(terminalAt ?? "");
1823
+ if (!Number.isFinite(terminalMs)) {
1824
+ recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, "blocked terminal time is invalid");
1825
+ return;
1826
+ }
1827
+ if (activeLaneStories(lane).length === 0) {
1828
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_no_open_stories" });
1829
+ return;
1830
+ }
1831
+ let pendingQuestion = questions.some((question) => question.lane_id === lane.lane_id && question.status === "pending");
1832
+ try {
1833
+ for (const id of listQuestionIds(hqRoot)) {
1834
+ const question = readQuestion(hqRoot, id);
1835
+ if (question.lane_id === lane.lane_id && question.status === "pending")
1836
+ pendingQuestion = true;
1837
+ }
1838
+ }
1839
+ catch (error) {
1840
+ markDirty(summary, { kind: "lane", id: lane.lane_id, why: "blocked keep-alive question read failed: " + (error instanceof Error ? error.message : String(error)) });
1841
+ return;
1842
+ }
1843
+ const blockedHistory = lane.status_gate_history ?? [];
1844
+ const priorResumes = lane.keep_alive_blocked_resume_streak ?? 0;
1845
+ const alreadyEscalated = blockedHistory.some((entry) => entry.event === "keep_alive_blocked_escalated" && entry.terminal_at === terminalAt);
1846
+ if (pendingQuestion) {
1847
+ appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "open_question", deps);
1848
+ return;
1849
+ }
1850
+ if (priorResumes >= 2) {
1851
+ if (!alreadyEscalated) {
1852
+ casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
1853
+ ...current,
1854
+ status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
1855
+ event: "keep_alive_blocked_escalated",
1856
+ at: now.toISOString(),
1857
+ terminal_at: terminalAt,
1858
+ }),
1859
+ }), deps.dryRun);
1860
+ }
1861
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked_escalated" });
1862
+ return;
1863
+ }
1864
+ if ((lane.keep_alive_resume_count ?? 0) >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES || lane.keep_alive_exhausted_at !== undefined) {
1865
+ if (lane.keep_alive_exhausted_at === undefined && !deps.dryRun) {
1866
+ casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" &&
1867
+ (current.keep_alive_resume_count ?? 0) >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, (current) => ({ ...current, keep_alive_exhausted_at: now.toISOString() }), false);
1868
+ }
1869
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_exhausted" });
1870
+ return;
1871
+ }
1872
+ if (now.getTime() - terminalMs < LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS) {
1873
+ appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
1874
+ return;
1875
+ }
1876
+ const latest = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
1877
+ if (latest.version !== lane.version || latest.state !== "blocked" || latest.last_envelope?.decision !== "blocked" || latest.last_envelope.at !== terminalAt)
1878
+ return;
1879
+ let hasOpenQuestion;
1880
+ try {
1881
+ hasOpenQuestion = listQuestionIds(hqRoot).some((id) => {
1882
+ const question = readQuestion(hqRoot, id);
1883
+ return question.lane_id === lane.lane_id && question.status === "pending";
1884
+ });
1885
+ }
1886
+ catch (error) {
1887
+ markDirty(summary, { kind: "lane", id: lane.lane_id, why: "blocked keep-alive question recheck failed: " + (error instanceof Error ? error.message : String(error)) });
1888
+ return;
1889
+ }
1890
+ if (hasOpenQuestion) {
1891
+ appendBlockedKeepAliveHistory(hqRoot, latest, terminalAt, now.toISOString(), "open_question", deps);
1892
+ return;
1893
+ }
1894
+ let current = latest;
1895
+ if (current.provider === "claude" && deps.prepareKeepAliveClaudeResume !== undefined) {
1896
+ try {
1897
+ const prepared = await deps.prepareKeepAliveClaudeResume(current);
1898
+ current = prepared.lane;
1899
+ }
1900
+ catch (error) {
1901
+ markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive account preparation failed: ${error instanceof Error ? error.message : String(error)}` });
1902
+ return;
1903
+ }
1904
+ const afterPreparation = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
1905
+ if (afterPreparation.state !== "blocked" || afterPreparation.last_envelope?.decision !== "blocked" || afterPreparation.last_envelope.at !== terminalAt)
1906
+ return;
1907
+ current = afterPreparation;
1908
+ }
1909
+ const expectedVersion = current.version;
1910
+ const result = queueLaneTerminalResume(current, now.toISOString(), LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, { allowBlockedResume: true });
1911
+ if (!result.resumeQueued) {
1912
+ if (result.resumeSkippedReason !== undefined) {
1913
+ summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_" + result.resumeSkippedReason });
1914
+ }
1915
+ return;
1916
+ }
1917
+ const queued = casLaneIf(hqRoot, latest.lane_id, (latestRecord) => latestRecord.version === expectedVersion && latestRecord.state === "blocked" && latestRecord.last_envelope?.decision === "blocked" && latestRecord.last_envelope.at === terminalAt, (current) => ({
1918
+ ...result.lane,
1919
+ version: current.version,
1920
+ keep_alive_blocked_resume_streak: priorResumes + 1,
1921
+ keep_alive_resume_count: (current.keep_alive_resume_count ?? 0) + 1,
1922
+ keep_alive_last_resume_at: now.toISOString(),
1923
+ status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
1924
+ event: "keep_alive_blocked_resumed",
1925
+ at: now.toISOString(),
1926
+ terminal_at: terminalAt,
1927
+ resume_queued: true,
1928
+ }),
1929
+ }), deps.dryRun);
1930
+ if (queued.changed) {
1931
+ summary.resumed.push({ id: lane.lane_id, from: "blocked", to: "queued", reason: "keep_alive_blocked" });
1932
+ if (!deps.dryRun) {
1933
+ recordKeepAliveEvent(hqRoot, lane.lane_id, summary, {
1934
+ event: "keep_alive_resume",
1935
+ at: now.toISOString(),
1936
+ attempt: (lane.keep_alive_resume_count ?? 0) + 1,
1937
+ resumed_from: "blocked",
1938
+ });
1939
+ }
1940
+ }
1941
+ }
1942
+ function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, deps) {
1943
+ if (deps.dryRun || lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_grace" && entry.terminal_at === terminalAt && entry.resume_skipped_reason === reason))
1944
+ return;
1945
+ casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
1946
+ ...current,
1947
+ status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
1948
+ event: "keep_alive_blocked_grace",
1949
+ at,
1950
+ terminal_at: terminalAt,
1951
+ resume_skipped_reason: reason,
1952
+ }),
1953
+ }), false);
1954
+ }
1955
+ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany) {
1783
1956
  const nowMs = now.getTime();
1784
1957
  const nowIso = now.toISOString();
1785
1958
  const config = loadLanesConfig(hqRoot);
@@ -1792,7 +1965,7 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
1792
1965
  resolvedCompanyByLaneVersion.set(key, company);
1793
1966
  return company;
1794
1967
  };
1795
- const keepAliveEnabled = [...flagByCompany.values()].some((enabled) => enabled);
1968
+ const keepAliveEnabled = [...flagByCompany.values()].some((enabled) => enabled) || [...blockedResumeFlagByCompany.values()].some(Boolean);
1796
1969
  let retiredLaneIds = null;
1797
1970
  if (keepAliveEnabled) {
1798
1971
  try {
@@ -1813,6 +1986,10 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
1813
1986
  recordKeepAliveSkip(hqRoot, originalLane, "terminal_state", summary, deps);
1814
1987
  continue;
1815
1988
  }
1989
+ if (originalLane.state === "blocked" && originalLane.keep_alive === true) {
1990
+ await resumeBlockedKeepAlive(hqRoot, originalLane, questions, summary, deps, now, flagByCompany, blockedResumeFlagByCompany, companyForLane, lanes, retiredLaneIds, supersessionLineageAwareByCompany);
1991
+ continue;
1992
+ }
1816
1993
  if ((originalLane.state !== "done" && originalLane.state !== "interrupted") ||
1817
1994
  (originalLane.keep_alive !== true && originalLane.worker_profile === undefined))
1818
1995
  continue;
@@ -28,6 +28,7 @@ import { registryWorkerRecord } from "./registry-persona.js";
28
28
  import { resolveMachineIdentity, } from "./machine-identity.js";
29
29
  import { appendCappedHistory, listLaneIds, readLane, retryUpdateLane } from "./store.js";
30
30
  import { managerStatusGateHeartbeat } from "./manager-status-gate.js";
31
+ import { LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT } from "./keep-alive.js";
31
32
  import { copySessionFiles, planSessionCopy } from "./move-account.js";
32
33
  import { ensureSessionHome } from "./session-relink.js";
33
34
  import { MESSAGE_REPLAY_STATE_NAME } from "./message-replay.js";
@@ -2133,6 +2134,9 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
2133
2134
  : {}),
2134
2135
  };
2135
2136
  }
2137
+ if (resume && pendingResume?.text !== LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT) {
2138
+ next.keep_alive_blocked_resume_streak = 0;
2139
+ }
2136
2140
  return next;
2137
2141
  });
2138
2142
  if (!claimed) {
@@ -481,6 +481,16 @@ export function parseLaneRecord(raw, filePath) {
481
481
  value: raw.keep_alive_resume_count,
482
482
  });
483
483
  }
484
+ if (raw.keep_alive_blocked_resume_streak !== undefined &&
485
+ (typeof raw.keep_alive_blocked_resume_streak !== "number" ||
486
+ !Number.isSafeInteger(raw.keep_alive_blocked_resume_streak) ||
487
+ raw.keep_alive_blocked_resume_streak < 0 || raw.keep_alive_blocked_resume_streak > 2)) {
488
+ throw new LaneSchemaError("keep_alive_blocked_resume_streak must be an integer from 0 through 2", {
489
+ path: filePath,
490
+ field: "keep_alive_blocked_resume_streak",
491
+ value: raw.keep_alive_blocked_resume_streak,
492
+ });
493
+ }
484
494
  for (const field of [
485
495
  "keep_alive_opted_in_at",
486
496
  "keep_alive_last_resume_at",
@@ -938,6 +948,12 @@ export function parseQuestionRecord(raw, filePath) {
938
948
  }
939
949
  requireString(raw.question_id, "question_id");
940
950
  requireString(raw.lane_id, "lane_id");
951
+ if (raw.priority !== undefined && raw.priority !== null && raw.priority !== "P0" && raw.priority !== "P1" && raw.priority !== "P2") {
952
+ throw new LaneSchemaError("priority must be P0, P1, or P2 when present", {
953
+ path: filePath,
954
+ value: raw.priority,
955
+ });
956
+ }
941
957
  requireInt(raw.version, "version");
942
958
  requireInt(raw.hop, "hop");
943
959
  parseOptionalCompanyField(raw, filePath);
@@ -964,7 +980,11 @@ export function parseQuestionRecord(raw, filePath) {
964
980
  value: delivered_at,
965
981
  });
966
982
  }
967
- return { ...raw, delivered_at };
983
+ return {
984
+ ...raw,
985
+ delivered_at,
986
+ ...(raw.priority === null ? { priority: undefined } : {}),
987
+ };
968
988
  }
969
989
  /**
970
990
  * HQ Sync sibling-twin grammar from `@indigoai-us/hq-cloud` `buildConflictPath`:
@@ -400,6 +400,13 @@ export interface KrFreshnessState {
400
400
  /** Stable fingerprint of the last stalled KPI set, which is resumed once. */
401
401
  stalled_fingerprint?: string;
402
402
  }
403
+ export interface KeepAliveBlockedHistoryEntry {
404
+ event: "keep_alive_blocked_grace" | "keep_alive_blocked_resumed" | "keep_alive_blocked_escalated";
405
+ at: string;
406
+ terminal_at: string;
407
+ resume_skipped_reason?: "open_question" | "blocked_grace";
408
+ resume_queued?: boolean;
409
+ }
403
410
  export interface KrFreshnessHistoryEntry {
404
411
  event: "kr_stalled";
405
412
  turn_id: string;
@@ -411,7 +418,7 @@ export interface KrFreshnessHistoryEntry {
411
418
  resume_queued?: boolean;
412
419
  resume_skipped_reason?: "open_question" | "blocked" | "miss_limit" | "no_provider_session";
413
420
  }
414
- export type LaneGateHistoryEntry = ManagerStatusGateHistoryEntry | BlockerFreshnessHistoryEntry | KrFreshnessHistoryEntry;
421
+ export type LaneGateHistoryEntry = ManagerStatusGateHistoryEntry | BlockerFreshnessHistoryEntry | KrFreshnessHistoryEntry | KeepAliveBlockedHistoryEntry;
415
422
  export interface LaneStory {
416
423
  project_id: string;
417
424
  story_id: string;
@@ -484,6 +491,8 @@ export interface LaneRecord {
484
491
  keep_alive_blocked_reason?: "not_opted_in" | "missing_opt_in_time" | "envelope_before_opt_in" | "interruption_before_opt_in" | "missing_interruption_time" | "retired" | "superseded" | "retired_list_invalid" | "terminal_state";
485
492
  /** Consecutive automatic keep-alive resumes since the last human reset. */
486
493
  keep_alive_resume_count?: number;
494
+ /** Blocked keep-alive resumes in the current blocked episode. */
495
+ keep_alive_blocked_resume_streak?: number;
487
496
  /** Time the most recent automatic keep-alive resume was queued. */
488
497
  keep_alive_last_resume_at?: string;
489
498
  /** Earliest time the next consecutive automatic resume may be queued. */
@@ -782,12 +791,15 @@ export interface QuestionAnswer {
782
791
  * question is flagged for the operator; nothing answers it automatically.
783
792
  */
784
793
  export type QuestionKind = "continue";
794
+ export type QuestionPriority = "P0" | "P1" | "P2";
785
795
  export interface QuestionRecord {
786
796
  schema_version: typeof QUESTION_SCHEMA_VERSION | string;
787
797
  question_id: string;
788
798
  lane_id: string;
789
799
  /** Set by the raiser when the options are only continue/stop. */
790
800
  question_kind?: QuestionKind;
801
+ /** Urgency selected by the lane author; absent on pre-priority records. */
802
+ priority?: QuestionPriority;
791
803
  /** `null` when the originating lane has no company. */
792
804
  company: LaneCompanyRef | null;
793
805
  project_id: string;
@@ -225,11 +225,11 @@ export const LANE_FIELD_WRITERS = {
225
225
  pickup_validation_pending: ["creator", "command", "spawn", "reconcile"],
226
226
  pickup_validation_pending_since: ["creator", "command", "spawn", "reconcile"],
227
227
  priority: ["command"], worker: ["creator", "command"], worker_path: ["creator", "command"], worker_pack: ["creator", "command"], worker_profile: ["creator", "command"], worker_profile_hash: ["creator", "command", "spawn"], registry_worker: ["creator", "command", "spawn"], registry_worker_approval_history: ["command", "watcher", "reconcile"], worker_profile_repin_history: ["command"], worker_profile_ignored: ["creator", "command", "spawn"], worker_policy_delivery: ["command", "spawn"],
228
- keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile"], keep_alive_last_resume_at: ["command", "reconcile"], keep_alive_next_eligible_at: ["command", "reconcile"], keep_alive_exhausted_at: ["command", "reconcile"],
228
+ keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile"], keep_alive_blocked_resume_streak: ["reconcile", "spawn", "watcher"], keep_alive_last_resume_at: ["command", "reconcile"], keep_alive_next_eligible_at: ["command", "reconcile"], keep_alive_exhausted_at: ["command", "reconcile"],
229
229
  provider: ["creator"], account: ["creator", "spawn", "command", "watcher", "reconcile"], account_selection: ["creator", "command"], admission_accounts: ["creator", "command"], account_history: ["command", "spawn", "watcher", "reconcile"], failed_resume_history: ["command"], pending_resume: ["command", "spawn", "watcher", "reconcile"], placement_default: ["creator"], host: ["creator", "command"], origin_host: ["creator"], origin_host_source: ["creator"],
230
230
  version: ["creator"], created_at: ["creator"], updated_at: ["creator"], ended_at: ["reconcile"], envelope_summary: ["reconcile"], attribution_history: ["command"],
231
231
  last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], model_source: ["creator", "spawn", "command"], effort: ["creator", "spawn", "command"], effort_source: ["creator", "spawn", "command"], fast_mode: ["spawn"],
232
- last_message_at: ["command"], status_gate: ["spawn", "watcher"], status_gate_history: ["watcher"], blocker_freshness_state: ["watcher"], kr_freshness_state: ["watcher"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
232
+ last_message_at: ["command"], status_gate: ["spawn", "watcher"], status_gate_history: ["watcher", "reconcile"], blocker_freshness_state: ["watcher"], kr_freshness_state: ["watcher"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
233
233
  overdue: ["reconcile"], overdue_at: ["reconcile"], last_host_report_at: ["watcher", "spawn", "reconcile"], stalled: ["watcher"], stalled_at: ["watcher"], stalled_reason: ["watcher"],
234
234
  admitted_reason: ["command", "reconcile"], terminal_reason: ["command", "watcher", "spawn", "reconcile"], stop_history: ["command", "reconcile"], settings_history: ["command"], pending_settings: ["command", "spawn"], interrupted_at: ["command", "watcher", "reconcile"], terminal_details: ["command", "spawn", "watcher", "reconcile"],
235
235
  quota_reset_at: ["watcher", "reconcile"], model_quota_marker: ["watcher", "reconcile"], outpost_command_id: ["command"],
@@ -411,6 +411,8 @@ export function applyTerminalToLaneWithResult(hqRoot, laneId, classification, op
411
411
  }), current.registry_worker_approval_history ?? []);
412
412
  }
413
413
  if (envelope) {
414
+ if (envelope.decision !== "blocked")
415
+ next.keep_alive_blocked_resume_streak = 0;
414
416
  next.last_envelope = {
415
417
  ref: envelopeRef,
416
418
  decision: envelope.decision,
@@ -1536,7 +1538,7 @@ async function runWatcherLoopOwned(hqRoot, laneId, deps) {
1536
1538
  updated = await settleKrFreshness(hqRoot, updated, manifest.turn_id, tick.emit.at, deps, logDecision);
1537
1539
  }
1538
1540
  const managerStatusFollowupQueued = manifest.turn_id !== undefined &&
1539
- updated.status_gate_history?.some((entry) => entry.turn_id === manifest.turn_id &&
1541
+ updated.status_gate_history?.some((entry) => "turn_id" in entry && entry.turn_id === manifest.turn_id &&
1540
1542
  ((entry.event === "status_missed" && entry.resume_queued === true) ||
1541
1543
  (entry.event === "blocker_stale" && entry.resume_queued === true) ||
1542
1544
  (entry.event === "kr_stalled" && entry.resume_queued === true))) === true;
@@ -42,7 +42,7 @@ function shellLocationAliases(contents) {
42
42
  const scriptRoot = SCRIPT_ROOT_TOKENS.exec(value);
43
43
  if (scriptRoot) {
44
44
  const suffix = value.slice(scriptRoot.index + scriptRoot[0].length)
45
- .match(/^\/([^"'`\s;|&)]+)/)?.[1] ?? ".";
45
+ .match(/^\/([^"'`\s;|&)}]+)/)?.[1] ?? ".";
46
46
  aliases.set(name, { kind: "asset", baseDirectory: path.posix.normalize(suffix) });
47
47
  continue;
48
48
  }
@@ -116,15 +116,15 @@ export function scanScaffoldSiblingReferences(scaffoldRoot) {
116
116
  (/\b(?:source|exec|bash|sh|node)\b/.test(line) || /^\s*\.\s+/.test(line)));
117
117
  for (const line of commandLines) {
118
118
  SCRIPT_ROOT_TOKENS.lastIndex = 0;
119
- for (const match of line.matchAll(/(?:\$\{SCRIPT_DIR\}|\$SCRIPT_DIR)\/([^"'`\s;|&)]+)/g)) {
119
+ for (const match of line.matchAll(/(?:\$\{SCRIPT_DIR\}|\$SCRIPT_DIR)\/([^"'`\s;|&)}]+)/g)) {
120
120
  recordRelative(sourceFile, match[1]);
121
121
  }
122
- const dirnamePath = new RegExp(String.raw `\$\(\s*dirname\s+["']?${BASH_SOURCE_TOKEN}["']?\s*\)(\/[^"'\x60\s;|&)]+)`, "g");
122
+ const dirnamePath = new RegExp(String.raw `\$\(\s*dirname\s+["']?${BASH_SOURCE_TOKEN}["']?\s*\)(\/[^"'\x60\s;|&)}]+)`, "g");
123
123
  for (const match of line.matchAll(dirnamePath))
124
124
  recordRelative(sourceFile, match[1]);
125
125
  for (const [alias, location] of aliases) {
126
126
  const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
127
- const aliasPath = new RegExp(String.raw `(?:\$\{${escapedAlias}\}|\$${escapedAlias})\/([^"'\x60\s;|&)]+)`, "g");
127
+ const aliasPath = new RegExp(String.raw `(?:\$\{${escapedAlias}\}|\$${escapedAlias})\/([^"'\x60\s;|&)}]+)`, "g");
128
128
  for (const match of line.matchAll(aliasPath)) {
129
129
  if (location.kind === "hq-root") {
130
130
  const reference = match[0];
@@ -161,6 +161,19 @@ export interface SelfUpdateDeps {
161
161
  interactive?: boolean;
162
162
  /** Best-effort daemon kick after the CLI install converges (tests). */
163
163
  restartDaemon?: () => RestartDaemonResult | void;
164
+ /**
165
+ * Whether `cli.staged-self-update` is on: build the new version beside the
166
+ * running package and swap it in, instead of moving the running package
167
+ * aside for the whole install. Read only when an npm global install is due.
168
+ */
169
+ stagedSelfUpdate?: () => Promise<boolean>;
170
+ /**
171
+ * Runs installs through a copy of the installer's supervisor outside the
172
+ * package until disposed. Defaults to {@link detachUpdateSupervisor}.
173
+ */
174
+ detachSupervisor?: () => {
175
+ dispose(): void;
176
+ } | null;
164
177
  }
165
178
  /**
166
179
  * Startup path: the running CLI is behind npm `latest`, so update in place and
@@ -63,7 +63,8 @@ import * as path from "node:path";
63
63
  import semver from "semver";
64
64
  import chalk from "chalk";
65
65
  import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
66
- import { buildBunInstallArgv, buildPnpmInstallArgv, buildPrefixedInstallArgv, buildSpawnPlan, checkUpdateConvergence, derivePnpmHome, isBunManagedPackageDir, isLocalDependencyInstall, isPnpmManagedPackageDir, isPrefixWritable, nonWritablePrefixNote, openInstallOutput, pnpmUpdateEnv, resolveRunningInstall, runUpdateCommand, runSupervisedUpdateCommand, } from "./version-gate.js";
66
+ import { buildBunInstallArgv, buildPnpmInstallArgv, buildPrefixedInstallArgv, buildSpawnPlan, checkUpdateConvergence, derivePnpmHome, detachUpdateSupervisor, isBunManagedPackageDir, isLocalDependencyInstall, isPnpmManagedPackageDir, isPrefixWritable, nonWritablePrefixNote, openInstallOutput, pnpmUpdateEnv, resolveRunningInstall, runUpdateCommand, runSupervisedUpdateCommand, } from "./version-gate.js";
67
+ import { canStageInstall, stagedNpmInstall } from "./staged-install.js";
67
68
  import { acquireUpdateLock as acquireSharedUpdateLock } from "./update-lock.js";
68
69
  import { markLatestIneffective } from "./version-check.js";
69
70
  import { restartDaemonServiceIfInstalled } from "../lib/mesh/live/daemon/install.js";
@@ -414,12 +415,18 @@ async function updateAndReexec(argv, flavor, known, deps) {
414
415
  console.error(chalk.dim(`hq-cli ${latest} is available, but this copy is a local dependency (${install.packageRoot}) — update the project that owns it.`));
415
416
  return { action: "skipped", latest };
416
417
  }
418
+ const staged = canStageInstall(install) && (await readStagedSelfUpdateFlag(deps, env));
417
419
  const releaseLock = flavor.lock ? (deps.acquireLock ?? acquireUpdateLock)() : () => { };
418
420
  if (!releaseLock)
419
421
  return { action: "skipped", latest };
420
422
  const renamePath = deps.renamePath ?? fs.renameSync;
421
- const installSnapshot = snapshotInstall(install, renamePath);
422
- if (!installSnapshot) {
423
+ // The in-place update moves the package aside, and the installer's
424
+ // supervisor ships inside it, so run the installer through a copy.
425
+ const supervisor = staged ? null : (deps.detachSupervisor ?? detachUpdateSupervisor)();
426
+ // The staged install keeps the running package in place until the swap and
427
+ // restores it itself, so it takes no snapshot.
428
+ const installSnapshot = staged ? null : snapshotInstall(install, renamePath);
429
+ if (!staged && !installSnapshot) {
423
430
  console.error(chalk.dim(" No backup could be made for this hq-cli install; proceeding without rollback protection."));
424
431
  }
425
432
  let result;
@@ -440,10 +447,15 @@ async function updateAndReexec(argv, flavor, known, deps) {
440
447
  // Forward env only when PNPM_HOME had to be injected, so the common path
441
448
  // spawns with the inherited environment and keeps the two-argument runner
442
449
  // call it has always made.
443
- result =
444
- updateEnv === undefined
445
- ? runner(plan.cmd, plan.args)
446
- : runner(plan.cmd, plan.args, updateEnv);
450
+ if (staged) {
451
+ result = stagedNpmInstall(install, latestValid, { run: runner, env, rename: renamePath });
452
+ }
453
+ else if (updateEnv === undefined) {
454
+ result = runner(plan.cmd, plan.args);
455
+ }
456
+ else {
457
+ result = runner(plan.cmd, plan.args, updateEnv);
458
+ }
447
459
  if (!result.ok && installSnapshot) {
448
460
  restoredAfterFailure = restoreInstall(installSnapshot, renamePath);
449
461
  if (restoredAfterFailure)
@@ -458,6 +470,7 @@ async function updateAndReexec(argv, flavor, known, deps) {
458
470
  throw error;
459
471
  }
460
472
  finally {
473
+ supervisor?.dispose();
461
474
  releaseLock();
462
475
  }
463
476
  if (!result.ok) {
@@ -537,6 +550,19 @@ async function updateAndReexec(argv, flavor, known, deps) {
537
550
  console.error(chalk.dim(`✓ hq-cli updated to ${latest}.`));
538
551
  return { action: "reexec", reexecStatus: status, latest };
539
552
  }
553
+ async function readStagedSelfUpdateFlag(deps, env) {
554
+ try {
555
+ if (deps.stagedSelfUpdate)
556
+ return await deps.stagedSelfUpdate();
557
+ // Loaded here so the startup path pays for the flag client only when an
558
+ // update is actually due.
559
+ const { readDaemonFlag, STAGED_SELF_UPDATE_FLAG } = await import("../lib/daemon/flag.js");
560
+ return await readDaemonFlag(STAGED_SELF_UPDATE_FLAG, { env });
561
+ }
562
+ catch {
563
+ return false;
564
+ }
565
+ }
540
566
  /**
541
567
  * Startup path: the running CLI is behind npm `latest`, so update in place and
542
568
  * re-run the user's command on the new version. `latest` is normally the cached
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Staged npm self-update: install the new hq-cli beside the running package,
3
+ * check that it starts, then swap it in with two renames.
4
+ *
5
+ * The in-place update moves the live package aside for the whole
6
+ * `npm install -g`, so anything that starts `hq` during the install finds no
7
+ * CLI on disk ("hq: command not found", MODULE_NOT_FOUND; outposts primary and
8
+ * 2, 5.290.0 -> 5.291.0). Here the live package stays in place until the new
9
+ * one is complete and has answered `--version`.
10
+ *
11
+ * Only a plain npm global layout is staged: `<prefix>/lib/node_modules/<pkg>`
12
+ * as a real directory, with bin links in `<prefix>/bin`. pnpm, Bun, Windows
13
+ * and linked checkouts keep the in-place update.
14
+ */
15
+ import * as fs from "node:fs";
16
+ import { type RunningInstall, type UpdateResult } from "./version-gate.js";
17
+ /** Set for the staging install; hq-cli's own postinstall skips itself on it. */
18
+ export declare const STAGED_INSTALL_ENV = "HQ_STAGED_INSTALL";
19
+ export interface StagedInstallDeps {
20
+ /** Runs the package manager. Defaults to {@link runUpdateCommand}. */
21
+ run?: (cmd: string, args: string[], env?: NodeJS.ProcessEnv) => UpdateResult;
22
+ env?: NodeJS.ProcessEnv;
23
+ /** The version the staged package's `hq --version` reports, or null. */
24
+ probeVersion?: (packageRoot: string) => string | null;
25
+ /** The package's postinstall, run from the live path after the swap. */
26
+ runPostinstall?: (packageRoot: string) => void;
27
+ rename?: typeof fs.renameSync;
28
+ /** Removes the staging and backup directories. */
29
+ remove?: (target: string) => void;
30
+ platform?: NodeJS.Platform;
31
+ }
32
+ export declare function canStageInstall(install: RunningInstall, platform?: NodeJS.Platform): boolean;
33
+ export declare function stagedNpmInstall(install: RunningInstall, version: string, deps?: StagedInstallDeps): UpdateResult;
34
+ //# sourceMappingURL=staged-install.d.ts.map