@indigoai-us/hq-cli 5.342.3 → 5.342.5

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.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.342.5] — 2026-10-04
6
+ - Pass explicit hook stdin to bundled shell companions through a real pipe on Windows.
7
+ - Run the paired fast-route benchmark only when its executable or build inputs change.
8
+ - Limit `hq mcp serve` resident preloads to its request handler modules; hosted MCP client dependencies remain in the daemon process.
9
+ - Keep a bot's local identity config aligned with reissued credentials during `hq bot restore --all`, preventing heartbeat identity drift.
10
+
11
+ ## [5.342.4] — 2026-10-04
12
+
5
13
  ## [5.342.3] — 2026-10-04
14
+
15
+ - Release package-use leases after daemon, bot, and mesh-daemon modules load; `hq mcp serve` starts its resident preload only after the first reply is written, so resident CLI processes do not block Desktop updates or slow MCP start-up.
16
+
6
17
  - `hq secrets env` now waits briefly for the company flag snapshot refresh before its synchronous exit, allowing the fast-path gate to use a persisted snapshot on later invocations.
7
18
 
8
19
  ## [5.342.2] — 2026-10-04
@@ -26,6 +26,8 @@ import { hasPromotionHold, promotionHoldSummary } from "../lib/bot/promotion-hol
26
26
  * list (online flag), and rm. Secrets are never printed.
27
27
  */
28
28
  import { promoteLocalBot } from "../lib/bot/promote.js";
29
+ import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
30
+ import { preloadResidentPackageModules, reportResidentPackagePreloadFailure } from "../utils/resident-package-preload.js";
29
31
  import { CLI_UPDATE_DISABLE_ENV, createCliUpdateWatcher, handOffToUpdatedCli } from "../lib/bot/cli-update.js";
30
32
  import { createVaultObjectDownloader } from "../lib/agent-kit/attachment-download.js";
31
33
  import { addBotToCompanies, describeCompanyFailures, removeBotFromCompanies } from "./bot-companies.js";
@@ -854,6 +856,7 @@ export async function runBotWorkers(opts) {
854
856
  }
855
857
  // ── run (resident) ───────────────────────────────────────────────────────────
856
858
  async function runBotResident(nameArg) {
859
+ await releasePackageUseLeaseAfterResidentPreload(() => preloadResidentPackageModules("bot"), reportResidentPackagePreloadFailure, releaseCurrentPackageUseLease);
857
860
  const name = validateBotName(nameArg);
858
861
  const dir = botDir(name);
859
862
  const config = readBotConfig(dir);
@@ -1653,6 +1656,7 @@ async function repairLocalBot(name, api, bot, daemon) {
1653
1656
  catch (err) {
1654
1657
  throw credentialError(err, name);
1655
1658
  }
1659
+ writeBotConfig(dir, { ...config, agentUid: bot.agentUid });
1656
1660
  writeBotCreds(dir, {
1657
1661
  username: reissued.identity.cognitoUsername,
1658
1662
  secret: reissued.identity.secret,
@@ -15,11 +15,14 @@ export function registerCoreDeriveTriggerFactsCommand(core) {
15
15
  .action(async (args = [], _options, commandContext) => {
16
16
  if (!await coreNativeHookHelpersEnabled()) {
17
17
  const root = liveRoot();
18
+ const stdin = process.platform === "win32" ? await readStdin() : undefined;
18
19
  const result = runBundledScript({
19
20
  asset: DERIVE_TRIGGER_FACTS_ASSET,
20
21
  args: commandContext.args.length ? commandContext.args : args,
21
22
  cwd: root,
22
23
  hqRoot: root,
24
+ msysScriptPath: process.platform === "win32",
25
+ ...(stdin !== undefined ? { stdin } : {}),
23
26
  });
24
27
  if (result.code !== 0)
25
28
  process.exit(result.code);
@@ -44,4 +47,10 @@ export function registerCoreDeriveTriggerFactsCommand(core) {
44
47
  });
45
48
  describeCoreCommand(command, { execution: "native", asset: DERIVE_TRIGGER_FACTS_ASSET, root: "live" });
46
49
  }
50
+ async function readStdin() {
51
+ const chunks = [];
52
+ for await (const chunk of process.stdin)
53
+ chunks.push(chunk);
54
+ return Buffer.concat(chunks).toString("utf8");
55
+ }
47
56
  //# sourceMappingURL=core-derive-trigger-facts-command.js.map
@@ -18,6 +18,9 @@ import { type LegacyUnit } from "../lib/daemon/legacy.js";
18
18
  import type { HostApp } from "../lib/daemon/supervisor.js";
19
19
  import { type SyncModeVaultClient } from "./sync-mode.js";
20
20
  import type { RunCommand } from "../lib/service-manager/launchd.js";
21
+ type DaemonRunnerModule = typeof import("../lib/daemon/run.js");
22
+ /** Load the daemon runtime without releasing its lease: it reads package files while resident. */
23
+ export declare function loadDaemonRunner(_release?: () => void, load?: () => Promise<DaemonRunnerModule>): Promise<DaemonRunnerModule["runDaemon"]>;
21
24
  export interface DaemonCommandDeps {
22
25
  env: NodeJS.ProcessEnv;
23
26
  home: string;
@@ -62,4 +65,5 @@ export declare function resolveDaemonHqRoot(flag: string | undefined, where: {
62
65
  home: string;
63
66
  }): string | undefined;
64
67
  export declare function registerDaemonCommand(program: Command, over?: Partial<DaemonCommandDeps>): Command;
68
+ export {};
65
69
  //# sourceMappingURL=daemon.d.ts.map
@@ -35,6 +35,11 @@ import { requireAnywhereRuntime } from "./anywhere-runtime-flag.js";
35
35
  import { hqdRequest, hqdSocketPath, HqdClientError } from "../lib/daemon/protocol.js";
36
36
  import { parseStdinMachineCredentials } from "../lib/daemon/machine-login.js";
37
37
  import { resolveDefaultHqRoot } from "../utils/cognito-session.js";
38
+ /** Load the daemon runtime without releasing its lease: it reads package files while resident. */
39
+ export async function loadDaemonRunner(_release = () => undefined, load = () => import("../lib/daemon/run.js")) {
40
+ const { runDaemon } = await load();
41
+ return runDaemon;
42
+ }
38
43
  class DaemonCommandError extends Error {
39
44
  }
40
45
  /**
@@ -778,7 +783,7 @@ export function registerDaemonCommand(program, over = {}) {
778
783
  .option("--host <app>", "Run as a child of this app (only \"desktop\"), which has handed sync over to the daemon")
779
784
  .action(wrap(async (d, opts) => {
780
785
  const host = parseDaemonHost(opts.host);
781
- const { runDaemon } = await import("../lib/daemon/run.js");
786
+ const runDaemon = await loadDaemonRunner();
782
787
  // Services inherit this environment, so they see the same folder.
783
788
  const env = opts.daemonDir ? { ...d.env, HQ_DAEMON_DIR: opts.daemonDir } : d.env;
784
789
  process.exitCode = await runDaemon({
@@ -20,9 +20,21 @@ export interface McpServeOptions {
20
20
  cwd?: string;
21
21
  /** Per-call hqd budget. Default 120s; policy checks and hq-mcp tools can be slow. */
22
22
  timeoutMs?: number;
23
+ /** Test seam for the resident module preload; production uses the default loader. */
24
+ preloadResidentModules?: (residentPath: "mcp", modules: readonly string[]) => Promise<void>;
25
+ /** Test seam for releasing the process package lease after resident preload. */
26
+ releasePackageUseLease?: () => void;
23
27
  }
24
28
  /** HQ root for prompts: HQ_ROOT, else the ~/.hq/root pointer written by `hq install --global`. */
25
29
  export declare function readHqRoot(env: NodeJS.ProcessEnv, home: string): string | null;
30
+ type McpToolsModule = typeof import("../lib/mcp/tools.js");
31
+ type McpPromptsModule = typeof import("../lib/mcp/prompts.js");
32
+ /** Load request handlers required to answer MCP requests. */
33
+ export declare function loadMcpPackageHandlers(loadTools?: () => Promise<McpToolsModule>, loadPrompts?: () => Promise<McpPromptsModule>): Promise<{
34
+ tools: McpToolsModule;
35
+ prompts: McpPromptsModule;
36
+ }>;
26
37
  /** Run the server until input ends. Resolves when stdin closes. */
27
38
  export declare function runMcpServe(opts?: McpServeOptions): Promise<void>;
39
+ export {};
28
40
  //# sourceMappingURL=mcp-serve.d.ts.map
@@ -18,6 +18,8 @@ import { CLI_VERSION } from "../cli-version.js";
18
18
  import { HQ_ANYWHERE_RUNTIME_FLAG } from "../lib/anywhere-runtime-flag-key.js";
19
19
  import { anywhereRuntimeEnabledFromSnapshot } from "../lib/anywhere-runtime-fast-flag.js";
20
20
  import { createLineSplitter, HqdClientError, hqdRequest, hqdSocketPath } from "../lib/daemon/protocol.js";
21
+ import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload } from "../utils/package-use-lease.js";
22
+ import { preloadResidentPackageModules, reportResidentPackagePreloadFailure, RESIDENT_PACKAGE_MODULES } from "../utils/resident-package-preload.js";
21
23
  export const MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
22
24
  const UNREACHABLE_CODES = new Set(["ENOENT", "ECONNREFUSED", "ECONNRESET", "EPIPE", "ETIMEDOUT", "ESOCKET", "EACCES"]);
23
25
  /** HQ root for prompts: HQ_ROOT, else the ~/.hq/root pointer written by `hq install --global`. */
@@ -39,8 +41,12 @@ function toolError(code, message) {
39
41
  const error = { code, message };
40
42
  return { content: [{ type: "text", text: JSON.stringify({ error }, null, 2) }], structuredContent: { error }, isError: true };
41
43
  }
44
+ /** Load request handlers required to answer MCP requests. */
45
+ export async function loadMcpPackageHandlers(loadTools = () => import("../lib/mcp/tools.js"), loadPrompts = () => import("../lib/mcp/prompts.js")) {
46
+ return Promise.all([loadTools(), loadPrompts()]).then(([tools, prompts]) => ({ tools, prompts }));
47
+ }
42
48
  /** Run the server until input ends. Resolves when stdin closes. */
43
- export function runMcpServe(opts = {}) {
49
+ export async function runMcpServe(opts = {}) {
44
50
  const input = opts.input ?? process.stdin;
45
51
  const output = opts.output ?? process.stdout;
46
52
  const env = opts.env ?? process.env;
@@ -51,15 +57,33 @@ export function runMcpServe(opts = {}) {
51
57
  if (!anywhereRuntimeEnabledFromSnapshot(env)) {
52
58
  process.stderr.write(`HQ Anywhere is not enabled. Enable the ${HQ_ANYWHERE_RUNTIME_FLAG} flag to use hq mcp serve.\n`);
53
59
  process.exitCode = 1;
54
- return Promise.resolve();
60
+ return;
55
61
  }
56
- const send = (msg) => output.write(`${JSON.stringify({ jsonrpc: "2.0", ...msg })}\n`);
62
+ const { tools, prompts } = await loadMcpPackageHandlers();
63
+ const preload = (residentPath, modules) => opts.preloadResidentModules
64
+ ? opts.preloadResidentModules(residentPath, modules)
65
+ : preloadResidentPackageModules(residentPath);
66
+ let residentPreloadStarted = false;
67
+ const startResidentPreload = () => {
68
+ if (residentPreloadStarted)
69
+ return;
70
+ residentPreloadStarted = true;
71
+ void releasePackageUseLeaseAfterResidentPreload(() => preload("mcp", RESIDENT_PACKAGE_MODULES.mcp), reportResidentPackagePreloadFailure, opts.releasePackageUseLease ?? releaseCurrentPackageUseLease).catch(() => {
72
+ process.exitCode = process.exitCode ?? 1;
73
+ process.emitWarning("hq-cli resident package preload completion failed (class=Error, module=unknown)");
74
+ });
75
+ };
76
+ const send = (msg) => {
77
+ const response = `${JSON.stringify({ jsonrpc: "2.0", ...msg })}\n`;
78
+ if (residentPreloadStarted)
79
+ return output.write(response);
80
+ return output.write(response, startResidentPreload);
81
+ };
57
82
  const reply = (id, result) => send({ id, result });
58
83
  const replyError = (id, code, message) => send({ id, error: { code, message } });
59
84
  const callTool = async (params) => {
60
- const { MCP_TOOLS } = await import("../lib/mcp/tools.js");
61
85
  const name = params.name;
62
- if (typeof name !== "string" || !MCP_TOOLS.some((t) => t.name === name)) {
86
+ if (typeof name !== "string" || !tools.MCP_TOOLS.some((t) => t.name === name)) {
63
87
  return toolError("unknown_tool", `unknown tool ${String(name)}`);
64
88
  }
65
89
  const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
@@ -95,22 +119,21 @@ export function runMcpServe(opts = {}) {
95
119
  reply(id, {});
96
120
  return;
97
121
  case "tools/list":
98
- reply(id, { tools: (await import("../lib/mcp/tools.js")).MCP_TOOLS });
122
+ reply(id, { tools: tools.MCP_TOOLS });
99
123
  return;
100
124
  case "tools/call":
101
125
  reply(id, await callTool(params));
102
126
  return;
103
127
  case "prompts/list":
104
- reply(id, { prompts: (await import("../lib/mcp/prompts.js")).MCP_PROMPTS });
128
+ reply(id, { prompts: prompts.MCP_PROMPTS });
105
129
  return;
106
130
  case "prompts/get": {
107
- const { getPrompt, PromptNotFoundError } = await import("../lib/mcp/prompts.js");
108
131
  const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
109
132
  try {
110
- reply(id, getPrompt(String(params.name), args, readHqRoot(env, home)));
133
+ reply(id, prompts.getPrompt(String(params.name), args, readHqRoot(env, home)));
111
134
  }
112
135
  catch (err) {
113
- if (!(err instanceof PromptNotFoundError))
136
+ if (!(err instanceof prompts.PromptNotFoundError))
114
137
  throw err;
115
138
  replyError(id, -32602, err.message);
116
139
  }
@@ -540,7 +540,7 @@ export async function runBot(deps) {
540
540
  configuredAgentUid: config.agentUid,
541
541
  ...(err.tokenAgentUid ? { tokenAgentUid: err.tokenAgentUid } : {}),
542
542
  agentUidSource: deps.agentUidSource ?? "config_file",
543
- credsSource: deps.credsSource ?? "default",
543
+ credsSource: deps.credsSource ?? "explicit",
544
544
  });
545
545
  break;
546
546
  }
package/dist/main.js CHANGED
@@ -53,6 +53,7 @@ import { emitPlanLimitNag } from "./lib/plan-limit-nag.js";
53
53
  import { kickFlagRegistryReadiness } from "./lib/flag-registry.js";
54
54
  import { isPackageRootResolutionError, packageRootCaptureContext, } from "./utils/package-root-diagnostics.js";
55
55
  import { registerCommandsWithRecovery } from "./startup-registration.js";
56
+ import { releasePackageUseLeaseAfterCommandLoad } from "./utils/package-use-lease.js";
56
57
  import { installTreeTornCaptureContext, installTreeTornStderrLine, isInstallTreeTornError, } from "./utils/install-tree-torn.js";
57
58
  import { isVaultAccessDeniedError, vaultAccessDeniedMessage, } from "./utils/vault-access-denied-error.js";
58
59
  import { fallbackOperatorMessage, unexpectedCliErrorMessage } from "./utils/unexpected-cli-error.js";
@@ -261,6 +262,7 @@ export async function runCli() {
261
262
  reexecStatus = registration.reexecStatus;
262
263
  return;
263
264
  }
265
+ await releasePackageUseLeaseAfterCommandLoad(process.argv);
264
266
  await program.parseAsync();
265
267
  maybeShowFirstSyncNextStep();
266
268
  }
@@ -16,6 +16,12 @@ export declare function packageUseUpdateRequestPath(prefix: string, stateDirecto
16
16
  declare function waitForUpdateRequest(requestPath: string, timeoutMs: number): void;
17
17
  export declare function acquirePackageUseLease(prefix: string, hqVersion: string, stateDirectory?: string): PackageUseLease;
18
18
  export declare function acquireCurrentPackageUseLease(): PackageUseLease | null;
19
+ /** Release this process's package-use lease after a long-running command has loaded its package modules. */
20
+ export declare function releaseCurrentPackageUseLease(): void;
21
+ /** The lease remains held through command execution and is released on process exit. */
22
+ export declare function releasePackageUseLeaseAfterCommandLoad(_argv?: readonly string[], _release?: () => void): void;
23
+ /** Release only after a command-specific preload succeeds; failures are reported and keep the lease. */
24
+ export declare function releasePackageUseLeaseAfterResidentPreload(preload: () => Promise<void>, reportFailure: (error: unknown) => void, release?: () => void): Promise<void>;
19
25
  /** True only when a valid update request belongs to a live updater process. */
20
26
  export declare function packageUseUpdatePending(prefix: string, stateDirectory?: string): boolean;
21
27
  /** True while the desktop updater owns the per-prefix update request. */
@@ -12,6 +12,7 @@ export class PackageUseUpdatePendingError extends Error {
12
12
  this.name = "PackageUseUpdatePendingError";
13
13
  }
14
14
  }
15
+ let currentPackageUseLease = null;
15
16
  function errorLabel(error) {
16
17
  if (error && typeof error === "object") {
17
18
  const value = error;
@@ -195,7 +196,29 @@ export function acquireCurrentPackageUseLease() {
195
196
  if (nodeModules > 0 && parts[nodeModules - 1] !== "lib")
196
197
  return null;
197
198
  }
198
- return acquirePackageUseLease(prefix, pkg.version);
199
+ const lease = acquirePackageUseLease(prefix, pkg.version);
200
+ currentPackageUseLease = lease;
201
+ return lease;
202
+ }
203
+ /** Release this process's package-use lease after a long-running command has loaded its package modules. */
204
+ export function releaseCurrentPackageUseLease() {
205
+ currentPackageUseLease?.release();
206
+ currentPackageUseLease = null;
207
+ }
208
+ /** The lease remains held through command execution and is released on process exit. */
209
+ export function releasePackageUseLeaseAfterCommandLoad(_argv = process.argv, _release = releaseCurrentPackageUseLease) {
210
+ // The package-use lease's process-exit listener owns its normal release.
211
+ }
212
+ /** Release only after a command-specific preload succeeds; failures are reported and keep the lease. */
213
+ export async function releasePackageUseLeaseAfterResidentPreload(preload, reportFailure, release = releaseCurrentPackageUseLease) {
214
+ try {
215
+ await preload();
216
+ }
217
+ catch (error) {
218
+ reportFailure(error);
219
+ return;
220
+ }
221
+ release();
199
222
  }
200
223
  /** True only when a valid update request belongs to a live updater process. */
201
224
  export function packageUseUpdatePending(prefix, stateDirectory) {
@@ -0,0 +1,13 @@
1
+ export type ResidentPackagePath = "bot" | "mcp";
2
+ /** Package imports reachable lazily from each resident command's runtime graph. */
3
+ export declare const RESIDENT_PACKAGE_MODULES: Record<ResidentPackagePath, readonly string[]>;
4
+ export declare class ResidentPackagePreloadError extends Error {
5
+ readonly moduleSpecifier: string;
6
+ readonly errorClass: string;
7
+ constructor(moduleSpecifier: string, errorClass: string);
8
+ }
9
+ /** Report only bounded runtime metadata; never include an exception message or path. */
10
+ export declare function reportResidentPackagePreloadFailure(error: unknown): void;
11
+ /** Resolve every lazy package module before a resident command releases its lease. */
12
+ export declare function preloadResidentPackageModules(residentPath: ResidentPackagePath, load?: () => Promise<unknown>): Promise<void>;
13
+ //# sourceMappingURL=resident-package-preload.d.ts.map
@@ -0,0 +1,57 @@
1
+ /** Package imports reachable lazily from each resident command's runtime graph. */
2
+ export const RESIDENT_PACKAGE_MODULES = {
3
+ bot: ["../lib/flag-registry.js", "../commands/access.js", "../sentry.js", "open"],
4
+ mcp: ["../lib/mcp/tools.js", "../lib/mcp/prompts.js"],
5
+ };
6
+ const LOADERS = {
7
+ bot: [
8
+ () => import("../lib/flag-registry.js"),
9
+ () => import("../commands/access.js"),
10
+ () => import("../sentry.js"),
11
+ () => import("open"),
12
+ ],
13
+ mcp: [
14
+ () => import("../lib/mcp/tools.js"),
15
+ () => import("../lib/mcp/prompts.js"),
16
+ ],
17
+ };
18
+ export class ResidentPackagePreloadError extends Error {
19
+ moduleSpecifier;
20
+ errorClass;
21
+ constructor(moduleSpecifier, errorClass) {
22
+ super("Resident package module preload failed");
23
+ this.moduleSpecifier = moduleSpecifier;
24
+ this.errorClass = errorClass;
25
+ this.name = "ResidentPackagePreloadError";
26
+ }
27
+ }
28
+ function boundedErrorClass(error) {
29
+ const value = error && typeof error === "object"
30
+ ? error
31
+ : {};
32
+ const label = typeof value.code === "string" ? value.code : typeof value.name === "string" ? value.name : "Error";
33
+ return /^[A-Za-z][A-Za-z0-9_]{0,39}$/.test(label) ? label : "Error";
34
+ }
35
+ /** Report only bounded runtime metadata; never include an exception message or path. */
36
+ export function reportResidentPackagePreloadFailure(error) {
37
+ const failure = error instanceof ResidentPackagePreloadError
38
+ ? error
39
+ : new ResidentPackagePreloadError("unknown", boundedErrorClass(error));
40
+ process.stderr.write(`hq-cli resident package preload failed (class=${failure.errorClass}, module=${failure.moduleSpecifier})\n`);
41
+ }
42
+ /** Resolve every lazy package module before a resident command releases its lease. */
43
+ export async function preloadResidentPackageModules(residentPath, load) {
44
+ if (load) {
45
+ await load();
46
+ return;
47
+ }
48
+ await Promise.all(LOADERS[residentPath].map(async (loader, index) => {
49
+ try {
50
+ await loader();
51
+ }
52
+ catch (error) {
53
+ throw new ResidentPackagePreloadError(RESIDENT_PACKAGE_MODULES[residentPath][index] ?? "unknown", boundedErrorClass(error));
54
+ }
55
+ }));
56
+ }
57
+ //# sourceMappingURL=resident-package-preload.js.map
@@ -168,8 +168,8 @@ export function runBundledScript(opts) {
168
168
  try {
169
169
  if (opts.stdin !== undefined) {
170
170
  // Some scaffold scripts reopen /dev/stdin by pathname. Node's spawnSync
171
- // input pipe cannot be reopened through /dev/stdin on Linux, so capture
172
- // mode supplies a private regular file descriptor to preserve that ABI.
171
+ // input pipe cannot be reopened through /dev/stdin on Git Bash or Linux,
172
+ // so supply a private regular file descriptor to preserve that ABI.
173
173
  const sharedMemory = process.platform === "linux" && fs.existsSync("/dev/shm")
174
174
  ? "/dev/shm"
175
175
  : os.tmpdir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.342.3",
3
+ "version": "5.342.5",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {