@indigoai-us/hq-cli 5.131.0 → 5.133.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 (66) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/command-catalog.generated.d.ts +12 -0
  3. package/dist/command-catalog.generated.js +16 -0
  4. package/dist/commands/lanes.d.ts +15 -4
  5. package/dist/commands/lanes.js +478 -48
  6. package/dist/commands/mesh.js +24 -2
  7. package/dist/commands/update.js +1 -1
  8. package/dist/lib/bot/paths.d.ts +7 -5
  9. package/dist/lib/bot/paths.js +8 -1
  10. package/dist/lib/bot/prompt.d.ts +0 -2
  11. package/dist/lib/bot/prompt.js +5 -11
  12. package/dist/lib/bot/room-policy.d.ts +1 -24
  13. package/dist/lib/bot/room-policy.js +2 -35
  14. package/dist/lib/bot/run.d.ts +7 -8
  15. package/dist/lib/bot/run.js +21 -33
  16. package/dist/lib/bot/session.d.ts +1 -1
  17. package/dist/lib/bot/session.js +1 -1
  18. package/dist/lib/lanes/accounts.d.ts +6 -0
  19. package/dist/lib/lanes/accounts.js +2 -1
  20. package/dist/lib/lanes/config.d.ts +4 -0
  21. package/dist/lib/lanes/config.js +9 -0
  22. package/dist/lib/lanes/constants.d.ts +3 -0
  23. package/dist/lib/lanes/constants.js +3 -0
  24. package/dist/lib/lanes/index.d.ts +11 -3
  25. package/dist/lib/lanes/index.js +6 -1
  26. package/dist/lib/lanes/mesh-events.js +4 -0
  27. package/dist/lib/lanes/models.d.ts +78 -0
  28. package/dist/lib/lanes/models.js +205 -0
  29. package/dist/lib/lanes/move-account.d.ts +56 -0
  30. package/dist/lib/lanes/move-account.js +193 -0
  31. package/dist/lib/lanes/provider-rejection.d.ts +86 -0
  32. package/dist/lib/lanes/provider-rejection.js +258 -0
  33. package/dist/lib/lanes/quota.d.ts +23 -3
  34. package/dist/lib/lanes/quota.js +88 -14
  35. package/dist/lib/lanes/reconcile-install.d.ts +24 -3
  36. package/dist/lib/lanes/reconcile-install.js +48 -12
  37. package/dist/lib/lanes/reconcile.d.ts +5 -2
  38. package/dist/lib/lanes/reconcile.js +135 -13
  39. package/dist/lib/lanes/spawn.js +26 -5
  40. package/dist/lib/lanes/store.js +10 -0
  41. package/dist/lib/lanes/types.d.ts +51 -2
  42. package/dist/lib/lanes/types.js +5 -3
  43. package/dist/lib/lanes/watcher.d.ts +27 -0
  44. package/dist/lib/lanes/watcher.js +68 -5
  45. package/dist/lib/mesh/live/backfill-held.d.ts +4 -3
  46. package/dist/lib/mesh/live/backfill-held.js +37 -14
  47. package/dist/lib/mesh/live/daemon/doctor.d.ts +6 -0
  48. package/dist/lib/mesh/live/daemon/doctor.js +16 -2
  49. package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
  50. package/dist/lib/mesh/live/daemon/install.js +100 -7
  51. package/dist/lib/mesh/live/daemon/legacy-listen.d.ts +43 -0
  52. package/dist/lib/mesh/live/daemon/legacy-listen.js +89 -0
  53. package/dist/lib/mesh/live/daemon/live-cache.d.ts +26 -0
  54. package/dist/lib/mesh/live/daemon/live-cache.js +116 -0
  55. package/dist/lib/mesh/live/daemon/presence.d.ts +7 -3
  56. package/dist/lib/mesh/live/daemon/presence.js +10 -9
  57. package/dist/lib/mesh/live/daemon/run.d.ts +18 -0
  58. package/dist/lib/mesh/live/daemon/run.js +65 -6
  59. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +31 -0
  60. package/dist/lib/mesh/live/daemon/work-subscribe.js +124 -0
  61. package/dist/lib/mesh/live/emit-evidence.d.ts +19 -0
  62. package/dist/lib/mesh/live/emit-evidence.js +20 -2
  63. package/dist/lib/work-context/company.d.ts +16 -0
  64. package/dist/lib/work-context/company.js +46 -14
  65. package/dist/utils/self-update.js +4 -1
  66. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,115 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.133.0] — 2026-09-22
6
+
7
+ ### Fixed
8
+
9
+ - Lane follow-ups now probe all enabled, logged-in accounts for an unpinned
10
+ model, preserve the later quota reset and its source, expire abandoned pickup
11
+ validation markers with a terminal reason, and parse Codex's ordinal
12
+ `try again at` reset time.
13
+
14
+ - `hq sync push` no longer fails every file when the target vault was
15
+ provisioned on or after 2026-09-19. Those vaults carry an S3 Object Lock
16
+ default retention rule, and S3 refuses a PutObject into them unless the
17
+ request carries a content checksum; the CLI's declared `@indigoai-us/hq-cloud`
18
+ range still allowed a client that sends none, so an affected push reported
19
+ `400 InvalidRequest: Content-MD5 OR x-amz-checksum- HTTP header is required
20
+ for Put Object requests with Object Lock parameters` on every file. The
21
+ dependency floor now starts at the release that attaches the checksum, so no
22
+ install can resolve a client that cannot write to a locked vault. A CI guard
23
+ holds the floor.
24
+
25
+ - Lane creation and re-parenting now allow depth 3. A depth-3 caller and any
26
+ derived depth 4 lane are refused with `max_lane_depth`.
27
+
28
+ - Question ageing now derives its two-advance cap from the depth-3 lane cap, so
29
+ an unanswered worker-lane question reaches the user after two hourly hops.
30
+
31
+ ## [5.132.0] — 2026-09-22
32
+
33
+ ### Added
34
+
35
+ - `hq lanes resume <lane-id> --account <id>` and `hq lanes message <lane-id>
36
+ --account <id>` move a lane's provider session to another account of the
37
+ same provider and continue it there, with its full history. The claude
38
+ transcript (`projects/<encoded cwd>/<session>.jsonl`, plus the session's
39
+ subagent directory) or the codex rollout
40
+ (`sessions/YYYY/MM/DD/rollout-*-<session>.jsonl`) is copied into the target
41
+ account's home; the source copy stays, and a copy the target already holds
42
+ is refreshed when it differs, so a lane moved back resumes the turns it ran
43
+ elsewhere. Every check runs before anything is copied or written: the lane must not be running (`lane_running`), the
44
+ target must be an account of the lane's provider
45
+ (`account_provider_mismatch`), exist, be enabled, be logged in, hold no
46
+ quota hold (`account_quota_held`), and know the lane's model; a target that
47
+ pins a different model needs `--model <id>` (new on both commands, accepted
48
+ only with `--account`). Grok lanes are refused with
49
+ `provider_session_not_portable`; a missing transcript or rollout with
50
+ `provider_session_missing`, naming the path searched. The record's
51
+ `account` moves, `account_history` gains an `operator_move` entry,
52
+ `last_resume.account` names the account the run started on, and
53
+ `hq lanes show` prints them; a record that changed after the move was
54
+ prepared is refused with `account_move_conflict`. The capacity decision
55
+ runs under the target account's admission lock, and a moved lane holds its
56
+ slot (`pending_resume`) until its spawn claims it, so two moves cannot share
57
+ one free slot. A target at its running cap queues the lane there with
58
+ `pending_resume`, and the reconcile pass resumes the session on the target,
59
+ honouring `--no-compact`. Mesh events for a moved lane carry `account` and
60
+ `account_move`. Credential files are never copied.
61
+
62
+ - A provider-level turn failure is a named lane failure. When the worker's
63
+ stdout carries a codex `turn.failed` event, a Claude error result with an
64
+ HTTP status, or a grok error object, the watcher classifies the lane
65
+ `failed` with `terminal_reason: provider_rejected` and writes the
66
+ provider's message (capped), status, error class, `retryable`, and the
67
+ model and account the run used to `terminal_details`. Such an exit is never
68
+ `interrupted` and never spawns the envelope repair turn. A 429 stays on the
69
+ quota path. `hq lanes show` prints `terminal_reason` and
70
+ `terminal_details`.
71
+ - `hq lanes create --model` is validated against the provider's own model
72
+ list before anything is written: codex from the account home's
73
+ `models_cache.json`, grok from `grok models`, claude from the aliases and
74
+ full-name prefix in `claude --help`. An unknown id is refused with
75
+ `model_unsupported` naming the provider, the value, the source and the
76
+ accepted ids. A provider whose list cannot be read (missing, corrupt, or
77
+ unreadable cache; failed probe) is not validated. Validation runs before
78
+ the mesh thread is ensured and before the admission lock is taken, so a
79
+ refusal leaves nothing behind and a slow provider probe never holds the
80
+ lock. An unpinned create reads the list of every enabled account the
81
+ balanced choice could land on before the lock; the account is still chosen
82
+ under the lock, restricted to the accounts whose list accepted the model,
83
+ so nothing is refused once the mesh thread exists and no probe or catalog
84
+ read runs while the lock is held. A `grok models` or `claude --help` probe that exits nonzero
85
+ is not a catalog, whatever it printed, and skips validation. Every outpost
86
+ lane is validated at pickup on the executing host, after its account
87
+ binds, and fails with `model_unsupported` there; a lane cancelled during
88
+ the pickup probe stays cancelled and is not spawned. A picked-up lane is
89
+ stored ineligible for admission (`pickup_validation_pending`) until its
90
+ account binds, so the reconcile pass cannot spawn it while the probe is
91
+ out, and the failure write refuses an already-terminal or running record
92
+ inside its compare-and-swap.
93
+ - A codex `turn.failed` or grok error with status 429, or with no status and
94
+ a usage-limit or rate-limit message, now reaches the quota path (reset time
95
+ and account hold recorded) instead of ending `interrupted`, and skips the
96
+ envelope repair turn so the hold applies at once; an explicit non-429
97
+ status wins over quota-sounding text. The reconcile pass applies the same
98
+ routing when it recovers a lane whose worker and watcher both died. The
99
+ provider's output is scanned from the tail one line at a time, so a long
100
+ run's events are never all parsed and held at once. A 429 whose message
101
+ names no reset time holds the account for the provider's
102
+ `quota_default_hold_ms` (fifteen minutes unless configured), recorded with
103
+ `reset_source: default_hold`; a message with a reset time keeps that time.
104
+
105
+ ### Fixed
106
+
107
+ - `hq lanes reconcile install` no longer copies the installing shell's PATH
108
+ into the systemd unit. The unit PATH is, per resolved provider, the
109
+ directory holding the command name and the real binary's directory when a
110
+ symlink points elsewhere, then the hq binary's directory, then the node
111
+ binary's directory, then `/usr/local/bin:/usr/bin:/bin`, deduplicated; directories synced from
112
+ another machine's profile are left out.
113
+
5
114
  ## [5.131.0] — 2026-09-22
6
115
 
7
116
  ### Added
@@ -19,6 +128,36 @@
19
128
 
20
129
  ### Fixed
21
130
 
131
+ ## [Unreleased]
132
+
133
+ - `hq mesh daemon install` and `hq mesh daemon run` unload and remove the
134
+ legacy `ai.getindigo.hq-work-mesh-listen` unit (LaunchAgent, systemd user
135
+ unit, or Scheduled Task) so only the daemon subscribes. The daemon writes
136
+ `~/.hq/work-mesh/live-cache.json` and bumps its cursor once per project-view
137
+ push. MQTT reconnect logs once per transition into reconnecting instead of
138
+ on every close. `hq mesh daemon doctor` reports "legacy listener still
139
+ installed" while that unit remains (US-006).
140
+
141
+ ## [Unreleased]
142
+
143
+ - Held-event repair no longer treats a later event timestamp as proof that
144
+ metadata belongs to the current session instance. A reused session id stays
145
+ unresolved when the held event's binding episode does not match the current
146
+ one. A CLI self-update now records the verified HQ root and rewrites a
147
+ legacy daemon unit (no `HQ_ROOT`, or `Restart=on-failure`) before restarting
148
+ it, so the new process does not come up with an empty root allowlist.
149
+
150
+ ## [Unreleased]
151
+
152
+ - A machine credential (fleet box or local bot) no longer posts mesh events
153
+ with no company. `HQ_SPAWN_COMPANY`, the bot's company, or the identity
154
+ file is sent with the event. When none of those is set, the event is held
155
+ as `needs_company` and `hq mesh daemon doctor` shows the held count.
156
+ A membership probe that a machine token cannot answer leaves the company
157
+ in place and retries, instead of clearing it.
158
+
159
+ ## [Unreleased]
160
+
22
161
  - Expected caller errors from company resolution, membership, and qmd lookups
23
162
  no longer create Sentry issues. Windows filesystem failures remain visible
24
163
  and now include redacted path context plus a sync-aware diagnostic.
@@ -41,6 +180,14 @@
41
180
  active `cognito-tokens.json` is left unchanged. `hq mesh daemon doctor`
42
181
  prints the whoami subject and the subject the daemon is using.
43
182
 
183
+ ### Changed
184
+
185
+ - Local bots can reply to other people and bots in direct messages and in
186
+ connected rooms without an owner-only sender restriction. Each DM
187
+ correspondent has a separate conversation, and the owner's private context
188
+ stays out of other people's conversations. Bots retain their configured
189
+ owner and check suspicious requests before taking sensitive actions.
190
+
44
191
  ## [5.130.0] — 2026-09-22
45
192
 
46
193
  ### Fixed
@@ -6780,6 +6780,12 @@ export declare const COMMAND_CATALOG: readonly [{
6780
6780
  }, {
6781
6781
  readonly flags: "--story <id>";
6782
6782
  readonly description: "Re-point story attribution from this message";
6783
+ }, {
6784
+ readonly flags: "--account <id>";
6785
+ readonly description: "Move the lane's session to this account of its provider before the message resumes it";
6786
+ }, {
6787
+ readonly flags: "--model <id>";
6788
+ readonly description: "Model for the moved lane (with --account only)";
6783
6789
  }, {
6784
6790
  readonly flags: "--no-compact";
6785
6791
  readonly description: "Skip the resume-time compaction check for this message";
@@ -6808,6 +6814,12 @@ export declare const COMMAND_CATALOG: readonly [{
6808
6814
  }, {
6809
6815
  readonly flags: "--story <id>";
6810
6816
  readonly description: "Re-point story attribution from this resume";
6817
+ }, {
6818
+ readonly flags: "--account <id>";
6819
+ readonly description: "Move the lane's session to this account of its provider before resuming";
6820
+ }, {
6821
+ readonly flags: "--model <id>";
6822
+ readonly description: "Model for the moved lane (with --account only)";
6811
6823
  }, {
6812
6824
  readonly flags: "--no-compact";
6813
6825
  readonly description: "Skip the resume-time compaction check for this resume";
@@ -8763,6 +8763,14 @@ export const COMMAND_CATALOG = [
8763
8763
  "flags": "--story <id>",
8764
8764
  "description": "Re-point story attribution from this message"
8765
8765
  },
8766
+ {
8767
+ "flags": "--account <id>",
8768
+ "description": "Move the lane's session to this account of its provider before the message resumes it"
8769
+ },
8770
+ {
8771
+ "flags": "--model <id>",
8772
+ "description": "Model for the moved lane (with --account only)"
8773
+ },
8766
8774
  {
8767
8775
  "flags": "--no-compact",
8768
8776
  "description": "Skip the resume-time compaction check for this message"
@@ -8800,6 +8808,14 @@ export const COMMAND_CATALOG = [
8800
8808
  "flags": "--story <id>",
8801
8809
  "description": "Re-point story attribution from this resume"
8802
8810
  },
8811
+ {
8812
+ "flags": "--account <id>",
8813
+ "description": "Move the lane's session to this account of its provider before resuming"
8814
+ },
8815
+ {
8816
+ "flags": "--model <id>",
8817
+ "description": "Model for the moved lane (with --account only)"
8818
+ },
8803
8819
  {
8804
8820
  "flags": "--no-compact",
8805
8821
  "description": "Skip the resume-time compaction check for this resume"
@@ -7,13 +7,14 @@
7
7
  import { Command } from "commander";
8
8
  import { type OutpostDispatchResult } from "../lib/lanes/outpost-dispatch.js";
9
9
  import { emitLaneDecisionEvents, type LaneMesh } from "../lib/lanes/mesh-events.js";
10
- import { type LaneCompanyRef, type LaneRecord, type QuestionRecord, type ResolvedLaneWorker, type SeniorRef, type LivenessDeps, type MachineIdentity, type RunCommand, type ThrottleDeps } from "../lib/lanes/index.js";
10
+ import { MAX_LANE_DEPTH, type LaneCompanyRef, type LaneRecord, type QuestionRecord, type ResolvedLaneWorker, type SeniorRef, type LivenessDeps, type MachineIdentity, type RunCommand, type ThrottleDeps } from "../lib/lanes/index.js";
11
11
  import { questionBodyAbsPath } from "../lib/lanes/questions.js";
12
12
  import { type RunCompactionFn } from "../lib/lanes/compaction.js";
13
+ import { type KnownModelsDeps, type ProbeCommand } from "../lib/lanes/models.js";
13
14
  import { spawnLaneWorker, type KillProcessFn, type ProveDetachedFn, type SpawnProcessFn } from "../lib/lanes/spawn.js";
14
15
  export { spawnLaneWorker };
15
16
  export { questionBodyAbsPath };
16
- export declare const MAX_LANE_DEPTH = 2;
17
+ export { MAX_LANE_DEPTH };
17
18
  export declare const OUTPOST_ID_PATH = "/etc/outpost/outpost-id";
18
19
  export declare const OUTPOST_DIR = "/etc/outpost";
19
20
  export declare const LANES_RUNS_REL: string;
@@ -62,6 +63,8 @@ export interface LanesCommandDeps {
62
63
  presenceConnected?: boolean;
63
64
  systemdRoot?: string;
64
65
  runSystemctl?: RunCommand;
66
+ /** Provider CLI probe for --model validation; tests inject a fake. */
67
+ probeProvider?: ProbeCommand;
65
68
  resolveMachineIdentity?: () => MachineIdentity;
66
69
  stageOutpostPayload?: (outpostId: string, bytes: Uint8Array) => Promise<{
67
70
  getUrl: string;
@@ -100,14 +103,22 @@ export declare function composeWorkerBrief(worker: ResolvedLaneWorker, taskBrief
100
103
  */
101
104
  export declare function composeGeneralBrief(taskBrief: string): string;
102
105
  export declare function parseMaxRuntimeSeconds(raw: unknown, workerId: string): number | undefined;
103
- export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled"];
106
+ export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled", "model_unsupported"];
104
107
  export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
105
108
  export type PickupTerminalReason = (typeof PICKUP_TERMINAL_REASONS)[number];
109
+ /**
110
+ * The CAS updater a pickup failure applies. A lane that is already terminal
111
+ * for any reason (a cancel that landed after the caller's last read, an
112
+ * earlier pickup failure) is returned unchanged: the guard lives inside the
113
+ * same compare-and-swap that writes the failure, so no read-then-write gap
114
+ * can overwrite another writer's terminal decision.
115
+ */
116
+ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<string, unknown>): (current: LaneRecord) => LaneRecord;
106
117
  /**
107
118
  * Outpost-side account bind (spec §5.2). Writes a named terminal reason,
108
119
  * never a plain `failed`.
109
120
  */
110
- export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string): LaneRecord;
121
+ export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
111
122
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
112
123
  export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
113
124
  export declare function registerLanesCommand(parent: Command, deps?: LanesCommandDeps): Command;