@indigoai-us/hq-cli 5.201.0 → 5.201.1

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.201.1] — 2026-09-25
6
+
7
+ ### Fixed
8
+
9
+ - Outpost grant checks now include child lanes from the owning Outpost. Owner
10
+ records take precedence over duplicate primary records, and each grant
11
+ command reads the owner lane list once.
12
+ - Outpost grant checks re-read owner lanes under the hire lock and use the
13
+ validated Outpost CLI override for the lane-list probe.
14
+
5
15
  ## [5.201.0] — 2026-09-25
6
16
 
7
17
  ### Added
@@ -479,6 +479,14 @@ function laneHireFields(lane, lanes) {
479
479
  hire_summary: hireSummary,
480
480
  };
481
481
  }
482
+ function mergeOwnerLaneHierarchy(supplied, ownerLanes) {
483
+ const byId = new Map();
484
+ for (const lane of supplied)
485
+ byId.set(lane.lane_id, lane);
486
+ for (const lane of ownerLanes)
487
+ byId.set(lane.lane_id, lane);
488
+ return [...byId.values()];
489
+ }
482
490
  function outpostCliOverride(ctx, outpostId) {
483
491
  const envKey = `HQ_LANES_OUTPOST_CLI_${outpostId.toUpperCase().replace(/[^A-Z0-9_]/g, "_")}`;
484
492
  const configured = ctx.env[envKey];
@@ -1031,11 +1039,12 @@ async function loadHostCapacitySnapshots(ctx, hqRoot, config = loadLanesConfig(h
1031
1039
  };
1032
1040
  }
1033
1041
  async function readOutpostLaneRecords(ctx, outpostId) {
1042
+ const hqBinary = outpostCliOverride(ctx, outpostId);
1034
1043
  let result;
1035
1044
  try {
1036
1045
  result = await ctx.dispatchOutpostExec({
1037
1046
  outpostId,
1038
- command: buildOutpostLaneListCommand(),
1047
+ command: buildOutpostLaneListCommand({ hqBinary }),
1039
1048
  timeoutSeconds: CAPACITY_PROBE_TIMEOUT_MS / 1000,
1040
1049
  waitMs: CAPACITY_PROBE_TIMEOUT_MS,
1041
1050
  });
@@ -3312,9 +3321,20 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3312
3321
  throw new LanesError("outpost_dispatch_failed", `Lane ${lane.lane_id} has no Outpost host to dispatch to`, { lane_id: lane.lane_id });
3313
3322
  }
3314
3323
  const outpostId = lane.host.id;
3315
- const grantContext = action === "grant"
3316
- ? createOutpostHireGrantContext(lane, loadAllLanes(ctx.hqRoot()), await ctx.laneHireGrantsEnabled(lane.company))
3317
- : undefined;
3324
+ let grantContext;
3325
+ let grantHierarchy;
3326
+ if (action === "grant") {
3327
+ const enforcementEnabled = await ctx.laneHireGrantsEnabled(lane.company);
3328
+ let hierarchy = loadAllLanes(ctx.hqRoot());
3329
+ const ownerLanes = await readOutpostLaneRecords(ctx, outpostId);
3330
+ const byId = new Map(hierarchy.map((record) => [record.lane_id, record]));
3331
+ for (const ownerLane of ownerLanes) {
3332
+ byId.set(ownerLane.lane_id, ownerLane);
3333
+ }
3334
+ hierarchy = [...byId.values()];
3335
+ grantHierarchy = hierarchy;
3336
+ grantContext = createOutpostHireGrantContext(lane, hierarchy, enforcementEnabled);
3337
+ }
3318
3338
  let dispatched;
3319
3339
  try {
3320
3340
  dispatched = await ctx.dispatchOutpostExec({
@@ -3407,6 +3427,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3407
3427
  ...remote,
3408
3428
  lane_id: ownerLane.lane_id,
3409
3429
  lane: ownerLane,
3430
+ ...laneHireFields(ownerLane, grantHierarchy ?? []),
3410
3431
  outpost_id: outpostId,
3411
3432
  outpost_command_id: dispatched.commandId,
3412
3433
  outpost_transport: "outposts_exec",
@@ -4518,7 +4539,11 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
4518
4539
  (!grantContext || !sameHireGrantSenior(current.senior, grantContext.expected_senior))) {
4519
4540
  throw new LanesError("hire_grant_senior_changed", `Lane ${laneId}'s senior changed before the Outpost applied the grant update`, { lane_id: laneId, current_senior: current.senior });
4520
4541
  }
4521
- const hierarchy = grantContext?.hierarchy ?? loadAllLanes(hqRoot);
4542
+ const hierarchy = grantContext
4543
+ ? authorization === "ssm-owner-dispatch"
4544
+ ? mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot))
4545
+ : grantContext.hierarchy
4546
+ : loadAllLanes(hqRoot);
4522
4547
  if (enforce && current.senior.kind === "lane") {
4523
4548
  if (!current.senior.id) {
4524
4549
  throw new LanesError("hire_grant_senior_invalid", `Lane ${laneId} has a lane senior without an id`, {
@@ -4604,7 +4629,8 @@ async function runGrantOwnerTarget(ctx, laneId, opts) {
4604
4629
  const hireGrant = parseHireGrant(opts.hire);
4605
4630
  const grantContext = parseOutpostHireGrantContext(opts.grantContext, laneId);
4606
4631
  const hqRoot = ctx.hqRoot();
4607
- printGrantResult(hqRoot, await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext), grantContext.hierarchy);
4632
+ const updated = await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext);
4633
+ printGrantResult(hqRoot, updated, mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot)));
4608
4634
  }
4609
4635
  async function runBackfillGrants(ctx, opts) {
4610
4636
  if (opts.apply && opts.dryRun) {
@@ -72,7 +72,7 @@ export declare function parseOutpostPickupPayload(raw: unknown): OutpostPickupPa
72
72
  export declare function buildOutpostPickupCommand(payloadUrl: string): string;
73
73
  /** Bounded, read-only capacity query executed by the target Outpost CLI. */
74
74
  export declare function buildOutpostCapacityCommand(): string;
75
- /** Read every lane on an Outpost, optionally using its configured HQ binary. */
75
+ /** Read lane records owned by an Outpost, using its configured HQ binary when set. */
76
76
  export declare function buildOutpostLaneListCommand(input?: {
77
77
  hqBinary?: string;
78
78
  }): string;
@@ -65,7 +65,7 @@ export function buildOutpostPickupCommand(payloadUrl) {
65
65
  export function buildOutpostCapacityCommand() {
66
66
  return withRemoteHqDir(joinCommandParts(["hq", "lanes", "capacity", "--json"]));
67
67
  }
68
- /** Read every lane on an Outpost, optionally using its configured HQ binary. */
68
+ /** Read lane records owned by an Outpost, using its configured HQ binary when set. */
69
69
  export function buildOutpostLaneListCommand(input = {}) {
70
70
  return withRemoteHqDir(joinCommandParts([
71
71
  input.hqBinary?.trim() || "hq",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.201.0",
3
+ "version": "5.201.1",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {