@indigoai-us/hq-cli 5.342.12 → 5.342.14

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 (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/assets/scaffold/core/scripts/hq-delegate-grant.sh +9 -0
  3. package/assets/scaffold/core/scripts/hq-delegate-send.sh +9 -0
  4. package/assets/scaffold/core/scripts/hq-job-probe.sh +9 -0
  5. package/assets/scaffold/core/scripts/jobs-validate.sh +9 -0
  6. package/dist/commands/agent-kit.d.ts +2 -0
  7. package/dist/commands/agent-kit.js +23 -17
  8. package/dist/commands/agent-mcp.d.ts +2 -0
  9. package/dist/commands/agent-mcp.js +21 -15
  10. package/dist/commands/core-delegate-w1b.js +19 -10
  11. package/dist/commands/core-delegate.d.ts +2 -0
  12. package/dist/commands/core-delegate.js +16 -4
  13. package/dist/commands/core-job-probe-native.d.ts +4 -1
  14. package/dist/commands/core-job-probe-native.js +62 -14
  15. package/dist/commands/core-job-run-native.js +14 -2
  16. package/dist/commands/core-jobs-native.js +2 -1
  17. package/dist/commands/core-ontology-native.js +18 -2
  18. package/dist/commands/core-worker-share-native.js +2 -1
  19. package/dist/commands/lanes-message.d.ts +20 -2
  20. package/dist/commands/lanes-message.js +99 -38
  21. package/dist/commands/lanes-shared.d.ts +1 -0
  22. package/dist/commands/lanes.d.ts +4 -0
  23. package/dist/commands/lanes.js +24 -30
  24. package/dist/commands/mesh.js +2 -1
  25. package/dist/commands/monitor.d.ts +14 -1
  26. package/dist/commands/monitor.js +43 -14
  27. package/dist/lib/core-utils/shell-display-path.d.ts +3 -0
  28. package/dist/lib/core-utils/shell-display-path.js +12 -0
  29. package/dist/lib/doctor/checks/anywhere.js +11 -6
  30. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  31. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  32. package/dist/lib/lanes/keep-alive-audit.d.ts +3 -0
  33. package/dist/lib/lanes/keep-alive-audit.js +59 -11
  34. package/dist/lib/lanes/reconcile.js +46 -3
  35. package/dist/lib/lanes/types.d.ts +2 -1
  36. package/dist/utils/resident-package-preload.d.ts +8 -1
  37. package/dist/utils/resident-package-preload.js +68 -0
  38. package/dist/utils/run-bundled-script.d.ts +2 -0
  39. package/dist/utils/run-bundled-script.js +1 -0
  40. package/dist/utils/windows-spawn.d.ts +2 -0
  41. package/dist/utils/windows-spawn.js +6 -0
  42. package/docs/anywhere-runtime.md +3 -1
  43. package/package.json +4 -4
  44. package/dist/lib/db/remote-engine.d.ts +0 -58
  45. package/dist/lib/db/remote-engine.js +0 -87
  46. package/dist/lib/db/remote-sql.d.ts +0 -22
  47. package/dist/lib/db/remote-sql.js +0 -36
@@ -14,6 +14,8 @@ interface KeepAliveAuditRow {
14
14
  due_at: string;
15
15
  due_reason: "next_check_at" | "backoff";
16
16
  restart: KeepAliveAuditRestart | null;
17
+ blocked_outcome?: "woken" | "skipped";
18
+ blocked_outcome_reason?: string;
17
19
  verdict: KeepAliveAuditVerdict;
18
20
  }
19
21
  interface KeepAliveAuditSummary {
@@ -42,6 +44,7 @@ export interface KeepAliveAuditResult {
42
44
  export interface StopEnvelope {
43
45
  name: string;
44
46
  mtimeMs: number;
47
+ decision: "done" | "blocked";
45
48
  nextCheckAt?: string;
46
49
  }
47
50
  export declare function readStopEnvelopes(hqRoot: string, laneId: string): {
@@ -1,11 +1,12 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS } from "./keep-alive.js";
3
+ import { LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS } from "./keep-alive.js";
4
4
  import { LanesError } from "./errors.js";
5
5
  import { laneRunDir } from "./spawn.js";
6
6
  import { compareEnvelopeNames } from "./envelope-sequence.js";
7
7
  import { listQuestionIds, readAllLanes, tryReadQuestion } from "./store.js";
8
- function readDoneClassifications(hqRoot, laneId) {
8
+ import { isIsoDateTime } from "./types.js";
9
+ function readTerminalClassifications(hqRoot, laneId) {
9
10
  const root = fs.realpathSync(hqRoot);
10
11
  const watcherLog = path.join(laneRunDir(root, laneId), "logs", "watcher.out");
11
12
  if (!withinRoot(root, watcherLog)) {
@@ -28,23 +29,23 @@ function readDoneClassifications(hqRoot, laneId) {
28
29
  return [];
29
30
  throw err;
30
31
  }
31
- const atTimes = [];
32
+ const classifications = [];
32
33
  for (const line of text.split(/\r?\n/)) {
33
34
  if (!line.includes('"classification"'))
34
35
  continue;
35
36
  try {
36
37
  const value = JSON.parse(line);
37
- if (!isRecord(value) || value.state !== "done")
38
+ if (!isRecord(value) || (value.state !== "done" && value.state !== "blocked"))
38
39
  continue;
39
40
  const at = parseTime(value.at);
40
41
  if (at !== undefined)
41
- atTimes.push(at);
42
+ classifications.push({ at, state: value.state });
42
43
  }
43
44
  catch (err) {
44
45
  throw new LanesError("keep_alive_audit_event_invalid", `Cannot parse lane watcher classification in ${watcherLog}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: laneId, path: watcherLog });
45
46
  }
46
47
  }
47
- return atTimes.sort((a, b) => a - b);
48
+ return classifications.sort((a, b) => a.at - b.at);
48
49
  }
49
50
  const KEEP_ALIVE_ON_TIME_TOLERANCE_MS = 60_000;
50
51
  const KEEP_ALIVE_NOT_RESTARTED_GRACE_MS = 5 * 60_000;
@@ -161,11 +162,15 @@ export function readStopEnvelopes(hqRoot, laneId) {
161
162
  if (!isRecord(raw) || raw.schema_version !== "hq-lane-envelope.v1") {
162
163
  throw new LanesError("keep_alive_audit_envelope_invalid", `Lane envelope is not hq-lane-envelope.v1: ${file}`, { lane_id: laneId, path: file });
163
164
  }
164
- if (raw.decision !== "done" || raw.story_complete !== false)
165
+ const doneStop = raw.decision === "done" && raw.story_complete === false;
166
+ const blockedStop = raw.decision === "blocked" && typeof raw.next_check_at === "string" &&
167
+ isIsoDateTime(raw.next_check_at) && Date.parse(raw.next_check_at) > stat.mtimeMs;
168
+ if (!doneStop && !blockedStop)
165
169
  continue;
166
170
  stops.push({
167
171
  name,
168
172
  mtimeMs: stat.mtimeMs,
173
+ decision: blockedStop ? "blocked" : "done",
169
174
  ...(typeof raw.next_check_at === "string" ? { nextCheckAt: raw.next_check_at } : {}),
170
175
  });
171
176
  }
@@ -227,11 +232,14 @@ function readKeepAliveResumeEvents(hqRoot, laneId) {
227
232
  return events.sort((a, b) => a.at - b.at);
228
233
  }
229
234
  function dueTime(stop, resumeEvents) {
235
+ const nextCheck = parseTime(stop.nextCheckAt);
236
+ if (stop.decision === "blocked" && nextCheck !== undefined && nextCheck > stop.mtimeMs) {
237
+ return { at: Math.min(nextCheck, stop.mtimeMs + LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS), reason: "next_check_at" };
238
+ }
230
239
  const previousResume = resumeEvents.filter((event) => event.at <= stop.mtimeMs).at(-1);
231
240
  const terminalPlusBackoff = stop.mtimeMs + LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS;
232
241
  let due = Math.max(terminalPlusBackoff, previousResume?.nextEligibleAt ?? 0);
233
242
  let reason = "backoff";
234
- const nextCheck = parseTime(stop.nextCheckAt);
235
243
  if (nextCheck !== undefined && nextCheck > stop.mtimeMs) {
236
244
  due = Math.max(due, Math.min(nextCheck, stop.mtimeMs + MAX_NEXT_CHECK_DELAY_MS));
237
245
  reason = "next_check_at";
@@ -329,6 +337,38 @@ function laneWasKeepAliveOffAtStop(lane, stoppedAt) {
329
337
  const optedInAt = parseTime(lane.keep_alive_opted_in_at);
330
338
  return optedInAt === undefined || optedInAt > stoppedAt;
331
339
  }
340
+ function blockedKeepAliveOutcome(lane, stop, nextEnvelopeMs) {
341
+ const terminalAt = lane.last_envelope?.ref === `envelopes/${stop.name}`
342
+ ? parseTime(lane.last_envelope.at)
343
+ : undefined;
344
+ const candidates = (lane.status_gate_history ?? []).flatMap((entry) => {
345
+ if (entry.event !== "keep_alive_blocked_resumed" && entry.event !== "keep_alive_blocked_skipped")
346
+ return [];
347
+ const at = parseTime(entry.at);
348
+ const terminal = parseTime(entry.terminal_at);
349
+ if (at === undefined || terminal === undefined)
350
+ return [];
351
+ if (nextEnvelopeMs !== undefined && at >= nextEnvelopeMs)
352
+ return [];
353
+ const sameStop = terminalAt !== undefined
354
+ ? terminal === terminalAt
355
+ : Math.abs(terminal - stop.mtimeMs) <= 10 * 60_000;
356
+ if (!sameStop)
357
+ return [];
358
+ if (entry.event === "keep_alive_blocked_skipped" && entry.reason !== "flag_off")
359
+ return [];
360
+ return [{
361
+ status: entry.event === "keep_alive_blocked_resumed" ? "woken" : "skipped",
362
+ ...(entry.reason === undefined ? {} : { reason: entry.reason }),
363
+ at: entry.at,
364
+ atMs: at,
365
+ }];
366
+ });
367
+ const latest = candidates.sort((left, right) => right.atMs - left.atMs)[0];
368
+ if (!latest)
369
+ return undefined;
370
+ return { status: latest.status, ...(latest.reason === undefined ? {} : { reason: latest.reason }), at: latest.at };
371
+ }
332
372
  function firstRestartAfter(lane, stop, nextEnvelopeMs, workerResumeEvents) {
333
373
  const recordedWorkerResume = workerResumeEvents
334
374
  .filter((event) => event.at > stop.mtimeMs && (nextEnvelopeMs === undefined || event.at < nextEnvelopeMs))
@@ -449,15 +489,18 @@ export function buildKeepAliveAudit(input) {
449
489
  const { allMtimes, stops } = readStopEnvelopes(input.hqRoot, lane.lane_id);
450
490
  const keepAliveResumeEvents = readKeepAliveResumeEvents(input.hqRoot, lane.lane_id);
451
491
  const laneWorkerResumeEvents = readLaneWorkerResumeEvents(input.hqRoot, lane.lane_id);
452
- const doneClassifications = readDoneClassifications(input.hqRoot, lane.lane_id);
492
+ const terminalClassifications = readTerminalClassifications(input.hqRoot, lane.lane_id);
453
493
  for (const stop of stops) {
454
494
  if (stop.mtimeMs < sinceMs || stop.mtimeMs > nowMs)
455
495
  continue;
456
- if (!doneClassifications.some((at) => at >= stop.mtimeMs && at - stop.mtimeMs <= 10 * 60_000))
496
+ if (!terminalClassifications.some((classification) => classification.state === stop.decision && classification.at >= stop.mtimeMs && classification.at - stop.mtimeMs <= 10 * 60_000))
457
497
  continue;
458
498
  const nextEnvelopeMs = allMtimes.find((mtime) => mtime > stop.mtimeMs);
499
+ const blockedOutcome = stop.decision === "blocked" ? blockedKeepAliveOutcome(lane, stop, nextEnvelopeMs) : undefined;
459
500
  const restart = firstRestartAfter(lane, stop, nextEnvelopeMs, laneWorkerResumeEvents);
460
501
  const due = dueTime(stop, keepAliveResumeEvents);
502
+ const reportBlockedOutcome = blockedOutcome?.status === "woken" ||
503
+ (blockedOutcome?.status === "skipped" && due.at <= nowMs);
461
504
  rows.push({
462
505
  lane: lane.lane_id,
463
506
  project: lane.project_id,
@@ -467,6 +510,10 @@ export function buildKeepAliveAudit(input) {
467
510
  due_at: iso(due.at),
468
511
  due_reason: due.reason,
469
512
  restart,
513
+ ...(reportBlockedOutcome && blockedOutcome ? {
514
+ blocked_outcome: blockedOutcome.status,
515
+ ...(blockedOutcome.reason === undefined ? {} : { blocked_outcome_reason: blockedOutcome.reason }),
516
+ } : {}),
470
517
  verdict: classifyVerdict({ dueAt: due.at, stoppedAt: stop.mtimeMs, nextCheckAt: stop.nextCheckAt, restart, lane, nowMs, resumeEvents: keepAliveResumeEvents, ...(nextEnvelopeMs === undefined ? {} : { nextEnvelopeAt: nextEnvelopeMs }), hqRoot: input.hqRoot }),
471
518
  });
472
519
  }
@@ -514,7 +561,7 @@ export function buildKeepAliveAudit(input) {
514
561
  };
515
562
  }
516
563
  export function formatKeepAliveAuditText(result) {
517
- const headings = ["LANE", "PROJECT", "PROFILE", "STOPPED", "NEXT_CHECK", "DUE", "RESTART", "VERDICT"];
564
+ const headings = ["LANE", "PROJECT", "PROFILE", "STOPPED", "NEXT_CHECK", "DUE", "RESTART", "OUTCOME", "VERDICT"];
518
565
  const values = result.rows.map((row) => [
519
566
  row.lane,
520
567
  row.project,
@@ -523,6 +570,7 @@ export function formatKeepAliveAuditText(result) {
523
570
  row.next_check_at ?? "—",
524
571
  row.due_at,
525
572
  row.restart ? `${row.restart.at ?? "untimed"} ${row.restart.trigger}${row.restart.caller ? ` (${row.restart.caller})` : ""}` : "—",
573
+ row.blocked_outcome === "woken" ? "woken" : row.blocked_outcome === "skipped" ? `due, not woken: ${(row.blocked_outcome_reason ?? "unknown").replaceAll("_", " ")}` : "—",
526
574
  row.verdict,
527
575
  ]);
528
576
  const widths = headings.map((heading, index) => Math.max(heading.length, ...values.map((row) => row[index].length)));
@@ -27,8 +27,9 @@ import { checkLaneDiskSpace, configuredCodexRolloutHomes, lowDiskGuardEnabledFor
27
27
  import { laneCompanyUidSource, resolveLaneCompany } from "./lane-company.js";
28
28
  import { appendDropboxMessage } from "./dropbox.js";
29
29
  import { DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG, listHostProcesses, runRunawayToolGuard, RUNAWAY_TOOL_GUARD_FLAG, } from "./runaway-tool-guard.js";
30
- import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS, 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";
30
+ import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS, 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, keepAliveNextCheckAtMs, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
31
31
  import { providerSupportsSessionResume } from "./delivery.js";
32
+ import { isIsoDateTime } from "./types.js";
32
33
  import { applyDefaultQuotaHold, clearAccountQuotaAfterSuccessfulTurnBestEffort, readQuotaSignal, recordAccountQuota, } from "./quota.js";
33
34
  import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
34
35
  import { SILENCE_MS, appendWatcherEvent, classifyLaneExit, isPidAlive, missingExpectedEnvelopeFile, readLatestEnvelopeFile, readLatestEnvelopeRecord, watcherEventsPath, watcherOutLogPath, writeWatcherDecision, } from "./watcher.js";
@@ -2252,6 +2253,9 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
2252
2253
  }
2253
2254
  const key = keepAliveCompanyKey(company);
2254
2255
  if (keepAliveFlags.get(key) !== true || blockedFlags.get(key) !== true) {
2256
+ if (keepAliveFlags.get(key) === true && blockedFlags.get(key) !== true) {
2257
+ recordBlockedKeepAliveFlagOff(hqRoot, lane, summary, deps);
2258
+ }
2255
2259
  summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked" });
2256
2260
  return;
2257
2261
  }
@@ -2323,10 +2327,22 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
2323
2327
  summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_exhausted" });
2324
2328
  return;
2325
2329
  }
2326
- if (now.getTime() - terminalMs < LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS) {
2330
+ const blockedEnvelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
2331
+ const nextCheckValue = blockedEnvelope?.decision === "blocked" ? blockedEnvelope.next_check_at : undefined;
2332
+ const nextCheckValid = typeof nextCheckValue === "string" && isIsoDateTime(nextCheckValue) &&
2333
+ Date.parse(nextCheckValue) > terminalMs;
2334
+ const nextCheckMs = nextCheckValid
2335
+ ? keepAliveNextCheckAtMs(nextCheckValue, now.getTime(), terminalMs)
2336
+ : undefined;
2337
+ if (nextCheckMs !== undefined && nextCheckMs > now.getTime()) {
2338
+ appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
2339
+ return;
2340
+ }
2341
+ if (!nextCheckValid && now.getTime() - terminalMs < LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS) {
2327
2342
  appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
2328
2343
  return;
2329
2344
  }
2345
+ const resumeReason = nextCheckValid ? "next_check_at_due" : "backoff";
2330
2346
  const latest = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
2331
2347
  if (latest.version !== lane.version || latest.state !== "blocked" || latest.last_envelope?.decision !== "blocked" || latest.last_envelope.at !== terminalAt)
2332
2348
  return;
@@ -2389,6 +2405,7 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
2389
2405
  at: now.toISOString(),
2390
2406
  terminal_at: terminalAt,
2391
2407
  resume_queued: true,
2408
+ reason: resumeReason,
2392
2409
  }),
2393
2410
  }), deps.dryRun);
2394
2411
  if (queued.changed) {
@@ -2399,7 +2416,7 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
2399
2416
  at: now.toISOString(),
2400
2417
  attempt: (lane.keep_alive_resume_count ?? 0) + 1,
2401
2418
  resumed_from: "blocked",
2402
- reason: "backoff",
2419
+ reason: resumeReason,
2403
2420
  ...(accountSelection !== undefined
2404
2421
  ? { account_selection: keepAliveAccountSelectionEvidence(accountSelection) }
2405
2422
  : {}),
@@ -2407,6 +2424,32 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
2407
2424
  }
2408
2425
  }
2409
2426
  }
2427
+ function recordBlockedKeepAliveFlagOff(hqRoot, lane, summary, deps) {
2428
+ const terminalAt = lane.last_envelope?.at;
2429
+ if (deps.dryRun || lane.last_envelope?.decision !== "blocked" || terminalAt === undefined)
2430
+ return;
2431
+ const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
2432
+ const nextCheckAt = envelope?.decision === "blocked" ? envelope.next_check_at : undefined;
2433
+ const terminalMs = Date.parse(lane.last_envelope.at);
2434
+ if (typeof nextCheckAt !== "string" || !isIsoDateTime(nextCheckAt) || Date.parse(nextCheckAt) <= terminalMs)
2435
+ return;
2436
+ if (lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_skipped" && entry.terminal_at === terminalAt && entry.reason === "flag_off"))
2437
+ return;
2438
+ try {
2439
+ casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
2440
+ ...current,
2441
+ status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
2442
+ event: "keep_alive_blocked_skipped",
2443
+ at: deps.now().toISOString(),
2444
+ terminal_at: terminalAt,
2445
+ reason: "flag_off",
2446
+ }),
2447
+ }), false);
2448
+ }
2449
+ catch (err) {
2450
+ markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive skip record failed: ${err instanceof Error ? err.message : String(err)}` });
2451
+ }
2452
+ }
2410
2453
  function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, deps) {
2411
2454
  if (deps.dryRun || lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_grace" && entry.terminal_at === terminalAt && entry.resume_skipped_reason === reason))
2412
2455
  return;
@@ -445,10 +445,11 @@ export interface KrFreshnessState {
445
445
  stalled_fingerprint?: string;
446
446
  }
447
447
  export interface KeepAliveBlockedHistoryEntry {
448
- event: "keep_alive_blocked_grace" | "keep_alive_blocked_resumed" | "keep_alive_blocked_escalated";
448
+ event: "keep_alive_blocked_grace" | "keep_alive_blocked_resumed" | "keep_alive_blocked_escalated" | "keep_alive_blocked_skipped";
449
449
  at: string;
450
450
  terminal_at: string;
451
451
  resume_skipped_reason?: "open_question" | "blocked_grace";
452
+ reason?: "flag_off" | "backoff" | "next_check_at_due";
452
453
  resume_queued?: boolean;
453
454
  }
454
455
  export interface KeepAliveResumeHistoryEntry {
@@ -1,4 +1,4 @@
1
- export type ResidentPackagePath = "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon";
1
+ export type ResidentPackagePath = "agent-kit" | "agent-mcp" | "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon" | "mesh-daemon" | "lanes-watch" | "monitor-wait" | "monitor-run";
2
2
  /** Package imports reachable lazily from each resident command's runtime graph. */
3
3
  export declare const RESIDENT_PACKAGE_MODULES: Record<ResidentPackagePath, readonly string[]>;
4
4
  export declare class ResidentPackagePreloadError extends Error {
@@ -10,4 +10,11 @@ export declare class ResidentPackagePreloadError extends Error {
10
10
  export declare function reportResidentPackagePreloadFailure(error: unknown): void;
11
11
  /** Resolve every lazy package module before a resident command releases its lease. */
12
12
  export declare function preloadResidentPackageModules(residentPath: ResidentPackagePath, load?: () => Promise<unknown>): Promise<void>;
13
+ export interface ResidentCommandLeaseOptions {
14
+ preload?: () => Promise<void>;
15
+ reportFailure?: (error: unknown) => void;
16
+ release?: () => void;
17
+ }
18
+ /** Start a resident loop only after its declared lazy package imports are loaded. */
19
+ export declare function runResidentCommandWithPackageLease<T>(residentPath: ResidentPackagePath, run: () => Promise<T>, options?: ResidentCommandLeaseOptions): Promise<T>;
13
20
  //# sourceMappingURL=resident-package-preload.d.ts.map
@@ -1,5 +1,8 @@
1
+ import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload, } from "./package-use-lease.js";
1
2
  /** Package imports reachable lazily from each resident command's runtime graph. */
2
3
  export const RESIDENT_PACKAGE_MODULES = {
4
+ "agent-kit": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
5
+ "agent-mcp": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../commands/access.js", "open"],
3
6
  login: [
4
7
  "../lib/mesh/api.js",
5
8
  "../lib/work-context/config.js",
@@ -40,8 +43,31 @@ export const RESIDENT_PACKAGE_MODULES = {
40
43
  "../utils/staged-npm-install.js",
41
44
  "open",
42
45
  ],
46
+ "mesh-daemon": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
47
+ "lanes-watch": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
48
+ "monitor-wait": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
49
+ "monitor-run": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "open"],
43
50
  };
44
51
  const LOADERS = {
52
+ "agent-kit": [
53
+ async () => {
54
+ const { preloadSentrySdk } = await import("../sentry.js");
55
+ preloadSentrySdk();
56
+ },
57
+ () => import("../lib/plan-limit-nag.js"),
58
+ () => import("../utils/plan-gate-error.js"),
59
+ () => import("open"),
60
+ ],
61
+ "agent-mcp": [
62
+ async () => {
63
+ const { preloadSentrySdk } = await import("../sentry.js");
64
+ preloadSentrySdk();
65
+ },
66
+ () => import("../lib/plan-limit-nag.js"),
67
+ () => import("../utils/plan-gate-error.js"),
68
+ () => import("../commands/access.js"),
69
+ () => import("open"),
70
+ ],
45
71
  login: [
46
72
  () => import("../lib/mesh/api.js"),
47
73
  () => import("../lib/work-context/config.js"),
@@ -101,6 +127,43 @@ const LOADERS = {
101
127
  () => import("../utils/staged-npm-install.js"),
102
128
  () => import("open"),
103
129
  ],
130
+ "mesh-daemon": [
131
+ async () => {
132
+ const { preloadSentrySdk } = await import("../sentry.js");
133
+ preloadSentrySdk();
134
+ },
135
+ () => import("../lib/plan-limit-nag.js"),
136
+ () => import("../utils/plan-gate-error.js"),
137
+ () => import("open"),
138
+ ],
139
+ "lanes-watch": [
140
+ async () => {
141
+ const { preloadSentrySdk } = await import("../sentry.js");
142
+ preloadSentrySdk();
143
+ },
144
+ () => import("../lib/plan-limit-nag.js"),
145
+ () => import("../utils/plan-gate-error.js"),
146
+ () => import("open"),
147
+ ],
148
+ "monitor-wait": [
149
+ async () => {
150
+ const { preloadSentrySdk } = await import("../sentry.js");
151
+ preloadSentrySdk();
152
+ },
153
+ () => import("../lib/plan-limit-nag.js"),
154
+ () => import("../utils/plan-gate-error.js"),
155
+ () => import("open"),
156
+ ],
157
+ "monitor-run": [
158
+ () => import("../commands/lanes.js"),
159
+ () => import("../lib/plan-limit-nag.js"),
160
+ () => import("../utils/plan-gate-error.js"),
161
+ async () => {
162
+ const { preloadSentrySdk } = await import("../sentry.js");
163
+ preloadSentrySdk();
164
+ },
165
+ () => import("open"),
166
+ ],
104
167
  };
105
168
  export class ResidentPackagePreloadError extends Error {
106
169
  moduleSpecifier;
@@ -141,4 +204,9 @@ export async function preloadResidentPackageModules(residentPath, load) {
141
204
  }
142
205
  }));
143
206
  }
207
+ /** Start a resident loop only after its declared lazy package imports are loaded. */
208
+ export async function runResidentCommandWithPackageLease(residentPath, run, options = {}) {
209
+ await releasePackageUseLeaseAfterResidentPreload(options.preload ?? (() => preloadResidentPackageModules(residentPath)), options.reportFailure ?? reportResidentPackagePreloadFailure, options.release ?? releaseCurrentPackageUseLease);
210
+ return run();
211
+ }
144
212
  //# sourceMappingURL=resident-package-preload.js.map
@@ -57,6 +57,8 @@ export type RunBundledScriptOptions = {
57
57
  captureOutput?: boolean;
58
58
  /** Bytes supplied on stdin. With no value, stdin is inherited as before. */
59
59
  stdin?: string | Buffer;
60
+ /** Optional child-process deadline for callers that need bounded script execution. */
61
+ timeoutMs?: number;
60
62
  };
61
63
  export type RunBundledScriptResult = {
62
64
  /** The child's exit code, or 128+signal if it was terminated by one. */
@@ -189,6 +189,7 @@ export function runBundledScript(opts) {
189
189
  cwd: opts.cwd,
190
190
  env,
191
191
  stdio,
192
+ ...(opts.timeoutMs === undefined ? {} : { timeout: opts.timeoutMs }),
192
193
  ...(opts.captureOutput ? { maxBuffer: Number.MAX_SAFE_INTEGER } : {}),
193
194
  });
194
195
  }
@@ -47,4 +47,6 @@ export declare function isWindowsBatchFile(command: string, platform?: NodeJS.Pl
47
47
  * passed through untouched.
48
48
  */
49
49
  export declare function planCommandSpawn(command: string, args: readonly string[], platform?: NodeJS.Platform): SpawnPlan;
50
+ /** Spawn a resolved executable, routing only Windows batch shims through the escaped cmd.exe plan. */
51
+ export declare function planResolvedCommandSpawn(command: string, args: readonly string[], env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): SpawnPlan;
50
52
  //# sourceMappingURL=windows-spawn.d.ts.map
@@ -108,4 +108,10 @@ export function planCommandSpawn(command, args, platform = process.platform) {
108
108
  return buildSpawnPlan(command, args, platform);
109
109
  return { cmd: command, args: [...args], shell: false };
110
110
  }
111
+ /** Spawn a resolved executable, routing only Windows batch shims through the escaped cmd.exe plan. */
112
+ export function planResolvedCommandSpawn(command, args, env = process.env, platform = process.platform) {
113
+ if (isWindowsBatchFile(command, platform))
114
+ return buildCmdExeSpawnPlan(command, args, env, platform);
115
+ return { cmd: command, args: [...args], shell: false };
116
+ }
111
117
  //# sourceMappingURL=windows-spawn.js.map
@@ -73,7 +73,9 @@ the named-pipe transport without Unix mode or owner fields. Use `hq doctor
73
73
  --json` for the same fields as structured details. The hqd health operation is
74
74
  read-only and available while the runtime flag is off; it reports only the
75
75
  current directory's binding and never returns authentication tokens. A Unix
76
- socket that cannot be verified as user-only produces a warning. If an older
76
+ socket that cannot be verified as user-only produces a warning. When a Unix
77
+ socket is unreachable, doctor recommends installing the daemon service if it is
78
+ absent or restarting it if it is installed. If an older
77
79
  resident daemon does not support the health operation, doctor falls back to
78
80
  `ping` for reachability, version and uptime, then warns that a restart is needed
79
81
  for full diagnostics.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.342.12",
3
+ "version": "5.342.14",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -59,7 +59,7 @@
59
59
  "js-yaml": "^4.1.0",
60
60
  "mqtt": "5.15.2",
61
61
  "open": "^10.1.0",
62
- "semver": "^7.6.3",
62
+ "semver": "^7.8.5",
63
63
  "simple-git": "^3.27.0",
64
64
  "smol-toml": "1.6.1",
65
65
  "varlock": "1.21.1"
@@ -69,11 +69,11 @@
69
69
  "@types/better-sqlite3": "^7.6.13",
70
70
  "@types/js-yaml": "^4.0.9",
71
71
  "@types/node": "^22.0.0",
72
- "@types/semver": "^7.5.8",
72
+ "@types/semver": "^7.8.0",
73
73
  "@vitest/coverage-v8": "4.1.6",
74
74
  "aedes": "1.1.2",
75
75
  "aws-sdk-client-mock": "^4.1.0",
76
- "esbuild": "0.27.0",
76
+ "esbuild": "0.28.2",
77
77
  "eslint": "^10.5.0",
78
78
  "husky": "^9.1.7",
79
79
  "lint-staged": "^15.5.2",
@@ -1,58 +0,0 @@
1
- /**
2
- * Pluggable remote vault DB engine interface (vault-databases US-007).
3
- *
4
- * Default shipped adapter id: aurora-dsql.
5
- * This module is interface + registry only — no production AWS provision here.
6
- */
7
- /** Connection material written only to secrets stores — never CLI stdout. */
8
- export interface RemoteConnectionSecretPayload {
9
- /** Opaque secret body (e.g. JSON with host/user/password or IAM token recipe). */
10
- payload: Record<string, unknown>;
11
- /** Suggested vault/Secrets Manager key suffix (namespaced by company elsewhere). */
12
- secretNameHint: string;
13
- }
14
- export interface RemoteProvisionRequest {
15
- companyUid: string;
16
- companySlug: string;
17
- region?: string;
18
- /** Engine-specific options (cluster params, etc.). */
19
- options?: Record<string, unknown>;
20
- }
21
- export interface RemoteProvisionResult {
22
- engineId: string;
23
- resourceArn: string;
24
- region: string;
25
- status: "ready" | "provisioning" | "failed" | "existing";
26
- /** Secret payload to store — callers must not print. */
27
- connectionSecret: RemoteConnectionSecretPayload;
28
- }
29
- export interface RemoteHealth {
30
- engineId: string;
31
- resourceArn: string;
32
- healthy: boolean;
33
- status: string;
34
- checkedAt: string;
35
- }
36
- /**
37
- * Remote Postgres-class engine adapter.
38
- * Implementations live in control plane (hq-pro) for real AWS; CLI may mock.
39
- */
40
- export interface RemoteDbEngine {
41
- readonly id: string;
42
- provision(req: RemoteProvisionRequest): Promise<RemoteProvisionResult>;
43
- deprovision(companyUid: string, resourceArn: string): Promise<void>;
44
- connectionSecretPayload(companyUid: string, resourceArn: string): Promise<RemoteConnectionSecretPayload>;
45
- healthCheck(resourceArn: string): Promise<RemoteHealth>;
46
- }
47
- /** Default engine selection (PRD decision). */
48
- export declare const DEFAULT_REMOTE_ENGINE_ID = "aurora-dsql";
49
- export declare function registerRemoteEngine(engine: RemoteDbEngine): void;
50
- export declare function getRemoteEngine(id?: string): RemoteDbEngine;
51
- export declare function listRemoteEngineIds(): string[];
52
- /** Clear registry (tests only). */
53
- export declare function _resetRemoteEngineRegistryForTests(): void;
54
- /**
55
- * In-memory mock engine for unit tests — no AWS.
56
- */
57
- export declare function createMockRemoteEngine(id?: string): RemoteDbEngine;
58
- //# sourceMappingURL=remote-engine.d.ts.map
@@ -1,87 +0,0 @@
1
- /**
2
- * Pluggable remote vault DB engine interface (vault-databases US-007).
3
- *
4
- * Default shipped adapter id: aurora-dsql.
5
- * This module is interface + registry only — no production AWS provision here.
6
- */
7
- /** Default engine selection (PRD decision). */
8
- export const DEFAULT_REMOTE_ENGINE_ID = "aurora-dsql";
9
- const registry = new Map();
10
- export function registerRemoteEngine(engine) {
11
- registry.set(engine.id, engine);
12
- }
13
- export function getRemoteEngine(id = DEFAULT_REMOTE_ENGINE_ID) {
14
- const engine = registry.get(id);
15
- if (!engine) {
16
- throw new Error(`remote DB engine not registered: ${id} (available: ${[...registry.keys()].join(", ") || "none"})`);
17
- }
18
- return engine;
19
- }
20
- export function listRemoteEngineIds() {
21
- return [...registry.keys()];
22
- }
23
- /** Clear registry (tests only). */
24
- export function _resetRemoteEngineRegistryForTests() {
25
- registry.clear();
26
- }
27
- /**
28
- * In-memory mock engine for unit tests — no AWS.
29
- */
30
- export function createMockRemoteEngine(id = "mock") {
31
- const resources = new Map();
32
- return {
33
- id,
34
- async provision(req) {
35
- const existing = [...resources.entries()].find(([, v]) => v.companyUid === req.companyUid);
36
- if (existing) {
37
- const [arn, meta] = existing;
38
- return {
39
- engineId: id,
40
- resourceArn: arn,
41
- region: meta.region,
42
- status: "existing",
43
- connectionSecret: {
44
- payload: { mock: true, companyUid: req.companyUid },
45
- secretNameHint: `db/remote/${req.companySlug}`,
46
- },
47
- };
48
- }
49
- const arn = `arn:mock:db:${req.companyUid}`;
50
- const region = req.region ?? "us-east-1";
51
- resources.set(arn, { companyUid: req.companyUid, region });
52
- return {
53
- engineId: id,
54
- resourceArn: arn,
55
- region,
56
- status: "ready",
57
- connectionSecret: {
58
- payload: { mock: true, companyUid: req.companyUid },
59
- secretNameHint: `db/remote/${req.companySlug}`,
60
- },
61
- };
62
- },
63
- async deprovision(companyUid, resourceArn) {
64
- const meta = resources.get(resourceArn);
65
- if (meta && meta.companyUid === companyUid) {
66
- resources.delete(resourceArn);
67
- }
68
- },
69
- async connectionSecretPayload(companyUid, resourceArn) {
70
- return {
71
- payload: { mock: true, companyUid, resourceArn },
72
- secretNameHint: `db/remote/${companyUid}`,
73
- };
74
- },
75
- async healthCheck(resourceArn) {
76
- const ok = resources.has(resourceArn);
77
- return {
78
- engineId: id,
79
- resourceArn,
80
- healthy: ok,
81
- status: ok ? "ready" : "missing",
82
- checkedAt: new Date().toISOString(),
83
- };
84
- },
85
- };
86
- }
87
- //# sourceMappingURL=remote-engine.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * Remote SQL helper with injected credentials and executor (US-010).
3
- *
4
- * Connection string never appears in argv, logs, or stdout.
5
- * The CLI command does not currently wire this helper to a remote executor.
6
- */
7
- export interface RemoteSqlOptions {
8
- company: string;
9
- sql: string;
10
- /** Injected secret material — never log. */
11
- getConnectionConfig: () => Promise<Record<string, unknown>>;
12
- /** Execute against remote; tests inject mock. */
13
- execute?: (config: Record<string, unknown>, sql: string) => Promise<Record<string, unknown>[]>;
14
- }
15
- export interface RemoteSqlResult {
16
- rows: Record<string, unknown>[];
17
- }
18
- /**
19
- * Run SQL on remote tier using vault-bound credentials.
20
- */
21
- export declare function runRemoteSql(opts: RemoteSqlOptions): Promise<RemoteSqlResult>;
22
- //# sourceMappingURL=remote-sql.d.ts.map