@zq-silk/yui 0.7.1 → 0.8.2
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/ARCHITECTURE.md +27 -28
- package/README.md +79 -71
- package/dist/cli/commandCatalog.js +283 -136
- package/dist/cli/completion.js +3 -3
- package/dist/cli/helpRenderer.js +3 -0
- package/dist/cli/interactionPolicy.js +48 -33
- package/dist/cli/interactiveSelection.js +1 -1
- package/dist/cli/invocationRouter.js +3 -2
- package/dist/cli/roleWizard.js +8 -8
- package/dist/cli.js +189 -93
- 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 +111 -59
- 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 +409 -79
- package/dist/controller/resourceInventory.js +9 -5
- package/dist/controller/runtime.js +112 -25
- package/dist/controller/runtimeLaunchCoordinator.js +18 -78
- package/dist/controller/structuredProviderObservation.js +273 -0
- package/dist/doctor/doctor.js +2 -2
- package/dist/executor/agentAdapter.js +40 -0
- package/dist/executor/agentExecutor.js +31 -7
- package/dist/executor/executorRegistry.js +11 -49
- package/dist/executor/fileRoleLaunchPlanner.js +115 -37
- package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
- package/dist/resources/autoResourceGc.js +3 -1
- package/dist/review/reviewConfig.js +0 -2
- package/dist/run/agentRun.js +2 -2
- package/dist/run/providerRetry.js +29 -16
- package/dist/run/providerRetryConfig.js +5 -3
- package/dist/runtime/agentHost.js +767 -158
- package/dist/runtime/builtinAgentDrivers.js +1 -5
- package/dist/runtime/codexAppServerRuntime.js +67 -60
- package/dist/runtime/exactControlPlane.js +7 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/launchBroker.js +30 -8
- package/dist/runtime/launchDiagnostics.js +1 -1
- package/dist/runtime/providerAuthorityFence.js +24 -0
- package/dist/runtime/providerControl.js +63 -0
- package/dist/runtime/providerRecoveryDecision.js +55 -0
- package/dist/runtime/providerRuntimeIdentity.js +269 -19
- package/dist/runtime/runtimeBinding.js +20 -11
- package/dist/runtime/structuredProviderHost.js +476 -0
- package/dist/runtime/tmuxAdapters.js +143 -42
- package/dist/scheduler/activeRoleRunDelivery.js +206 -120
- package/dist/scheduler/leaderWakeupProcessor.js +141 -16
- 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 +169 -0
- package/dist/storage/taskStore.js +22 -3
- 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/tmux/tmuxManager.js +1 -1
- package/dist/web/webSnapshot.js +5 -3
- package/i18n/README.zh-CN.md +48 -40
- 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.");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
3
|
import { createRunAssignment } from "../context/runContextContract.js";
|
|
4
4
|
import { buildRunContextPack, buildRunContextDelta, contextSnapshotDeltaRefIds, expandRunContextRef, freezeRunContextSnapshot } from "../context/runContextPack.js";
|
|
@@ -7,7 +7,8 @@ import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageErr
|
|
|
7
7
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
8
8
|
import { clearMatchingLeaderStallAttention, isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
|
|
9
9
|
import { readCommandText } from "./textInput.js";
|
|
10
|
-
import { createRoleSessionSet, roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
|
|
10
|
+
import { createRoleSessionSet, roleAgentSessionResumeMode, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
|
|
11
|
+
import { currentProviderActivation, transferProviderAuthority } from "../runtime/providerRuntimeIdentity.js";
|
|
11
12
|
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
12
13
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
13
14
|
import { formatTimestamp } from "../output/timePresentation.js";
|
|
@@ -16,11 +17,10 @@ import { createTaskMessage, taskMessageAuthorLabel } from "../message/message.js
|
|
|
16
17
|
import { cancelInputRequest } from "../input/inputRequest.js";
|
|
17
18
|
import { recoverExactAgentRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
|
|
18
19
|
import { resetTaskRoleSessionGeneration } from "../lifecycle/taskRoleSessionReset.js";
|
|
19
|
-
import {
|
|
20
|
+
import { copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole, updateRoleStatus } from "../role/role.js";
|
|
20
21
|
import { agentRunDeliveryReceiptId, createAgentRun, withAgentRunContextSnapshot } from "../run/agentRun.js";
|
|
21
22
|
import { projectRunRecovery, readRunRecoveryFacts } from "../run/recoveryProjection.js";
|
|
22
23
|
import { matchYieldReceipt } from "../run/yieldReceipt.js";
|
|
23
|
-
import { providerRetryConfig } from "../run/providerRetryConfig.js";
|
|
24
24
|
import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, deltaRecheckBlocksAcceptance, finishReviewRound, parseReviewYieldReport, recordReviewWorkspaceDisposition, retryTaskReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
|
|
25
25
|
import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
|
|
26
26
|
import { buildTaskFinalReviewFindingContext, dispositionReviewFinding, planRepairGroups, reconcileReviewFindings, reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
|
|
@@ -241,7 +241,6 @@ export function runTaskCommand(args, store, options = {}) {
|
|
|
241
241
|
case "milestone": return taskMilestoneCommand(rest, store, options);
|
|
242
242
|
case "event": return taskEventCommand(rest, store);
|
|
243
243
|
case "continuation": return taskContinuationCommand(rest, store);
|
|
244
|
-
case "enter": return enterTaskRoleAlias(rest, store, options);
|
|
245
244
|
default:
|
|
246
245
|
throw usageError(command === undefined
|
|
247
246
|
? "Task command is required."
|
|
@@ -430,8 +429,8 @@ function updateTaskCommand(args, store, options) {
|
|
|
430
429
|
/** Compatibility helper for call sites that cannot yet handle foreground enter. */
|
|
431
430
|
export function runTaskOutputCommand(args, store, options = {}) {
|
|
432
431
|
const execution = runTaskCommand(args, store, options);
|
|
433
|
-
if (execution.kind
|
|
434
|
-
throw runtimeError("Task
|
|
432
|
+
if (execution.kind !== "output") {
|
|
433
|
+
throw runtimeError("Task Role foreground runtime control requires the CLI.");
|
|
435
434
|
}
|
|
436
435
|
return execution.output;
|
|
437
436
|
}
|
|
@@ -1193,8 +1192,12 @@ function taskRoleCommand(args, store, options) {
|
|
|
1193
1192
|
return output(unbindTaskRole(rest, store, options));
|
|
1194
1193
|
if (command === "reset")
|
|
1195
1194
|
return resetTaskRole(rest, store, options);
|
|
1196
|
-
if (command === "
|
|
1197
|
-
return
|
|
1195
|
+
if (command === "view")
|
|
1196
|
+
return viewTaskRole(rest, store);
|
|
1197
|
+
if (command === "takeover")
|
|
1198
|
+
return transferTaskRoleAuthority(rest, store, options, "takeover");
|
|
1199
|
+
if (command === "release")
|
|
1200
|
+
return transferTaskRoleAuthority(rest, store, options, "release");
|
|
1198
1201
|
throw usageError(command === undefined
|
|
1199
1202
|
? "Task role command is required."
|
|
1200
1203
|
: `Unknown command: task role ${command}`);
|
|
@@ -1507,60 +1510,111 @@ function unbindTaskRole(args, store, options) {
|
|
|
1507
1510
|
});
|
|
1508
1511
|
return `Unbound Agent ${args[2]} from ${result.taskId}/${result.name}\n`;
|
|
1509
1512
|
}
|
|
1510
|
-
function
|
|
1511
|
-
const usage = "Task role
|
|
1512
|
-
|
|
1513
|
-
const
|
|
1514
|
-
exactPositionals(parsed.positionals, 2, usage);
|
|
1515
|
-
if (parsed.options.has("--read-only") && parsed.options.has("--read-write")) {
|
|
1516
|
-
throw usageError("--read-only and --read-write are mutually exclusive.", usage);
|
|
1517
|
-
}
|
|
1518
|
-
const task = requireTask(store, parsed.positionals[0]);
|
|
1513
|
+
function viewTaskRole(args, store) {
|
|
1514
|
+
const usage = "Task role view usage: yui task role view <task> <role>.";
|
|
1515
|
+
exactPositionals(args, 2, usage);
|
|
1516
|
+
const task = requireTask(store, args[0]);
|
|
1519
1517
|
if (task.status !== "active") {
|
|
1520
|
-
throw usageError(inactiveTaskMessage(task, "
|
|
1518
|
+
throw usageError(inactiveTaskMessage(task, "viewing a role session"));
|
|
1521
1519
|
}
|
|
1522
|
-
const role = requireRole(store, task.id,
|
|
1523
|
-
const
|
|
1524
|
-
if (
|
|
1525
|
-
|
|
1520
|
+
const role = requireRole(store, task.id, args[1]);
|
|
1521
|
+
const session = store.getRoleSession(task.id, role.name);
|
|
1522
|
+
if (session === null || session.status === "stopped" || session.status === "broken") {
|
|
1523
|
+
throw usageError(`Task Role has no live Provider view: ${task.id}/${role.name}.`);
|
|
1526
1524
|
}
|
|
1527
1525
|
return {
|
|
1528
|
-
kind: "
|
|
1526
|
+
kind: "view",
|
|
1529
1527
|
taskId: task.id,
|
|
1530
1528
|
roleName: role.name,
|
|
1531
|
-
access,
|
|
1532
|
-
output: `
|
|
1529
|
+
access: "read-only",
|
|
1530
|
+
output: `Viewing ${role.name} for ${task.id} (read-only)\n`
|
|
1533
1531
|
};
|
|
1534
1532
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1533
|
+
function transferTaskRoleAuthority(args, store, options, action) {
|
|
1534
|
+
const usage = `Task role ${action} usage: yui task role ${action} <task> <role>.`;
|
|
1535
|
+
exactPositionals(args, 2, usage);
|
|
1536
|
+
const now = clock(options);
|
|
1537
|
+
try {
|
|
1538
|
+
return store.transaction((tx) => {
|
|
1539
|
+
const task = requireTask(tx, args[0]);
|
|
1540
|
+
if (task.status !== "active") {
|
|
1541
|
+
throw usageError(inactiveTaskMessage(task, `${action} Provider authority`));
|
|
1542
|
+
}
|
|
1543
|
+
const role = requireRole(tx, task.id, args[1]);
|
|
1544
|
+
const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
|
|
1545
|
+
const session = sessions?.sessions[role.activeAgentId];
|
|
1546
|
+
const binding = sessions?.providerBinding;
|
|
1547
|
+
if (sessions === null || sessions === undefined || session === undefined
|
|
1548
|
+
|| binding === null || binding === undefined
|
|
1549
|
+
|| session.launchId === undefined
|
|
1550
|
+
|| session.status === "stopped" || session.status === "broken") {
|
|
1551
|
+
throw new Error(`Task Role has no live managed Provider: ${task.id}/${role.name}.`);
|
|
1552
|
+
}
|
|
1553
|
+
const activation = currentProviderActivation(binding);
|
|
1554
|
+
if (activation === null) {
|
|
1555
|
+
throw new Error(`Provider Activation is not live: ${task.id}/${role.name}.`);
|
|
1556
|
+
}
|
|
1557
|
+
if (action === "takeover") {
|
|
1558
|
+
const activeRun = tx.getActiveAgentRun(task.id, role.name);
|
|
1559
|
+
if (sessions.inFlight === null || activeRun?.id !== sessions.inFlight.runId) {
|
|
1560
|
+
throw new Error(`Task Role has no active managed Run for takeover: ${task.id}/${role.name}.`);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
if (action === "takeover"
|
|
1564
|
+
&& binding.authority.owner !== "controller"
|
|
1565
|
+
&& binding.authority.owner !== "human") {
|
|
1566
|
+
throw new Error(`Provider authority is not Controller-owned: ${task.id}/${role.name}.`);
|
|
1567
|
+
}
|
|
1568
|
+
if (action === "release"
|
|
1569
|
+
&& binding.authority.owner !== "human"
|
|
1570
|
+
&& binding.authority.owner !== "controller") {
|
|
1571
|
+
throw new Error(`Provider authority is not human-owned: ${task.id}/${role.name}.`);
|
|
1572
|
+
}
|
|
1573
|
+
const desiredOwner = action === "takeover" ? "human" : "controller";
|
|
1574
|
+
const unchanged = binding.authority.owner === desiredOwner;
|
|
1575
|
+
const updatedBinding = unchanged
|
|
1576
|
+
? binding
|
|
1577
|
+
: transferProviderAuthority(binding, {
|
|
1578
|
+
expectedEpoch: binding.authority.epoch,
|
|
1579
|
+
expectedOwner: binding.authority.owner,
|
|
1580
|
+
owner: desiredOwner,
|
|
1581
|
+
holderId: action === "takeover" ? `human:${randomUUID()}` : activation.activationId,
|
|
1582
|
+
changedAt: now.toISOString()
|
|
1583
|
+
});
|
|
1584
|
+
const authority = updatedBinding.authority;
|
|
1585
|
+
if (authority.owner !== "controller" && authority.owner !== "human") {
|
|
1586
|
+
throw new Error("Provider authority transfer did not produce a writer.");
|
|
1587
|
+
}
|
|
1588
|
+
if (!unchanged) {
|
|
1589
|
+
tx.saveTaskRoleSessionSet(updateTaskRoleProviderRuntime(sessions, updatedBinding, now));
|
|
1590
|
+
recordTaskEvent(tx, task.id, "runtime.provider-authority-transferred", {
|
|
1591
|
+
role: role.name,
|
|
1592
|
+
owner: authority.owner,
|
|
1593
|
+
holderId: authority.holderId,
|
|
1594
|
+
epoch: String(authority.epoch)
|
|
1595
|
+
}, now);
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
kind: "authority",
|
|
1599
|
+
action,
|
|
1600
|
+
taskId: task.id,
|
|
1601
|
+
roleName: role.name,
|
|
1602
|
+
launchId: session.launchId,
|
|
1603
|
+
nativeSessionId: session.nativeSessionId,
|
|
1604
|
+
authority: {
|
|
1605
|
+
epoch: authority.epoch,
|
|
1606
|
+
owner: authority.owner,
|
|
1607
|
+
holderId: authority.holderId
|
|
1608
|
+
},
|
|
1609
|
+
output: action === "takeover"
|
|
1610
|
+
? `Human authority ${unchanged ? "replayed" : "acquired"} for ${task.id}/${role.name} at epoch ${authority.epoch}.\n`
|
|
1611
|
+
: `Controller authority ${unchanged ? "replayed" : "restored"} for ${task.id}/${role.name} at epoch ${authority.epoch}.\n`
|
|
1612
|
+
};
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
catch (error) {
|
|
1616
|
+
throw usageError(messageOf(error), usage);
|
|
1557
1617
|
}
|
|
1558
|
-
return enterTaskRole([
|
|
1559
|
-
parsed.positionals[0],
|
|
1560
|
-
parsed.positionals[1] ?? LEADER_ROLE,
|
|
1561
|
-
...(parsed.options.has("--read-only") ? ["--read-only"] : []),
|
|
1562
|
-
...(parsed.options.has("--read-write") ? ["--read-write"] : [])
|
|
1563
|
-
], store, options);
|
|
1564
1618
|
}
|
|
1565
1619
|
function taskWorkCommand(args, store, options) {
|
|
1566
1620
|
const [command, ...rest] = args;
|
|
@@ -2840,7 +2894,7 @@ function requestTaskReviewRound(args, store, options) {
|
|
|
2840
2894
|
const reviewConfig = tx.getReviewConfig();
|
|
2841
2895
|
if (reviewConfig === null || reviewConfig.deltaRecheck !== "enabled") {
|
|
2842
2896
|
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` "
|
|
2897
|
+
+ "Set `yui config workflow set review --role <role> --trigger final --delta-recheck enabled` "
|
|
2844
2898
|
+ "or request a full Review.");
|
|
2845
2899
|
}
|
|
2846
2900
|
if (taskFinalContract !== undefined) {
|
|
@@ -4434,9 +4488,7 @@ function buildYieldOutcome(run, inputSummary, options) {
|
|
|
4434
4488
|
* for the same outcome, fails closed for a different outcome, or returns
|
|
4435
4489
|
* `null` to keep the legacy "already terminal" behavior.
|
|
4436
4490
|
*/
|
|
4437
|
-
function replayYieldReceipt(run, inputSummary, options
|
|
4438
|
-
if (!retryConfig.yieldReceiptReplay)
|
|
4439
|
-
return null;
|
|
4491
|
+
function replayYieldReceipt(run, inputSummary, options) {
|
|
4440
4492
|
if (run.yieldReceipt === undefined)
|
|
4441
4493
|
return null;
|
|
4442
4494
|
const outcome = buildYieldOutcome(run, inputSummary, options);
|
|
@@ -4492,7 +4544,7 @@ function yieldRun(args, store, options) {
|
|
|
4492
4544
|
// transaction; the receipt is immutable once committed.
|
|
4493
4545
|
const existing = requireRun(store, parsed.positionals[0], options);
|
|
4494
4546
|
if (existing.status !== "active") {
|
|
4495
|
-
const replayed = replayYieldReceipt(existing, inputSummary, options
|
|
4547
|
+
const replayed = replayYieldReceipt(existing, inputSummary, options);
|
|
4496
4548
|
if (replayed !== null)
|
|
4497
4549
|
return replayed;
|
|
4498
4550
|
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
|
+
}
|