@indigoai-us/hq-cli 5.309.0 → 5.310.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 (84) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
  3. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
  4. package/dist/command-catalog.generated.d.ts +30 -1
  5. package/dist/command-catalog.generated.js +37 -1
  6. package/dist/commands/agents.js +6 -0
  7. package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -10
  8. package/dist/commands/core-checkpoint-gate-setting.js +3 -3
  9. package/dist/commands/core-dm-bind-native.d.ts +32 -0
  10. package/dist/commands/core-dm-bind-native.js +668 -0
  11. package/dist/commands/core-native-utilities.d.ts +2 -1
  12. package/dist/commands/core-native-utilities.js +4 -2
  13. package/dist/commands/core-ontology.d.ts +1 -9
  14. package/dist/commands/core-ontology.js +4 -4
  15. package/dist/commands/core-policy.d.ts +0 -3
  16. package/dist/commands/core-policy.js +3 -3
  17. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
  18. package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
  19. package/dist/commands/core.js +6 -0
  20. package/dist/commands/files.js +1 -1
  21. package/dist/commands/index-cmd.js +1 -1
  22. package/dist/commands/integrations-api.d.ts +9 -4
  23. package/dist/commands/integrations-api.js +9 -6
  24. package/dist/commands/integrations-manage.js +11 -3
  25. package/dist/commands/integrations.d.ts +8 -0
  26. package/dist/commands/integrations.js +83 -0
  27. package/dist/commands/lanes-checks.d.ts +0 -2
  28. package/dist/commands/lanes-checks.js +2 -2
  29. package/dist/commands/lanes.d.ts +0 -11
  30. package/dist/commands/lanes.js +6 -6
  31. package/dist/commands/mcp-registration.d.ts +5 -11
  32. package/dist/commands/mcp-registration.js +2 -6
  33. package/dist/lib/core-utils/sentry-report.d.ts +5 -11
  34. package/dist/lib/core-utils/sentry-report.js +2 -2
  35. package/dist/lib/db/local.d.ts +0 -5
  36. package/dist/lib/db/local.js +1 -1
  37. package/dist/lib/db/migrate.d.ts +0 -12
  38. package/dist/lib/db/migrate.js +5 -5
  39. package/dist/lib/db/sql.d.ts +0 -10
  40. package/dist/lib/db/sql.js +4 -8
  41. package/dist/lib/doctor/checks/codex-wiring.js +1 -33
  42. package/dist/lib/doctor/checks/grok-wiring.js +1 -34
  43. package/dist/lib/doctor/deep/effects.js +1 -3
  44. package/dist/lib/doctor/deep/sandbox.js +1 -3
  45. package/dist/lib/doctor/fixtures/schema.js +1 -3
  46. package/dist/lib/doctor/shared.d.ts +8 -0
  47. package/dist/lib/doctor/shared.js +38 -0
  48. package/dist/lib/lanes/autopilot.js +6 -4
  49. package/dist/lib/monitor.d.ts +2 -6
  50. package/dist/lib/monitor.js +5 -5
  51. package/dist/lib/report-publish.d.ts +1 -10
  52. package/dist/lib/report-publish.js +3 -9
  53. package/dist/lib/search-index/index.d.ts +7 -0
  54. package/dist/lib/search-index/index.js +21 -2
  55. package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +5 -0
  56. package/dist/lib/search-index/qmd-exit-diagnostics.js +40 -0
  57. package/dist/lib/service-manager/launchd.d.ts +0 -7
  58. package/dist/lib/service-manager/launchd.js +2 -2
  59. package/dist/main.js +7 -2
  60. package/dist/startup-registration.js +9 -1
  61. package/dist/utils/client-health.d.ts +0 -4
  62. package/dist/utils/client-health.js +2 -2
  63. package/dist/utils/feedback-diagnostics.d.ts +2 -1
  64. package/dist/utils/feedback-log-bundle.d.ts +0 -25
  65. package/dist/utils/feedback-log-bundle.js +5 -5
  66. package/dist/utils/feedback-logs.d.ts +4 -38
  67. package/dist/utils/feedback-logs.js +7 -7
  68. package/dist/utils/feedback-screenshots.d.ts +0 -1
  69. package/dist/utils/feedback-screenshots.js +1 -1
  70. package/dist/utils/install-tree-torn.d.ts +19 -13
  71. package/dist/utils/install-tree-torn.js +77 -2
  72. package/dist/utils/local-files-overview.d.ts +0 -6
  73. package/dist/utils/local-files-overview.js +1 -1
  74. package/dist/utils/qmd-model-download-error.d.ts +0 -8
  75. package/dist/utils/qmd-model-download-error.js +1 -1
  76. package/dist/utils/qmd-query-document-error.d.ts +0 -6
  77. package/dist/utils/qmd-query-document-error.js +1 -1
  78. package/dist/utils/scaffold-fast-import-guard.d.ts +0 -1
  79. package/dist/utils/scaffold-fast-import-guard.js +1 -1
  80. package/dist/utils/scaffold-sibling-references.d.ts +3 -2
  81. package/dist/utils/sentry-fingerprint.js +17 -0
  82. package/dist/utils/version-gate.d.ts +4 -0
  83. package/dist/utils/version-gate.js +13 -0
  84. package/package.json +1 -1
@@ -4,15 +4,12 @@ export declare const MONITOR_FLAG = "monitor.enabled";
4
4
  export declare const MONITOR_FLAG_DEFAULT = false;
5
5
  export declare const MONITOR_DEFAULT_TIMEOUT_MS: number;
6
6
  export declare const MONITOR_DEFAULT_CHECKIN_MS: number;
7
- export declare const MONITOR_PROMPT_CACHE_TTL_MS: number;
8
7
  export declare const MONITOR_BATCH_WINDOW_MS = 200;
9
8
  export declare const MONITOR_POLL_INTERVAL_MS = 1000;
10
9
  export declare const MONITOR_RATE_WINDOW_MS = 10000;
11
10
  /** Sustained stdout floods stop after 3 windows; the per-window cap can be overridden with HQ_MONITOR_MAX_EVENTS_PER_WINDOW. */
12
11
  export declare const MONITOR_MAX_EVENTS_PER_WINDOW = 60;
13
12
  export declare const MONITOR_MAX_FLOOD_WINDOWS = 3;
14
- export declare const MONITOR_LOCK_STALE_MS = 30000;
15
- export declare const MONITOR_MAX_TIMER_DELAY_MS = 2147000000;
16
13
  export type MonitorTarget = {
17
14
  kind: "lane";
18
15
  laneId: string;
@@ -38,7 +35,7 @@ export interface NearestMonitorRuntime {
38
35
  export interface MonitorRuntimeIdentity extends NearestMonitorRuntime {
39
36
  starttime: number;
40
37
  }
41
- export interface MonitorSessionOwner {
38
+ interface MonitorSessionOwner {
42
39
  pid: number;
43
40
  starttime: number | null;
44
41
  }
@@ -107,7 +104,6 @@ export interface MonitorCheckinResult {
107
104
  /** This guard is first in monitor start so a conflicting invocation has no side effects. */
108
105
  export declare function assertMonitorTimeoutCompatibility(persistent: boolean, timeoutProvided: boolean): void;
109
106
  export declare function parseMonitorDuration(value: string, kind: "timeout" | "checkin"): number;
110
- export declare function assertMonitorSessionId(sessionId: string): void;
111
107
  export declare function parseMonitorTarget(value: string): MonitorTarget;
112
108
  export declare function monitorTargetKey(target: MonitorTarget): string;
113
109
  export declare function detectMonitorTarget(options: DetectMonitorTargetOptions): MonitorTarget;
@@ -119,7 +115,6 @@ export declare function findNearestMonitorRuntime(options: {
119
115
  export declare function monitorSessionDir(hqRoot: string, provider: LaneProvider, sessionId: string): string;
120
116
  export declare function monitorSessionHasActiveMarkers(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
121
117
  export declare function monitorSessionHasActiveMarkerForOwner(hqRoot: string, provider: LaneProvider, sessionId: string, owner: MonitorRuntimeIdentity): boolean;
122
- export declare function monitorSessionHasInboxFile(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
123
118
  export declare function monitorSessionInboxHasEvents(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
124
119
  export declare function monitorSessionHasWork(hqRoot: string, provider: LaneProvider, sessionId: string): boolean;
125
120
  export declare function monitorDir(hqRoot: string, monitorId: string): string;
@@ -150,4 +145,5 @@ export declare function recordMonitorTargetDeliveryAtPath(activityPath: string,
150
145
  export declare function formatElapsed(ms: number): string;
151
146
  export declare function runMonitorCheckinTick(hqRoot: string, targetKey: string, deps: MonitorCheckinDeps): Promise<MonitorCheckinResult>;
152
147
  export declare function readMonitorPidLiveness(pid: number | null, starttime: number | null, readStarttime?: (pid: number) => number | null): boolean;
148
+ export {};
153
149
  //# sourceMappingURL=monitor.d.ts.map
@@ -9,15 +9,15 @@ export const MONITOR_FLAG = "monitor.enabled";
9
9
  export const MONITOR_FLAG_DEFAULT = false;
10
10
  export const MONITOR_DEFAULT_TIMEOUT_MS = 5 * 60_000;
11
11
  export const MONITOR_DEFAULT_CHECKIN_MS = 55 * 60_000;
12
- export const MONITOR_PROMPT_CACHE_TTL_MS = 60 * 60_000;
12
+ const MONITOR_PROMPT_CACHE_TTL_MS = 60 * 60_000;
13
13
  export const MONITOR_BATCH_WINDOW_MS = 200;
14
14
  export const MONITOR_POLL_INTERVAL_MS = 1_000;
15
15
  export const MONITOR_RATE_WINDOW_MS = 10_000;
16
16
  /** Sustained stdout floods stop after 3 windows; the per-window cap can be overridden with HQ_MONITOR_MAX_EVENTS_PER_WINDOW. */
17
17
  export const MONITOR_MAX_EVENTS_PER_WINDOW = 60;
18
18
  export const MONITOR_MAX_FLOOD_WINDOWS = 3;
19
- export const MONITOR_LOCK_STALE_MS = 30_000;
20
- export const MONITOR_MAX_TIMER_DELAY_MS = 2_147_000_000;
19
+ const MONITOR_LOCK_STALE_MS = 30_000;
20
+ const MONITOR_MAX_TIMER_DELAY_MS = 2_147_000_000;
21
21
  const SESSION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
22
22
  const SAFE_LANE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
23
23
  const RUNTIME_ENV = {
@@ -59,7 +59,7 @@ function assertLaneTargetId(laneId) {
59
59
  throw monitorError("monitor_target_invalid", `Invalid lane target ${JSON.stringify(laneId)}.`, { lane_id: laneId });
60
60
  }
61
61
  }
62
- export function assertMonitorSessionId(sessionId) {
62
+ function assertMonitorSessionId(sessionId) {
63
63
  if (!SESSION_ID_RE.test(sessionId)) {
64
64
  throw monitorError("monitor_session_id_invalid", "Session ids must contain only letters, numbers, underscores, or hyphens and be 1–128 characters long.", { session_id: sessionId });
65
65
  }
@@ -154,7 +154,7 @@ export function monitorSessionHasActiveMarkerForOwner(hqRoot, provider, sessionI
154
154
  && markerOwner.starttime === owner.starttime;
155
155
  });
156
156
  }
157
- export function monitorSessionHasInboxFile(hqRoot, provider, sessionId) {
157
+ function monitorSessionHasInboxFile(hqRoot, provider, sessionId) {
158
158
  return fs.existsSync(path.join(monitorSessionDir(hqRoot, provider, sessionId), "dropbox", "inbox.jsonl"));
159
159
  }
160
160
  export function monitorSessionInboxHasEvents(hqRoot, provider, sessionId) {
@@ -1,9 +1,5 @@
1
- /** Mirrors the deploy skill's static-report guardrails. */
2
- export declare const REPORT_SOURCE_MAX_BYTES: number;
3
- export declare const REPORT_BUNDLE_MAX_BYTES: number;
4
- export declare const REPORT_SOURCE_MAX_FILES = 100;
5
1
  export declare const REPORT_CSP = "default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'none'; frame-src 'none'; form-action 'none'";
6
- export type ReportLastRun = {
2
+ type ReportLastRun = {
7
3
  at: string;
8
4
  status: "running" | "succeeded" | "failed";
9
5
  reason?: string;
@@ -175,10 +171,5 @@ type BuiltTarball = {
175
171
  };
176
172
  /** hq-deploy static payload format: index.html stays at the archive root. */
177
173
  export declare function buildReportTarball(files: StaticFile[]): BuiltTarball;
178
- /** Isolated test helper for creating a compliant report source. */
179
- export declare function makeReportFixture(html: string): {
180
- directory: string;
181
- cleanup: () => void;
182
- };
183
174
  export {};
184
175
  //# sourceMappingURL=report-publish.d.ts.map
@@ -13,9 +13,9 @@ import { gzipSync } from "node:zlib";
13
13
  import { REPORT_PREVIEW_FILE } from "./report-snapshot.js";
14
14
  import { loadUpdateReportRunbook } from "./report-templates.js";
15
15
  /** Mirrors the deploy skill's static-report guardrails. */
16
- export const REPORT_SOURCE_MAX_BYTES = 25 * 1024 * 1024;
17
- export const REPORT_BUNDLE_MAX_BYTES = 10 * 1024 * 1024;
18
- export const REPORT_SOURCE_MAX_FILES = 100;
16
+ const REPORT_SOURCE_MAX_BYTES = 25 * 1024 * 1024;
17
+ const REPORT_BUNDLE_MAX_BYTES = 10 * 1024 * 1024;
18
+ const REPORT_SOURCE_MAX_FILES = 100;
19
19
  export const REPORT_CSP = "default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'none'; frame-src 'none'; form-action 'none'";
20
20
  /**
21
21
  * Write the files exactly as they will be published, protected index.html
@@ -418,10 +418,4 @@ function writeOctal(buffer, value, offset, length) {
418
418
  buffer.write(value.toString(8).padStart(length - 1, "0").slice(-length + 1), offset, length - 1, "ascii");
419
419
  buffer[offset + length - 1] = 0;
420
420
  }
421
- /** Isolated test helper for creating a compliant report source. */
422
- export function makeReportFixture(html) {
423
- const directory = fs.mkdtempSync(path.join(os.tmpdir(), "hq-report-test-"));
424
- fs.writeFileSync(path.join(directory, "index.html"), html);
425
- return { directory, cleanup: () => fs.rmSync(directory, { recursive: true, force: true }) };
426
- }
427
421
  //# sourceMappingURL=report-publish.js.map
@@ -11,6 +11,7 @@ export type QmdProcessResult = {
11
11
  */
12
12
  signal?: NodeJS.Signals | string | null;
13
13
  };
14
+ export type QmdUpdateEntryPath = 'index-sync';
14
15
  export type QmdProcessRunner = (bin: string, args: string[], options: {
15
16
  cwd?: string;
16
17
  env?: NodeJS.ProcessEnv;
@@ -113,6 +114,10 @@ export declare class QmdExitError extends Error {
113
114
  readonly stdout: string;
114
115
  readonly stderr: string;
115
116
  name: string;
117
+ /** Safe route labels for telemetry; raw qmd streams remain local to the error. */
118
+ entryPath?: QmdUpdateEntryPath;
119
+ /** Present only when the invoked executable is the bundled qmd. */
120
+ qmdVersion?: string;
116
121
  constructor(message: string, args: string[], status: number | null, stdout: string, stderr: string);
117
122
  }
118
123
  export declare class QmdCollectionMissingError extends QmdExitError {
@@ -507,6 +512,8 @@ export type ResolveQmdInvocationOptions = {
507
512
  export declare function resolveQmdInvocation(options?: ResolveQmdInvocationOptions): QmdInvocation;
508
513
  export type RunQmdOptions = {
509
514
  bin?: string;
515
+ /** Source of an update failure, used only for bounded Sentry tags. */
516
+ entryPath?: QmdUpdateEntryPath;
510
517
  cwd?: string;
511
518
  env?: NodeJS.ProcessEnv;
512
519
  runner?: QmdProcessRunner;
@@ -76,6 +76,10 @@ export class QmdExitError extends Error {
76
76
  stdout;
77
77
  stderr;
78
78
  name = 'QmdExitError';
79
+ /** Safe route labels for telemetry; raw qmd streams remain local to the error. */
80
+ entryPath;
81
+ /** Present only when the invoked executable is the bundled qmd. */
82
+ qmdVersion;
79
83
  constructor(message, args, status, stdout, stderr) {
80
84
  super(message);
81
85
  this.args = args;
@@ -1239,6 +1243,21 @@ function finishRunQmd(result, bin, args, attribution) {
1239
1243
  }
1240
1244
  throw exitError;
1241
1245
  }
1246
+ function finishRunQmdWithDiagnostics(result, bin, args, options, attribution) {
1247
+ try {
1248
+ return finishRunQmd(result, bin, args, attribution);
1249
+ }
1250
+ catch (error) {
1251
+ if (error instanceof QmdExitError) {
1252
+ if (args[0] === 'update' && options.entryPath)
1253
+ error.entryPath = options.entryPath;
1254
+ const bundledBins = [packageLocalBin(), packageLocalNodeEntry()].filter((candidate) => Boolean(candidate));
1255
+ if (bundledBins.includes(bin))
1256
+ error.qmdVersion = resolveQmdVersion();
1257
+ }
1258
+ throw error;
1259
+ }
1260
+ }
1242
1261
  /** Run qmd with captured output and typed failures. */
1243
1262
  export function runQmd(args, options = {}) {
1244
1263
  // Legacy string-bin runner seam (tests and callers that inject a fake process):
@@ -1246,7 +1265,7 @@ export function runQmd(args, options = {}) {
1246
1265
  if (options.runner) {
1247
1266
  const bin = options.bin ?? resolveQmdBin({ env: options.env });
1248
1267
  const result = options.runner(bin, args, { cwd: options.cwd, env: options.env });
1249
- return finishRunQmd(result, bin, args);
1268
+ return finishRunQmdWithDiagnostics(result, bin, args, options);
1250
1269
  }
1251
1270
  // Default path: resolve the full invocation (shell-free Node launcher preferred)
1252
1271
  // and spawn it through the shared Windows-aware plan. An explicit string bin is
@@ -1279,7 +1298,7 @@ export function runQmd(args, options = {}) {
1279
1298
  // genuinely missing qmd — instead of blaming the launcher for all three
1280
1299
  // (HQ-CLI-1A). `command` is the process actually spawned; `launcher` is qmd's
1281
1300
  // file identity.
1282
- return finishRunQmd(result, launcher, args, {
1301
+ return finishRunQmdWithDiagnostics(result, launcher, args, options, {
1283
1302
  command: invocation.command,
1284
1303
  launcher,
1285
1304
  cwd: options.cwd,
@@ -0,0 +1,5 @@
1
+ /** Bounded Sentry tags for qmd exits; never return captured streams or full argv. */
2
+ export declare function qmdExitCaptureContext(err: unknown): {
3
+ tags: Record<string, string>;
4
+ } | undefined;
5
+ //# sourceMappingURL=qmd-exit-diagnostics.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { QmdExitError } from "./index.js";
2
+ const QMD_ERROR_CODE_TOKENS = [
3
+ "SQLITE_BUSY",
4
+ "SQLITE_CORRUPT",
5
+ "SQLITE_FULL",
6
+ "ENOSPC",
7
+ "EACCES",
8
+ "EPERM",
9
+ "ENOENT",
10
+ "ECONNRESET",
11
+ "ETIMEDOUT",
12
+ ];
13
+ const QMD_UPDATE_ENTRY_PATHS = new Set(["index-sync"]);
14
+ /** Bounded Sentry tags for qmd exits; never return captured streams or full argv. */
15
+ export function qmdExitCaptureContext(err) {
16
+ if (!(err instanceof QmdExitError))
17
+ return undefined;
18
+ const output = err.stderr + "\n" + err.stdout;
19
+ const errorCode = QMD_ERROR_CODE_TOKENS.find((token) => new RegExp("(^|[^A-Za-z0-9_])" + token + "(?=$|[^A-Za-z0-9_])").test(output)) ?? "none";
20
+ const firstArg = err.args[0];
21
+ const subcommand = typeof firstArg === "string" && /^[a-z][a-z0-9_-]{0,31}$/.test(firstArg)
22
+ ? firstArg
23
+ : "unknown";
24
+ const exitStatus = typeof err.status === "number" && Number.isSafeInteger(err.status)
25
+ ? String(err.status)
26
+ : "unknown";
27
+ const tags = {
28
+ qmd_error_code: errorCode,
29
+ qmd_subcommand: subcommand,
30
+ qmd_exit_status: exitStatus,
31
+ };
32
+ if (typeof err.qmdVersion === "string" && /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(err.qmdVersion)) {
33
+ tags.qmd_version = err.qmdVersion;
34
+ }
35
+ if (typeof err.entryPath === "string" && QMD_UPDATE_ENTRY_PATHS.has(err.entryPath)) {
36
+ tags.qmd_entry_path = err.entryPath;
37
+ }
38
+ return { tags };
39
+ }
40
+ //# sourceMappingURL=qmd-exit-diagnostics.js.map
@@ -7,7 +7,6 @@
7
7
  * touch launchctl.
8
8
  */
9
9
  import { type ServiceHostPaths, type ServiceSpec } from "./types.js";
10
- export declare const LAUNCHD_PATH_ENV = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin";
11
10
  export declare function launchAgentsDir(home: string): string;
12
11
  export declare function launchdPlistPath(home: string, spec: Pick<ServiceSpec, "label">): string;
13
12
  export declare function renderLaunchdPlist(spec: ServiceSpec, host: ServiceHostPaths): string;
@@ -27,12 +26,6 @@ export declare const LAUNCHD_BOOTSTRAP_BACKOFF_MS: readonly number[];
27
26
  export declare const LAUNCHD_BOOTSTRAP_ATTEMPTS: number;
28
27
  export declare const defaultRunCommand: RunCommand;
29
28
  export declare function launchdServiceTarget(plistPath: string, uid?: number): string;
30
- /** launchd returns EIO (5) when bootstrap races the previous job's exit. */
31
- export declare function isRetryableLaunchdBootstrap(r: {
32
- status: number | null;
33
- stdout: string;
34
- stderr: string;
35
- }): boolean;
36
29
  /** ProgramArguments from a LaunchAgent plist. */
37
30
  export declare function launchdPlistProgramArguments(plist: string): string[];
38
31
  /**
@@ -10,7 +10,7 @@ import { spawnSync } from "node:child_process";
10
10
  import * as os from "node:os";
11
11
  import * as path from "node:path";
12
12
  import { escapeXml, } from "./types.js";
13
- export const LAUNCHD_PATH_ENV = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin";
13
+ const LAUNCHD_PATH_ENV = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin";
14
14
  export function launchAgentsDir(home) {
15
15
  return path.join(home, "Library", "LaunchAgents");
16
16
  }
@@ -89,7 +89,7 @@ function bootstrapErrorText(r) {
89
89
  return `${r.stderr} ${r.stdout} exit ${r.status ?? ""}`;
90
90
  }
91
91
  /** launchd returns EIO (5) when bootstrap races the previous job's exit. */
92
- export function isRetryableLaunchdBootstrap(r) {
92
+ function isRetryableLaunchdBootstrap(r) {
93
93
  if (r.status === 0)
94
94
  return false;
95
95
  return /Input\/output error|\bEIO\b|Bootstrap failed: 5|Boot-strap failed: 5/i.test(bootstrapErrorText(r));
package/dist/main.js CHANGED
@@ -24,6 +24,7 @@ import { qmdStoreUnopenableMessage } from "./utils/qmd-store-unopenable-error.js
24
24
  import { qmdQueryDocumentMessage } from "./utils/qmd-query-document-error.js";
25
25
  import { qmdModelDownloadMessage } from "./utils/qmd-model-download-error.js";
26
26
  import { qmdWorkdirMissingMessage } from "./utils/qmd-workdir-missing-error.js";
27
+ import { qmdExitCaptureContext } from "./lib/search-index/qmd-exit-diagnostics.js";
27
28
  import { hqStateWriteErrorMessage } from "./utils/hq-state-write-error.js";
28
29
  import { checkpointStampWriteCaptureContext, checkpointStampWriteMessage, } from "./utils/checkpoint-stamp-write-error.js";
29
30
  import { incompleteInstallMessage, incompleteInstallCaptureContext, } from "./utils/incomplete-install-error.js";
@@ -872,9 +873,13 @@ export async function handleTopLevelError(err, deps = defaultTopLevelErrorDepend
872
873
  // Every other error captures bare, exactly as before.
873
874
  const spawnContext = qmdSpawnFailureCaptureContext(err);
874
875
  const installContext = incompleteInstallCaptureContext(err);
875
- if (spawnContext || installContext) {
876
+ const qmdContext = qmdExitCaptureContext(err);
877
+ if (spawnContext || installContext || qmdContext) {
876
878
  deps.sentry.captureException(err, {
877
- contexts: { ...spawnContext?.contexts, ...installContext },
879
+ ...qmdContext,
880
+ ...((spawnContext || installContext)
881
+ ? { contexts: { ...spawnContext?.contexts, ...installContext } }
882
+ : {}),
878
883
  });
879
884
  }
880
885
  else {
@@ -17,7 +17,7 @@
17
17
  * spawns, waits, or a real install tree.
18
18
  */
19
19
  import { spawnSync } from "node:child_process";
20
- import { resolveRunningInstall } from "./utils/version-gate.js";
20
+ import { getVersionGatePhase, resolveRunningInstall } from "./utils/version-gate.js";
21
21
  import { stringDerivedPackageRoot } from "./utils/hq-roots.js";
22
22
  import { updateLockPath, UPDATE_LOCK_STALE_MS } from "./utils/update-lock.js";
23
23
  import { classifyEvalThrow, classifyModuleNotFound, installRewriteInProgress, InstallTreeTornError, waitForInstallTreeSettled, } from "./utils/install-tree-torn.js";
@@ -122,6 +122,7 @@ export async function registerCommandsWithRecovery(args) {
122
122
  cause: err,
123
123
  classified,
124
124
  packageRoot,
125
+ versionGatePhase: getVersionGatePhase(),
125
126
  outcome: "guarded",
126
127
  treeState: env[INSTALL_TREE_RECOVERY_TREE_STATE_ENV] === "stale" ? "stale" : "in_flight",
127
128
  attempt: 2,
@@ -165,6 +166,9 @@ export async function registerCommandsWithRecovery(args) {
165
166
  sawLock: result.sawLock,
166
167
  sawRetired: result.sawRetired,
167
168
  retiredSiblingPresent: result.retiredSiblingPresent,
169
+ retiredSiblingKind: result.retiredSiblingKind,
170
+ installTargetState: result.installTargetState,
171
+ updaterPidAlive: result.updaterPidAlive,
168
172
  sawTreeChange: result.sawTreeChange,
169
173
  lockHeldAtEnd: result.lockHeldAtEnd,
170
174
  lockedMs: result.lockedMs,
@@ -189,6 +193,7 @@ export async function registerCommandsWithRecovery(args) {
189
193
  cause: err,
190
194
  classified,
191
195
  packageRoot,
196
+ versionGatePhase: getVersionGatePhase(),
192
197
  outcome: "reexec-failed",
193
198
  treeState,
194
199
  attempt: 1,
@@ -196,6 +201,9 @@ export async function registerCommandsWithRecovery(args) {
196
201
  sawLock: result.sawLock,
197
202
  sawRetired: result.sawRetired,
198
203
  retiredSiblingPresent: result.retiredSiblingPresent,
204
+ retiredSiblingKind: result.retiredSiblingKind,
205
+ installTargetState: result.installTargetState,
206
+ updaterPidAlive: result.updaterPidAlive,
199
207
  sawTreeChange: result.sawTreeChange,
200
208
  lockTool: result.lockTool,
201
209
  });
@@ -33,8 +33,6 @@
33
33
  import { isExpiring, loadCachedTokens } from "./cognito-session.js";
34
34
  import { type VersionInfo } from "./feedback-versions.js";
35
35
  import { type ClientHealthArch, type ClientHealthHeartbeat, type ClientHealthLocalFilesOverview, type ClientHealthPlatform } from "./client-health-contract.js";
36
- /** Same bound as CLI telemetry: a heartbeat may never stall a command. */
37
- export declare const CLIENT_HEALTH_TIMEOUT_MS = 1200;
38
36
  export declare const CLIENT_HEALTH_STATE_FILE = "cli-client-health.json";
39
37
  /**
40
38
  * Single-flight marker for the journal observation below. Held only for the
@@ -66,8 +64,6 @@ export declare const CLIENT_HEALTH_OBSERVATION_FILE = "cli-client-health.observa
66
64
  * always re-observe — they have opened the store anyway, so it costs nothing.
67
65
  */
68
66
  export declare const JOURNAL_OBSERVATION_TTL_MS: number;
69
- /** An observation lock older than this is treated as abandoned. */
70
- export declare const JOURNAL_OBSERVATION_LOCK_STALE_MS: number;
71
67
  export interface CliClientHealthState {
72
68
  /** Stable random installation identity — regenerated only if invalid. */
73
69
  installationId: string;
@@ -40,7 +40,7 @@ import { collectVersions } from "./feedback-versions.js";
40
40
  import { CLIENT_HEALTH_CONTRACT_VERSION, parseClientHealthHeartbeat, } from "./client-health-contract.js";
41
41
  import { collectLocalFilesOverview } from "./local-files-overview.js";
42
42
  /** Same bound as CLI telemetry: a heartbeat may never stall a command. */
43
- export const CLIENT_HEALTH_TIMEOUT_MS = 1_200;
43
+ const CLIENT_HEALTH_TIMEOUT_MS = 1_200;
44
44
  export const CLIENT_HEALTH_STATE_FILE = "cli-client-health.json";
45
45
  /**
46
46
  * Single-flight marker for the journal observation below. Held only for the
@@ -73,7 +73,7 @@ export const CLIENT_HEALTH_OBSERVATION_FILE = "cli-client-health.observation.jso
73
73
  */
74
74
  export const JOURNAL_OBSERVATION_TTL_MS = 15 * 60_000;
75
75
  /** An observation lock older than this is treated as abandoned. */
76
- export const JOURNAL_OBSERVATION_LOCK_STALE_MS = 5 * 60_000;
76
+ const JOURNAL_OBSERVATION_LOCK_STALE_MS = 5 * 60_000;
77
77
  /**
78
78
  * Pre-filter mirror of the contract's SemVer shape (the contract keeps its
79
79
  * validators private). A version failing this is DROPPED individually so one
@@ -1,6 +1,6 @@
1
1
  import { type VersionInfo } from "./feedback-versions.js";
2
2
  import type { FeedbackLogsBlob } from "./feedback-logs.js";
3
- export interface GitContext {
3
+ interface GitContext {
4
4
  branch: string | null;
5
5
  head: string | null;
6
6
  dirty: boolean;
@@ -37,4 +37,5 @@ export interface DiagnosticsBlob {
37
37
  }
38
38
  export declare function sanitizeArgv(argv: string[]): string[];
39
39
  export declare function collectDiagnostics(): DiagnosticsBlob;
40
+ export {};
40
41
  //# sourceMappingURL=feedback-diagnostics.d.ts.map
@@ -61,29 +61,6 @@ import { vaultApiFetch } from "./vault-api.js";
61
61
  * stay in step or the CLI will build bundles the server will not accept.
62
62
  */
63
63
  export declare const LOG_BUNDLE_MAX_BYTES: number;
64
- /**
65
- * What the presign request declares, and what the server must answer with.
66
- * Mirrors `LOG_BUNDLE_FORMAT_ZIP` / `LOG_BUNDLE_ZIP_CONTENT_TYPE` in the hq-pro
67
- * handler `feedback-log-bundles.ts`.
68
- */
69
- export declare const LOG_BUNDLE_FORMAT = "zip";
70
- export declare const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
71
- /**
72
- * Headroom between the size we stop feeding at and the hard cap.
73
- *
74
- * deflate reports compressed bytes only as its internal buffer flushes, so the
75
- * running total lags the bytes actually consumed. The lag is bounded by that
76
- * buffer (tens of KiB); a 1 MiB margin covers it with three orders of magnitude
77
- * to spare, and the final size is asserted against the real cap regardless.
78
- */
79
- export declare const LOG_BUNDLE_SAFETY_MARGIN_BYTES: number;
80
- /**
81
- * Per-file raw ceiling. The desktop logger rotates at 32 MiB (hq-desktop-core
82
- * `logfile.rs`), so this admits a full generation with headroom while stopping
83
- * one pathological file from consuming the whole bundle. Files above it are
84
- * read from the TAIL — the end of a log is what explains a failure.
85
- */
86
- export declare const LOG_BUNDLE_MAX_FILE_RAW_BYTES: number;
87
64
  export interface LogBundleResult {
88
65
  /** The zipped NDJSON bytes, ready to PUT. */
89
66
  bytes: Buffer;
@@ -150,8 +127,6 @@ export declare const ZIP32_UNCOMPRESSED_STOP_BYTES: number;
150
127
  */
151
128
  export declare function entryCeiling(requested?: number): number;
152
129
  export declare function crc32Fallback(buf: Buffer, seed?: number): number;
153
- /** Exported for the test that proves the fallback agrees with the native one. */
154
- export declare function crc32(buf: Buffer, seed?: number): number;
155
130
  /**
156
131
  * Bytes of ZIP framing around one entry: local header, central directory
157
132
  * header, end-of-central-directory. The name is stored twice.
@@ -71,8 +71,8 @@ export const LOG_BUNDLE_MAX_BYTES = 50 * 1024 * 1024;
71
71
  * Mirrors `LOG_BUNDLE_FORMAT_ZIP` / `LOG_BUNDLE_ZIP_CONTENT_TYPE` in the hq-pro
72
72
  * handler `feedback-log-bundles.ts`.
73
73
  */
74
- export const LOG_BUNDLE_FORMAT = "zip";
75
- export const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
74
+ const LOG_BUNDLE_FORMAT = "zip";
75
+ const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
76
76
  /**
77
77
  * Headroom between the size we stop feeding at and the hard cap.
78
78
  *
@@ -81,14 +81,14 @@ export const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
81
81
  * buffer (tens of KiB); a 1 MiB margin covers it with three orders of magnitude
82
82
  * to spare, and the final size is asserted against the real cap regardless.
83
83
  */
84
- export const LOG_BUNDLE_SAFETY_MARGIN_BYTES = 1024 * 1024;
84
+ const LOG_BUNDLE_SAFETY_MARGIN_BYTES = 1024 * 1024;
85
85
  /**
86
86
  * Per-file raw ceiling. The desktop logger rotates at 32 MiB (hq-desktop-core
87
87
  * `logfile.rs`), so this admits a full generation with headroom while stopping
88
88
  * one pathological file from consuming the whole bundle. Files above it are
89
89
  * read from the TAIL — the end of a log is what explains a failure.
90
90
  */
91
- export const LOG_BUNDLE_MAX_FILE_RAW_BYTES = 64 * 1024 * 1024;
91
+ const LOG_BUNDLE_MAX_FILE_RAW_BYTES = 64 * 1024 * 1024;
92
92
  /** Read slice size. Bounds peak memory; unrelated to the output chunking. */
93
93
  const READ_SLICE_BYTES = 4 * 1024 * 1024;
94
94
  /** Target raw text per emitted chunk record. Keeps NDJSON lines manageable. */
@@ -217,7 +217,7 @@ export function crc32Fallback(buf, seed = 0) {
217
217
  return (~crc >>> 0) >>> 0;
218
218
  }
219
219
  /** Exported for the test that proves the fallback agrees with the native one. */
220
- export function crc32(buf, seed = 0) {
220
+ function crc32(buf, seed = 0) {
221
221
  const native = zlib.crc32;
222
222
  return typeof native === "function" ? native(buf, seed) : crc32Fallback(buf, seed);
223
223
  }
@@ -84,13 +84,6 @@ export declare const FEEDBACK_LOGS_MIN_USEFUL_BYTES = 1024;
84
84
  * evidence. The overflow count preserves the signal without the bytes.
85
85
  */
86
86
  export declare const FEEDBACK_MAX_SKIPPED_ENTRIES = 25;
87
- /** Subdirectory of `~/.hq` whose log files are eligible. Not recursive. */
88
- export declare const FEEDBACK_LOGS_DIRNAME = "logs";
89
- /**
90
- * Eligible extensions inside `~/.hq/logs/`. `.jsonl` is included because the
91
- * CLI's MCP registry audit trail is JSON-lines, not plain text.
92
- */
93
- export declare const FEEDBACK_LOG_EXTENSIONS: readonly string[];
94
87
  /**
95
88
  * True for a log filename, including a ROTATED generation.
96
89
  *
@@ -103,26 +96,6 @@ export declare const FEEDBACK_LOG_EXTENSIONS: readonly string[];
103
96
  export declare function isEligibleLogName(name: string): boolean;
104
97
  /** Per-state-file ceiling. These are status documents, not streams. */
105
98
  export declare const FEEDBACK_STATE_MAX_FILE_BYTES: number;
106
- /**
107
- * Ceiling on all state snapshots combined. Deliberately a small slice of the
108
- * overall budget: state is dense but finite, and the rest belongs to logs.
109
- */
110
- export declare const FEEDBACK_STATE_MAX_TOTAL_BYTES: number;
111
- /**
112
- * State files at the `~/.hq` ROOT that are eligible, by EXACT name.
113
- *
114
- * The root is where `cognito-tokens.json`, `deploy-passwords.json`, and
115
- * `secrets-cache/` live, so it is never globbed. Adding a name here is a
116
- * deliberate act: confirm the file carries no credential before listing it.
117
- */
118
- export declare const FEEDBACK_STATE_FILENAMES: readonly string[];
119
- /**
120
- * Per-company sync journal LOCATORS (`sync-journal.<slug>.json`) are also
121
- * eligible. Matched by an exact prefix AND a `.json` suffix, which is what
122
- * excludes the multi-hundred-megabyte `…json.last-good` snapshots sitting
123
- * beside them. No credential file at the root carries this prefix.
124
- */
125
- export declare const FEEDBACK_STATE_JOURNAL_PREFIX = "sync-journal.";
126
99
  /**
127
100
  * `~/.hq/locks/` — held operation locks and background-work claims.
128
101
  *
@@ -141,8 +114,6 @@ export declare const FEEDBACK_LOCKS_DIRNAME = "locks";
141
114
  * bounds that walk carries.
142
115
  */
143
116
  export declare const FEEDBACK_JOBS_DIRNAME = "jobs";
144
- /** Per-entry ceiling for a lock file. Their value is the name plus a little context. */
145
- export declare const FEEDBACK_LOCK_MAX_FILE_BYTES = 512;
146
117
  /**
147
118
  * Bounds on the `jobs/` walk. Depth 3 reaches
148
119
  * `jobs/probes/<job>/last-attempt.json`; the file cap stops a pathological
@@ -152,15 +123,9 @@ export declare const FEEDBACK_JOBS_MAX_DEPTH = 3;
152
123
  export declare const FEEDBACK_JOBS_MAX_FILES = 20;
153
124
  /** Ceiling on how many lock entries are collected. */
154
125
  export declare const FEEDBACK_LOCKS_MAX_FILES = 20;
155
- /**
156
- * Log files that live at the root of `~/.hq` rather than under `logs/`.
157
- * Listed explicitly — the root directory holds credentials and must never be
158
- * enumerated by pattern.
159
- */
160
- export declare const FEEDBACK_ROOT_LOG_FILENAMES: readonly string[];
161
126
  /** Why a discovered log file contributed nothing to the submission. */
162
- export type SkipReason = "budget" | "empty" | "unreadable";
163
- export interface LogTail {
127
+ type SkipReason = "budget" | "empty" | "unreadable";
128
+ interface LogTail {
164
129
  /** Path relative to `~/.hq`, e.g. `logs/hq-sync.log`. Never absolute. */
165
130
  name: string;
166
131
  /** Full on-disk size, even when only a tail was included. */
@@ -177,7 +142,7 @@ export interface LogTail {
177
142
  tail: string;
178
143
  }
179
144
  /** A small JSON status document included whole (or head-truncated). */
180
- export interface StateSnapshot {
145
+ interface StateSnapshot {
181
146
  /** Path relative to `~/.hq`, e.g. `sync-progress.json`. Never absolute. */
182
147
  name: string;
183
148
  sizeBytes: number;
@@ -331,4 +296,5 @@ export interface CollectFeedbackLogsOptions {
331
296
  * omit the field entirely rather than attach an empty object. Never throws.
332
297
  */
333
298
  export declare function collectFeedbackLogs(opts?: CollectFeedbackLogsOptions): FeedbackLogsBlob | undefined;
299
+ export {};
334
300
  //# sourceMappingURL=feedback-logs.d.ts.map
@@ -89,12 +89,12 @@ export const FEEDBACK_LOGS_MIN_USEFUL_BYTES = 1024;
89
89
  */
90
90
  export const FEEDBACK_MAX_SKIPPED_ENTRIES = 25;
91
91
  /** Subdirectory of `~/.hq` whose log files are eligible. Not recursive. */
92
- export const FEEDBACK_LOGS_DIRNAME = "logs";
92
+ const FEEDBACK_LOGS_DIRNAME = "logs";
93
93
  /**
94
94
  * Eligible extensions inside `~/.hq/logs/`. `.jsonl` is included because the
95
95
  * CLI's MCP registry audit trail is JSON-lines, not plain text.
96
96
  */
97
- export const FEEDBACK_LOG_EXTENSIONS = [".log", ".jsonl"];
97
+ const FEEDBACK_LOG_EXTENSIONS = [".log", ".jsonl"];
98
98
  /**
99
99
  * True for a log filename, including a ROTATED generation.
100
100
  *
@@ -120,7 +120,7 @@ export const FEEDBACK_STATE_MAX_FILE_BYTES = 4 * 1024;
120
120
  * Ceiling on all state snapshots combined. Deliberately a small slice of the
121
121
  * overall budget: state is dense but finite, and the rest belongs to logs.
122
122
  */
123
- export const FEEDBACK_STATE_MAX_TOTAL_BYTES = 12 * 1024;
123
+ const FEEDBACK_STATE_MAX_TOTAL_BYTES = 12 * 1024;
124
124
  /**
125
125
  * State files at the `~/.hq` ROOT that are eligible, by EXACT name.
126
126
  *
@@ -128,7 +128,7 @@ export const FEEDBACK_STATE_MAX_TOTAL_BYTES = 12 * 1024;
128
128
  * `secrets-cache/` live, so it is never globbed. Adding a name here is a
129
129
  * deliberate act: confirm the file carries no credential before listing it.
130
130
  */
131
- export const FEEDBACK_STATE_FILENAMES = [
131
+ const FEEDBACK_STATE_FILENAMES = [
132
132
  // In-flight sync: phase, file counts, conflict count, current file.
133
133
  "sync-progress.json",
134
134
  // Sync contract version the installation last wrote.
@@ -150,7 +150,7 @@ export const FEEDBACK_STATE_FILENAMES = [
150
150
  * excludes the multi-hundred-megabyte `…json.last-good` snapshots sitting
151
151
  * beside them. No credential file at the root carries this prefix.
152
152
  */
153
- export const FEEDBACK_STATE_JOURNAL_PREFIX = "sync-journal.";
153
+ const FEEDBACK_STATE_JOURNAL_PREFIX = "sync-journal.";
154
154
  /**
155
155
  * `~/.hq/locks/` — held operation locks and background-work claims.
156
156
  *
@@ -170,7 +170,7 @@ export const FEEDBACK_LOCKS_DIRNAME = "locks";
170
170
  */
171
171
  export const FEEDBACK_JOBS_DIRNAME = "jobs";
172
172
  /** Per-entry ceiling for a lock file. Their value is the name plus a little context. */
173
- export const FEEDBACK_LOCK_MAX_FILE_BYTES = 512;
173
+ const FEEDBACK_LOCK_MAX_FILE_BYTES = 512;
174
174
  /**
175
175
  * Bounds on the `jobs/` walk. Depth 3 reaches
176
176
  * `jobs/probes/<job>/last-attempt.json`; the file cap stops a pathological
@@ -185,7 +185,7 @@ export const FEEDBACK_LOCKS_MAX_FILES = 20;
185
185
  * Listed explicitly — the root directory holds credentials and must never be
186
186
  * enumerated by pattern.
187
187
  */
188
- export const FEEDBACK_ROOT_LOG_FILENAMES = [
188
+ const FEEDBACK_ROOT_LOG_FILENAMES = [
189
189
  "boot-sync.log",
190
190
  "boot-capture.log",
191
191
  ];
@@ -1,5 +1,4 @@
1
1
  export declare const MAX_SCREENSHOTS = 5;
2
- export declare const MAX_SCREENSHOT_BYTES: number;
3
2
  export declare function contentTypeForPath(filePath: string): string;
4
3
  export interface ScreenshotInput {
5
4
  path: string;