@zq-silk/yui 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -23
- package/dist/cli/commandCatalog.js +234 -122
- package/dist/cli/completion.js +3 -3
- package/dist/cli/helpRenderer.js +3 -0
- package/dist/cli/interactionPolicy.js +44 -23
- package/dist/cli/interactiveSelection.js +1 -1
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/roleWizard.js +8 -8
- package/dist/cli.js +116 -72
- package/dist/commands/agentCommands.js +5 -5
- package/dist/commands/configCommands.js +351 -104
- package/dist/commands/configOverview.js +60 -0
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/globalRoleCommands.js +9 -9
- package/dist/commands/profileCommands.js +8 -8
- package/dist/commands/resourcesCommands.js +6 -5
- package/dist/commands/taskCommands.js +3 -6
- package/dist/commands/taskRoleRuntimeStatus.js +3 -1
- package/dist/commands/telemetryCommands.js +11 -6
- package/dist/config/configCatalog.js +42 -0
- package/dist/config/yuiConfig.js +80 -35
- package/dist/context/sessionBootstrapManifest.js +1 -1
- package/dist/controller/clientRuntime.js +0 -2
- package/dist/controller/controller.js +21 -9
- package/dist/controller/fileSchedulerStoreAdapter.js +20 -9
- package/dist/controller/runtime.js +32 -18
- package/dist/coordination/workMailbox.js +25 -22
- package/dist/doctor/doctor.js +2 -2
- package/dist/resources/autoResourceGc.js +3 -1
- package/dist/review/reviewConfig.js +0 -2
- package/dist/run/providerRetry.js +29 -16
- package/dist/run/providerRetryConfig.js +5 -3
- package/dist/runtime/launchDiagnostics.js +1 -1
- package/dist/scheduler/roleRunStall.js +12 -9
- package/dist/setup/setupCommand.js +153 -492
- package/dist/storage/compatibleTaskStore.js +9 -5
- package/dist/storage/migration/productionRegistry.js +58 -0
- package/dist/storage/sqliteStore.js +9 -2
- package/dist/storage/taskStore.js +21 -2
- package/dist/telemetry/sqliteTelemetryStore.js +9 -1
- package/dist/telemetry/telemetryConfig.js +1 -18
- package/dist/telemetry/telemetryStore.js +2 -2
- package/dist/telemetry/telemetryWiring.js +6 -5
- package/dist/web/webSnapshot.js +5 -3
- package/i18n/README.zh-CN.md +37 -32
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +12 -5
- package/skills/yui-operator/SKILL.md +44 -6
- package/skills/yui-runtime/SKILL.md +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { inspectCompletionStates, renderCompletionStateTable } from "../completion/completionState.js";
|
|
2
|
+
import { usageError } from "../errors/cliError.js";
|
|
3
|
+
import { SYSTEM_ROLE_NAMES } from "../role/systemRoles.js";
|
|
4
|
+
import { runAgentCommand } from "./agentCommands.js";
|
|
5
|
+
import { runConfigCommand } from "./configCommands.js";
|
|
6
|
+
import { runGlobalRoleCommand } from "./globalRoleCommands.js";
|
|
7
|
+
import { runProfileCommand } from "./profileCommands.js";
|
|
8
|
+
import { CONFIG_DOMAINS } from "../config/configCatalog.js";
|
|
9
|
+
/**
|
|
10
|
+
* One complete projection of every persistent configuration domain. Human and
|
|
11
|
+
* JSON callers intentionally consume the same store projection; the Operator can
|
|
12
|
+
* therefore explain what the user sees without reconstructing configuration
|
|
13
|
+
* from help text or implementation defaults.
|
|
14
|
+
*/
|
|
15
|
+
export function runConfigOverview(args, store, environment, identity, roleOptions) {
|
|
16
|
+
if (args.length !== 0)
|
|
17
|
+
throw usageError("Config show usage: yui config show.");
|
|
18
|
+
const domains = Object.fromEntries(CONFIG_DOMAINS.map((domain) => [
|
|
19
|
+
domain,
|
|
20
|
+
runConfigCommand(domain, ["show"], store)
|
|
21
|
+
]));
|
|
22
|
+
const agents = store.listConfiguredAgents();
|
|
23
|
+
const roles = store.listGlobalRoles();
|
|
24
|
+
const systemRoles = Object.fromEntries(SYSTEM_ROLE_NAMES.map((name) => [
|
|
25
|
+
name,
|
|
26
|
+
store.getGlobalRole(name)
|
|
27
|
+
]));
|
|
28
|
+
const profiles = store.listAgentProfiles();
|
|
29
|
+
const completion = inspectCompletionStates(store.getConfig(), environment, identity);
|
|
30
|
+
const roleOutput = runGlobalRoleCommand(["list"], store, roleOptions);
|
|
31
|
+
if (typeof roleOutput !== "string") {
|
|
32
|
+
throw new Error("Config overview received an invalid Role control result.");
|
|
33
|
+
}
|
|
34
|
+
const profileOutput = runProfileCommand(["list"], store).output;
|
|
35
|
+
const agentOutput = runAgentCommand(["list"], store);
|
|
36
|
+
const domainData = Object.fromEntries(CONFIG_DOMAINS.map((domain) => [
|
|
37
|
+
domain,
|
|
38
|
+
domains[domain].data
|
|
39
|
+
]));
|
|
40
|
+
return {
|
|
41
|
+
output: `${[
|
|
42
|
+
"Yui configuration",
|
|
43
|
+
...CONFIG_DOMAINS.map((domain) => domains[domain].output.trimEnd()),
|
|
44
|
+
agentOutput.trimEnd(),
|
|
45
|
+
roleOutput.trimEnd(),
|
|
46
|
+
profileOutput.trimEnd(),
|
|
47
|
+
renderCompletionStateTable(completion).trimEnd()
|
|
48
|
+
].join("\n\n")}\n`,
|
|
49
|
+
data: {
|
|
50
|
+
...domainData,
|
|
51
|
+
agents,
|
|
52
|
+
roles: {
|
|
53
|
+
system: systemRoles,
|
|
54
|
+
custom: roles.filter(({ name }) => !SYSTEM_ROLE_NAMES.includes(name))
|
|
55
|
+
},
|
|
56
|
+
profiles,
|
|
57
|
+
completion
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveLeaderNextActionMode } from "../config/yuiConfig.js";
|
|
1
|
+
import { resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns } from "../config/yuiConfig.js";
|
|
2
2
|
import { usageError } from "../errors/cliError.js";
|
|
3
3
|
import { detectDeliveryDuplicates, evaluateDeliveryGuard, evaluateSemanticBudget, formatDeliveryDuplicate } from "../task/deliveryGuard.js";
|
|
4
4
|
export function runDeliveryGuardPreflight(store, taskId, intent, options = {}) {
|
|
@@ -15,7 +15,7 @@ export function runDeliveryGuardPreflight(store, taskId, intent, options = {}) {
|
|
|
15
15
|
+ "Reuse the existing proof, or set leader.nextActionMode=warn to proceed with a warning.");
|
|
16
16
|
}
|
|
17
17
|
if (options.budget === true) {
|
|
18
|
-
const budget = evaluateSemanticBudget(facts);
|
|
18
|
+
const budget = evaluateSemanticBudget(facts, resolveLeaderSemanticBudgetTurns(store.getConfig().leaderSemanticBudgetTurns));
|
|
19
19
|
if (budget.exhausted) {
|
|
20
20
|
warnings.push(`Semantic progress budget: ${budget.reason}`);
|
|
21
21
|
}
|
|
@@ -24,13 +24,13 @@ export function runGlobalRoleCommand(args, store, options = {}) {
|
|
|
24
24
|
default:
|
|
25
25
|
throw usageError(command === undefined
|
|
26
26
|
? "Role command is required."
|
|
27
|
-
: `Unknown command: role ${command}`);
|
|
27
|
+
: `Unknown command: config role ${command}`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
function roleContext(args, store, options) {
|
|
31
31
|
const [rawName, ...rest] = args;
|
|
32
32
|
const name = roleName(rawName);
|
|
33
|
-
assertNoArguments(rest, "
|
|
33
|
+
assertNoArguments(rest, "Session context usage: yui session context <role>");
|
|
34
34
|
const environment = options.env ?? process.env;
|
|
35
35
|
if (environment.YUI_SESSION_SCOPE !== undefined) {
|
|
36
36
|
if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ROLE !== name) {
|
|
@@ -125,7 +125,7 @@ function addRole(args, store, options) {
|
|
|
125
125
|
return presentRole(`Added role ${name}`, created, store);
|
|
126
126
|
}
|
|
127
127
|
function listRoles(args, store) {
|
|
128
|
-
assertNoArguments(args, "Role list usage: yui role list");
|
|
128
|
+
assertNoArguments(args, "Role list usage: yui config role list");
|
|
129
129
|
const rows = new Map();
|
|
130
130
|
for (const name of SYSTEM_ROLE_NAMES) {
|
|
131
131
|
const role = store.getGlobalRole(name);
|
|
@@ -152,7 +152,7 @@ function listRoles(args, store) {
|
|
|
152
152
|
function showRole(args, store) {
|
|
153
153
|
const [rawName, ...rest] = args;
|
|
154
154
|
const name = roleName(rawName);
|
|
155
|
-
assertNoArguments(rest, "Role show usage: yui role show <role>");
|
|
155
|
+
assertNoArguments(rest, "Role show usage: yui config role show <role>");
|
|
156
156
|
const role = store.getGlobalRole(name);
|
|
157
157
|
if (role === null) {
|
|
158
158
|
if (isSystemRoleName(name))
|
|
@@ -218,7 +218,7 @@ function bindRole(args, store) {
|
|
|
218
218
|
const [rawName, rawAgentId, ...rest] = args;
|
|
219
219
|
const name = roleName(rawName);
|
|
220
220
|
const agentId = required(rawAgentId, "Agent id");
|
|
221
|
-
assertNoArguments(rest, "Role bind usage: yui role bind <role> <agent-id>");
|
|
221
|
+
assertNoArguments(rest, "Role bind usage: yui config role bind <role> <agent-id>");
|
|
222
222
|
const now = new Date();
|
|
223
223
|
const result = store.transaction((tx) => {
|
|
224
224
|
const role = requireRole(name, tx);
|
|
@@ -269,7 +269,7 @@ function assertOperatorAdapterAvailable(role, agent) {
|
|
|
269
269
|
function removeRole(args, store) {
|
|
270
270
|
const [rawName, ...rest] = args;
|
|
271
271
|
const name = roleName(rawName);
|
|
272
|
-
assertNoArguments(rest, "Role remove usage: yui role remove <role>");
|
|
272
|
+
assertNoArguments(rest, "Role remove usage: yui config role remove <role>");
|
|
273
273
|
if (isSystemRoleName(name))
|
|
274
274
|
throw usageError(`System role cannot be removed: ${name}`);
|
|
275
275
|
store.transaction((tx) => {
|
|
@@ -291,7 +291,7 @@ function unbindRole(args, store) {
|
|
|
291
291
|
const [rawName, rawAgentId, ...rest] = args;
|
|
292
292
|
const name = roleName(rawName);
|
|
293
293
|
const agentId = required(rawAgentId, "Agent id");
|
|
294
|
-
assertNoArguments(rest, "Role unbind usage: yui role unbind <role> <agent-id>");
|
|
294
|
+
assertNoArguments(rest, "Role unbind usage: yui config role unbind <role> <agent-id>");
|
|
295
295
|
store.transaction((tx) => {
|
|
296
296
|
const role = requireRole(name, tx);
|
|
297
297
|
try {
|
|
@@ -307,14 +307,14 @@ function unbindRole(args, store) {
|
|
|
307
307
|
function enterRole(args, store) {
|
|
308
308
|
const [rawName, ...rest] = args;
|
|
309
309
|
const name = roleName(rawName);
|
|
310
|
-
assertNoArguments(rest, "Role enter usage: yui
|
|
310
|
+
assertNoArguments(rest, "Role enter usage: yui session enter <role>");
|
|
311
311
|
const role = requireRole(name, store);
|
|
312
312
|
return { kind: "enter", role };
|
|
313
313
|
}
|
|
314
314
|
function roleSession(args, store, options) {
|
|
315
315
|
const [command, rawName, ...tail] = args;
|
|
316
316
|
if (command !== "record" && command !== "replace") {
|
|
317
|
-
throw usageError("
|
|
317
|
+
throw usageError("Session usage: yui session record|replace <role> --native-id <id> [--reason <reason>].");
|
|
318
318
|
}
|
|
319
319
|
const name = roleName(rawName);
|
|
320
320
|
const parsed = parseOptions(tail, new Map([
|
|
@@ -14,11 +14,11 @@ export function runProfileCommand(args, store, now = () => new Date()) {
|
|
|
14
14
|
default:
|
|
15
15
|
throw usageError(command === undefined
|
|
16
16
|
? "Profile command is required."
|
|
17
|
-
: `Unknown command: profile ${command}`);
|
|
17
|
+
: `Unknown command: config profile ${command}`);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
function addProfile(args, store, now) {
|
|
21
|
-
const usage = "Profile add usage: yui profile add <id> [--access <read|write>] [Profile settings].";
|
|
21
|
+
const usage = "Profile add usage: yui config profile add <id> [--access <read|write>] [Profile settings].";
|
|
22
22
|
const [id, ...tail] = args;
|
|
23
23
|
if (id === undefined || id.startsWith("--"))
|
|
24
24
|
throw usageError("Profile id is required.", usage);
|
|
@@ -37,7 +37,7 @@ function addProfile(args, store, now) {
|
|
|
37
37
|
return { output: `Added Agent Profile ${profile.id}\n`, data: { profile } };
|
|
38
38
|
}
|
|
39
39
|
function listProfiles(args, store) {
|
|
40
|
-
noArgs(args, "Profile list usage: yui profile list.");
|
|
40
|
+
noArgs(args, "Profile list usage: yui config profile list.");
|
|
41
41
|
const profiles = store.listAgentProfiles();
|
|
42
42
|
const output = profiles.length === 0
|
|
43
43
|
? "No Agent Profiles found.\n"
|
|
@@ -55,7 +55,7 @@ function listProfiles(args, store) {
|
|
|
55
55
|
return { output, data: { profiles } };
|
|
56
56
|
}
|
|
57
57
|
function showProfile(args, store) {
|
|
58
|
-
const profile = requireProfile(store, oneArg(args, "Profile show usage: yui profile show <id>."));
|
|
58
|
+
const profile = requireProfile(store, oneArg(args, "Profile show usage: yui config profile show <id>."));
|
|
59
59
|
return {
|
|
60
60
|
output: `${[
|
|
61
61
|
`Agent Profile: ${profile.id}`,
|
|
@@ -71,7 +71,7 @@ function showProfile(args, store) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
function updateProfile(args, store, now) {
|
|
74
|
-
const usage = "Profile update usage: yui profile update <id> [--access <read|write>] [Profile settings].";
|
|
74
|
+
const usage = "Profile update usage: yui config profile update <id> [--access <read|write>] [Profile settings].";
|
|
75
75
|
const [id, ...tail] = args;
|
|
76
76
|
if (id === undefined || id.startsWith("--"))
|
|
77
77
|
throw usageError("Profile id is required.", usage);
|
|
@@ -91,16 +91,16 @@ function updateProfile(args, store, now) {
|
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
function removeProfile(args, store) {
|
|
94
|
-
const id = oneArg(args, "Profile remove usage: yui profile remove <id>.");
|
|
94
|
+
const id = oneArg(args, "Profile remove usage: yui config profile remove <id>.");
|
|
95
95
|
if (BUILTIN_PROFILE_IDS.includes(id)) {
|
|
96
|
-
throw usageError(`Built-in Agent Profile cannot be removed: ${id}. Use profile reset instead.`);
|
|
96
|
+
throw usageError(`Built-in Agent Profile cannot be removed: ${id}. Use yui config profile reset instead.`);
|
|
97
97
|
}
|
|
98
98
|
if (!store.removeAgentProfile(id))
|
|
99
99
|
throw usageError(`Agent Profile not found: ${id}.`);
|
|
100
100
|
return { output: `Removed Agent Profile ${id}\n`, data: { profileId: id } };
|
|
101
101
|
}
|
|
102
102
|
function resetProfiles(args, store, now) {
|
|
103
|
-
noArgs(args, "Profile reset usage: yui profile reset.");
|
|
103
|
+
noArgs(args, "Profile reset usage: yui config profile reset.");
|
|
104
104
|
store.transaction((tx) => {
|
|
105
105
|
for (const desired of builtinAgentProfileInputs()) {
|
|
106
106
|
const existing = tx.getAgentProfile(desired.id);
|
|
@@ -12,7 +12,7 @@ import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
|
12
12
|
import { applyResourceGc, planResourceGc, purgeResourceQuarantine, restoreAllResourceGc } from "../resources/resourceGc.js";
|
|
13
13
|
import { createResourceRegistryStore } from "../resources/resourceRegistryStore.js";
|
|
14
14
|
import { resourceKindLabel, resourceOwnerLabel } from "../resources/resourceDiscovery.js";
|
|
15
|
-
import { resolveResourcesGcMode } from "../config/yuiConfig.js";
|
|
15
|
+
import { resolveResourcesGcMode, resolveResourcesQuarantineTtlHours } from "../config/yuiConfig.js";
|
|
16
16
|
export async function runResourcesCommand(args, store, options = {}) {
|
|
17
17
|
const [command, ...rest] = args;
|
|
18
18
|
if (command !== "gc") {
|
|
@@ -23,7 +23,7 @@ export async function runResourcesCommand(args, store, options = {}) {
|
|
|
23
23
|
}
|
|
24
24
|
async function runGcCommand(args, store, options) {
|
|
25
25
|
const action = parseGcAction(args);
|
|
26
|
-
const ttlHours = parseTtlHours(args);
|
|
26
|
+
const ttlHours = parseTtlHours(args, store);
|
|
27
27
|
const now = options.now?.() ?? new Date();
|
|
28
28
|
const home = resolve(store.rootDirectory());
|
|
29
29
|
const mode = resolveGcMode(store);
|
|
@@ -95,10 +95,11 @@ function parseGcAction(args) {
|
|
|
95
95
|
return "restore";
|
|
96
96
|
return "dry-run";
|
|
97
97
|
}
|
|
98
|
-
function parseTtlHours(args) {
|
|
98
|
+
function parseTtlHours(args, store) {
|
|
99
99
|
const index = args.indexOf("--quarantine-ttl-hours");
|
|
100
|
-
if (index === -1)
|
|
101
|
-
return
|
|
100
|
+
if (index === -1) {
|
|
101
|
+
return resolveResourcesQuarantineTtlHours(store.getConfig().resourcesQuarantineTtlHours);
|
|
102
|
+
}
|
|
102
103
|
const value = Number(args[index + 1]);
|
|
103
104
|
if (!Number.isFinite(value) || value < 1 || value > 24 * 30) {
|
|
104
105
|
throw usageError("Quarantine TTL hours must be between 1 and 720.");
|
|
@@ -20,7 +20,6 @@ import { activeRoleAgentBinding, copyGlobalRoleToTaskRole, createRole, createRol
|
|
|
20
20
|
import { agentRunDeliveryReceiptId, createAgentRun, withAgentRunContextSnapshot } from "../run/agentRun.js";
|
|
21
21
|
import { projectRunRecovery, readRunRecoveryFacts } from "../run/recoveryProjection.js";
|
|
22
22
|
import { matchYieldReceipt } from "../run/yieldReceipt.js";
|
|
23
|
-
import { providerRetryConfig } from "../run/providerRetryConfig.js";
|
|
24
23
|
import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, deltaRecheckBlocksAcceptance, finishReviewRound, parseReviewYieldReport, recordReviewWorkspaceDisposition, retryTaskReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
|
|
25
24
|
import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
|
|
26
25
|
import { buildTaskFinalReviewFindingContext, dispositionReviewFinding, planRepairGroups, reconcileReviewFindings, reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
|
|
@@ -2840,7 +2839,7 @@ function requestTaskReviewRound(args, store, options) {
|
|
|
2840
2839
|
const reviewConfig = tx.getReviewConfig();
|
|
2841
2840
|
if (reviewConfig === null || reviewConfig.deltaRecheck !== "enabled") {
|
|
2842
2841
|
throw usageError("Delta-recheck is not enabled for this Project's review policy. "
|
|
2843
|
-
+ "Set `yui config set review --role <role> --trigger final --delta-recheck enabled` "
|
|
2842
|
+
+ "Set `yui config workflow set review --role <role> --trigger final --delta-recheck enabled` "
|
|
2844
2843
|
+ "or request a full Review.");
|
|
2845
2844
|
}
|
|
2846
2845
|
if (taskFinalContract !== undefined) {
|
|
@@ -4434,9 +4433,7 @@ function buildYieldOutcome(run, inputSummary, options) {
|
|
|
4434
4433
|
* for the same outcome, fails closed for a different outcome, or returns
|
|
4435
4434
|
* `null` to keep the legacy "already terminal" behavior.
|
|
4436
4435
|
*/
|
|
4437
|
-
function replayYieldReceipt(run, inputSummary, options
|
|
4438
|
-
if (!retryConfig.yieldReceiptReplay)
|
|
4439
|
-
return null;
|
|
4436
|
+
function replayYieldReceipt(run, inputSummary, options) {
|
|
4440
4437
|
if (run.yieldReceipt === undefined)
|
|
4441
4438
|
return null;
|
|
4442
4439
|
const outcome = buildYieldOutcome(run, inputSummary, options);
|
|
@@ -4492,7 +4489,7 @@ function yieldRun(args, store, options) {
|
|
|
4492
4489
|
// transaction; the receipt is immutable once committed.
|
|
4493
4490
|
const existing = requireRun(store, parsed.positionals[0], options);
|
|
4494
4491
|
if (existing.status !== "active") {
|
|
4495
|
-
const replayed = replayYieldReceipt(existing, inputSummary, options
|
|
4492
|
+
const replayed = replayYieldReceipt(existing, inputSummary, options);
|
|
4496
4493
|
if (replayed !== null)
|
|
4497
4494
|
return replayed;
|
|
4498
4495
|
throw usageError(`Run ${existing.id} is already terminal: ${existing.status}.`);
|
|
@@ -5,6 +5,7 @@ import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunSta
|
|
|
5
5
|
import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
6
6
|
import { classifyRuntimeHealth, projectRuntimeMailbox, projectRuntimeObservation, projectRuntimeTaskEvents, runtimeDisplayStatus } from "../runtime/runtimeProjection.js";
|
|
7
7
|
import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
|
|
8
|
+
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
8
9
|
import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
9
10
|
export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
|
|
10
11
|
const taskOpenInputRequestCount = store.listInputRequests(taskId)
|
|
@@ -397,7 +398,8 @@ function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, task
|
|
|
397
398
|
const classification = classifyRuntimeHealth({
|
|
398
399
|
projection,
|
|
399
400
|
semanticProgressAt: semanticProgress.progressAt,
|
|
400
|
-
now
|
|
401
|
+
now,
|
|
402
|
+
policy: resolveRuntimeHealth(store.getConfig().runtimeHealth)
|
|
401
403
|
});
|
|
402
404
|
return {
|
|
403
405
|
driverId,
|
|
@@ -2,14 +2,19 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { usageError } from "../errors/cliError.js";
|
|
4
4
|
import { STORAGE_SCHEMA_FILE } from "../storage/storageSchema.js";
|
|
5
|
+
import { CURRENT_CONFIG_SCHEMA_VERSION } from "../storage/taskStore.js";
|
|
5
6
|
import { openTaskStore, SqliteTaskStore } from "../storage/sqliteStore.js";
|
|
6
7
|
import { COMMITTED_DATABASE_FILENAME } from "../storage/upgrade/sqliteStateMigration.js";
|
|
7
|
-
import { DEFAULT_RUN_CAP, DEFAULT_TERMINAL_KEEP, resolveRunCap,
|
|
8
|
+
import { DEFAULT_RUN_CAP, DEFAULT_TERMINAL_KEEP, resolveRunCap, resolveTerminalKeep } from "../telemetry/telemetryConfig.js";
|
|
9
|
+
import { resolveTelemetryEnabled } from "../config/yuiConfig.js";
|
|
8
10
|
import { applyTelemetryCompaction, planTelemetryCompaction } from "../telemetry/telemetryCompaction.js";
|
|
9
11
|
import { SqliteTelemetryStore } from "../telemetry/sqliteTelemetryStore.js";
|
|
10
12
|
/** Read the durable config, falling back to defaults when no store is available. */
|
|
11
13
|
function storeConfig(options) {
|
|
12
|
-
return options.store?.getConfig() ?? { schemaVersion:
|
|
14
|
+
return options.store?.getConfig() ?? { schemaVersion: CURRENT_CONFIG_SCHEMA_VERSION };
|
|
15
|
+
}
|
|
16
|
+
function telemetryMode(config) {
|
|
17
|
+
return resolveTelemetryEnabled(config.telemetryEnabled) ? "on" : "off";
|
|
13
18
|
}
|
|
14
19
|
export async function runTelemetryCommand(args, options) {
|
|
15
20
|
const [command, ...rest] = args;
|
|
@@ -31,7 +36,7 @@ export async function runTelemetryCommand(args, options) {
|
|
|
31
36
|
// -- status ---------------------------------------------------------------------
|
|
32
37
|
function telemetryStatus(args, options) {
|
|
33
38
|
const flags = parseFlags(args, new Set([]));
|
|
34
|
-
const mode =
|
|
39
|
+
const mode = telemetryMode(storeConfig(options));
|
|
35
40
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
36
41
|
mode,
|
|
37
42
|
terminalKeep: resolveTerminalKeep(storeConfig(options).telemetryTerminalKeep),
|
|
@@ -90,7 +95,7 @@ function telemetryPrune(args, options) {
|
|
|
90
95
|
const cap = resolveRunCap(storeConfig(options).telemetryRunCap);
|
|
91
96
|
const store = requireStore(options);
|
|
92
97
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
93
|
-
mode:
|
|
98
|
+
mode: telemetryMode(storeConfig(options)),
|
|
94
99
|
terminalKeep: keep,
|
|
95
100
|
runCap: cap
|
|
96
101
|
});
|
|
@@ -196,7 +201,7 @@ function telemetryCompact(args, options) {
|
|
|
196
201
|
copyStoreFiles(resolvedFrom, resolvedStaged);
|
|
197
202
|
const store = openTaskStore(resolvedStaged, backend);
|
|
198
203
|
const telemetry = new SqliteTelemetryStore(resolvedStaged, {
|
|
199
|
-
mode: "
|
|
204
|
+
mode: "on",
|
|
200
205
|
terminalKeep: keep,
|
|
201
206
|
runCap: DEFAULT_RUN_CAP
|
|
202
207
|
});
|
|
@@ -240,7 +245,7 @@ function telemetryRead(args, options) {
|
|
|
240
245
|
const limit = integerOption(args, "--limit", 100);
|
|
241
246
|
const offset = integerOption(args, "--offset", 0);
|
|
242
247
|
const telemetry = new SqliteTelemetryStore(options.home, {
|
|
243
|
-
mode:
|
|
248
|
+
mode: telemetryMode(storeConfig(options)),
|
|
244
249
|
terminalKeep: resolveTerminalKeep(storeConfig(options).telemetryTerminalKeep),
|
|
245
250
|
runCap: resolveRunCap(storeConfig(options).telemetryRunCap)
|
|
246
251
|
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { supportedAgentAdapterIds } from "../agent/adapterCatalog.js";
|
|
2
|
+
import { MAX_RUN_CAP } from "../telemetry/telemetryConfig.js";
|
|
3
|
+
import { MAX_CONTEXT_BUDGET_TOKENS, MAX_PROVIDER_RETRY_ATTEMPTS, MIN_CONTEXT_BUDGET_TOKENS } from "./yuiConfig.js";
|
|
4
|
+
export const CONFIG_DOMAINS = ["system", "runtime", "workflow", "resources", "tools"];
|
|
5
|
+
/**
|
|
6
|
+
* Public durable configuration contract. CLI help, completion, config show,
|
|
7
|
+
* config describe, and Operator guidance all project from this one catalog.
|
|
8
|
+
* Runtime-only implementation constants deliberately do not appear here.
|
|
9
|
+
*/
|
|
10
|
+
export const CONFIG_DEFINITIONS = Object.freeze([
|
|
11
|
+
{ key: "default-agent", domain: "system", property: "defaultAgent", label: "Default Agent", summary: "Configured Agent used when a Task or Role does not select one explicitly.", takesEffect: "Future Role and Task defaults; existing bindings are unchanged." },
|
|
12
|
+
{ key: "default-workspace", domain: "system", property: "defaultWorkspace", label: "Default workspace", summary: "Absolute workspace root outside YUI_HOME used when no workspace is selected.", takesEffect: "Future workspace selection; existing Roles and worktrees are unchanged." },
|
|
13
|
+
{ key: "time-zone", domain: "system", property: "timeZone", label: "Time zone", summary: "IANA timezone for human-facing timestamps (default: Asia/Shanghai).", takesEffect: "The next human-readable timestamp rendering; durable JSON remains UTC." },
|
|
14
|
+
{ key: "reconciliation-interval-seconds", domain: "runtime", property: "reconciliationIntervalSeconds", label: "Reconciliation interval", summary: "Recovery reconciliation interval, 5-300 seconds (default: 120).", takesEffect: "The running Controller is refreshed after the value is saved." },
|
|
15
|
+
{ key: "controller-task-concurrency", domain: "runtime", property: "controllerTaskConcurrency", label: "Controller Task concurrency", summary: "Maximum Tasks reconciled concurrently, 1-32 (default: 4).", takesEffect: "After the Controller restarts." },
|
|
16
|
+
{ key: "runtime-health", domain: "runtime", property: "runtimeHealth", label: "Runtime health thresholds", summary: "Quiet, diagnostic, and semantic-stall thresholds in seconds; values must be strictly increasing (defaults: 300/600/1800).", takesEffect: "CLI and Web projections immediately; scheduler stall handling after the Controller restarts." },
|
|
17
|
+
{ key: "agent-launch-inactivity-timeout-seconds", domain: "runtime", property: "agentLaunchInactivityTimeoutSeconds", label: "Agent launch inactivity timeout", summary: "Maximum launch silence before startup fails, 15-3600 seconds (default: 300).", takesEffect: "After the Controller restarts." },
|
|
18
|
+
{ key: "delivery-timeout-seconds", domain: "runtime", property: "deliveryTimeoutSeconds", label: "Delivery timeout", summary: "Total control-plane delivery retry budget, 5-600 seconds (default: 120).", takesEffect: "After the Controller restarts; internal retry cadence remains automatic." },
|
|
19
|
+
{ key: "provider-retry-mode", domain: "runtime", property: "providerRetryMode", label: "Provider retry mode", summary: "Provider retry mode: off, shadow, or enforce (default: enforce).", takesEffect: "The next eligible provider-failure decision." },
|
|
20
|
+
{ key: "provider-retry-adapters", domain: "runtime", property: "providerRetryAdapters", label: "Provider retry adapters", summary: `Adapters with in-place retry: all, off, or a comma-separated subset of ${supportedAgentAdapterIds().join(", ")} (default: all).`, takesEffect: "The next eligible provider-failure decision." },
|
|
21
|
+
{ key: "provider-retry-delays-seconds", domain: "runtime", property: "providerRetryDelaysSeconds", label: "Provider retry delays", summary: `Ordered comma-separated retry delays of 1-600 seconds, with 1-${MAX_PROVIDER_RETRY_ATTEMPTS} attempts (default: 2,5,15).`, takesEffect: "The next eligible provider-failure decision; attempt count is the list length." },
|
|
22
|
+
{ key: "provider-retry-max-window-seconds", domain: "runtime", property: "providerRetryMaxWindowSeconds", label: "Provider retry max window", summary: "Positive-integer total retry budget per Run lineage in seconds (default: 600).", takesEffect: "The next retry episode; an active episode keeps its existing deadline." },
|
|
23
|
+
{ key: "leader-next-action", domain: "workflow", property: "leaderNextActionMode", label: "Leader next-action mode", summary: "Leader next-action mode: display, warn, or enforce (default: display).", takesEffect: "The next Leader next-action projection or gate." },
|
|
24
|
+
{ key: "leader-semantic-budget-turns", domain: "workflow", property: "leaderSemanticBudgetTurns", label: "Leader semantic budget", summary: "Consecutive yielded Leader turns without durable delivery progress before warning, 1-20 (default: 3).", takesEffect: "The next Leader delivery guard evaluation." },
|
|
25
|
+
{ key: "context-budget", domain: "workflow", property: "contextBudget", label: "Context budget", summary: `Per-Session context token budget; soft and hard are ${MIN_CONTEXT_BUDGET_TOKENS}-${MAX_CONTEXT_BUDGET_TOKENS}, with soft below hard (defaults: 100000/120000).`, takesEffect: "The next context-budget evaluation; retained conversation content is unchanged." },
|
|
26
|
+
{ key: "review", domain: "workflow", property: "review", label: "Review", summary: "Optional global WorkItem review rule: a configured Role plus always, leader, or final trigger; optional finding-ledger and delta-recheck controls. Setup leaves it disabled so the Leader may review directly or delegate selectively.", takesEffect: "The next Candidate snapshot; in-flight Candidates and ReviewRounds keep their policy." },
|
|
27
|
+
{ key: "resources-gc-mode", domain: "resources", property: "resourcesGcMode", label: "Resources GC mode", summary: "Resource GC mode: report or quarantine (default: report).", takesEffect: "The next resource GC operation." },
|
|
28
|
+
{ key: "resources-gc-auto-quarantine", domain: "resources", property: "resourcesGcAutoQuarantine", label: "Resources GC auto-quarantine", summary: "Automatically quarantine eligible terminal Task resources (default: false).", takesEffect: "The next eligible automatic resource GC operation." },
|
|
29
|
+
{ key: "resources-quarantine-ttl-hours", domain: "resources", property: "resourcesQuarantineTtlHours", label: "Resource quarantine TTL", summary: "Default observation window before purge, 1-720 hours (default: 24).", takesEffect: "The next resource GC command without an explicit TTL override." },
|
|
30
|
+
{ key: "tmux-bin", domain: "tools", property: "tmuxBin", label: "Tmux bin", summary: "Command or path to the tmux binary (default: tmux).", takesEffect: "The next CLI-owned tmux invocation; restart the Controller for Controller-owned launches." },
|
|
31
|
+
{ key: "tmux-history-limit", domain: "tools", property: "tmuxHistoryLimit", label: "Tmux history limit", summary: "History lines retained for newly created tmux sessions, 1000-1000000 (default: 100000).", takesEffect: "New tmux sessions; existing sessions report drift until recreated." },
|
|
32
|
+
{ key: "telemetry-enabled", domain: "tools", property: "telemetryEnabled", label: "Diagnostic telemetry", summary: "Enable the optional SQLite diagnostic telemetry projection (default: false); requires a SQLite Home.", takesEffect: "Standalone telemetry commands immediately; scheduler writes after Controller restart." },
|
|
33
|
+
{ key: "telemetry-terminal-keep", domain: "tools", property: "telemetryTerminalKeep", label: "Telemetry terminal keep", summary: "Positive-integer rows retained per terminal Run generation (default: 200).", takesEffect: "Explicit retention immediately; automatic retention after the Controller restarts." },
|
|
34
|
+
{ key: "telemetry-run-cap", domain: "tools", property: "telemetryRunCap", label: "Telemetry Run cap", summary: `Positive-integer maximum rows retained per Run, at most ${MAX_RUN_CAP} (default: 50000).`, takesEffect: "Explicit retention immediately; automatic write-time capping after the Controller restarts." }
|
|
35
|
+
]);
|
|
36
|
+
export const CONFIG_KEYS = Object.freeze(CONFIG_DEFINITIONS.map(({ key }) => key));
|
|
37
|
+
export function configDefinitionsForDomain(domain) {
|
|
38
|
+
return CONFIG_DEFINITIONS.filter((definition) => definition.domain === domain);
|
|
39
|
+
}
|
|
40
|
+
export function configDefinition(key) {
|
|
41
|
+
return CONFIG_DEFINITIONS.find((definition) => definition.key === key);
|
|
42
|
+
}
|
package/dist/config/yuiConfig.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { supportedAgentAdapterIds } from "../agent/adapterCatalog.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { DEFAULT_RUN_CAP, DEFAULT_TERMINAL_KEEP, MAX_RUN_CAP } from "../telemetry/telemetryConfig.js";
|
|
3
|
+
import { DEFAULT_RUNTIME_HEALTH_POLICY } from "../runtime/runtimeHealthPolicy.js";
|
|
4
4
|
export const DEFAULT_RECONCILIATION_INTERVAL_SECONDS = 120;
|
|
5
5
|
export const MIN_RECONCILIATION_INTERVAL_SECONDS = 5;
|
|
6
6
|
export const MAX_RECONCILIATION_INTERVAL_SECONDS = 300;
|
|
7
7
|
export const DEFAULT_RESOURCES_GC_MODE = "report";
|
|
8
|
+
export const DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS = 24;
|
|
9
|
+
export const MIN_RESOURCES_QUARANTINE_TTL_HOURS = 1;
|
|
10
|
+
export const MAX_RESOURCES_QUARANTINE_TTL_HOURS = 720;
|
|
8
11
|
/**
|
|
9
12
|
* Resolves the Resource GC mode. `report` (default) only reports candidates;
|
|
10
13
|
* `quarantine` allows `yui resources gc --apply` to quarantine releasable
|
|
@@ -29,6 +32,9 @@ export function resolveResourcesGcAutoQuarantine(value) {
|
|
|
29
32
|
return value;
|
|
30
33
|
throw new TypeError("resourcesGcAutoQuarantine must be a boolean.");
|
|
31
34
|
}
|
|
35
|
+
export function resolveResourcesQuarantineTtlHours(value) {
|
|
36
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, MIN_RESOURCES_QUARANTINE_TTL_HOURS, MAX_RESOURCES_QUARANTINE_TTL_HOURS, "resourcesQuarantineTtlHours");
|
|
37
|
+
}
|
|
32
38
|
/**
|
|
33
39
|
* Resolves the durable Yui setting used for low-frequency recovery
|
|
34
40
|
* reconciliation. Normal durable state changes wake the Controller through
|
|
@@ -72,6 +78,9 @@ export function resolveLeaderNextActionMode(value) {
|
|
|
72
78
|
// ── Issue 01: Provider retry ──────────────────────────────────────────────
|
|
73
79
|
export const PROVIDER_RETRY_MODES = ["off", "shadow", "enforce"];
|
|
74
80
|
export const DEFAULT_PROVIDER_RETRY_MODE = "enforce";
|
|
81
|
+
export const DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS = Object.freeze([2, 5, 15]);
|
|
82
|
+
export const DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS = 600;
|
|
83
|
+
export const MAX_PROVIDER_RETRY_ATTEMPTS = 10;
|
|
75
84
|
export function resolveProviderRetryMode(value) {
|
|
76
85
|
if (value === undefined || value === null)
|
|
77
86
|
return DEFAULT_PROVIDER_RETRY_MODE;
|
|
@@ -122,21 +131,27 @@ export function resolveProviderRetryAdapters(value) {
|
|
|
122
131
|
}
|
|
123
132
|
return adapters;
|
|
124
133
|
}
|
|
125
|
-
export function
|
|
134
|
+
export function resolveProviderRetryDelaysSeconds(value) {
|
|
126
135
|
if (value === undefined || value === null)
|
|
127
|
-
return
|
|
128
|
-
if (
|
|
129
|
-
throw new TypeError(
|
|
136
|
+
return [...DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS];
|
|
137
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > MAX_PROVIDER_RETRY_ATTEMPTS) {
|
|
138
|
+
throw new TypeError(`providerRetryDelaysSeconds must contain 1-${MAX_PROVIDER_RETRY_ATTEMPTS} positive integers.`);
|
|
130
139
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
const delays = value.map((entry) => {
|
|
141
|
+
if (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 1 || entry > 600) {
|
|
142
|
+
throw new TypeError("providerRetryDelaysSeconds entries must be integers from 1 to 600.");
|
|
143
|
+
}
|
|
144
|
+
return entry;
|
|
145
|
+
});
|
|
146
|
+
for (let index = 1; index < delays.length; index += 1) {
|
|
147
|
+
if (delays[index] < delays[index - 1]) {
|
|
148
|
+
throw new TypeError("providerRetryDelaysSeconds must be ordered from shortest to longest.");
|
|
149
|
+
}
|
|
138
150
|
}
|
|
139
|
-
return
|
|
151
|
+
return delays;
|
|
152
|
+
}
|
|
153
|
+
export function resolveProviderRetryMaxWindowSeconds(value) {
|
|
154
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS, 1, Number.MAX_SAFE_INTEGER, "providerRetryMaxWindowSeconds");
|
|
140
155
|
}
|
|
141
156
|
// ── Executable paths ──────────────────────────────────────────────────────
|
|
142
157
|
export function resolveTmuxBin(value) {
|
|
@@ -147,29 +162,13 @@ export function resolveTmuxBin(value) {
|
|
|
147
162
|
}
|
|
148
163
|
return value.trim();
|
|
149
164
|
}
|
|
150
|
-
export function resolveGitBin(value) {
|
|
151
|
-
if (value === undefined || value === null)
|
|
152
|
-
return "git";
|
|
153
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
154
|
-
throw new TypeError("gitBin must be a non-empty string.");
|
|
155
|
-
}
|
|
156
|
-
return value.trim();
|
|
157
|
-
}
|
|
158
165
|
// ── Telemetry ─────────────────────────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
export function resolveTelemetryMode(value) {
|
|
166
|
+
export function resolveTelemetryEnabled(value) {
|
|
161
167
|
if (value === undefined || value === null)
|
|
162
|
-
return
|
|
163
|
-
if (typeof value !== "
|
|
164
|
-
throw new TypeError("
|
|
165
|
-
|
|
166
|
-
const normalized = value.trim().toLowerCase();
|
|
167
|
-
if (normalized.length === 0)
|
|
168
|
-
return DEFAULT_TELEMETRY_MODE;
|
|
169
|
-
if (!TELEMETRY_MODES.includes(normalized)) {
|
|
170
|
-
throw new TypeError(`telemetryMode must be one of ${TELEMETRY_MODES.join(", ")}; got ${JSON.stringify(normalized)}.`);
|
|
171
|
-
}
|
|
172
|
-
return normalized;
|
|
168
|
+
return false;
|
|
169
|
+
if (typeof value !== "boolean")
|
|
170
|
+
throw new TypeError("telemetryEnabled must be a boolean.");
|
|
171
|
+
return value;
|
|
173
172
|
}
|
|
174
173
|
export function resolveTelemetryTerminalKeep(value) {
|
|
175
174
|
if (value === undefined || value === null)
|
|
@@ -190,6 +189,43 @@ export function resolveTelemetryRunCap(value) {
|
|
|
190
189
|
}
|
|
191
190
|
return value;
|
|
192
191
|
}
|
|
192
|
+
// ── Runtime and workflow policy ───────────────────────────────────────────
|
|
193
|
+
export const DEFAULT_CONTROLLER_TASK_CONCURRENCY = 4;
|
|
194
|
+
export const MAX_CONTROLLER_TASK_CONCURRENCY = 32;
|
|
195
|
+
export const DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS = 300;
|
|
196
|
+
export const DEFAULT_DELIVERY_TIMEOUT_SECONDS = 120;
|
|
197
|
+
export const DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS = 3;
|
|
198
|
+
export const DEFAULT_TMUX_HISTORY_LIMIT = 100_000;
|
|
199
|
+
export function resolveRuntimeHealth(configured) {
|
|
200
|
+
if (configured === undefined || configured === null)
|
|
201
|
+
return DEFAULT_RUNTIME_HEALTH_POLICY;
|
|
202
|
+
if (typeof configured !== "object" || Array.isArray(configured)) {
|
|
203
|
+
throw new TypeError("runtimeHealth must be an object.");
|
|
204
|
+
}
|
|
205
|
+
const value = configured;
|
|
206
|
+
const quietAfterMs = resolveBoundedPositiveInteger(value.quietAfterSeconds, DEFAULT_RUNTIME_HEALTH_POLICY.quietAfterMs / 1_000, 30, 86_400, "runtimeHealth.quietAfterSeconds") * 1_000;
|
|
207
|
+
const diagnosticAfterMs = resolveBoundedPositiveInteger(value.diagnosticAfterSeconds, DEFAULT_RUNTIME_HEALTH_POLICY.diagnosticAfterMs / 1_000, 30, 86_400, "runtimeHealth.diagnosticAfterSeconds") * 1_000;
|
|
208
|
+
const stallWindowMs = resolveBoundedPositiveInteger(value.stallAfterSeconds, DEFAULT_RUNTIME_HEALTH_POLICY.stallWindowMs / 1_000, 60, 604_800, "runtimeHealth.stallAfterSeconds") * 1_000;
|
|
209
|
+
if (!(quietAfterMs < diagnosticAfterMs && diagnosticAfterMs < stallWindowMs)) {
|
|
210
|
+
throw new TypeError("runtimeHealth thresholds must be ordered quietAfterSeconds < diagnosticAfterSeconds < stallAfterSeconds.");
|
|
211
|
+
}
|
|
212
|
+
return Object.freeze({ quietAfterMs, diagnosticAfterMs, stallWindowMs });
|
|
213
|
+
}
|
|
214
|
+
export function resolveControllerTaskConcurrency(value) {
|
|
215
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_CONTROLLER_TASK_CONCURRENCY, 1, MAX_CONTROLLER_TASK_CONCURRENCY, "controllerTaskConcurrency");
|
|
216
|
+
}
|
|
217
|
+
export function resolveAgentLaunchInactivityTimeoutSeconds(value) {
|
|
218
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, 15, 3_600, "agentLaunchInactivityTimeoutSeconds");
|
|
219
|
+
}
|
|
220
|
+
export function resolveDeliveryTimeoutSeconds(value) {
|
|
221
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_DELIVERY_TIMEOUT_SECONDS, 5, 600, "deliveryTimeoutSeconds");
|
|
222
|
+
}
|
|
223
|
+
export function resolveLeaderSemanticBudgetTurns(value) {
|
|
224
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS, 1, 20, "leaderSemanticBudgetTurns");
|
|
225
|
+
}
|
|
226
|
+
export function resolveTmuxHistoryLimit(value) {
|
|
227
|
+
return resolveBoundedPositiveInteger(value, DEFAULT_TMUX_HISTORY_LIMIT, 1_000, 1_000_000, "tmuxHistoryLimit");
|
|
228
|
+
}
|
|
193
229
|
/**
|
|
194
230
|
* Issue 04 (context token budget): thresholds for one native Session
|
|
195
231
|
* generation's observed per-request input peak, measured in tokens. When the
|
|
@@ -231,3 +267,12 @@ export function resolveContextBudget(configured) {
|
|
|
231
267
|
}
|
|
232
268
|
return { softTokens, hardTokens };
|
|
233
269
|
}
|
|
270
|
+
function resolveBoundedPositiveInteger(value, fallback, minimum, maximum, label) {
|
|
271
|
+
if (value === undefined || value === null)
|
|
272
|
+
return fallback;
|
|
273
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)
|
|
274
|
+
|| value < minimum || value > maximum) {
|
|
275
|
+
throw new TypeError(`${label} must be an integer from ${minimum} to ${maximum}.`);
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
@@ -53,7 +53,7 @@ export function materializeSessionBootstrap(input) {
|
|
|
53
53
|
roleProfileRef: { digest: profileDigest, path: roleProfilePath },
|
|
54
54
|
contextProtocol: input.owner.scope === "global"
|
|
55
55
|
? {
|
|
56
|
-
loadCommand: `\"${sessionCliPath}\"
|
|
56
|
+
loadCommand: `\"${sessionCliPath}\" session context \"$YUI_ROLE\" --json`
|
|
57
57
|
}
|
|
58
58
|
: {
|
|
59
59
|
loadCommand: `\"${sessionCliPath}\" task run context \"$YUI_TASK_ID/<run-id>\" --json`,
|
|
@@ -29,8 +29,6 @@ const CONTROLLER_OPERATIONAL_ENVIRONMENT = [
|
|
|
29
29
|
// rr13/test: Forward the liveness seam so an integration test's Controller
|
|
30
30
|
// subprocess does not reap a saved active Leader Run without a real tmux role.
|
|
31
31
|
"YUI_TEST_ROLE_LIVENESS_PRESENT",
|
|
32
|
-
// Issue 02: agent-signal-driven launch monitoring is Controller-owned.
|
|
33
|
-
"YUI_LAUNCH_INACTIVITY_TIMEOUT_MS",
|
|
34
32
|
...EPHEMERAL_DOMAIN_ENVIRONMENT_NAMES
|
|
35
33
|
];
|
|
36
34
|
/**
|