@indigoai-us/hq-cli 5.227.0 → 5.228.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.
- package/CHANGELOG.md +13 -0
- package/dist/command-catalog.generated.d.ts +1 -1
- package/dist/command-catalog.generated.js +1 -1
- package/dist/commands/agents.js +2 -1
- package/dist/commands/files-browse.js +7 -4
- package/dist/commands/files.js +13 -8
- package/dist/commands/groups.js +6 -5
- package/dist/commands/lanes.d.ts +1 -6
- package/dist/commands/lanes.js +33 -219
- package/dist/lib/lanes/{claude-account-balance.d.ts → claude-balance.d.ts} +1 -3
- package/dist/lib/lanes/{claude-account-balance.js → claude-balance.js} +1 -7
- package/dist/lib/lanes/{hire-grants.d.ts → grants.d.ts} +1 -2
- package/dist/lib/lanes/{hire-grants.js → grants.js} +1 -2
- package/dist/lib/lanes/hire-grant-dispatch.d.ts +1 -1
- package/dist/lib/lanes/hire-grant-dispatch.js +15 -17
- package/dist/lib/lanes/index.d.ts +2 -2
- package/dist/lib/lanes/index.js +1 -1
- package/dist/lib/lanes/reconcile.js +10 -24
- package/dist/lib/lanes/spawn.js +1 -1
- package/dist/utils/table-width.d.ts +7 -0
- package/dist/utils/table-width.js +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.228.0] — 2026-09-27
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Lane hire grants and Claude account balancing are always active.
|
|
10
|
+
`backfill-grants --apply` refuses to write; dry-run output remains available.
|
|
11
|
+
|
|
12
|
+
## [5.227.1] — 2026-09-27
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- `hq files browse` and `hq files search` no longer crash on vaults with many objects.
|
|
17
|
+
|
|
5
18
|
## [5.227.0] — 2026-09-27
|
|
6
19
|
|
|
7
20
|
### Added
|
|
@@ -8285,7 +8285,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
8285
8285
|
readonly description: "claude";
|
|
8286
8286
|
}, {
|
|
8287
8287
|
readonly flags: "--apply";
|
|
8288
|
-
readonly description: "Move eligible between-turn sessions
|
|
8288
|
+
readonly description: "Move eligible between-turn sessions to balance Claude accounts";
|
|
8289
8289
|
}, {
|
|
8290
8290
|
readonly flags: "--json";
|
|
8291
8291
|
readonly description: "JSON on stdout (always)";
|
|
@@ -10717,7 +10717,7 @@ export const COMMAND_CATALOG = [
|
|
|
10717
10717
|
},
|
|
10718
10718
|
{
|
|
10719
10719
|
"flags": "--apply",
|
|
10720
|
-
"description": "Move eligible between-turn sessions
|
|
10720
|
+
"description": "Move eligible between-turn sessions to balance Claude accounts"
|
|
10721
10721
|
},
|
|
10722
10722
|
{
|
|
10723
10723
|
"flags": "--json",
|
package/dist/commands/agents.js
CHANGED
|
@@ -30,6 +30,7 @@ import chalk from "chalk";
|
|
|
30
30
|
import { randomUUID } from "node:crypto";
|
|
31
31
|
import * as readline from "node:readline";
|
|
32
32
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
33
|
+
import { maxLength } from "../utils/table-width.js";
|
|
33
34
|
import { DEFAULT_VAULT_API_URL } from "../utils/cognito-session.js";
|
|
34
35
|
import { gateApiKeyCapabilities } from "../utils/api-key-command-gate.js";
|
|
35
36
|
import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
|
|
@@ -849,7 +850,7 @@ export function formatJobsTable(jobs) {
|
|
|
849
850
|
j.lastRunOutcome ?? "-",
|
|
850
851
|
j.status ?? "",
|
|
851
852
|
]);
|
|
852
|
-
const widths = cols.map((c, i) =>
|
|
853
|
+
const widths = cols.map((c, i) => maxLength(rows, (row) => row[i].length, c.length));
|
|
853
854
|
const render = (cells) => cells.map((cell, i) => cell.padEnd(widths[i])).join(" ");
|
|
854
855
|
return [chalk.bold(render(cols)), ...rows.map(render)].join("\n");
|
|
855
856
|
}
|
|
@@ -44,6 +44,7 @@ import { VaultClient, } from "@indigoai-us/hq-cloud";
|
|
|
44
44
|
import { isPrivateFolderPattern } from "./_patterns.js";
|
|
45
45
|
import { DEFAULT_HQ_ROOT, DEFAULT_COGNITO, buildVaultConfig, } from "../utils/cognito-session.js";
|
|
46
46
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
47
|
+
import { maxLength } from "../utils/table-width.js";
|
|
47
48
|
import { getCompanyUid, looksLikeCompanyUid, looksLikePersonVaultUid, vaultApiFetch, } from "../utils/vault-api.js";
|
|
48
49
|
import { resolveCanonicalPersonUid } from "./cloud.js";
|
|
49
50
|
// ── Pure helpers ────────────────────────────────────────────────────────────
|
|
@@ -181,13 +182,14 @@ export function formatBrowseTable(rows) {
|
|
|
181
182
|
r.lastModified ? r.lastModified.toISOString() : "—",
|
|
182
183
|
displayAclSource(r.aclSource),
|
|
183
184
|
]);
|
|
184
|
-
const widths = cols.map((c, i) =>
|
|
185
|
+
const widths = cols.map((c, i) => maxLength(data, (row) => row[i].length, c.length));
|
|
185
186
|
const renderRow = (row) => row.map((cell, i) => cell.padEnd(widths[i])).join(" ");
|
|
186
187
|
const lines = [
|
|
187
188
|
chalk.bold(renderRow(cols)),
|
|
188
189
|
chalk.dim(renderRow(widths.map((w) => "─".repeat(w)))),
|
|
189
|
-
...data.map(renderRow),
|
|
190
190
|
];
|
|
191
|
+
for (const row of data)
|
|
192
|
+
lines.push(renderRow(row));
|
|
191
193
|
return lines.join("\n");
|
|
192
194
|
}
|
|
193
195
|
/** Guard: the personal path needs an S3 factory. */
|
|
@@ -494,13 +496,14 @@ export function formatSharedWithMeTable(rows) {
|
|
|
494
496
|
r.permission,
|
|
495
497
|
r.source,
|
|
496
498
|
]);
|
|
497
|
-
const widths = cols.map((c, i) =>
|
|
499
|
+
const widths = cols.map((c, i) => maxLength(data, (row) => row[i].length, c.length));
|
|
498
500
|
const renderRow = (row) => row.map((cell, i) => cell.padEnd(widths[i])).join(" ");
|
|
499
501
|
const lines = [
|
|
500
502
|
chalk.bold(renderRow(cols)),
|
|
501
503
|
chalk.dim(renderRow(widths.map((w) => "─".repeat(w)))),
|
|
502
|
-
...data.map(renderRow),
|
|
503
504
|
];
|
|
505
|
+
for (const row of data)
|
|
506
|
+
lines.push(renderRow(row));
|
|
504
507
|
if (rows.some((row) => isPrivateFolderPattern(row.path))) {
|
|
505
508
|
lines.push("Private folders expose the folder only; children are creator-only.");
|
|
506
509
|
}
|
package/dist/commands/files.js
CHANGED
|
@@ -11,6 +11,7 @@ import { formatAccessDenied, offerAccessLadder, } from "../utils/access-denied-h
|
|
|
11
11
|
import { fetchChannels, matchChannelsByName } from "./dm.js";
|
|
12
12
|
import { resolveCompanySlug, resolvePersonWithRosterFallback, } from "./people.js";
|
|
13
13
|
import { findHqRoot } from "../utils/manifest.js";
|
|
14
|
+
import { maxLength } from "../utils/table-width.js";
|
|
14
15
|
const PERSON_UID_PATTERN = /^(prs|agt)_[A-Za-z0-9_-]+$/;
|
|
15
16
|
function collectMentions(value, previous) {
|
|
16
17
|
return [...previous, value];
|
|
@@ -1227,13 +1228,15 @@ export function formatFilesVersionsTable(rows) {
|
|
|
1227
1228
|
row.lastModified,
|
|
1228
1229
|
row.isDeleteMarker ? "—" : formatBytes(row.size),
|
|
1229
1230
|
]);
|
|
1230
|
-
const widths = cols.map((col, index) =>
|
|
1231
|
+
const widths = cols.map((col, index) => maxLength(data, (row) => row[index].length, col.length));
|
|
1231
1232
|
const renderRow = (row) => row.map((cell, index) => cell.padEnd(widths[index])).join(" ");
|
|
1232
|
-
|
|
1233
|
+
const lines = [
|
|
1233
1234
|
chalk.bold(renderRow(cols)),
|
|
1234
1235
|
chalk.dim(renderRow(widths.map((width) => "─".repeat(width)))),
|
|
1235
|
-
|
|
1236
|
-
|
|
1236
|
+
];
|
|
1237
|
+
for (const row of data)
|
|
1238
|
+
lines.push(renderRow(row));
|
|
1239
|
+
return lines.join("\n");
|
|
1237
1240
|
}
|
|
1238
1241
|
/** Render durable delete tombstones without implying the objects still exist. */
|
|
1239
1242
|
export function formatFilesTrashTable(rows) {
|
|
@@ -1247,13 +1250,15 @@ export function formatFilesTrashTable(rows) {
|
|
|
1247
1250
|
row.deletedBy,
|
|
1248
1251
|
row.deletedPrefix,
|
|
1249
1252
|
]);
|
|
1250
|
-
const widths = cols.map((col, index) =>
|
|
1253
|
+
const widths = cols.map((col, index) => maxLength(data, (row) => row[index].length, col.length));
|
|
1251
1254
|
const renderRow = (row) => row.map((cell, index) => cell.padEnd(widths[index])).join(" ");
|
|
1252
|
-
|
|
1255
|
+
const lines = [
|
|
1253
1256
|
chalk.bold(renderRow(cols)),
|
|
1254
1257
|
chalk.dim(renderRow(widths.map((width) => "─".repeat(width)))),
|
|
1255
|
-
|
|
1256
|
-
|
|
1258
|
+
];
|
|
1259
|
+
for (const row of data)
|
|
1260
|
+
lines.push(renderRow(row));
|
|
1261
|
+
return lines.join("\n");
|
|
1257
1262
|
}
|
|
1258
1263
|
function throwRecoveryHttpError(status, body, statusText) {
|
|
1259
1264
|
throw new FilesRecoveryHttpError(status, body.message ?? body.error ?? statusText, body.code);
|
package/dist/commands/groups.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
3
3
|
import { vaultApiFetch, getCompanyUid } from "./secrets.js";
|
|
4
4
|
import { GROUP_ID_PATTERN } from "./_patterns.js";
|
|
5
|
+
import { maxLength } from "../utils/table-width.js";
|
|
5
6
|
const EMAIL_PATTERN = /^[^\s]+@[^\s]+$/;
|
|
6
7
|
const PERSON_UID_PATTERN = /^prs_[A-Za-z0-9_-]+$/;
|
|
7
8
|
const AGENT_UID_PATTERN = /^agt_[A-Za-z0-9_-]+$/;
|
|
@@ -261,9 +262,9 @@ export function registerGroupsCommand(program) {
|
|
|
261
262
|
console.log(chalk.gray("No groups in this company yet."));
|
|
262
263
|
return;
|
|
263
264
|
}
|
|
264
|
-
const idW =
|
|
265
|
-
const nameW =
|
|
266
|
-
const descW =
|
|
265
|
+
const idW = maxLength(data.groups, (group) => group.groupId.length, 8);
|
|
266
|
+
const nameW = maxLength(data.groups, (group) => group.name.length, 4);
|
|
267
|
+
const descW = maxLength(data.groups, (group) => (group.description ?? "").length, 11);
|
|
267
268
|
console.log(chalk.bold([
|
|
268
269
|
"GROUP_ID".padEnd(idW),
|
|
269
270
|
"NAME".padEnd(nameW),
|
|
@@ -326,8 +327,8 @@ export function registerGroupsCommand(program) {
|
|
|
326
327
|
console.log(chalk.gray("No members yet — use 'hq groups add' to add."));
|
|
327
328
|
return;
|
|
328
329
|
}
|
|
329
|
-
const uidW =
|
|
330
|
-
const byW =
|
|
330
|
+
const uidW = maxLength(data.members, (member) => member.personUid.length, 10);
|
|
331
|
+
const byW = maxLength(data.members, (member) => member.addedBy.length, 8);
|
|
331
332
|
console.log(chalk.bold([
|
|
332
333
|
"PERSON_UID".padEnd(uidW),
|
|
333
334
|
"ADDED_BY".padEnd(byW),
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -10,10 +10,9 @@ import { type TrackerMeshRequest } from "../lib/lanes/tracker-mesh.js";
|
|
|
10
10
|
import type { OutpostIdentity } from "../lib/lanes/admission-host.js";
|
|
11
11
|
export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
|
|
12
12
|
export type { OutpostIdentity } from "../lib/lanes/admission-host.js";
|
|
13
|
-
export { LANE_HIRE_GRANTS_FLAG };
|
|
14
13
|
import { type OutpostDispatchResult } from "../lib/lanes/outpost-dispatch.js";
|
|
15
14
|
import { emitLaneDecisionEvents, type LaneMesh } from "../lib/lanes/mesh-events.js";
|
|
16
|
-
import {
|
|
15
|
+
import { MAX_LANE_DEPTH, withLaneHireLocks, type LaneCompanyRef, type QuestionBody, type LaneRecord, type QuestionRecord, type ResolvedLaneWorker, type SeniorRef, type LivenessDeps, type MachineIdentity, type MonitorEvidenceReader, type RunCommand, type ThrottleDeps } from "../lib/lanes/index.js";
|
|
17
16
|
import { questionBodyAbsPath } from "../lib/lanes/questions.js";
|
|
18
17
|
import { type RunCompactionFn } from "../lib/lanes/compaction.js";
|
|
19
18
|
import type { WatcherLoopDeps } from "../lib/lanes/watcher.js";
|
|
@@ -87,16 +86,12 @@ export interface LanesCommandDeps {
|
|
|
87
86
|
monitorEvidence?: MonitorEvidenceReader;
|
|
88
87
|
/** Test seam for the default-off outpost placement rollout flag. */
|
|
89
88
|
lanePlacementEnabled?: () => boolean;
|
|
90
|
-
/** Test seam for the default-off lane hire-grants rollout gate. */
|
|
91
|
-
laneHireGrantsEnabled?: (company?: LaneCompanyRef | null) => boolean | Promise<boolean>;
|
|
92
89
|
/** Test seam for the default-off story ledger flag. */
|
|
93
90
|
storyLedgerEnabled?: () => boolean;
|
|
94
91
|
/** Test seam for the SSM-only Outpost owner grant path. */
|
|
95
92
|
isSsmOwnerDispatch?: () => boolean;
|
|
96
93
|
/** Test seam for changing a lane between pre-lock and locked grant reads. */
|
|
97
94
|
withGrantHireLocks?: typeof withLaneHireLocks;
|
|
98
|
-
/** Test seam for the Claude account-balancing gate. */
|
|
99
|
-
laneClaudeAccountBalanceEnabled?: (company?: LaneCompanyRef | null) => boolean | Promise<boolean>;
|
|
100
95
|
/** Test seam for the default-off Outpost question visibility gate. */
|
|
101
96
|
laneOutpostQuestionVisibilityEnabled?: (company?: LaneCompanyRef | null) => boolean | Promise<boolean>;
|
|
102
97
|
/** Test seam for the hq-flags gated tracker commands. */
|
package/dist/commands/lanes.js
CHANGED
|
@@ -27,11 +27,10 @@ import { defaultTrackerActor, LANES_TRACKER_FLAG, LANES_TRACKER_MESH_FLAG, regis
|
|
|
27
27
|
import { createOutpostHireGrantContext, parseOutpostHireGrantContext, sameHireGrantSenior, } from "../lib/lanes/hire-grant-dispatch.js";
|
|
28
28
|
import { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity, resolveAdmissionHost, } from "../lib/lanes/admission-host.js";
|
|
29
29
|
export { OUTPOST_DIR, OUTPOST_ID_PATH, readOutpostIdentity } from "../lib/lanes/admission-host.js";
|
|
30
|
-
export { LANE_HIRE_GRANTS_FLAG };
|
|
31
30
|
import { buildOutpostCapacityCommand, buildOutpostBalanceMoveCommand, buildOutpostIdentityBackfillCommand, buildOutpostLaneCommand, buildOutpostMoveTargetCommand, buildOutpostQuestionAnswerCommand, buildOutpostQuestionListCommand, buildOutpostQuestionShowCommand, buildOutpostPickupCommand, createOutpostsClient, dispatchErrorMessage, dispatchOutpostPickup, buildOutpostLaneListCommand, outpostRejectedEffortOption, parseOutpostPickupPayload, stageOutpostPayload, } from "../lib/lanes/outpost-dispatch.js";
|
|
32
31
|
import { outpostHireSummaryCacheKey, readOutpostHireSummaryCache, writeOutpostHireSummaryCache, } from "../lib/lanes/outpost-hire-cache.js";
|
|
33
32
|
import { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "../lib/lanes/host-capacity.js";
|
|
34
|
-
import { BALANCE_MOVABLE_STATES,
|
|
33
|
+
import { BALANCE_MOVABLE_STATES, accountLoadsForReport, planClaudeBalanceMoves, selectClaudeAccount, } from "../lib/lanes/claude-balance.js";
|
|
35
34
|
import { parseUserYaml } from "../utils/user-yaml-error.js";
|
|
36
35
|
import { backfillOutpostIdentityMarker as backfillOutpostIdentityMarkerImpl, createOutpostIdentityMarkerIfMissing, validateOutpostIdentityId, } from "../lib/lanes/outpost-identity.js";
|
|
37
36
|
import { vaultApiFetch } from "../utils/vault-api.js";
|
|
@@ -42,7 +41,7 @@ import { assertNoSecretText } from "../lib/lanes/text-scan.js";
|
|
|
42
41
|
import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshEventIdentity, } from "../lib/lanes/mesh-events.js";
|
|
43
42
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
44
43
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
45
|
-
import { AccountResolutionError, compareLaneQueueOrder, coveredLaneIds, DEFAULT_ACCOUNT_HOMES, LANE_PROVIDERS, LANE_STATES, PROVIDER_HOME_ENV, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, createQuestion, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installDrainHook, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError,
|
|
44
|
+
import { AccountResolutionError, compareLaneQueueOrder, coveredLaneIds, DEFAULT_ACCOUNT_HOMES, LANE_PROVIDERS, LANE_STATES, PROVIDER_HOME_ENV, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, createQuestion, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installDrainHook, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listAccountStatuses, listLaneIds, listQuestionIds, listSessionLiveness, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, MONITOR_ENGINES, MONITOR_MAX_TIMEOUT_MS, MONITOR_UNCOVERED_EXIT_CODE, MAX_WATCHED_LANES, monitorReminderText, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseLivenessState, parseQuestionRecord, parseQuestionStatus, parseSeniorKind, readDaemonPresenceConnected, readLane, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, runReconcilePass, wakeAnsweredLaneNow, runLaneWatch, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, writeLanesConfig, appendDropboxMessage, answerDropboxMessage, deliveryRefusalReason, resumeCommandRefusal, dropboxDir, dropboxInboxPath, laneAcceptsDropboxDelivery, laneAcceptsOperatorMessage, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, withLaneHireLocks, laneIdForAnswerDelivery, operatorMessageRefusalReason, raiseLaneQuestion, sha256Text, writeQuestionBody, } from "../lib/lanes/index.js";
|
|
46
45
|
import { activeLaneStories, laneStories, laneStory, storyWarning, } from "../lib/lanes/stories.js";
|
|
47
46
|
import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
|
|
48
47
|
import { keepAliveExclusion, LEAD_LANE_KEEP_ALIVE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
|
|
@@ -77,20 +76,12 @@ export const LANE_HOST_SCOPED_ACCOUNT_HOMES_FLAG = "lanes.host-scoped-account-ho
|
|
|
77
76
|
function defaultLanePlacementEnabled() {
|
|
78
77
|
return resolveProcessFlagGate(LANE_OUTPOST_LOAD_BALANCING_FLAG, { fallback: false }, () => false);
|
|
79
78
|
}
|
|
80
|
-
function defaultLaneClaudeAccountBalanceEnabled(company) {
|
|
81
|
-
// Keep the local HQ_FLAG override and the registry reader's fail-closed
|
|
82
|
-
// behavior, matching the lane hire-grants gate.
|
|
83
|
-
return readLanesRegistryFlag(CLAUDE_ACCOUNT_BALANCE_FLAG, { company });
|
|
84
|
-
}
|
|
85
79
|
function defaultLaneTrackerEnabled(company) {
|
|
86
80
|
return readLanesRegistryFlag(LANES_TRACKER_FLAG, { company });
|
|
87
81
|
}
|
|
88
82
|
function defaultLaneTrackerMeshEnabled(company) {
|
|
89
83
|
return readLanesRegistryFlag(LANES_TRACKER_MESH_FLAG, { company });
|
|
90
84
|
}
|
|
91
|
-
function defaultLaneHireGrantsEnabled(company) {
|
|
92
|
-
return readLanesRegistryFlag(LANE_HIRE_GRANTS_FLAG, { company });
|
|
93
|
-
}
|
|
94
85
|
function defaultLaneOutpostQuestionVisibilityEnabled(company, env) {
|
|
95
86
|
return readLanesRegistryFlag(LANE_OUTPOST_QUESTION_VISIBILITY_FLAG, {
|
|
96
87
|
company,
|
|
@@ -320,11 +311,9 @@ function resolveDeps(deps = {}) {
|
|
|
320
311
|
})),
|
|
321
312
|
fetchText: deps.fetchText ?? defaultFetchText,
|
|
322
313
|
lanePlacementEnabled: deps.lanePlacementEnabled ?? defaultLanePlacementEnabled,
|
|
323
|
-
laneHireGrantsEnabled: deps.laneHireGrantsEnabled ?? defaultLaneHireGrantsEnabled,
|
|
324
314
|
storyLedgerEnabled: deps.storyLedgerEnabled ?? laneStoryLedgerEnabled,
|
|
325
315
|
isSsmOwnerDispatch: deps.isSsmOwnerDispatch ?? (() => isSsmRunCommandProcess()),
|
|
326
316
|
withGrantHireLocks: deps.withGrantHireLocks ?? withLaneHireLocks,
|
|
327
|
-
laneClaudeAccountBalanceEnabled: deps.laneClaudeAccountBalanceEnabled ?? defaultLaneClaudeAccountBalanceEnabled,
|
|
328
317
|
laneOutpostQuestionVisibilityEnabled: deps.laneOutpostQuestionVisibilityEnabled ??
|
|
329
318
|
((company) => defaultLaneOutpostQuestionVisibilityEnabled(company, env)),
|
|
330
319
|
laneTrackerEnabled: deps.laneTrackerEnabled ?? defaultLaneTrackerEnabled,
|
|
@@ -1022,9 +1011,6 @@ async function laneHirePresentation(ctx, lane, lanes) {
|
|
|
1022
1011
|
laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
|
|
1023
1012
|
return { ...local, hire_source: "local" };
|
|
1024
1013
|
}
|
|
1025
|
-
if (!await ctx.laneHireGrantsEnabled(lane.company)) {
|
|
1026
|
-
return { ...local, hire_source: "local" };
|
|
1027
|
-
}
|
|
1028
1014
|
try {
|
|
1029
1015
|
return { ...(await ownerLaneHireFields(ctx, lane)), hire_source: "owner" };
|
|
1030
1016
|
}
|
|
@@ -1059,8 +1045,6 @@ async function withResumeHireAdmission(ctx, lane, fn) {
|
|
|
1059
1045
|
!lane.senior.id) {
|
|
1060
1046
|
return fn();
|
|
1061
1047
|
}
|
|
1062
|
-
if (!await ctx.laneHireGrantsEnabled(lane.company))
|
|
1063
|
-
return fn();
|
|
1064
1048
|
return withHireGrantAdmission(hqRoot, lane.senior.id, (snapshot) => {
|
|
1065
1049
|
const current = readLane(hqRoot, lane.lane_id);
|
|
1066
1050
|
// A different resume already reactivated this lane; it occupies the slot
|
|
@@ -2664,18 +2648,14 @@ async function runCreate(ctx, opts) {
|
|
|
2664
2648
|
}
|
|
2665
2649
|
const senior = opts.senior ? parseSeniorRef(opts.senior) : defaultSenior(ctx);
|
|
2666
2650
|
assertSeniorExists(hqRoot, senior, ctx);
|
|
2667
|
-
// Resolve tenant context before the registry read so the flag evaluator
|
|
2668
|
-
// applies the same company scope as this lane.
|
|
2669
2651
|
const company = companyFlag
|
|
2670
2652
|
? await ctx.resolveCompany(companyFlag)
|
|
2671
2653
|
: null;
|
|
2672
|
-
const hireGrantsEnabled = await ctx.laneHireGrantsEnabled(company);
|
|
2673
2654
|
if (requestedHireGrant !== undefined &&
|
|
2674
2655
|
!callerMatchesSenior(ctx, senior)) {
|
|
2675
2656
|
throw new LanesError("hire_grant_unauthorized", `Only senior ${seniorKey(senior)} may set a lane's hire grant`, { senior, hire_grant: requestedHireGrant });
|
|
2676
2657
|
}
|
|
2677
|
-
if (
|
|
2678
|
-
ctx.env.HQ_LANE_ID?.trim() &&
|
|
2658
|
+
if (ctx.env.HQ_LANE_ID?.trim() &&
|
|
2679
2659
|
!callerMatchesSenior(ctx, senior)) {
|
|
2680
2660
|
throw new LanesError("hire_grant_unauthorized", `Only senior lane ${senior.id} may create a direct child lane`, { senior_lane_id: senior.id });
|
|
2681
2661
|
}
|
|
@@ -3031,7 +3011,7 @@ async function runCreate(ctx, opts) {
|
|
|
3031
3011
|
}
|
|
3032
3012
|
const allowedAccounts = validatedAccounts.size > 0 ? [...validatedAccounts] : profileAccounts;
|
|
3033
3013
|
let accountSelection;
|
|
3034
|
-
if (provider === "claude"
|
|
3014
|
+
if (provider === "claude") {
|
|
3035
3015
|
const targetHost = placementHostOverride ?? (outpostId !== undefined
|
|
3036
3016
|
? { kind: "outpost", id: outpostId }
|
|
3037
3017
|
: laneHostFromCtx(ctx));
|
|
@@ -3223,13 +3203,13 @@ async function runCreate(ctx, opts) {
|
|
|
3223
3203
|
};
|
|
3224
3204
|
const lane = senior.kind === "lane"
|
|
3225
3205
|
? withHireGrantAdmission(hqRoot, senior.id, ({ parent, children, parentGrant, availableChildGrant }) => {
|
|
3226
|
-
if (
|
|
3206
|
+
if (isTerminalLaneState(parent.state)) {
|
|
3227
3207
|
throw new LanesError("hire_grant_senior_inactive", `Senior lane ${parent.lane_id} is ${parent.state} and cannot hire lanes`, { senior_lane_id: parent.lane_id, state: parent.state });
|
|
3228
3208
|
}
|
|
3229
|
-
if (
|
|
3209
|
+
if (children.length >= parentGrant) {
|
|
3230
3210
|
throw hireGrantCapacityError(parent, children);
|
|
3231
3211
|
}
|
|
3232
|
-
if (
|
|
3212
|
+
if (hireGrant > availableChildGrant) {
|
|
3233
3213
|
throw new LanesError("hire_grant_carve_exceeded", `Lane ${parent.lane_id} has ${availableChildGrant} unassigned hire grant remaining after its other alive children. Ask its senior to raise its grant before assigning ${hireGrant}.`, {
|
|
3234
3214
|
senior_lane_id: parent.lane_id,
|
|
3235
3215
|
parent_grant: parentGrant,
|
|
@@ -3622,7 +3602,6 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
|
|
|
3622
3602
|
let grantContext;
|
|
3623
3603
|
let grantHierarchy;
|
|
3624
3604
|
if (action === "grant") {
|
|
3625
|
-
const enforcementEnabled = await ctx.laneHireGrantsEnabled(lane.company);
|
|
3626
3605
|
let hierarchy = loadAllLanes(ctx.hqRoot());
|
|
3627
3606
|
const ownerLanes = await readOutpostLaneRecords(ctx, outpostId);
|
|
3628
3607
|
const byId = new Map(hierarchy.map((record) => [record.lane_id, record]));
|
|
@@ -3631,7 +3610,7 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options) {
|
|
|
3631
3610
|
}
|
|
3632
3611
|
hierarchy = [...byId.values()];
|
|
3633
3612
|
grantHierarchy = hierarchy;
|
|
3634
|
-
grantContext = createOutpostHireGrantContext(lane, hierarchy
|
|
3613
|
+
grantContext = createOutpostHireGrantContext(lane, hierarchy);
|
|
3635
3614
|
}
|
|
3636
3615
|
let dispatched;
|
|
3637
3616
|
try {
|
|
@@ -4177,25 +4156,15 @@ async function runList(ctx, opts) {
|
|
|
4177
4156
|
})
|
|
4178
4157
|
.sort(compareLaneQueueOrder);
|
|
4179
4158
|
const ownerEligibleLaneIds = new Set();
|
|
4180
|
-
const companyContexts = new Map();
|
|
4181
4159
|
for (const lane of selectedLanes) {
|
|
4182
4160
|
if (lane.host.kind !== "outpost" ||
|
|
4183
4161
|
isTerminalLaneState(lane.state) ||
|
|
4184
4162
|
laneRunsHere(lane, host)) {
|
|
4185
4163
|
continue;
|
|
4186
4164
|
}
|
|
4187
|
-
const companyKey = JSON.stringify([
|
|
4188
|
-
lane.company?.uid ?? null,
|
|
4189
|
-
lane.company?.slug ?? null,
|
|
4190
|
-
]);
|
|
4191
4165
|
ownerEligibleLaneIds.add(lane.lane_id);
|
|
4192
|
-
companyContexts.set(companyKey, lane.company);
|
|
4193
4166
|
}
|
|
4194
|
-
const
|
|
4195
|
-
companyKey,
|
|
4196
|
-
await ctx.laneHireGrantsEnabled(company),
|
|
4197
|
-
])));
|
|
4198
|
-
const ownerLanes = [];
|
|
4167
|
+
const ownerLanes = selectedLanes.filter((lane) => ownerEligibleLaneIds.has(lane.lane_id));
|
|
4199
4168
|
const hireByLane = new Map();
|
|
4200
4169
|
for (const lane of selectedLanes) {
|
|
4201
4170
|
if (!ownerEligibleLaneIds.has(lane.lane_id)) {
|
|
@@ -4205,18 +4174,6 @@ async function runList(ctx, opts) {
|
|
|
4205
4174
|
});
|
|
4206
4175
|
continue;
|
|
4207
4176
|
}
|
|
4208
|
-
const companyKey = JSON.stringify([
|
|
4209
|
-
lane.company?.uid ?? null,
|
|
4210
|
-
lane.company?.slug ?? null,
|
|
4211
|
-
]);
|
|
4212
|
-
if (enabledByCompany.get(companyKey))
|
|
4213
|
-
ownerLanes.push(lane);
|
|
4214
|
-
else {
|
|
4215
|
-
hireByLane.set(lane.lane_id, {
|
|
4216
|
-
...laneHireFields(lane, allLanes),
|
|
4217
|
-
hire_source: "local",
|
|
4218
|
-
});
|
|
4219
|
-
}
|
|
4220
4177
|
}
|
|
4221
4178
|
if (ownerLanes.length > 0) {
|
|
4222
4179
|
const ownerSummaries = await batchOwnerLaneHireFields(ctx, ownerLanes);
|
|
@@ -4852,8 +4809,6 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
|
|
|
4852
4809
|
(!grantContext || !sameHireGrantSenior(initial.senior, grantContext.expected_senior))) {
|
|
4853
4810
|
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 });
|
|
4854
4811
|
}
|
|
4855
|
-
const enforce = grantContext?.enforcement_enabled ??
|
|
4856
|
-
await ctx.laneHireGrantsEnabled(initial.company);
|
|
4857
4812
|
const lockedLaneIds = [
|
|
4858
4813
|
laneId,
|
|
4859
4814
|
...(initial.senior.kind === "lane" && initial.senior.id
|
|
@@ -4878,7 +4833,7 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
|
|
|
4878
4833
|
? mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot))
|
|
4879
4834
|
: grantContext.hierarchy
|
|
4880
4835
|
: loadAllLanes(hqRoot);
|
|
4881
|
-
if (
|
|
4836
|
+
if (current.senior.kind === "lane") {
|
|
4882
4837
|
if (!current.senior.id) {
|
|
4883
4838
|
throw new LanesError("hire_grant_senior_invalid", `Lane ${laneId} has a lane senior without an id`, {
|
|
4884
4839
|
lane_id: laneId,
|
|
@@ -4908,19 +4863,17 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
|
|
|
4908
4863
|
});
|
|
4909
4864
|
}
|
|
4910
4865
|
}
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
});
|
|
4923
|
-
}
|
|
4866
|
+
const children = aliveDirectChildren(hierarchy, current.lane_id);
|
|
4867
|
+
const allocated = assignedChildHireGrants(children);
|
|
4868
|
+
const minimumGrant = Math.max(allocated, children.length);
|
|
4869
|
+
if (hireGrant < minimumGrant) {
|
|
4870
|
+
throw new LanesError(allocated > children.length ? "hire_grant_below_allocated" : "hire_grant_below_occupancy", `Lane ${laneId} cannot lower its grant to ${hireGrant}; it has ${children.length} alive children and ${allocated} grant allocated to them`, {
|
|
4871
|
+
lane_id: laneId,
|
|
4872
|
+
requested_hire_grant: hireGrant,
|
|
4873
|
+
alive_children: children.map((child) => child.lane_id),
|
|
4874
|
+
alive_child_count: children.length,
|
|
4875
|
+
assigned_child_grants: allocated,
|
|
4876
|
+
});
|
|
4924
4877
|
}
|
|
4925
4878
|
return retryUpdateLane(hqRoot, laneId, "command", (record) => ({
|
|
4926
4879
|
...record,
|
|
@@ -5011,11 +4964,8 @@ async function runBackfillGrants(ctx, opts) {
|
|
|
5011
4964
|
}
|
|
5012
4965
|
const apply = Boolean(opts.apply);
|
|
5013
4966
|
const hqRoot = ctx.hqRoot();
|
|
5014
|
-
if (apply
|
|
5015
|
-
throw new LanesError("hire_grant_backfill_gate_enabled", "
|
|
5016
|
-
}
|
|
5017
|
-
if (apply && ctx.env.HQ_LANE_ID?.trim()) {
|
|
5018
|
-
throw new LanesError("hire_grant_backfill_unauthorized", "A lane cannot apply the hire-grant backfill; run it from an operator session", {});
|
|
4967
|
+
if (apply) {
|
|
4968
|
+
throw new LanesError("hire_grant_backfill_gate_enabled", "Grant backfill can only be applied before always-on enforcement is released", {});
|
|
5019
4969
|
}
|
|
5020
4970
|
const allLanes = loadAllLanes(hqRoot);
|
|
5021
4971
|
const profileIds = new Set(allLanes
|
|
@@ -5040,76 +4990,11 @@ async function runBackfillGrants(ctx, opts) {
|
|
|
5040
4990
|
if (maxConcurrent !== undefined)
|
|
5041
4991
|
profileMaxConcurrent.set(profileId, maxConcurrent);
|
|
5042
4992
|
}
|
|
5043
|
-
|
|
5044
|
-
if (apply) {
|
|
5045
|
-
const lanesById = new Map(allLanes.map((lane) => [lane.lane_id, lane]));
|
|
5046
|
-
const targetCompanies = new Map();
|
|
5047
|
-
for (const row of rows) {
|
|
5048
|
-
if (row.action === "unchanged")
|
|
5049
|
-
continue;
|
|
5050
|
-
const lane = lanesById.get(row.lane_id);
|
|
5051
|
-
if (!lane) {
|
|
5052
|
-
throw new LanesError("hire_grant_backfill_target_changed", `Backfill target lane ${row.lane_id} is unavailable`, { lane_id: row.lane_id });
|
|
5053
|
-
}
|
|
5054
|
-
const company = lane.company;
|
|
5055
|
-
if (!company)
|
|
5056
|
-
continue;
|
|
5057
|
-
targetCompanies.set(JSON.stringify([company.uid ?? null, company.slug ?? null]), company);
|
|
5058
|
-
}
|
|
5059
|
-
const companyGates = await Promise.all([...targetCompanies.values()].map((company) => ctx.laneHireGrantsEnabled(company)));
|
|
5060
|
-
if (companyGates.some(Boolean)) {
|
|
5061
|
-
throw new LanesError("hire_grant_backfill_gate_enabled", "Backfill must be applied and verified before lanes.hire-grants is enabled", { flag: LANE_HIRE_GRANTS_FLAG });
|
|
5062
|
-
}
|
|
5063
|
-
const shortfalls = rows.filter((row) => row.grant_shortfall > 0);
|
|
5064
|
-
if (shortfalls.length > 0) {
|
|
5065
|
-
throw new LanesError("hire_grant_backfill_allocation_shortfall", "Existing parent grants do not cover the grants allocated to alive children; resolve these rows before applying the backfill", { lanes: shortfalls });
|
|
5066
|
-
}
|
|
5067
|
-
rows = rows.map((row) => {
|
|
5068
|
-
if (row.action === "unchanged")
|
|
5069
|
-
return row;
|
|
5070
|
-
const lane = readLane(hqRoot, row.lane_id);
|
|
5071
|
-
const lockIds = [
|
|
5072
|
-
lane.lane_id,
|
|
5073
|
-
...(lane.senior.kind === "lane" && lane.senior.id ? [lane.senior.id] : []),
|
|
5074
|
-
];
|
|
5075
|
-
return withLaneHireLocks(hqRoot, lockIds, () => {
|
|
5076
|
-
const current = readLane(hqRoot, row.lane_id);
|
|
5077
|
-
if (current.hire_grant !== undefined) {
|
|
5078
|
-
return {
|
|
5079
|
-
...row,
|
|
5080
|
-
existing_grant: current.hire_grant,
|
|
5081
|
-
proposed_grant: current.hire_grant,
|
|
5082
|
-
action: "unchanged",
|
|
5083
|
-
};
|
|
5084
|
-
}
|
|
5085
|
-
const latestLanes = loadAllLanes(hqRoot);
|
|
5086
|
-
const latestRow = planHireGrantBackfill(latestLanes, profileMaxConcurrent)
|
|
5087
|
-
.find((candidate) => candidate.lane_id === current.lane_id);
|
|
5088
|
-
if (!latestRow) {
|
|
5089
|
-
throw new LanesError("hire_grant_backfill_target_changed", `Lane ${current.lane_id} no longer qualifies for the hire-grant backfill`, { lane_id: current.lane_id });
|
|
5090
|
-
}
|
|
5091
|
-
if (latestRow.grant_shortfall > 0) {
|
|
5092
|
-
throw new LanesError("hire_grant_backfill_allocation_shortfall", `Lane ${current.lane_id}'s existing grant does not cover grants allocated to its alive children`, { lane: latestRow });
|
|
5093
|
-
}
|
|
5094
|
-
const proposed = latestRow.proposed_grant;
|
|
5095
|
-
const written = retryUpdateLane(hqRoot, current.lane_id, "command", (record) => ({
|
|
5096
|
-
...record,
|
|
5097
|
-
...(record.hire_grant === undefined ? { hire_grant: proposed } : {}),
|
|
5098
|
-
}));
|
|
5099
|
-
const value = written.hire_grant ?? 0;
|
|
5100
|
-
return {
|
|
5101
|
-
...row,
|
|
5102
|
-
existing_grant: value,
|
|
5103
|
-
proposed_grant: value,
|
|
5104
|
-
action: value === proposed ? "set" : "unchanged",
|
|
5105
|
-
};
|
|
5106
|
-
});
|
|
5107
|
-
});
|
|
5108
|
-
}
|
|
4993
|
+
const rows = planHireGrantBackfill(allLanes, profileMaxConcurrent);
|
|
5109
4994
|
printJson({
|
|
5110
4995
|
ok: true,
|
|
5111
4996
|
action: "backfill-grants",
|
|
5112
|
-
mode:
|
|
4997
|
+
mode: "dry-run",
|
|
5113
4998
|
lanes: rows,
|
|
5114
4999
|
});
|
|
5115
5000
|
}
|
|
@@ -5584,7 +5469,6 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
5584
5469
|
async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, options = {}) {
|
|
5585
5470
|
if (lane.provider !== "claude")
|
|
5586
5471
|
return undefined;
|
|
5587
|
-
const balanceEnabled = await ctx.laneClaudeAccountBalanceEnabled(lane.company);
|
|
5588
5472
|
const hqRoot = ctx.hqRoot();
|
|
5589
5473
|
const config = loadLanesConfig(hqRoot);
|
|
5590
5474
|
const currentConfig = lane.account === undefined
|
|
@@ -5601,19 +5485,6 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
5601
5485
|
const accountNeedsRepair = lane.account !== undefined &&
|
|
5602
5486
|
(currentConfig?.enabled !== true || currentLogin === false);
|
|
5603
5487
|
const repairUnavailableAccount = options.repairUnavailableAccount === true && accountNeedsRepair;
|
|
5604
|
-
if (!balanceEnabled && !(options.repairUnavailableAccount && accountNeedsRepair)) {
|
|
5605
|
-
if (lane.account === undefined)
|
|
5606
|
-
return undefined;
|
|
5607
|
-
if (currentConfig === undefined || !currentConfig.enabled) {
|
|
5608
|
-
throw new AccountResolutionError(currentConfig === undefined ? "unknown_account" : "account_disabled", currentConfig === undefined
|
|
5609
|
-
? `Lane ${lane.lane_id} records account ${JSON.stringify(lane.account)} which is not configured for claude`
|
|
5610
|
-
: `Account ${JSON.stringify(lane.account)} is unavailable for claude`, { provider: "claude", account: lane.account });
|
|
5611
|
-
}
|
|
5612
|
-
if (currentLogin === false) {
|
|
5613
|
-
throw new LanesError("account_not_logged_in", `Account ${lane.account} for claude is not logged in`, { provider: "claude", account: lane.account, lane_id: lane.lane_id });
|
|
5614
|
-
}
|
|
5615
|
-
return undefined;
|
|
5616
|
-
}
|
|
5617
5488
|
const profile = workerProfile?.profile ?? (lane.worker_profile !== undefined
|
|
5618
5489
|
? loadWorkerProfile(hqRoot, lane.worker_profile)
|
|
5619
5490
|
: undefined);
|
|
@@ -5641,28 +5512,7 @@ async function selectAutomaticClaudeResumeAccount(ctx, lane, workerProfile, opti
|
|
|
5641
5512
|
const mayRebalance = BALANCE_MOVABLE_STATES.includes(lane.state);
|
|
5642
5513
|
if (!accountNeedsRepair && !mayRebalance)
|
|
5643
5514
|
return undefined;
|
|
5644
|
-
const balanceHosts =
|
|
5645
|
-
? await loadClaudeBalanceHostSnapshots(ctx, hqRoot, config)
|
|
5646
|
-
: (() => {
|
|
5647
|
-
const lanes = loadAllLanes(hqRoot).filter((candidate) => candidate.host.kind === lane.host.kind && candidate.host.id === lane.host.id);
|
|
5648
|
-
return {
|
|
5649
|
-
snapshots: [{
|
|
5650
|
-
capacity: buildHostCapacitySnapshot({
|
|
5651
|
-
host: lane.host,
|
|
5652
|
-
lanes,
|
|
5653
|
-
config,
|
|
5654
|
-
existsSync: ctx.existsSync,
|
|
5655
|
-
probe: ctx.probeProvider,
|
|
5656
|
-
env: ctx.env,
|
|
5657
|
-
hostname: ctx.hostname(),
|
|
5658
|
-
now: ctx.now(),
|
|
5659
|
-
}),
|
|
5660
|
-
lanes,
|
|
5661
|
-
laneListAvailable: true,
|
|
5662
|
-
}],
|
|
5663
|
-
unavailableHosts: [],
|
|
5664
|
-
};
|
|
5665
|
-
})();
|
|
5515
|
+
const balanceHosts = await loadClaudeBalanceHostSnapshots(ctx, hqRoot, config);
|
|
5666
5516
|
const targetHost = balanceHosts.snapshots.find((host) => host.capacity.host.kind === lane.host.kind && host.capacity.host.id === lane.host.id);
|
|
5667
5517
|
const currentTargetAccount = lane.account === undefined
|
|
5668
5518
|
? undefined
|
|
@@ -6294,7 +6144,6 @@ async function applyClaudeBalanceMove(ctx, suggestion) {
|
|
|
6294
6144
|
return { lane_id: suggestion.lane_id, host: suggestion.host, from: suggestion.from, to: suggestion.to, state: suggestion.state };
|
|
6295
6145
|
}
|
|
6296
6146
|
const lane = laneWithRecoveredProviderSession(ctx.hqRoot(), readLane(ctx.hqRoot(), suggestion.lane_id));
|
|
6297
|
-
assertBalanceApplyEnabled(await ctx.laneClaudeAccountBalanceEnabled(lane.company));
|
|
6298
6147
|
if (lane.state === "running") {
|
|
6299
6148
|
throw new LanesError("lane_running", `Lane ${lane.lane_id} is running; balance never moves a running turn`, {
|
|
6300
6149
|
lane_id: lane.lane_id,
|
|
@@ -6324,7 +6173,6 @@ async function applyClaudeBalanceMove(ctx, suggestion) {
|
|
|
6324
6173
|
}
|
|
6325
6174
|
async function runClaudeBalanceMoveTarget(ctx, laneId, opts) {
|
|
6326
6175
|
const lane = laneWithRecoveredProviderSession(ctx.hqRoot(), readLane(ctx.hqRoot(), laneId));
|
|
6327
|
-
assertBalanceApplyEnabled(await ctx.laneClaudeAccountBalanceEnabled(lane.company));
|
|
6328
6176
|
assertLaneRunsHereForOperatorMutation(ctx, lane, "balance-move-target");
|
|
6329
6177
|
if (lane.provider !== "claude") {
|
|
6330
6178
|
throw new LanesError("claude_account_balance_provider_mismatch", `Lane ${laneId} does not use Claude`, {
|
|
@@ -7072,19 +6920,14 @@ async function runReparent(ctx, laneId, opts) {
|
|
|
7072
6920
|
const nextSenior = parseSeniorRef(opts.senior);
|
|
7073
6921
|
assertSeniorExists(hqRoot, nextSenior, ctx);
|
|
7074
6922
|
const initial = readLane(hqRoot, laneId);
|
|
7075
|
-
const needsHireGrantGate = nextSenior.kind === "lane" &&
|
|
7076
|
-
seniorKey(initial.senior) !== seniorKey(nextSenior);
|
|
7077
|
-
const hireGrantsEnabled = needsHireGrantGate
|
|
7078
|
-
? await ctx.laneHireGrantsEnabled(initial.company)
|
|
7079
|
-
: false;
|
|
7080
6923
|
const lockIds = [
|
|
7081
6924
|
laneId,
|
|
7082
6925
|
...(initial.senior.kind === "lane" && initial.senior.id ? [initial.senior.id] : []),
|
|
7083
6926
|
...(nextSenior.kind === "lane" && nextSenior.id ? [nextSenior.id] : []),
|
|
7084
6927
|
];
|
|
7085
|
-
withLaneHireLocks(hqRoot, lockIds, () => runReparentLocked(ctx, laneId, opts
|
|
6928
|
+
withLaneHireLocks(hqRoot, lockIds, () => runReparentLocked(ctx, laneId, opts));
|
|
7086
6929
|
}
|
|
7087
|
-
function runReparentLocked(ctx, laneId, opts
|
|
6930
|
+
function runReparentLocked(ctx, laneId, opts) {
|
|
7088
6931
|
const hqRoot = ctx.hqRoot();
|
|
7089
6932
|
if (!opts.senior) {
|
|
7090
6933
|
throw new LanesError("missing_senior", "--senior is required", {});
|
|
@@ -7098,8 +6941,7 @@ function runReparentLocked(ctx, laneId, opts, hireGrantsEnabled) {
|
|
|
7098
6941
|
}
|
|
7099
6942
|
const seniorCompany = companyOfSenior(hqRoot, nextSenior, ctx);
|
|
7100
6943
|
assertSameTenant(lane.company, nextSenior, seniorCompany, { lane_id: laneId });
|
|
7101
|
-
if (
|
|
7102
|
-
nextSenior.kind === "lane" &&
|
|
6944
|
+
if (nextSenior.kind === "lane" &&
|
|
7103
6945
|
seniorKey(lane.senior) !== seniorKey(nextSenior)) {
|
|
7104
6946
|
const parent = readLane(hqRoot, nextSenior.id);
|
|
7105
6947
|
if (isTerminalLaneState(parent.state)) {
|
|
@@ -8558,27 +8400,9 @@ async function runAccountsBalance(ctx, opts) {
|
|
|
8558
8400
|
const hqRoot = ctx.hqRoot();
|
|
8559
8401
|
const config = loadLanesConfig(hqRoot);
|
|
8560
8402
|
const loaded = await loadClaudeBalanceHostSnapshots(ctx, hqRoot, config);
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
const
|
|
8564
|
-
for (const lane of lanes) {
|
|
8565
|
-
const companyKey = JSON.stringify([lane.company?.uid ?? null, lane.company?.slug ?? null]);
|
|
8566
|
-
let enabled = companyBalanceGateEnabled.get(companyKey);
|
|
8567
|
-
if (enabled === undefined) {
|
|
8568
|
-
enabled = await ctx.laneClaudeAccountBalanceEnabled(lane.company);
|
|
8569
|
-
companyBalanceGateEnabled.set(companyKey, enabled);
|
|
8570
|
-
}
|
|
8571
|
-
laneBalanceGateEnabled.set(lane.lane_id, enabled);
|
|
8572
|
-
}
|
|
8573
|
-
// With no lanes there is no tenant scope to resolve, so report and enforce
|
|
8574
|
-
// the unscoped/default gate. Otherwise the report reflects whether any
|
|
8575
|
-
// represented lane company has the gate enabled.
|
|
8576
|
-
if (lanes.length === 0) {
|
|
8577
|
-
companyBalanceGateEnabled.set(JSON.stringify([null, null]), await ctx.laneClaudeAccountBalanceEnabled());
|
|
8578
|
-
}
|
|
8579
|
-
const balanceGateEnabled = [...companyBalanceGateEnabled.values()].some(Boolean);
|
|
8580
|
-
if (opts.apply)
|
|
8581
|
-
assertBalanceApplyEnabled(balanceGateEnabled);
|
|
8403
|
+
// Retain the established response field for clients; balancing is now
|
|
8404
|
+
// unconditional, so this compatibility value is always true.
|
|
8405
|
+
const balanceGateEnabled = true;
|
|
8582
8406
|
const loads = accountLoadsForReport({ hosts: loaded.snapshots, config });
|
|
8583
8407
|
const suggestions = planClaudeBalanceMoves({ hosts: loaded.snapshots, config });
|
|
8584
8408
|
const selections = loaded.snapshots.map((host) => {
|
|
@@ -8640,16 +8464,6 @@ async function runAccountsBalance(ctx, opts) {
|
|
|
8640
8464
|
});
|
|
8641
8465
|
continue;
|
|
8642
8466
|
}
|
|
8643
|
-
if (laneBalanceGateEnabled.get(suggestion.lane_id) !== true) {
|
|
8644
|
-
skipped.push({
|
|
8645
|
-
lane_id: suggestion.lane_id,
|
|
8646
|
-
host: suggestion.host,
|
|
8647
|
-
from: suggestion.from,
|
|
8648
|
-
to: suggestion.to,
|
|
8649
|
-
reason: "claude_account_balance_disabled",
|
|
8650
|
-
});
|
|
8651
|
-
continue;
|
|
8652
|
-
}
|
|
8653
8467
|
try {
|
|
8654
8468
|
moved.push(await applyClaudeBalanceMove(ctx, suggestion));
|
|
8655
8469
|
}
|
|
@@ -9514,7 +9328,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
9514
9328
|
.command("balance")
|
|
9515
9329
|
.description("Report Claude account load and suggest safe between-turn moves")
|
|
9516
9330
|
.requiredOption("--provider <name>", "claude")
|
|
9517
|
-
.option("--apply", "Move eligible between-turn sessions
|
|
9331
|
+
.option("--apply", "Move eligible between-turn sessions to balance Claude accounts")
|
|
9518
9332
|
.option("--json", "JSON on stdout (always)")
|
|
9519
9333
|
.action(wrap(async (opts) => {
|
|
9520
9334
|
await runAccountsBalance(ctx, opts);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AuthState } from "./accounts.js";
|
|
2
2
|
import type { HostCapacityAccount, HostCapacitySnapshot } from "./host-capacity.js";
|
|
3
3
|
import type { LaneHost, LaneRecord, LanesConfig } from "./types.js";
|
|
4
|
-
export declare const CLAUDE_ACCOUNT_BALANCE_FLAG = "lanes.claude-account-balance";
|
|
5
4
|
export declare const BALANCE_LIVE_STATES: readonly ["running", "queued", "awaiting_input"];
|
|
6
5
|
export declare const BALANCE_MOVABLE_STATES: readonly ["interrupted", "done", "awaiting_input"];
|
|
7
6
|
export interface BalanceHostSnapshot {
|
|
@@ -82,5 +81,4 @@ export declare function planClaudeBalanceMoves(input: {
|
|
|
82
81
|
hosts: readonly BalanceHostSnapshot[];
|
|
83
82
|
config: LanesConfig;
|
|
84
83
|
}): BalanceMoveSuggestion[];
|
|
85
|
-
|
|
86
|
-
//# sourceMappingURL=claude-account-balance.d.ts.map
|
|
84
|
+
//# sourceMappingURL=claude-balance.d.ts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { LanesError } from "./errors.js";
|
|
2
|
-
export const CLAUDE_ACCOUNT_BALANCE_FLAG = "lanes.claude-account-balance";
|
|
3
2
|
export const BALANCE_LIVE_STATES = ["running", "queued", "awaiting_input"];
|
|
4
3
|
export const BALANCE_MOVABLE_STATES = ["interrupted", "done", "awaiting_input"];
|
|
5
4
|
function sameHost(a, b) {
|
|
@@ -263,9 +262,4 @@ export function planClaudeBalanceMoves(input) {
|
|
|
263
262
|
}
|
|
264
263
|
return suggestions;
|
|
265
264
|
}
|
|
266
|
-
|
|
267
|
-
if (!enabled) {
|
|
268
|
-
throw new LanesError("claude_account_balance_disabled", `Applying Claude account balance moves requires hq-flags gate ${CLAUDE_ACCOUNT_BALANCE_FLAG}`, { flag: CLAUDE_ACCOUNT_BALANCE_FLAG });
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
//# sourceMappingURL=claude-account-balance.js.map
|
|
265
|
+
//# sourceMappingURL=claude-balance.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type LaneRecord } from "./types.js";
|
|
2
2
|
import { LanesError } from "./errors.js";
|
|
3
|
-
export declare const LANE_HIRE_GRANTS_FLAG = "lanes.hire-grants";
|
|
4
3
|
export type LaneHireGrantRow = Pick<LaneRecord, "lane_id" | "state" | "senior" | "hire_grant">;
|
|
5
4
|
export declare function aliveDirectChildren<T extends LaneHireGrantRow>(lanes: readonly T[], parentLaneId: string): T[];
|
|
6
5
|
export declare function assignedChildHireGrants(children: readonly LaneHireGrantRow[], exceptLaneId?: string): number;
|
|
@@ -27,4 +26,4 @@ export interface HireGrantBackfillRow {
|
|
|
27
26
|
}
|
|
28
27
|
/** Select alive manager lanes and calculate the non-destructive one-time backfill. */
|
|
29
28
|
export declare function planHireGrantBackfill(lanes: readonly LaneRecord[], profileMaxConcurrent: ReadonlyMap<string, number>): HireGrantBackfillRow[];
|
|
30
|
-
//# sourceMappingURL=
|
|
29
|
+
//# sourceMappingURL=grants.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isTerminalLaneState } from "./types.js";
|
|
2
2
|
import { listLaneIds, readLane, withLaneHireLocks } from "./store.js";
|
|
3
3
|
import { LanesError } from "./errors.js";
|
|
4
|
-
export const LANE_HIRE_GRANTS_FLAG = "lanes.hire-grants";
|
|
5
4
|
export function aliveDirectChildren(lanes, parentLaneId) {
|
|
6
5
|
return lanes.filter((lane) => lane.senior.kind === "lane" &&
|
|
7
6
|
lane.senior.id === parentLaneId &&
|
|
@@ -144,4 +143,4 @@ export function planHireGrantBackfill(lanes, profileMaxConcurrent) {
|
|
|
144
143
|
})
|
|
145
144
|
.sort((a, b) => a.lane_id.localeCompare(b.lane_id));
|
|
146
145
|
}
|
|
147
|
-
//# sourceMappingURL=
|
|
146
|
+
//# sourceMappingURL=grants.js.map
|
|
@@ -13,7 +13,7 @@ export interface OutpostHireGrantContext {
|
|
|
13
13
|
hierarchy: HireGrantLaneSnapshot[];
|
|
14
14
|
}
|
|
15
15
|
/** Encode only the owner-relevant hierarchy from the primary's complete store. */
|
|
16
|
-
export declare function createOutpostHireGrantContext(target: LaneRecord, lanes: readonly LaneRecord[]
|
|
16
|
+
export declare function createOutpostHireGrantContext(target: LaneRecord, lanes: readonly LaneRecord[]): string;
|
|
17
17
|
/** Decode and validate an owner grant context, binding it to the target lane. */
|
|
18
18
|
export declare function parseOutpostHireGrantContext(encoded: string, targetLaneId: string): OutpostHireGrantContext;
|
|
19
19
|
export declare function sameHireGrantSenior(a: SeniorRef, b: SeniorRef): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { aliveDirectChildren } from "./
|
|
1
|
+
import { aliveDirectChildren } from "./grants.js";
|
|
2
2
|
import { LanesError } from "./errors.js";
|
|
3
3
|
import { isLaneState, isSeniorKind, } from "./types.js";
|
|
4
4
|
const HIRE_GRANT_CONTEXT_SCHEMA = "hq-lane-hire-grant-context.v1";
|
|
@@ -24,29 +24,27 @@ function snapshot(lane) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
/** Encode only the owner-relevant hierarchy from the primary's complete store. */
|
|
27
|
-
export function createOutpostHireGrantContext(target, lanes
|
|
27
|
+
export function createOutpostHireGrantContext(target, lanes) {
|
|
28
28
|
const current = lanes.find((lane) => lane.lane_id === target.lane_id);
|
|
29
29
|
if (!current || !sameSenior(current.senior, target.senior)) {
|
|
30
30
|
throw new LanesError("hire_grant_hierarchy_unavailable", `Cannot build an authoritative hire-grant hierarchy for lane ${target.lane_id}`, { lane_id: target.lane_id });
|
|
31
31
|
}
|
|
32
32
|
const relevant = new Map();
|
|
33
33
|
relevant.set(current.lane_id, current);
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw new LanesError("hire_grant_senior_invalid", `Lane ${target.lane_id} has no senior lane in the primary hierarchy`, { lane_id: target.lane_id, senior_lane_id: parentId ?? null });
|
|
40
|
-
}
|
|
41
|
-
relevant.set(parent.lane_id, parent);
|
|
42
|
-
for (const sibling of aliveDirectChildren(lanes, parent.lane_id)) {
|
|
43
|
-
relevant.set(sibling.lane_id, sibling);
|
|
44
|
-
}
|
|
34
|
+
if (current.senior.kind === "lane") {
|
|
35
|
+
const parentId = current.senior.id;
|
|
36
|
+
const parent = parentId ? lanes.find((lane) => lane.lane_id === parentId) : undefined;
|
|
37
|
+
if (!parent) {
|
|
38
|
+
throw new LanesError("hire_grant_senior_invalid", `Lane ${target.lane_id} has no senior lane in the primary hierarchy`, { lane_id: target.lane_id, senior_lane_id: parentId ?? null });
|
|
45
39
|
}
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
relevant.set(parent.lane_id, parent);
|
|
41
|
+
for (const sibling of aliveDirectChildren(lanes, parent.lane_id)) {
|
|
42
|
+
relevant.set(sibling.lane_id, sibling);
|
|
48
43
|
}
|
|
49
44
|
}
|
|
45
|
+
for (const child of aliveDirectChildren(lanes, current.lane_id)) {
|
|
46
|
+
relevant.set(child.lane_id, child);
|
|
47
|
+
}
|
|
50
48
|
const hierarchy = [...relevant.values()]
|
|
51
49
|
.map(snapshot)
|
|
52
50
|
.sort((a, b) => a.lane_id.localeCompare(b.lane_id));
|
|
@@ -55,7 +53,7 @@ export function createOutpostHireGrantContext(target, lanes, enforcementEnabled)
|
|
|
55
53
|
}
|
|
56
54
|
const context = {
|
|
57
55
|
schema_version: HIRE_GRANT_CONTEXT_SCHEMA,
|
|
58
|
-
enforcement_enabled:
|
|
56
|
+
enforcement_enabled: true,
|
|
59
57
|
expected_senior: copySenior(current.senior),
|
|
60
58
|
hierarchy,
|
|
61
59
|
};
|
|
@@ -150,7 +148,7 @@ export function parseOutpostHireGrantContext(encoded, targetLaneId) {
|
|
|
150
148
|
if (!target || !sameSenior(target.senior, expectedSenior)) {
|
|
151
149
|
throw contextError("Outpost grant context does not match the target's expected senior");
|
|
152
150
|
}
|
|
153
|
-
if (
|
|
151
|
+
if (expectedSenior.kind === "lane" &&
|
|
154
152
|
(!expectedSenior.id || !byId.has(expectedSenior.id))) {
|
|
155
153
|
throw contextError("Outpost grant context is missing the target's senior lane");
|
|
156
154
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { AccountResolutionError, LaneNotFoundError, LaneSchemaError, LaneVersionConflict, LanesConfigError, LanesError, WorkerResolutionError, isLaneVersionConflict, isLanesError, } from "./errors.js";
|
|
5
5
|
export { MAX_LANE_DEPTH } from "./constants.js";
|
|
6
|
-
export { assignedChildHireGrants, aliveDirectChildren, hireGrantCapacityError,
|
|
7
|
-
export type { HireGrantBackfillRow } from "./
|
|
6
|
+
export { assignedChildHireGrants, aliveDirectChildren, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
7
|
+
export type { HireGrantBackfillRow } from "./grants.js";
|
|
8
8
|
export { compareLaneQueueOrder, hasActiveAdmissionReservation, hasActivePendingResume, hasEarlierEligibleQueuedLane, laneOccupiesRunningSlot, laneAdmissionCapReason, laneRunsOnHost, withAllLaneAdmissionLocks, withLaneAdmissionLock, } from "./admission.js";
|
|
9
9
|
export type { AdmissionCapReason, AdmissionHost } from "./admission.js";
|
|
10
10
|
export { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "./host-capacity.js";
|
package/dist/lib/lanes/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { AccountResolutionError, LaneNotFoundError, LaneSchemaError, LaneVersionConflict, LanesConfigError, LanesError, WorkerResolutionError, isLaneVersionConflict, isLanesError, } from "./errors.js";
|
|
5
5
|
export { MAX_LANE_DEPTH } from "./constants.js";
|
|
6
|
-
export { assignedChildHireGrants, aliveDirectChildren, hireGrantCapacityError,
|
|
6
|
+
export { assignedChildHireGrants, aliveDirectChildren, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
7
7
|
export { compareLaneQueueOrder, hasActiveAdmissionReservation, hasActivePendingResume, hasEarlierEligibleQueuedLane, laneOccupiesRunningSlot, laneAdmissionCapReason, laneRunsOnHost, withAllLaneAdmissionLocks, withLaneAdmissionLock, } from "./admission.js";
|
|
8
8
|
export { buildHostCapacitySnapshot, chooseHostForLane, parseHostCapacitySnapshot, } from "./host-capacity.js";
|
|
9
9
|
export { CONFIG_SCHEMA_VERSION, ENVELOPE_DECISIONS, ENVELOPE_SCHEMA_VERSION, HISTORY_CAP, HOST_KINDS, LANE_PROVIDERS, LANE_SCHEMA_VERSION, LANE_STORY_STATUSES, LANE_STATES, LIVENESS_STATES, QUESTION_BODY_STORES, QUESTION_SCHEMA_VERSION, QUESTION_STATUSES, QUEUED_REASONS, RAISED_VIA, ROUTING_REASONS, SENIOR_KINDS, STORY_STATUSES, TERMINAL_LANE_STATES, isEnvelopeDecision, isHostKind, isLaneProvider, isLaneState, isLivenessState, isQuestionStatus, isSeniorKind, isTerminalLaneState, parseLaneEnvelope, parseLaneProvider, parseLaneState, parseLivenessState, parseMajorSchema, parseQuestionStatus, parseSeniorKind, } from "./types.js";
|
|
@@ -15,7 +15,7 @@ import { loadLanesConfig, providerQuotaDefaultHoldMs } from "./config.js";
|
|
|
15
15
|
import { MAX_LANE_DEPTH } from "./constants.js";
|
|
16
16
|
import { AccountResolutionError, LanesError, isLaneVersionConflict } from "./errors.js";
|
|
17
17
|
import { loadWorkerProfile } from "./profile.js";
|
|
18
|
-
import {
|
|
18
|
+
import { terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
|
|
19
19
|
import { classifySessionLiveness, defaultLivenessDeps, loadLivenessIndex, readDaemonPresenceConnected, unindexedClaudeSessionLiveness, } from "./liveness.js";
|
|
20
20
|
import { ANSWERED_QUESTIONS_FILE, armLaneWatcher, envelopeRepairMode, isPidRecordAlive, laneRunDir, readPidRecord, readSpawnManifest, markSpawnFailure, spawnEnvelopeRepair, spawnLaneWorker, retryLaneOnAlternateAccount, } from "./spawn.js";
|
|
21
21
|
import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, updateLane, updateQuestion, } from "./store.js";
|
|
@@ -807,7 +807,6 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
807
807
|
}
|
|
808
808
|
const readRegistryFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
|
|
809
809
|
const keepAliveFlagCompanies = new Map();
|
|
810
|
-
const keepAliveHireGrantFlagCompanies = new Map();
|
|
811
810
|
for (const lane of lanes) {
|
|
812
811
|
if (!(lane.state === "done" || lane.state === "interrupted" || lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") ||
|
|
813
812
|
(lane.keep_alive !== true && lane.worker_profile === undefined) ||
|
|
@@ -824,30 +823,18 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
824
823
|
}
|
|
825
824
|
const key = keepAliveCompanyKey(lane.company);
|
|
826
825
|
keepAliveFlagCompanies.set(key, lane.company);
|
|
827
|
-
if (lane.senior.kind === "lane" && lane.senior.id) {
|
|
828
|
-
keepAliveHireGrantFlagCompanies.set(key, lane.company);
|
|
829
|
-
}
|
|
830
826
|
}
|
|
831
827
|
const keepAliveFlagByCompany = new Map();
|
|
832
|
-
const keepAliveHireGrantFlagByCompany = new Map();
|
|
833
828
|
const env = deps.env ?? process.env;
|
|
834
|
-
await Promise.all([
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
}),
|
|
841
|
-
...[...keepAliveHireGrantFlagCompanies.entries()].map(async ([key, company]) => {
|
|
842
|
-
keepAliveHireGrantFlagByCompany.set(key, await readRegistryFlag(LANE_HIRE_GRANTS_FLAG, {
|
|
843
|
-
company: company ?? undefined,
|
|
844
|
-
env,
|
|
845
|
-
}));
|
|
846
|
-
}),
|
|
847
|
-
]);
|
|
829
|
+
await Promise.all([...keepAliveFlagCompanies.entries()].map(async ([key, company]) => {
|
|
830
|
+
keepAliveFlagByCompany.set(key, await readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, {
|
|
831
|
+
company: company ?? undefined,
|
|
832
|
+
env,
|
|
833
|
+
}));
|
|
834
|
+
}));
|
|
848
835
|
await withAllLaneAdmissionLocks(hqRoot, async () => {
|
|
849
836
|
const freshLanes = loadLaneRecords(hqRoot, summary);
|
|
850
|
-
resumeKeepAlive(hqRoot, freshLanes, summary, deps, now, keepAliveFlagByCompany
|
|
837
|
+
resumeKeepAlive(hqRoot, freshLanes, summary, deps, now, keepAliveFlagByCompany);
|
|
851
838
|
const drainLanes = loadLaneRecords(hqRoot, summary);
|
|
852
839
|
const freshQuestions = loadQuestionRecords(hqRoot, summary);
|
|
853
840
|
await admitQueued(hqRoot, drainLanes, summary, deps, now);
|
|
@@ -1545,7 +1532,7 @@ function latestEnvelopeMtimeMs(hqRoot, lane, ref) {
|
|
|
1545
1532
|
throw new LanesError("keep_alive_envelope_timestamp_unavailable", `Cannot stat latest envelope for lane ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: lane.lane_id, path: filePath });
|
|
1546
1533
|
}
|
|
1547
1534
|
}
|
|
1548
|
-
function resumeKeepAlive(hqRoot, lanes, summary, deps, now, flagByCompany
|
|
1535
|
+
function resumeKeepAlive(hqRoot, lanes, summary, deps, now, flagByCompany) {
|
|
1549
1536
|
const nowMs = now.getTime();
|
|
1550
1537
|
const nowIso = now.toISOString();
|
|
1551
1538
|
const config = loadLanesConfig(hqRoot);
|
|
@@ -1882,8 +1869,7 @@ function resumeKeepAlive(hqRoot, lanes, summary, deps, now, flagByCompany, hireG
|
|
|
1882
1869
|
}), deps.dryRun);
|
|
1883
1870
|
const seniorId = current.senior.kind === "lane" ? current.senior.id : undefined;
|
|
1884
1871
|
let queued;
|
|
1885
|
-
if (seniorId
|
|
1886
|
-
hireGrantFlagByCompany.get(keepAliveCompanyKey(current.company)) === true) {
|
|
1872
|
+
if (seniorId) {
|
|
1887
1873
|
try {
|
|
1888
1874
|
queued = withHireGrantAdmission(hqRoot, seniorId, (snapshot) => {
|
|
1889
1875
|
const latestLane = readLane(hqRoot, current.lane_id);
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { PROVIDER_HOME_ENV, accountStatus, isLoggedOut, resolveAccount, selectBalancedAccount, } from "./accounts.js";
|
|
16
16
|
import { effectiveAccountHome } from "./account-home.js";
|
|
17
17
|
import { buildHostCapacitySnapshot } from "./host-capacity.js";
|
|
18
|
-
import { selectClaudeAccount } from "./claude-
|
|
18
|
+
import { selectClaudeAccount } from "./claude-balance.js";
|
|
19
19
|
import { COMPACTION_PROMPT, compactionAfterUsage, compactionDue, compactLaneSession, } from "./compaction.js";
|
|
20
20
|
import { installDrainHook } from "./hooks-install.js";
|
|
21
21
|
import { loadLanesConfig, providerAutoCompactTokens, providerCompactAfterMs, } from "./config.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the largest measured cell length without passing an unbounded number
|
|
3
|
+
* of values as function arguments. Large vault tables can contain hundreds of
|
|
4
|
+
* thousands of rows, beyond the argument limit of Math.max(...values).
|
|
5
|
+
*/
|
|
6
|
+
export declare function maxLength<T>(values: Iterable<T>, measure: (value: T) => number, floor: number): number;
|
|
7
|
+
//# sourceMappingURL=table-width.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the largest measured cell length without passing an unbounded number
|
|
3
|
+
* of values as function arguments. Large vault tables can contain hundreds of
|
|
4
|
+
* thousands of rows, beyond the argument limit of Math.max(...values).
|
|
5
|
+
*/
|
|
6
|
+
export function maxLength(values, measure, floor) {
|
|
7
|
+
let maximum = floor;
|
|
8
|
+
for (const value of values) {
|
|
9
|
+
const length = measure(value);
|
|
10
|
+
if (length > maximum)
|
|
11
|
+
maximum = length;
|
|
12
|
+
}
|
|
13
|
+
return maximum;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=table-width.js.map
|