@indigoai-us/hq-cli 5.345.201 → 5.345.202

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,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.202] — 2026-11-04
6
+ - The long-running `hq daemon run` and `hq mesh daemon run` processes no longer start Sentry. On some Macs, Sentry in these processes kept opening connections after network changes until the machine ran out of ports, so Claude and HQ lost internet access while other apps kept working. Short-lived CLI commands still report errors to Sentry, and the daemons keep their local logs.
7
+ - The Executive (meeting rider, action-item lanes and source report DMs) is now off unless `executiveMeetingRider.enabled` is set to `true` in the HQ menubar settings file. In 5.345.200 it was on by default for every setup bot.
8
+ - Native command guards on Windows now resolve tools with the same Git directories and home bin precedence as the Bash fallback, preventing a plain Windows PATH from selecting System32 timeout or missing Git tools. Added exact CLI command-argument parity coverage and portable temporary-path fixtures.
9
+ - Port the complete policy reminder injector to Rust, including scope resolution, trigger matching, relevance ranking, output ceilings, deferred policies, cadence and seen ledgers, and receipt-backed enforcement. Shadow runs use isolated state so the Bash reference sees the original session history. Add a pinned full-injector corpus using staging regressions, release-shipped core policies and reproducibly generated synthetic policy/event payloads, with byte comparisons of output and state after each event. No rollout flags change.
10
+ - Match Bash locale collation when indexing policy files, including Git Bash Windows collation and the macOS non-ASCII fallback order. Index format 3 rebuilds old cache files.
11
+
5
12
  ## [5.345.201] — 2026-11-04
6
13
  - `hq deploy domains buy` accepts a registrar price in `usd` or `USD`. Search shows that price as US dollars, and renew accepts the same code. A price in another currency is still refused, and the message shows the currency code the server sent.
7
14
  - The Executive sends source reports to its owner when a meeting ends and one final threaded report after all source lanes finish. Delivery receipts suppress normal duplicates and retain reports during the ten-minute cooldown. Recovery retries an intent without a sent receipt; a crash after sending but before recording the receipt can resend that report.
@@ -1,4 +1,10 @@
1
- /** Shared Executive identity and the desktop's default-on rider preference. */
1
+ /**
2
+ * Shared Executive identity and its opt-in switch.
3
+ *
4
+ * The Executive (meeting rider, signal-to-lane, source report DMs) is off
5
+ * unless ~/.hq/menubar.json sets executiveMeetingRider.enabled to true. It
6
+ * stays internal until the live end-to-end proof passes.
7
+ */
2
8
  import * as fs from "node:fs";
3
9
  import * as path from "node:path";
4
10
  import * as os from "node:os";
@@ -7,15 +13,17 @@ import { tailBotEvents } from "./events.js";
7
13
  export function executiveRiderEnabled(home = os.homedir()) {
8
14
  try {
9
15
  const settings = JSON.parse(fs.readFileSync(path.join(home, ".hq/menubar.json"), "utf8"));
10
- return settings?.executiveMeetingRider?.enabled !== false;
16
+ return settings?.executiveMeetingRider?.enabled === true;
11
17
  }
12
- catch (err) {
13
- return err.code === "ENOENT";
18
+ catch {
19
+ return false;
14
20
  }
15
21
  }
16
22
  export function isExecutiveBot(config, home = os.homedir()) {
17
23
  if (effectiveBotKind(config) !== "personal")
18
24
  return false;
25
+ if (!executiveRiderEnabled(home))
26
+ return false;
19
27
  let name = "setup";
20
28
  try {
21
29
  const value = JSON.parse(fs.readFileSync(path.join(home, ".hq/executive.json"), "utf8"));
@@ -7,9 +7,10 @@ export declare function ensureSignalToLaneJobs(dir: string, config: BotConfig, c
7
7
  /** Only new locally extracted signals reach here; cloud upload/garden is separate. */
8
8
  export declare function emitExecutiveSignalWritten(hqRoot: string, company: string, payload: Record<string, string>, root?: string, home?: string): boolean;
9
9
  /** Persist intent before publishing a new signal file; ordinary redo creates none. */
10
- export declare function prepareSignalPublication(hqRoot: string, company: string, payload: Record<string, string>): void;
10
+ /** Records nothing while no opted-in Executive serves the company, so no backlog builds up. */
11
+ export declare function prepareSignalPublication(hqRoot: string, company: string, payload: Record<string, string>, root?: string, home?: string): void;
11
12
  /** A pre-append crash retries; a post-append crash uses the event journal's dedupe. */
12
- export declare function flushSignalPublications(hqRoot: string, company: string, emit?: typeof emitExecutiveSignalWritten): void;
13
+ export declare function flushSignalPublications(hqRoot: string, company: string, emit?: typeof emitExecutiveSignalWritten, root?: string, home?: string): void;
13
14
  type Create = (input: {
14
15
  hqRoot: string;
15
16
  company: string;
@@ -41,10 +41,10 @@ export function ensureSignalToLaneJobs(dir, config, companies = signalLaneCompan
41
41
  return jobs.filter(job => job.context?.jobKind === SIGNAL_TO_LANE_JOB_KIND);
42
42
  });
43
43
  }
44
- /** Only new locally extracted signals reach here; cloud upload/garden is separate. */
45
- export function emitExecutiveSignalWritten(hqRoot, company, payload, root = botsRoot(), home) {
44
+ /** The opted-in Executive bot dir serving this HQ root and company, if any. */
45
+ function executiveFor(hqRoot, company, root = botsRoot(), home) {
46
46
  if (!fs.existsSync(root))
47
- return false;
47
+ return null;
48
48
  for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
49
49
  if (!entry.isDirectory())
50
50
  continue;
@@ -54,10 +54,17 @@ export function emitExecutiveSignalWritten(hqRoot, company, payload, root = bots
54
54
  continue;
55
55
  if (!signalLaneCompanies(config).includes(company))
56
56
  continue;
57
- emitBotEvent(dir, "signal.written", { ...payload, company }, new Date(), prior => prior.event === "signal.written" && prior.payload.company === company && prior.payload.signal_id === payload.signal_id);
58
- return true;
57
+ return dir;
59
58
  }
60
- return false;
59
+ return null;
60
+ }
61
+ /** Only new locally extracted signals reach here; cloud upload/garden is separate. */
62
+ export function emitExecutiveSignalWritten(hqRoot, company, payload, root = botsRoot(), home) {
63
+ const dir = executiveFor(hqRoot, company, root, home);
64
+ if (!dir)
65
+ return false;
66
+ emitBotEvent(dir, "signal.written", { ...payload, company }, new Date(), prior => prior.event === "signal.written" && prior.payload.company === company && prior.payload.signal_id === payload.signal_id);
67
+ return true;
61
68
  }
62
69
  function publicationDir(hqRoot, company) {
63
70
  if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(company))
@@ -65,8 +72,11 @@ function publicationDir(hqRoot, company) {
65
72
  return path.join(hqRoot, "workspace", "signal-publications", company);
66
73
  }
67
74
  /** Persist intent before publishing a new signal file; ordinary redo creates none. */
68
- export function prepareSignalPublication(hqRoot, company, payload) {
75
+ /** Records nothing while no opted-in Executive serves the company, so no backlog builds up. */
76
+ export function prepareSignalPublication(hqRoot, company, payload, root = botsRoot(), home) {
69
77
  const dir = publicationDir(hqRoot, company);
78
+ if (!executiveFor(hqRoot, company, root, home))
79
+ return;
70
80
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
71
81
  const file = path.join(dir, createHash("sha256").update(payload.signal_id).digest("hex") + ".json");
72
82
  const tmp = `${file}.${randomUUID()}.tmp`;
@@ -74,12 +84,25 @@ export function prepareSignalPublication(hqRoot, company, payload) {
74
84
  fs.renameSync(tmp, file);
75
85
  }
76
86
  /** A pre-append crash retries; a post-append crash uses the event journal's dedupe. */
77
- export function flushSignalPublications(hqRoot, company, emit = emitExecutiveSignalWritten) {
87
+ export function flushSignalPublications(hqRoot, company, emit = emitExecutiveSignalWritten, root = botsRoot(), home) {
78
88
  const dir = publicationDir(hqRoot, company);
79
89
  if (!fs.existsSync(dir))
80
90
  return;
91
+ // With the Executive off, drop stale intents so a later opt-in does not
92
+ // turn past action items into a burst of lanes.
93
+ const optedIn = executiveFor(hqRoot, company, root, home) !== null;
81
94
  for (const name of fs.readdirSync(dir).filter(name => /^[a-f0-9]{64}\.json$/.test(name))) {
82
95
  const file = path.join(dir, name);
96
+ if (!optedIn) {
97
+ try {
98
+ fs.unlinkSync(file);
99
+ }
100
+ catch (error) {
101
+ if (error.code !== "ENOENT")
102
+ throw error;
103
+ }
104
+ continue;
105
+ }
83
106
  let payload;
84
107
  try {
85
108
  payload = JSON.parse(fs.readFileSync(file, "utf8"));
package/dist/main.js CHANGED
@@ -61,7 +61,7 @@ import { packageRoot } from "./utils/hq-roots.js";
61
61
  import { classifyModuleNotFound, installTreeTornCaptureContext, installTreeTornStderrLine, isInstallTreeTornError, } from "./utils/install-tree-torn.js";
62
62
  import { isVaultAccessDeniedError, vaultAccessDeniedMessage, } from "./utils/vault-access-denied-error.js";
63
63
  import { fallbackOperatorMessage, unexpectedCliErrorMessage } from "./utils/unexpected-cli-error.js";
64
- import { runGlobalNetworkLifecycle } from "./utils/cli-network-lifecycle.js";
64
+ import { isHqDaemonRunCommand, runGlobalNetworkLifecycle, runSentryInitialization, } from "./utils/cli-network-lifecycle.js";
65
65
  import * as os from "node:os";
66
66
  import { adoptAgentIdentityEnv } from "./lib/agent-kit/adopt-identity.js";
67
67
  import { agentKitPaths } from "./lib/agent-kit/paths.js";
@@ -131,7 +131,7 @@ let activeForegroundNetworkBudget;
131
131
  */
132
132
  const RUNNING_ENTRY_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "index.js");
133
133
  installStreamErrorBoundary();
134
- runGlobalNetworkLifecycle(process.argv, () => initSentry());
134
+ runSentryInitialization(process.argv, () => initSentry());
135
135
  const program = new Command();
136
136
  program
137
137
  .name("hq")
@@ -169,7 +169,7 @@ program.hook("preAction", async () => {
169
169
  export async function runCli() {
170
170
  const xpcServiceName = process.env.XPC_SERVICE_NAME ?? "";
171
171
  const launchedByHqAgent = xpcServiceName.startsWith("ai.indigo.") || xpcServiceName.startsWith("ai.getindigo.");
172
- const isDaemonEntryPoint = process.argv[2] === "daemon" && process.argv[3] === "run";
172
+ const isDaemonEntryPoint = isHqDaemonRunCommand(process.argv);
173
173
  const isBotHotPath = process.argv[2] === "bot" && process.argv[3] === "run";
174
174
  if (process.platform === "darwin" && typeof process.getuid === "function" &&
175
175
  !launchedByHqAgent && !isDaemonEntryPoint && !isBotHotPath) {
@@ -1,3 +1,12 @@
1
+ export declare function isHqDaemonRunCommand(argv: readonly string[]): boolean;
2
+ /**
3
+ * Resident daemons keep their own bounded local logs. Do not initialize the
4
+ * process-wide Sentry transport in these commands: they can live across many
5
+ * network changes, while the SDK is designed around one short CLI invocation.
6
+ */
7
+ export declare function isLongLivedDaemonCommand(argv: readonly string[]): boolean;
8
+ /** Suppress only Sentry startup; every other global lifecycle operation still runs. */
9
+ export declare function runSentryInitialization<T>(argv: readonly string[], operation: () => T): T | undefined;
1
10
  export declare function isMonitorFastPathCommand(argv: readonly string[]): boolean;
2
11
  /** The commit-msg hook runs `hq fingerprint self --offline`; it must stay fast and offline. */
3
12
  export declare function isOfflineFingerprintCommand(argv: readonly string[]): boolean;
@@ -1,4 +1,27 @@
1
1
  const MONITOR_HIDDEN_NETWORK_BYPASS = new Set(["drain", "wait", "enabled", "_run"]);
2
+ function commandArgv(argv) {
3
+ return argv.slice(2).filter((arg) => arg !== "--verbose");
4
+ }
5
+ export function isHqDaemonRunCommand(argv) {
6
+ const command = commandArgv(argv);
7
+ return command[0] === "daemon" && command[1] === "run";
8
+ }
9
+ /**
10
+ * Resident daemons keep their own bounded local logs. Do not initialize the
11
+ * process-wide Sentry transport in these commands: they can live across many
12
+ * network changes, while the SDK is designed around one short CLI invocation.
13
+ */
14
+ export function isLongLivedDaemonCommand(argv) {
15
+ const command = commandArgv(argv);
16
+ return (isHqDaemonRunCommand(argv) ||
17
+ (command[0] === "mesh" && command[1] === "daemon" && command[2] === "run"));
18
+ }
19
+ /** Suppress only Sentry startup; every other global lifecycle operation still runs. */
20
+ export function runSentryInitialization(argv, operation) {
21
+ if (isLongLivedDaemonCommand(argv))
22
+ return undefined;
23
+ return operation();
24
+ }
2
25
  export function isMonitorFastPathCommand(argv) {
3
26
  return argv[2] === "monitor" && MONITOR_HIDDEN_NETWORK_BYPASS.has(argv[3] ?? "");
4
27
  }
@@ -8,8 +31,9 @@ export function isOfflineFingerprintCommand(argv) {
8
31
  }
9
32
  /** Skip process-wide network lifecycle work for hooks and detached monitor supervisors. */
10
33
  export function runGlobalNetworkLifecycle(argv, operation) {
11
- if (isMonitorFastPathCommand(argv) || isOfflineFingerprintCommand(argv))
34
+ if (isMonitorFastPathCommand(argv) || isOfflineFingerprintCommand(argv)) {
12
35
  return undefined;
36
+ }
13
37
  return operation();
14
38
  }
15
39
  //# sourceMappingURL=cli-network-lifecycle.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.201",
3
+ "version": "5.345.202",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -48,7 +48,8 @@
48
48
  "prepare": "husky || true",
49
49
  "check:no-hermes": "bash scripts/check-no-hermes.sh",
50
50
  "prepack": "node scripts/hook-engine-manifest.mjs",
51
- "postpublish": "node scripts/hook-engine-manifest.mjs restore"
51
+ "postpublish": "node scripts/hook-engine-manifest.mjs restore",
52
+ "build:ci": "node scripts/prepare-fixture-privacy-baseline.mjs && pnpm run build"
52
53
  },
53
54
  "dependencies": {
54
55
  "@aws-sdk/client-iot-data-plane": "^3.1096.0",
@@ -116,11 +117,11 @@
116
117
  "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
117
118
  },
118
119
  "optionalDependencies": {
119
- "@indigoai-us/hq-hook-win32-x64": "5.345.201",
120
- "@indigoai-us/hq-hook-win32-arm64": "5.345.201",
121
- "@indigoai-us/hq-hook-darwin-x64": "5.345.201",
122
- "@indigoai-us/hq-hook-darwin-arm64": "5.345.201",
123
- "@indigoai-us/hq-hook-linux-x64": "5.345.201",
124
- "@indigoai-us/hq-hook-linux-arm64": "5.345.201"
120
+ "@indigoai-us/hq-hook-win32-x64": "5.345.202",
121
+ "@indigoai-us/hq-hook-win32-arm64": "5.345.202",
122
+ "@indigoai-us/hq-hook-darwin-x64": "5.345.202",
123
+ "@indigoai-us/hq-hook-darwin-arm64": "5.345.202",
124
+ "@indigoai-us/hq-hook-linux-x64": "5.345.202",
125
+ "@indigoai-us/hq-hook-linux-arm64": "5.345.202"
125
126
  }
126
127
  }