@indigoai-us/hq-cli 5.311.0 → 5.311.2

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 (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/commands/core-delegate-w1b.js +111 -14
  3. package/dist/commands/core-delegate.d.ts +9 -0
  4. package/dist/commands/core-delegate.js +44 -23
  5. package/dist/commands/integrations-core.d.ts +2 -0
  6. package/dist/commands/integrations-core.js +4 -0
  7. package/dist/commands/integrations.js +2 -2
  8. package/dist/commands/lanes-message.js +18 -7
  9. package/dist/commands/lanes-shared.d.ts +2 -0
  10. package/dist/commands/lanes-watch.js +1 -5
  11. package/dist/commands/lanes.js +4 -10
  12. package/dist/fast-scaffold.d.ts +4 -0
  13. package/dist/fast-scaffold.js +5 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/lib/bot/runtime/windows.d.ts +2 -0
  17. package/dist/lib/bot/runtime/windows.js +1 -1
  18. package/dist/lib/flag-registry.d.ts +2 -0
  19. package/dist/lib/flag-registry.js +19 -12
  20. package/dist/lib/lanes/autopilot.js +19 -2
  21. package/dist/lib/lanes/delivery.d.ts +1 -0
  22. package/dist/lib/lanes/keep-alive.d.ts +1 -3
  23. package/dist/lib/lanes/keep-alive.js +2 -4
  24. package/dist/lib/lanes/reconcile.js +14 -36
  25. package/dist/lib/lanes/spawn.d.ts +2 -0
  26. package/dist/lib/lanes/spawn.js +35 -12
  27. package/dist/lib/lanes/types.d.ts +2 -2
  28. package/dist/lib/lanes/watch.d.ts +0 -1
  29. package/dist/lib/lanes/watch.js +4 -9
  30. package/dist/main.js +1 -1
  31. package/dist/node-network-compat-core.d.ts +12 -0
  32. package/dist/node-network-compat-core.js +41 -0
  33. package/dist/node-network-compat-fast.d.ts +7 -0
  34. package/dist/node-network-compat-fast.js +23 -0
  35. package/dist/node-network-compat.d.ts +2 -18
  36. package/dist/node-network-compat.js +4 -39
  37. package/dist/node-preflight.d.ts +7 -1
  38. package/dist/node-preflight.js +5 -5
  39. package/dist/utils/hq-core-yaml.d.ts +10 -0
  40. package/dist/utils/hq-core-yaml.js +40 -0
  41. package/dist/utils/pack-contributions.d.ts +2 -9
  42. package/dist/utils/pack-contributions.js +1 -39
  43. package/dist/utils/run-bundled-script.d.ts +6 -0
  44. package/dist/utils/run-bundled-script.js +14 -3
  45. package/dist/utils/scaffold-fast-import-guard.js +5 -1
  46. package/dist/utils/version-report.js +1 -1
  47. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.311.2] — 2026-10-01
6
+ - Reduce fast-path startup work by loading network compatibility only on routes that need it.
7
+ - Make envelope `next_check_at` an unconditional keep-alive hold and remove its completed rollout flag.
8
+ - Lead autopilot no longer asks a lead to review its own lane as a finished worker, or to relaunch a worker for a story assigned to the lead itself.
9
+ - Lead autopilot counts a story's current `lane` together with its `lanes` history, so `lanes: []` no longer hides the assigned lane.
10
+ - Keep the CLI company seed fixture byte-matched to the hq-core starter golden, including the session sentinel ignore rule.
11
+ - Record lane-watcher continuations as `watcher` resumes and label their prompt source accurately; record monitor-event and system-notice message sources distinctly in lane resume history.
12
+
13
+ ## [5.311.1] — 2026-10-01
14
+ - Preserve shell-compatible diagnostics when delegate pickup receives a malformed manifest.
15
+ - Print URLs returned by `hq integrations call` without redacting their paths or query values. Other integration JSON output keeps its existing URL redaction.
16
+
5
17
  ## [5.311.0] — 2026-10-01
6
18
  - Add native `hq core hq-session` and `checkpoint-stop-gate` handlers on the fast route behind the default-off `core-native-session` flag, retaining bundled shell rollback companions.
7
19
 
@@ -6,7 +6,7 @@ import * as path from "node:path";
6
6
  import { serializeJqJson } from "../lib/core-utils/jq-json-serializer.js";
7
7
  import { matchingSecretPatterns } from "../lib/core-utils/secret-patterns.js";
8
8
  import { describeCoreCommand } from "./core-command-catalog.js";
9
- import { isRecord, jsonAt, jqFallback, jqRawPath, jqRawPaths, moveFile, readJsonFile, subprocess, writeJsonAtomically, } from "./core-delegate.js";
9
+ import { commandExists, isRecord, jsonAt, jqFallback, jqRawPath, jqRawPaths, moveFile, readJsonFile, subprocess, writeJsonAtomically, } from "./core-delegate.js";
10
10
  import { resolveLiveRoot } from "../utils/hq-roots.js";
11
11
  import { runBundledScript } from "../utils/run-bundled-script.js";
12
12
  const FLAG = "core-native-delegate";
@@ -61,6 +61,15 @@ function jsonFile(file) {
61
61
  return undefined;
62
62
  }
63
63
  }
64
+ function malformedJsonFile(file) {
65
+ try {
66
+ JSON.parse(fs.readFileSync(file, "utf8"));
67
+ return false;
68
+ }
69
+ catch (error) {
70
+ return error instanceof SyntaxError;
71
+ }
72
+ }
64
73
  function jsonText(value) { return serializeJqJson(value, "pretty"); }
65
74
  function checksum(file) {
66
75
  return createHash("sha256").update(fs.readFileSync(file)).digest("hex");
@@ -125,7 +134,21 @@ function parseFlags(args, allowed) {
125
134
  }
126
135
  function usage(name) { return USAGE[name] ?? ""; }
127
136
  function usageFailure(name) { err(usage(name)); return 1; }
128
- function runBundle(args, context) {
137
+ function runShellFallback(name, args, context) {
138
+ const result = runBundledScript({
139
+ asset: ASSETS[name],
140
+ args,
141
+ cwd: context.cwd,
142
+ hqRoot: context.root ?? context.cwd,
143
+ captureOutput: true,
144
+ });
145
+ if (result.stdout?.length)
146
+ process.stdout.write(result.stdout);
147
+ if (result.stderr?.length)
148
+ process.stderr.write(result.stderr);
149
+ return result.code;
150
+ }
151
+ function runBundle(args, context, onMalformedWhoami) {
129
152
  const root = context.root ?? context.cwd;
130
153
  if (args[0] !== "build")
131
154
  return usageFailure("bundle");
@@ -175,7 +198,9 @@ function runBundle(args, context) {
175
198
  try {
176
199
  identity = JSON.parse(who.stdout ?? "");
177
200
  }
178
- catch {
201
+ catch (error) {
202
+ if (error instanceof SyntaxError)
203
+ return onMalformedWhoami ? onMalformedWhoami() : runShellFallback("bundle", args, context);
179
204
  identity = undefined;
180
205
  }
181
206
  const identityValue = isRecord(identity) ? identity : {};
@@ -291,6 +316,32 @@ function runBundle(args, context) {
291
316
  function runHq(args, context, stdout = "capture") {
292
317
  return subprocess("hq", args, context.cwd, { stdout });
293
318
  }
319
+ function commandSubstitutionBytes(value) {
320
+ const bytes = value.stdoutBytes ?? Buffer.from(value.stdout ?? "");
321
+ let end = bytes.length;
322
+ while (end > 0 && bytes[end - 1] === 0x0a)
323
+ end -= 1;
324
+ return bytes.subarray(0, end);
325
+ }
326
+ function emitJqParseDiagnostic(input, args, context) {
327
+ if (!commandExists("jq", context.cwd))
328
+ return;
329
+ const result = subprocess("jq", args, context.cwd, { input, stdout: "ignore", stderr: "pipe" });
330
+ const diagnostic = result.stderrBytes ?? Buffer.from(result.stderr ?? "");
331
+ if (diagnostic.length)
332
+ err(diagnostic);
333
+ }
334
+ const RESOLVE_FOUND_FILTER = '.status == "found" and (.person.personUid | type == "string") and (.person.companyUid | type == "string")';
335
+ const RESOLVE_NOT_FOUND_FILTER = '.status == "not_found"';
336
+ const ACL_FILTER = `
337
+ .schemaVersion == 1 and
338
+ (
339
+ .prefix == $prefix or
340
+ (($prefix | endswith("/*")) and .prefix == ($prefix | sub("\\\\*$"; "")))
341
+ ) and
342
+ (.companyUid | type == "string") and ($company == "" or .companyUid == $company) and
343
+ (.direct | type == "array") and (.exists | type == "boolean")
344
+ `;
294
345
  function runGrant(args, context) {
295
346
  const parsed = parseFlags(args, ["--manifest", "--yes"]);
296
347
  if (parsed.help) {
@@ -397,7 +448,12 @@ function runGrant(args, context) {
397
448
  try {
398
449
  response = JSON.parse(resolved.stdout ?? "");
399
450
  }
400
- catch {
451
+ catch (error) {
452
+ if (error instanceof SyntaxError) {
453
+ const input = commandSubstitutionBytes(resolved);
454
+ emitJqParseDiagnostic(input, ["-e", RESOLVE_FOUND_FILTER], context);
455
+ emitJqParseDiagnostic(input, ["-e", RESOLVE_NOT_FOUND_FILTER], context);
456
+ }
401
457
  response = undefined;
402
458
  }
403
459
  if (isRecord(response) && response.status === "found" && isRecord(response.person) && typeof response.person.personUid === "string" && typeof response.person.companyUid === "string") {
@@ -426,7 +482,11 @@ function runGrant(args, context) {
426
482
  try {
427
483
  value = JSON.parse(response.stdout ?? "");
428
484
  }
429
- catch {
485
+ catch (error) {
486
+ if (error instanceof SyntaxError) {
487
+ const input = commandSubstitutionBytes(response);
488
+ emitJqParseDiagnostic(input, ["-e", "--arg", "prefix", prefix, "--arg", "company", expectedCompany, ACL_FILTER], context);
489
+ }
430
490
  value = undefined;
431
491
  }
432
492
  const rawPrefix = isRecord(value) ? value.prefix : undefined;
@@ -501,6 +561,22 @@ function runPublish(args, context) {
501
561
  if (args[0] !== "--manifest" || !args[1] || !fs.existsSync(args[1]) || !fs.statSync(args[1]).isFile())
502
562
  return fail("publish", "usage: --manifest <path>");
503
563
  const manifestPath = args[1];
564
+ if (malformedJsonFile(manifestPath))
565
+ return runShellFallback("publish", args, context);
566
+ let preflightManifest;
567
+ try {
568
+ preflightManifest = readJsonFile(manifestPath);
569
+ }
570
+ catch {
571
+ preflightManifest = undefined;
572
+ }
573
+ if (isRecord(preflightManifest) && jqRawPath(preflightManifest, "mode") === "transfer") {
574
+ const root = context.root ?? context.cwd;
575
+ const prd = path.join(root, "companies", jqRawPath(preflightManifest, "company"), "projects", jqRawPath(preflightManifest, "project.name"), "prd.json");
576
+ if (fs.existsSync(prd) && fs.statSync(prd).isFile() && malformedJsonFile(prd)) {
577
+ return runShellFallback("publish", args, context);
578
+ }
579
+ }
504
580
  const lock = `${manifestPath}.send-lock`;
505
581
  const lockHeld = args[2] === "--lock-held";
506
582
  let ownLock = false;
@@ -658,6 +734,12 @@ function runSend(args, context) {
658
734
  const manifest = jsonFile(manifestPath);
659
735
  if (!manifest)
660
736
  return fail("send", `manifest is not valid JSON: ${manifestPath}`);
737
+ if (send) {
738
+ const publishedPath = path.join(context.root ?? context.cwd, jqRawPath(manifest, "publication.manifestPath"));
739
+ if (fs.existsSync(publishedPath) && fs.statSync(publishedPath).isFile() && malformedJsonFile(publishedPath)) {
740
+ return runShellFallback("send", args, context);
741
+ }
742
+ }
661
743
  const lock = `${manifestPath}.send-lock`;
662
744
  if (send) {
663
745
  try {
@@ -819,14 +901,14 @@ function sleepNative(milliseconds) {
819
901
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
820
902
  }
821
903
  function captureRun(run) {
822
- let stdout = "";
823
- let stderr = "";
904
+ const stdout = [];
905
+ const stderr = [];
824
906
  const stdoutWrite = process.stdout.write.bind(process.stdout);
825
907
  const stderrWrite = process.stderr.write.bind(process.stderr);
826
- process.stdout.write = ((chunk) => { stdout += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"); return true; });
827
- process.stderr.write = ((chunk) => { stderr += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"); return true; });
908
+ process.stdout.write = ((chunk) => { stdout.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk)); return true; });
909
+ process.stderr.write = ((chunk) => { stderr.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk)); return true; });
828
910
  try {
829
- return { code: run(), stdout, stderr };
911
+ return { code: run(), stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr) };
830
912
  }
831
913
  finally {
832
914
  process.stdout.write = stdoutWrite;
@@ -854,16 +936,29 @@ function runVerify(args, context) {
854
936
  return fail("verify", "--dry-run requires --project");
855
937
  if (!recipient)
856
938
  return fail("verify", "--dry-run requires --to");
857
- const plan = captureRun(() => runBundle(["build", "--company", company, "--project", project, "--to", recipient, "--mode", mode, "--dry-run"], context));
858
- if (plan.stderr)
939
+ let malformedWhoamiFallbackCode;
940
+ const plan = captureRun(() => runBundle(["build", "--company", company, "--project", project, "--to", recipient, "--mode", mode, "--dry-run"], context, () => {
941
+ malformedWhoamiFallbackCode = runShellFallback("verify", args, context);
942
+ return malformedWhoamiFallbackCode;
943
+ }));
944
+ if (malformedWhoamiFallbackCode !== undefined) {
945
+ if (plan.stdout.length)
946
+ out(plan.stdout);
947
+ if (plan.stderr.length)
948
+ err(plan.stderr);
949
+ return malformedWhoamiFallbackCode;
950
+ }
951
+ if (plan.stderr.length)
859
952
  err(plan.stderr);
860
953
  if (plan.code !== 0)
861
954
  return fail("verify", "could not derive the delegation plan (see builder error above)");
862
955
  let manifest;
863
956
  try {
864
- manifest = JSON.parse(plan.stdout);
957
+ manifest = JSON.parse(plan.stdout.toString("utf8"));
865
958
  }
866
- catch {
959
+ catch (error) {
960
+ if (error instanceof SyntaxError)
961
+ return runShellFallback("verify", args, context);
867
962
  return fail("verify", "could not derive the delegation plan (see builder error above)");
868
963
  }
869
964
  const repoRelative = jqRawPath(manifest, "repo.path");
@@ -893,6 +988,8 @@ function runVerify(args, context) {
893
988
  return fail("verify", "--manifest is required (or --dry-run with --company/--project/--to)");
894
989
  if (!fs.existsSync(manifestPath) || !fs.statSync(manifestPath).isFile())
895
990
  return fail("verify", `manifest not found: ${manifestPath}`);
991
+ if (malformedJsonFile(manifestPath))
992
+ return runShellFallback("verify", args, context);
896
993
  const manifest = jsonFile(manifestPath);
897
994
  if (!manifest)
898
995
  return fail("verify", `jq: error: ${manifestPath}: Invalid JSON`);
@@ -9,12 +9,21 @@ type CommandResult = {
9
9
  code: number;
10
10
  stdout?: string;
11
11
  stderr?: string;
12
+ stdoutBytes?: Buffer;
13
+ stderrBytes?: Buffer;
12
14
  };
15
+ export type CommandExistsOptions = {
16
+ platform?: NodeJS.Platform;
17
+ env?: NodeJS.ProcessEnv;
18
+ existsSync?: (candidate: string) => boolean;
19
+ };
20
+ export declare function commandExists(name: string, cwd?: string, options?: CommandExistsOptions): boolean;
13
21
  export declare function subprocess(command: string, args: string[], cwd: string, options?: {
14
22
  input?: Buffer | string;
15
23
  stdinFd?: number;
16
24
  stderr?: "inherit" | "ignore" | "pipe";
17
25
  stdout?: "capture" | "inherit" | "ignore";
26
+ maxBuffer?: number;
18
27
  }): CommandResult;
19
28
  type JsonRecord = Record<string, unknown>;
20
29
  export declare function isRecord(value: unknown): value is JsonRecord;
@@ -4,6 +4,7 @@ import * as fs from "node:fs";
4
4
  import * as os from "node:os";
5
5
  import * as path from "node:path";
6
6
  import { serializeJqJson } from "../lib/core-utils/jq-json-serializer.js";
7
+ import { resolveWindowsExecutable } from "../lib/bot/runtime/windows.js";
7
8
  import { describeCoreCommand } from "./core-command-catalog.js";
8
9
  import { resolveLiveRoot } from "../utils/hq-roots.js";
9
10
  import { runBundledScript } from "../utils/run-bundled-script.js";
@@ -28,6 +29,20 @@ function writeOut(value) {
28
29
  function writeErr(value) {
29
30
  process.stderr.write(value);
30
31
  }
32
+ function runCapturedShell(asset, args, context) {
33
+ const result = runBundledScript({
34
+ asset,
35
+ args,
36
+ cwd: context.cwd,
37
+ ...(context.root ? { hqRoot: context.root } : {}),
38
+ captureOutput: true,
39
+ });
40
+ if (result.stdout?.length)
41
+ process.stdout.write(result.stdout);
42
+ if (result.stderr?.length)
43
+ process.stderr.write(result.stderr);
44
+ return result.code;
45
+ }
31
46
  const USAGE_TEXT = {
32
47
  resolve: "hq-delegate-resolve.sh — resolve a delegation recipient to a confirmed\nprincipal (person email / prs_ uid, or fleet-agent agt_ uid). Read-only.\n\nUsage:\n core/scripts/hq-delegate-resolve.sh --company <slug> --to <token>\n\nOutput (stdout, JSON): {kind, principal, displayName, source}\n kind — \"person\" | \"agent\"\n principal — confirmed email, prs_… or agt_… uid\n source — \"verbatim\" | \"people-resolve\" | \"agents-list\"\n\nExit codes:\n 0 — resolved\n 1 — usage error (missing/invalid arguments)\n 3 — ambiguous: candidate list printed to stdout as JSON\n {status:\"ambiguous\", matches:[…]} — caller must present a picker\n 4 — not found (or found with no email): plain message on stderr\n\nTenancy: every lookup is scoped with an explicit --company. This helper\nnever enumerates or falls back across companies — single-company by\nconstruction, matching the /dm skill's resolution contract.\n\nFast path: a token containing \"@\" or starting with prs_ / agt_ passes\nthrough verbatim with NO lookup, preserving existing invocations exactly.\n",
33
48
  // Preserve the companion usage function’s historical raw source lines.
@@ -39,8 +54,20 @@ const USAGE_TEXT = {
39
54
  function usage(name) {
40
55
  return USAGE_TEXT[name];
41
56
  }
42
- function commandPath(name, cwd = process.cwd()) {
43
- const directories = (process.env.PATH ?? "").split(path.delimiter);
57
+ function commandPath(name, cwd = process.cwd(), options = {}) {
58
+ const env = options.env ?? process.env;
59
+ if ((options.platform ?? process.platform) === "win32") {
60
+ const existsSync = options.existsSync ?? ((candidate) => {
61
+ try {
62
+ return fs.statSync(candidate).isFile();
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ });
68
+ return resolveWindowsExecutable(name, env, existsSync, cwd) ?? undefined;
69
+ }
70
+ const directories = (env.PATH ?? "").split(path.delimiter);
44
71
  for (const directory of directories) {
45
72
  const candidate = path.resolve(cwd, directory || ".", name);
46
73
  try {
@@ -56,24 +83,30 @@ function commandPath(name, cwd = process.cwd()) {
56
83
  }
57
84
  return undefined;
58
85
  }
59
- function commandExists(name, cwd = process.cwd()) {
60
- return commandPath(name, cwd) !== undefined;
86
+ export function commandExists(name, cwd = process.cwd(), options = {}) {
87
+ return commandPath(name, cwd, options) !== undefined;
61
88
  }
62
89
  export function subprocess(command, args, cwd, options = {}) {
63
90
  const result = spawnSync(command, args, {
64
91
  cwd,
65
92
  env: process.env,
66
93
  input: options.input,
67
- encoding: "utf8",
68
- stdio: [options.stdinFd ?? "inherit", options.stdout === "inherit" ? "inherit" : options.stdout === "ignore" ? "ignore" : "pipe", options.stderr === "pipe" ? "pipe" : options.stderr ?? "inherit"],
94
+ maxBuffer: options.maxBuffer,
95
+ encoding: "buffer",
96
+ stdio: [options.input === undefined ? options.stdinFd ?? "inherit" : "pipe", options.stdout === "inherit" ? "inherit" : options.stdout === "ignore" ? "ignore" : "pipe", options.stderr === "pipe" ? "pipe" : options.stderr ?? "inherit"],
69
97
  });
70
98
  if (result.error) {
71
- return { code: 127, stderr: result.error.message, stdout: "" };
99
+ const stderr = Buffer.from(result.error.message);
100
+ return { code: 127, stderr: stderr.toString("utf8"), stderrBytes: stderr, stdout: "", stdoutBytes: Buffer.alloc(0) };
72
101
  }
102
+ const stdoutBytes = Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.alloc(0);
103
+ const stderrBytes = Buffer.isBuffer(result.stderr) ? result.stderr : Buffer.alloc(0);
73
104
  return {
74
105
  code: result.status ?? (result.signal ? 128 : 1),
75
- stdout: typeof result.stdout === "string" ? result.stdout : "",
76
- stderr: typeof result.stderr === "string" ? result.stderr : "",
106
+ stdout: stdoutBytes.toString("utf8"),
107
+ stderr: stderrBytes.toString("utf8"),
108
+ stdoutBytes,
109
+ stderrBytes,
77
110
  };
78
111
  }
79
112
  export function isRecord(value) {
@@ -82,15 +115,6 @@ export function isRecord(value) {
82
115
  export function readJsonText(text) {
83
116
  return JSON.parse(text);
84
117
  }
85
- function jqParseDiagnostic(text) {
86
- const bareToken = /^\s*([A-Za-z][A-Za-z0-9_-]*)\s*$/.exec(text);
87
- if (bareToken && !["null", "true", "false"].includes(bareToken[1] ?? "")) {
88
- const line = text.split("\n").length;
89
- const column = Buffer.byteLength(text.slice(text.lastIndexOf("\n") + 1));
90
- return `jq: parse error: Invalid numeric literal at line ${line}, column ${column}\n`;
91
- }
92
- return "jq: parse error: Invalid JSON text\n";
93
- }
94
118
  export function readJsonFile(file) {
95
119
  return readJsonText(fs.readFileSync(file, "utf8"));
96
120
  }
@@ -373,11 +397,8 @@ function runPickup(args, context) {
373
397
  document = readJsonFile(file);
374
398
  }
375
399
  catch (error) {
376
- if (error instanceof SyntaxError) {
377
- const diagnostic = jqParseDiagnostic(fs.readFileSync(file, "utf8"));
378
- for (let index = 0; index < 4; index += 1)
379
- writeErr(diagnostic);
380
- }
400
+ if (error instanceof SyntaxError)
401
+ return runCapturedShell(SCRIPT_ASSETS.pickup, args, context);
381
402
  document = {};
382
403
  }
383
404
  const status = jqRawPath(document, "status");
@@ -272,6 +272,8 @@ interface QueuedOutcome {
272
272
  }
273
273
  export declare function queuedOutcome(payload: unknown): QueuedOutcome | null;
274
274
  export declare function printJson(value: unknown): void;
275
+ /** Print the successful payload from `hq integrations call` without altering URLs. */
276
+ export declare function printIntegrationCallResultJson(value: unknown): void;
275
277
  /**
276
278
  * Print why a connection is unusable and how to repair it, directly beneath its
277
279
  * status line.
@@ -485,6 +485,10 @@ export function queuedOutcome(payload) {
485
485
  export function printJson(value) {
486
486
  console.log(JSON.stringify(redactSensitiveUrls(value), null, 2));
487
487
  }
488
+ /** Print the successful payload from `hq integrations call` without altering URLs. */
489
+ export function printIntegrationCallResultJson(value) {
490
+ console.log(JSON.stringify(value, null, 2));
491
+ }
488
492
  /**
489
493
  * Print why a connection is unusable and how to repair it, directly beneath its
490
494
  * status line.
@@ -54,7 +54,7 @@ import chalk from "chalk";
54
54
  import { ensureCognitoIdToken } from "../utils/cognito-session.js";
55
55
  import { getCompanyUid, vaultApiFetch } from "../utils/vault-api.js";
56
56
  import { redactSensitiveUrl, redactSensitiveUrls, redactUrlsInText } from "../utils/redact-url.js";
57
- import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isClientError, printHealthNotice, printJson, raiseIfUnauthorized, raiseIfUpstreamUnavailable, gatewayResultIsError, selectConnection, revokedConnectionDetails, toolPrefixForProvider, unwrapGatewayResult, queuedOutcome, } from "./integrations-core.js";
57
+ import { IntegrationsCliError, bareProvider, callGateway, fetchConnections, isClientError, printHealthNotice, printJson, printIntegrationCallResultJson, raiseIfUnauthorized, raiseIfUpstreamUnavailable, gatewayResultIsError, selectConnection, revokedConnectionDetails, toolPrefixForProvider, unwrapGatewayResult, queuedOutcome, } from "./integrations-core.js";
58
58
  import { registerConnectCommands } from "./integrations-connect.js";
59
59
  import { registerImportCommands } from "./integrations-import.js";
60
60
  import { registerManageCommands } from "./integrations-manage.js";
@@ -367,7 +367,7 @@ export function registerIntegrationsCommand(program) {
367
367
  // parsing output. The gateway double-wraps the provider result as a
368
368
  // text-content string, so `isError` lands on the UNWRAPPED payload, not
369
369
  // on the raw envelope; check both levels so either shape is caught.
370
- printJson(payload);
370
+ printIntegrationCallResultJson(payload);
371
371
  if (gatewayResultIsError(payload) || gatewayResultIsError(message.result)) {
372
372
  process.exitCode = 1;
373
373
  }
@@ -448,6 +448,11 @@ export function createLaneMessageHandlers(deps) {
448
448
  text: promptedText,
449
449
  });
450
450
  const deliveryText = framedMonitorEvent ?? promptedText;
451
+ const messageHistoryTrigger = opts.monitorEvent !== undefined
452
+ ? "monitor_event"
453
+ : opts.messageSource ?? "message";
454
+ const isMonitorEvent = messageHistoryTrigger === "monitor_event";
455
+ const isSystemNotice = messageHistoryTrigger === "system_notice";
451
456
  const dropboxMessage = {
452
457
  at,
453
458
  message_id: messageId,
@@ -562,9 +567,9 @@ export function createLaneMessageHandlers(deps) {
562
567
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
563
568
  ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
564
569
  pending: {
565
- trigger: "message",
570
+ trigger: messageHistoryTrigger,
566
571
  text: deliveryText,
567
- ...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
572
+ ...(isMonitorEvent ? { monitor_event: true } : {}),
568
573
  message_id: messageId,
569
574
  compact: opts.compact !== false,
570
575
  },
@@ -589,9 +594,9 @@ export function createLaneMessageHandlers(deps) {
589
594
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
590
595
  ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
591
596
  pending: {
592
- trigger: "message",
597
+ trigger: messageHistoryTrigger,
593
598
  text: deliveryText,
594
- ...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
599
+ ...(isMonitorEvent ? { monitor_event: true } : {}),
595
600
  message_id: messageId,
596
601
  compact: opts.compact !== false,
597
602
  },
@@ -674,10 +679,10 @@ export function createLaneMessageHandlers(deps) {
674
679
  queued_at: at,
675
680
  pending_resume: {
676
681
  session_id: sessionId,
677
- trigger: "message",
682
+ trigger: messageHistoryTrigger,
678
683
  caller: ctx.env.HQ_LANE_ID?.trim() || "user",
679
684
  text: deliveryText,
680
- ...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
685
+ ...(isMonitorEvent ? { monitor_event: true } : {}),
681
686
  message_id: messageId,
682
687
  compact: opts.compact !== false,
683
688
  at,
@@ -708,7 +713,8 @@ export function createLaneMessageHandlers(deps) {
708
713
  kind: "message",
709
714
  text: deliveryText,
710
715
  message_id: messageId,
711
- ...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
716
+ ...(isMonitorEvent ? { monitor_event: true } : {}),
717
+ ...(isSystemNotice ? { system_notice: true } : {}),
712
718
  },
713
719
  compact: opts.compact !== false,
714
720
  effort: resumeEffort ?? recordedResumeEffort(lane),
@@ -868,6 +874,11 @@ export function createLaneMessageHandlers(deps) {
868
874
  try {
869
875
  await runMessage(ctx, laneId, {
870
876
  text: formatDropboxMessages(messages),
877
+ messageSource: messages.some((message) => message.kind === "monitor_event")
878
+ ? "monitor_event"
879
+ : messages.some((message) => message.kind === "system_notice")
880
+ ? "system_notice"
881
+ : undefined,
871
882
  compact: false,
872
883
  silent: true,
873
884
  onAccepted: () => { accepted = true; },
@@ -52,6 +52,8 @@ export interface LaneMessageOptions {
52
52
  description: string;
53
53
  sequence: number;
54
54
  };
55
+ /** Source kind for a message recovered from the lane dropbox. */
56
+ messageSource?: "monitor_event" | "system_notice";
55
57
  /** Internal recovery messages do not emit a second command response. */
56
58
  silent?: boolean;
57
59
  /** Internal recovery claim is safe to archive once this callback runs. */
@@ -1,5 +1,5 @@
1
1
  import { accountStatus, coveredLaneIds, loadLanesConfig, MAX_WATCHED_LANES, MONITOR_ENGINES, MONITOR_MAX_TIMEOUT_MS, MONITOR_UNCOVERED_EXIT_CODE, monitorReminderText, runLaneWatch, } from "../lib/lanes/index.js";
2
- import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG } from "../lib/lanes/keep-alive.js";
2
+ import { LEAD_LANE_KEEP_ALIVE_FLAG } from "../lib/lanes/keep-alive.js";
3
3
  import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
4
4
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
5
5
  import { isLanesError, LanesError } from "../lib/lanes/errors.js";
@@ -50,10 +50,6 @@ export function createLaneWatchHandlers(deps) {
50
50
  company: resolveLaneCompany(hqRoot, lane).company ?? undefined,
51
51
  env: ctx.env,
52
52
  }),
53
- keepAliveNextCheckAtEnabled: (lane) => readLanesRegistryFlag(LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, {
54
- company: resolveLaneCompany(hqRoot, lane).company ?? undefined,
55
- env: ctx.env,
56
- }),
57
53
  readAccountStatuses: async (lanes) => {
58
54
  if (lanes.length === 0)
59
55
  return [];
@@ -61,7 +61,7 @@ import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STA
61
61
  import { laneStories, storyWarning, } from "../lib/lanes/stories.js";
62
62
  import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
63
63
  import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
64
- import { doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveNextCheckAtMs, keepAliveExclusion, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
64
+ import { doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveNextCheckAtMs, keepAliveExclusion, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
65
65
  import { questionBodyAbsPath, resolveQuestionCompanyWithFallback, resolveProjectQuestionCompany, } from "../lib/lanes/questions.js";
66
66
  import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
67
67
  import { compactionAfterUsage, } from "../lib/lanes/compaction.js";
@@ -5159,17 +5159,11 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane) {
5159
5159
  const keepAliveState = resolveLaneKeepAlive(hqRoot, storedLane);
5160
5160
  const keepAliveCompany = resolveLaneCompany(hqRoot, storedLane);
5161
5161
  const now = ctx.now().getTime();
5162
- const nextCheckAtEnabled = storedLane.state === "done" && storedLane.keep_alive === true
5163
- ? await readLanesRegistryFlag(LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, {
5164
- company: keepAliveCompany.company ?? undefined,
5165
- env: ctx.env,
5166
- })
5167
- : false;
5168
- const hasPendingWork = storedLane.keep_alive_standing === true || nextCheckAtEnabled
5162
+ const hasPendingWork = storedLane.state === "done" && storedLane.keep_alive === true
5169
5163
  ? hasPendingKeepAliveWork(hqRoot, storedLane)
5170
5164
  : false;
5171
5165
  const doneStateVerdict = storedLane.state === "done" && storedLane.keep_alive === true
5172
- ? doneKeepAliveVerdict(storedLane, { decision: storedLane.last_envelope?.decision, story_complete: storedLane.story_complete, next_check_at: storedLane.last_envelope?.next_check_at }, storedLane.keep_alive_standing === true && hasPendingWork, { nextCheckAtEnabled, hasPendingWork, nowMs: now })
5166
+ ? doneKeepAliveVerdict(storedLane, { decision: storedLane.last_envelope?.decision, story_complete: storedLane.story_complete, next_check_at: storedLane.last_envelope?.next_check_at }, storedLane.keep_alive_standing === true && hasPendingWork, { hasPendingWork, nowMs: now })
5173
5167
  : null;
5174
5168
  let managerStatusTarget;
5175
5169
  let managerStatusGateError;
@@ -5191,7 +5185,7 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane) {
5191
5185
  now: ctx.now().getTime(),
5192
5186
  });
5193
5187
  const laneIsSenior = allLanes.some((item) => item.senior.kind === "lane" && item.senior.id === laneId);
5194
- const requestedNextCheckMs = nextCheckAtEnabled && !hasPendingWork
5188
+ const requestedNextCheckMs = storedLane.state === "done" && storedLane.keep_alive === true && !hasPendingWork
5195
5189
  ? keepAliveNextCheckAtMs(storedLane.last_envelope?.next_check_at, now, Date.parse(storedLane.last_envelope?.at ?? storedLane.updated_at))
5196
5190
  : undefined;
5197
5191
  const storedNextEligibleMs = Date.parse(storedLane.keep_alive_next_eligible_at ?? "");
@@ -16,6 +16,8 @@ export type FastScaffoldScript = {
16
16
  /** Path under `assets/scaffold/`, e.g. `core/scripts/foo.sh`. */
17
17
  asset: string;
18
18
  root?: "live";
19
+ /** Whether the bundled fast-route module closure needs Node's network timeout patch. */
20
+ requiresNodeNetworkCompatibility?: boolean;
19
21
  } | {
20
22
  /** Command path, joined with spaces, e.g. `policy frontmatter`. */
21
23
  name: string;
@@ -38,6 +40,8 @@ export type FastScaffoldScript = {
38
40
  shellHqRoot?: "none" | "live" | "reconcile-default";
39
41
  /** Whether this handler consumes stdin. Shell companions inherit stdin directly. */
40
42
  readsStdin?: boolean;
43
+ /** Whether the bundled fast-route module closure needs Node's network timeout patch. */
44
+ requiresNodeNetworkCompatibility?: boolean;
41
45
  };
42
46
  /** The relocated scripts that qualify for the entrypoint fast path. */
43
47
  export declare const FAST_SCAFFOLD_SCRIPTS: readonly FastScaffoldScript[];
@@ -115,6 +115,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
115
115
  flagKey: "hq-core-native-fast-route",
116
116
  familyFlagKey: "core-native-handoff",
117
117
  asset: "core/scripts/worktree-gc.sh",
118
+ requiresNodeNetworkCompatibility: true,
118
119
  root: "live",
119
120
  shellCwd: "live",
120
121
  shellHqRoot: "live",
@@ -127,6 +128,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
127
128
  flagKey: "hq-core-native-fast-route",
128
129
  familyFlagKey: "core-native-handoff",
129
130
  asset: "core/scripts/clean-worktree-reconcile-handoff.sh",
131
+ requiresNodeNetworkCompatibility: true,
130
132
  root: "cwd",
131
133
  shellCwd: "caller",
132
134
  shellHqRoot: "none",
@@ -139,6 +141,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
139
141
  flagKey: "hq-core-native-fast-route",
140
142
  familyFlagKey: "core-native-handoff",
141
143
  asset: "core/scripts/resume-thread-lock.sh",
144
+ requiresNodeNetworkCompatibility: true,
142
145
  root: "live",
143
146
  shellCwd: "live",
144
147
  shellHqRoot: "live",
@@ -151,6 +154,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
151
154
  flagKey: "hq-core-native-fast-route",
152
155
  familyFlagKey: "core-native-handoff",
153
156
  asset: "core/scripts/verify-story-deliverables.sh",
157
+ requiresNodeNetworkCompatibility: true,
154
158
  root: "live",
155
159
  shellCwd: "live",
156
160
  shellHqRoot: "live",
@@ -163,6 +167,7 @@ export const FAST_SCAFFOLD_SCRIPTS = [
163
167
  flagKey: "hq-core-native-fast-route",
164
168
  familyFlagKey: "core-native-jobs",
165
169
  asset: `core/scripts/${name === "reconcile" ? "outpost-jobs-reconcile" : `jobs-${name}`}.sh`,
170
+ requiresNodeNetworkCompatibility: true,
166
171
  root: name === "reconcile" ? "passthrough" : "cwd",
167
172
  shellCwd: "caller",
168
173
  shellHqRoot: name === "reconcile" ? "reconcile-default" : "none",
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import "./node-preflight.js";
3
- import "./node-network-compat.js";
3
+ import "./node-network-compat-fast.js";
4
4
  import { isVersionRequest } from "./version-request.js";
5
5
  import { isFastCoreRequest } from "./fast-scaffold.js";
6
6
  export declare const __test__: {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // MUST be first: guard the Node version before any dependency that needs a
3
3
  // Node 20+ API (e.g. util.styleText) or a newer native ABI is evaluated.
4
4
  import "./node-preflight.js";
5
- import "./node-network-compat.js";
5
+ import "./node-network-compat-fast.js";
6
6
  import { isVersionRequest, wantsLabelledVersionReport } from "./version-request.js";
7
7
  // Dependency-light: a pure parser + table, no command graph. Safe to load on
8
8
  // every path (including --version) without reintroducing the heavy startup.
@@ -1,5 +1,7 @@
1
1
  import { type SpawnPlan } from "../../../utils/windows-spawn.js";
2
2
  type ExistsSync = (candidate: string) => boolean;
3
+ /** Resolve an npm-installed provider command using Windows PATH and PATHEXT. */
4
+ export declare function resolveWindowsExecutable(name: string, env: NodeJS.ProcessEnv, existsSync?: ExistsSync, cwd?: string): string | null;
3
5
  /** Preserve non-Windows lookup. Windows returns the first PATH/PATHEXT match. */
4
6
  export declare function resolveRuntimeBinary(name: string, env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, existsSync?: ExistsSync, cwd?: string): string;
5
7
  /** Directly spawn native files; pass only batch shims through cmd.exe. */