@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
@@ -5,6 +5,7 @@ export type UtilityOutput = {
5
5
  code: number;
6
6
  stdout: string;
7
7
  stderr: string;
8
+ stderrBytes?: Uint8Array;
8
9
  };
9
10
  export type UtilityContext = {
10
11
  root: string;
@@ -18,7 +19,7 @@ export type NativeUtilityDefinition = {
18
19
  root: CoreCommandRoot;
19
20
  summary: string;
20
21
  resolveRoot: (cwd: string, hqRoot?: string) => string;
21
- run: (args: string[], context: UtilityContext) => UtilityOutput;
22
+ run: (args: string[], context: UtilityContext) => UtilityOutput | Promise<UtilityOutput>;
22
23
  };
23
24
  /** Register one raw-argv command and retain the original shell as rollback. */
24
25
  export declare function registerNativeUtility(core: Command, definition: NativeUtilityDefinition): void;
@@ -31,10 +31,12 @@ export function registerNativeUtility(core, definition) {
31
31
  process.exitCode = result.code;
32
32
  return;
33
33
  }
34
- const result = definition.run(operands, { root, cwd, env: process.env, now: () => new Date() });
34
+ const result = await definition.run(operands, { root, cwd, env: process.env, now: () => new Date() });
35
35
  if (result.stdout)
36
36
  process.stdout.write(result.stdout);
37
- if (result.stderr)
37
+ if (result.stderrBytes)
38
+ process.stderr.write(result.stderrBytes);
39
+ else if (result.stderr)
38
40
  process.stderr.write(result.stderr);
39
41
  if (result.code !== 0)
40
42
  process.exitCode = result.code;
@@ -9,11 +9,7 @@
9
9
  */
10
10
  import { Command } from "commander";
11
11
  import { type FlagReader } from "../lib/checkpoint-ontology/plan.js";
12
- import { type AliasDrop } from "../lib/checkpoint-ontology/drop-aliases.js";
13
- import { type DeleteVaultKey, type ListedPair, type MergeCopiesReport } from "../lib/checkpoint-ontology/merge-copies.js";
14
- export declare const ONTOLOGY_PAYLOAD_FILE = "payload.json";
15
- /** The hq-flags read, scoped to the payload company. */
16
- export declare const registryFlagReader: FlagReader;
12
+ import { type DeleteVaultKey, type MergeCopiesReport } from "../lib/checkpoint-ontology/merge-copies.js";
17
13
  export interface OntologyCommandDeps {
18
14
  readFlag?: FlagReader;
19
15
  now?: () => Date;
@@ -41,8 +37,6 @@ export interface MergeCopiesCommandOptions {
41
37
  json?: boolean;
42
38
  }
43
39
  export declare function mergeCopiesSummary(report: MergeCopiesReport): string;
44
- /** Reads a `--only` file: a JSON array of `{ copy, original }` company-relative paths. */
45
- export declare function readListedPairs(file: string): ListedPair[];
46
40
  /**
47
41
  * `hq core ontology merge-copies`: merge `<slug> <n>.md` copies into their
48
42
  * original (`--apply`), then delete merged copies from the vault
@@ -58,8 +52,6 @@ export declare function readListedPairs(file: string): ListedPair[];
58
52
  * which a person reviewed, instead of the pairs a finder produces.
59
53
  */
60
54
  export declare function runOntologyMergeCopies(hqRoot: string, slug: string | undefined, options: MergeCopiesCommandOptions, deps?: OntologyCommandDeps): Promise<string>;
61
- /** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
62
- export declare function readAliasDrops(file: string): AliasDrop[];
63
55
  /**
64
56
  * `hq core ontology drop-aliases`: remove the reviewed `{ entity, alias }`
65
57
  * pairs in `--only` from entity files (`--apply`); without it only reports.
@@ -22,7 +22,7 @@ import { MERGE_COPIES_FLAG, MERGE_LISTED_PAIRS_FLAG, MERGE_REWRITE_REFS_FLAG, ME
22
22
  import { callDeleteEndpoint } from "./files.js";
23
23
  import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
24
24
  import { registerCoreOntologyNativeCommands } from "./core-ontology-native.js";
25
- export const ONTOLOGY_PAYLOAD_FILE = "payload.json";
25
+ const ONTOLOGY_PAYLOAD_FILE = "payload.json";
26
26
  class OntologyUsageError extends Error {
27
27
  }
28
28
  function readPayload(runDir) {
@@ -49,7 +49,7 @@ function requireRunDir(runDir) {
49
49
  return resolved;
50
50
  }
51
51
  /** The hq-flags read, scoped to the payload company. */
52
- export const registryFlagReader = (flagKey, company) => readLanesRegistryFlag(flagKey, {
52
+ const registryFlagReader = (flagKey, company) => readLanesRegistryFlag(flagKey, {
53
53
  company: company.cloudUid ? { slug: company.slug, uid: company.cloudUid } : null,
54
54
  });
55
55
  function companyBriefSuffix(brief) {
@@ -160,7 +160,7 @@ export function mergeCopiesSummary(report) {
160
160
  return lines.join("\n");
161
161
  }
162
162
  /** Reads a `--only` file: a JSON array of `{ copy, original }` company-relative paths. */
163
- export function readListedPairs(file) {
163
+ function readListedPairs(file) {
164
164
  let parsed;
165
165
  try {
166
166
  parsed = JSON.parse(fs.readFileSync(file, "utf8"));
@@ -270,7 +270,7 @@ export async function runOntologyMergeCopies(hqRoot, slug, options, deps = {}) {
270
270
  return options.json ? JSON.stringify(report, null, 2) : mergeCopiesSummary(report);
271
271
  }
272
272
  /** Reads a `drop-aliases --only` file: a JSON array of `{ entity, alias }`. */
273
- export function readAliasDrops(file) {
273
+ function readAliasDrops(file) {
274
274
  let parsed;
275
275
  try {
276
276
  parsed = JSON.parse(fs.readFileSync(file, "utf8"));
@@ -1,8 +1,5 @@
1
1
  import type { Command } from 'commander';
2
2
  import { type FrontmatterResult } from './core-policy-frontmatter.js';
3
- export declare const POLICY_AGE_REPORT_ASSET = "core/scripts/policy-age-report.sh";
4
- export declare const POLICY_RETIRE_ASSET = "core/scripts/policy-retire.sh";
5
- export declare const POLICY_FRONTMATTER_ASSET = "core/scripts/read-policy-frontmatter.sh";
6
3
  export type CorePolicyResult = {
7
4
  code: number;
8
5
  stdout: Buffer;
@@ -8,9 +8,9 @@ import { resolveLiveRoot, resolveBundledAsset } from '../utils/hq-roots.js';
8
8
  import { runBundledScript } from '../utils/run-bundled-script.js';
9
9
  import { describeCoreCommand } from './core-command-catalog.js';
10
10
  import { runPolicyFrontmatter as runPolicyFrontmatterImpl } from './core-policy-frontmatter.js';
11
- export const POLICY_AGE_REPORT_ASSET = 'core/scripts/policy-age-report.sh';
12
- export const POLICY_RETIRE_ASSET = 'core/scripts/policy-retire.sh';
13
- export const POLICY_FRONTMATTER_ASSET = 'core/scripts/read-policy-frontmatter.sh';
11
+ const POLICY_AGE_REPORT_ASSET = 'core/scripts/policy-age-report.sh';
12
+ const POLICY_RETIRE_ASSET = 'core/scripts/policy-retire.sh';
13
+ const POLICY_FRONTMATTER_ASSET = 'core/scripts/read-policy-frontmatter.sh';
14
14
  const AGE_HELP = [
15
15
  '# hq-core: public',
16
16
  '# policy-age-report.sh — measure policy usage and flag retirement candidates',
@@ -0,0 +1,32 @@
1
+ /** Native implementation of core/scripts/work-mesh-live-bind-trusted.sh. */
2
+ import * as fs from "node:fs";
3
+ import type { Command } from "commander";
4
+ import { type UtilityOutput } from "./core-native-utilities.js";
5
+ export declare const WORK_MESH_BIND_TRUSTED_ASSET = "core/scripts/work-mesh-live-bind-trusted.sh";
6
+ export declare const WORK_MESH_BIND_TRUSTED_HELP = "hq-core: public\nUS-011 \u2014 write session meta + reconcile with observation.trustedContext.\nNo --trusted CLI flag in hq-cli; trusted bind is the observation field.\nset -euo pipefail\n\nROOT=\"${HQ_ROOT:-${CLAUDE_PROJECT_DIR:-}}\"\nif [ -z \"$ROOT\" ]; then\n";
7
+ export type WorkMeshBindDeps = {
8
+ root: string;
9
+ cwd: string;
10
+ env: NodeJS.ProcessEnv;
11
+ now: () => Date;
12
+ fs: typeof fs;
13
+ runHq: (args: string[], env: NodeJS.ProcessEnv) => {
14
+ code: number;
15
+ stdout: string;
16
+ stderr: string;
17
+ };
18
+ runHqSession: (root: string, args: string[], env: NodeJS.ProcessEnv) => {
19
+ code: number;
20
+ stdout: string;
21
+ stderr: string;
22
+ };
23
+ spawnHq: (args: string[], env: NodeJS.ProcessEnv) => void;
24
+ pid: number;
25
+ tmpDir: string;
26
+ ulid: (now: Date) => string;
27
+ };
28
+ export declare const createWorkMeshBindDeps: (root: string, cwd: string, env: NodeJS.ProcessEnv, now: () => Date) => WorkMeshBindDeps;
29
+ export declare function resolveWorkMeshBindSessionId(root: string, env: NodeJS.ProcessEnv, fileSystem?: typeof fs): string;
30
+ export declare function runWorkMeshBindTrusted(args: string[], deps: WorkMeshBindDeps): UtilityOutput;
31
+ export declare function registerCoreWorkMeshBindTrustedNative(core: Command): void;
32
+ //# sourceMappingURL=core-work-mesh-bind-trusted-native.d.ts.map
@@ -0,0 +1,190 @@
1
+ /** Native implementation of core/scripts/work-mesh-live-bind-trusted.sh. */
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { spawn, spawnSync } from "node:child_process";
5
+ import { generateUlid } from "../lib/mesh/live/ulid.js";
6
+ import { resolveLiveRoot } from "../utils/hq-roots.js";
7
+ import { registerNativeUtility } from "./core-native-utilities.js";
8
+ export const WORK_MESH_BIND_TRUSTED_ASSET = "core/scripts/work-mesh-live-bind-trusted.sh";
9
+ export const WORK_MESH_BIND_TRUSTED_HELP = `hq-core: public
10
+ US-011 — write session meta + reconcile with observation.trustedContext.
11
+ No --trusted CLI flag in hq-cli; trusted bind is the observation field.
12
+ set -euo pipefail
13
+
14
+ ROOT="\${HQ_ROOT:-\${CLAUDE_PROJECT_DIR:-}}"
15
+ if [ -z "$ROOT" ]; then
16
+ `;
17
+ export const createWorkMeshBindDeps = (root, cwd, env, now) => ({
18
+ root, cwd, env, now, fs, pid: process.pid, tmpDir: env.TMPDIR || "/tmp", ulid: (date) => generateUlid(date.getTime()),
19
+ runHq: (args, childEnv) => {
20
+ const result = spawnSync("hq", args, { cwd, env: { ...childEnv, HQ_ROOT: root }, encoding: "utf8" });
21
+ return { code: result.status ?? 127, stdout: result.stdout ?? "", stderr: result.stderr ?? (result.error ? `${result.error.message}\n` : "") };
22
+ },
23
+ runHqSession: (scriptRoot, args, childEnv) => {
24
+ const result = spawnSync("bash", [path.join(scriptRoot, "core/scripts/hq-session.sh"), ...args], { cwd, env: childEnv, encoding: "utf8" });
25
+ return { code: result.status ?? 127, stdout: result.stdout ?? "", stderr: result.stderr ?? (result.error ? `${result.error.message}\n` : "") };
26
+ },
27
+ spawnHq: (args, childEnv) => {
28
+ const child = spawn("hq", args, { cwd, env: { ...childEnv, HQ_ROOT: root }, detached: true, stdio: "ignore" });
29
+ child.unref();
30
+ },
31
+ });
32
+ export function resolveWorkMeshBindSessionId(root, env, fileSystem = fs) {
33
+ for (const key of ["HQ_SESSION_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID"]) {
34
+ const value = (env[key] ?? "").replace(/\s/g, "");
35
+ if (value)
36
+ return value;
37
+ }
38
+ try {
39
+ const value = fileSystem.readFileSync(path.join(root, "workspace/sessions/.current"), "utf8").replace(/\s/g, "");
40
+ return value;
41
+ }
42
+ catch {
43
+ return "";
44
+ }
45
+ }
46
+ function hasExecutable(deps, command) {
47
+ for (const directory of (deps.env.PATH ?? "").split(path.delimiter)) {
48
+ if (!directory)
49
+ continue;
50
+ try {
51
+ const stat = deps.fs.statSync(path.join(directory, command));
52
+ if (stat.isFile() && (stat.mode & 0o111) !== 0)
53
+ return true;
54
+ }
55
+ catch { /* PATH entry is absent */ }
56
+ }
57
+ return false;
58
+ }
59
+ function workMeshJsonQuote(value) {
60
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
61
+ }
62
+ function workMeshObservationJson(observation) {
63
+ const identity = observation.identity;
64
+ const trusted = observation.trustedContext;
65
+ let json = `{"contractVersion":1,"clientOperationId":${workMeshJsonQuote(String(observation.clientOperationId))},"identity":{"sessionId":${workMeshJsonQuote(identity.sessionId)},"harness":${workMeshJsonQuote(identity.harness)},"adapterVersion":${workMeshJsonQuote(identity.adapterVersion)}},"cwd":${workMeshJsonQuote(String(observation.cwd))},"hqRoot":${workMeshJsonQuote(String(observation.hqRoot))}`;
66
+ if (trusted) {
67
+ json += ',"trustedContext":{';
68
+ let first = true;
69
+ for (const [key, value] of Object.entries(trusted)) {
70
+ if (!first)
71
+ json += ",";
72
+ first = false;
73
+ json += `${workMeshJsonQuote(key)}:${workMeshJsonQuote(value)}`;
74
+ }
75
+ json += "}";
76
+ }
77
+ return `${json}}`;
78
+ }
79
+ export function runWorkMeshBindTrusted(args, deps) {
80
+ let root = deps.env.HQ_ROOT || deps.env.CLAUDE_PROJECT_DIR || deps.root;
81
+ let company = "", project = "", task = "", sid = "", noReconcile = false;
82
+ const output = [];
83
+ for (let i = 0; i < args.length;) {
84
+ const arg = args[i];
85
+ if (["--company", "--company-slug", "--project", "--task", "--session", "--session-id", "--root"].includes(arg)) {
86
+ const value = args[i + 1] ?? "";
87
+ if (arg === "--company" || arg === "--company-slug")
88
+ company = value;
89
+ else if (arg === "--project")
90
+ project = value;
91
+ else if (arg === "--task")
92
+ task = value;
93
+ else if (arg === "--session" || arg === "--session-id")
94
+ sid = value;
95
+ else
96
+ root = value;
97
+ i += 2;
98
+ }
99
+ else if (arg === "--no-reconcile") {
100
+ noReconcile = true;
101
+ i += 1;
102
+ }
103
+ else if (arg === "-h" || arg === "--help")
104
+ return { code: 0, stdout: WORK_MESH_BIND_TRUSTED_HELP.replace(/^# ?/gm, ""), stderr: "" };
105
+ else
106
+ i += 1;
107
+ }
108
+ if (!sid) {
109
+ sid = resolveWorkMeshBindSessionId(root, deps.env, deps.fs);
110
+ if (!sid) {
111
+ try {
112
+ sid = deps.fs.readFileSync(path.join(root, "workspace/sessions/.current"), "utf8").replace(/\s/g, "");
113
+ }
114
+ catch {
115
+ sid = "";
116
+ }
117
+ }
118
+ }
119
+ const childEnv = { ...deps.env, HQ_ROOT: root };
120
+ const pin = sid ? ["--session-id", sid] : [];
121
+ let stderr = "";
122
+ for (const [key, value] of [["company_slug", company], ["project", project], ["task", task]]) {
123
+ if (!value)
124
+ continue;
125
+ const result = deps.runHqSession(root, [...pin, "set", key, value], deps.env);
126
+ output.push(result.stdout);
127
+ stderr += result.stderr;
128
+ if (result.code !== 0)
129
+ return { code: result.code, stdout: output.join(""), stderr };
130
+ }
131
+ if (noReconcile || !sid)
132
+ return { code: 0, stdout: output.join(""), stderr };
133
+ let harness = deps.env.HQ_HARNESS || deps.env.HQ_WORK_MESH_HARNESS || "claude-code";
134
+ if (["claude", "Claude", "ClaudeCode"].includes(harness))
135
+ harness = "claude-code";
136
+ else if (harness === "Codex")
137
+ harness = "codex";
138
+ else if (harness === "Grok")
139
+ harness = "grok";
140
+ const adapter = deps.env.HQ_ADAPTER_CONTRACT_VERSION || "1.0.0";
141
+ const clientOperationId = deps.ulid(deps.now());
142
+ const observation = {
143
+ contractVersion: 1,
144
+ clientOperationId,
145
+ identity: { sessionId: sid, harness, adapterVersion: adapter },
146
+ cwd: deps.env.PWD ?? "",
147
+ hqRoot: root,
148
+ };
149
+ if (company || project || task) {
150
+ const trusted = {};
151
+ if (company)
152
+ trusted.companySlug = company;
153
+ if (project)
154
+ trusted.project = project;
155
+ if (task)
156
+ trusted.task = task;
157
+ observation.trustedContext = trusted;
158
+ }
159
+ const directory = path.join(deps.tmpDir, "hq-work-mesh-obs");
160
+ try {
161
+ deps.fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
162
+ deps.fs.chmodSync(directory, 0o700);
163
+ }
164
+ catch { /* legacy mkdir/chmod are best effort */ }
165
+ const observationFile = path.join(directory, `${sid}.bind.${deps.pid}.json`);
166
+ try {
167
+ deps.fs.writeFileSync(observationFile, `${workMeshObservationJson(observation)}\n`);
168
+ }
169
+ catch { /* legacy printf redirection failure is non-fatal */ }
170
+ if (deps.env.HQ_WORK_MESH_RECONCILE_LOG) {
171
+ try {
172
+ deps.fs.appendFileSync(deps.env.HQ_WORK_MESH_RECONCILE_LOG, `reconcile-trusted ${observationFile}\n`);
173
+ }
174
+ catch { /* legacy log write is best effort */ }
175
+ }
176
+ if (deps.env.HQ_WORK_MESH_RECONCILE_STUB === "1")
177
+ return { code: 0, stdout: output.join(""), stderr };
178
+ if (hasExecutable(deps, "hq") && deps.fs.existsSync(observationFile)) {
179
+ deps.spawnHq(["mesh", "context", "reconcile", "--observation-file", observationFile, "--machine"], childEnv);
180
+ }
181
+ return { code: 0, stdout: output.join(""), stderr };
182
+ }
183
+ export function registerCoreWorkMeshBindTrustedNative(core) {
184
+ registerNativeUtility(core, {
185
+ name: "work-mesh-bind-trusted", asset: WORK_MESH_BIND_TRUSTED_ASSET, root: "live", summary: "Bind trusted session context and reconcile it with Work Mesh",
186
+ resolveRoot: (_cwd, hqRoot) => resolveLiveRoot({ hqRoot }),
187
+ run: (args, context) => runWorkMeshBindTrusted(args, createWorkMeshBindDeps(context.root, context.cwd, context.env, context.now)),
188
+ });
189
+ }
190
+ //# sourceMappingURL=core-work-mesh-bind-trusted-native.js.map
@@ -40,9 +40,11 @@ import { registerCoreJobsNativeCommand } from "./core-job-native.js";
40
40
  import { registerCoreResolveCompanyNativeCommand } from "./core-resolve-company-command.js";
41
41
  import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
42
42
  import { registerCoreAuditLogNative } from "./core-audit-log-native.js";
43
+ import { registerCoreDmBindNative } from "./core-dm-bind-native.js";
43
44
  import { registerCoreGitPackExtensionCheckNative } from "./core-git-pack-extension-check-native.js";
44
45
  import { registerCorePinNative } from "./core-pin-native.js";
45
46
  import { registerCoreRefreshVaultAccessNative } from "./core-refresh-vault-access-native.js";
47
+ import { registerCoreWorkMeshBindTrustedNative } from "./core-work-mesh-bind-trusted-native.js";
46
48
  import { resolveLiveRoot } from "../utils/hq-roots.js";
47
49
  import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
48
50
  import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
@@ -227,6 +229,8 @@ export const SCAFFOLD_ONLY_ASSETS = [
227
229
  { asset: "core/scripts/hq-job-run.sh", root: "live", summary: "Scheduled jobs runner used by generated systemd units" },
228
230
  { asset: "core/scripts/hq-job-notify.sh", root: "live", summary: "Scheduled jobs failure notification helper" },
229
231
  { asset: "core/scripts/hq-job-remediate.sh", root: "live", summary: "Scheduled jobs bounded remediation helper" },
232
+ { asset: "core/scripts/hq-dm-bind.sh", root: "live", summary: "Rollback shell for native dm-bind" },
233
+ { asset: "core/scripts/work-mesh-live-bind-trusted.sh", root: "live", summary: "Rollback shell for native work-mesh-bind-trusted" },
230
234
  // Shared shell libs sourced by the two relocated scripts (and hooks). Never
231
235
  // dispatched on their own (so no `hq core` command), but they MUST ship in the
232
236
  // tarball, or the bundled hq-session / checkpoint-stop-gate die at `source`
@@ -372,6 +376,8 @@ export function registerCoreCommands(program) {
372
376
  registerCoreGitPackExtensionCheckNative(core);
373
377
  registerCorePinNative(core);
374
378
  registerCoreRefreshVaultAccessNative(core);
379
+ registerCoreDmBindNative(core);
380
+ registerCoreWorkMeshBindTrustedNative(core);
375
381
  // Wave 2 handoff/worktree commands retain their byte-identical shell assets
376
382
  // as a default-off rollback path for one release. Keep the gate helper small
377
383
  // so sibling Wave 2 work can reuse it without editing this command table.
@@ -511,7 +511,7 @@ export function registerFilesCommand(program) {
511
511
  });
512
512
  files
513
513
  .command("delete <prefix>")
514
- .description("Delete vault objects under a prefix (bounded + scoped). Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.")
514
+ .description("Delete vault objects by exact key or bounded prefix. A trailing slash expands recursively; use * for an explicit wildcard prefix. Always previews the exact count first; prompts for confirmation unless --yes. Use --personal to target your own personal vault instead of a company.")
515
515
  .option("--dry-run", "List what WOULD be deleted without deleting anything")
516
516
  .option("--personal", "Target your own personal vault instead of a company vault (mutually exclusive with --company)")
517
517
  .option("-y, --yes", "Skip the confirmation prompt (for scripts)")
@@ -38,7 +38,7 @@ export function syncSearchIndex(hqRoot, embed, dependencies = defaults) {
38
38
  catch {
39
39
  /* capping is an optimisation; indexing is the job */
40
40
  }
41
- dependencies.runQmd(['update'], { cwd: hqRoot });
41
+ dependencies.runQmd(['update'], { cwd: hqRoot, entryPath: 'index-sync' });
42
42
  };
43
43
  const writeStderr = dependencies.writeStderr ?? ((text) => process.stderr.write(text));
44
44
  const singleFlightEnabled = (dependencies.singleFlightEnabled ?? qmdReindexSingleFlightEnabled)();
@@ -288,13 +288,18 @@ export interface PendingApproval {
288
288
  requestedByName?: string | null;
289
289
  createdAt: string;
290
290
  expiresAt: string;
291
+ /** Queue state is present on history rows; older servers may omit it. */
292
+ status?: string;
293
+ /** Owner-facing outcome projected by hq-pro for decided rows. */
294
+ outcome?: string;
291
295
  }
292
296
  /**
293
- * Live open-approval list, read from hq-pro's confirm-queue state (not the
294
- * audit feed). Unlike the audit-derived reconstruction this replaces, a call
295
- * that was approved/rejected/expired no longer appears here.
297
+ * Read open approvals from hq-pro's confirm-queue state (not the audit feed).
298
+ * Pass `includeHistory` to include recently decided rows alongside open ones.
296
299
  */
297
- export declare function fetchPendingApprovals(token: string, companyUid: string): Promise<PendingApproval[]>;
300
+ export declare function fetchPendingApprovals(token: string, companyUid: string, options?: {
301
+ includeHistory?: boolean;
302
+ }): Promise<PendingApproval[]>;
298
303
  export declare function getConnectionAccess(token: string, companyUid: string, connectionId: string): Promise<ConnectionAccess>;
299
304
  export declare function mutateConnectionAccess(token: string, companyUid: string, action: "grant" | "revoke", input: {
300
305
  connectionId: string;
@@ -148,20 +148,23 @@ export async function setReadSafe(token, companyUid, connectionId, toolName, rea
148
148
  await raiseForResponse(res, "Failed to update the tool's read-safe setting");
149
149
  }
150
150
  /**
151
- * Live open-approval list, read from hq-pro's confirm-queue state (not the
152
- * audit feed). Unlike the audit-derived reconstruction this replaces, a call
153
- * that was approved/rejected/expired no longer appears here.
151
+ * Read open approvals from hq-pro's confirm-queue state (not the audit feed).
152
+ * Pass `includeHistory` to include recently decided rows alongside open ones.
154
153
  */
155
- export async function fetchPendingApprovals(token, companyUid) {
154
+ export async function fetchPendingApprovals(token, companyUid, options = {}) {
156
155
  const res = await vaultApiFetch({
157
156
  token,
158
157
  path: "/v1/integrations/factory/pending",
159
- query: { companyUid },
158
+ query: {
159
+ companyUid,
160
+ ...(options.includeHistory ? { include: "history" } : {}),
161
+ },
160
162
  });
161
163
  if (!res.ok)
162
164
  await raiseForResponse(res, "Failed to list pending approvals");
163
165
  const body = (await res.json());
164
- return body.pending ?? [];
166
+ const pending = body.pending ?? [];
167
+ return options.includeHistory ? [...pending, ...(body.history ?? [])] : pending;
165
168
  }
166
169
  export async function getConnectionAccess(token, companyUid, connectionId) {
167
170
  const res = await vaultApiFetch({
@@ -562,6 +562,7 @@ export function registerManageCommands(integrations) {
562
562
  .description("Calls that were queued for an owner's approval")
563
563
  .option("--company <slug>", "Company slug, e.g. indigo")
564
564
  .option("--json", "Machine-readable output")
565
+ .option("--history", "Include recently decided calls")
565
566
  .action(async (opts) => {
566
567
  const token = await ensureCognitoIdToken();
567
568
  const companyUid = await getCompanyUid(token, opts.company);
@@ -570,7 +571,9 @@ export function registerManageCommands(integrations) {
570
571
  // could only show that a call was once queued.
571
572
  let pending;
572
573
  try {
573
- pending = await fetchPendingApprovals(token, companyUid);
574
+ pending = await fetchPendingApprovals(token, companyUid, {
575
+ includeHistory: opts.history,
576
+ });
574
577
  }
575
578
  catch (err) {
576
579
  // This CLI can ship ahead of the hq-pro deploy that adds the pending
@@ -586,14 +589,19 @@ export function registerManageCommands(integrations) {
586
589
  return;
587
590
  }
588
591
  if (pending.length === 0) {
589
- console.log("Nothing is waiting for approval.");
592
+ console.log(opts.history ? "No recent approval history." : "Nothing is waiting for approval.");
590
593
  return;
591
594
  }
592
595
  for (const row of pending) {
593
596
  const who = row.requestedByName ?? row.requestedBy;
594
597
  const label = row.requestedTool ?? row.toolName;
595
598
  console.log(`${chalk.bold(label)} ${chalk.dim(`${who} · ${row.createdAt}`)}`);
596
- console.log(chalk.dim(` hq integrations approve ${row.queueId}${row.provider ? ` --provider ${bareProvider(row.provider)}` : ""}`));
599
+ if (opts.history) {
600
+ console.log(chalk.dim(` ${row.queueId} · ${row.status ?? row.outcome ?? "status unavailable"}`));
601
+ }
602
+ else {
603
+ console.log(chalk.dim(` hq integrations approve ${row.queueId}${row.provider ? ` --provider ${bareProvider(row.provider)}` : ""}`));
604
+ }
597
605
  }
598
606
  });
599
607
  integrations
@@ -22,6 +22,7 @@
22
22
  * hq integrations tools --provider <p> What the connected app can do.
23
23
  * hq integrations call <tool> --provider <p> --args '<json>'
24
24
  * hq integrations pending Calls awaiting approval.
25
+ * hq integrations pending --history Recently decided calls, including status.
25
26
  * hq integrations approve|reject <queueId> Decide a queued call.
26
27
  *
27
28
  * Govern and remove:
@@ -49,7 +50,14 @@
49
50
  * `integrations-manage.ts`.
50
51
  */
51
52
  import { Command } from "commander";
53
+ import { type PendingApproval } from "./integrations-api.js";
52
54
  export { IntegrationsCliError, callGateway, fetchConnections, queuedOutcome, selectConnection, toolPrefixForProvider, unwrapGatewayResult, } from "./integrations-core.js";
53
55
  export type { AdminConnection } from "./integrations-core.js";
56
+ /** Wait for a decided queue item without resending the approve request. */
57
+ export declare function waitForApprovalTerminal(token: string, companyUid: string, queueId: string, options?: {
58
+ timeoutMs?: number;
59
+ now?: () => number;
60
+ sleep?: (milliseconds: number) => Promise<void>;
61
+ }): Promise<PendingApproval | null>;
54
62
  export declare function registerIntegrationsCommand(program: Command): void;
55
63
  //# sourceMappingURL=integrations.d.ts.map
@@ -22,6 +22,7 @@
22
22
  * hq integrations tools --provider <p> What the connected app can do.
23
23
  * hq integrations call <tool> --provider <p> --args '<json>'
24
24
  * hq integrations pending Calls awaiting approval.
25
+ * hq integrations pending --history Recently decided calls, including status.
25
26
  * hq integrations approve|reject <queueId> Decide a queued call.
26
27
  *
27
28
  * Govern and remove:
@@ -57,6 +58,7 @@ import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isCl
57
58
  import { registerConnectCommands } from "./integrations-connect.js";
58
59
  import { registerImportCommands } from "./integrations-import.js";
59
60
  import { registerManageCommands } from "./integrations-manage.js";
61
+ import { fetchPendingApprovals } from "./integrations-api.js";
60
62
  export { IntegrationsCliError, callGateway, fetchConnections, queuedOutcome, selectConnection, toolPrefixForProvider, unwrapGatewayResult, } from "./integrations-core.js";
61
63
  /**
62
64
  * Gateway metadata is additive and may be absent when talking to an older
@@ -160,6 +162,55 @@ function reportRevokedConnection(connection, opts) {
160
162
  }
161
163
  return true;
162
164
  }
165
+ const APPROVAL_POLL_TIMEOUT_MS = 5 * 60_000;
166
+ const APPROVAL_POLL_INITIAL_DELAY_MS = 1_000;
167
+ const APPROVAL_POLL_MAX_DELAY_MS = 15_000;
168
+ const APPROVAL_TERMINAL_STATUSES = new Set([
169
+ "completed",
170
+ "resumed",
171
+ "failed",
172
+ "write_unknown",
173
+ "rejected",
174
+ "expired",
175
+ ]);
176
+ const APPROVAL_TERMINAL_OUTCOMES = new Set([
177
+ "ran",
178
+ "failed",
179
+ "unknown",
180
+ "turned_down",
181
+ "timed_out",
182
+ ]);
183
+ function isTerminalApproval(row) {
184
+ return APPROVAL_TERMINAL_STATUSES.has(row.status ?? "") ||
185
+ APPROVAL_TERMINAL_OUTCOMES.has(row.outcome ?? "");
186
+ }
187
+ /** Wait for a decided queue item without resending the approve request. */
188
+ export async function waitForApprovalTerminal(token, companyUid, queueId, options = {}) {
189
+ const timeoutMs = options.timeoutMs ?? APPROVAL_POLL_TIMEOUT_MS;
190
+ const now = options.now ?? Date.now;
191
+ const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
192
+ const deadline = now() + timeoutMs;
193
+ let delayMs = APPROVAL_POLL_INITIAL_DELAY_MS;
194
+ while (now() < deadline) {
195
+ const rows = await fetchPendingApprovals(token, companyUid, { includeHistory: true });
196
+ const row = rows.find((candidate) => candidate.queueId === queueId);
197
+ if (row && isTerminalApproval(row))
198
+ return row;
199
+ const remainingMs = deadline - now();
200
+ if (remainingMs <= 0)
201
+ break;
202
+ await sleep(Math.min(delayMs, remainingMs));
203
+ delayMs = Math.min(delayMs * 2, APPROVAL_POLL_MAX_DELAY_MS);
204
+ }
205
+ return null;
206
+ }
207
+ function isPendingApprovalResponse(status) {
208
+ return ["pending", "approved", "approved_pending_run", "in_progress"].includes(status ?? "");
209
+ }
210
+ function approvalOutcomeFailed(row) {
211
+ return ["failed", "write_unknown"].includes(row.status ?? "") ||
212
+ ["failed", "unknown"].includes(row.outcome ?? "");
213
+ }
163
214
  export function registerIntegrationsCommand(program) {
164
215
  const integrations = program
165
216
  .command("integrations")
@@ -347,6 +398,38 @@ export function registerIntegrationsCommand(program) {
347
398
  raiseIfUpstreamUnavailable(res);
348
399
  throw new IntegrationsCliError(body.error ?? `${decision} failed (HTTP ${res.status})`, { expected: isClientError(res.status) });
349
400
  }
401
+ const pendingApprove = decision === "approve" &&
402
+ (res.status === 202 || isPendingApprovalResponse(body.status));
403
+ if (pendingApprove) {
404
+ const terminal = await waitForApprovalTerminal(token, companyUid, queueId);
405
+ const historyCommand = `hq integrations pending --history${opts.company ? ` --company ${opts.company}` : ""}`;
406
+ if (!terminal) {
407
+ if (opts.json) {
408
+ printJson({ ...body, status: "pending", pollTimedOut: true, checkCommand: historyCommand });
409
+ }
410
+ else {
411
+ console.log(chalk.yellow(`Approval is still running after 5 minutes. Check queue ${queueId} later with: ${historyCommand}`));
412
+ }
413
+ return;
414
+ }
415
+ if (opts.json)
416
+ printJson(terminal);
417
+ else if (approvalOutcomeFailed(terminal)) {
418
+ const unknown = terminal.status === "write_unknown" || terminal.outcome === "unknown";
419
+ console.log(chalk.red(unknown
420
+ ? `The provider outcome is unknown for ${queueId}. Check the provider before taking any action; ${historyCommand} shows the latest queue state.`
421
+ : `The approved call failed (${terminal.status ?? terminal.outcome}). See ${historyCommand} for its status.`));
422
+ }
423
+ else if (terminal.status === "resumed" || terminal.status === "completed" || terminal.outcome === "ran") {
424
+ console.log(chalk.green("Approved — the call completed."));
425
+ }
426
+ else {
427
+ console.log(chalk.yellow(`Approval ended with status ${terminal.status ?? terminal.outcome ?? "unknown"}.`));
428
+ }
429
+ if (approvalOutcomeFailed(terminal))
430
+ process.exitCode = 1;
431
+ return;
432
+ }
350
433
  if (opts.json) {
351
434
  printJson(body);
352
435
  return;
@@ -19,7 +19,6 @@ export interface ProjectMapping {
19
19
  excludePrefix?: string;
20
20
  }
21
21
  export declare function mappingIncludesStory(mapping: ProjectMapping, storyId: string): boolean;
22
- export declare function ambiguousProjectMappings(mappings: readonly ProjectMapping[]): Map<string, string[]>;
23
22
  export interface ProjectReadResult {
24
23
  stories: Array<Record<string, unknown>>;
25
24
  mtime: number;
@@ -35,7 +34,6 @@ export declare function readProject(root: string, company: string, prdPath: stri
35
34
  export declare function loadCompanyQuestionRecords(root: string, company: LaneCompanyRef, projectId?: string): QuestionRecord[];
36
35
  export declare function readTeamInputs(root: string, company: LaneCompanyRef, projects: readonly ProjectMapping[], lanes: readonly LaneRecord[]): TrackerCheckTeam[];
37
36
  export declare function pathForTrackerRecord(projectDirectory: string, kind: TrackerKind, id: string): string;
38
- export declare function listTrackerRecords(projectDirectory: string, kind: TrackerKind): TrackerRecord[];
39
37
  export interface TolerantTrackerReadIssue {
40
38
  path: string;
41
39
  field: string;