@indigoai-us/hq-cli 5.306.0 → 5.308.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 (56) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/assets/scaffold/core/scripts/audit-log.sh +384 -0
  3. package/assets/scaffold/core/scripts/git-pack-extension-check.sh +164 -0
  4. package/assets/scaffold/core/scripts/hq-pin.sh +118 -0
  5. package/assets/scaffold/core/scripts/lib/portable.sh +211 -0
  6. package/assets/scaffold/core/scripts/refresh-vault-access.sh +188 -0
  7. package/assets/scaffold/core/scripts/resolve-company.sh +199 -0
  8. package/dist/command-catalog.generated.d.ts +65 -0
  9. package/dist/command-catalog.generated.js +80 -0
  10. package/dist/commands/core-audit-log-native.d.ts +9 -0
  11. package/dist/commands/core-audit-log-native.js +257 -0
  12. package/dist/commands/core-git-pack-extension-check-native.d.ts +22 -0
  13. package/dist/commands/core-git-pack-extension-check-native.js +190 -0
  14. package/dist/commands/core-native-utilities-flag.d.ts +4 -0
  15. package/dist/commands/core-native-utilities-flag.js +7 -0
  16. package/dist/commands/core-native-utilities.d.ts +28 -0
  17. package/dist/commands/core-native-utilities.js +52 -0
  18. package/dist/commands/core-pin-native.d.ts +14 -0
  19. package/dist/commands/core-pin-native.js +236 -0
  20. package/dist/commands/core-refresh-vault-access-native.d.ts +20 -0
  21. package/dist/commands/core-refresh-vault-access-native.js +229 -0
  22. package/dist/commands/core-resolve-company-command.d.ts +4 -0
  23. package/dist/commands/core-resolve-company-command.js +42 -0
  24. package/dist/commands/core-resolve-company-native.d.ts +28 -0
  25. package/dist/commands/core-resolve-company-native.js +408 -0
  26. package/dist/commands/core.js +18 -0
  27. package/dist/commands/dm.js +6 -1
  28. package/dist/commands/lanes.js +43 -1
  29. package/dist/fast-scaffold.js +12 -0
  30. package/dist/lib/agent-kit/fallback.d.ts +0 -7
  31. package/dist/lib/agent-kit/fallback.js +4 -4
  32. package/dist/lib/agent-kit/inbox-state.d.ts +0 -2
  33. package/dist/lib/agent-kit/inbox-state.js +2 -2
  34. package/dist/lib/agent-kit/kit-config.d.ts +0 -5
  35. package/dist/lib/agent-kit/kit-config.js +5 -5
  36. package/dist/lib/agent-kit/log.d.ts +0 -1
  37. package/dist/lib/agent-kit/log.js +1 -1
  38. package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +3 -12
  39. package/dist/lib/agent-kit/mcp/jsonrpc.js +4 -4
  40. package/dist/lib/agent-kit/mcp/tools.d.ts +0 -2
  41. package/dist/lib/agent-kit/mcp/tools.js +2 -2
  42. package/dist/lib/agent-kit/services.d.ts +0 -1
  43. package/dist/lib/agent-kit/services.js +1 -1
  44. package/dist/lib/checkpoint-ontology/apply.d.ts +0 -49
  45. package/dist/lib/checkpoint-ontology/apply.js +5 -5
  46. package/dist/lib/checkpoint-ontology/identity.d.ts +2 -1
  47. package/dist/lib/checkpoint-ontology/identity.js +1 -1
  48. package/dist/lib/checkpoint-ontology/multi-writer.d.ts +4 -9
  49. package/dist/lib/checkpoint-ontology/multi-writer.js +6 -6
  50. package/dist/lib/checkpoint-ontology/plan.d.ts +4 -17
  51. package/dist/lib/checkpoint-ontology/plan.js +5 -5
  52. package/dist/lib/checkpoint-ontology/queued-touches.d.ts +0 -3
  53. package/dist/lib/checkpoint-ontology/queued-touches.js +3 -3
  54. package/dist/lib/db/control-plane.js +6 -0
  55. package/dist/lib/deploy-apps.js +5 -0
  56. package/package.json +1 -1
@@ -0,0 +1,408 @@
1
+ /** Native port of resolve-company.sh. This module stays on the fast import path. */
2
+ import { spawn } from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ export const RESOLVE_COMPANY_ASSET = "core/scripts/resolve-company.sh";
6
+ export const RESOLVE_COMPANY_HELP = [
7
+ "hq-core: public",
8
+ "resolve-company.sh — resolve the active company for skills and routing.",
9
+ "",
10
+ "Resolution order (first hit wins): explicit manifest slug in the prompt,",
11
+ "session company_slug, enabled device default, then none. Prompt matching is",
12
+ "whole-token only; free text never guesses a company.",
13
+ "",
14
+ "Output: {\"company\":\"<slug>\",\"source\":\"prompt|session|device_default|none\"}",
15
+ "Always exits 0 and never writes state.",
16
+ "",
17
+ "set -uo pipefail",
18
+ "",
19
+ "ROOT=\"\"",
20
+ ].join("\n") + "\n";
21
+ const EMPTY_ENV = {};
22
+ const keepSlug = (slug) => slug !== "" && slug !== "_template" && slug !== "companies" && slug !== "unaffiliated_repos";
23
+ function errorCode(error) {
24
+ return error && typeof error === "object" && "code" in error && typeof error.code === "string"
25
+ ? error.code
26
+ : undefined;
27
+ }
28
+ function readFile(filePath, fileSystem) {
29
+ try {
30
+ return fileSystem.readFileSync(filePath, "utf8");
31
+ }
32
+ catch (error) {
33
+ const code = errorCode(error);
34
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
35
+ return undefined;
36
+ throw error;
37
+ }
38
+ }
39
+ function readManifest(filePath, fileSystem) {
40
+ try {
41
+ return fileSystem.readFileSync(filePath, "utf8");
42
+ }
43
+ catch (error) {
44
+ const code = errorCode(error);
45
+ if (code === "EACCES") {
46
+ return {
47
+ code: 0,
48
+ stdout: Buffer.from('{"company":"","source":"none"}\n'),
49
+ stderr: Buffer.from("awk: fatal: cannot open file `" + filePath + "' for reading: Permission denied\n"),
50
+ };
51
+ }
52
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP")
53
+ return output("", "none");
54
+ throw error;
55
+ }
56
+ }
57
+ function isFile(filePath, fileSystem) {
58
+ try {
59
+ return fileSystem.statSync(filePath).isFile();
60
+ }
61
+ catch (error) {
62
+ const code = errorCode(error);
63
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
64
+ return false;
65
+ throw error;
66
+ }
67
+ }
68
+ function parseManifestSlugs(text) {
69
+ const slugs = new Set();
70
+ let wrapped = false;
71
+ for (const rawLine of text.split("\n")) {
72
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
73
+ if (/^companies:[ \t]*$/.test(line)) {
74
+ wrapped = true;
75
+ continue;
76
+ }
77
+ if (wrapped && /^[^ \t][^:]*:[ \t]*$/.test(line))
78
+ wrapped = false;
79
+ if (wrapped) {
80
+ const match = line.match(/^ {2}([A-Za-z][A-Za-z0-9_-]*):/);
81
+ if (match && keepSlug(match[1]))
82
+ slugs.add(match[1]);
83
+ continue;
84
+ }
85
+ const match = line.match(/^([A-Za-z][A-Za-z0-9_-]*):/);
86
+ if (match && keepSlug(match[1]))
87
+ slugs.add(match[1]);
88
+ }
89
+ return [...slugs];
90
+ }
91
+ function promptSlug(prompt, slugs) {
92
+ if (!prompt)
93
+ return "";
94
+ const asciiLower = (value) => value.replace(/[A-Z]/g, (letter) => String.fromCharCode(letter.charCodeAt(0) + 32));
95
+ const normalized = asciiLower(prompt).replace(/[^a-z0-9_-]/g, " ");
96
+ const positions = new Map();
97
+ normalized.split(/[ \t]+/).forEach((token, index) => {
98
+ if (token && !positions.has(token))
99
+ positions.set(token, index + 1);
100
+ });
101
+ const matches = new Map();
102
+ for (const slug of slugs) {
103
+ const key = asciiLower(slug);
104
+ if (!positions.has(key))
105
+ continue;
106
+ matches.set(key, [...(matches.get(key) ?? []), slug]);
107
+ }
108
+ let bestSlug = "";
109
+ let bestLength = 0;
110
+ let bestPosition = 0;
111
+ for (const [key, candidates] of matches) {
112
+ if (candidates.length !== 1)
113
+ continue;
114
+ const slug = candidates[0];
115
+ const length = slug.length;
116
+ const position = positions.get(key);
117
+ if (length > bestLength || (length === bestLength && position < bestPosition)) {
118
+ bestSlug = slug;
119
+ bestLength = length;
120
+ bestPosition = position;
121
+ }
122
+ }
123
+ return bestSlug;
124
+ }
125
+ function validSessionId(value) {
126
+ return value !== "" && value !== "." && value !== ".." && /^[A-Za-z0-9._-]+$/.test(value);
127
+ }
128
+ function sessionId(root, env, fileSystem) {
129
+ for (const name of ["HQ_SESSION_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "GROK_SESSION_ID"]) {
130
+ const value = (env[name] ?? "").replace(/[ \t\n\v\f\r]/g, "");
131
+ if (validSessionId(value))
132
+ return value;
133
+ }
134
+ const current = readFile(path.join(root, "workspace/sessions/.current"), fileSystem)?.replace(/[ \t\n\v\f\r]/g, "") ?? "";
135
+ return validSessionId(current) ? current : "";
136
+ }
137
+ function sessionCompany(root, env, fileSystem) {
138
+ const helper = path.join(root, "core/scripts/hq-session.sh");
139
+ try {
140
+ fileSystem.accessSync(helper, fs.constants.X_OK);
141
+ }
142
+ catch (error) {
143
+ const code = errorCode(error);
144
+ if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
145
+ return "";
146
+ throw error;
147
+ }
148
+ // A directory may be executable according to access(2), but `bash directory`
149
+ // fails without output, just as hq-session.sh does here.
150
+ if (!isFile(helper, fileSystem))
151
+ return "";
152
+ const id = sessionId(root, env, fileSystem);
153
+ if (!id)
154
+ return "";
155
+ const meta = readFile(path.join(root, "workspace/sessions", id, "meta.yaml"), fileSystem);
156
+ if (meta === undefined)
157
+ return "";
158
+ let value = "";
159
+ for (const line of meta.split("\n")) {
160
+ const firstField = line.replace(/^[ \t]+/, "").split(/[ \t]+/, 1)[0];
161
+ if (firstField !== "company_slug:")
162
+ continue;
163
+ value = line.replace(/^[^:]+:[ \t]*/, "").replace(/^"|"$/g, "");
164
+ break;
165
+ }
166
+ // The shell's command substitution strips trailing newlines, then tr removes
167
+ // all remaining whitespace and double quotes from the helper's output.
168
+ return value.replace(/[ \t\n\v\f\r"]/g, "");
169
+ }
170
+ function fleetIdentityPresent(rootPrefix, env, fileSystem) {
171
+ if (env.HQ_AGENT_WORKDIR || env.HQ_AGENT_COMPANY_DIR || env.HQ_AGENT_IDENTITY_FILE)
172
+ return true;
173
+ const paths = [
174
+ "/etc/hq-agent/identity.json",
175
+ "/etc/hq-agent/machine-creds.json",
176
+ rootPrefix ? `${rootPrefix.replace(/\/$/, "")}/var/lib/hq-agent/identity.json` : "/var/lib/hq-agent/identity.json",
177
+ "/var/lib/hq-agent/machine-creds.json",
178
+ ];
179
+ return paths.some((filePath) => isFile(filePath, fileSystem));
180
+ }
181
+ function parseJson(text) {
182
+ try {
183
+ return JSON.parse(text);
184
+ }
185
+ catch {
186
+ return undefined;
187
+ }
188
+ }
189
+ function record(value) {
190
+ return value !== null && typeof value === "object" && !Array.isArray(value)
191
+ ? value
192
+ : undefined;
193
+ }
194
+ function normalizeShellValue(value) {
195
+ return value.replace(/[ \t\n\v\f\r"]/g, "");
196
+ }
197
+ function defaultSlugFromJson(text, knownSlugs) {
198
+ const root = record(parseJson(text));
199
+ if (!root)
200
+ return "";
201
+ const candidate = root.defaultCompany;
202
+ const preference = candidate === null || candidate === undefined || candidate === false
203
+ ? root
204
+ : record(candidate);
205
+ if (!preference)
206
+ return "";
207
+ if (preference.enabled !== true || (preference.source ?? "") === "disabled" || (preference.needsChoice ?? false) !== false)
208
+ return "";
209
+ if (typeof preference.slug !== "string")
210
+ return "";
211
+ const slug = normalizeShellValue(preference.slug);
212
+ return knownSlugs.has(slug) ? slug : "";
213
+ }
214
+ function validPreflight(text, slug, session, operation) {
215
+ const value = record(parseJson(text));
216
+ return Boolean(value && value.contractVersion === 1 && value.sessionId === session &&
217
+ value.clientOperationId === operation &&
218
+ typeof value.kind === "string" && ["queued", "needs_project", "needs_task", "bound"].includes(value.kind) &&
219
+ typeof value.classification === "string" && ["needs_project", "needs_task", "bound"].includes(value.classification) &&
220
+ typeof value.delivery === "string" && ["queued", "acked"].includes(value.delivery) && value.lifecycle === "open" &&
221
+ value.companySlug === slug &&
222
+ typeof value.companyUid === "string" && value.companyUid.length > 0);
223
+ }
224
+ function commandOnPath(name, env) {
225
+ const pathValue = env.PATH ?? "";
226
+ for (const directory of pathValue.split(path.delimiter)) {
227
+ const candidate = path.join(directory || ".", name);
228
+ try {
229
+ fs.accessSync(candidate, fs.constants.X_OK);
230
+ return true;
231
+ }
232
+ catch {
233
+ // `command -v` simply advances to the next PATH entry.
234
+ }
235
+ }
236
+ return false;
237
+ }
238
+ function runHq(args, env, timeoutMs, cwd) {
239
+ return new Promise((resolve) => {
240
+ let settled = false;
241
+ let stdout = "";
242
+ let child;
243
+ let timeoutKill;
244
+ let timedOut = false;
245
+ const signalGroup = (signal) => {
246
+ if (!child?.pid)
247
+ return;
248
+ try {
249
+ process.kill(-child.pid, signal);
250
+ }
251
+ catch (error) {
252
+ if (errorCode(error) !== "ESRCH")
253
+ finish({ code: 1, stdout });
254
+ }
255
+ };
256
+ const finish = (result) => {
257
+ if (settled)
258
+ return;
259
+ settled = true;
260
+ clearTimeout(timer);
261
+ if (timeoutKill)
262
+ clearTimeout(timeoutKill);
263
+ resolve(result);
264
+ };
265
+ const timer = setTimeout(() => {
266
+ timedOut = true;
267
+ if (child?.pid) {
268
+ signalGroup("SIGTERM");
269
+ // The shell wrapper returns shortly after its 2 second deadline. Do
270
+ // not wait for inherited stdio handles after killing the process group.
271
+ timeoutKill = setTimeout(() => {
272
+ signalGroup("SIGKILL");
273
+ finish({ code: 124, stdout });
274
+ }, 250);
275
+ }
276
+ else {
277
+ finish({ code: 124, stdout });
278
+ }
279
+ }, timeoutMs);
280
+ try {
281
+ child = spawn("hq", args, { cwd, detached: true, stdio: ["ignore", "pipe", "ignore"], env });
282
+ }
283
+ catch {
284
+ finish({ code: 127, stdout });
285
+ return;
286
+ }
287
+ child.stdout?.setEncoding("utf8");
288
+ child.stdout?.on("data", (chunk) => { stdout += chunk; });
289
+ child.once("error", () => finish({ code: 127, stdout }));
290
+ child.once("exit", (code, signal) => {
291
+ signalGroup("SIGTERM");
292
+ finish({ code: timedOut ? 124 : typeof code === "number" ? code : signal ? 128 : 1, stdout });
293
+ });
294
+ });
295
+ }
296
+ function output(company, source) {
297
+ return { code: 0, stdout: Buffer.from(`{"company":"${company}","source":"${source}"}\n`), stderr: Buffer.alloc(0) };
298
+ }
299
+ function parseArgs(args) {
300
+ let root = "";
301
+ let prompt = "";
302
+ let havePrompt = false;
303
+ for (let index = 0; index < args.length;) {
304
+ const arg = args[index];
305
+ if (arg === "--root") {
306
+ root = args[index + 1] ?? "";
307
+ index += Math.min(2, args.length - index);
308
+ }
309
+ else if (arg === "--prompt") {
310
+ prompt = args[index + 1] ?? "";
311
+ havePrompt = true;
312
+ index += Math.min(2, args.length - index);
313
+ }
314
+ else if (arg === "--help" || arg === "-h") {
315
+ return { root, prompt, havePrompt, help: true };
316
+ }
317
+ else {
318
+ index += 1;
319
+ }
320
+ }
321
+ return { root, prompt, havePrompt, help: false };
322
+ }
323
+ async function readStdin() {
324
+ const chunks = [];
325
+ for await (const chunk of process.stdin)
326
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
327
+ return Buffer.concat(chunks).toString("utf8");
328
+ }
329
+ export async function resolveCompany(args, dependencies) {
330
+ const env = dependencies.env ?? process.env;
331
+ const fileSystem = dependencies.fs ?? fs;
332
+ const parsed = parseArgs(args);
333
+ if (parsed.help)
334
+ return { code: 0, stdout: Buffer.from(RESOLVE_COMPANY_HELP), stderr: Buffer.alloc(0) };
335
+ let prompt = parsed.prompt;
336
+ if (!parsed.havePrompt && dependencies.stdinIsTty !== true) {
337
+ prompt = dependencies.stdin ?? await readStdin();
338
+ }
339
+ const root = parsed.root || dependencies.hqRoot || env.HQ_ROOT || env.CLAUDE_PROJECT_DIR || dependencies.cwd;
340
+ const manifest = path.join(root, "companies/manifest.yaml");
341
+ if (!isFile(manifest, fileSystem))
342
+ return output("", "none");
343
+ const manifestResult = readManifest(manifest, fileSystem);
344
+ if (typeof manifestResult !== "string")
345
+ return manifestResult;
346
+ const manifestText = manifestResult;
347
+ const slugs = parseManifestSlugs(manifestText);
348
+ if (slugs.length === 0)
349
+ return output("", "none");
350
+ const knownSlugs = new Set(slugs);
351
+ const promptMatch = promptSlug(prompt, slugs);
352
+ if (promptMatch)
353
+ return output(promptMatch, "prompt");
354
+ const sessionMatch = sessionCompany(root, env, fileSystem);
355
+ if (sessionMatch && knownSlugs.has(sessionMatch))
356
+ return output(sessionMatch, "session");
357
+ if (fleetIdentityPresent(env.HQ_AGENT_ROOT_PREFIX ?? "", env, fileSystem))
358
+ return output("", "none");
359
+ // jq is intentionally no longer needed. The native route is always hosted by
360
+ // Node; the old shell route required `node`, `hq`, and `jq` on PATH.
361
+ if (!commandOnPath("hq", env))
362
+ return output("", "none");
363
+ const hqEnv = { ...EMPTY_ENV, ...env };
364
+ if (dependencies.hqRoot) {
365
+ hqEnv.HQ_ROOT = dependencies.hqRoot;
366
+ hqEnv.CLAUDE_PROJECT_DIR = dependencies.hqRoot;
367
+ }
368
+ const invoke = dependencies.spawnHq ?? runHq;
369
+ const defaultResponse = await invoke(["mesh", "context", "default", "get", "--json"], hqEnv, 2_000, root);
370
+ const defaultSlug = defaultSlugFromJson(defaultResponse.stdout, knownSlugs);
371
+ if (!defaultSlug)
372
+ return output("", "none");
373
+ const pid = dependencies.pid ?? process.pid;
374
+ const random = dependencies.random ?? Math.random;
375
+ const session = `resolve-default-${pid}-${Math.floor(random() * 32768)}`;
376
+ const operation = `resolve-default-op-${pid}-${Math.floor(random() * 32768)}`;
377
+ const observation = JSON.stringify({
378
+ contractVersion: 1,
379
+ clientOperationId: operation,
380
+ identity: { sessionId: session, harness: "hq-core", adapterVersion: "1" },
381
+ cwd: root,
382
+ hqRoot: root,
383
+ }).replaceAll("\u007f", "\\u007f");
384
+ const preflight = await invoke([
385
+ "mesh", "context", "reconcile", "--observation-json", observation, "--machine", "--offline",
386
+ ], hqEnv, 2_000, root);
387
+ if (preflight.code === 0 && validPreflight(preflight.stdout, defaultSlug, session, operation)) {
388
+ return output(defaultSlug, "device_default");
389
+ }
390
+ return output("", "none");
391
+ }
392
+ export async function runResolveCompanyFast(args, stdin, cwd, hqRoot, _commandName, _hqRootArg, stdinReader = readStdin) {
393
+ const parsed = parseArgs(args);
394
+ const actualStdin = parsed.havePrompt || process.stdin.isTTY === true
395
+ ? stdin
396
+ : stdin || await stdinReader();
397
+ const result = await resolveCompany(args, {
398
+ cwd,
399
+ hqRoot,
400
+ stdin: actualStdin,
401
+ stdinIsTty: process.stdin.isTTY === true,
402
+ });
403
+ process.stdout.write(result.stdout);
404
+ if (result.stderr.length)
405
+ process.stderr.write(result.stderr);
406
+ return result.code;
407
+ }
408
+ //# sourceMappingURL=core-resolve-company-native.js.map
@@ -37,7 +37,12 @@ import { registerCoreOntologyCommand } from "./core-ontology.js";
37
37
  import { registerCoreKnowledgePrefsCommand } from "./core-knowledge-prefs.js";
38
38
  import { registerCorePolicyCommand } from "./core-policy.js";
39
39
  import { registerCoreJobsNativeCommand } from "./core-job-native.js";
40
+ import { registerCoreResolveCompanyNativeCommand } from "./core-resolve-company-command.js";
40
41
  import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
42
+ import { registerCoreAuditLogNative } from "./core-audit-log-native.js";
43
+ import { registerCoreGitPackExtensionCheckNative } from "./core-git-pack-extension-check-native.js";
44
+ import { registerCorePinNative } from "./core-pin-native.js";
45
+ import { registerCoreRefreshVaultAccessNative } from "./core-refresh-vault-access-native.js";
41
46
  import { resolveLiveRoot } from "../utils/hq-roots.js";
42
47
  import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
43
48
  import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
@@ -214,6 +219,7 @@ export const SCAFFOLD_COMMANDS = [
214
219
  * direct `hq core` command.
215
220
  */
216
221
  export const SCAFFOLD_ONLY_ASSETS = [
222
+ { asset: "core/scripts/resolve-company.sh", root: "live", summary: "Rollback companion for native resolve-company" },
217
223
  { asset: "core/scripts/jobs-schedule-parse.sh", root: "live", summary: "Rollback companion for native jobs schedule-parse" },
218
224
  { asset: "core/scripts/jobs-validate.sh", root: "live", summary: "Rollback companion for native jobs validate" },
219
225
  { asset: "core/scripts/outpost-jobs-reconcile.sh", root: "live", summary: "Rollback companion for native jobs reconcile" },
@@ -271,6 +277,13 @@ export const SCAFFOLD_ONLY_ASSETS = [
271
277
  { asset: "core/scripts/handoff-post.sh", root: "live", summary: "Rollback shell for native handoff post" },
272
278
  { asset: "core/scripts/handoff-open-steps.sh", root: "live", summary: "Rollback shell for native handoff open-steps" },
273
279
  { asset: "core/scripts/qmd-reindex-bg.sh", root: "live", summary: "Rollback handoff reindex forwarder" },
280
+ // Wave 5 native utility rollback companions, sourced verbatim from staging
281
+ // because these assets were not present in the hq-cli bundle at launch.
282
+ { asset: "core/scripts/audit-log.sh", root: "live", summary: "Rollback shell for native audit-log" },
283
+ { asset: "core/scripts/git-pack-extension-check.sh", root: "cwd", summary: "Rollback shell for native git-pack-extension-check" },
284
+ { asset: "core/scripts/hq-pin.sh", root: "live", summary: "Rollback shell for native pin" },
285
+ { asset: "core/scripts/refresh-vault-access.sh", root: "cwd", summary: "Rollback shell for native refresh-vault-access" },
286
+ { asset: "core/scripts/lib/portable.sh", root: "live", summary: "Shared portable helpers sourced by the audit-log rollback shell" },
274
287
  ...NATIVE_UTILITY_COMMANDS.map(({ asset, root, summary }) => ({
275
288
  asset,
276
289
  root,
@@ -354,6 +367,11 @@ export function registerCoreCommands(program) {
354
367
  registerCoreKnowledgePrefsCommand(core);
355
368
  registerCorePolicyCommand(core);
356
369
  registerCoreJobsNativeCommand(core);
370
+ registerCoreResolveCompanyNativeCommand(core);
371
+ registerCoreAuditLogNative(core);
372
+ registerCoreGitPackExtensionCheckNative(core);
373
+ registerCorePinNative(core);
374
+ registerCoreRefreshVaultAccessNative(core);
357
375
  // Wave 2 handoff/worktree commands retain their byte-identical shell assets
358
376
  // as a default-off rollback path for one release. Keep the gate helper small
359
377
  // so sibling Wave 2 work can reuse it without editing this command table.
@@ -407,7 +407,12 @@ async function runConnectionAction(action, identifier) {
407
407
  const token = await ensureCognitoToken();
408
408
  // Resolve the pairKey from the caller's pending requests when possible so
409
409
  // accept/decline/block work against a friendly email or personUid.
410
- const requests = await fetchRequests(token).catch(() => []);
410
+ const requests = await fetchRequests(token).catch((err) => {
411
+ console.error("hq dm: pending connection request lookup failed", {
412
+ errorName: err instanceof Error ? err.name : typeof err,
413
+ });
414
+ return [];
415
+ });
411
416
  const matched = matchRequest(requests, identifier);
412
417
  const body = buildConnectionActionBody(identifier, matched);
413
418
  const res = await vaultApiFetch({
@@ -184,8 +184,12 @@ async function defaultDispatchOutpostExec(input) {
184
184
  throw new LanesError("outpost_dispatch_failed", `Outpost exec failed: ${dispatchErrorMessage(err)}`, { outpost_id: input.outpostId, message: dispatchErrorMessage(err) });
185
185
  }
186
186
  }
187
+ // Pickup payloads are small; 30 seconds leaves room for normal network variance.
188
+ const OUTPOST_PICKUP_PAYLOAD_TIMEOUT_MS = 30_000;
187
189
  async function defaultFetchText(url) {
188
- const res = await fetch(url);
190
+ const res = await fetch(url, {
191
+ signal: AbortSignal.timeout(OUTPOST_PICKUP_PAYLOAD_TIMEOUT_MS),
192
+ });
189
193
  if (!res.ok) {
190
194
  throw new LanesError("outpost_payload_invalid", `Failed to download pickup payload: HTTP ${res.status}`, { status: res.status });
191
195
  }
@@ -1006,6 +1010,8 @@ const OUTPOST_HIRE_SUMMARY_TIMEOUT_SECONDS = 20;
1006
1010
  const OUTPOST_HIRE_SUMMARY_WAIT_MS = 15_000;
1007
1011
  const OUTPOST_SINGLE_LANE_WAIT_ENV = "HQ_LANES_OUTPOST_SINGLE_LANE_WAIT_MS";
1008
1012
  const OUTPOST_SINGLE_LANE_WAIT_MAX_MS = 600_000;
1013
+ const LEGACY_PICKUP_SETTLEMENT_MARGIN_MS = 60_000;
1014
+ const LEGACY_PICKUP_SETTLEMENT_MIN_AGE_MS = OUTPOST_SINGLE_LANE_WAIT_MAX_MS + LEGACY_PICKUP_SETTLEMENT_MARGIN_MS;
1009
1015
  function outpostSingleLaneWaitMs(env) {
1010
1016
  const raw = env[OUTPOST_SINGLE_LANE_WAIT_ENV]?.trim() || String(PICKUP_WAIT_MS);
1011
1017
  const value = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
@@ -1363,6 +1369,25 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
1363
1369
  !ownerIsHostedByOutpost ||
1364
1370
  (owner.company?.uid ?? null) !== (mirror.company?.uid ?? null) ||
1365
1371
  (owner.company?.slug ?? null) !== (mirror.company?.slug ?? null)) {
1372
+ if (matches.length === 0 && isLegacyFailedDispatchWithoutPickupEvidence(mirror, nowMs)) {
1373
+ const settled = retryUpdateLane(hqRoot, mirror.lane_id, "reconcile", (current) => {
1374
+ if (!isLegacyFailedDispatchWithoutPickupEvidence(current, nowMs) ||
1375
+ current.host.id !== outpostId) {
1376
+ return current;
1377
+ }
1378
+ return {
1379
+ ...current,
1380
+ terminal_details: {
1381
+ ...(current.terminal_details ?? {}),
1382
+ pickup_persisted: false,
1383
+ },
1384
+ };
1385
+ });
1386
+ if (isUndispatchedOutpostMirror(settled)) {
1387
+ result.updated.set(mirror.lane_id, settled);
1388
+ continue;
1389
+ }
1390
+ }
1366
1391
  if (matches.length === 0)
1367
1392
  result.missingOnOwner.set(mirror.lane_id, mirror);
1368
1393
  const detail = matches.length === 0
@@ -2530,6 +2555,23 @@ export function isUndispatchedOutpostMirror(lane) {
2530
2555
  lane.terminal_details?.pickup_persisted === false &&
2531
2556
  lane.last_host_report_at == null;
2532
2557
  }
2558
+ function isLegacyFailedDispatchWithoutPickupEvidence(lane, nowMs) {
2559
+ const terminalDetails = lane.terminal_details;
2560
+ const updatedAtMs = Date.parse(lane.updated_at);
2561
+ const completedFailure = (terminalDetails?.status === "Failed" ||
2562
+ terminalDetails?.status === "Cancelled" ||
2563
+ terminalDetails?.status === "TimedOut") &&
2564
+ typeof terminalDetails.exit_code === "number" &&
2565
+ Number.isSafeInteger(terminalDetails.exit_code);
2566
+ return lane.host.kind === "outpost" &&
2567
+ isTerminalLaneState(lane.state) &&
2568
+ lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
2569
+ lane.last_host_report_at == null &&
2570
+ lane.terminal_details?.pickup_persisted === undefined &&
2571
+ completedFailure &&
2572
+ Number.isFinite(updatedAtMs) &&
2573
+ nowMs - updatedAtMs > LEGACY_PICKUP_SETTLEMENT_MIN_AGE_MS;
2574
+ }
2533
2575
  function parsePickupTerminalReason(value) {
2534
2576
  if (PICKUP_TERMINAL_REASONS.includes(value)) {
2535
2577
  return value;
@@ -144,6 +144,18 @@ export const FAST_SCAFFOLD_SCRIPTS = [
144
144
  shellCwd: "caller",
145
145
  shellHqRoot: name === "reconcile" ? "reconcile-default" : "none",
146
146
  })),
147
+ {
148
+ name: "resolve-company",
149
+ kind: "native",
150
+ modulePath: "./commands/core-resolve-company-native.js",
151
+ exportName: "runResolveCompanyFast",
152
+ flagKey: "hq-core-native-fast-route",
153
+ familyFlagKey: "core-native-utilities",
154
+ asset: "core/scripts/resolve-company.sh",
155
+ root: "live",
156
+ shellCwd: "live",
157
+ shellHqRoot: "live",
158
+ },
147
159
  ...["resolve", "pickup", "repo", "secrets", "transfer"].map((name) => ({
148
160
  name: `delegate ${name}`,
149
161
  kind: "native",
@@ -9,10 +9,7 @@
9
9
  */
10
10
  import type { ServiceHostPaths, ServiceSetResult } from "../service-manager/index.js";
11
11
  import type { AgentKitPaths } from "./paths.js";
12
- export declare const KIT_PID_NAME = "kit.pid";
13
- export declare const KIT_FALLBACK_LOG_NAME = "kit.log";
14
12
  export declare function kitPidPath(paths: Pick<AgentKitPaths, "agentDir">): string;
15
- export declare function kitFallbackLogPath(paths: Pick<AgentKitPaths, "logsDir">): string;
16
13
  /** True when an activation error means the service manager itself is unusable. */
17
14
  export declare function isServiceManagerUnavailable(error: string | undefined): boolean;
18
15
  /**
@@ -46,10 +43,6 @@ export interface DetachedSpawn {
46
43
  unref(): void;
47
44
  };
48
45
  }
49
- export declare function fallbackCommand(host: ServiceHostPaths): {
50
- cmd: string;
51
- args: string[];
52
- };
53
46
  /** crontab line that restarts the fallback supervisor after a reboot. */
54
47
  export declare function rebootCrontabLine(paths: Pick<AgentKitPaths, "agentDir" | "logsDir">, host: ServiceHostPaths): string;
55
48
  export declare function startFallback(paths: AgentKitPaths, host: ServiceHostPaths, deps?: {
@@ -12,12 +12,12 @@ import * as fs from "node:fs";
12
12
  import * as path from "node:path";
13
13
  import { kitServiceEnv } from "./services.js";
14
14
  import { writePrivateFile } from "./private-file.js";
15
- export const KIT_PID_NAME = "kit.pid";
16
- export const KIT_FALLBACK_LOG_NAME = "kit.log";
15
+ const KIT_PID_NAME = "kit.pid";
16
+ const KIT_FALLBACK_LOG_NAME = "kit.log";
17
17
  export function kitPidPath(paths) {
18
18
  return path.join(paths.agentDir, KIT_PID_NAME);
19
19
  }
20
- export function kitFallbackLogPath(paths) {
20
+ function kitFallbackLogPath(paths) {
21
21
  return path.join(paths.logsDir, KIT_FALLBACK_LOG_NAME);
22
22
  }
23
23
  const UNAVAILABLE_PATTERNS = [
@@ -93,7 +93,7 @@ export function stopFallback(paths, kill = defaultKill) {
93
93
  fs.rmSync(kitPidPath(paths), { force: true });
94
94
  return { pid, killed };
95
95
  }
96
- export function fallbackCommand(host) {
96
+ function fallbackCommand(host) {
97
97
  return { cmd: host.nodeBinary, args: [host.hqBinary, "agent", "kit", "run", "all"] };
98
98
  }
99
99
  /** crontab line that restarts the fallback supervisor after a reboot. */
@@ -9,8 +9,6 @@
9
9
  * always drain exactly what it has not handled yet and never reply twice.
10
10
  */
11
11
  import type { AgentKitPaths } from "./paths.js";
12
- export declare const DONE_IDS_NAME = "done-ids.json";
13
- export declare const MAX_DONE_IDS = 5000;
14
12
  export interface MirroredInboxEntry {
15
13
  id: string;
16
14
  mirroredAt?: string;
@@ -11,8 +11,8 @@
11
11
  import * as fs from "node:fs";
12
12
  import * as path from "node:path";
13
13
  import { writePrivateFile } from "./private-file.js";
14
- export const DONE_IDS_NAME = "done-ids.json";
15
- export const MAX_DONE_IDS = 5000;
14
+ const DONE_IDS_NAME = "done-ids.json";
15
+ const MAX_DONE_IDS = 5000;
16
16
  const INBOX_JSONL = "inbox.jsonl";
17
17
  export function readDoneIds(paths) {
18
18
  try {
@@ -5,10 +5,6 @@
5
5
  * contract-shaped credential file and nothing else.
6
6
  */
7
7
  import { type AgentKitPaths, type KitComponent } from "./paths.js";
8
- export declare const DEFAULT_SYNC_INTERVAL_MS: number;
9
- export declare const DEFAULT_INBOX_POLL_MS = 15000;
10
- export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 60000;
11
- export declare const DEFAULT_MESH_REFRESH_MS: number;
12
8
  export interface KitConfig {
13
9
  /** Local HQ tree the company vault syncs into (companies/<slug>/ …). */
14
10
  hqRoot: string;
@@ -53,7 +49,6 @@ export interface KitConfig {
53
49
  * unhealthy, so reporting it either way would be a lie.
54
50
  */
55
51
  export declare function enabledKitComponents(config: Pick<KitConfig, "syncFiles">): readonly KitComponent[];
56
- export declare function defaultHqRoot(paths: Pick<AgentKitPaths, "agentDir">): string;
57
52
  export declare function defaultKitConfig(paths: Pick<AgentKitPaths, "agentDir">, cliVersion: string, now?: () => Date): KitConfig;
58
53
  export declare function writeKitConfig(paths: Pick<AgentKitPaths, "kitConfigPath">, config: KitConfig): void;
59
54
  /** Read kit.json, falling back to defaults for any missing/invalid field. */