@indigoai-us/hq-cli 5.188.0 → 5.189.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.
@@ -13,12 +13,14 @@ import * as path from "node:path";
13
13
  import { registerOutpostPrunerCommand } from "./outpost-pruner.js";
14
14
  import { ensureCognitoToken } from "../utils/cognito-session.js";
15
15
  import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
16
+ import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
16
17
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
17
18
  import { resolveProcessFlagGate } from "../lib/flag-registry.js";
19
+ import { createOutpostHireGrantContext, parseOutpostHireGrantContext, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
18
20
  import { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity, resolveAdmissionHost, } from "../lib/lanes/admission-host.js";
19
21
  export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
20
22
  export { LANE_HIRE_GRANTS_FLAG };
21
- import { buildOutpostCapacityCommand, buildOutpostBalanceMoveCommand, buildOutpostIdentityBackfillCommand, buildOutpostLaneCommand, buildOutpostMoveTargetCommand, buildOutpostPickupCommand, createOutpostsClient, dispatchErrorMessage, dispatchOutpostPickup, buildOutpostLaneListCommand, parseOutpostPickupPayload, stageOutpostPayload, } from "../lib/lanes/outpost-dispatch.js";
23
+ import { buildOutpostCapacityCommand, buildOutpostBalanceMoveCommand, buildOutpostIdentityBackfillCommand, buildOutpostLaneCommand, buildOutpostMoveTargetCommand, buildOutpostPickupCommand, createOutpostsClient, dispatchErrorMessage, dispatchOutpostPickup, buildOutpostLaneListCommand, outpostRejectedEffortOption, parseOutpostPickupPayload, stageOutpostPayload, } from "../lib/lanes/outpost-dispatch.js";
22
24
  import { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "../lib/lanes/host-capacity.js";
23
25
  import { BALANCE_MOVABLE_STATES, CLAUDE_ACCOUNT_BALANCE_FLAG, accountLoadsForReport, assertBalanceApplyEnabled, planClaudeBalanceMoves, selectClaudeAccount, } from "../lib/lanes/claude-account-balance.js";
24
26
  import { parseUserYaml } from "../utils/user-yaml-error.js";
@@ -32,7 +34,7 @@ import { activeLaneStories, laneStories, laneStory, storyWarning, } from "../lib
32
34
  import { questionBodyAbsPath } from "../lib/lanes/questions.js";
33
35
  import { compactionAfterUsage, } from "../lib/lanes/compaction.js";
34
36
  import { providerCompactAfterMs } from "../lib/lanes/config.js";
35
- import { parseCliEffort, resolveLaneEffort } from "../lib/lanes/effort.js";
37
+ import { parseCliEffort, resolveLaneEffort, } from "../lib/lanes/effort.js";
36
38
  import { LanesError } from "../lib/lanes/errors.js";
37
39
  import { accountQuotaView, loadQuotaState, quotaBlockFor } from "../lib/lanes/quota.js";
38
40
  import { SESSION_MOVE_REASON } from "../lib/lanes/move-account.js";
@@ -81,7 +83,7 @@ function failJson(err) {
81
83
  if (err.error === "hire_grant_exhausted") {
82
84
  process.stderr.write(`hq lanes: ${err.message}\n`);
83
85
  }
84
- printJson(err.toJSON());
86
+ printJson({ ...err.toJSON(), message: err.message });
85
87
  process.exit(1);
86
88
  }
87
89
  const message = err instanceof Error ? err.message : String(err);
@@ -255,6 +257,8 @@ function resolveDeps(deps = {}) {
255
257
  fetchText: deps.fetchText ?? defaultFetchText,
256
258
  lanePlacementEnabled: deps.lanePlacementEnabled ?? defaultLanePlacementEnabled,
257
259
  laneHireGrantsEnabled: deps.laneHireGrantsEnabled ?? defaultLaneHireGrantsEnabled,
260
+ isSsmOwnerDispatch: deps.isSsmOwnerDispatch ?? (() => isSsmRunCommandProcess()),
261
+ withGrantHireLocks: deps.withGrantHireLocks ?? withLaneHireLocks,
258
262
  laneClaudeAccountBalanceEnabled: deps.laneClaudeAccountBalanceEnabled ?? defaultLaneClaudeAccountBalanceEnabled,
259
263
  };
260
264
  }
@@ -451,6 +455,81 @@ function laneHireFields(lane, lanes) {
451
455
  hire_summary: hireSummary,
452
456
  };
453
457
  }
458
+ function outpostCliOverride(ctx, outpostId) {
459
+ const envKey = `HQ_LANES_OUTPOST_CLI_${outpostId.toUpperCase().replace(/[^A-Z0-9_]/g, "_")}`;
460
+ const configured = ctx.env[envKey];
461
+ const hqBinary = configured === undefined ? undefined : configured.trim();
462
+ if (configured !== undefined &&
463
+ (!hqBinary ||
464
+ !path.posix.isAbsolute(hqBinary) ||
465
+ hqBinary.includes("\0") ||
466
+ hqBinary.includes("\r") ||
467
+ hqBinary.includes("\n"))) {
468
+ throw new LanesError("outpost_lane_cli_invalid", `${envKey} must be an absolute remote executable path`, { outpost_id: outpostId });
469
+ }
470
+ return hqBinary;
471
+ }
472
+ function safeOwnerErrorValue(value, key = "") {
473
+ if (/(?:env|environment|token|secret|password|credential|authorization|api[_-]?key|access[_-]?key|private[_-]?key)/i.test(key)) {
474
+ return undefined;
475
+ }
476
+ if (typeof value === "string") {
477
+ return value
478
+ .replace(/\b(Bearer|Basic)\s+[^\s,;]+/gi, "$1 [redacted]")
479
+ .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|AKIA[A-Z0-9]{16})\b/g, "[redacted]")
480
+ .replace(/\b["']?([A-Z][A-Z0-9_]{1,})["']?\s*[:=]\s*["']?([^\s"',;}]+)["']?/g, "$1=[redacted]");
481
+ }
482
+ if (Array.isArray(value)) {
483
+ return value.map((item) => safeOwnerErrorValue(item));
484
+ }
485
+ if (value === null || typeof value !== "object")
486
+ return value;
487
+ const safe = {};
488
+ for (const [childKey, childValue] of Object.entries(value)) {
489
+ const nested = safeOwnerErrorValue(childValue, childKey);
490
+ if (nested !== undefined)
491
+ safe[childKey] = nested;
492
+ }
493
+ return safe;
494
+ }
495
+ function ownerErrorFromOutput(stdout) {
496
+ const trimmed = stdout.trim();
497
+ if (!trimmed)
498
+ return undefined;
499
+ let parsed;
500
+ try {
501
+ parsed = JSON.parse(trimmed);
502
+ }
503
+ catch {
504
+ process.stderr.write("hq lanes: Outpost lanes dispatch returned malformed JSON\n");
505
+ return undefined;
506
+ }
507
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
508
+ return undefined;
509
+ }
510
+ const owner = parsed;
511
+ if (typeof owner.error !== "string" || !/^[a-z][a-z0-9_]{0,99}$/.test(owner.error)) {
512
+ return undefined;
513
+ }
514
+ if (safeOwnerErrorValue(owner.error) !== owner.error)
515
+ return undefined;
516
+ if (owner.error === "already_terminal" || owner.error === "outpost_self_target") {
517
+ return undefined;
518
+ }
519
+ const rawDetails = {};
520
+ for (const [key, value] of Object.entries(owner)) {
521
+ if (key !== "error" && key !== "message" && key !== "details")
522
+ rawDetails[key] = value;
523
+ }
524
+ if (owner.details !== null && typeof owner.details === "object" && !Array.isArray(owner.details)) {
525
+ Object.assign(rawDetails, owner.details);
526
+ }
527
+ const details = safeOwnerErrorValue(rawDetails);
528
+ const message = typeof owner.message === "string" && owner.message.trim()
529
+ ? String(safeOwnerErrorValue(owner.message))
530
+ : `Outpost owner returned ${owner.error}`;
531
+ return { error: owner.error, message, details };
532
+ }
454
533
  async function ownerLaneHireFields(ctx, lane) {
455
534
  if (lane.host.kind !== "outpost") {
456
535
  throw new LanesError("outpost_lane_hire_summary_invalid", "Owner summary requires an Outpost-hosted lane", {
@@ -458,17 +537,7 @@ async function ownerLaneHireFields(ctx, lane) {
458
537
  });
459
538
  }
460
539
  const outpostId = lane.host.id;
461
- const outpostCliEnvKey = `HQ_LANES_OUTPOST_CLI_${outpostId.toUpperCase().replace(/[^A-Z0-9_]/g, "_")}`;
462
- const configuredHqBinary = ctx.env[outpostCliEnvKey];
463
- const hqBinary = configuredHqBinary === undefined ? undefined : configuredHqBinary.trim();
464
- if (configuredHqBinary !== undefined &&
465
- (!hqBinary ||
466
- !path.posix.isAbsolute(hqBinary) ||
467
- hqBinary.includes("\0") ||
468
- hqBinary.includes("\r") ||
469
- hqBinary.includes("\n"))) {
470
- throw new LanesError("outpost_lane_cli_invalid", `${outpostCliEnvKey} must be an absolute remote executable path`, { outpost_id: outpostId });
471
- }
540
+ const hqBinary = outpostCliOverride(ctx, outpostId);
472
541
  let dispatched;
473
542
  try {
474
543
  dispatched = await ctx.dispatchOutpostExec({
@@ -2783,7 +2852,8 @@ async function dispatchCreatedOutpostLane(ctx, input) {
2783
2852
  const staged = await ctx.stageOutpostPayload(outpostId, bytes);
2784
2853
  const command = buildOutpostPickupCommand(staged.getUrl);
2785
2854
  const dispatched = await ctx.dispatchOutpostExec({ outpostId, command });
2786
- if (preserveQueuedOnFailure && dispatched.done !== true) {
2855
+ const dispatchedOwnerError = ownerErrorFromOutput(dispatched.stdout);
2856
+ if (preserveQueuedOnFailure && dispatched.done !== true && !dispatchedOwnerError) {
2787
2857
  throw new LanesError("outpost_dispatch_failed", "Outpost pickup exec has not finished; its outcome is unknown", {
2788
2858
  outpost_id: outpostId,
2789
2859
  lane_id: lane.lane_id,
@@ -2813,6 +2883,40 @@ async function dispatchCreatedOutpostLane(ctx, input) {
2813
2883
  await emitLaneTerminalStateSoft(ctx, cancelled);
2814
2884
  return cancelled;
2815
2885
  }
2886
+ const ownerError = dispatchedOwnerError;
2887
+ if (ownerError) {
2888
+ if (preserveQueuedOnFailure) {
2889
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected lane pickup: ${ownerError.message}`, {
2890
+ outpost_id: outpostId,
2891
+ lane_id: lane.lane_id,
2892
+ command_id: dispatched.commandId,
2893
+ status: dispatched.status,
2894
+ done: dispatched.done ?? false,
2895
+ exit_code: dispatched.exitCode,
2896
+ ...(remote?.pickup_persisted === false ||
2897
+ ownerError.details.pickup_persisted === false
2898
+ ? { pickup_persisted: false }
2899
+ : {}),
2900
+ owner_error: ownerError,
2901
+ });
2902
+ }
2903
+ const failed = markDispatchFailure(hqRoot, lane.lane_id, {
2904
+ outpost_id: outpostId,
2905
+ command_id: dispatched.commandId,
2906
+ exit_code: dispatched.exitCode,
2907
+ status: dispatched.status,
2908
+ owner_error: ownerError,
2909
+ });
2910
+ await emitLaneTerminalStateSoft(ctx, failed);
2911
+ throw new LanesError(DISPATCH_TERMINAL_REASON, `Outpost ${outpostId} rejected lane pickup: ${ownerError.message}`, {
2912
+ outpost_id: outpostId,
2913
+ lane_id: failed.lane_id,
2914
+ state: failed.state,
2915
+ terminal_reason: DISPATCH_TERMINAL_REASON,
2916
+ command_id: dispatched.commandId,
2917
+ owner_error: ownerError,
2918
+ });
2919
+ }
2816
2920
  if (preserveQueuedOnFailure && remote?.pickup_persisted === false) {
2817
2921
  throw new LanesError("outpost_dispatch_failed", "Outpost pickup finished and reported that it did not persist the lane", {
2818
2922
  outpost_id: outpostId,
@@ -2980,19 +3084,26 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
2980
3084
  throw new LanesError("outpost_dispatch_failed", `Lane ${lane.lane_id} has no Outpost host to dispatch to`, { lane_id: lane.lane_id });
2981
3085
  }
2982
3086
  const outpostId = lane.host.id;
3087
+ const grantContext = action === "grant"
3088
+ ? createOutpostHireGrantContext(lane, loadAllLanes(ctx.hqRoot()), await ctx.laneHireGrantsEnabled(lane.company))
3089
+ : undefined;
2983
3090
  let dispatched;
2984
3091
  try {
2985
3092
  dispatched = await ctx.dispatchOutpostExec({
2986
3093
  outpostId,
2987
3094
  command: buildOutpostLaneCommand({
2988
- action,
3095
+ action: action === "grant" ? "grant-target" : action,
2989
3096
  laneId: lane.lane_id,
3097
+ hqBinary: action === "grant" ? outpostCliOverride(ctx, outpostId) : undefined,
2990
3098
  text: options.text,
2991
3099
  project: options.project,
2992
3100
  story: options.story,
2993
3101
  account: options.account,
2994
3102
  model: options.model,
3103
+ effort: options.effort,
3104
+ worker: options.worker,
2995
3105
  hire: options.hire,
3106
+ grantContext,
2996
3107
  compact: options.compact,
2997
3108
  }),
2998
3109
  });
@@ -3000,6 +3111,30 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3000
3111
  catch (err) {
3001
3112
  throw new LanesError("outpost_dispatch_failed", `Could not dispatch ${action} for lane ${lane.lane_id} to Outpost ${outpostId}: ${dispatchErrorMessage(err)}`, { lane_id: lane.lane_id, outpost_id: outpostId });
3002
3113
  }
3114
+ const ownerError = ownerErrorFromOutput(dispatched.stdout);
3115
+ if (options.effort !== undefined && outpostRejectedEffortOption(dispatched)) {
3116
+ throw new LanesError("outpost_effort_unsupported", `Outpost ${outpostId} rejected --effort; update hq-cli on that Outpost before changing a lane's effort remotely`, {
3117
+ lane_id: lane.lane_id,
3118
+ outpost_id: outpostId,
3119
+ command_id: dispatched.commandId,
3120
+ effort: options.effort,
3121
+ status: dispatched.status,
3122
+ done: dispatched.done ?? false,
3123
+ exit_code: dispatched.exitCode,
3124
+ owner_error: ownerError,
3125
+ });
3126
+ }
3127
+ if (ownerError) {
3128
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected the ${action} command: ${ownerError.message}`, {
3129
+ lane_id: lane.lane_id,
3130
+ outpost_id: outpostId,
3131
+ command_id: dispatched.commandId,
3132
+ status: dispatched.status,
3133
+ done: dispatched.done ?? false,
3134
+ exit_code: dispatched.exitCode,
3135
+ owner_error: ownerError,
3136
+ });
3137
+ }
3003
3138
  if (dispatched.done !== true ||
3004
3139
  dispatched.status !== "Success" ||
3005
3140
  dispatched.exitCode !== 0) {
@@ -3014,13 +3149,11 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
3014
3149
  }
3015
3150
  const remote = parseRemoteJson(dispatched.stdout, "Outpost lane command");
3016
3151
  if (remote?.error !== undefined) {
3017
- throw new LanesError("outpost_remote_command_failed", typeof remote.message === "string"
3018
- ? remote.message
3019
- : `Outpost ${outpostId} rejected the ${action} command`, {
3152
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected the ${action} command`, {
3020
3153
  lane_id: lane.lane_id,
3021
3154
  outpost_id: outpostId,
3022
3155
  command_id: dispatched.commandId,
3023
- remote_error: remote.error,
3156
+ owner_error: ownerErrorFromOutput(dispatched.stdout),
3024
3157
  });
3025
3158
  }
3026
3159
  const resultConfirmed = remote !== null &&
@@ -3642,6 +3775,9 @@ async function commitLaneMove(ctx, laneId, target, account) {
3642
3775
  outcome: "unknown",
3643
3776
  ...(commandId !== undefined ? { command_id: commandId } : {}),
3644
3777
  check_command: checkCommand,
3778
+ ...(dispatchDetails.owner_error !== undefined
3779
+ ? { owner_error: dispatchDetails.owner_error }
3780
+ : {}),
3645
3781
  });
3646
3782
  }
3647
3783
  let restored = false;
@@ -3682,6 +3818,9 @@ async function commitLaneMove(ctx, laneId, target, account) {
3682
3818
  source_host: before.host,
3683
3819
  target_host: target,
3684
3820
  source_restored: restored,
3821
+ ...(dispatchDetails.owner_error !== undefined
3822
+ ? { owner_error: dispatchDetails.owner_error }
3823
+ : {}),
3685
3824
  ...(rollbackError !== undefined
3686
3825
  ? { rollback_error: dispatchErrorMessage(rollbackError) }
3687
3826
  : {}),
@@ -3711,6 +3850,18 @@ async function dispatchRemoteLaneMove(ctx, source, target, account) {
3711
3850
  catch (err) {
3712
3851
  throw new LanesError("lane_move_dispatch_failed", `Could not ask Outpost ${source.host.id} to move lane ${source.lane_id}: ${dispatchErrorMessage(err)}`, { lane_id: source.lane_id, source_outpost: source.host.id });
3713
3852
  }
3853
+ const ownerError = ownerErrorFromOutput(dispatched.stdout);
3854
+ if (ownerError) {
3855
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${source.host.id} rejected the lane move: ${ownerError.message}`, {
3856
+ lane_id: source.lane_id,
3857
+ source_outpost: source.host.id,
3858
+ command_id: dispatched.commandId,
3859
+ status: dispatched.status,
3860
+ done: dispatched.done ?? false,
3861
+ exit_code: dispatched.exitCode,
3862
+ owner_error: ownerError,
3863
+ });
3864
+ }
3714
3865
  if (dispatched.done !== true || dispatched.status !== "Success" || dispatched.exitCode !== 0) {
3715
3866
  throw new LanesError("lane_move_dispatch_failed", `Outpost ${source.host.id} did not confirm the lane move`, {
3716
3867
  lane_id: source.lane_id,
@@ -3830,40 +3981,54 @@ function callerMatchesSenior(ctx, seniorRef) {
3830
3981
  function forceAdmissionAuthorized(ctx, lane) {
3831
3982
  return callerMatchesSenior(ctx, lane.senior);
3832
3983
  }
3833
- async function runGrant(ctx, laneId, opts) {
3834
- if (opts.hire === undefined) {
3835
- throw new LanesError("missing_hire_grant", "--hire is required", {});
3836
- }
3837
- const hireGrant = parseHireGrant(opts.hire);
3984
+ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContext) {
3838
3985
  const hqRoot = ctx.hqRoot();
3839
3986
  const initial = readLane(hqRoot, laneId);
3840
- if (!callerMatchesSenior(ctx, initial.senior)) {
3987
+ if (authorization === "senior" && !callerMatchesSenior(ctx, initial.senior)) {
3841
3988
  throw new LanesError("hire_grant_unauthorized", `Only senior ${seniorKey(initial.senior)} may change lane ${laneId}'s hire grant`, { lane_id: laneId, senior: initial.senior });
3842
3989
  }
3843
- if (initial.host.kind === "outpost" && !laneRunsHere(initial, reconcileHostFromCtx(ctx))) {
3844
- await dispatchOutpostLaneCommand(ctx, initial, "grant", { hire: String(hireGrant) });
3845
- return;
3990
+ if (authorization === "ssm-owner-dispatch" &&
3991
+ (initial.host.kind !== "outpost" || !laneIsHostedHere(ctx, initial))) {
3992
+ throw new LanesError("lane_remote", `Outpost grant dispatch cannot mutate lane ${laneId} on this host`, { lane_id: laneId, host: initial.host });
3846
3993
  }
3847
- const enforce = await ctx.laneHireGrantsEnabled(initial.company);
3994
+ if (authorization === "ssm-owner-dispatch" &&
3995
+ (!grantContext || !sameHireGrantSenior(initial.senior, grantContext.expected_senior))) {
3996
+ throw new LanesError("hire_grant_senior_changed", `Lane ${laneId}'s senior changed after the primary authorized its grant update`, { lane_id: laneId, current_senior: initial.senior });
3997
+ }
3998
+ const enforce = grantContext?.enforcement_enabled ??
3999
+ await ctx.laneHireGrantsEnabled(initial.company);
3848
4000
  const lockedLaneIds = [
3849
4001
  laneId,
3850
4002
  ...(initial.senior.kind === "lane" && initial.senior.id
3851
4003
  ? [initial.senior.id]
3852
4004
  : []),
3853
4005
  ];
3854
- const updated = withLaneHireLocks(hqRoot, lockedLaneIds, () => {
4006
+ const updated = ctx.withGrantHireLocks(hqRoot, lockedLaneIds, () => {
3855
4007
  const current = readLane(hqRoot, laneId);
3856
- if (!callerMatchesSenior(ctx, current.senior)) {
4008
+ if (authorization === "senior" && !callerMatchesSenior(ctx, current.senior)) {
3857
4009
  throw new LanesError("hire_grant_unauthorized", `Only senior ${seniorKey(current.senior)} may change lane ${laneId}'s hire grant`, { lane_id: laneId, senior: current.senior });
3858
4010
  }
3859
- const hierarchy = loadAllLanes(hqRoot);
4011
+ if (authorization === "ssm-owner-dispatch" &&
4012
+ (current.host.kind !== "outpost" || !laneIsHostedHere(ctx, current))) {
4013
+ throw new LanesError("lane_remote", `Outpost grant dispatch cannot mutate lane ${laneId} on this host`, { lane_id: laneId, host: current.host });
4014
+ }
4015
+ if (authorization === "ssm-owner-dispatch" &&
4016
+ (!grantContext || !sameHireGrantSenior(current.senior, grantContext.expected_senior))) {
4017
+ 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 });
4018
+ }
4019
+ const hierarchy = grantContext?.hierarchy ?? loadAllLanes(hqRoot);
3860
4020
  if (enforce && current.senior.kind === "lane") {
3861
4021
  if (!current.senior.id) {
3862
4022
  throw new LanesError("hire_grant_senior_invalid", `Lane ${laneId} has a lane senior without an id`, {
3863
4023
  lane_id: laneId,
3864
4024
  });
3865
4025
  }
3866
- const parent = readLane(hqRoot, current.senior.id);
4026
+ const parent = hierarchy.find((lane) => lane.lane_id === current.senior.id);
4027
+ if (!parent) {
4028
+ throw new LanesError(authorization === "ssm-owner-dispatch"
4029
+ ? "hire_grant_context_invalid"
4030
+ : "hire_grant_senior_invalid", `Lane ${laneId}'s senior lane is missing from the grant hierarchy`, { lane_id: laneId, senior_lane_id: current.senior.id });
4031
+ }
3867
4032
  if (isTerminalLaneState(parent.state)) {
3868
4033
  throw new LanesError("hire_grant_senior_inactive", `Senior lane ${parent.lane_id} is ${parent.state} and cannot change grants`, { senior_lane_id: parent.lane_id, state: parent.state });
3869
4034
  }
@@ -3901,9 +4066,44 @@ async function runGrant(ctx, laneId, opts) {
3901
4066
  hire_grant: hireGrant,
3902
4067
  }));
3903
4068
  });
3904
- const fields = laneHireFields(updated, loadAllLanes(hqRoot));
4069
+ return updated;
4070
+ }
4071
+ function printGrantResult(hqRoot, updated, hierarchy) {
4072
+ const fields = laneHireFields(updated, hierarchy ?? loadAllLanes(hqRoot));
3905
4073
  printJson({ ok: true, action: "grant", lane_id: updated.lane_id, lane: updated, ...fields });
3906
4074
  }
4075
+ async function runGrant(ctx, laneId, opts) {
4076
+ if (opts.hire === undefined) {
4077
+ throw new LanesError("missing_hire_grant", "--hire is required", {});
4078
+ }
4079
+ const hireGrant = parseHireGrant(opts.hire);
4080
+ const hqRoot = ctx.hqRoot();
4081
+ const initial = readLane(hqRoot, laneId);
4082
+ if (!callerMatchesSenior(ctx, initial.senior)) {
4083
+ throw new LanesError("hire_grant_unauthorized", `Only senior ${seniorKey(initial.senior)} may change lane ${laneId}'s hire grant`, { lane_id: laneId, senior: initial.senior });
4084
+ }
4085
+ if (initial.host.kind === "outpost" && !laneRunsHere(initial, reconcileHostFromCtx(ctx))) {
4086
+ await dispatchOutpostLaneCommand(ctx, initial, "grant", { hire: String(hireGrant) });
4087
+ return;
4088
+ }
4089
+ const updated = await applyHireGrant(ctx, laneId, hireGrant, "senior");
4090
+ printGrantResult(hqRoot, updated);
4091
+ }
4092
+ async function runGrantOwnerTarget(ctx, laneId, opts) {
4093
+ if (!ctx.isSsmOwnerDispatch()) {
4094
+ throw new LanesError("hire_grant_dispatch_unauthorized", "The Outpost grant target is available only to an SSM Run Command dispatch", { lane_id: laneId });
4095
+ }
4096
+ if (opts.hire === undefined) {
4097
+ throw new LanesError("missing_hire_grant", "--hire is required", {});
4098
+ }
4099
+ if (opts.grantContext === undefined) {
4100
+ throw new LanesError("hire_grant_context_invalid", "--grant-context is required", { lane_id: laneId });
4101
+ }
4102
+ const hireGrant = parseHireGrant(opts.hire);
4103
+ const grantContext = parseOutpostHireGrantContext(opts.grantContext, laneId);
4104
+ const hqRoot = ctx.hqRoot();
4105
+ printGrantResult(hqRoot, await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext), grantContext.hierarchy);
4106
+ }
3907
4107
  async function runBackfillGrants(ctx, opts) {
3908
4108
  if (opts.apply && opts.dryRun) {
3909
4109
  throw new LanesError("hire_grant_backfill_mode_conflict", "Pass either --dry-run or --apply, not both", {});
@@ -4150,34 +4350,43 @@ function validateResumeProfileModel(ctx, lane, profile) {
4150
4350
  env: ctx.env,
4151
4351
  });
4152
4352
  }
4353
+ function applyResumeProfileAndEffort(current, next, input) {
4354
+ if (input.profile !== undefined) {
4355
+ const profile = input.profile.profile;
4356
+ next.worker = input.profile.id;
4357
+ next.worker_path = input.profile.path;
4358
+ next.worker_pack = input.profile.pack;
4359
+ next.worker_profile = profile.id;
4360
+ next.worker_profile_hash = profile.contentHash;
4361
+ next.worker_profile_ignored = ignoredWorkerProfileFields(current.provider);
4362
+ next.worker_policy_delivery = undefined;
4363
+ if (next.model_source !== "cli") {
4364
+ if (profile.meta.provider === current.provider && profile.meta.model !== undefined) {
4365
+ next.model = profile.meta.model;
4366
+ next.model_source = "profile";
4367
+ }
4368
+ else if (profile.meta.provider === current.provider) {
4369
+ next.model = undefined;
4370
+ next.model_source = "none";
4371
+ }
4372
+ }
4373
+ }
4374
+ if (input.effort !== undefined) {
4375
+ next.effort = input.effort.effort;
4376
+ next.effort_source = input.effort.source;
4377
+ }
4378
+ }
4153
4379
  function recordResumeSetup(ctx, lane, input) {
4154
- if (input.profile === undefined && input.meshCompany === undefined)
4380
+ if (input.profile === undefined &&
4381
+ input.effort === undefined &&
4382
+ input.meshCompany === undefined)
4155
4383
  return lane;
4156
4384
  return casUpdateLane(ctx.hqRoot(), lane.lane_id, (current) => {
4157
4385
  if (current.version !== lane.version) {
4158
4386
  throw new LanesError("lane_resume_setup_conflict", `Lane ${lane.lane_id} changed while resume settings were prepared; run the resume again`, { lane_id: lane.lane_id, prepared_version: lane.version, version: current.version });
4159
4387
  }
4160
4388
  const next = { ...current };
4161
- if (input.profile !== undefined) {
4162
- const profile = input.profile.profile;
4163
- next.worker = input.profile.id;
4164
- next.worker_path = input.profile.path;
4165
- next.worker_pack = input.profile.pack;
4166
- next.worker_profile = profile.id;
4167
- next.worker_profile_hash = profile.contentHash;
4168
- next.worker_profile_ignored = ignoredWorkerProfileFields(current.provider);
4169
- next.worker_policy_delivery = undefined;
4170
- if (current.model_source !== "cli") {
4171
- if (profile.meta.provider === current.provider && profile.meta.model !== undefined) {
4172
- next.model = profile.meta.model;
4173
- next.model_source = "profile";
4174
- }
4175
- else if (profile.meta.provider === current.provider) {
4176
- next.model = undefined;
4177
- next.model_source = "none";
4178
- }
4179
- }
4180
- }
4389
+ applyResumeProfileAndEffort(current, next, input);
4181
4390
  if (input.meshCompany !== undefined) {
4182
4391
  if (current.company === null)
4183
4392
  next.mesh_company = input.meshCompany;
@@ -4187,6 +4396,40 @@ function recordResumeSetup(ctx, lane, input) {
4187
4396
  return next;
4188
4397
  });
4189
4398
  }
4399
+ /** Resolve an explicit resume override or newly attached profile like create does. */
4400
+ function resolveResumeEffort(ctx, lane, input) {
4401
+ if (input.cliEffort === undefined && input.profile === undefined)
4402
+ return undefined;
4403
+ const config = loadLanesConfig(ctx.hqRoot());
4404
+ const providerConfig = config.providers[lane.provider];
4405
+ const accountId = input.account?.trim() || lane.account;
4406
+ const accountConfig = accountId === undefined
4407
+ ? undefined
4408
+ : providerConfig.accounts[accountId];
4409
+ const profileDefaultsApply = input.profile?.profile.meta.provider === lane.provider;
4410
+ const pinnedModel = lane.model_source === undefined || lane.model_source === "cli"
4411
+ ? lane.model
4412
+ : undefined;
4413
+ const model = resolveLaneModel({
4414
+ cliModel: input.model?.trim() || pinnedModel,
4415
+ profileModel: profileDefaultsApply ? input.profile?.profile.meta.model : undefined,
4416
+ accountModel: accountConfig?.model,
4417
+ providerModel: providerConfig.model,
4418
+ });
4419
+ return resolveLaneEffort({
4420
+ cliEffort: input.cliEffort,
4421
+ profileEffort: profileDefaultsApply ? input.profile?.profile.meta.effort : undefined,
4422
+ accountEffort: accountConfig?.effort,
4423
+ providerEffort: providerConfig.effort,
4424
+ model,
4425
+ });
4426
+ }
4427
+ function recordedResumeEffort(lane) {
4428
+ return {
4429
+ ...(lane.effort !== undefined ? { effort: lane.effort } : {}),
4430
+ source: lane.effort_source ?? "none",
4431
+ };
4432
+ }
4190
4433
  /**
4191
4434
  * US-023: move the lane's provider session to `--account` (same provider)
4192
4435
  * before a resume or a message resume. Every check runs before anything is
@@ -4254,7 +4497,7 @@ async function prepareAccountMove(ctx, lane, input) {
4254
4497
  }).logged_in !== true) {
4255
4498
  throw new LanesError("account_not_logged_in", `Account ${target} for ${lane.provider} is not logged in (no credential file)`, { provider: lane.provider, account: target, lane_id: laneId });
4256
4499
  }
4257
- const profile = input.workerProfile ?? (lane.worker_profile !== undefined
4500
+ const profile = input.workerProfile?.profile ?? (lane.worker_profile !== undefined
4258
4501
  ? loadWorkerProfile(hqRoot, lane.worker_profile)
4259
4502
  : undefined);
4260
4503
  const profileDefaultsApply = profile !== undefined && profile.meta.provider === lane.provider;
@@ -4356,6 +4599,10 @@ async function prepareAccountMove(ctx, lane, input) {
4356
4599
  ? undefined
4357
4600
  : { session_id: sessionId, at, ...input.pending },
4358
4601
  };
4602
+ applyResumeProfileAndEffort(current, next, {
4603
+ ...(input.workerProfile !== undefined ? { profile: input.workerProfile } : {}),
4604
+ ...(input.effort !== undefined ? { effort: input.effort } : {}),
4605
+ });
4359
4606
  if (input.deferTurnStart) {
4360
4607
  next.state = current.state;
4361
4608
  }
@@ -4432,6 +4679,10 @@ async function runMessage(ctx, laneId, opts) {
4432
4679
  return;
4433
4680
  }
4434
4681
  assertLaneRunsHereForOperatorMutation(ctx, lane, "message");
4682
+ const cliEffort = parseCliEffort(lane.provider, opts.effort);
4683
+ if (cliEffort !== undefined && !providerSupportsSessionResume(lane.provider)) {
4684
+ throw new LanesError("effort_resume_unsupported", `Provider ${lane.provider} cannot change effort on a resumed session`, { lane_id: laneId, provider: lane.provider });
4685
+ }
4435
4686
  // Check before transcript recovery, account probing, or message writes.
4436
4687
  // The final state transition repeats this check under the senior lock.
4437
4688
  await withResumeHireAdmission(ctx, lane, () => undefined);
@@ -4463,12 +4714,24 @@ async function runMessage(ctx, laneId, opts) {
4463
4714
  if (opts.worker !== undefined && mechanism === "dropbox") {
4464
4715
  throw new LanesError("worker_profile_requires_resume", `Lane ${laneId} is still running; --worker can only be applied when message starts a resumed provider turn`, { lane_id: laneId, state: lane.state });
4465
4716
  }
4717
+ if (cliEffort !== undefined && mechanism === "dropbox") {
4718
+ throw new LanesError("lane_effort_requires_resume", `Lane ${laneId} is still running; --effort can only be applied when message starts a resumed provider turn`, { lane_id: laneId, state: lane.state });
4719
+ }
4466
4720
  const resumeProfile = opts.worker !== undefined
4467
4721
  ? resolveResumeProfile(ctx, lane, opts.worker)
4468
4722
  : undefined;
4469
4723
  if (opts.model !== undefined && opts.account === undefined) {
4470
4724
  throw new LanesError("model_requires_account", "--model on hq lanes message is only accepted together with --account", {});
4471
4725
  }
4726
+ const automaticAccountSelection = opts.account === undefined && mechanism === "resume"
4727
+ ? await selectAutomaticClaudeResumeAccount(ctx, lane)
4728
+ : undefined;
4729
+ const resumeEffort = resolveResumeEffort(ctx, lane, {
4730
+ cliEffort,
4731
+ profile: resumeProfile,
4732
+ account: opts.account ?? automaticAccountSelection?.account,
4733
+ model: opts.model,
4734
+ });
4472
4735
  const messageId = mintMessageId();
4473
4736
  const at = ctx.now().toISOString();
4474
4737
  const projectId = opts.project?.trim() || lane.project_id;
@@ -4489,7 +4752,8 @@ async function runMessage(ctx, laneId, opts) {
4489
4752
  move = await prepareAccountMove(ctx, lane, {
4490
4753
  account: opts.account,
4491
4754
  cliModel: opts.model?.trim() || undefined,
4492
- ...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
4755
+ ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
4756
+ ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4493
4757
  pending: { trigger: "message", text, message_id: messageId, compact: opts.compact !== false },
4494
4758
  });
4495
4759
  lane = move.lane;
@@ -4498,13 +4762,14 @@ async function runMessage(ctx, laneId, opts) {
4498
4762
  }
4499
4763
  }
4500
4764
  else if (mechanism === "resume") {
4501
- const selection = await selectAutomaticClaudeResumeAccount(ctx, lane);
4765
+ const selection = automaticAccountSelection;
4502
4766
  if (selection !== undefined) {
4503
4767
  if (selection.account !== lane.account) {
4504
4768
  move = await prepareAccountMove(ctx, lane, {
4505
4769
  account: selection.account,
4506
4770
  cliModel: undefined,
4507
- ...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
4771
+ ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
4772
+ ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4508
4773
  pending: {
4509
4774
  trigger: "message",
4510
4775
  text,
@@ -4522,10 +4787,14 @@ async function runMessage(ctx, laneId, opts) {
4522
4787
  }
4523
4788
  }
4524
4789
  }
4525
- if (resumeProfile !== undefined) {
4526
- if (opts.account === undefined)
4527
- validateResumeProfileModel(ctx, lane, resumeProfile.profile);
4528
- lane = recordResumeSetup(ctx, lane, { profile: resumeProfile });
4790
+ if (resumeProfile !== undefined && opts.account === undefined) {
4791
+ validateResumeProfileModel(ctx, lane, resumeProfile.profile);
4792
+ }
4793
+ if (move === null && (resumeProfile !== undefined || resumeEffort !== undefined)) {
4794
+ lane = recordResumeSetup(ctx, lane, {
4795
+ ...(resumeProfile !== undefined ? { profile: resumeProfile } : {}),
4796
+ ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4797
+ });
4529
4798
  }
4530
4799
  if (move?.queued) {
4531
4800
  // Parked at the target's cap: reconcile resumes the session there.
@@ -4599,6 +4868,7 @@ async function runMessage(ctx, laneId, opts) {
4599
4868
  }, {
4600
4869
  trigger: { kind: "message", text, message_id: messageId },
4601
4870
  compact: opts.compact !== false,
4871
+ effort: resumeEffort ?? recordedResumeEffort(lane),
4602
4872
  });
4603
4873
  archiveResumeMessage(hqRoot, laneId, dropboxMessage);
4604
4874
  return started;
@@ -4885,6 +5155,10 @@ async function runResume(ctx, laneId, opts) {
4885
5155
  // slot that its senior has already assigned elsewhere.
4886
5156
  await withResumeHireAdmission(ctx, lane, () => undefined);
4887
5157
  assertLaneRunsHereForOperatorMutation(ctx, lane, "resume");
5158
+ const cliEffort = parseCliEffort(lane.provider, opts.effort);
5159
+ if (cliEffort !== undefined && !providerSupportsSessionResume(lane.provider)) {
5160
+ throw new LanesError("effort_resume_unsupported", `Provider ${lane.provider} cannot change effort on a resumed session`, { lane_id: laneId, provider: lane.provider });
5161
+ }
4888
5162
  lane = laneWithRecoveredProviderSession(hqRoot, lane);
4889
5163
  if (opts.model !== undefined && opts.account === undefined) {
4890
5164
  throw new LanesError("model_requires_account", "--model on hq lanes resume is only accepted together with --account", {});
@@ -4941,12 +5215,19 @@ async function runResume(ctx, laneId, opts) {
4941
5215
  const automaticAccountSelection = opts.account === undefined
4942
5216
  ? await selectAutomaticClaudeResumeAccount(ctx, lane)
4943
5217
  : undefined;
5218
+ const resumeEffort = resolveResumeEffort(ctx, lane, {
5219
+ cliEffort,
5220
+ profile: resumeProfile,
5221
+ account: opts.account ?? automaticAccountSelection?.account,
5222
+ model: opts.model,
5223
+ });
4944
5224
  let move = null;
4945
5225
  if (opts.account !== undefined) {
4946
5226
  move = await prepareAccountMove(ctx, lane, {
4947
5227
  account: opts.account,
4948
5228
  cliModel: opts.model?.trim() || undefined,
4949
- ...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
5229
+ ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
5230
+ ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4950
5231
  pending: {
4951
5232
  trigger: "resume",
4952
5233
  resumed_from: resumedFrom,
@@ -4962,7 +5243,8 @@ async function runResume(ctx, laneId, opts) {
4962
5243
  move = await prepareAccountMove(ctx, lane, {
4963
5244
  account: automaticAccountSelection.account,
4964
5245
  cliModel: undefined,
4965
- ...(resumeProfile !== undefined ? { workerProfile: resumeProfile.profile } : {}),
5246
+ ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
5247
+ ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4966
5248
  pending: {
4967
5249
  trigger: "resume",
4968
5250
  resumed_from: resumedFrom,
@@ -4979,7 +5261,8 @@ async function runResume(ctx, laneId, opts) {
4979
5261
  }
4980
5262
  }
4981
5263
  lane = recordResumeSetup(ctx, lane, {
4982
- ...(resumeProfile !== undefined ? { profile: resumeProfile } : {}),
5264
+ ...(move === null && resumeProfile !== undefined ? { profile: resumeProfile } : {}),
5265
+ ...(move === null && resumeEffort !== undefined ? { effort: resumeEffort } : {}),
4983
5266
  ...(requestedMeshCompany !== undefined
4984
5267
  ? { meshCompany: requestedMeshCompany, threadId: meshThreadId }
4985
5268
  : {}),
@@ -5053,6 +5336,7 @@ async function runResume(ctx, laneId, opts) {
5053
5336
  ...(messageId !== undefined ? { message_id: messageId } : {}),
5054
5337
  },
5055
5338
  compact: opts.compact !== false,
5339
+ effort: resumeEffort ?? recordedResumeEffort(lane),
5056
5340
  });
5057
5341
  });
5058
5342
  }, ctx.sleep);
@@ -5718,6 +6002,16 @@ async function readOutpostTerminalLane(ctx, laneId, outpostId, stopCommandId) {
5718
6002
  !commandId ||
5719
6003
  dispatched.status !== "Success" ||
5720
6004
  dispatched.exitCode !== 0) {
6005
+ const ownerError = ownerErrorFromOutput(dispatched.stdout);
6006
+ if (ownerError) {
6007
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected the lane read after stop: ${ownerError.message}`, {
6008
+ lane_id: laneId,
6009
+ outpost_id: outpostId,
6010
+ command_id: stopCommandId,
6011
+ sync_command_id: commandId || null,
6012
+ owner_error: ownerError,
6013
+ });
6014
+ }
5721
6015
  throw new LanesError("outpost_delivery_unconfirmed", `Outpost ${outpostId} did not confirm a lane read after stop ${laneId}`, {
5722
6016
  lane_id: laneId,
5723
6017
  outpost_id: outpostId,
@@ -5741,6 +6035,16 @@ async function readOutpostTerminalLane(ctx, laneId, outpostId, stopCommandId) {
5741
6035
  cause: err instanceof Error ? err.message : String(err),
5742
6036
  });
5743
6037
  }
6038
+ const ownerError = ownerErrorFromOutput(dispatched.stdout);
6039
+ if (ownerError) {
6040
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected the lane read after stop: ${ownerError.message}`, {
6041
+ lane_id: laneId,
6042
+ outpost_id: outpostId,
6043
+ command_id: stopCommandId,
6044
+ sync_command_id: commandId,
6045
+ owner_error: ownerError,
6046
+ });
6047
+ }
5744
6048
  if (remote?.ok !== true || remote.action !== "show") {
5745
6049
  throw new LanesError("outpost_delivery_unconfirmed", `Outpost ${outpostId} did not return lane ${laneId} after stop`, {
5746
6050
  lane_id: laneId,
@@ -5771,6 +6075,18 @@ async function dispatchOutpostLaneStop(ctx, lane) {
5771
6075
  catch (err) {
5772
6076
  throw new LanesError("outpost_dispatch_failed", `Could not dispatch stop for lane ${lane.lane_id} to Outpost ${outpostId}: ${dispatchErrorMessage(err)}`, { lane_id: lane.lane_id, outpost_id: outpostId });
5773
6077
  }
6078
+ const ownerError = ownerErrorFromOutput(dispatched.stdout);
6079
+ if (ownerError) {
6080
+ throw new LanesError("outpost_dispatch_failed", `Outpost ${outpostId} rejected the stop command: ${ownerError.message}`, {
6081
+ lane_id: lane.lane_id,
6082
+ outpost_id: outpostId,
6083
+ command_id: dispatched.commandId,
6084
+ status: dispatched.status,
6085
+ done: dispatched.done ?? false,
6086
+ exit_code: dispatched.exitCode,
6087
+ owner_error: ownerError,
6088
+ });
6089
+ }
5774
6090
  const commandId = dispatched.commandId?.trim();
5775
6091
  if (dispatched.done !== true || !commandId) {
5776
6092
  throw new LanesError("outpost_delivery_unconfirmed", `Outpost ${outpostId} did not return a completed stop command for lane ${lane.lane_id}`, {
@@ -5814,13 +6130,13 @@ async function dispatchOutpostLaneStop(ctx, lane) {
5814
6130
  syncCommandId = owner.commandId;
5815
6131
  }
5816
6132
  else if (remote?.error !== undefined) {
5817
- throw new LanesError("outpost_remote_command_failed", typeof remote.message === "string"
6133
+ throw new LanesError("outpost_dispatch_failed", typeof remote.message === "string"
5818
6134
  ? remote.message
5819
6135
  : `Outpost ${outpostId} rejected the stop command for lane ${lane.lane_id}`, {
5820
6136
  lane_id: lane.lane_id,
5821
6137
  outpost_id: outpostId,
5822
6138
  command_id: commandId,
5823
- remote_error: remote.error,
6139
+ owner_error: ownerErrorFromOutput(dispatched.stdout),
5824
6140
  });
5825
6141
  }
5826
6142
  else {
@@ -6661,6 +6977,14 @@ export function registerLanesCommand(parent, deps = {}) {
6661
6977
  .action(wrap(async (opts) => {
6662
6978
  await runCreate(ctx, opts);
6663
6979
  }));
6980
+ lanes
6981
+ .command("grant-target", { hidden: true })
6982
+ .argument("<lane-id>", "Lane id")
6983
+ .option("--hire <n>", "Non-negative safe integer hire grant")
6984
+ .option("--grant-context <base64url>", "Primary-authorized hire hierarchy and gate decision")
6985
+ .action(wrap(async (laneId, opts) => {
6986
+ await runGrantOwnerTarget(ctx, laneId, opts);
6987
+ }));
6664
6988
  lanes
6665
6989
  .command("grant")
6666
6990
  .description("Set a lane's alive child-lane grant as its senior")
@@ -6823,6 +7147,7 @@ export function registerLanesCommand(parent, deps = {}) {
6823
7147
  .option("--story <id>", "Re-point story attribution from this message")
6824
7148
  .option("--account <id>", "Move the lane's session to this account of its provider before the message resumes it")
6825
7149
  .option("--model <id>", "Model for the moved lane (with --account only)")
7150
+ .option("--effort <level>", "Reasoning effort for the resumed provider turn")
6826
7151
  .option("--worker <profile>", "Attach a personal worker profile when this message resumes the lane")
6827
7152
  .option("--no-compact", "Skip the resume-time compaction check for this message")
6828
7153
  .action(wrap(async (laneId, opts) => {
@@ -6837,6 +7162,7 @@ export function registerLanesCommand(parent, deps = {}) {
6837
7162
  .option("--story <id>", "Re-point story attribution from this resume")
6838
7163
  .option("--account <id>", "Move the lane's session to this account of its provider before resuming")
6839
7164
  .option("--model <id>", "Model for the moved lane (with --account only)")
7165
+ .option("--effort <level>", "Reasoning effort for the resumed provider turn")
6840
7166
  .option("--worker <profile>", "Attach a personal worker profile for the resumed turn")
6841
7167
  .option("--mesh-company <slug>", "Attach Work Mesh attribution after verifying company membership")
6842
7168
  .option("--no-compact", "Skip the resume-time compaction check for this resume")