@indigoai-us/hq-cli 5.345.15 → 5.345.16

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,9 +2,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [5.345.15] — 2026-10-06
5
+ ## [5.345.16] — 2026-10-06
6
6
  - Route live hook Sentry reports through the fast CLI path.
7
7
 
8
+ - Claude account model settings are defaults, so explicit lane models remain eligible; explicit account selections on create, resume, and message are recorded as CLI choices.
8
9
  - When `hq-anywhere-runtime` is enabled, `hq install --global --runtime claude --via plugin` installs the `hq` plugin from the `indigoai-us/hq-packages` Claude marketplace, and `hq install @indigoai-us/hq-anywhere` installs the Codex pack from that repository. The flag-off path refuses the HQ Anywhere alias without fetching a package source.
9
10
 
10
11
  ## [5.345.14] — 2026-10-06
@@ -518,7 +518,7 @@ export function createLaneMessageHandlers(deps) {
518
518
  automaticAccountSelection = undefined;
519
519
  }
520
520
  }
521
- const profileRefreshAccount = refreshProfileSettings && resumeProfile !== undefined &&
521
+ const profileRefreshAccount = opts.account === undefined && refreshProfileSettings && resumeProfile !== undefined &&
522
522
  lane.account_source !== undefined && lane.account_source !== "cli" &&
523
523
  resumeProfile.profile.meta.provider === lane.provider
524
524
  ? resumeProfile.profile.meta.account ?? (resumeProfile.profile.meta.accounts !== undefined &&
@@ -665,9 +665,10 @@ export function createLaneMessageHandlers(deps) {
665
665
  // lane (the dropbox mechanism) is refused inside prepareAccountMove.
666
666
  move = await prepareAccountMove(ctx, lane, {
667
667
  account: opts.account,
668
+ accountSource: "cli",
668
669
  cliModel: opts.model?.trim() || undefined,
669
670
  ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
670
- ...(refreshProfileSettings ? { refreshProfileSettings: true, accountSource: "cli" } : {}),
671
+ ...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
671
672
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
672
673
  ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
673
674
  pending: {
@@ -694,6 +695,7 @@ export function createLaneMessageHandlers(deps) {
694
695
  account: selection.account,
695
696
  cliModel: undefined,
696
697
  ...(automatic.historyReason !== undefined ? { accountHistoryReason: automatic.historyReason } : {}),
698
+ ...(automatic.preservedModel !== undefined ? { preservedModel: automatic.preservedModel } : {}),
697
699
  ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
698
700
  ...(refreshProfileSettings ? { refreshProfileSettings: true, accountSource: "balanced" } : {}),
699
701
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
@@ -746,8 +748,9 @@ export function createLaneMessageHandlers(deps) {
746
748
  if (resumeProfile !== undefined && !refreshProfileSettings && (opts.account === undefined || keepCurrentAccount)) {
747
749
  validateResumeProfileModel(ctx, lane, resumeProfile.profile);
748
750
  }
749
- if (move === null && (resumeProfile !== undefined || resumeEffort !== undefined || (keepCurrentAccount && opts.model !== undefined))) {
751
+ if (move === null && (resumeProfile !== undefined || resumeEffort !== undefined || keepCurrentAccount)) {
750
752
  lane = recordResumeSetup(ctx, lane, {
753
+ ...(keepCurrentAccount ? { accountSource: "cli" } : {}),
751
754
  ...(resumeProfile !== undefined ? { profile: resumeProfile } : {}),
752
755
  ...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
753
756
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
@@ -279,7 +279,7 @@ export function createLaneResumeHandlers(deps) {
279
279
  ...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
280
280
  })
281
281
  : undefined;
282
- const profileRefreshAccount = refreshProfileSettings && resumeProfile !== undefined &&
282
+ const profileRefreshAccount = opts.account === undefined && refreshProfileSettings && resumeProfile !== undefined &&
283
283
  lane.account_source !== undefined && lane.account_source !== "cli" &&
284
284
  resumeProfile.profile.meta.provider === lane.provider
285
285
  ? resumeProfile.profile.meta.account ?? (resumeProfile.profile.meta.accounts !== undefined &&
@@ -346,9 +346,10 @@ export function createLaneResumeHandlers(deps) {
346
346
  if (opts.account !== undefined && !keepCurrentAccount) {
347
347
  move = await prepareAccountMove(ctx, lane, {
348
348
  account: opts.account,
349
+ accountSource: "cli",
349
350
  cliModel: opts.model?.trim() || undefined,
350
351
  ...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
351
- ...(refreshProfileSettings ? { refreshProfileSettings: true, accountSource: "cli" } : {}),
352
+ ...(refreshProfileSettings ? { refreshProfileSettings: true } : {}),
352
353
  ...(profileRepin !== undefined ? { profileRepin } : {}),
353
354
  ...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
354
355
  ...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
@@ -432,6 +433,7 @@ export function createLaneResumeHandlers(deps) {
432
433
  ...(move === null && profileRepin !== undefined ? { profileRepin } : {}),
433
434
  ...(move === null && resumeEffort !== undefined ? { effort: resumeEffort } : {}),
434
435
  ...(keepAlive !== undefined ? { keepAlive } : {}),
436
+ ...(move === null && keepCurrentAccount ? { accountSource: "cli" } : {}),
435
437
  ...(move === null && keepCurrentAccount && opts.model !== undefined ? { cliModel: opts.model } : {}),
436
438
  ...(requestedMeshCompany !== undefined
437
439
  ? { meshCompany: requestedMeshCompany, threadId: meshThreadId }
@@ -6488,6 +6488,7 @@ function recordResumeSetup(ctx, lane, input) {
6488
6488
  input.effort === undefined &&
6489
6489
  input.keepAlive === undefined &&
6490
6490
  input.cliModel === undefined &&
6491
+ input.accountSource === undefined &&
6491
6492
  input.meshCompany === undefined &&
6492
6493
  input.clearWorkerProfileChangedQueue !== true &&
6493
6494
  input.refreshProfileSettings !== true &&
@@ -6516,7 +6517,11 @@ function recordResumeSetup(ctx, lane, input) {
6516
6517
  // recorded, so keep-alive may continue it again after this turn.
6517
6518
  if (next.keep_alive_blocked_reason === "terminal_state")
6518
6519
  next.keep_alive_blocked_reason = undefined;
6520
+ if (input.accountSource !== undefined)
6521
+ next.account_source = input.accountSource;
6519
6522
  applyResumeProfileAndEffort(current, next, { ...input, at: ctx.now().toISOString() });
6523
+ if (input.accountSource !== undefined)
6524
+ next.account_source = input.accountSource;
6520
6525
  if (input.clearWorkerProfileChangedQueue === true) {
6521
6526
  next.queued_reason = undefined;
6522
6527
  next.queued_details = undefined;
@@ -6628,19 +6633,14 @@ async function prepareAccountMove(ctx, lane, input) {
6628
6633
  }
6629
6634
  // The model the moved run would use, resolved the way spawn resolves it:
6630
6635
  // the operator's --model, else the lane's own, else the target account's
6631
- // default, else the provider's. The quota and validation checks use this
6632
- // effective model so a per-model hold on the target's default is seen
6633
- // here, before anything is copied. A target that pins a different model
6634
- // refuses the lane's model unless the operator names one for the move.
6636
+ // default, else the provider's. The target account model is a default, not
6637
+ // a restriction; quota and provider validation use this effective model.
6635
6638
  const model = resolveLaneModel({
6636
6639
  cliModel: input.cliModel ?? pinnedModel,
6637
6640
  profileModel,
6638
6641
  accountModel: targetCfg.model,
6639
6642
  providerModel: providerCfg.model,
6640
6643
  });
6641
- if (input.cliModel === undefined && lane.model !== undefined && targetCfg.model !== undefined && model !== targetCfg.model) {
6642
- throw new LanesError("model_unsupported", `Account ${target} pins model ${targetCfg.model}; lane ${laneId} resolves to ${model ?? lane.model}. Pass --model ${targetCfg.model} to move it onto that model`, { provider: lane.provider, account: target, model: model ?? lane.model, account_model: targetCfg.model, lane_id: laneId });
6643
- }
6644
6644
  const block = quotaBlockFor(loadQuotaState(hqRoot), lane.provider, target, model, now);
6645
6645
  if (block.blocked) {
6646
6646
  throw new LanesError("account_quota_held", `Account ${target} for ${lane.provider} is held${block.quota_reset_at ? ` until ${block.quota_reset_at}` : ""}${block.marker ? ` (${block.marker})` : ""}`, { provider: lane.provider, account: target, quota_reset_at: block.quota_reset_at ?? null, marker: block.marker ?? null, model: model ?? null });
@@ -6785,26 +6785,26 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6785
6785
  : lane.model_source === undefined || lane.model_source === "cli" || lane.model_source === "preserved"
6786
6786
  ? lane.model
6787
6787
  : undefined;
6788
- const recordedModelAccount = lane.account !== undefined &&
6789
- (lane.model_source === "cli" || lane.model_source === "preserved")
6790
- ? lane.account
6791
- : undefined;
6788
+ const profileModel = profileDefaultsApply ? profile?.meta.model : undefined;
6792
6789
  const quotaModel = resolveLaneModel({
6793
- cliModel: pinnedModel ?? (lane.model_source === "account" ? lane.model : undefined),
6794
- profileModel: profileDefaultsApply ? profile?.meta.model : undefined,
6795
- accountModel: lane.account === undefined ? undefined : config.providers.claude.accounts[lane.account]?.model,
6790
+ cliModel: pinnedModel,
6791
+ profileModel,
6792
+ accountModel: currentConfig?.model,
6796
6793
  providerModel: config.providers.claude.model,
6797
6794
  });
6798
6795
  const recordedAccountQuotaHeld = lane.account !== undefined &&
6799
6796
  quotaBlockFor(loadQuotaState(hqRoot), "claude", lane.account, quotaModel, ctx.now()).blocked;
6800
6797
  const accountNeedsRepair = recordedAccountUnavailable || recordedAccountQuotaHeld;
6801
6798
  const repairUnavailableAccount = options.repairUnavailableAccount === true && accountNeedsRepair;
6802
- const preservedModel = repairUnavailableAccount && lane.model_source === "account"
6799
+ const preservedModel = repairUnavailableAccount && recordedAccountUnavailable && lane.model_source === "account"
6803
6800
  ? lane.model ?? currentConfig?.model
6804
6801
  : undefined;
6802
+ // Repair keeps an account-derived model pinned through the move; plain
6803
+ // automatic rebalancing resolves each candidate's account default itself.
6804
+ const selectionModel = preservedModel ?? pinnedModel ?? profileModel;
6805
6805
  const model = resolveLaneModel({
6806
- cliModel: preservedModel ?? pinnedModel,
6807
- profileModel: profileDefaultsApply ? profile?.meta.model : undefined,
6806
+ cliModel: pinnedModel,
6807
+ profileModel,
6808
6808
  accountModel: lane.account === undefined ? undefined : config.providers.claude.accounts[lane.account]?.model,
6809
6809
  providerModel: config.providers.claude.model,
6810
6810
  });
@@ -6824,9 +6824,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6824
6824
  allowedAccounts?.includes(lane.account) &&
6825
6825
  currentConfig?.enabled === true &&
6826
6826
  currentTargetAccount?.enabled === true &&
6827
- currentTargetAccount.logged_in === true &&
6828
- (model === undefined || currentConfig.model === undefined || currentConfig.model === model ||
6829
- lane.account === recordedModelAccount))
6827
+ currentTargetAccount.logged_in === true)
6830
6828
  return undefined;
6831
6829
  const selectionInput = {
6832
6830
  hosts: balanceHosts.snapshots,
@@ -6835,8 +6833,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6835
6833
  quotaState: loadQuotaState(hqRoot),
6836
6834
  recentUsageEnabled: await readClaudeAccountBalanceSignalsEnabled(ctx, lane.company ?? undefined),
6837
6835
  now: ctx.now(),
6838
- ...(model !== undefined ? { model } : {}),
6839
- ...(recordedModelAccount !== undefined ? { recordedModelAccount } : {}),
6836
+ ...(selectionModel !== undefined ? { model: selectionModel } : {}),
6840
6837
  unavailableHosts: balanceHosts.unavailableHosts,
6841
6838
  };
6842
6839
  let selected;
@@ -6853,7 +6850,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
6853
6850
  Array.isArray(err.details.account_reasons)
6854
6851
  ? err.details.account_reasons.filter((reason) => reason !== null && typeof reason === "object" && !Array.isArray(reason))
6855
6852
  : [];
6856
- const unavailablePinReasons = new Set(["disabled", "home_missing", "not_logged_in", "model_mismatch", "quota_held"]);
6853
+ const unavailablePinReasons = new Set(["disabled", "home_missing", "not_logged_in", "quota_held"]);
6857
6854
  const everyProfileAccountUnavailable = profilePin !== undefined && profilePin.length > 0 &&
6858
6855
  profilePin.every((account) => accountReasons.some((reason) => reason.account === account && typeof reason.reason === "string" && unavailablePinReasons.has(reason.reason)));
6859
6856
  if (!everyProfileAccountUnavailable || lane.account === undefined ||
@@ -76,8 +76,6 @@ export declare function selectClaudeAccount(input: {
76
76
  config: LanesConfig;
77
77
  allowedAccounts?: readonly string[];
78
78
  model?: string;
79
- /** Keep the recorded account eligible for its already-running CLI model. */
80
- recordedModelAccount?: string;
81
79
  quotaState?: QuotaState;
82
80
  /** Recent completed-run tiebreak; callers pass the default-off hq-flags result. */
83
81
  recentUsageEnabled?: boolean;
@@ -131,12 +131,7 @@ function buildLoads(input) {
131
131
  const quotaBlock = input.quotaState === undefined
132
132
  ? undefined
133
133
  : quotaBlockFor(input.quotaState, "claude", account, quotaModel, input.now);
134
- const ineligible = targetIneligibilityReason(account, configAccounts[account]?.enabled === true, input.allowedAccounts, targetLoad) ?? (account !== input.recordedModelAccount &&
135
- input.model !== undefined && configuredModel !== undefined && configuredModel !== input.model
136
- ? "model_mismatch"
137
- : quotaBlock?.blocked === true
138
- ? "quota_held"
139
- : undefined);
134
+ const ineligible = targetIneligibilityReason(account, configAccounts[account]?.enabled === true, input.allowedAccounts, targetLoad) ?? (quotaBlock?.blocked === true ? "quota_held" : undefined);
140
135
  if (ineligible !== undefined)
141
136
  skipped.push({ account, host: targetHost, reason: ineligible });
142
137
  const knownCounts = hosts.flatMap((host) => host.live_lanes === null ? [] : [host.live_lanes]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.15",
3
+ "version": "5.345.16",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {