@indigoai-us/hq-cli 5.295.0 → 5.297.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 (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/command-catalog.generated.d.ts +58 -0
  3. package/dist/command-catalog.generated.js +73 -0
  4. package/dist/commands/core-job-native.d.ts +7 -0
  5. package/dist/commands/core-job-native.js +20 -0
  6. package/dist/commands/core-job-probe-native.d.ts +45 -0
  7. package/dist/commands/core-job-probe-native.js +564 -0
  8. package/dist/commands/core-job-remote-launcher.d.ts +7 -0
  9. package/dist/commands/core-job-remote-launcher.js +51 -0
  10. package/dist/commands/core-job-run-native.d.ts +47 -0
  11. package/dist/commands/core-job-run-native.js +1215 -0
  12. package/dist/commands/core.js +1 -1
  13. package/dist/commands/lanes-message.js +1 -0
  14. package/dist/commands/lanes-resume.js +1 -0
  15. package/dist/commands/lanes-stories.d.ts +1 -0
  16. package/dist/commands/lanes-stories.js +40 -1
  17. package/dist/commands/lanes.js +20 -2
  18. package/dist/commands/mesh.js +3 -1
  19. package/dist/commands/run.js +5 -0
  20. package/dist/lib/daemon/handoff.d.ts +1 -0
  21. package/dist/lib/daemon/handoff.js +18 -2
  22. package/dist/lib/daemon/legacy.d.ts +7 -0
  23. package/dist/lib/daemon/legacy.js +6 -0
  24. package/dist/lib/daemon/lock.js +1 -1
  25. package/dist/lib/daemon/run.js +57 -2
  26. package/dist/lib/daemon/supervisor.d.ts +2 -0
  27. package/dist/lib/daemon/supervisor.js +26 -0
  28. package/dist/lib/doctor/checks/work-context.js +5 -1
  29. package/dist/lib/lanes/mesh-events.js +3 -0
  30. package/dist/lib/lanes/reconcile.js +23 -19
  31. package/dist/lib/lanes/spawn.js +39 -0
  32. package/dist/lib/lanes/store.js +32 -0
  33. package/dist/lib/lanes/stories.d.ts +7 -1
  34. package/dist/lib/lanes/stories.js +16 -0
  35. package/dist/lib/lanes/types.d.ts +12 -0
  36. package/dist/lib/lanes/types.js +1 -1
  37. package/dist/lib/lanes/watcher.js +20 -22
  38. package/dist/lib/mesh/live/daemon/doctor.d.ts +2 -0
  39. package/dist/lib/mesh/live/daemon/doctor.js +10 -0
  40. package/dist/lib/work-context/outbox-quarantine-status-flag.d.ts +4 -0
  41. package/dist/lib/work-context/outbox-quarantine-status-flag.js +7 -0
  42. package/dist/lib/work-context/outbox.d.ts +1 -0
  43. package/dist/lib/work-context/outbox.js +7 -1
  44. package/dist/run/env-graph-guard.d.ts +2 -2
  45. package/dist/run/env-graph-guard.js +5 -2
  46. package/dist/run/hq-plugin.js +6 -5
  47. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import type { LaneRecord, LaneStory } from "./types.js";
1
+ import type { LaneRecord, LaneStory, LaneStoryStatus } from "./types.js";
2
2
  /** Board-facing states for the stories a lane currently carries. */
3
3
  export declare const ACTIVE_LANE_STORY_STATUSES: readonly ["in_progress", "blocked", "review"];
4
4
  export declare function storyKey(projectId: string, storyId: string): string;
@@ -14,4 +14,10 @@ export declare function storyWarning(lane: LaneRecord): string | undefined;
14
14
  export declare function withCurrentStory(lane: LaneRecord, projectId: string, storyId: string): LaneRecord;
15
15
  export declare function withLaneStories(lane: LaneRecord, stories: LaneStory[]): LaneRecord;
16
16
  export declare function storyStatusForEnvelope(status: string): "in_progress" | "review";
17
+ /**
18
+ * Resolve the story status requested by a terminal envelope. Already-final
19
+ * carried stories are immutable through the envelope path; senior verification
20
+ * and explicit story commands own those transitions.
21
+ */
22
+ export declare function terminalEnvelopeStoryStatus(lane: LaneRecord, decision: "done" | "blocked" | "ask", envelopeStatus?: string): LaneStoryStatus | undefined;
17
23
  //# sourceMappingURL=stories.d.ts.map
@@ -73,4 +73,20 @@ export function storyStatusForEnvelope(status) {
73
73
  return "review";
74
74
  return "in_progress";
75
75
  }
76
+ /**
77
+ * Resolve the story status requested by a terminal envelope. Already-final
78
+ * carried stories are immutable through the envelope path; senior verification
79
+ * and explicit story commands own those transitions.
80
+ */
81
+ export function terminalEnvelopeStoryStatus(lane, decision, envelopeStatus = "in_progress") {
82
+ const primary = laneStory(lane, lane.project_id, lane.story_id);
83
+ if (primary && (String(primary.status) === "done" || String(primary.status) === "dropped")) {
84
+ return undefined;
85
+ }
86
+ if (decision === "blocked")
87
+ return "blocked";
88
+ if (decision === "done")
89
+ return "review";
90
+ return storyStatusForEnvelope(envelopeStatus);
91
+ }
76
92
  //# sourceMappingURL=stories.js.map
@@ -412,6 +412,14 @@ export interface KeepAliveResumeHistoryEntry {
412
412
  resumed_from: "done" | "interrupted" | "blocked";
413
413
  account: string | null;
414
414
  }
415
+ /** Bounded audit entry for every lane worker start after its first start. */
416
+ export interface LaneResumeHistoryEntry {
417
+ at: string;
418
+ /** Stable trigger class; path-specific starts may use their path name. */
419
+ trigger: string;
420
+ /** HQ lane id for an agent caller, or `user` for an explicit human CLI action. */
421
+ caller?: string;
422
+ }
415
423
  export interface KrFreshnessHistoryEntry {
416
424
  event: "kr_stalled";
417
425
  turn_id: string;
@@ -659,6 +667,8 @@ export interface LaneRecord {
659
667
  provider_home_at_spawn?: string;
660
668
  /** Set at spawn when this run continued a recorded session instead of starting fresh. */
661
669
  last_resume?: LaneResumeRecord;
670
+ /** Last 50 lane worker starts after the initial start, including actor provenance. */
671
+ resume_history?: LaneResumeHistoryEntry[];
662
672
  /** Cache counters from the last exited run's result. Nothing else from the payload. */
663
673
  last_run_usage?: LaneRunUsage;
664
674
  /** Completion time paired with last_run_usage; written by the watcher. */
@@ -723,6 +733,8 @@ export interface PendingResume {
723
733
  message_id?: string;
724
734
  /** The text is already framed as untrusted monitor data, not an operator instruction. */
725
735
  monitor_event?: true;
736
+ /** Caller identity captured when a deferred start was requested, when known. */
737
+ caller?: string;
726
738
  /** False when the command passed --no-compact; the deferred spawn honours it. */
727
739
  compact?: boolean;
728
740
  at: string;
@@ -233,7 +233,7 @@ export const LANE_FIELD_WRITERS = {
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"],
236
- provider_session_id: ["watcher", "command", "spawn"], provider_session_id_absent: ["watcher", "command"], provider_home_at_spawn: ["spawn"], last_resume: ["spawn"], last_run_usage: ["watcher"], last_run_usage_at: ["watcher"], envelope_repair: ["watcher", "reconcile"], envelope_validation_errors: ["watcher"], last_progress_event_at: ["watcher"],
236
+ provider_session_id: ["watcher", "command", "spawn"], provider_session_id_absent: ["watcher", "command"], provider_home_at_spawn: ["spawn"], last_resume: ["spawn"], resume_history: ["spawn", "watcher"], last_run_usage: ["watcher"], last_run_usage_at: ["watcher"], envelope_repair: ["watcher", "reconcile"], envelope_validation_errors: ["watcher"], last_progress_event_at: ["watcher"],
237
237
  };
238
238
  function isPlainObject(value) {
239
239
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -36,7 +36,7 @@ import * as path from "node:path";
36
36
  import { LanesError } from "./errors.js";
37
37
  import { questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
38
38
  import { isTerminalLaneState, ENVELOPE_INVALID_REASON, parseLaneEnvelope, STORY_STATUSES, LANE_PROVIDERS, } from "./types.js";
39
- import { laneStories, storyStatusForEnvelope } from "./stories.js";
39
+ import { laneStories, terminalEnvelopeStoryStatus } from "./stories.js";
40
40
  import { appendCappedHistory, listQuestionIds, readLane, readQuestion, retryUpdateLane, withLaneHireLocks } from "./store.js";
41
41
  import { enforceRegistryApprovalGate } from "./approval-gate.js";
42
42
  import { readWorkerRunResult } from "./delivery.js";
@@ -423,27 +423,25 @@ export function applyTerminalToLaneWithResult(hqRoot, laneId, classification, op
423
423
  artifacts: envelope.artifacts,
424
424
  ...(envelope.blocked_reason !== undefined ? { blocked_reason: envelope.blocked_reason } : {}),
425
425
  };
426
- next.story_status = envelope.story_status;
427
- next.story_complete = envelope.story_complete;
428
- next.story_complete_reason = envelope.story_complete_reason;
429
- const currentProject = current.project_id;
430
- const currentStory = current.story_id;
431
- const storyStatus = envelope.decision === "blocked"
432
- ? "blocked"
433
- : envelope.decision === "done"
434
- ? "review"
435
- : storyStatusForEnvelope(envelope.story_status);
436
- const updatedStories = laneStories(current).map((story) => story.project_id === currentProject && story.story_id === currentStory
437
- ? {
438
- ...story,
439
- status: storyStatus,
440
- updated_at: at,
441
- ...(envelope.decision === "blocked"
442
- ? { blocked_reason: envelope.blocked_reason ?? "" }
443
- : { blocked_reason: undefined }),
444
- }
445
- : story);
446
- next.stories = updatedStories;
426
+ const storyStatus = terminalEnvelopeStoryStatus(current, envelope.decision, envelope.story_status);
427
+ if (storyStatus !== undefined) {
428
+ next.story_status = envelope.story_status;
429
+ next.story_complete = envelope.story_complete;
430
+ next.story_complete_reason = envelope.story_complete_reason;
431
+ const currentProject = current.project_id;
432
+ const currentStory = current.story_id;
433
+ const updatedStories = laneStories(current).map((story) => story.project_id === currentProject && story.story_id === currentStory
434
+ ? {
435
+ ...story,
436
+ status: storyStatus,
437
+ updated_at: at,
438
+ ...(envelope.decision === "blocked"
439
+ ? { blocked_reason: envelope.blocked_reason ?? "" }
440
+ : { blocked_reason: undefined }),
441
+ }
442
+ : story);
443
+ next.stories = updatedStories;
444
+ }
447
445
  }
448
446
  return next;
449
447
  });
@@ -25,6 +25,8 @@ export interface DaemonDoctorReport {
25
25
  /** Events in any local queue still marked no-company / NEEDS_COMPANY / NONE. */
26
26
  unattributedEvents: UnattributedEventCounts;
27
27
  outboxDepth: number;
28
+ outboxQuarantined?: number;
29
+ outboxLastQuarantineReason?: string;
28
30
  lastFlushAt?: string;
29
31
  lastFlushResult?: DaemonStateFile["lastFlushResult"];
30
32
  unhealthy: boolean;
@@ -9,6 +9,7 @@ import { describeCognitoTokenSource, formatCognitoSubject, loadMachineCachedToke
9
9
  import { listOutboxOperations, outboxStats, } from "../../../work-context/outbox.js";
10
10
  import { readSessionState } from "../../../work-context/state.js";
11
11
  import { workContextRoot } from "../../../work-context/paths.js";
12
+ import { outboxQuarantineStatusEnabled } from "../../../work-context/outbox-quarantine-status-flag.js";
12
13
  import { formatDroppedPresenceCompany } from "./credentials.js";
13
14
  import { countJsonlLines, countUnattributedEvents, } from "../spool.js";
14
15
  import { workMeshDeadLetterPath, workMeshHeldPath, workMeshRoot, workMeshSpoolPath, } from "../paths.js";
@@ -258,6 +259,12 @@ export function collectDaemonDoctor(deps = {}) {
258
259
  deadLetterCount,
259
260
  unattributedEvents,
260
261
  outboxDepth: outbox.depth,
262
+ ...(outbox.quarantined > 0 && outboxQuarantineStatusEnabled()
263
+ ? {
264
+ outboxQuarantined: outbox.quarantined,
265
+ outboxLastQuarantineReason: outbox.lastQuarantineReason ?? undefined,
266
+ }
267
+ : {}),
261
268
  lastFlushAt: state?.lastFlushAt,
262
269
  lastFlushResult: state?.lastFlushResult,
263
270
  unhealthy: unhealthyReasons.length > 0,
@@ -302,6 +309,9 @@ export function formatDaemonDoctor(report) {
302
309
  // receive-only (direct) mode.
303
310
  lines.push(`spool depth: ${report.spoolDepth}`, `held: ${report.heldCount}${report.heldCount > 0 ? " (needs_company; not sent as NONE)" : ""}`, `held with no local evidence: ${report.heldWithNoLocalEvidence}`, `outbox depth: ${report.outboxDepth}`);
304
311
  }
312
+ if (report.outboxQuarantined !== undefined) {
313
+ lines.push(`outbox quarantined: ${report.outboxQuarantined} (last reason=${report.outboxLastQuarantineReason ?? "unknown"})`);
314
+ }
305
315
  if (report.emitMode !== "legacy" && report.heldCount > 0) {
306
316
  lines.push(`held: ${report.heldCount} (needs_company; not sent as NONE)`);
307
317
  }
@@ -0,0 +1,4 @@
1
+ export declare const OUTBOX_QUARANTINE_STATUS_FLAG = "agents.work-context-outbox-quarantine-status";
2
+ export declare const OUTBOX_QUARANTINE_STATUS_FALLBACK = false;
3
+ export declare function outboxQuarantineStatusEnabled(): boolean;
4
+ //# sourceMappingURL=outbox-quarantine-status-flag.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { resolveProcessFlagGate } from "../flag-registry.js";
2
+ export const OUTBOX_QUARANTINE_STATUS_FLAG = "agents.work-context-outbox-quarantine-status";
3
+ export const OUTBOX_QUARANTINE_STATUS_FALLBACK = false;
4
+ export function outboxQuarantineStatusEnabled() {
5
+ return resolveProcessFlagGate(OUTBOX_QUARANTINE_STATUS_FLAG, { fallback: OUTBOX_QUARANTINE_STATUS_FALLBACK }, () => OUTBOX_QUARANTINE_STATUS_FALLBACK);
6
+ }
7
+ //# sourceMappingURL=outbox-quarantine-status-flag.js.map
@@ -118,6 +118,7 @@ export interface OutboxStats {
118
118
  quarantined: number;
119
119
  queued: number;
120
120
  acked: number;
121
+ lastQuarantineReason: string | null;
121
122
  }
122
123
  export declare function outboxStats(root: string): OutboxStats;
123
124
  export type DeliverFn = (op: OutboxOperation) => Promise<{
@@ -458,11 +458,17 @@ export function listOutboxOperations(root, deps = {}) {
458
458
  }
459
459
  export function outboxStats(root) {
460
460
  const ops = listOutboxOperations(root);
461
+ const quarantinedOps = ops.filter((o) => o.delivery === "quarantined");
462
+ const latestQuarantine = quarantinedOps.reduce((latest, op) => !latest || op.updatedAt > latest.updatedAt ||
463
+ (op.updatedAt === latest.updatedAt && op.operationId > latest.operationId)
464
+ ? op
465
+ : latest, null);
461
466
  return {
462
467
  depth: ops.filter((o) => o.delivery === "queued").length,
463
- quarantined: ops.filter((o) => o.delivery === "quarantined").length,
468
+ quarantined: quarantinedOps.length,
464
469
  queued: ops.filter((o) => o.delivery === "queued").length,
465
470
  acked: ops.filter((o) => o.delivery === "acked").length,
471
+ lastQuarantineReason: latestQuarantine?.lastErrorCode ?? null,
466
472
  };
467
473
  }
468
474
  function isOutboxDue(op, nowIso) {
@@ -1,5 +1,5 @@
1
1
  /** The varlock version hq-cli is pinned to (package.json `dependencies`). */
2
- export declare const VARLOCK_PINNED_VERSION = "1.0.0";
2
+ export declare const VARLOCK_PINNED_VERSION = "1.21.1";
3
3
  /** A schema/plugin source varlock could not finish loading. */
4
4
  interface GuardDataSource {
5
5
  readonly isValid: boolean;
@@ -68,7 +68,7 @@ export declare function assertEnvGraphLoaded(graph: LoadedEnvGraph): void;
68
68
  * rejection into a thrown EnvResolutionError instead of hanging on the promise
69
69
  * that never settles.
70
70
  *
71
- * varlock@1.0.0's resolveEnvValues dispatches its per-item resolver un-awaited
71
+ * varlock's resolveEnvValues dispatches its per-item resolver un-awaited
72
72
  * and with a dead `_reject`, so a throw inside it becomes a process-level
73
73
  * unhandled rejection AND leaves the returned promise permanently pending. We
74
74
  * race the resolve against a scoped `unhandledRejection` listener: whichever
@@ -34,7 +34,7 @@
34
34
  // actionable `hq: …` line WITHOUT a Sentry capture (see isVarlockEnvError).
35
35
  import { claimRejection, releaseRejection, } from "../unhandled-rejection-boundary.js";
36
36
  /** The varlock version hq-cli is pinned to (package.json `dependencies`). */
37
- export const VARLOCK_PINNED_VERSION = "1.0.0";
37
+ export const VARLOCK_PINNED_VERSION = "1.21.1";
38
38
  /**
39
39
  * A schema/entry file could not be loaded, so the env graph never finished
40
40
  * processing. Carries an actionable, file-naming message; classified by
@@ -114,7 +114,7 @@ export function assertEnvGraphLoaded(graph) {
114
114
  * rejection into a thrown EnvResolutionError instead of hanging on the promise
115
115
  * that never settles.
116
116
  *
117
- * varlock@1.0.0's resolveEnvValues dispatches its per-item resolver un-awaited
117
+ * varlock's resolveEnvValues dispatches its per-item resolver un-awaited
118
118
  * and with a dead `_reject`, so a throw inside it becomes a process-level
119
119
  * unhandled rejection AND leaves the returned promise permanently pending. We
120
120
  * race the resolve against a scoped `unhandledRejection` listener: whichever
@@ -143,6 +143,9 @@ export async function resolveEnvValuesOrThrow(graph) {
143
143
  });
144
144
  process.prependOnceListener("unhandledRejection", onRejection);
145
145
  try {
146
+ // Preserve current varlock's direct rejection unchanged. The race only
147
+ // turns a floated rejection (the 1.0.0 behavior) into a typed error rather
148
+ // than leaving the caller waiting on a promise that never settles.
146
149
  await Promise.race([graph.resolveEnvValues(), rejection]);
147
150
  }
148
151
  finally {
@@ -23,8 +23,9 @@ export function installHqPlugin(graph /* EnvGraph */, opts) {
23
23
  errorsByName: new Map(),
24
24
  loadedSecretsByName: new Map(),
25
25
  };
26
- // varlock@1.0.0's plugin-lib.js omits the Resolver export (d.ts/JS mismatch);
27
- // extract it at runtime from any already-registered built-in resolver's prototype.
26
+ // In varlock@1.21.1, Resolver remains declared in plugin-lib's .d.mts but
27
+ // is not exported from its runtime module. Derive the runtime base class from
28
+ // a built-in resolver registered on the graph; the shape test pins this API.
28
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
30
  let RuntimeResolver;
30
31
  try {
@@ -39,8 +40,8 @@ export function installHqPlugin(graph /* EnvGraph */, opts) {
39
40
  RuntimeResolver = proto;
40
41
  }
41
42
  catch (e) {
42
- throw new Error('varlock Resolver base class extraction failed — the varlock@1.0.0 d.ts/JS mismatch ' +
43
- 'may have been resolved; switch to `import { Resolver } from "varlock/plugin-lib"`. ' +
43
+ throw new Error('varlock Resolver base class extraction failed — varlock@1.21.1 still declares ' +
44
+ 'Resolver in plugin-lib types without exporting it at runtime. ' +
44
45
  `Underlying: ${e instanceof Error ? e.message : String(e)}`);
45
46
  }
46
47
  // HqResolver is declared INSIDE installHqPlugin so its static def.resolve
@@ -96,7 +97,7 @@ export function installHqPlugin(graph /* EnvGraph */, opts) {
96
97
  }
97
98
  }
98
99
  }
99
- // varlock@1.0.0's env-spec parser rejects dots in decorator names ([a-zA-Z0-9_] only),
100
+ // Varlock's env-spec parser rejects dots in decorator names ([a-zA-Z0-9_] only),
100
101
  // so `@hq.company` is not valid syntax. We register as `@hqCompany` (camelCase) instead.
101
102
  // Schema files must use `# @hqCompany("slug")` followed by a blank line so the parser
102
103
  // treats it as a file-level root decorator rather than an item decorator for the next var.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.295.0",
3
+ "version": "5.297.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -58,7 +58,7 @@
58
58
  "semver": "^7.6.3",
59
59
  "simple-git": "^3.27.0",
60
60
  "smol-toml": "1.6.1",
61
- "varlock": "1.0.0"
61
+ "varlock": "1.21.1"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@eslint/js": "^10.0.1",