@indigoai-us/hq-cli 5.309.1 → 5.311.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 (99) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
  3. package/assets/scaffold/core/scripts/jobs-validate.sh +1 -1
  4. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
  5. package/dist/command-catalog.generated.d.ts +28 -2
  6. package/dist/command-catalog.generated.js +34 -2
  7. package/dist/commands/channels.d.ts +1 -9
  8. package/dist/commands/channels.js +2 -2
  9. package/dist/commands/company-transfer.d.ts +4 -28
  10. package/dist/commands/company-transfer.js +5 -49
  11. package/dist/commands/core-checkpoint-stop-gate-native.d.ts +34 -0
  12. package/dist/commands/core-checkpoint-stop-gate-native.js +713 -0
  13. package/dist/commands/core-checkpoint.d.ts +0 -24
  14. package/dist/commands/core-checkpoint.js +4 -4
  15. package/dist/commands/core-command-catalog.js +1 -1
  16. package/dist/commands/core-dm-bind-native.d.ts +32 -0
  17. package/dist/commands/core-dm-bind-native.js +668 -0
  18. package/dist/commands/core-hq-session-native.d.ts +39 -0
  19. package/dist/commands/core-hq-session-native.js +579 -0
  20. package/dist/commands/core-jobs-native.js +1 -4
  21. package/dist/commands/core-native-session-flag.d.ts +4 -0
  22. package/dist/commands/core-native-session-flag.js +7 -0
  23. package/dist/commands/core-native-utilities.d.ts +2 -1
  24. package/dist/commands/core-native-utilities.js +4 -2
  25. package/dist/commands/core-native-worker-skills-flag.d.ts +4 -0
  26. package/dist/commands/core-native-worker-skills-flag.js +7 -0
  27. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
  28. package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
  29. package/dist/commands/core-worker-lint-native.d.ts +4 -0
  30. package/dist/commands/core-worker-lint-native.js +160 -0
  31. package/dist/commands/core-worker-share-native.d.ts +5 -0
  32. package/dist/commands/core-worker-share-native.js +260 -0
  33. package/dist/commands/core-worker-skills-command.d.ts +8 -0
  34. package/dist/commands/core-worker-skills-command.js +33 -0
  35. package/dist/commands/core-worker-skills-runtime.d.ts +33 -0
  36. package/dist/commands/core-worker-skills-runtime.js +86 -0
  37. package/dist/commands/core.js +10 -2
  38. package/dist/commands/dm.d.ts +3 -7
  39. package/dist/commands/dm.js +3 -3
  40. package/dist/commands/integrations-import.d.ts +1 -1
  41. package/dist/commands/integrations-import.js +34 -19
  42. package/dist/commands/lanes-accounts.js +3 -1
  43. package/dist/commands/lanes-message.js +84 -10
  44. package/dist/commands/lanes-shared.d.ts +4 -1
  45. package/dist/commands/lanes.d.ts +4 -1
  46. package/dist/commands/lanes.js +48 -7
  47. package/dist/commands/personal-connector-settings-flag.d.ts +4 -0
  48. package/dist/commands/personal-connector-settings-flag.js +7 -0
  49. package/dist/commands/report.d.ts +1 -26
  50. package/dist/commands/report.js +6 -6
  51. package/dist/fast-core.js +15 -2
  52. package/dist/fast-scaffold.js +25 -2
  53. package/dist/index.js +8 -0
  54. package/dist/lib/bot/continuity.d.ts +2 -1
  55. package/dist/lib/bot/job-completions.d.ts +0 -2
  56. package/dist/lib/bot/job-completions.js +2 -2
  57. package/dist/lib/bot/local-config.d.ts +0 -10
  58. package/dist/lib/bot/local-config.js +3 -9
  59. package/dist/lib/bot/owner-context.d.ts +2 -6
  60. package/dist/lib/bot/owner-context.js +3 -3
  61. package/dist/lib/bot/runtime/windows.d.ts +0 -2
  62. package/dist/lib/bot/runtime/windows.js +1 -1
  63. package/dist/lib/bot/setup-explain.d.ts +0 -4
  64. package/dist/lib/bot/setup-explain.js +2 -2
  65. package/dist/lib/daemon/catalog.d.ts +3 -2
  66. package/dist/lib/daemon/hq-cloud.d.ts +2 -2
  67. package/dist/lib/daemon/hq-cloud.js +1 -1
  68. package/dist/lib/daemon/machine-type.d.ts +3 -4
  69. package/dist/lib/daemon/machine-type.js +2 -2
  70. package/dist/lib/daemon/remote-control.d.ts +0 -1
  71. package/dist/lib/daemon/remote-control.js +1 -1
  72. package/dist/lib/daemon/supervisor.d.ts +4 -9
  73. package/dist/lib/daemon/supervisor.js +5 -5
  74. package/dist/lib/lanes/admission-host.d.ts +3 -0
  75. package/dist/lib/lanes/admission-host.js +4 -0
  76. package/dist/lib/lanes/autopilot-registry.d.ts +62 -0
  77. package/dist/lib/lanes/autopilot-registry.js +192 -0
  78. package/dist/lib/lanes/autopilot.d.ts +2 -0
  79. package/dist/lib/lanes/autopilot.js +18 -5
  80. package/dist/lib/lanes/claude-balance.d.ts +6 -0
  81. package/dist/lib/lanes/claude-balance.js +17 -2
  82. package/dist/lib/lanes/keep-alive.d.ts +3 -0
  83. package/dist/lib/lanes/keep-alive.js +3 -0
  84. package/dist/lib/lanes/reconcile.js +14 -6
  85. package/dist/lib/lanes/spawn.js +2 -0
  86. package/dist/lib/mesh/live/daemon/credentials.d.ts +2 -17
  87. package/dist/lib/mesh/live/daemon/credentials.js +9 -9
  88. package/dist/lib/mesh/live/daemon/index.d.ts +5 -5
  89. package/dist/lib/mesh/live/daemon/index.js +2 -2
  90. package/dist/lib/mesh/live/daemon/state.d.ts +1 -1
  91. package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +4 -11
  92. package/dist/lib/mesh/live/daemon/transcript-watch.js +5 -5
  93. package/dist/lib/work-context/company.d.ts +2 -25
  94. package/dist/lib/work-context/company.js +3 -3
  95. package/dist/lib/work-context/config.d.ts +3 -8
  96. package/dist/lib/work-context/config.js +2 -13
  97. package/dist/lib/work-context/state.d.ts +4 -13
  98. package/dist/lib/work-context/state.js +2 -30
  99. package/package.json +1 -1
@@ -33,7 +33,9 @@ import { withCliFeatureUsed } from "../utils/cli-telemetry.js";
33
33
  import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
34
34
  import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
35
35
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
36
+ import { isControllerAliasOutpostId } from "../lib/lanes/admission-host.js";
36
37
  import { runLeadAutopilotPass } from "../lib/lanes/autopilot.js";
38
+ import { syncOutpostLeadRegistries } from "../lib/lanes/autopilot-registry.js";
37
39
  import { OutpostSnapshotInvocationMemo } from "../lib/lanes/outpost-snapshot-memo.js";
38
40
  import { createOutpostHireGrantContext, parseOutpostHireGrantContext, selectOutpostHireGrantHierarchy, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
39
41
  import { createOutpostProfileRepinContext, parseOutpostProfileRepinContext, } from "../lib/lanes/outpost-profile-repin.js";
@@ -253,6 +255,7 @@ function resolveDeps(deps = {}) {
253
255
  now: deps.now ?? (() => new Date()),
254
256
  hostname: deps.hostname ?? (() => os.hostname()),
255
257
  resolveCompany: deps.resolveCompany ?? defaultResolveCompany,
258
+ readLeadAutopilotFlag: deps.readLeadAutopilotFlag ?? readLanesRegistryFlag,
256
259
  resolveActiveCompany: deps.resolveActiveCompany ?? defaultResolveActiveCompany,
257
260
  listOutpostIds: deps.listOutpostIds ?? defaultListOutpostIds,
258
261
  listOutpostRoutes,
@@ -1112,8 +1115,7 @@ async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(h
1112
1115
  .map((id) => id.trim())
1113
1116
  // `primary` aliases the controller when running on the primary box. From
1114
1117
  // another Outpost it identifies a separate host and must be measured.
1115
- .filter((id) => id !== "primary" ||
1116
- (localHost.kind === "outpost" && localHost.id !== "primary"))
1118
+ .filter((id) => !isControllerAliasOutpostId(id, localHost))
1117
1119
  .filter((id) => localHost.kind !== "outpost" || id !== localHost.id)
1118
1120
  .sort();
1119
1121
  const local = buildHostCapacitySnapshot({
@@ -1676,7 +1678,7 @@ async function loadClaudeBalanceHostSnapshots(ctx, hqRoot, config) {
1676
1678
  });
1677
1679
  }
1678
1680
  const remoteIds = outpostIds.map((id) => id.trim())
1679
- .filter((id) => id !== "primary" || (localHost.kind === "outpost" && localHost.id !== "primary"))
1681
+ .filter((id) => !isControllerAliasOutpostId(id, localHost))
1680
1682
  .filter((id) => localHost.kind !== "outpost" || id !== localHost.id)
1681
1683
  .sort();
1682
1684
  const remoteResults = await mapWithConcurrency(remoteIds, OUTPOST_HIRE_SUMMARY_CONCURRENCY, async (id) => {
@@ -3282,6 +3284,8 @@ async function runCreate(ctx, opts) {
3282
3284
  hosts: balanceHostLoad.snapshots,
3283
3285
  targetHost,
3284
3286
  config,
3287
+ quotaState: loadQuotaState(hqRoot),
3288
+ ...(modelToValidate !== undefined ? { model: modelToValidate } : {}),
3285
3289
  recentUsageEnabled: await readClaudeAccountBalanceSignalsEnabled(ctx, company ?? undefined),
3286
3290
  now: ctx.now(),
3287
3291
  ...(allowedAccounts !== undefined ? { allowedAccounts } : {}),
@@ -3329,6 +3333,8 @@ async function runCreate(ctx, opts) {
3329
3333
  hosts,
3330
3334
  targetHost: balanceTargetHost,
3331
3335
  config,
3336
+ quotaState: loadQuotaState(hqRoot),
3337
+ ...(modelToValidate !== undefined ? { model: modelToValidate } : {}),
3332
3338
  recentUsageEnabled: await readClaudeAccountBalanceSignalsEnabled(ctx, company ?? undefined),
3333
3339
  now: ctx.now(),
3334
3340
  ...(allowedAccounts !== undefined ? { allowedAccounts } : {}),
@@ -6391,9 +6397,8 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6391
6397
  probe: ctx.probeProvider,
6392
6398
  env: ctx.env,
6393
6399
  }).logged_in;
6394
- const accountNeedsRepair = lane.account !== undefined &&
6400
+ const recordedAccountUnavailable = lane.account !== undefined &&
6395
6401
  (currentConfig?.enabled !== true || currentLogin === false);
6396
- const repairUnavailableAccount = options.repairUnavailableAccount === true && accountNeedsRepair;
6397
6402
  const profile = workerProfile?.profile ?? (lane.worker_profile !== undefined
6398
6403
  ? loadWorkerProfile(hqRoot, lane.worker_profile)
6399
6404
  : undefined);
@@ -6413,6 +6418,16 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6413
6418
  (lane.model_source === "cli" || lane.model_source === "preserved")
6414
6419
  ? lane.account
6415
6420
  : undefined;
6421
+ const quotaModel = resolveLaneModel({
6422
+ cliModel: pinnedModel ?? (lane.model_source === "account" ? lane.model : undefined),
6423
+ profileModel: profileDefaultsApply ? profile?.meta.model : undefined,
6424
+ accountModel: lane.account === undefined ? undefined : config.providers.claude.accounts[lane.account]?.model,
6425
+ providerModel: config.providers.claude.model,
6426
+ });
6427
+ const recordedAccountQuotaHeld = lane.account !== undefined &&
6428
+ quotaBlockFor(loadQuotaState(hqRoot), "claude", lane.account, quotaModel, ctx.now()).blocked;
6429
+ const accountNeedsRepair = recordedAccountUnavailable || recordedAccountQuotaHeld;
6430
+ const repairUnavailableAccount = options.repairUnavailableAccount === true && accountNeedsRepair;
6416
6431
  const preservedModel = repairUnavailableAccount && lane.model_source === "account"
6417
6432
  ? lane.model ?? currentConfig?.model
6418
6433
  : undefined;
@@ -6430,7 +6445,8 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6430
6445
  const currentTargetAccount = lane.account === undefined
6431
6446
  ? undefined
6432
6447
  : targetHost?.capacity.providers.claude.accounts[lane.account];
6433
- if (profilePin !== undefined &&
6448
+ if (!recordedAccountQuotaHeld &&
6449
+ profilePin !== undefined &&
6434
6450
  lane.account !== undefined &&
6435
6451
  allowedAccounts?.includes(lane.account) &&
6436
6452
  currentConfig?.enabled === true &&
@@ -6443,6 +6459,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6443
6459
  hosts: balanceHosts.snapshots,
6444
6460
  targetHost: lane.host,
6445
6461
  config,
6462
+ quotaState: loadQuotaState(hqRoot),
6446
6463
  recentUsageEnabled: await readClaudeAccountBalanceSignalsEnabled(ctx, lane.company ?? undefined),
6447
6464
  now: ctx.now(),
6448
6465
  ...(!repairUnavailableAccount && allowedAccounts !== undefined ? { allowedAccounts } : {}),
@@ -7810,6 +7827,29 @@ async function runReconcile(ctx, opts, recoverQueuedMessages) {
7810
7827
  summary.ok = false;
7811
7828
  }
7812
7829
  const terminalizedRemoteIds = new Set(remoteLost.map(({ id }) => id));
7830
+ let autopilotRegistrySync;
7831
+ const reconcileHost = reconcileHostFromCtx(ctx);
7832
+ const localHost = laneHostFromCtx(ctx);
7833
+ if (!opts.dryRun && (reconcileHost.outpostId === null || isControllerAliasOutpostId(reconcileHost.outpostId, localHost))) {
7834
+ try {
7835
+ autopilotRegistrySync = await syncOutpostLeadRegistries({
7836
+ hqRoot,
7837
+ outpostIds: await ctx.listOutpostIds(),
7838
+ generatedAt: ctx.now().toISOString(),
7839
+ localHost,
7840
+ resolveCompany: (slug) => ctx.resolveCompany(slug),
7841
+ readFlag: (flag, options) => ctx.readLeadAutopilotFlag(flag, options),
7842
+ dispatch: ctx.dispatchOutpostExec,
7843
+ });
7844
+ }
7845
+ catch (error) {
7846
+ const code = error instanceof LanesError ? error.error : "lead_autopilot_registry_sync_failed";
7847
+ const message = error instanceof Error ? error.message : String(error);
7848
+ summary.ok = false;
7849
+ summary.errors.push({ code, message });
7850
+ process.stderr.write(`hq lanes autopilot registry: ${message}\n`);
7851
+ }
7852
+ }
7813
7853
  let autopilot;
7814
7854
  try {
7815
7855
  const host = reconcileHostFromCtx(ctx);
@@ -7842,11 +7882,12 @@ async function runReconcile(ctx, opts, recoverQueuedMessages) {
7842
7882
  catch (error) {
7843
7883
  const message = error instanceof Error ? error.message : String(error);
7844
7884
  summary.ok = false;
7845
- summary.errors.push({ code: "lead_autopilot_failed", message });
7885
+ summary.errors.push({ code: error instanceof LanesError ? error.error : "lead_autopilot_failed", message });
7846
7886
  process.stderr.write(`hq lanes autopilot: ${message}\n`);
7847
7887
  }
7848
7888
  printJson({
7849
7889
  ...summary,
7890
+ ...(autopilotRegistrySync ? { autopilot_registry_sync: autopilotRegistrySync } : {}),
7850
7891
  ...(autopilot ? { autopilot } : {}),
7851
7892
  ...(remoteLost.length > 0 ? { remote_lost: remoteLost } : {}),
7852
7893
  ...(mirrorRefresh.unverifiedRemote.size > 0
@@ -0,0 +1,4 @@
1
+ export declare const CLI_PERSONAL_CONNECTOR_SETTINGS_FLAG = "cli.personal-connector-settings";
2
+ /** Keep company-shared connector storage unchanged until the flag is explicitly enabled. */
3
+ export declare function personalConnectorSettingsEnabled(): boolean;
4
+ //# sourceMappingURL=personal-connector-settings-flag.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { resolveProcessFlagGate } from "../lib/flag-registry.js";
2
+ export const CLI_PERSONAL_CONNECTOR_SETTINGS_FLAG = "cli.personal-connector-settings";
3
+ /** Keep company-shared connector storage unchanged until the flag is explicitly enabled. */
4
+ export function personalConnectorSettingsEnabled() {
5
+ return resolveProcessFlagGate(CLI_PERSONAL_CONNECTOR_SETTINGS_FLAG, { fallback: false }, () => false);
6
+ }
7
+ //# sourceMappingURL=personal-connector-settings-flag.js.map
@@ -1,44 +1,19 @@
1
1
  import { Command } from "commander";
2
- import { buildReportTarball, deployOrgContext, type ChannelReport, type DeployAccessPolicy, type ReportDeployment, type ReportPatch, ReportPublishError } from "../lib/report-publish.js";
3
- type ChannelRow = {
4
- channelId?: string;
5
- scope?: string;
6
- companyUid?: string | null;
7
- };
2
+ import { deployOrgContext, type ChannelReport, type ReportPatch } from "../lib/report-publish.js";
8
3
  type ReportPatchInput = ReportPatch | ((current: ChannelReport) => ReportPatch);
9
- /** Any company-owned channel (company or project scope) can carry reports; personal channels cannot. */
10
- export declare function isCompanyReportChannel(channel: ChannelRow): boolean;
11
- /**
12
- * Records created before hq-pro stamped companyUid carry null. The channel the
13
- * caller can see knows its company, and publish needs it to pick the deploy
14
- * org, so fill it from there rather than failing every publish.
15
- */
16
- export declare function withChannelCompany(report: ChannelReport, channels: ChannelRow[]): ChannelReport;
17
4
  /** Explicit context has priority; discovery is the authenticated fallback for normal CLI use. */
18
5
  export declare function resolveReport(token: string, reportId: string, requestedChannelId?: string): Promise<ChannelReport>;
19
6
  export declare function patchReport(token: string, report: ChannelReport, patch: ReportPatchInput): Promise<ChannelReport>;
20
- /** hq-deploy refused the subdomain: another app, not visible to this caller, holds it. */
21
- export declare class SubdomainTakenError extends ReportPublishError {
22
- constructor();
23
- }
24
7
  /**
25
8
  * Subdomains to try, in order. The first is the report's own slug; the rest
26
9
  * are used only when an app the caller cannot see already holds it (for
27
10
  * example, one left by an earlier publish under a different account).
28
11
  */
29
12
  export declare function candidateSlugs(slug: string, attempts?: number): string[];
30
- export declare function resolveCompanySlug(token: string, companyUid: string): Promise<string>;
31
13
  export declare function findOrCreateApp(token: string, slug: string, org: ReturnType<typeof deployOrgContext>): Promise<{
32
14
  id: string;
33
15
  subdomain: string;
34
16
  }>;
35
- export declare function deployReportStatic(token: string, input: {
36
- slug: string;
37
- files: Parameters<typeof buildReportTarball>[0];
38
- access: DeployAccessPolicy;
39
- org: ReturnType<typeof deployOrgContext>;
40
- }): Promise<ReportDeployment>;
41
17
  export declare function registerReportCommand(program: Command): void;
42
- export declare function runReportCommand(action: () => Promise<void>): Promise<void>;
43
18
  export {};
44
19
  //# sourceMappingURL=report.d.ts.map
@@ -99,7 +99,7 @@ async function listReportChannels(token) {
99
99
  return Array.isArray(listed.channels) ? listed.channels : [];
100
100
  }
101
101
  /** Any company-owned channel (company or project scope) can carry reports; personal channels cannot. */
102
- export function isCompanyReportChannel(channel) {
102
+ function isCompanyReportChannel(channel) {
103
103
  if (channel.scope === "personal")
104
104
  return false;
105
105
  return channel.scope === "company" || channel.scope === "project" || Boolean(channel.companyUid);
@@ -125,7 +125,7 @@ async function readReportInChannel(token, channelId, reportId) {
125
125
  * caller can see knows its company, and publish needs it to pick the deploy
126
126
  * org, so fill it from there rather than failing every publish.
127
127
  */
128
- export function withChannelCompany(report, channels) {
128
+ function withChannelCompany(report, channels) {
129
129
  if (report.companyUid)
130
130
  return report;
131
131
  const companyUid = channels.find((row) => row.channelId === report.channelId)?.companyUid;
@@ -232,7 +232,7 @@ async function deployJson(token, route, method, body, extraHeaders = {}) {
232
232
  return await res.json();
233
233
  }
234
234
  /** hq-deploy refused the subdomain: another app, not visible to this caller, holds it. */
235
- export class SubdomainTakenError extends ReportPublishError {
235
+ class SubdomainTakenError extends ReportPublishError {
236
236
  constructor() {
237
237
  super("hq-deploy rejected the report deployment.");
238
238
  }
@@ -245,7 +245,7 @@ export class SubdomainTakenError extends ReportPublishError {
245
245
  export function candidateSlugs(slug, attempts = 3) {
246
246
  return [slug, ...Array.from({ length: attempts }, (_, i) => `${slug}-${i + 2}`)];
247
247
  }
248
- export async function resolveCompanySlug(token, companyUid) {
248
+ async function resolveCompanySlug(token, companyUid) {
249
249
  const res = await vaultApiFetch({ token, path: `/entity/${encodeURIComponent(companyUid)}` });
250
250
  if (!res.ok)
251
251
  throw new ReportPublishError("Could not resolve the report's company for deploy.");
@@ -290,7 +290,7 @@ export async function findOrCreateApp(token, slug, org) {
290
290
  throw new ReportPublishError("hq-deploy returned an incomplete app record.");
291
291
  return { id, subdomain };
292
292
  }
293
- export async function deployReportStatic(token, input) {
293
+ async function deployReportStatic(token, input) {
294
294
  const app = await findOrCreateApp(token, input.slug, input.org);
295
295
  await deployJson(token, `/api/apps/${encodeURIComponent(app.id)}/access-policy`, "PUT", input.access, input.org.headers);
296
296
  const tarball = buildReportTarball(input.files);
@@ -525,7 +525,7 @@ export function registerReportCommand(program) {
525
525
  print(templates, options.json === true);
526
526
  });
527
527
  }
528
- export async function runReportCommand(action) {
528
+ async function runReportCommand(action) {
529
529
  try {
530
530
  await action();
531
531
  }
package/dist/fast-core.js CHANGED
@@ -21,8 +21,11 @@ export async function runFastCore(argv) {
21
21
  const entry = parsed.entry;
22
22
  // The full graph's process gate accepts textual true/false overrides. It ignores
23
23
  // numeric env values, so preserve that behavior for family flags here too.
24
- const familyEnabled = entry.kind !== "native" || !entry.familyFlagKey ||
25
- await (await import("./native-fast-route.js")).fastFlagEnabled(entry.familyFlagKey, process.env, { numericEnvironmentOverride: false });
24
+ let familyEnabled = true;
25
+ if (entry.kind === "native" && entry.familyFlagKey) {
26
+ const envOverride = localBooleanFlagOverride(entry.familyFlagKey, process.env);
27
+ familyEnabled = envOverride ?? await (await import("./native-fast-route.js")).fastFlagEnabled(entry.familyFlagKey, process.env, { numericEnvironmentOverride: false });
28
+ }
26
29
  const useShell = entry.kind === "native" && Boolean(entry.familyFlagKey) && !familyEnabled;
27
30
  const shouldInjectRoot = useShell
28
31
  ? entry.shellHqRoot === "live" || (entry.shellHqRoot === "reconcile-default" &&
@@ -87,4 +90,14 @@ export async function runFastCore(argv) {
87
90
  // Propagate verbatim: these scripts use their exit code as their interface.
88
91
  process.exit(code);
89
92
  }
93
+ /** Match hq-flags-client's literal true/false env override without loading its module. */
94
+ function localBooleanFlagOverride(flagKey, env) {
95
+ const envKey = `HQ_FLAG_${flagKey.replace(/[.-]/g, "_").toUpperCase()}`;
96
+ const value = env[envKey];
97
+ if (value === "true")
98
+ return true;
99
+ if (value === "false")
100
+ return false;
101
+ return null;
102
+ }
90
103
  //# sourceMappingURL=fast-core.js.map
@@ -11,8 +11,31 @@
11
11
  */
12
12
  /** The relocated scripts that qualify for the entrypoint fast path. */
13
13
  export const FAST_SCAFFOLD_SCRIPTS = [
14
- { name: "checkpoint-stop-gate", kind: "shell", asset: "core/scripts/checkpoint-stop-gate.sh", root: "live" },
15
- { name: "hq-session", kind: "shell", asset: "core/scripts/hq-session.sh", root: "live" },
14
+ {
15
+ name: "checkpoint-stop-gate",
16
+ kind: "native",
17
+ modulePath: "./commands/core-checkpoint-stop-gate-native.js",
18
+ exportName: "runCheckpointStopGateFast",
19
+ flagKey: "hq-core-native-fast-route",
20
+ familyFlagKey: "core-native-session",
21
+ asset: "core/scripts/checkpoint-stop-gate.sh",
22
+ root: "live",
23
+ shellCwd: "live",
24
+ shellHqRoot: "live",
25
+ readsStdin: true,
26
+ },
27
+ {
28
+ name: "hq-session",
29
+ kind: "native",
30
+ modulePath: "./commands/core-hq-session-native.js",
31
+ exportName: "runHqSessionFast",
32
+ flagKey: "hq-core-native-fast-route",
33
+ familyFlagKey: "core-native-session",
34
+ asset: "core/scripts/hq-session.sh",
35
+ root: "live",
36
+ shellCwd: "live",
37
+ shellHqRoot: "live",
38
+ },
16
39
  {
17
40
  name: "timeout-guard",
18
41
  kind: "native",
package/dist/index.js CHANGED
@@ -35,6 +35,14 @@ if (isVersionRequest(process.argv)) {
35
35
  });
36
36
  }
37
37
  }
38
+ else if (parseFastCore(process.argv)?.entry.kind === "native" &&
39
+ (process.env.HQ_FLAG_HQ_CORE_NATIVE_FAST_ROUTE === "true" ||
40
+ process.env.HQ_FLAG_HQ_CORE_NATIVE_FAST_ROUTE === "1")) {
41
+ // An explicit enabled override has already decided the global gate. Skip the
42
+ // snapshot reader import on hot native plumbing paths; runFastCore still
43
+ // checks the command-family flag before choosing native or shell rollback.
44
+ void import("./fast-core.js").then(({ runFastCore }) => runFastCore(process.argv));
45
+ }
38
46
  else if (parseFastCore(process.argv)?.entry.kind === "native") {
39
47
  // Native routes are default-off. Resolve the company-scoped persisted flag
40
48
  // without loading the command graph; a miss or stale snapshot takes the
@@ -1,5 +1,5 @@
1
1
  import type { BotConfig } from "./config.js";
2
- export interface ContinuityFile {
2
+ interface ContinuityFile {
3
3
  area: "worker" | "memory";
4
4
  path: string;
5
5
  bytes: number;
@@ -63,4 +63,5 @@ export declare function planFleetContinuity(bundle: BotContinuityBundle, target:
63
63
  sha256: string;
64
64
  };
65
65
  };
66
+ export {};
66
67
  //# sourceMappingURL=continuity.d.ts.map
@@ -45,7 +45,6 @@
45
45
  * everything it needs in its own prompt.
46
46
  */
47
47
  import type { JobRunStatus } from "./api.js";
48
- export declare const BOT_JOB_COMPLETIONS_NAME = "job-completions.json";
49
48
  /** First retry gap; each failure doubles it. */
50
49
  export declare const COMPLETION_RETRY_BASE_MS = 2000;
51
50
  /** Never wait longer than this between retries. */
@@ -75,7 +74,6 @@ export interface PendingJobCompletion {
75
74
  /** Epoch ms before which this must not be retried (exponential backoff). */
76
75
  nextAttemptAt: number;
77
76
  }
78
- export declare function botJobCompletionsPath(dir: string): string;
79
77
  export declare function readPendingJobCompletions(dir: string): PendingJobCompletion[];
80
78
  /** The gap before attempt N (1-based), doubling and capped. */
81
79
  export declare function completionRetryDelayMs(attempts: number): number;
@@ -46,7 +46,7 @@
46
46
  */
47
47
  import * as fs from "node:fs";
48
48
  import * as path from "node:path";
49
- export const BOT_JOB_COMPLETIONS_NAME = "job-completions.json";
49
+ const BOT_JOB_COMPLETIONS_NAME = "job-completions.json";
50
50
  /** First retry gap; each failure doubles it. */
51
51
  export const COMPLETION_RETRY_BASE_MS = 2_000;
52
52
  /** Never wait longer than this between retries. */
@@ -57,7 +57,7 @@ export const COMPLETION_RETRY_MAX_MS = 5 * 60_000;
57
57
  * in-progress slot on its own, so a later report changes nothing.
58
58
  */
59
59
  export const MAX_COMPLETION_AGE_MS = 60 * 60_000;
60
- export function botJobCompletionsPath(dir) {
60
+ function botJobCompletionsPath(dir) {
61
61
  return path.join(dir, BOT_JOB_COMPLETIONS_NAME);
62
62
  }
63
63
  function isPending(raw) {
@@ -10,10 +10,6 @@
10
10
  * live only in ~/.hq/bots/<name>/machine-creds.json and never travel here.
11
11
  */
12
12
  import { type BotConfig, type BotKind, type BotMemoryMode, type BotRuntimeId } from "./config.js";
13
- /** Same limit the server enforces on the stored value. */
14
- export declare const BOT_LOCAL_CONFIG_MAX_BYTES: number;
15
- /** Key names the server rejects outright (case-insensitive, substring). */
16
- export declare const BOT_LOCAL_CONFIG_FORBIDDEN_KEY_RE: RegExp;
17
13
  /**
18
14
  * Everything needed to rebuild a bot here, minus anything secret. Written on
19
15
  * create, returned by `GET /v1/agents/mine?local=1` and by the credential
@@ -62,10 +58,4 @@ export declare const BOT_RESTORE_DEFAULTS: {
62
58
  };
63
59
  /** Human summary of the settings a restore is about to apply. */
64
60
  export declare function describeBotLocalConfig(local: BotLocalConfig | null): string;
65
- /** Exported so callers can present the accepted values without importing config.ts. */
66
- export declare const BOT_LOCAL_CONFIG_VALUES: {
67
- readonly runtimes: readonly ["claude", "codex", "grok"];
68
- readonly kinds: readonly ["personal", "company"];
69
- readonly memoryModes: readonly ["synced", "local"];
70
- };
71
61
  //# sourceMappingURL=local-config.d.ts.map
@@ -9,11 +9,11 @@
9
9
  * checks the result before it leaves this machine: a bot's machine credentials
10
10
  * live only in ~/.hq/bots/<name>/machine-creds.json and never travel here.
11
11
  */
12
- import { BOT_KINDS, BOT_MEMORY_MODES, BOT_RUNTIMES, botMemoryMode, effectiveBotCompanies, effectiveBotKind, isBotKind, isBotMemoryMode, isBotRuntimeId, } from "./config.js";
12
+ import { botMemoryMode, effectiveBotCompanies, effectiveBotKind, isBotKind, isBotMemoryMode, isBotRuntimeId, } from "./config.js";
13
13
  /** Same limit the server enforces on the stored value. */
14
- export const BOT_LOCAL_CONFIG_MAX_BYTES = 8 * 1024;
14
+ const BOT_LOCAL_CONFIG_MAX_BYTES = 8 * 1024;
15
15
  /** Key names the server rejects outright (case-insensitive, substring). */
16
- export const BOT_LOCAL_CONFIG_FORBIDDEN_KEY_RE = /secret|token|password|creds/i;
16
+ const BOT_LOCAL_CONFIG_FORBIDDEN_KEY_RE = /secret|token|password|creds/i;
17
17
  /** Walks a value and throws when any key looks like it could carry a secret. */
18
18
  export function assertNoSecretKeys(value, path = "localConfig") {
19
19
  if (Array.isArray(value)) {
@@ -142,10 +142,4 @@ export function describeBotLocalConfig(local) {
142
142
  bits.push(local.model);
143
143
  return bits.join(", ");
144
144
  }
145
- /** Exported so callers can present the accepted values without importing config.ts. */
146
- export const BOT_LOCAL_CONFIG_VALUES = {
147
- runtimes: BOT_RUNTIMES,
148
- kinds: BOT_KINDS,
149
- memoryModes: BOT_MEMORY_MODES,
150
- };
151
145
  //# sourceMappingURL=local-config.js.map
@@ -13,7 +13,7 @@
13
13
  * slugs, uids, roles) reaches the model; the owner's token never does. A failed
14
14
  * lookup is reported as "could not check", never as "no companies".
15
15
  */
16
- export interface OwnerCompany {
16
+ interface OwnerCompany {
17
17
  companyUid: string;
18
18
  /** Present when the server enriched it. */
19
19
  companySlug?: string;
@@ -55,15 +55,10 @@ export interface OwnerLookupIo {
55
55
  /** Test seam for the retry pause. */
56
56
  sleep?: (ms: number) => Promise<void>;
57
57
  }
58
- export declare const defaultOwnerLookupIo: OwnerLookupIo;
59
- /** A read right after sign-in or bot start can fail once; try again before reporting it. */
60
- export declare const OWNER_LOOKUP_RETRY_DELAYS_MS: number[];
61
58
  export declare function lookupOwnerContext(ownerUid: string, io?: OwnerLookupIo): Promise<OwnerContext>;
62
59
  /** Successful lookups are reused for 5 minutes, failures retried after 1 minute. */
63
60
  export declare const OWNER_CONTEXT_TTL_MS: number;
64
61
  export declare const OWNER_CONTEXT_RETRY_MS = 60000;
65
- /** A turn never waits longer than this for a refresh; the lookup keeps going in the background. */
66
- export declare const OWNER_CONTEXT_WAIT_MS = 8000;
67
62
  export declare class OwnerContextCache {
68
63
  private readonly lookup;
69
64
  private readonly nowMs;
@@ -80,4 +75,5 @@ export declare class OwnerContextCache {
80
75
  * model cannot confuse the owner's memberships with its own.
81
76
  */
82
77
  export declare function ownerContextBlock(ctx: OwnerContext, agentUid: string): string;
78
+ export {};
83
79
  //# sourceMappingURL=owner-context.d.ts.map
@@ -18,7 +18,7 @@ import { resolveCallerPersonUid, vaultApiFetch } from "../../utils/vault-api.js"
18
18
  function message(err) {
19
19
  return err instanceof Error ? err.message : String(err);
20
20
  }
21
- export const defaultOwnerLookupIo = {
21
+ const defaultOwnerLookupIo = {
22
22
  // tokenSource "person" never uses the bot's machine creds, even though
23
23
  // HQ_MACHINE_CREDS_FILE is set for this process.
24
24
  personToken: () => ensureCognitoToken({ tokenSource: "person", interactive: false }),
@@ -34,7 +34,7 @@ export const defaultOwnerLookupIo = {
34
34
  },
35
35
  };
36
36
  /** A read right after sign-in or bot start can fail once; try again before reporting it. */
37
- export const OWNER_LOOKUP_RETRY_DELAYS_MS = [1_000, 2_000];
37
+ const OWNER_LOOKUP_RETRY_DELAYS_MS = [1_000, 2_000];
38
38
  async function withRetry(fn, io) {
39
39
  const sleep = io.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
40
40
  let lastErr;
@@ -84,7 +84,7 @@ export async function lookupOwnerContext(ownerUid, io = defaultOwnerLookupIo) {
84
84
  export const OWNER_CONTEXT_TTL_MS = 5 * 60_000;
85
85
  export const OWNER_CONTEXT_RETRY_MS = 60_000;
86
86
  /** A turn never waits longer than this for a refresh; the lookup keeps going in the background. */
87
- export const OWNER_CONTEXT_WAIT_MS = 8_000;
87
+ const OWNER_CONTEXT_WAIT_MS = 8_000;
88
88
  export class OwnerContextCache {
89
89
  lookup;
90
90
  nowMs;
@@ -1,7 +1,5 @@
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;
5
3
  /** Preserve non-Windows lookup. Windows returns the first PATH/PATHEXT match. */
6
4
  export declare function resolveRuntimeBinary(name: string, env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, existsSync?: ExistsSync, cwd?: string): string;
7
5
  /** Directly spawn native files; pass only batch shims through cmd.exe. */
@@ -8,7 +8,7 @@ function envValue(env, name) {
8
8
  return key ? env[key] : undefined;
9
9
  }
10
10
  /** Resolve an npm-installed provider command using Windows PATH and PATHEXT. */
11
- export function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
11
+ function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
12
12
  const pathValue = envValue(env, "PATH") ?? "";
13
13
  const rawExtensions = (envValue(env, "PATHEXT") || DEFAULT_PATHEXT)
14
14
  .split(";")
@@ -16,10 +16,6 @@
16
16
  export declare const EXPLAIN_HQ_SUGGESTION = "Explain HQ first";
17
17
  /** Where the explanation lives, relative to the setup worker folder. */
18
18
  export declare const EXPLAIN_HQ_FILE: string;
19
- /** The setup bot's progress note in its memory folder (setup worker.yaml). */
20
- export declare const SETUP_PROGRESS_FILE = "setup-progress.md";
21
- /** Runner-owned state, in the bot's own folder (never the model's memory). */
22
- export declare const SETUP_INTRO_STATE_FILE = "setup-intro.json";
23
19
  /**
24
20
  * Put in front of the next message from the person, so the model knows the
25
21
  * explanation went out without it. It never reaches the person.
@@ -19,9 +19,9 @@ export const EXPLAIN_HQ_SUGGESTION = "Explain HQ first";
19
19
  /** Where the explanation lives, relative to the setup worker folder. */
20
20
  export const EXPLAIN_HQ_FILE = path.join("skills", "explain-hq.md");
21
21
  /** The setup bot's progress note in its memory folder (setup worker.yaml). */
22
- export const SETUP_PROGRESS_FILE = "setup-progress.md";
22
+ const SETUP_PROGRESS_FILE = "setup-progress.md";
23
23
  /** Runner-owned state, in the bot's own folder (never the model's memory). */
24
- export const SETUP_INTRO_STATE_FILE = "setup-intro.json";
24
+ const SETUP_INTRO_STATE_FILE = "setup-intro.json";
25
25
  const EXPLANATION_HEADING = /^##[ \t]+The explanation[ \t]*$/m;
26
26
  const CLOSING_HEADING = /^##[ \t]+After the explanation, when the app sends it[ \t]*$/m;
27
27
  /**
@@ -23,7 +23,7 @@ export declare function enabledServices(machineType: MachineType, overrides: {
23
23
  enable: readonly string[];
24
24
  disable: readonly string[];
25
25
  }): ServiceName[];
26
- export type UnitCommand = {
26
+ type UnitCommand = {
27
27
  type: "hq";
28
28
  args: string[];
29
29
  } | {
@@ -38,7 +38,7 @@ export type UnitCommand = {
38
38
  type: "internal";
39
39
  task: "rotate-logs";
40
40
  };
41
- export interface UnitBlock {
41
+ interface UnitBlock {
42
42
  /** `waiting` clears on its own once the requirement appears; `unavailable` needs a newer CLI. */
43
43
  state: "waiting" | "unavailable";
44
44
  reason: string;
@@ -128,4 +128,5 @@ export declare const CODEX_RUNNER_SCRIPT = "/usr/local/bin/outpost-codex-runner.
128
128
  export declare const CLAUDE_RUNNER_SCRIPT = "/usr/local/bin/outpost-runner.sh";
129
129
  /** Turn enabled services into concrete units for this host. */
130
130
  export declare function planUnits(services: readonly ServiceName[], ctx: PlanContext): UnitPlan[];
131
+ export {};
131
132
  //# sourceMappingURL=catalog.d.ts.map
@@ -52,7 +52,6 @@ export declare function hqCloudInstallCommand(prefix: string, version: string, p
52
52
  args: string[];
53
53
  shell: boolean;
54
54
  };
55
- export declare function readHqCloudCopy(root: string): HqCloudCopy | null;
56
55
  /** The copy bundled with this CLI. */
57
56
  export declare function bundledHqCloud(): HqCloudCopy | null;
58
57
  /** The hq-cloud the daemon should run right now. */
@@ -63,7 +62,7 @@ export interface HqCloudUpdateOutcome {
63
62
  to: string | null;
64
63
  detail?: string;
65
64
  }
66
- export interface NpmRunResult {
65
+ interface NpmRunResult {
67
66
  status: number | null;
68
67
  stdout?: string | null;
69
68
  stderr?: string | null;
@@ -76,4 +75,5 @@ export interface UpdateHqCloudDeps {
76
75
  }
77
76
  /** Install or update the updater's copy, as this (the login) user. */
78
77
  export declare function updateManagedHqCloud(daemonRoot: string, env: NodeJS.ProcessEnv, deps?: UpdateHqCloudDeps): HqCloudUpdateOutcome;
78
+ export {};
79
79
  //# sourceMappingURL=hq-cloud.d.ts.map
@@ -108,7 +108,7 @@ export function hqCloudInstallCommand(prefix, version, platform = process.platfo
108
108
  const plan = buildSpawnPlan("npm", ["install", "--prefix", prefix, "--no-audit", "--no-fund", "--omit=dev", `${HQ_CLOUD_PACKAGE}@${version}`], platform);
109
109
  return { file: plan.cmd, args: plan.args, shell: plan.shell };
110
110
  }
111
- export function readHqCloudCopy(root) {
111
+ function readHqCloudCopy(root) {
112
112
  try {
113
113
  const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
114
114
  return typeof pkg.version === "string" ? { root, version: pkg.version } : null;
@@ -12,12 +12,10 @@
12
12
  * 6. macOS or Windows → desktop
13
13
  * 7. any other host → self-hosted
14
14
  */
15
- export declare const MACHINE_TYPES: readonly ["desktop", "self-hosted", "outpost", "agent-box", "agent-kit"];
15
+ declare const MACHINE_TYPES: readonly ["desktop", "self-hosted", "outpost", "agent-box", "agent-kit"];
16
16
  export type MachineType = (typeof MACHINE_TYPES)[number];
17
17
  export declare const MACHINE_TYPE_MARKER_PATH = "/etc/hq/machine-type";
18
- export type MachineTypeSource = "flag" | "config" | "marker" | "outpost-units" | "agent-kit-creds" | "container" | "platform";
19
- /** The Claude runner unit outpost provisioning installs; its `User=` is the box's login user. */
20
- export declare const OUTPOST_RUNNER_UNIT = "/etc/systemd/system/outpost-runner.service";
18
+ type MachineTypeSource = "flag" | "config" | "marker" | "outpost-units" | "agent-kit-creds" | "container" | "platform";
21
19
  export interface MachineTypeResult {
22
20
  type: MachineType;
23
21
  source: MachineTypeSource;
@@ -48,4 +46,5 @@ export declare function parseMachineTypeMarker(text: string): {
48
46
  export declare function detectMachineType(flag: string | undefined, deps: DetectMachineTypeDeps): MachineTypeResult;
49
47
  /** The home directory of `user` in passwd-format text, or null when the account is not listed. */
50
48
  export declare function homeFromPasswd(passwd: string, user: string): string | null;
49
+ export {};
51
50
  //# sourceMappingURL=machine-type.d.ts.map
@@ -14,10 +14,10 @@
14
14
  */
15
15
  import * as path from "node:path";
16
16
  import { AGENT_DIR_ENV, MACHINE_CREDS_NAME, agentRootDir } from "../agent-kit/paths.js";
17
- export const MACHINE_TYPES = ["desktop", "self-hosted", "outpost", "agent-box", "agent-kit"];
17
+ const MACHINE_TYPES = ["desktop", "self-hosted", "outpost", "agent-box", "agent-kit"];
18
18
  export const MACHINE_TYPE_MARKER_PATH = "/etc/hq/machine-type";
19
19
  /** The Claude runner unit outpost provisioning installs; its `User=` is the box's login user. */
20
- export const OUTPOST_RUNNER_UNIT = "/etc/systemd/system/outpost-runner.service";
20
+ const OUTPOST_RUNNER_UNIT = "/etc/systemd/system/outpost-runner.service";
21
21
  export function isMachineType(value) {
22
22
  return typeof value === "string" && MACHINE_TYPES.includes(value);
23
23
  }
@@ -21,7 +21,6 @@
21
21
  * user's own tmux sessions.
22
22
  */
23
23
  import type { RunCommand } from "../service-manager/launchd.js";
24
- export declare const TMUX_SOCKET = "hq-daemon";
25
24
  export declare const TMUX_SESSION = "remote-control";
26
25
  export declare const CODEX_TMUX_SESSION = "codex-remote-control";
27
26
  export declare function remoteControlArgs(opts: {