@indigoai-us/hq-cli 5.312.1 → 5.313.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 +4 -0
- package/dist/command-catalog.generated.d.ts +15 -0
- package/dist/command-catalog.generated.js +19 -0
- package/dist/commands/core-knowledge-prefs.d.ts +0 -1
- package/dist/commands/core-knowledge-prefs.js +1 -1
- package/dist/commands/core-worker-share-native.d.ts +0 -1
- package/dist/commands/core-worker-share-native.js +1 -1
- package/dist/commands/lanes-message.js +3 -0
- package/dist/commands/lanes-questions.js +2 -1
- package/dist/commands/lanes-resume.js +3 -0
- package/dist/commands/lanes.js +151 -21
- package/dist/lib/lanes/config.d.ts +3 -0
- package/dist/lib/lanes/config.js +14 -0
- package/dist/lib/lanes/disk-guard.d.ts +66 -0
- package/dist/lib/lanes/disk-guard.js +172 -0
- package/dist/lib/lanes/flag-keys.d.ts +2 -0
- package/dist/lib/lanes/flag-keys.js +2 -0
- package/dist/lib/lanes/grants.d.ts +4 -3
- package/dist/lib/lanes/grants.js +12 -7
- package/dist/lib/lanes/hire-grant-dispatch.js +2 -3
- package/dist/lib/lanes/index.d.ts +1 -1
- package/dist/lib/lanes/index.js +1 -1
- package/dist/lib/lanes/reconcile.d.ts +7 -1
- package/dist/lib/lanes/reconcile.js +142 -11
- package/dist/lib/lanes/registry-flag.d.ts +6 -0
- package/dist/lib/lanes/registry-flag.js +25 -0
- package/dist/lib/lanes/spawn.d.ts +5 -0
- package/dist/lib/lanes/spawn.js +26 -1
- package/dist/lib/lanes/store.js +11 -0
- package/dist/lib/lanes/types.d.ts +11 -1
- package/dist/lib/lanes/types.js +1 -0
- package/dist/utils/pack-contributions.d.ts +0 -5
- package/dist/utils/pack-contributions.js +1 -1
- package/dist/utils/redact-error-text.d.ts +0 -2
- package/dist/utils/redact-error-text.js +1 -1
- package/dist/utils/sandbox-runner-client.d.ts +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.313.0] — 2026-10-02
|
|
6
|
+
- Gate live-only lane hire-grant counting by company; blocked and awaiting-input children no longer occupy slots when enabled, and their resume is checked against current capacity.
|
|
7
|
+
- Add company-gated lane low-disk admission protection and Codex rollout retention.
|
|
8
|
+
|
|
5
9
|
## [5.312.1] — 2026-10-02
|
|
6
10
|
- Fix checkpoint Stop hook-state creation and the jq-absent early exit to match the bundled shell.
|
|
7
11
|
- Add native `hq core eval-trigger`, `migrate-policy-triggers`, `register-project`, and `work-mesh-live-rebind` on the fast route behind the default-off `core-native-hook-helpers` flag.
|
|
@@ -9403,6 +9403,21 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
9403
9403
|
}];
|
|
9404
9404
|
readonly subcommands: readonly [];
|
|
9405
9405
|
}];
|
|
9406
|
+
}, {
|
|
9407
|
+
readonly name: "gc-rollouts";
|
|
9408
|
+
readonly description: "Compress or remove unreferenced old Codex session rollouts";
|
|
9409
|
+
readonly aliases: readonly [];
|
|
9410
|
+
readonly hidden: false;
|
|
9411
|
+
readonly usage: "[options]";
|
|
9412
|
+
readonly arguments: readonly [];
|
|
9413
|
+
readonly options: readonly [{
|
|
9414
|
+
readonly flags: "--dry-run";
|
|
9415
|
+
readonly description: "Report changes without modifying rollout files";
|
|
9416
|
+
}, {
|
|
9417
|
+
readonly flags: "--json";
|
|
9418
|
+
readonly description: "Emit JSON on stdout (default)";
|
|
9419
|
+
}];
|
|
9420
|
+
readonly subcommands: readonly [];
|
|
9406
9421
|
}, {
|
|
9407
9422
|
readonly name: "_keep-alive-terminal-trigger";
|
|
9408
9423
|
readonly description: "";
|
|
@@ -12124,6 +12124,25 @@ export const COMMAND_CATALOG = [
|
|
|
12124
12124
|
}
|
|
12125
12125
|
]
|
|
12126
12126
|
},
|
|
12127
|
+
{
|
|
12128
|
+
"name": "gc-rollouts",
|
|
12129
|
+
"description": "Compress or remove unreferenced old Codex session rollouts",
|
|
12130
|
+
"aliases": [],
|
|
12131
|
+
"hidden": false,
|
|
12132
|
+
"usage": "[options]",
|
|
12133
|
+
"arguments": [],
|
|
12134
|
+
"options": [
|
|
12135
|
+
{
|
|
12136
|
+
"flags": "--dry-run",
|
|
12137
|
+
"description": "Report changes without modifying rollout files"
|
|
12138
|
+
},
|
|
12139
|
+
{
|
|
12140
|
+
"flags": "--json",
|
|
12141
|
+
"description": "Emit JSON on stdout (default)"
|
|
12142
|
+
}
|
|
12143
|
+
],
|
|
12144
|
+
"subcommands": []
|
|
12145
|
+
},
|
|
12127
12146
|
{
|
|
12128
12147
|
"name": "_keep-alive-terminal-trigger",
|
|
12129
12148
|
"description": "",
|
|
@@ -9,7 +9,7 @@ import * as path from 'node:path';
|
|
|
9
9
|
import { resolveLiveRoot } from '../utils/hq-roots.js';
|
|
10
10
|
import { runBundledScript } from '../utils/run-bundled-script.js';
|
|
11
11
|
import { describeCoreCommand } from './core-command-catalog.js';
|
|
12
|
-
|
|
12
|
+
const KNOWLEDGE_PREFS_ASSET = 'core/scripts/knowledge-prefs.sh';
|
|
13
13
|
const DEFAULTS = {
|
|
14
14
|
signals_enabled: 'true',
|
|
15
15
|
ontology_enabled: 'true',
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type WorkerSkillDependencies, type WorkerSkillResult } from "./core-worker-skills-runtime.js";
|
|
2
2
|
export declare const WORKER_SKILL_SHARE_ASSET = "core/scripts/share-worker-skill.sh";
|
|
3
|
-
export declare const WORKER_SKILL_SHARE_USAGE = "usage: $0 [--apply] [--force] <scope-dir> <skill-name> <worker-skill-path>...\n";
|
|
4
3
|
export declare function runWorkerSkillShare(argv: string[], dependencies: WorkerSkillDependencies): WorkerSkillResult;
|
|
5
4
|
//# sourceMappingURL=core-worker-share-native.d.ts.map
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { resolveBundledAsset } from "../utils/hq-roots.js";
|
|
4
4
|
import { existsFollowingLinks, isDirectory, isSymlink, resolveWorkerCallerRoot, runGit, errorCode, } from "./core-worker-skills-runtime.js";
|
|
5
5
|
export const WORKER_SKILL_SHARE_ASSET = "core/scripts/share-worker-skill.sh";
|
|
6
|
-
|
|
6
|
+
const WORKER_SKILL_SHARE_USAGE = "usage: $0 [--apply] [--force] <scope-dir> <skill-name> <worker-skill-path>...\n";
|
|
7
7
|
function error(message, code = 2) {
|
|
8
8
|
return { code, stdout: "", stderr: `${message}\n` };
|
|
9
9
|
}
|
|
@@ -12,6 +12,7 @@ import { artifactIdentityKeys, rollbackStorySwitchAttribution, storySwitchNotice
|
|
|
12
12
|
import { laneRunDir, probePidRecord, readPidRecord, readProcStarttime, spawnLaneWorker } from "../lib/lanes/spawn.js";
|
|
13
13
|
import { keepAliveExclusion, keepAliveSupersededBy, LANE_RUNNING_SUPERSEDED_MESSAGE_DELIVERY_FLAG, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds } from "../lib/lanes/keep-alive.js";
|
|
14
14
|
import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
|
|
15
|
+
import { lowDiskGuardEnabledForLane } from "../lib/lanes/disk-guard.js";
|
|
15
16
|
import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
16
17
|
import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, callerMatchesSessionSenior, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
|
|
17
18
|
export function createLaneMessageHandlers(deps) {
|
|
@@ -666,6 +667,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
666
667
|
const admission = await assessLaneAdmission(ctx, lane, { queueAt: at });
|
|
667
668
|
if (admission.action === "refuse" && admission.error)
|
|
668
669
|
throw admission.error;
|
|
670
|
+
const guardEnabled = admission.action === "queue" ? false : await lowDiskGuardEnabledForLane(hqRoot, lane);
|
|
669
671
|
return withResumeHireAdmission(ctx, lane, () => {
|
|
670
672
|
if (admission.action === "queue") {
|
|
671
673
|
const sessionId = lane.provider_session_id;
|
|
@@ -709,6 +711,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
709
711
|
return queued;
|
|
710
712
|
}
|
|
711
713
|
const started = spawnLaneWorker(hqRoot, lane, {
|
|
714
|
+
lowDiskGuardEnabled: guardEnabled,
|
|
712
715
|
env: ctx.env,
|
|
713
716
|
modelDeps: {
|
|
714
717
|
readFileSync: ctx.readFileSync,
|
|
@@ -134,7 +134,8 @@ export function createLaneQuestionsHandlers(deps) {
|
|
|
134
134
|
const awaitingWakeConfirmed = lane.state !== "awaiting_input" ||
|
|
135
135
|
(remote?.wake_status === "resumed" && remote.lane_state === "running") ||
|
|
136
136
|
(remote?.wake_status === "queued" && remote.lane_state === "queued") ||
|
|
137
|
-
(remote?.wake_status === "awaiting_answers"
|
|
137
|
+
((remote?.wake_status === "awaiting_answers" || remote?.wake_status === "deferred") &&
|
|
138
|
+
remote.lane_state === "awaiting_input");
|
|
138
139
|
if (remote?.ok !== true ||
|
|
139
140
|
remote.action !== "questions.answer" ||
|
|
140
141
|
remoteQuestion?.question_id !== questionId ||
|
|
@@ -7,6 +7,7 @@ import { assertNoSecretText } from "../lib/lanes/text-scan.js";
|
|
|
7
7
|
import { laneStories, laneStory, storyWarning } from "../lib/lanes/stories.js";
|
|
8
8
|
import { artifactIdentityKeys, rollbackStorySwitchAttribution, storySwitchNotice } from "../lib/lanes/story-switch.js";
|
|
9
9
|
import { spawnLaneWorker } from "../lib/lanes/spawn.js";
|
|
10
|
+
import { lowDiskGuardEnabledForLane } from "../lib/lanes/disk-guard.js";
|
|
10
11
|
import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, hasKeepAliveProgress, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
|
|
11
12
|
export function createLaneResumeHandlers(deps) {
|
|
12
13
|
const { archiveResumeMessage, assertLaneRunsHereForAdmission, assertLaneRunsHereForOperatorMutation, assessLaneAdmission, automaticBalanceMoveHistoryReason, authorizeFailedResume, authorizeResumeProfileRepin, casUpdateLane, dispatchOutpostLaneCommand, laneIsHostedHere, laneWithRecoveredProviderSession, mintMessageId, persistClaudeAccountSelection, prepareAccountMove, printJson, recordResumeSetup, resolveResumeProfile, selectAutomaticClaudeResumeAccount, validateResumeProfileModel, withResumeHireAdmission } = deps;
|
|
@@ -396,6 +397,7 @@ export function createLaneResumeHandlers(deps) {
|
|
|
396
397
|
const admission = await assessLaneAdmission(ctx, lane, { queueAt: at });
|
|
397
398
|
if (admission.action === "refuse" && admission.error)
|
|
398
399
|
throw admission.error;
|
|
400
|
+
const guardEnabled = admission.action === "queue" ? false : await lowDiskGuardEnabledForLane(hqRoot, lane);
|
|
399
401
|
return withResumeHireAdmission(ctx, lane, () => {
|
|
400
402
|
if (admission.action === "queue") {
|
|
401
403
|
const sessionId = lane.provider_session_id;
|
|
@@ -439,6 +441,7 @@ export function createLaneResumeHandlers(deps) {
|
|
|
439
441
|
});
|
|
440
442
|
}
|
|
441
443
|
const started = spawnLaneWorker(hqRoot, lane, {
|
|
444
|
+
lowDiskGuardEnabled: guardEnabled,
|
|
442
445
|
env: ctx.env,
|
|
443
446
|
modelDeps: {
|
|
444
447
|
readFileSync: ctx.readFileSync,
|
package/dist/commands/lanes.js
CHANGED
|
@@ -32,8 +32,9 @@ import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
|
32
32
|
import { withCliFeatureUsed } from "../utils/cli-telemetry.js";
|
|
33
33
|
import { resolveLanesHqRoot } from "../lib/lanes/hq-root.js";
|
|
34
34
|
import { isSsmRunCommandProcess } from "../lib/lanes/ssm-command-context.js";
|
|
35
|
-
import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
|
|
36
|
-
import { LANE_MESSAGE_ROUTE_OWNER_HOST_FLAG } from "../lib/lanes/flag-keys.js";
|
|
35
|
+
import { readLanesRegistryFlag, readLanesRegistryFlagFailOff } from "../lib/lanes/registry-flag.js";
|
|
36
|
+
import { LANE_HIRE_GRANT_FINISHED_LANES_FLAG, LANE_MESSAGE_ROUTE_OWNER_HOST_FLAG } from "../lib/lanes/flag-keys.js";
|
|
37
|
+
import { CODEX_ROLLOUT_RETENTION_FLAG, configuredCodexRolloutHomes, lowDiskGuardEnabledForLane, retainCodexRollouts } from "../lib/lanes/disk-guard.js";
|
|
37
38
|
import { isControllerAliasOutpostId } from "../lib/lanes/admission-host.js";
|
|
38
39
|
import { runLeadAutopilotPass } from "../lib/lanes/autopilot.js";
|
|
39
40
|
import { syncOutpostLeadRegistries } from "../lib/lanes/autopilot-registry.js";
|
|
@@ -58,10 +59,10 @@ import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, lan
|
|
|
58
59
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
59
60
|
import { listLaneReviews } from "../lib/lanes/reviews.js";
|
|
60
61
|
import { effectiveAccountHome } from "../lib/lanes/account-home.js";
|
|
61
|
-
import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, DEAD_HOST_MS, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
|
|
62
|
+
import { AccountResolutionError, compareLaneQueueOrder, LANE_PROVIDERS, LANE_STATES, QUESTION_STATUSES, SENIOR_KINDS, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, isFinishedResumableHireChild, aliveDirectChildren, assignedChildHireGrants, hireGrantCapacityError, planHireGrantBackfill, terminalChildResumeHireGrantError, withHireGrantAdmission, laneStoryLedgerEnabled, validateProjectStories, buildLaneStatusSnapshot, formatLaneStatusHuman, listLaneIds, listQuestionIds, loadLanesConfig, laneRunsHere, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneRecord, parseLaneState, parseQuestionRecord, parseSeniorKind, readAllLanes, readLane, sanitizeHostComponent, retryUpdateLane, syncLaneRecordFromOwner, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, loadWorkerProfile, loadWorkerProfileWithRegistry, registryWorkerRecord, runReconcilePass, DEAD_HOST_MS, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, dropboxDir, laneDeliveryMechanism, hasActiveAdmissionReservation, hasActivePendingResume, hasRecordedSessionId, hasEarlierEligibleQueuedLane, laneAdmissionCapReason, providerSupportsSessionResume, providerSessionPortable, sessionNotPortableMessage, withLaneAdmissionLock, withAllLaneAdmissionLocks, readWorkerRunResult, resumeCommandRefusal, withLaneHireLocks, raiseLaneQuestion, sha256Text, } from "../lib/lanes/index.js";
|
|
62
63
|
import { laneStories, storyWarning, } from "../lib/lanes/stories.js";
|
|
63
64
|
import { resolveLaneKeepAlive } from "../lib/lanes/profile.js";
|
|
64
|
-
import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
65
|
+
import { laneCompanyUidSource, resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
65
66
|
import { doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveNextCheckAtMs, keepAliveExclusion, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, loadRetiredLaneIds, } from "../lib/lanes/keep-alive.js";
|
|
66
67
|
import { questionBodyAbsPath, resolveQuestionCompanyWithFallback, resolveProjectQuestionCompany, } from "../lib/lanes/questions.js";
|
|
67
68
|
import { assertQuestionBodySecretSafe } from "../lib/lanes/questions.js";
|
|
@@ -475,10 +476,10 @@ function readQuestionBody(hqRoot, question) {
|
|
|
475
476
|
function loadAllLanes(hqRoot) {
|
|
476
477
|
return readAllLanes(hqRoot);
|
|
477
478
|
}
|
|
478
|
-
function laneHireFields(lane, lanes, aliveChildrenByParent) {
|
|
479
|
+
function laneHireFields(lane, lanes, aliveChildrenByParent, finishedChildrenDoNotCount = false) {
|
|
479
480
|
const aliveChildCount = aliveChildrenByParent
|
|
480
481
|
? aliveChildrenByParent.get(lane.lane_id) ?? 0
|
|
481
|
-
: aliveDirectChildren(lanes, lane.lane_id).length;
|
|
482
|
+
: aliveDirectChildren(lanes, lane.lane_id, finishedChildrenDoNotCount).length;
|
|
482
483
|
const hireGrant = lane.hire_grant ?? 0;
|
|
483
484
|
const hireSummary = `hire ${aliveChildCount}/${hireGrant}`;
|
|
484
485
|
return {
|
|
@@ -488,6 +489,15 @@ function laneHireFields(lane, lanes, aliveChildrenByParent) {
|
|
|
488
489
|
hire_summary: hireSummary,
|
|
489
490
|
};
|
|
490
491
|
}
|
|
492
|
+
function hireGrantFinishedChildrenDoNotCount(ctx, lane) {
|
|
493
|
+
const resolution = resolveLaneCompany(ctx.hqRoot(), lane);
|
|
494
|
+
const company = resolution.company ?? undefined;
|
|
495
|
+
return readLanesRegistryFlagFailOff(LANE_HIRE_GRANT_FINISHED_LANES_FLAG, {
|
|
496
|
+
company,
|
|
497
|
+
...(company ? { companyUidSource: laneCompanyUidSource(resolution.source) } : {}),
|
|
498
|
+
env: ctx.env,
|
|
499
|
+
}, readLanesRegistryFlag);
|
|
500
|
+
}
|
|
491
501
|
function mergeOwnerLaneHierarchy(supplied, ownerLanes) {
|
|
492
502
|
const byId = new Map();
|
|
493
503
|
for (const lane of supplied)
|
|
@@ -861,6 +871,18 @@ async function fetchOutpostListHireSummaries(ctx, outpostId, lanes, localLanes)
|
|
|
861
871
|
}
|
|
862
872
|
}
|
|
863
873
|
const hierarchy = mergeOwnerLaneHierarchy(localLanes, ownerLanes);
|
|
874
|
+
const finishedChildrenDoNotCountByCompany = new Map();
|
|
875
|
+
const companyByLaneId = new Map();
|
|
876
|
+
for (const lane of lanes) {
|
|
877
|
+
const company = resolveLaneCompany(ctx.hqRoot(), lane).company;
|
|
878
|
+
companyByLaneId.set(lane.lane_id, company);
|
|
879
|
+
if (!company)
|
|
880
|
+
continue;
|
|
881
|
+
const key = `${company.uid}\0${company.slug}`;
|
|
882
|
+
if (!finishedChildrenDoNotCountByCompany.has(key)) {
|
|
883
|
+
finishedChildrenDoNotCountByCompany.set(key, await hireGrantFinishedChildrenDoNotCount(ctx, lane));
|
|
884
|
+
}
|
|
885
|
+
}
|
|
864
886
|
const outcomes = new Map();
|
|
865
887
|
for (const lane of lanes) {
|
|
866
888
|
const matchingLanes = ownerLanes.filter((ownerLane) => ownerLane.lane_id === lane.lane_id);
|
|
@@ -878,7 +900,13 @@ async function fetchOutpostListHireSummaries(ctx, outpostId, lanes, localLanes)
|
|
|
878
900
|
outcomes.set(lane.lane_id, { error: new LanesError("outpost_lane_hire_summary_invalid", `Outpost ${outpostId} returned a lane owned by a different host`, { outpost_id: outpostId, lane_id: lane.lane_id }) });
|
|
879
901
|
continue;
|
|
880
902
|
}
|
|
881
|
-
|
|
903
|
+
const company = companyByLaneId.get(lane.lane_id) ?? resolveLaneCompany(ctx.hqRoot(), ownerLane).company;
|
|
904
|
+
const companyKey = company
|
|
905
|
+
? `${company.uid}\0${company.slug}`
|
|
906
|
+
: undefined;
|
|
907
|
+
outcomes.set(lane.lane_id, {
|
|
908
|
+
fields: laneHireFields(ownerLane, hierarchy, undefined, companyKey === undefined ? false : finishedChildrenDoNotCountByCompany.get(companyKey) ?? false),
|
|
909
|
+
});
|
|
882
910
|
}
|
|
883
911
|
return outcomes;
|
|
884
912
|
}
|
|
@@ -925,7 +953,14 @@ async function batchOwnerLaneHireFields(ctx, lanes, localLanes, options = {}) {
|
|
|
925
953
|
}
|
|
926
954
|
const OUTPOST_HIRE_SUMMARY_CONCURRENCY = 4;
|
|
927
955
|
async function laneHirePresentation(ctx, lane, lanes) {
|
|
928
|
-
|
|
956
|
+
// The flag only changes the count when a direct child is finished but resumable.
|
|
957
|
+
const hasFinishedChild = lanes.some((child) => child.senior.kind === "lane" &&
|
|
958
|
+
child.senior.id === lane.lane_id &&
|
|
959
|
+
isFinishedResumableHireChild(child.state));
|
|
960
|
+
const finishedChildrenDoNotCount = hasFinishedChild
|
|
961
|
+
? await hireGrantFinishedChildrenDoNotCount(ctx, lane)
|
|
962
|
+
: false;
|
|
963
|
+
const local = laneHireFields(lane, lanes, undefined, finishedChildrenDoNotCount);
|
|
929
964
|
if (lane.host.kind !== "outpost" ||
|
|
930
965
|
isTerminalLaneState(lane.state) ||
|
|
931
966
|
laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
|
|
@@ -966,7 +1001,12 @@ async function withResumeHireAdmission(ctx, lane, fn, grantContext) {
|
|
|
966
1001
|
!sameHireGrantSenior(lane.senior, grantContext.expected_senior))) {
|
|
967
1002
|
throw new LanesError("hire_grant_senior_changed", `Lane ${lane.lane_id}'s senior changed after the primary authorized its Outpost dispatch`, { lane_id: lane.lane_id, expected_senior: grantContext.expected_senior, current_senior: lane.senior });
|
|
968
1003
|
}
|
|
969
|
-
|
|
1004
|
+
const finishedChildrenDoNotCount = isFinishedResumableHireChild(lane.state)
|
|
1005
|
+
? await hireGrantFinishedChildrenDoNotCount(ctx, lane)
|
|
1006
|
+
: false;
|
|
1007
|
+
const shouldCheckGrant = isTerminalLaneState(lane.state) ||
|
|
1008
|
+
(finishedChildrenDoNotCount && isFinishedResumableHireChild(lane.state));
|
|
1009
|
+
if (!shouldCheckGrant ||
|
|
970
1010
|
lane.senior.kind !== "lane" ||
|
|
971
1011
|
!lane.senior.id) {
|
|
972
1012
|
return fn();
|
|
@@ -1000,13 +1040,15 @@ async function withResumeHireAdmission(ctx, lane, fn, grantContext) {
|
|
|
1000
1040
|
}
|
|
1001
1041
|
// A different resume already reactivated this lane; it occupies the slot
|
|
1002
1042
|
// in the shared snapshot and is not a second hire.
|
|
1003
|
-
|
|
1043
|
+
const currentNeedsGrantCheck = isTerminalLaneState(current.state) ||
|
|
1044
|
+
(finishedChildrenDoNotCount && isFinishedResumableHireChild(current.state));
|
|
1045
|
+
if (!currentNeedsGrantCheck)
|
|
1004
1046
|
return fn();
|
|
1005
|
-
const refusal = terminalChildResumeHireGrantError(current, snapshot);
|
|
1047
|
+
const refusal = terminalChildResumeHireGrantError(current, snapshot, finishedChildrenDoNotCount);
|
|
1006
1048
|
if (refusal !== null)
|
|
1007
1049
|
throw refusal;
|
|
1008
1050
|
return fn();
|
|
1009
|
-
}, suppliedHierarchy, remoteSenior === undefined ? undefined : new Set([remoteSenior.lane_id]));
|
|
1051
|
+
}, suppliedHierarchy, remoteSenior === undefined ? undefined : new Set([remoteSenior.lane_id]), finishedChildrenDoNotCount);
|
|
1010
1052
|
}
|
|
1011
1053
|
const CAPACITY_PROBE_TIMEOUT_MS = 15_000;
|
|
1012
1054
|
const MAX_PLACEMENT_OUTPOSTS = 16;
|
|
@@ -3520,6 +3562,10 @@ async function runCreate(ctx, opts) {
|
|
|
3520
3562
|
if (senior.kind === "lane" && senior.id) {
|
|
3521
3563
|
await refreshRemoteLaneMirrors(ctx, hqRoot, loadAllLanes(hqRoot), new Set([senior.id]));
|
|
3522
3564
|
}
|
|
3565
|
+
const seniorLane = senior.kind === "lane" ? readLane(hqRoot, senior.id) : undefined;
|
|
3566
|
+
const finishedChildrenDoNotCount = seniorLane
|
|
3567
|
+
? await hireGrantFinishedChildrenDoNotCount(ctx, seniorLane)
|
|
3568
|
+
: false;
|
|
3523
3569
|
const lane = senior.kind === "lane"
|
|
3524
3570
|
? withHireGrantAdmission(hqRoot, senior.id, ({ parent, children, parentGrant, availableChildGrant }) => {
|
|
3525
3571
|
if (isTerminalLaneState(parent.state)) {
|
|
@@ -3540,7 +3586,7 @@ async function runCreate(ctx, opts) {
|
|
|
3540
3586
|
});
|
|
3541
3587
|
}
|
|
3542
3588
|
return createLane(hqRoot, laneInit);
|
|
3543
|
-
})
|
|
3589
|
+
}, undefined, new Set(), finishedChildrenDoNotCount)
|
|
3544
3590
|
: createLane(hqRoot, laneInit);
|
|
3545
3591
|
return { lane, admission, validation };
|
|
3546
3592
|
};
|
|
@@ -3606,6 +3652,7 @@ async function runCreate(ctx, opts) {
|
|
|
3606
3652
|
let started = lane;
|
|
3607
3653
|
if (admission.action === "start") {
|
|
3608
3654
|
started = spawnLaneWorker(hqRoot, lane, {
|
|
3655
|
+
lowDiskGuardEnabled: await lowDiskGuardEnabledForLane(hqRoot, lane),
|
|
3609
3656
|
env: ctx.env,
|
|
3610
3657
|
modelDeps: {
|
|
3611
3658
|
readFileSync: ctx.readFileSync,
|
|
@@ -4360,11 +4407,12 @@ async function dispatchOutpostLaneCommand(ctx, lane, action, options, behavior =
|
|
|
4360
4407
|
}
|
|
4361
4408
|
if (action === "grant") {
|
|
4362
4409
|
const ownerLane = dispatchedOwnerLane(ctx, lane.lane_id, outpostId, remote.lane, behavior);
|
|
4410
|
+
const finishedChildrenDoNotCount = await hireGrantFinishedChildrenDoNotCount(ctx, ownerLane);
|
|
4363
4411
|
printJson({
|
|
4364
4412
|
...remote,
|
|
4365
4413
|
lane_id: ownerLane.lane_id,
|
|
4366
4414
|
lane: ownerLane,
|
|
4367
|
-
...laneHireFields(ownerLane, grantHierarchy ?? []),
|
|
4415
|
+
...laneHireFields(ownerLane, grantHierarchy ?? [], undefined, finishedChildrenDoNotCount),
|
|
4368
4416
|
outpost_id: outpostId,
|
|
4369
4417
|
outpost_command_id: dispatched.commandId,
|
|
4370
4418
|
outpost_transport: "outposts_exec",
|
|
@@ -4729,6 +4777,7 @@ async function runPickup(ctx, opts) {
|
|
|
4729
4777
|
}
|
|
4730
4778
|
try {
|
|
4731
4779
|
return spawnLaneWorker(hqRoot, current, {
|
|
4780
|
+
lowDiskGuardEnabled: await lowDiskGuardEnabledForLane(hqRoot, current),
|
|
4732
4781
|
env: ctx.env,
|
|
4733
4782
|
modelDeps: {
|
|
4734
4783
|
readFileSync: ctx.readFileSync,
|
|
@@ -4874,10 +4923,28 @@ async function runList(ctx, opts) {
|
|
|
4874
4923
|
const now = ctx.now();
|
|
4875
4924
|
const host = reconcileHostFromCtx(ctx);
|
|
4876
4925
|
const allLanes = laneSource;
|
|
4926
|
+
const lanesById = new Map(allLanes.map((lane) => [lane.lane_id, lane]));
|
|
4927
|
+
const companiesForHireFlags = new Map();
|
|
4928
|
+
const resolvedCompanyByLaneId = new Map();
|
|
4929
|
+
for (const lane of allLanes) {
|
|
4930
|
+
const company = resolveLaneCompany(hqRoot, lane).company;
|
|
4931
|
+
resolvedCompanyByLaneId.set(lane.lane_id, company);
|
|
4932
|
+
if (company)
|
|
4933
|
+
companiesForHireFlags.set(`${company.uid}\0${company.slug}`, lane);
|
|
4934
|
+
}
|
|
4935
|
+
const finishedChildrenFlagByCompany = new Map();
|
|
4936
|
+
await Promise.all([...companiesForHireFlags].map(async ([key, lane]) => {
|
|
4937
|
+
finishedChildrenFlagByCompany.set(key, await hireGrantFinishedChildrenDoNotCount(ctx, lane));
|
|
4938
|
+
}));
|
|
4877
4939
|
const aliveChildrenByParent = new Map();
|
|
4878
4940
|
for (const child of allLanes) {
|
|
4879
4941
|
if (child.senior.kind !== "lane" || !child.senior.id || isTerminalLaneState(child.state))
|
|
4880
4942
|
continue;
|
|
4943
|
+
const parent = lanesById.get(child.senior.id);
|
|
4944
|
+
const parentCompany = parent ? resolvedCompanyByLaneId.get(parent.lane_id) : undefined;
|
|
4945
|
+
const flagKey = parentCompany ? `${parentCompany.uid}\0${parentCompany.slug}` : undefined;
|
|
4946
|
+
if (flagKey !== undefined && finishedChildrenFlagByCompany.get(flagKey) && isFinishedResumableHireChild(child.state))
|
|
4947
|
+
continue;
|
|
4881
4948
|
aliveChildrenByParent.set(child.senior.id, (aliveChildrenByParent.get(child.senior.id) ?? 0) + 1);
|
|
4882
4949
|
}
|
|
4883
4950
|
const ownerEligibleLaneIds = new Set();
|
|
@@ -4933,7 +5000,12 @@ async function runList(ctx, opts) {
|
|
|
4933
5000
|
story_ids: laneStories(lane).map((story) => story.story_id),
|
|
4934
5001
|
no_story_reason: lane.no_story_reason ?? null,
|
|
4935
5002
|
...(remoteQueued
|
|
4936
|
-
? {
|
|
5003
|
+
? {
|
|
5004
|
+
state: "remote",
|
|
5005
|
+
remote_state: lane.state,
|
|
5006
|
+
queued_reason: lane.queued_reason === "low_disk" ? lane.queued_reason : undefined,
|
|
5007
|
+
queued_details: lane.queued_reason === "low_disk" ? lane.queued_details : undefined,
|
|
5008
|
+
}
|
|
4937
5009
|
: {}),
|
|
4938
5010
|
overdue: Boolean(lane.overdue) || isOverdue(hqRoot, lane.lane_id, now),
|
|
4939
5011
|
stalled: Boolean(lane.stalled),
|
|
@@ -4982,8 +5054,11 @@ function formatLaneListHuman(lanes) {
|
|
|
4982
5054
|
const storyLabel = ids.length > 0
|
|
4983
5055
|
? "stories: " + ids.join(", ")
|
|
4984
5056
|
: "no story: " + (lane.no_story_reason ?? "reason not recorded");
|
|
5057
|
+
const queueLabel = (lane.state === "queued" || lane.remote_state === "queued") && lane.queued_reason === "low_disk"
|
|
5058
|
+
? ` queued: low_disk (HQ ${lane.queued_details?.reason === "low_disk" ? lane.queued_details.hq_free_bytes : "?"} bytes; provider ${lane.queued_details?.reason === "low_disk" ? lane.queued_details.provider_free_bytes : "?"} bytes free)`
|
|
5059
|
+
: "";
|
|
4985
5060
|
return lane.lane_id + " " + lane.state + " " + lane.provider + " " + lane.project_id +
|
|
4986
|
-
" " + storyLabel;
|
|
5061
|
+
" " + storyLabel + queueLabel;
|
|
4987
5062
|
});
|
|
4988
5063
|
if (rows.length === 0)
|
|
4989
5064
|
rows.push("No lanes.");
|
|
@@ -5050,6 +5125,45 @@ async function runCapacity(ctx) {
|
|
|
5050
5125
|
});
|
|
5051
5126
|
printJson(snapshot);
|
|
5052
5127
|
}
|
|
5128
|
+
async function runGcRollouts(ctx, opts) {
|
|
5129
|
+
const hqRoot = ctx.hqRoot();
|
|
5130
|
+
const lanes = readAllLanes(hqRoot);
|
|
5131
|
+
const companies = new Map();
|
|
5132
|
+
for (const lane of lanes) {
|
|
5133
|
+
const company = resolveLaneCompany(hqRoot, lane).company;
|
|
5134
|
+
if (company)
|
|
5135
|
+
companies.set(company.uid ?? company.slug, company);
|
|
5136
|
+
}
|
|
5137
|
+
if (companies.size === 0) {
|
|
5138
|
+
const company = await ctx.resolveCompany();
|
|
5139
|
+
companies.set(company.uid ?? company.slug, company);
|
|
5140
|
+
}
|
|
5141
|
+
let enabled = false;
|
|
5142
|
+
for (const company of companies.values()) {
|
|
5143
|
+
enabled ||= await readLanesRegistryFlag(CODEX_ROLLOUT_RETENTION_FLAG, {
|
|
5144
|
+
company,
|
|
5145
|
+
companyUidSource: "resolved_lane_company",
|
|
5146
|
+
env: ctx.env,
|
|
5147
|
+
});
|
|
5148
|
+
if (enabled)
|
|
5149
|
+
break;
|
|
5150
|
+
}
|
|
5151
|
+
if (!enabled) {
|
|
5152
|
+
printJson({ ok: true, action: "gc-rollouts", enabled: false, dry_run: Boolean(opts.dryRun), scanned: 0, compressed: [], deleted: [], bytes_freed: 0 });
|
|
5153
|
+
return;
|
|
5154
|
+
}
|
|
5155
|
+
const config = loadLanesConfig(hqRoot);
|
|
5156
|
+
const homes = configuredCodexRolloutHomes(hqRoot, config, ctx.hostname());
|
|
5157
|
+
const result = await retainCodexRollouts({
|
|
5158
|
+
homes,
|
|
5159
|
+
lanes,
|
|
5160
|
+
nowMs: ctx.now().getTime(),
|
|
5161
|
+
compressAfterMs: config.codex_rollout_compress_after_hours * 60 * 60 * 1000,
|
|
5162
|
+
deleteAfterMs: config.codex_rollout_delete_after_days * 24 * 60 * 60 * 1000,
|
|
5163
|
+
dryRun: opts.dryRun,
|
|
5164
|
+
});
|
|
5165
|
+
printJson({ ok: true, action: "gc-rollouts", enabled: true, dry_run: Boolean(opts.dryRun), ...result });
|
|
5166
|
+
}
|
|
5053
5167
|
async function runOutpostIdentityBackfill(ctx, opts) {
|
|
5054
5168
|
const id = opts.id?.trim();
|
|
5055
5169
|
if (!id) {
|
|
@@ -5191,7 +5305,13 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
|
5191
5305
|
storedLane.host.kind === "outpost" &&
|
|
5192
5306
|
!laneRunsHere(storedLane, reconcileHostFromCtx(ctx));
|
|
5193
5307
|
const lane = remoteQueued
|
|
5194
|
-
? {
|
|
5308
|
+
? {
|
|
5309
|
+
...storedLane,
|
|
5310
|
+
state: "remote",
|
|
5311
|
+
remote_state: storedLane.state,
|
|
5312
|
+
queued_reason: storedLane.queued_reason === "low_disk" ? storedLane.queued_reason : undefined,
|
|
5313
|
+
queued_details: storedLane.queued_reason === "low_disk" ? storedLane.queued_details : undefined,
|
|
5314
|
+
}
|
|
5195
5315
|
: storedLane;
|
|
5196
5316
|
const askDeliveryFailed = readJsonFile(askDeliveryFailedPath(hqRoot, laneId));
|
|
5197
5317
|
const delivery = storedLane.state === "awaiting_input" &&
|
|
@@ -5535,8 +5655,9 @@ async function applyHireGrant(ctx, laneId, hireGrant, authorization, grantContex
|
|
|
5535
5655
|
});
|
|
5536
5656
|
return updated;
|
|
5537
5657
|
}
|
|
5538
|
-
function printGrantResult(hqRoot, updated, hierarchy) {
|
|
5539
|
-
const
|
|
5658
|
+
async function printGrantResult(ctx, hqRoot, updated, hierarchy) {
|
|
5659
|
+
const finishedChildrenDoNotCount = await hireGrantFinishedChildrenDoNotCount(ctx, updated);
|
|
5660
|
+
const fields = laneHireFields(updated, hierarchy ?? loadAllLanes(hqRoot), undefined, finishedChildrenDoNotCount);
|
|
5540
5661
|
printJson({ ok: true, action: "grant", lane_id: updated.lane_id, lane: updated, ...fields });
|
|
5541
5662
|
}
|
|
5542
5663
|
/**
|
|
@@ -5739,7 +5860,7 @@ async function runGrant(ctx, laneId, opts) {
|
|
|
5739
5860
|
return;
|
|
5740
5861
|
}
|
|
5741
5862
|
const updated = await applyHireGrant(ctx, initial.lane_id, hireGrant, "senior");
|
|
5742
|
-
printGrantResult(hqRoot, updated);
|
|
5863
|
+
await printGrantResult(ctx, hqRoot, updated);
|
|
5743
5864
|
}
|
|
5744
5865
|
async function runApprove(ctx, laneId, opts) {
|
|
5745
5866
|
const checkpoint = opts.checkpoint?.trim();
|
|
@@ -5892,7 +6013,7 @@ async function runGrantOwnerTarget(ctx, laneId, opts) {
|
|
|
5892
6013
|
const grantContext = parseOutpostHireGrantContext(opts.grantContext, laneId);
|
|
5893
6014
|
const hqRoot = ctx.hqRoot();
|
|
5894
6015
|
const updated = await applyHireGrant(ctx, laneId, hireGrant, "ssm-owner-dispatch", grantContext);
|
|
5895
|
-
printGrantResult(hqRoot, updated, mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot)));
|
|
6016
|
+
await printGrantResult(ctx, hqRoot, updated, mergeOwnerLaneHierarchy(grantContext.hierarchy, loadAllLanes(hqRoot)));
|
|
5896
6017
|
}
|
|
5897
6018
|
async function runBackfillGrants(ctx, opts) {
|
|
5898
6019
|
if (opts.apply && opts.dryRun) {
|
|
@@ -6020,6 +6141,7 @@ async function runAdmit(ctx, laneId, opts) {
|
|
|
6020
6141
|
return;
|
|
6021
6142
|
}
|
|
6022
6143
|
const started = spawnLaneWorker(hqRoot, lane, {
|
|
6144
|
+
lowDiskGuardEnabled: await lowDiskGuardEnabledForLane(hqRoot, lane),
|
|
6023
6145
|
env: ctx.env,
|
|
6024
6146
|
modelDeps: {
|
|
6025
6147
|
readFileSync: ctx.readFileSync,
|
|
@@ -8797,6 +8919,14 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8797
8919
|
await messageHandlers.recoverQueuedMessages(ctx, lane.lane_id);
|
|
8798
8920
|
});
|
|
8799
8921
|
}));
|
|
8922
|
+
lanes
|
|
8923
|
+
.command("gc-rollouts")
|
|
8924
|
+
.description("Compress or remove unreferenced old Codex session rollouts")
|
|
8925
|
+
.option("--dry-run", "Report changes without modifying rollout files")
|
|
8926
|
+
.option("--json", "Emit JSON on stdout (default)")
|
|
8927
|
+
.action(wrap(async (opts) => {
|
|
8928
|
+
await runGcRollouts(ctx, opts);
|
|
8929
|
+
}));
|
|
8800
8930
|
lanes
|
|
8801
8931
|
.command("_keep-alive-terminal-trigger <laneId> <version> <dueAt>", { hidden: true })
|
|
8802
8932
|
.action(wrap(async (laneId, versionText, dueAt) => {
|
|
@@ -43,6 +43,9 @@ export declare const DEFAULT_SESSION_QUESTION_TIMEOUT_SECS: number;
|
|
|
43
43
|
export declare const DEFAULT_BLOCKER_FRESHNESS_HOURS = 4;
|
|
44
44
|
/** Manager key results must be re-measured within six hours by default. */
|
|
45
45
|
export declare const DEFAULT_KR_FRESHNESS_HOURS = 6;
|
|
46
|
+
export declare const DEFAULT_LOW_DISK_MIN_FREE_BYTES: number;
|
|
47
|
+
export declare const DEFAULT_CODEX_ROLLOUT_COMPRESS_AFTER_HOURS = 24;
|
|
48
|
+
export declare const DEFAULT_CODEX_ROLLOUT_DELETE_AFTER_DAYS = 7;
|
|
46
49
|
export declare const DEFAULT_TRACKER_CHECKS: TrackerChecksConfig;
|
|
47
50
|
export declare const DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG: RunawaySearchGuardConfig;
|
|
48
51
|
export declare const DEFAULT_LIVENESS_WINDOWS: LivenessWindows;
|
package/dist/lib/lanes/config.js
CHANGED
|
@@ -47,6 +47,9 @@ export const DEFAULT_SESSION_QUESTION_TIMEOUT_SECS = 10 * 60;
|
|
|
47
47
|
export const DEFAULT_BLOCKER_FRESHNESS_HOURS = 4;
|
|
48
48
|
/** Manager key results must be re-measured within six hours by default. */
|
|
49
49
|
export const DEFAULT_KR_FRESHNESS_HOURS = 6;
|
|
50
|
+
export const DEFAULT_LOW_DISK_MIN_FREE_BYTES = 5 * 1024 ** 3;
|
|
51
|
+
export const DEFAULT_CODEX_ROLLOUT_COMPRESS_AFTER_HOURS = 24;
|
|
52
|
+
export const DEFAULT_CODEX_ROLLOUT_DELETE_AFTER_DAYS = 7;
|
|
50
53
|
export const DEFAULT_TRACKER_CHECKS = {
|
|
51
54
|
owner_question_surface_ms: 10 * 60 * 1000,
|
|
52
55
|
open_question_age_ms: 2 * 60 * 60 * 1000,
|
|
@@ -455,6 +458,9 @@ export function documentedDefaultConfig(deps = defaultDeps()) {
|
|
|
455
458
|
liveness: { ...DEFAULT_LIVENESS_WINDOWS },
|
|
456
459
|
tracker: { checks: { ...DEFAULT_TRACKER_CHECKS } },
|
|
457
460
|
runaway_search_guard: { ...DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG },
|
|
461
|
+
low_disk_min_free_bytes: DEFAULT_LOW_DISK_MIN_FREE_BYTES,
|
|
462
|
+
codex_rollout_compress_after_hours: DEFAULT_CODEX_ROLLOUT_COMPRESS_AFTER_HOURS,
|
|
463
|
+
codex_rollout_delete_after_days: DEFAULT_CODEX_ROLLOUT_DELETE_AFTER_DAYS,
|
|
458
464
|
session_question_timeout_secs: DEFAULT_SESSION_QUESTION_TIMEOUT_SECS,
|
|
459
465
|
blocker_freshness_hours: DEFAULT_BLOCKER_FRESHNESS_HOURS,
|
|
460
466
|
kr_freshness_hours: DEFAULT_KR_FRESHNESS_HOURS,
|
|
@@ -491,6 +497,11 @@ export function parseLanesConfig(raw, deps = defaultDeps()) {
|
|
|
491
497
|
? documentedDefaultConfig(deps).providers[provider]
|
|
492
498
|
: parseProvider(provider, entry, deps);
|
|
493
499
|
}
|
|
500
|
+
const compressAfterHours = positiveInt(raw.codex_rollout_compress_after_hours, "codex_rollout_compress_after_hours", DEFAULT_CODEX_ROLLOUT_COMPRESS_AFTER_HOURS);
|
|
501
|
+
const deleteAfterDays = positiveInt(raw.codex_rollout_delete_after_days, "codex_rollout_delete_after_days", DEFAULT_CODEX_ROLLOUT_DELETE_AFTER_DAYS);
|
|
502
|
+
if (deleteAfterDays * 24 < compressAfterHours) {
|
|
503
|
+
throw new LanesConfigError("invalid_config", "codex_rollout_delete_after_days must be at least codex_rollout_compress_after_hours / 24", { codex_rollout_compress_after_hours: compressAfterHours, codex_rollout_delete_after_days: deleteAfterDays });
|
|
504
|
+
}
|
|
494
505
|
return {
|
|
495
506
|
schema_version: typeof raw.schema_version === "string"
|
|
496
507
|
? raw.schema_version
|
|
@@ -500,6 +511,9 @@ export function parseLanesConfig(raw, deps = defaultDeps()) {
|
|
|
500
511
|
liveness: parseLiveness(raw.liveness),
|
|
501
512
|
tracker: { checks: parseTrackerChecks(raw.tracker) },
|
|
502
513
|
runaway_search_guard: parseRunawaySearchGuard(raw.runaway_search_guard),
|
|
514
|
+
low_disk_min_free_bytes: positiveInt(raw.low_disk_min_free_bytes, "low_disk_min_free_bytes", DEFAULT_LOW_DISK_MIN_FREE_BYTES),
|
|
515
|
+
codex_rollout_compress_after_hours: compressAfterHours,
|
|
516
|
+
codex_rollout_delete_after_days: deleteAfterDays,
|
|
503
517
|
session_question_timeout_secs: positiveInt(raw.session_question_timeout_secs, "session_question_timeout_secs", DEFAULT_SESSION_QUESTION_TIMEOUT_SECS),
|
|
504
518
|
blocker_freshness_hours: positiveNumber(raw.blocker_freshness_hours, "blocker_freshness_hours", DEFAULT_BLOCKER_FRESHNESS_HOURS),
|
|
505
519
|
kr_freshness_hours: positiveNumber(raw.kr_freshness_hours, "kr_freshness_hours", DEFAULT_KR_FRESHNESS_HOURS),
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { type ReadLanesFlagOptions } from "./registry-flag.js";
|
|
3
|
+
import type { LaneRecord, LanesConfig } from "./types.js";
|
|
4
|
+
export declare const LOW_DISK_GUARD_FLAG = "lanes.low-disk-guard";
|
|
5
|
+
export declare const CODEX_ROLLOUT_RETENTION_FLAG = "lanes.codex-rollout-retention";
|
|
6
|
+
export declare const DEFAULT_LOW_DISK_FREE_BYTES: number;
|
|
7
|
+
export declare const DEFAULT_LOW_DISK_FRACTION = 0.02;
|
|
8
|
+
export declare function lowDiskGuardEnabledForLane(hqRoot: string, lane: LaneRecord, readFlag?: (key: string, options: ReadLanesFlagOptions) => Promise<boolean>): Promise<boolean>;
|
|
9
|
+
export interface DiskStats {
|
|
10
|
+
bsize: number;
|
|
11
|
+
blocks: number;
|
|
12
|
+
bavail: number;
|
|
13
|
+
}
|
|
14
|
+
export type StatFs = (target: string) => DiskStats;
|
|
15
|
+
export interface DiskSpaceMeasurement {
|
|
16
|
+
path: string;
|
|
17
|
+
free_bytes: number;
|
|
18
|
+
total_bytes: number;
|
|
19
|
+
threshold_bytes: number;
|
|
20
|
+
low: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function measureDiskSpace(target: string, statfs?: StatFs, minFreeBytes?: number): DiskSpaceMeasurement;
|
|
23
|
+
export declare function checkLaneDiskSpace(hqRoot: string, providerHome: string, statfs?: StatFs, minFreeBytes?: number): {
|
|
24
|
+
low: boolean;
|
|
25
|
+
hq: DiskSpaceMeasurement;
|
|
26
|
+
provider: DiskSpaceMeasurement;
|
|
27
|
+
};
|
|
28
|
+
export interface RolloutLane {
|
|
29
|
+
provider: string;
|
|
30
|
+
state: string;
|
|
31
|
+
provider_session_id?: string;
|
|
32
|
+
last_resume?: {
|
|
33
|
+
session_id?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export interface RolloutRetentionOptions {
|
|
37
|
+
homes: string[];
|
|
38
|
+
lanes: RolloutLane[];
|
|
39
|
+
nowMs?: number;
|
|
40
|
+
compressAfterMs?: number;
|
|
41
|
+
deleteAfterMs?: number;
|
|
42
|
+
dryRun?: boolean;
|
|
43
|
+
readdirSync?: typeof fs.readdirSync;
|
|
44
|
+
statSync?: typeof fs.statSync;
|
|
45
|
+
readFileSync?: typeof fs.readFileSync;
|
|
46
|
+
writeFileSync?: typeof fs.writeFileSync;
|
|
47
|
+
renameSync?: typeof fs.renameSync;
|
|
48
|
+
unlinkSync?: typeof fs.unlinkSync;
|
|
49
|
+
utimesSync?: typeof fs.utimesSync;
|
|
50
|
+
compressFile?: (source: string, target: string) => Promise<number>;
|
|
51
|
+
}
|
|
52
|
+
export interface RolloutRetentionResult {
|
|
53
|
+
scanned: number;
|
|
54
|
+
compressed: string[];
|
|
55
|
+
deleted: string[];
|
|
56
|
+
bytes_freed: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Codex homes that rollout retention may touch: enabled accounts in the
|
|
60
|
+
* operator's lanes config file. Without that file the documented defaults
|
|
61
|
+
* point at the real `~/.codex`, so retention gets no homes at all.
|
|
62
|
+
*/
|
|
63
|
+
export declare function configuredCodexRolloutHomes(hqRoot: string, config: LanesConfig, hostname: string): string[];
|
|
64
|
+
/** Operates only on rollout files under configured Codex `sessions/` trees. */
|
|
65
|
+
export declare function retainCodexRollouts(options: RolloutRetentionOptions): Promise<RolloutRetentionResult>;
|
|
66
|
+
//# sourceMappingURL=disk-guard.d.ts.map
|