@indigoai-us/hq-cli 5.342.18 → 5.343.1

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 (42) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/command-catalog.generated.d.ts +12 -4
  3. package/dist/command-catalog.generated.js +14 -4
  4. package/dist/commands/company.d.ts +12 -0
  5. package/dist/commands/company.js +103 -6
  6. package/dist/commands/daemon.js +2 -11
  7. package/dist/commands/dm.js +10 -1
  8. package/dist/commands/lanes-shared.d.ts +1 -0
  9. package/dist/commands/lanes.d.ts +1 -0
  10. package/dist/commands/lanes.js +11 -0
  11. package/dist/commands/mesh.js +6 -2
  12. package/dist/commands/pack-install.d.ts +2 -0
  13. package/dist/commands/pack-install.js +7 -0
  14. package/dist/commands/pkg-update.d.ts +2 -0
  15. package/dist/commands/pkg-update.js +5 -1
  16. package/dist/commands/secrets.js +2 -3
  17. package/dist/lib/agent-kit/mcp/tools.js +1 -1
  18. package/dist/lib/bot/api.d.ts +1 -0
  19. package/dist/lib/bot/api.js +2 -0
  20. package/dist/lib/bot/progress.d.ts +12 -14
  21. package/dist/lib/bot/progress.js +29 -40
  22. package/dist/lib/bot/run.js +24 -24
  23. package/dist/lib/core-utils/sentry-report.d.ts +1 -1
  24. package/dist/lib/core-utils/sentry-report.js +18 -1
  25. package/dist/lib/daemon/command-call.d.ts +9 -0
  26. package/dist/lib/daemon/command-call.js +18 -0
  27. package/dist/lib/doctor/checks/work-context.js +5 -0
  28. package/dist/lib/lanes/outpost-dispatch.d.ts +1 -0
  29. package/dist/lib/lanes/outpost-dispatch.js +1 -0
  30. package/dist/lib/mesh/client.js +11 -0
  31. package/dist/lib/mesh/denial-codes.d.ts +13 -2
  32. package/dist/lib/mesh/denial-codes.js +25 -1
  33. package/dist/lib/mesh/live/daemon/doctor.js +4 -0
  34. package/dist/lib/work-context/outbox.js +8 -2
  35. package/dist/utils/cli-telemetry.js +5 -1
  36. package/dist/utils/install-tree-torn.d.ts +16 -13
  37. package/dist/utils/install-tree-torn.js +58 -26
  38. package/dist/utils/resident-package-preload.d.ts +1 -1
  39. package/dist/utils/resident-package-preload.js +62 -0
  40. package/dist/utils/secrets-sandbox-resident.d.ts +5 -0
  41. package/dist/utils/secrets-sandbox-resident.js +8 -0
  42. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.343.1] — 2026-10-04
6
+
7
+ - Include the bounded hq-cli version in `cli_feature_used` telemetry so feature-use rows can be grouped by release.
8
+
9
+ - Release the HQ CLI package-use lease before hq daemon call waits for its daemon response, after preloading the request protocol.
10
+ - Release the package-use lease after Outpost lane message and resume requests are submitted and their result-wait imports are preloaded.
11
+ - Preserve human, agent, and both audience tags when CLI tools and local bots post or reply in channels.
12
+ - Wait for a live installer to finish before retrying a failed CLI import when the install tree is still in flight.
13
+ - Preserve bounded master-hook slow-phase and per-phase timeout attribution in Sentry reports.
14
+
15
+ ## [5.343.0] — 2026-10-04
16
+ - `hq company --company <slug> settings set --work-registry-enabled <true|false>` and `--work-mesh-live-enabled <true|false>` let a company owner turn work tracking on or off. Work tracking needs both switches; after either is set the command says whether work tracking is on and prints the exact command for the switch that is still off. `hq company --company <slug> settings get` now also shows both switches for an owner or admin.
17
+ - `hq mesh context requeue` now also re-sends, by default, sessions that were refused while work tracking was off. `hq doctor` and `hq mesh daemon doctor` explain those refusals in plain words.
18
+ - Release the hq-cli package-use lease before the secrets sandbox poll and bounded package downloads after preloading their lazy runtime imports.
19
+
5
20
  ## [5.342.18] — 2026-10-04
6
21
  - Preserve confirmed remote lane-message responses for in-process monitor delivery.
7
22
  - Use native TypeScript renderers for `hq core rebuild-index` commands.
@@ -3204,7 +3204,7 @@ export declare const COMMAND_CATALOG: readonly [{
3204
3204
  readonly hidden: false;
3205
3205
  }, {
3206
3206
  readonly flags: "--audience <human|agent|both>";
3207
- readonly description: "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both (1:1 DMs only)";
3207
+ readonly description: "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both";
3208
3208
  readonly hidden: false;
3209
3209
  }];
3210
3210
  readonly subcommands: readonly [];
@@ -5334,7 +5334,7 @@ export declare const COMMAND_CATALOG: readonly [{
5334
5334
  readonly options: readonly [];
5335
5335
  readonly subcommands: readonly [{
5336
5336
  readonly name: "set";
5337
- readonly description: "Set per-company settings (PUT /company-settings). Require at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled.";
5337
+ readonly description: "Set per-company settings (PUT /company-settings). Require at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled / --work-registry-enabled / --work-mesh-live-enabled.";
5338
5338
  readonly aliases: readonly [];
5339
5339
  readonly hidden: false;
5340
5340
  readonly helpOption: true;
@@ -5358,11 +5358,19 @@ export declare const COMMAND_CATALOG: readonly [{
5358
5358
  readonly flags: "--checkpoint-gate-enabled <true|false>";
5359
5359
  readonly description: "Require (true) or stop requiring (false) a checkpoint before HQ sessions in this company end a turn";
5360
5360
  readonly hidden: false;
5361
+ }, {
5362
+ readonly flags: "--work-registry-enabled <true|false>";
5363
+ readonly description: "Turn work-session registration on or off for this company (default off). Work tracking needs this and --work-mesh-live-enabled";
5364
+ readonly hidden: false;
5365
+ }, {
5366
+ readonly flags: "--work-mesh-live-enabled <true|false>";
5367
+ readonly description: "Turn live work tracking on or off for this company (default off). Work tracking needs this and --work-registry-enabled";
5368
+ readonly hidden: false;
5361
5369
  }];
5362
5370
  readonly subcommands: readonly [];
5363
5371
  }, {
5364
5372
  readonly name: "get";
5365
- readonly description: "Show whether the checkpoint Stop gate is on for this company (GET /company-settings/checkpoint-gate; any member may read it).";
5373
+ readonly description: "Show whether the checkpoint Stop gate is on for this company (GET /company-settings/checkpoint-gate; any member may read it) and, for an owner or admin, the two work tracking switches (GET /company-settings).";
5366
5374
  readonly aliases: readonly [];
5367
5375
  readonly hidden: false;
5368
5376
  readonly helpOption: true;
@@ -9635,7 +9643,7 @@ export declare const COMMAND_CATALOG: readonly [{
9635
9643
  readonly subcommands: readonly [];
9636
9644
  }, {
9637
9645
  readonly name: "requeue";
9638
- readonly description: "Requeue quarantined outbox operations (default: only AUTH_DENIED — work-mesh-live gap 10 recovery)";
9646
+ readonly description: "Requeue quarantined outbox operations (default: AUTH_DENIED - work-mesh-live gap 10 recovery - plus sessions refused while work tracking was off). Run once after the company owner turns work tracking on";
9639
9647
  readonly aliases: readonly [];
9640
9648
  readonly hidden: false;
9641
9649
  readonly helpOption: true;
@@ -3874,7 +3874,7 @@ export const COMMAND_CATALOG = [
3874
3874
  },
3875
3875
  {
3876
3876
  "flags": "--audience <human|agent|both>",
3877
- "description": "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both (1:1 DMs only)",
3877
+ "description": "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both",
3878
3878
  "hidden": false
3879
3879
  }
3880
3880
  ],
@@ -6478,7 +6478,7 @@ export const COMMAND_CATALOG = [
6478
6478
  "subcommands": [
6479
6479
  {
6480
6480
  "name": "set",
6481
- "description": "Set per-company settings (PUT /company-settings). Require at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled.",
6481
+ "description": "Set per-company settings (PUT /company-settings). Require at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled / --work-registry-enabled / --work-mesh-live-enabled.",
6482
6482
  "aliases": [],
6483
6483
  "hidden": false,
6484
6484
  "helpOption": true,
@@ -6506,13 +6506,23 @@ export const COMMAND_CATALOG = [
6506
6506
  "flags": "--checkpoint-gate-enabled <true|false>",
6507
6507
  "description": "Require (true) or stop requiring (false) a checkpoint before HQ sessions in this company end a turn",
6508
6508
  "hidden": false
6509
+ },
6510
+ {
6511
+ "flags": "--work-registry-enabled <true|false>",
6512
+ "description": "Turn work-session registration on or off for this company (default off). Work tracking needs this and --work-mesh-live-enabled",
6513
+ "hidden": false
6514
+ },
6515
+ {
6516
+ "flags": "--work-mesh-live-enabled <true|false>",
6517
+ "description": "Turn live work tracking on or off for this company (default off). Work tracking needs this and --work-registry-enabled",
6518
+ "hidden": false
6509
6519
  }
6510
6520
  ],
6511
6521
  "subcommands": []
6512
6522
  },
6513
6523
  {
6514
6524
  "name": "get",
6515
- "description": "Show whether the checkpoint Stop gate is on for this company (GET /company-settings/checkpoint-gate; any member may read it).",
6525
+ "description": "Show whether the checkpoint Stop gate is on for this company (GET /company-settings/checkpoint-gate; any member may read it) and, for an owner or admin, the two work tracking switches (GET /company-settings).",
6516
6526
  "aliases": [],
6517
6527
  "hidden": false,
6518
6528
  "helpOption": true,
@@ -11686,7 +11696,7 @@ export const COMMAND_CATALOG = [
11686
11696
  },
11687
11697
  {
11688
11698
  "name": "requeue",
11689
- "description": "Requeue quarantined outbox operations (default: only AUTH_DENIED — work-mesh-live gap 10 recovery)",
11699
+ "description": "Requeue quarantined outbox operations (default: AUTH_DENIED - work-mesh-live gap 10 recovery - plus sessions refused while work tracking was off). Run once after the company owner turns work tracking on",
11690
11700
  "aliases": [],
11691
11701
  "hidden": false,
11692
11702
  "helpOption": true,
@@ -1,3 +1,15 @@
1
1
  import { Command } from "commander";
2
+ /**
3
+ * The two per-company switches that together turn work tracking on. The server
4
+ * refuses work-session registration unless both are true. `null` means the
5
+ * values could not be read.
6
+ */
7
+ interface WorkTrackingSettings {
8
+ workRegistryEnabled: boolean;
9
+ workMeshLiveEnabled: boolean;
10
+ }
11
+ /** Plain next steps after a work tracking switch was set, from the effective settings. */
12
+ export declare function workTrackingNextSteps(companySlug: string, settings: WorkTrackingSettings | null): string[];
2
13
  export declare function registerCompanyCommand(program: Command): void;
14
+ export {};
3
15
  //# sourceMappingURL=company.d.ts.map
@@ -4,6 +4,64 @@ import { vaultApiFetch, getEntityUid } from "../utils/vault-api.js";
4
4
  import { registerCompanyTransferCommand } from "./company-transfer.js";
5
5
  import { registerCompanyRenameCommand } from "./company-rename.js";
6
6
  import { describeRefreshOutcome, refreshCheckpointGateSetting, } from "./core-checkpoint-gate-setting.js";
7
+ /** Both switches from a settings response body, or null unless both are booleans. */
8
+ function workTrackingFromBody(body) {
9
+ const { workRegistryEnabled, workMeshLiveEnabled } = body;
10
+ return typeof workRegistryEnabled === "boolean" && typeof workMeshLiveEnabled === "boolean"
11
+ ? { workRegistryEnabled, workMeshLiveEnabled }
12
+ : null;
13
+ }
14
+ /**
15
+ * Read both switches from GET /company-settings (owner or admin only). The
16
+ * server returns effective values, so an absent field means the default (off).
17
+ * A refused or failed read returns null rather than a guess.
18
+ */
19
+ async function readWorkTrackingSettings(token, companyUid) {
20
+ try {
21
+ const res = await vaultApiFetch({
22
+ token,
23
+ path: "/company-settings",
24
+ query: { companyUid },
25
+ });
26
+ if (!res.ok)
27
+ return null;
28
+ const body = (await res.json().catch(() => ({})));
29
+ return {
30
+ workRegistryEnabled: body.workRegistryEnabled === true,
31
+ workMeshLiveEnabled: body.workMeshLiveEnabled === true,
32
+ };
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /** Plain next steps after a work tracking switch was set, from the effective settings. */
39
+ export function workTrackingNextSteps(companySlug, settings) {
40
+ const command = `hq company --company ${companySlug} settings`;
41
+ if (!settings) {
42
+ return [`Could not read the settings back. Check both switches with: ${command} get`];
43
+ }
44
+ if (settings.workRegistryEnabled && settings.workMeshLiveEnabled) {
45
+ return [
46
+ "Work tracking is on.",
47
+ "Sessions refused while it was off are held on each machine. " +
48
+ "Each person should run once: hq mesh context requeue",
49
+ ];
50
+ }
51
+ if (settings.workRegistryEnabled) {
52
+ return [
53
+ "Work tracking is not on yet. Sessions will still be refused until live tracking is also on. " +
54
+ `Run: ${command} set --work-mesh-live-enabled true`,
55
+ ];
56
+ }
57
+ if (settings.workMeshLiveEnabled) {
58
+ return [
59
+ "Work tracking is not on yet. Sessions will still be refused until the work registry is also on. " +
60
+ `Run: ${command} set --work-registry-enabled true`,
61
+ ];
62
+ }
63
+ return [`Work tracking is off for ${companySlug}.`];
64
+ }
7
65
  /**
8
66
  * Parse a `--flag <true|false>` string into a boolean, or throw. Commander
9
67
  * passes the raw string; we validate strictly so a typo never silently writes
@@ -32,11 +90,14 @@ export function registerCompanyCommand(program) {
32
90
  .command("set")
33
91
  .description("Set per-company settings (PUT /company-settings). Require at least one " +
34
92
  "of --crm-enabled / --ontology-enabled / --accounting-enabled / " +
35
- "--checkpoint-gate-enabled.")
93
+ "--checkpoint-gate-enabled / --work-registry-enabled / " +
94
+ "--work-mesh-live-enabled.")
36
95
  .option("--crm-enabled <true|false>", "Enable/disable the native CRM for this company")
37
96
  .option("--ontology-enabled <true|false>", "Enable/disable the ontology gardener for this company")
38
97
  .option("--accounting-enabled <true|false>", "Enable/disable the accounting pack for this company")
39
98
  .option("--checkpoint-gate-enabled <true|false>", "Require (true) or stop requiring (false) a checkpoint before HQ sessions in this company end a turn")
99
+ .option("--work-registry-enabled <true|false>", "Turn work-session registration on or off for this company (default off). Work tracking needs this and --work-mesh-live-enabled")
100
+ .option("--work-mesh-live-enabled <true|false>", "Turn live work tracking on or off for this company (default off). Work tracking needs this and --work-registry-enabled")
40
101
  .action(async (opts) => {
41
102
  try {
42
103
  const companySlug = company.opts().company;
@@ -47,8 +108,10 @@ export function registerCompanyCommand(program) {
47
108
  if (opts.crmEnabled === undefined &&
48
109
  opts.ontologyEnabled === undefined &&
49
110
  opts.accountingEnabled === undefined &&
50
- opts.checkpointGateEnabled === undefined) {
51
- console.error(chalk.red("Error: provide at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled."));
111
+ opts.checkpointGateEnabled === undefined &&
112
+ opts.workRegistryEnabled === undefined &&
113
+ opts.workMeshLiveEnabled === undefined) {
114
+ console.error(chalk.red("Error: provide at least one of --crm-enabled / --ontology-enabled / --accounting-enabled / --checkpoint-gate-enabled / --work-registry-enabled / --work-mesh-live-enabled."));
52
115
  process.exit(1);
53
116
  }
54
117
  const body = {};
@@ -64,6 +127,14 @@ export function registerCompanyCommand(program) {
64
127
  if (opts.checkpointGateEnabled !== undefined) {
65
128
  body.checkpointGateEnabled = parseBoolFlag(opts.checkpointGateEnabled, "--checkpoint-gate-enabled");
66
129
  }
130
+ if (opts.workRegistryEnabled !== undefined) {
131
+ body.workRegistryEnabled = parseBoolFlag(opts.workRegistryEnabled, "--work-registry-enabled");
132
+ }
133
+ if (opts.workMeshLiveEnabled !== undefined) {
134
+ body.workMeshLiveEnabled = parseBoolFlag(opts.workMeshLiveEnabled, "--work-mesh-live-enabled");
135
+ }
136
+ const setsWorkTracking = body.workRegistryEnabled !== undefined ||
137
+ body.workMeshLiveEnabled !== undefined;
67
138
  const token = await ensureCognitoToken();
68
139
  const companyUid = await getEntityUid(token, { companySlug });
69
140
  body.companyUid = companyUid;
@@ -76,7 +147,9 @@ export function registerCompanyCommand(program) {
76
147
  if (!res.ok) {
77
148
  const errBody = (await res.json().catch(() => ({})));
78
149
  if (res.status === 403) {
79
- console.error(chalk.red("Requires owner role on this company to change its settings."));
150
+ console.error(chalk.red(setsWorkTracking
151
+ ? "Only the company owner can change this setting."
152
+ : "Requires owner role on this company to change its settings."));
80
153
  process.exit(1);
81
154
  }
82
155
  console.error(chalk.red(`Failed to update company settings: ${errBody.error ?? res.statusText}`));
@@ -98,6 +171,16 @@ export function registerCompanyCommand(program) {
98
171
  // The setting is saved; the cache refreshes on the next Stop.
99
172
  }
100
173
  }
174
+ if (setsWorkTracking) {
175
+ // Registration needs both switches, so report the effective pair:
176
+ // from the PUT response when it echoes both, otherwise from a GET.
177
+ const resBody = (await res.json().catch(() => ({})));
178
+ const effective = workTrackingFromBody(resBody) ??
179
+ (await readWorkTrackingSettings(token, companyUid));
180
+ for (const line of workTrackingNextSteps(companySlug, effective)) {
181
+ console.log(line);
182
+ }
183
+ }
101
184
  }
102
185
  catch (err) {
103
186
  console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
@@ -107,7 +190,8 @@ export function registerCompanyCommand(program) {
107
190
  settings
108
191
  .command("get")
109
192
  .description("Show whether the checkpoint Stop gate is on for this company " +
110
- "(GET /company-settings/checkpoint-gate; any member may read it).")
193
+ "(GET /company-settings/checkpoint-gate; any member may read it) and, " +
194
+ "for an owner or admin, the two work tracking switches (GET /company-settings).")
111
195
  .option("--json", "Print the result as JSON")
112
196
  .action(async (opts) => {
113
197
  try {
@@ -137,11 +221,24 @@ export function registerCompanyCommand(program) {
137
221
  console.error(chalk.red("Failed to read company settings: no checkpointGateEnabled in the response."));
138
222
  process.exit(1);
139
223
  }
224
+ // The work tracking switches are only on the owner/admin settings
225
+ // route. A refused or failed read is reported as unknown, not guessed.
226
+ const workTracking = await readWorkTrackingSettings(token, companyUid);
140
227
  if (opts.json) {
141
- console.log(JSON.stringify({ company: companySlug, checkpointGateEnabled: enabled }));
228
+ console.log(JSON.stringify({
229
+ company: companySlug,
230
+ checkpointGateEnabled: enabled,
231
+ workRegistryEnabled: workTracking?.workRegistryEnabled ?? null,
232
+ workMeshLiveEnabled: workTracking?.workMeshLiveEnabled ?? null,
233
+ }));
142
234
  }
143
235
  else {
144
236
  console.log(`checkpointGateEnabled=${enabled}`);
237
+ for (const key of ["workRegistryEnabled", "workMeshLiveEnabled"]) {
238
+ console.log(workTracking
239
+ ? `${key}=${workTracking[key]}`
240
+ : `${key}=unknown (only the company owner or an admin can read this setting)`);
241
+ }
145
242
  }
146
243
  }
147
244
  catch (err) {
@@ -29,6 +29,7 @@ import { MACHINE_TYPE_MARKER_PATH, detectMachineType, homeFromPasswd, } from "..
29
29
  import { daemonPaths } from "../lib/daemon/paths.js";
30
30
  import { processCommandLine } from "../lib/daemon/lock.js";
31
31
  import { writeControlRequest } from "../lib/daemon/requests.js";
32
+ import { runDaemonCall } from "../lib/daemon/command-call.js";
32
33
  import { isSyncRunnerCommandLine, readWatchRunnerOwnerStatus, watchRunnerOwnerStatusPath } from "../lib/daemon/sync-runner.js";
33
34
  import { getSyncModeForCompany, setSyncMode } from "./sync-mode.js";
34
35
  import { requireAnywhereRuntime } from "./anywhere-runtime-flag.js";
@@ -857,17 +858,7 @@ export function registerDaemonCommand(program, over = {}) {
857
858
  const timeoutMs = Number(opts.timeout ?? (op === "policy.check" ? "40000" : "20000"));
858
859
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
859
860
  throw new Error("--timeout must be a positive number of milliseconds");
860
- const { hqdRequest, hqdSocketPath, HqdClientError } = await import("../lib/daemon/protocol.js");
861
- try {
862
- const result = await hqdRequest(op, args, { socketPath: hqdSocketPath(d.home, d.env), timeoutMs });
863
- d.out(JSON.stringify(result, null, 2));
864
- }
865
- catch (err) {
866
- if (!(err instanceof HqdClientError))
867
- throw err;
868
- d.out(JSON.stringify({ error: { code: err.code, message: err.message } }, null, 2));
869
- process.exitCode = 1;
870
- }
861
+ await runResidentCommandWithPackageLease("daemon-call", () => runDaemonCall(op, args, timeoutMs, d));
871
862
  }));
872
863
  daemon
873
864
  .command("status")
@@ -614,11 +614,20 @@ async function runChannelSend(channelName, message, opts) {
614
614
  // Send the human-readable form: the quotes only existed for parsing.
615
615
  if (mentions.length > 0)
616
616
  body = stripMentionQuotes(body);
617
+ let audience;
618
+ if (opts.audience !== undefined && opts.audience !== "") {
619
+ if (!DM_AUDIENCE_VALUES.includes(opts.audience)) {
620
+ throw new Error(`Invalid --audience '${opts.audience}': must be human, agent, or both.`);
621
+ }
622
+ audience = opts.audience;
623
+ }
617
624
  const payload = { body };
618
625
  if (mentions.length > 0)
619
626
  payload.mentions = mentions;
620
627
  if (rootEventId)
621
628
  payload.rootEventId = rootEventId;
629
+ if (audience)
630
+ payload.audience = audience;
622
631
  const sendRes = await vaultApiFetch({
623
632
  token,
624
633
  path: `/v1/notify/channels/${encodeURIComponent(channel.channelId)}/messages`,
@@ -1327,7 +1336,7 @@ export function registerDmCommand(program) {
1327
1336
  .option("--details-file <path>", "Read the details from a file (1:1 DMs only)")
1328
1337
  .option("--at <iso>", "Schedule delivery at an ISO8601 time (store-and-forward; delivered within ~60s of the time; 1:1 DMs only)")
1329
1338
  .option("--in <duration>", "Schedule delivery after a relative delay: 30s, 10m, 2h, 1d (1:1 DMs only)")
1330
- .option("--audience <human|agent|both>", "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both (1:1 DMs only)")
1339
+ .option("--audience <human|agent|both>", "Tag who this message is for: human (a person must read it), agent (progress/status, hidden from humans by default), or both")
1331
1340
  .action(async (recipient, message, opts) => {
1332
1341
  await runDmSend(recipient, message, opts);
1333
1342
  });
@@ -272,6 +272,7 @@ export interface Ctx {
272
272
  command: string;
273
273
  timeoutSeconds?: number;
274
274
  waitMs?: number;
275
+ beforeWait?: () => Promise<void>;
275
276
  }) => Promise<OutpostDispatchResult>;
276
277
  backfillOutpostIdentity: (expectedId: string) => "created" | "unchanged";
277
278
  fetchText: (url: string) => Promise<string>;
@@ -86,6 +86,7 @@ export interface LanesCommandDeps {
86
86
  command: string;
87
87
  timeoutSeconds?: number;
88
88
  waitMs?: number;
89
+ beforeWait?: () => Promise<void>;
89
90
  }) => Promise<OutpostDispatchResult>;
90
91
  /** Test seam for the atomic, non-overwriting marker repair. */
91
92
  backfillOutpostIdentity?: (expectedId: string) => "created" | "unchanged";
@@ -4317,11 +4317,22 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
4317
4317
  let dispatched;
4318
4318
  let grantContextRejected = false;
4319
4319
  let failedResumeOptionRejected = false;
4320
+ let outpostWaitLeasePreparationStarted = false;
4320
4321
  try {
4321
4322
  const dispatchCommand = (contexts) => ctx.dispatchOutpostExec({
4322
4323
  outpostId,
4323
4324
  timeoutSeconds: Math.ceil(waitMs / 1000),
4324
4325
  waitMs,
4326
+ ...(action === "message" || action === "resume"
4327
+ ? {
4328
+ beforeWait: async () => {
4329
+ if (outpostWaitLeasePreparationStarted)
4330
+ return;
4331
+ outpostWaitLeasePreparationStarted = true;
4332
+ await runResidentCommandWithPackageLease("outpost-wait", async () => undefined);
4333
+ },
4334
+ }
4335
+ : {}),
4325
4336
  command: buildOutpostLaneCommand({
4326
4337
  action: action === "grant"
4327
4338
  ? "grant-target"
@@ -1424,7 +1424,9 @@ async function runContextRequeue(opts) {
1424
1424
  }
1425
1425
  const scope = errorCode === null
1426
1426
  ? "all quarantine reasons"
1427
- : `lastErrorCode=${errorCode === undefined ? "AUTH_DENIED" : errorCode}`;
1427
+ : errorCode === undefined
1428
+ ? "lastErrorCode=AUTH_DENIED or refused while work tracking was off"
1429
+ : `lastErrorCode=${errorCode}`;
1428
1430
  console.log(`Requeued ${requeued.length} quarantined outbox operation${requeued.length === 1 ? "" : "s"} (${scope}). The mesh daemon will retry them on its next replay.`);
1429
1431
  }
1430
1432
  async function runContextUntracked(sessionId, opts) {
@@ -2016,7 +2018,9 @@ export function registerMeshCommand(program) {
2016
2018
  .action((opts) => wrap(() => runContextCorrect(opts))());
2017
2019
  context
2018
2020
  .command("requeue")
2019
- .description("Requeue quarantined outbox operations (default: only AUTH_DENIED — work-mesh-live gap 10 recovery)")
2021
+ .description("Requeue quarantined outbox operations (default: AUTH_DENIED - work-mesh-live gap 10 recovery - " +
2022
+ "plus sessions refused while work tracking was off). " +
2023
+ "Run once after the company owner turns work tracking on")
2020
2024
  .option("--all", "Requeue every quarantined op regardless of lastErrorCode")
2021
2025
  .option("--error-code <code>", "Requeue quarantined ops with this lastErrorCode")
2022
2026
  .option("--json", "Print machine-readable JSON")
@@ -34,6 +34,7 @@
34
34
  * from each pack's package.yaml; rationale lives in the layout-fix PR.)
35
35
  */
36
36
  import { type KeyObject } from 'node:crypto';
37
+ import { type ResidentCommandLeaseOptions } from '../utils/resident-package-preload.js';
37
38
  import { type SecretResolver } from './mcp-registration.js';
38
39
  import type { PackManifest } from '../types.js';
39
40
  export interface ResolveLatestOptions {
@@ -447,6 +448,7 @@ export interface InstallPackOptions {
447
448
  */
448
449
  requireSignature?: boolean;
449
450
  }
451
+ export declare function runPackInstallWithLease<T>(run: () => Promise<T>, leaseOptions?: ResidentCommandLeaseOptions): Promise<T>;
450
452
  export declare function installPack(source: string, opts?: InstallPackOptions): Promise<void>;
451
453
  export {};
452
454
  //# sourceMappingURL=pack-install.d.ts.map
@@ -50,6 +50,7 @@ import { codexTargets } from '../lib/install/codex-targets.js';
50
50
  import { readHqVersion, routeContribution, listInstalledPacks, mcpServerNameForContribution } from '../utils/pack-contributions.js';
51
51
  import { CONTRIBUTION_TABLE, payloadFor } from '../utils/contribution-table.js';
52
52
  import { safeExtractTarball } from './safe-extract.js';
53
+ import { runResidentCommandWithPackageLease } from '../utils/resident-package-preload.js';
53
54
  import { getCompanyUid, vaultApiFetch, vaultApiFetchPublic, } from '../utils/vault-api.js';
54
55
  import { ensureCognitoToken } from '../utils/cognito-session.js';
55
56
  import { formatApiKeyCapabilityDenial, peekHqApiKey, } from '../utils/resolve-vault-credential.js';
@@ -1677,7 +1678,13 @@ export function runScanPackages(hqRoot, opts = {}) {
1677
1678
  console.log(chalk.yellow(' scan-packages.sh exited non-zero; see output above.'));
1678
1679
  }
1679
1680
  }
1681
+ export function runPackInstallWithLease(run, leaseOptions) {
1682
+ return runResidentCommandWithPackageLease("pack-install", run, leaseOptions);
1683
+ }
1680
1684
  export async function installPack(source, opts = {}) {
1685
+ return runPackInstallWithLease(() => installPackWithoutLease(source, opts));
1686
+ }
1687
+ async function installPackWithoutLease(source, opts) {
1681
1688
  // NOTE: the HQ_API_KEY fail-closed gate is NOT enforced here. `installPack`
1682
1689
  // is a shared primitive called both by the top-level `hq install` CLI route
1683
1690
  // (where Cognito-only is the right gate — checked there instead) AND by
@@ -5,5 +5,7 @@
5
5
  * If slug given: update only that package.
6
6
  */
7
7
  import { Command } from 'commander';
8
+ import { type ResidentCommandLeaseOptions } from '../utils/resident-package-preload.js';
8
9
  export declare function registerPackageUpdateCommand(parent: Command): void;
10
+ export declare function runPackageUpdateWithLease<T>(run: () => Promise<T>, leaseOptions?: ResidentCommandLeaseOptions): Promise<T>;
9
11
  //# sourceMappingURL=pkg-update.d.ts.map
@@ -9,6 +9,7 @@ import * as os from 'os';
9
9
  import * as path from 'path';
10
10
  import { parseUserYaml } from '../utils/user-yaml-error.js';
11
11
  import { execSync } from 'child_process';
12
+ import { runResidentCommandWithPackageLease } from '../utils/resident-package-preload.js';
12
13
  import chalk from 'chalk';
13
14
  import { ensureCognitoToken } from '../utils/cognito-session.js';
14
15
  import { resolveDefaultHqRoot } from '../utils/cognito-session.js';
@@ -21,7 +22,7 @@ export function registerPackageUpdateCommand(parent) {
21
22
  .description('Check for and apply package updates')
22
23
  .action(async (slug) => {
23
24
  try {
24
- await updatePackages(slug);
25
+ await runPackageUpdateWithLease(() => updatePackages(slug));
25
26
  }
26
27
  catch (error) {
27
28
  console.error(chalk.red('Update failed:'), error instanceof Error ? error.message : 'Unknown error');
@@ -29,6 +30,9 @@ export function registerPackageUpdateCommand(parent) {
29
30
  }
30
31
  });
31
32
  }
33
+ export function runPackageUpdateWithLease(run, leaseOptions) {
34
+ return runResidentCommandWithPackageLease("pkg-update", run, leaseOptions);
35
+ }
32
36
  async function updatePackages(slug) {
33
37
  const hqRoot = resolveDefaultHqRoot({ onMissing: 'throw' });
34
38
  const entries = readRegistry(hqRoot);
@@ -10,6 +10,7 @@ import { SECRET_NAME_PATTERN, GROUP_ID_PATTERN, EMAIL_PATTERN } from "./_pattern
10
10
  import { describeSecretsScope, formatSecretSaved, formatSecretsListEmpty, formatSecretsListHeader, } from "./secrets-scope.js";
11
11
  import { vaultApiFetch, getCompanyUid, getEntityUid, looksLikeCompanyUid, } from "../utils/vault-api.js";
12
12
  import { formatApiKeyCapabilityDenial, requireApiKeyCapability, resolveVaultCredentialForCapability, } from "../utils/resolve-vault-credential.js";
13
+ import { runSandboxJobAfterSubmission } from "../utils/secrets-sandbox-resident.js";
13
14
  export { vaultApiFetch, getCompanyUid, getEntityUid };
14
15
  /**
15
16
  * Mirrors `RESOURCE_SURFACES` in hq-pro
@@ -1061,9 +1062,7 @@ export function registerSecretsCommand(program) {
1061
1062
  secretNames: keys,
1062
1063
  command,
1063
1064
  });
1064
- const job = started.status === "succeeded" || started.status === "failed"
1065
- ? await client.getJob(token, started.jobId)
1066
- : await client.pollJob(token, started.jobId);
1065
+ const job = await runSandboxJobAfterSubmission(client, token, started);
1067
1066
  renderSandboxJobResult(job, keys);
1068
1067
  const exitCode = typeof job.exitCode === "number" ? job.exitCode : undefined;
1069
1068
  if (job.success === false || (exitCode !== undefined && exitCode !== 0) || job.status === "failed") {
@@ -237,7 +237,7 @@ export function buildAgentMcpTools(clients) {
237
237
  const token = await clients.getToken();
238
238
  const res = await clients.apiJson(token, `/v1/notify/channels/${encodeURIComponent(to)}/messages`, {
239
239
  method: "POST",
240
- body: { body: message },
240
+ body: { body: message, ...(audience ? { audience } : {}) },
241
241
  });
242
242
  if (res.status < 200 || res.status >= 300) {
243
243
  return errorResult(`Channel send failed (${res.status}): ${describeError(res.body)}`);
@@ -239,6 +239,7 @@ export declare class BotApi {
239
239
  channelId: string;
240
240
  body: string;
241
241
  rootEventId?: string;
242
+ audience?: "human" | "agent" | "both";
242
243
  }): Promise<{
243
244
  eventId?: string;
244
245
  createdAt?: string;
@@ -195,6 +195,8 @@ export class BotApi {
195
195
  const payload = { body: input.body };
196
196
  if (input.rootEventId)
197
197
  payload.rootEventId = input.rootEventId;
198
+ if (input.audience)
199
+ payload.audience = input.audience;
198
200
  const { body } = await this.call(`/v1/notify/channels/${encodeURIComponent(input.channelId)}/messages`, { method: "POST", body: payload });
199
201
  return body;
200
202
  }
@@ -28,13 +28,10 @@ export declare const PROGRESS_MIN_GAP_MS = 4000;
28
28
  export declare const WORKING_NOTICE_MS = 60000;
29
29
  export declare const WORKING_NOTICE_TEXT = "Working on it \u2014 I'll reply here when it's done.";
30
30
  export interface ProgressPosterOptions {
31
- /** Post one message; resolve true when it was delivered. */
32
- send: (body: string) => Promise<boolean>;
33
- /**
34
- * If provided, the final reply is sent through this instead of `send`.
35
- * Use to tag the final reply differently from in-between progress messages.
36
- */
37
- sendFinal?: (body: string) => Promise<boolean>;
31
+ /** Post a live progress beat while the turn is still running. */
32
+ sendProgress: (body: string) => Promise<boolean>;
33
+ /** Post terminal content that the person needs to read. */
34
+ sendToPerson: (body: string) => Promise<boolean>;
38
35
  now?: () => number;
39
36
  setTimer?: (fn: () => void, ms: number) => unknown;
40
37
  clearTimer?: (handle: unknown) => void;
@@ -63,8 +60,8 @@ export declare function normalizeForCompare(text: string): string;
63
60
  /** Matches {"action":"no_reply"} anywhere in a model reply, with or without preamble text. */
64
61
  export declare function containsNoReplySentinel(text: string): boolean;
65
62
  export declare class ProgressPoster {
66
- private readonly send;
67
- private readonly sendFinalFn?;
63
+ private readonly sendProgress;
64
+ private readonly sendToPerson;
68
65
  private readonly now;
69
66
  private readonly setTimer;
70
67
  private readonly clearTimer;
@@ -95,6 +92,8 @@ export declare class ProgressPoster {
95
92
  * deliveries posted the placeholder right after a real message.
96
93
  */
97
94
  private queued;
95
+ /** Posts queued for the person, excluding agent-lane status beats. */
96
+ private personQueued;
98
97
  constructor(opts: ProgressPosterOptions);
99
98
  /** Arm the one-time "working on it" note. Call when the turn starts. */
100
99
  start(): void;
@@ -109,11 +108,9 @@ export declare class ProgressPoster {
109
108
  /** True once any message from the model was said this turn (not counting the notice). */
110
109
  get saidAnything(): boolean;
111
110
  /**
112
- * The turn succeeded: post whatever is pending, then the final answer unless
113
- * it repeats what was already said. Resolves once everything is sent.
114
- *
115
- * When `sendFinal` was provided, progress items flush through `send` and the
116
- * final reply goes through `sendFinal` — keeping their audience tags distinct.
111
+ * The turn succeeded: post pending human-facing text, then a human-lane
112
+ * terminal message when the final adds something. Agent-lane status beats
113
+ * alone still receive a human-lane fallback before the turn closes.
117
114
  */
118
115
  finish(finalText: string): Promise<void>;
119
116
  /**
@@ -125,6 +122,7 @@ export declare class ProgressPoster {
125
122
  private scheduleFlush;
126
123
  private flushNow;
127
124
  private enqueue;
125
+ private enqueueToPerson;
128
126
  private enqueueWith;
129
127
  private cancelFlush;
130
128
  private cancelNotice;