@indigoai-us/hq-cli 5.131.0 → 5.132.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 (54) hide show
  1. package/CHANGELOG.md +111 -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 +13 -2
  5. package/dist/commands/lanes.js +394 -42
  6. package/dist/commands/mesh.js +20 -1
  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/index.d.ts +10 -3
  23. package/dist/lib/lanes/index.js +5 -1
  24. package/dist/lib/lanes/mesh-events.js +4 -0
  25. package/dist/lib/lanes/models.d.ts +78 -0
  26. package/dist/lib/lanes/models.js +205 -0
  27. package/dist/lib/lanes/move-account.d.ts +56 -0
  28. package/dist/lib/lanes/move-account.js +193 -0
  29. package/dist/lib/lanes/provider-rejection.d.ts +86 -0
  30. package/dist/lib/lanes/provider-rejection.js +258 -0
  31. package/dist/lib/lanes/quota.d.ts +17 -0
  32. package/dist/lib/lanes/quota.js +24 -0
  33. package/dist/lib/lanes/reconcile-install.d.ts +24 -3
  34. package/dist/lib/lanes/reconcile-install.js +48 -12
  35. package/dist/lib/lanes/reconcile.js +41 -11
  36. package/dist/lib/lanes/spawn.js +26 -5
  37. package/dist/lib/lanes/store.js +10 -0
  38. package/dist/lib/lanes/types.d.ts +49 -2
  39. package/dist/lib/lanes/types.js +4 -3
  40. package/dist/lib/lanes/watcher.d.ts +27 -0
  41. package/dist/lib/lanes/watcher.js +68 -5
  42. package/dist/lib/mesh/live/backfill-held.d.ts +4 -3
  43. package/dist/lib/mesh/live/backfill-held.js +37 -14
  44. package/dist/lib/mesh/live/daemon/doctor.js +5 -2
  45. package/dist/lib/mesh/live/daemon/install.d.ts +5 -0
  46. package/dist/lib/mesh/live/daemon/install.js +84 -1
  47. package/dist/lib/mesh/live/daemon/run.d.ts +5 -0
  48. package/dist/lib/mesh/live/daemon/run.js +14 -4
  49. package/dist/lib/mesh/live/emit-evidence.d.ts +19 -0
  50. package/dist/lib/mesh/live/emit-evidence.js +20 -2
  51. package/dist/lib/work-context/company.d.ts +16 -0
  52. package/dist/lib/work-context/company.js +46 -14
  53. package/dist/utils/self-update.js +4 -1
  54. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,89 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.132.0] — 2026-09-22
6
+
7
+ ### Added
8
+
9
+ - `hq lanes resume <lane-id> --account <id>` and `hq lanes message <lane-id>
10
+ --account <id>` move a lane's provider session to another account of the
11
+ same provider and continue it there, with its full history. The claude
12
+ transcript (`projects/<encoded cwd>/<session>.jsonl`, plus the session's
13
+ subagent directory) or the codex rollout
14
+ (`sessions/YYYY/MM/DD/rollout-*-<session>.jsonl`) is copied into the target
15
+ account's home; the source copy stays, and a copy the target already holds
16
+ is refreshed when it differs, so a lane moved back resumes the turns it ran
17
+ elsewhere. Every check runs before anything is copied or written: the lane must not be running (`lane_running`), the
18
+ target must be an account of the lane's provider
19
+ (`account_provider_mismatch`), exist, be enabled, be logged in, hold no
20
+ quota hold (`account_quota_held`), and know the lane's model; a target that
21
+ pins a different model needs `--model <id>` (new on both commands, accepted
22
+ only with `--account`). Grok lanes are refused with
23
+ `provider_session_not_portable`; a missing transcript or rollout with
24
+ `provider_session_missing`, naming the path searched. The record's
25
+ `account` moves, `account_history` gains an `operator_move` entry,
26
+ `last_resume.account` names the account the run started on, and
27
+ `hq lanes show` prints them; a record that changed after the move was
28
+ prepared is refused with `account_move_conflict`. The capacity decision
29
+ runs under the target account's admission lock, and a moved lane holds its
30
+ slot (`pending_resume`) until its spawn claims it, so two moves cannot share
31
+ one free slot. A target at its running cap queues the lane there with
32
+ `pending_resume`, and the reconcile pass resumes the session on the target,
33
+ honouring `--no-compact`. Mesh events for a moved lane carry `account` and
34
+ `account_move`. Credential files are never copied.
35
+
36
+ - A provider-level turn failure is a named lane failure. When the worker's
37
+ stdout carries a codex `turn.failed` event, a Claude error result with an
38
+ HTTP status, or a grok error object, the watcher classifies the lane
39
+ `failed` with `terminal_reason: provider_rejected` and writes the
40
+ provider's message (capped), status, error class, `retryable`, and the
41
+ model and account the run used to `terminal_details`. Such an exit is never
42
+ `interrupted` and never spawns the envelope repair turn. A 429 stays on the
43
+ quota path. `hq lanes show` prints `terminal_reason` and
44
+ `terminal_details`.
45
+ - `hq lanes create --model` is validated against the provider's own model
46
+ list before anything is written: codex from the account home's
47
+ `models_cache.json`, grok from `grok models`, claude from the aliases and
48
+ full-name prefix in `claude --help`. An unknown id is refused with
49
+ `model_unsupported` naming the provider, the value, the source and the
50
+ accepted ids. A provider whose list cannot be read (missing, corrupt, or
51
+ unreadable cache; failed probe) is not validated. Validation runs before
52
+ the mesh thread is ensured and before the admission lock is taken, so a
53
+ refusal leaves nothing behind and a slow provider probe never holds the
54
+ lock. An unpinned create reads the list of every enabled account the
55
+ balanced choice could land on before the lock; the account is still chosen
56
+ under the lock, restricted to the accounts whose list accepted the model,
57
+ so nothing is refused once the mesh thread exists and no probe or catalog
58
+ read runs while the lock is held. A `grok models` or `claude --help` probe that exits nonzero
59
+ is not a catalog, whatever it printed, and skips validation. Every outpost
60
+ lane is validated at pickup on the executing host, after its account
61
+ binds, and fails with `model_unsupported` there; a lane cancelled during
62
+ the pickup probe stays cancelled and is not spawned. A picked-up lane is
63
+ stored ineligible for admission (`pickup_validation_pending`) until its
64
+ account binds, so the reconcile pass cannot spawn it while the probe is
65
+ out, and the failure write refuses an already-terminal or running record
66
+ inside its compare-and-swap.
67
+ - A codex `turn.failed` or grok error with status 429, or with no status and
68
+ a usage-limit or rate-limit message, now reaches the quota path (reset time
69
+ and account hold recorded) instead of ending `interrupted`, and skips the
70
+ envelope repair turn so the hold applies at once; an explicit non-429
71
+ status wins over quota-sounding text. The reconcile pass applies the same
72
+ routing when it recovers a lane whose worker and watcher both died. The
73
+ provider's output is scanned from the tail one line at a time, so a long
74
+ run's events are never all parsed and held at once. A 429 whose message
75
+ names no reset time holds the account for the provider's
76
+ `quota_default_hold_ms` (fifteen minutes unless configured), recorded with
77
+ `reset_source: default_hold`; a message with a reset time keeps that time.
78
+
79
+ ### Fixed
80
+
81
+ - `hq lanes reconcile install` no longer copies the installing shell's PATH
82
+ into the systemd unit. The unit PATH is, per resolved provider, the
83
+ directory holding the command name and the real binary's directory when a
84
+ symlink points elsewhere, then the hq binary's directory, then the node
85
+ binary's directory, then `/usr/local/bin:/usr/bin:/bin`, deduplicated; directories synced from
86
+ another machine's profile are left out.
87
+
5
88
  ## [5.131.0] — 2026-09-22
6
89
 
7
90
  ### Added
@@ -19,6 +102,26 @@
19
102
 
20
103
  ### Fixed
21
104
 
105
+ ## [Unreleased]
106
+
107
+ - Held-event repair no longer treats a later event timestamp as proof that
108
+ metadata belongs to the current session instance. A reused session id stays
109
+ unresolved when the held event's binding episode does not match the current
110
+ one. A CLI self-update now records the verified HQ root and rewrites a
111
+ legacy daemon unit (no `HQ_ROOT`, or `Restart=on-failure`) before restarting
112
+ it, so the new process does not come up with an empty root allowlist.
113
+
114
+ ## [Unreleased]
115
+
116
+ - A machine credential (fleet box or local bot) no longer posts mesh events
117
+ with no company. `HQ_SPAWN_COMPANY`, the bot's company, or the identity
118
+ file is sent with the event. When none of those is set, the event is held
119
+ as `needs_company` and `hq mesh daemon doctor` shows the held count.
120
+ A membership probe that a machine token cannot answer leaves the company
121
+ in place and retries, instead of clearing it.
122
+
123
+ ## [Unreleased]
124
+
22
125
  - Expected caller errors from company resolution, membership, and qmd lookups
23
126
  no longer create Sentry issues. Windows filesystem failures remain visible
24
127
  and now include redacted path context plus a sync-aware diagnostic.
@@ -41,6 +144,14 @@
41
144
  active `cognito-tokens.json` is left unchanged. `hq mesh daemon doctor`
42
145
  prints the whoami subject and the subject the daemon is using.
43
146
 
147
+ ### Changed
148
+
149
+ - Local bots can reply to other people and bots in direct messages and in
150
+ connected rooms without an owner-only sender restriction. Each DM
151
+ correspondent has a separate conversation, and the owner's private context
152
+ stays out of other people's conversations. Bots retain their configured
153
+ owner and check suspicious requests before taking sensitive actions.
154
+
44
155
  ## [5.130.0] — 2026-09-22
45
156
 
46
157
  ### 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"
@@ -10,6 +10,7 @@ import { emitLaneDecisionEvents, type LaneMesh } from "../lib/lanes/mesh-events.
10
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";
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 };
@@ -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;