@zq-silk/yui 2.16.0 → 2.18.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/dist/cli/commandCatalog.js +19 -12
- package/dist/cli/interactionCandidates.js +20 -5
- package/dist/cli/interactionPolicy.js +5 -4
- package/dist/cli/roleWizard.js +7 -0
- package/dist/commands/controllerCommands.js +13 -4
- package/dist/commands/durableJobCommands.js +50 -3
- package/dist/commands/globalRoleCommands.js +31 -14
- package/dist/commands/inboxCommands.js +1 -0
- package/dist/commands/operatorCommands.js +12 -2
- package/dist/commands/profileCommands.js +19 -2
- package/dist/commands/roleConfiguration.js +3 -0
- package/dist/commands/taskCommands.js +16 -0
- package/dist/commands/taskRoleRuntimeStatus.js +3 -0
- package/dist/controlPlaneCli.js +21 -4
- package/dist/controller/agentJobControl.js +9 -1
- package/dist/controller/clientRuntime.js +34 -13
- package/dist/controller/controller.js +35 -22
- package/dist/controller/diagnosticJournal.js +214 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
- package/dist/controller/globalInputDelivery.js +19 -9
- package/dist/controller/globalRuntimeAttention.js +11 -3
- package/dist/controller/jobControl.js +1 -1
- package/dist/controller/jobDefinitionControl.js +24 -13
- package/dist/controller/jobSupervisor.js +4 -0
- package/dist/controller/providerRetryAdmission.js +9 -0
- package/dist/controller/providerRetryDelivery.js +16 -0
- package/dist/controller/runtime.js +80 -4
- package/dist/controller/runtimeLaunchCoordinator.js +3 -1
- package/dist/controller/workerSchedulerStore.js +24 -0
- package/dist/core/boundedRpc.js +4 -0
- package/dist/core/controllerServer.js +1 -1
- package/dist/core/controllerTelemetry.js +92 -19
- package/dist/executor/fileRoleLaunchPlanner.js +3 -0
- package/dist/job/globalShell.js +51 -0
- package/dist/job/jobBusinessResult.js +4 -3
- package/dist/job/jobDefinition.js +6 -1
- package/dist/job/jobRunner.js +9 -0
- package/dist/job/scheduleService.js +2 -0
- package/dist/message/globalInboxRecovery.js +30 -19
- package/dist/message/globalProviderRetry.js +7 -1
- package/dist/message/messageContinuation.js +4 -0
- package/dist/output/rolePresentation.js +3 -0
- package/dist/profile/agentProfile.js +8 -0
- package/dist/profile/agentProfileRuntime.js +16 -1
- package/dist/review/reviewDecision.js +6 -0
- package/dist/role/availability.js +28 -0
- package/dist/role/role.js +6 -0
- package/dist/runtime/builtinAgentErrorMappers.js +4 -0
- package/dist/runtime/codexNativeAccess.js +21 -4
- package/dist/runtime/globalSessionHealth.js +39 -0
- package/dist/runtime/tmuxAdapters.js +5 -0
- package/dist/scheduler/activeRoleRunDelivery.js +9 -0
- package/dist/scheduler/leaderWakeupProcessor.js +8 -1
- package/dist/scheduler/roleRunLiveness.js +3 -0
- package/dist/scheduler/roleRunStall.js +10 -10
- package/dist/storage/persistenceWorker.js +41 -19
- package/dist/storage/sqliteSchema.js +43 -9
- package/dist/storage/sqliteStore.js +86 -8
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +25 -11
- package/dist/storage/taskReadRevisionSchema.js +39 -0
- package/dist/web/assets/client/app/catalog.js +0 -29
- package/dist/web/assets/client/app/jobs.js +115 -0
- package/dist/web/assets/client/app/main.js +32 -28
- package/dist/web/assets/client/app/manage.js +1 -0
- package/dist/web/assets/client/app/settings.js +1 -5
- package/dist/web/assets/client/app/taskView.js +7 -11
- package/dist/web/assets/client/i18n/en.js +265 -36
- package/dist/web/assets/client/i18n/zh-CN.js +264 -35
- package/dist/web/assets/client/index.js +4 -0
- package/dist/web/assets/client/layout/manageShell.js +61 -15
- package/dist/web/assets/client/layout/router.js +2 -1
- package/dist/web/assets/client/layout/shortcuts.js +2 -2
- package/dist/web/assets/client/layout/workspace.js +99 -112
- package/dist/web/assets/client/lib/prefs.js +1 -1
- package/dist/web/assets/client/ui/primitives.js +15 -2
- package/dist/web/assets/client/views/dock/conversation.js +421 -123
- package/dist/web/assets/client/views/dock/session.js +229 -0
- package/dist/web/assets/client/views/dock/terminal.js +48 -39
- package/dist/web/assets/client/views/jobs.js +1042 -160
- package/dist/web/assets/client/views/overview.js +2 -2
- package/dist/web/assets/client/views/sidebar.js +3 -0
- package/dist/web/assets/client/views/task/page.js +2 -12
- package/dist/web/assets/shared/manageNav.js +3 -1
- package/dist/web/assets/shell/appRail.js +2 -1
- package/dist/web/assets/shell/dock.js +9 -27
- package/dist/web/assets/shell/manage.js +7 -2
- package/dist/web/assets/shell/models.js +4 -3
- package/dist/web/assets/shell/settingsContent.js +0 -3
- package/dist/web/assets/shell/sidebar.js +0 -1
- package/dist/web/assets/styles/views/conversation.js +96 -17
- package/dist/web/assets/styles/views/dock.js +17 -5
- package/dist/web/assets/styles/views/jobs.js +301 -12
- package/dist/web/assets/styles/views/sidebar.js +1 -2
- package/dist/web/assets/styles/views/task.js +1 -2
- package/dist/web/assets/styles/views/terminal.js +12 -8
- package/dist/web/webConversation.js +1 -0
- package/dist/web/webServer.js +28 -1
- package/dist/web/webSettings.js +10 -2
- package/docs/global-shell-jobs.md +78 -0
- package/docs/job-runs.md +6 -3
- package/docs/managed-turn-and-session-runtime.md +84 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
- package/docs/release-workflow.md +67 -0
- package/docs/release-workflow.zh-CN.md +48 -0
- package/docs/roles-and-configuration.md +45 -1
- package/docs/roles-and-configuration.zh-CN.md +35 -0
- package/docs/storage-baseline.md +19 -1
- package/docs/storage-baseline.zh-CN.md +10 -1
- package/docs/testing/review-evaluation.md +35 -1
- package/docs/testing/review-evaluation.zh-CN.md +25 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/execution.md +19 -8
- package/skills/yui-reviewer/SKILL.md +13 -2
- package/skills/yui-reviewer/VERSION +1 -1
|
@@ -176,13 +176,15 @@ const roleChildren = [
|
|
|
176
176
|
},
|
|
177
177
|
{ name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "List global Roles." },
|
|
178
178
|
{ name: "show", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "Show one global Role.", usage: "yui config role show <name>" },
|
|
179
|
+
{ name: "enable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Enable new execution and template selection; does not start a Session.", usage: "yui config role enable <name>" },
|
|
180
|
+
{ name: "disable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Disable new execution and template selection; keeps running Sessions and input.", usage: "yui config role disable <name>" },
|
|
179
181
|
{
|
|
180
182
|
name: "update", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
181
183
|
summary: "Update a global Role.",
|
|
182
184
|
usage: "yui config role update <name> [profile options] [clear options]",
|
|
183
|
-
options: ["--agent", "--workspace", ...roleProfileOptions, ...roleAgentOptions,
|
|
185
|
+
options: ["--enabled", "--agent", "--workspace", ...roleProfileOptions, ...roleAgentOptions,
|
|
184
186
|
...roleProfileClearOptions, ...roleAgentClearOptions, "--yes"],
|
|
185
|
-
optionValues: roleAgentOptionValues,
|
|
187
|
+
optionValues: { ...roleAgentOptionValues, "--enabled": ["true", "false"] },
|
|
186
188
|
fileOptions: ["--workspace"]
|
|
187
189
|
},
|
|
188
190
|
{ name: "remove", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Remove a global Role.", usage: "yui config role remove <name>" },
|
|
@@ -238,17 +240,19 @@ const profileChildren = [
|
|
|
238
240
|
name: "add", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
239
241
|
summary: "Add a reusable Agent Profile that inherits Worker runtime or selects one explicit Agent.",
|
|
240
242
|
usage: "yui config profile add <id> [--access <read|write>] [--inherit-worker | --agent <id> [--model <model>] [--effort <effort>]] [Profile settings]",
|
|
241
|
-
options: ["--access", ...agentProfileOptions],
|
|
242
|
-
optionValues: { "--access": ["read", "write"] }
|
|
243
|
+
options: ["--enabled", "--access", ...agentProfileOptions],
|
|
244
|
+
optionValues: { "--enabled": ["true", "false"], "--access": ["read", "write"] }
|
|
243
245
|
},
|
|
244
246
|
{ name: "list", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "List Agent Profiles." },
|
|
245
247
|
{ name: "show", discovery: { surface: "public", audiences: ["public", "operator", "leader", "assignment", "global"] }, summary: "Show one Agent Profile.", usage: "yui config profile show <id>" },
|
|
248
|
+
{ name: "enable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Enable new Profile applications; existing Task bindings are unchanged.", usage: "yui config profile enable <id>" },
|
|
249
|
+
{ name: "disable", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Disable new Profile applications without deleting configuration.", usage: "yui config profile disable <id>" },
|
|
246
250
|
{
|
|
247
251
|
name: "update", discovery: { surface: "public", audiences: ["public", "operator"] },
|
|
248
252
|
summary: "Update an Agent Profile's behavior or runtime source.",
|
|
249
253
|
usage: "yui config profile update <id> [--access <read|write>] [--inherit-worker | [--agent <id>] [--model <model>|--clear-model] [--effort <effort>|--clear-effort]] [Profile settings]",
|
|
250
|
-
options: ["--access", ...agentProfileOptions, ...agentProfileClearOptions],
|
|
251
|
-
optionValues: { "--access": ["read", "write"] }
|
|
254
|
+
options: ["--enabled", "--access", ...agentProfileOptions, ...agentProfileClearOptions],
|
|
255
|
+
optionValues: { "--enabled": ["true", "false"], "--access": ["read", "write"] }
|
|
252
256
|
},
|
|
253
257
|
{ name: "remove", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Remove a custom Agent Profile.", usage: "yui config profile remove <id>" },
|
|
254
258
|
{ name: "reset", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Reset all built-in Agent Profiles." }
|
|
@@ -688,7 +692,7 @@ const taskChildren = [
|
|
|
688
692
|
name: "role", discovery: { surface: "managed", audiences: ["operator", "leader"] },
|
|
689
693
|
summary: "Manage Roles within a Task.",
|
|
690
694
|
sections: [{ id: "manage", title: "Commands", entries: [
|
|
691
|
-
"add", "list", "status", "show", "update", "remove", "bind", "unbind",
|
|
695
|
+
"add", "list", "status", "show", "update", "enable", "disable", "remove", "bind", "unbind",
|
|
692
696
|
"capabilities", "session", "interrupt", "view", "takeover", "release"
|
|
693
697
|
] }],
|
|
694
698
|
children: [
|
|
@@ -707,6 +711,8 @@ const taskChildren = [
|
|
|
707
711
|
options: ["--cursor"]
|
|
708
712
|
},
|
|
709
713
|
{ name: "show", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Show one Task Role.", usage: "yui task role show <task> <role> [--cursor <cursor>]", options: ["--cursor"] },
|
|
714
|
+
{ name: "enable", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Enable this Task Role; does not change Task execution authority.", usage: "yui task role enable <task> <role>" },
|
|
715
|
+
{ name: "disable", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Block new execution of this Task Role without stopping its current Run.", usage: "yui task role disable <task> <role>" },
|
|
710
716
|
{ name: "capabilities", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Read native options using this Role's desired or exact failed launch configuration.",
|
|
711
717
|
usage: "yui task role capabilities <task> <role> [--error <event-id>] [--refresh]",
|
|
712
718
|
options: ["--error", "--refresh"] },
|
|
@@ -714,9 +720,9 @@ const taskChildren = [
|
|
|
714
720
|
name: "update", discovery: { surface: "managed", audiences: ["operator", "leader"] },
|
|
715
721
|
summary: "Update a Task Role; Agent settings target the named or active binding without switching it.",
|
|
716
722
|
usage: "yui task role update <task> <role> [--profile <id>] [--agent <id>] [--environment <preparation-id> | --managed-environment] [Role and Agent settings]",
|
|
717
|
-
options: ["--profile", "--agent", "--environment", "--managed-environment", ...roleProfileOptions, ...roleAgentOptions,
|
|
723
|
+
options: ["--enabled", "--profile", "--agent", "--environment", "--managed-environment", ...roleProfileOptions, ...roleAgentOptions,
|
|
718
724
|
...roleProfileClearOptions, ...roleAgentClearOptions, "--yes"],
|
|
719
|
-
optionValues: roleAgentOptionValues
|
|
725
|
+
optionValues: { ...roleAgentOptionValues, "--enabled": ["true", "false"] }
|
|
720
726
|
},
|
|
721
727
|
{ name: "remove", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Remove a Task Role.", usage: "yui task role remove <task> <role>" },
|
|
722
728
|
{ name: "bind", discovery: { surface: "managed", audiences: ["operator", "leader"] }, summary: "Bind and activate an Agent for a Task Role.", usage: "yui task role bind <task> <role> <agent-id>" },
|
|
@@ -1394,7 +1400,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1394
1400
|
examples: ["yui config profile list", "yui config profile show implementer"],
|
|
1395
1401
|
sections: [
|
|
1396
1402
|
{ id: "inspect", title: "Inspect", entries: ["list", "show"] },
|
|
1397
|
-
{ id: "manage", title: "Manage", entries: ["add", "update", "remove", "reset"] }
|
|
1403
|
+
{ id: "manage", title: "Manage", entries: ["add", "update", "enable", "disable", "remove", "reset"] }
|
|
1398
1404
|
],
|
|
1399
1405
|
children: profileChildren
|
|
1400
1406
|
},
|
|
@@ -1404,7 +1410,7 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1404
1410
|
examples: ["yui config role list", "yui config role show operator"],
|
|
1405
1411
|
sections: [
|
|
1406
1412
|
{ id: "inspect", title: "Inspect", entries: ["list", "show"] },
|
|
1407
|
-
{ id: "manage", title: "Manage", entries: ["add", "update", "remove", "bind", "unbind"] }
|
|
1413
|
+
{ id: "manage", title: "Manage", entries: ["add", "update", "enable", "disable", "remove", "bind", "unbind"] }
|
|
1408
1414
|
],
|
|
1409
1415
|
children: roleChildren
|
|
1410
1416
|
},
|
|
@@ -1690,7 +1696,8 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1690
1696
|
: name === "run-delegation" ? "Read the exact execution Task/WorkItem and attention projection; never a grant."
|
|
1691
1697
|
: `${name} a stable Job or exact Run.`,
|
|
1692
1698
|
usage: `yui job ${name} (--task <id>|--global)${name === "list" ? "" : " --job <id>"}`
|
|
1693
|
-
+ (name === "define"
|
|
1699
|
+
+ (name === "define" ? " --title <text> (--input <json>|--input-file <file>|--command <shell>) [--script <path=local-file> ...]" : "")
|
|
1700
|
+
+ (name === "update" ? " [--title <text>] [--input <json>|--input-file <file>|--command <shell>] [--script <path=local-file> ...]" : "")
|
|
1694
1701
|
+ (name === "update" ? " --revision <current>" : "")
|
|
1695
1702
|
+ (name === "run" ? " --request-id <key> [--retry-of <run-id>]" : "")
|
|
1696
1703
|
+ (["run-get", "run-result", "run-evidence", "run-confirm", "run-delegation", "logs", "run-cancel", "run-acknowledge"].includes(name) ? " --run <run-id>" : "")
|
|
@@ -21,9 +21,22 @@ export async function getSelectionCandidates(selector, ports, args) {
|
|
|
21
21
|
return { ...set, defaultValue };
|
|
22
22
|
}
|
|
23
23
|
case "agent-profiles":
|
|
24
|
-
return entities("Agent Profile", "Select Agent Profile", await list(ports, "profile.list", {})
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
return entities("Agent Profile", "Select Agent Profile", (await list(ports, "profile.list", {}))
|
|
25
|
+
.filter(p => selector.actionTarget || p.enabled !== false
|
|
26
|
+
&& p.effectiveRuntime?.status !== "unavailable")
|
|
27
|
+
.map(p => ({ ...p, availability: p.enabled === false ? "disabled" : "enabled" })), ["id", "availability", "revision", "defaultAccess", "agentId"]);
|
|
28
|
+
case "global-roles": {
|
|
29
|
+
let roles = await list(ports, "role.list", {});
|
|
30
|
+
// Review uses a Task-local binding first; a disabled same-named template
|
|
31
|
+
// must not hide an independently enabled existing Reviewer (or vice versa).
|
|
32
|
+
if (args[0] === "task" && args[1] === "review" && args[2] === "request" && args[3] !== undefined) {
|
|
33
|
+
const local = await list(ports, "task.role.list", { taskId: args[3] });
|
|
34
|
+
roles = [...roles.filter(r => !local.some(t => t.name === r.name)), ...local];
|
|
35
|
+
}
|
|
36
|
+
return entities("global role", "Select global Role", roles
|
|
37
|
+
.filter(r => selector.actionTarget && !(args[0] === "session" && args[1] === "enter") || r.enabled !== false)
|
|
38
|
+
.map(r => ({ ...r, availability: r.enabled === false ? "disabled" : "enabled" })), ["name", "availability", "activeAgentId", "workspace"]);
|
|
39
|
+
}
|
|
27
40
|
case "jobs":
|
|
28
41
|
return entities("job", "Select job", await list(ports, "jobs.list", {}), ["id", "type", "status"]);
|
|
29
42
|
case "input-requests": {
|
|
@@ -41,9 +54,11 @@ export async function getSelectionCandidates(selector, ports, args) {
|
|
|
41
54
|
const taskId = dependencyValue(selector, args);
|
|
42
55
|
if (taskId === undefined)
|
|
43
56
|
return null;
|
|
44
|
-
const roles = orderRoleOptions(await list(ports, "task.role.list", { taskId }))
|
|
57
|
+
const roles = orderRoleOptions((await list(ports, "task.role.list", { taskId }))
|
|
58
|
+
.filter(r => selector.actionTarget || r.enabled !== false))
|
|
59
|
+
.map(r => ({ ...r, availability: r.enabled === false ? "disabled" : "enabled" }));
|
|
45
60
|
return entities("task role", `Select Task role: ${taskId}`, roles, [
|
|
46
|
-
"name", "kind", "activeAgentId"
|
|
61
|
+
"name", "availability", "kind", "activeAgentId"
|
|
47
62
|
]);
|
|
48
63
|
}
|
|
49
64
|
case "task-decisions": {
|
|
@@ -92,7 +92,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
92
92
|
"--search": "value"
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
-
...["show", "update", "remove", "enter", "context"].map((command) => ({
|
|
95
|
+
...["show", "update", "remove", "enable", "disable", "enter", "context"].map((command) => ({
|
|
96
96
|
commandPath: command === "enter" || command === "context"
|
|
97
97
|
? ["session", command]
|
|
98
98
|
: ["config", "role", command],
|
|
@@ -129,12 +129,13 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
129
129
|
commandPath: ["config", "profile", "add"],
|
|
130
130
|
selectors: [],
|
|
131
131
|
trailingOptions: {
|
|
132
|
+
"--enabled": "value",
|
|
132
133
|
"--description": "value", "--instructions": "value",
|
|
133
134
|
"--skill": "value", "--agent": "value", "--model": "value",
|
|
134
135
|
"--effort": "value", "--access": "value", "--inherit-worker": "flag"
|
|
135
136
|
}
|
|
136
137
|
},
|
|
137
|
-
...["show", "update", "remove"].map((command) => ({
|
|
138
|
+
...["show", "update", "remove", "enable", "disable"].map((command) => ({
|
|
138
139
|
commandPath: ["config", "profile", command],
|
|
139
140
|
selectors: [{
|
|
140
141
|
argumentIndex: 3,
|
|
@@ -145,7 +146,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
145
146
|
...(command === "update"
|
|
146
147
|
? {
|
|
147
148
|
trailingOptions: {
|
|
148
|
-
"--description": "option-like-value",
|
|
149
|
+
"--enabled": "value", "--description": "option-like-value",
|
|
149
150
|
"--instructions": "option-like-value", "--skill": "value",
|
|
150
151
|
"--agent": "value", "--inherit-worker": "flag",
|
|
151
152
|
"--model": "option-like-value", "--effort": "option-like-value",
|
|
@@ -380,7 +381,7 @@ export const INTERACTION_POLICIES = Object.freeze([
|
|
|
380
381
|
commandPath: ["task", "role", "list"],
|
|
381
382
|
selectors: [{ argumentIndex: 3, entity: "task", provider: "tasks", actionTarget: true }]
|
|
382
383
|
},
|
|
383
|
-
...["show", "status", "update", "remove"].map((command) => ({
|
|
384
|
+
...["show", "status", "update", "remove", "enable", "disable"].map((command) => ({
|
|
384
385
|
commandPath: ["task", "role", command],
|
|
385
386
|
selectors: [
|
|
386
387
|
{ argumentIndex: 3, entity: "task", provider: "tasks", actionTarget: true },
|
package/dist/cli/roleWizard.js
CHANGED
|
@@ -281,6 +281,7 @@ async function settingsSection(title, io, taskRole) {
|
|
|
281
281
|
}
|
|
282
282
|
async function updateRoleSettings(args, role, ports, io, taskRole) {
|
|
283
283
|
const fields = [
|
|
284
|
+
{ value: "enabled", cells: ["Enabled (not Session state)", role.enabled === false ? "false" : "true"] },
|
|
284
285
|
profileChoice("description", "Description", role.description),
|
|
285
286
|
profileChoice("responsibilities", "Responsibilities", role.responsibilities),
|
|
286
287
|
profileChoice("constraints", "Constraints", role.constraints),
|
|
@@ -292,6 +293,11 @@ async function updateRoleSettings(args, role, ports, io, taskRole) {
|
|
|
292
293
|
const field = await choose("Role settings", fields, [TEXT_COLUMN, { header: "Current", minWidth: 12, maxWidth: 52 }], io, undefined, "field");
|
|
293
294
|
if (field === undefined)
|
|
294
295
|
return { kind: "cancelled", args };
|
|
296
|
+
if (field === "enabled") {
|
|
297
|
+
const value = await choose("Allow new execution; running Sessions are not stopped", [{ value: "true", cells: ["Enabled"] }, { value: "false", cells: ["Disabled"] }], [TEXT_COLUMN], io, role.enabled === false ? "false" : "true", "enabled");
|
|
298
|
+
return value === undefined ? { kind: "cancelled", args }
|
|
299
|
+
: { kind: "resolved", args: [...args, "--enabled", value] };
|
|
300
|
+
}
|
|
295
301
|
if (field === "active-agent") {
|
|
296
302
|
const target = await selectActiveAgent(role, ports, io);
|
|
297
303
|
if (target === undefined)
|
|
@@ -672,6 +678,7 @@ function asRole(value) {
|
|
|
672
678
|
}));
|
|
673
679
|
return {
|
|
674
680
|
name,
|
|
681
|
+
enabled: input.enabled !== false,
|
|
675
682
|
activeAgentId,
|
|
676
683
|
agentBindings,
|
|
677
684
|
...(stringField(input, "workspace") === undefined ? {} : { workspace: stringField(input, "workspace") }),
|
|
@@ -161,10 +161,9 @@ export function parseControllerRuntimeSnapshot(result, droppedEvents) {
|
|
|
161
161
|
const drain = runtime.drain;
|
|
162
162
|
const commands = runtime.commands;
|
|
163
163
|
const routes = commands?.routes;
|
|
164
|
-
const dispatcher = commands?.dispatcher;
|
|
165
164
|
const eventLoopDelay = commands?.eventLoopDelay;
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
// Dispatcher work is a subset of authenticated routes, not another queue.
|
|
166
|
+
const queueDepth = typeof routes?.inFlight === "number" ? routes.inFlight : undefined;
|
|
168
167
|
return {
|
|
169
168
|
source: "controller.status",
|
|
170
169
|
pid: typeof record.pid === "number" ? record.pid : undefined,
|
|
@@ -182,6 +181,16 @@ export function parseControllerRuntimeSnapshot(result, droppedEvents) {
|
|
|
182
181
|
? drain.progressEventsCoalesced
|
|
183
182
|
: undefined,
|
|
184
183
|
droppedEvents,
|
|
184
|
+
observations: {
|
|
185
|
+
commands: (runtime.commands ?? null),
|
|
186
|
+
phases: (runtime.phases ?? null),
|
|
187
|
+
reads: (runtime.reads ?? null),
|
|
188
|
+
persistence: (runtime.persistence ?? null),
|
|
189
|
+
diagnostics: (runtime.diagnostics ?? null),
|
|
190
|
+
scheduler: (runtime.scheduler ?? null),
|
|
191
|
+
preRoutingWait: "unmeasured; event-loop lag is sampled, not per-request queue time",
|
|
192
|
+
clock: "process-monotonic milliseconds; target/request labels are sha256 prefixes"
|
|
193
|
+
},
|
|
185
194
|
queueDepth,
|
|
186
195
|
oldestInFlightAgeMs: typeof routes?.oldestInFlightAgeMs === "number"
|
|
187
196
|
? routes.oldestInFlightAgeMs
|
|
@@ -210,7 +219,7 @@ export function renderRuntimeIdentitySection(input) {
|
|
|
210
219
|
}
|
|
211
220
|
lines.push(` Health ${health.status === "ok" ? "ok" : health.status === "degraded" ? "DEGRADED (needs-repair above)" : "FAIL (contradictions above)"}`, ` Controller ${runtime.source === "controller.status" && runtime.pid !== undefined
|
|
212
221
|
? `PID ${runtime.pid}${runtime.uptimeMs === undefined ? "" : `, up ${formatDuration(runtime.uptimeMs)}`}${runtime.protocolVersion === undefined ? "" : `, protocol ${runtime.protocolVersion}`}`
|
|
213
|
-
: "not running (unsupported)"}`, ` Controller RSS ${runtime.rssBytes !== undefined ? formatBytes(runtime.rssBytes) : UNSUPPORTED} (inventory ${formatBytes(inventoryRssBytes)})`, `
|
|
222
|
+
: "not running (unsupported)"}`, ` Controller RSS ${runtime.rssBytes !== undefined ? formatBytes(runtime.rssBytes) : UNSUPPORTED} (inventory ${formatBytes(inventoryRssBytes)})`, ` Commands in flight ${runtime.queueDepth ?? UNSUPPORTED} · oldest ${runtime.oldestInFlightAgeMs === undefined
|
|
214
223
|
? UNSUPPORTED
|
|
215
224
|
: runtime.oldestInFlightAgeMs === null
|
|
216
225
|
? "—"
|
|
@@ -4,6 +4,7 @@ import { callFileTaskController } from "../controller/clientRuntime.js";
|
|
|
4
4
|
import { acknowledgeDurableJob, cancelDurableJob, getDurableJob, startDurableJob } from "../controller/jobClient.js";
|
|
5
5
|
import { resolveJobCaller, taskLocalActor } from "../task/taskAuthority.js";
|
|
6
6
|
import { boundedJobArtifactPayload } from "../artifacts/jobArtifactTransport.js";
|
|
7
|
+
import { readFileSync, statSync } from "node:fs";
|
|
7
8
|
/**
|
|
8
9
|
* The textual `--owner` forms accepted by `job start`. The public help text in
|
|
9
10
|
* the command catalog must document exactly these forms.
|
|
@@ -38,9 +39,25 @@ export async function runDurableJobCommand(args, options) {
|
|
|
38
39
|
}
|
|
39
40
|
async function definitionCommand(command, args, options) {
|
|
40
41
|
const flags = new Map();
|
|
42
|
+
const scripts = [];
|
|
43
|
+
const readText = (path) => {
|
|
44
|
+
if (!statSync(path).isFile() || statSync(path).size > 512 * 1024)
|
|
45
|
+
throw usageError("Job input files must be text files up to 512 KiB.");
|
|
46
|
+
return readFileSync(path, "utf8");
|
|
47
|
+
};
|
|
41
48
|
for (let i = 0; i < args.length; i++) {
|
|
42
49
|
const flag = args[i];
|
|
43
|
-
if (
|
|
50
|
+
if (flag === "--script") {
|
|
51
|
+
const value = args[++i] ?? "", separator = value.indexOf("=");
|
|
52
|
+
if (separator < 1 || separator === value.length - 1)
|
|
53
|
+
throw usageError("--script requires relative-path=local-file.");
|
|
54
|
+
const path = value.slice(0, separator);
|
|
55
|
+
if (scripts.some(file => file.path === path))
|
|
56
|
+
throw usageError("Repeated script path.");
|
|
57
|
+
scripts.push({ path, content: readText(value.slice(separator + 1)) });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!["--global", "--task", "--job", "--title", "--input", "--input-file", "--command", "--revision", "--request-id", "--retry-of", "--run", "--artifact", "--reference", "--offset", "--result", "--expected-session", "--expected-attempt"].includes(flag)
|
|
44
61
|
|| flags.has(flag))
|
|
45
62
|
throw usageError(`Unknown or repeated Job flag: ${flag}.`);
|
|
46
63
|
if (flag === "--global") {
|
|
@@ -61,14 +78,44 @@ async function definitionCommand(command, args, options) {
|
|
|
61
78
|
?? (executionRead ? options.environment?.YUI_TASK_ID : undefined) ?? "");
|
|
62
79
|
const scope = taskId === undefined ? { kind: "global" } : { kind: "task", taskId };
|
|
63
80
|
let configuration;
|
|
64
|
-
if (flags.has("--input"))
|
|
81
|
+
if (flags.has("--input") && flags.has("--input-file"))
|
|
82
|
+
throw usageError("Choose --input or --input-file.");
|
|
83
|
+
if (flags.has("--input") || flags.has("--input-file")) {
|
|
65
84
|
try {
|
|
66
|
-
configuration = JSON.parse(flags.get("--input"));
|
|
85
|
+
configuration = JSON.parse(flags.has("--input-file") ? readText(flags.get("--input-file")) : flags.get("--input"));
|
|
67
86
|
}
|
|
68
87
|
catch {
|
|
69
88
|
throw usageError("--input must be a JSON Job configuration.");
|
|
70
89
|
}
|
|
71
90
|
}
|
|
91
|
+
if ((flags.has("--command") || scripts.length || flags.has("--input-file")) && !["define", "update"].includes(command)) {
|
|
92
|
+
throw usageError("Command and script edits require job define or update.");
|
|
93
|
+
}
|
|
94
|
+
if (flags.has("--command") || scripts.length) {
|
|
95
|
+
if (taskId !== undefined || configuration !== undefined)
|
|
96
|
+
throw usageError("--command/--script are global Shell shortcuts; use --input for a complete configuration.");
|
|
97
|
+
let shell = { kind: "shell", env: {}, steps: [], files: [] };
|
|
98
|
+
if (command === "update") {
|
|
99
|
+
const existing = await callFileTaskController(options.home, "job.definition", {
|
|
100
|
+
op: "show", scope, caller, id: flags.get("--job")
|
|
101
|
+
}, { environment: options.environment });
|
|
102
|
+
if (existing.revision !== Number(flags.get("--revision")))
|
|
103
|
+
throw usageError("Job revision changed; inspect before editing.");
|
|
104
|
+
if (existing.configuration.kind !== "shell")
|
|
105
|
+
throw usageError("Changing executor requires a new Job.");
|
|
106
|
+
shell = existing.configuration;
|
|
107
|
+
if (!flags.has("--title"))
|
|
108
|
+
flags.set("--title", existing.title);
|
|
109
|
+
}
|
|
110
|
+
if (flags.has("--command")) {
|
|
111
|
+
if (shell.steps.length > 1)
|
|
112
|
+
throw usageError("Use --input-file to edit a multi-step Job without discarding steps.");
|
|
113
|
+
shell = { ...shell, steps: [{ ...shell.steps[0], name: shell.steps[0]?.name ?? "run", command: flags.get("--command") }] };
|
|
114
|
+
// The command shortcut intentionally selects shell execution, not stale argv.
|
|
115
|
+
delete shell.steps[0].argv;
|
|
116
|
+
}
|
|
117
|
+
configuration = { ...shell, files: [...shell.files.filter(file => !scripts.some(next => next.path === file.path)), ...scripts] };
|
|
118
|
+
}
|
|
72
119
|
const requestId = flags.get("--request-id");
|
|
73
120
|
const parseObject = (flag) => {
|
|
74
121
|
if (!flags.has(flag))
|
|
@@ -2,7 +2,8 @@ import { roleNotFound, usageError } from "../errors/cliError.js";
|
|
|
2
2
|
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
3
3
|
import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
|
|
4
4
|
import { boundedDocument, recordPage, readOptions, readDigest, messageReceipt, summaryText, summaryMetadata } from "../output/boundedRead.js";
|
|
5
|
-
import { controlProviderRetry, providerRetryProjection,
|
|
5
|
+
import { controlProviderRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
|
|
6
|
+
import { globalSessionHealth } from "../runtime/globalSessionHealth.js";
|
|
6
7
|
import { pendingGlobalInboxMessages, inboxHandlingState, globalInboxDeliveryState } from "../message/inboxReceipt.js";
|
|
7
8
|
import { settleGlobalRetryInput } from "../message/globalProviderRetry.js";
|
|
8
9
|
import { globalInputBacklog } from "../message/globalInputBacklog.js";
|
|
@@ -10,6 +11,7 @@ import { inboxEntryCategory } from "../message/inboxTurn.js";
|
|
|
10
11
|
import { checkCurrentSkills, SKILL_REUSE_RULE } from "../context/currentSkills.js";
|
|
11
12
|
import { runtimeDiagnosticGroups, runtimeDiagnosticEvidence } from "../controller/globalRuntimeAttention.js";
|
|
12
13
|
import { roleSkillIds } from "../context/roleSessionContext.js";
|
|
14
|
+
import { isEnabled } from "../role/availability.js";
|
|
13
15
|
import { createRoleSessionSet, recordRoleAgentSession } from "../executor/agentExecutor.js";
|
|
14
16
|
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
15
17
|
import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
|
|
@@ -96,6 +98,12 @@ export function runGlobalRoleCommand(args, store, options = {}) {
|
|
|
96
98
|
case "show": return showRole(rest, store);
|
|
97
99
|
case "context": return roleContext(rest, store, options);
|
|
98
100
|
case "update": return updateRole(rest, store, options);
|
|
101
|
+
case "enable":
|
|
102
|
+
case "disable": {
|
|
103
|
+
if (rest.length !== 1)
|
|
104
|
+
throw usageError(`Usage: yui config role ${command} <role>`);
|
|
105
|
+
return updateRole([rest[0], "--enabled", String(command === "enable")], store, options);
|
|
106
|
+
}
|
|
99
107
|
case "remove": return removeRole(rest, store);
|
|
100
108
|
case "bind": return bindRole(rest, store);
|
|
101
109
|
case "unbind": return unbindRole(rest, store);
|
|
@@ -127,6 +135,7 @@ function roleContext(args, store, options) {
|
|
|
127
135
|
const effective = resolveEffectiveLaunch({ role, purpose: "execution" });
|
|
128
136
|
const sessions = store.getGlobalRoleSessionSet(name);
|
|
129
137
|
const session = sessions?.sessions[role.activeAgentId];
|
|
138
|
+
const health = globalSessionHealth(role, sessions);
|
|
130
139
|
const currentSkills = checkCurrentSkills(options.yuiHome ?? environment.YUI_HOME, roleSkillIds(session?.effective.context ?? role, role.name === "operator" ? "operator" : "global"));
|
|
131
140
|
// Native delivery and explicit Agent handling settle the same queued input
|
|
132
141
|
// through distinct receipts; a context read does neither.
|
|
@@ -157,7 +166,9 @@ function roleContext(args, store, options) {
|
|
|
157
166
|
agentId: binding.agentId,
|
|
158
167
|
adapterId: binding.adapterId,
|
|
159
168
|
workspace: role.workspace,
|
|
160
|
-
|
|
169
|
+
desiredRevision: health.desiredRevision,
|
|
170
|
+
effectiveRevision: health.effectiveRevision,
|
|
171
|
+
configurationDrift: health.configurationDrift,
|
|
161
172
|
...(session?.nativeSessionId === undefined
|
|
162
173
|
? {}
|
|
163
174
|
: { nativeSessionId: session.nativeSessionId })
|
|
@@ -173,7 +184,7 @@ function roleContext(args, store, options) {
|
|
|
173
184
|
authority: {
|
|
174
185
|
view: role.name === "operator" ? "operator" : "global",
|
|
175
186
|
taskImplementation: false,
|
|
176
|
-
writableProjectIds: effective.writeProjectIds,
|
|
187
|
+
writableProjectIds: (session?.effective ?? effective).writeProjectIds,
|
|
177
188
|
allowedActions: role.name === "operator"
|
|
178
189
|
? ["route-request", "inspect-catalog", "answer-user-boundary"]
|
|
179
190
|
: ["perform-global-role-request"]
|
|
@@ -189,17 +200,14 @@ function roleContext(args, store, options) {
|
|
|
189
200
|
diagnostics: `role message list ${name} --diagnostics`,
|
|
190
201
|
retry: `session retry ${name} show`
|
|
191
202
|
},
|
|
192
|
-
nativeTurn:
|
|
193
|
-
|
|
194
|
-
nativeTurnId: sessions.providerBinding.run.nativeTurnId,
|
|
195
|
-
status: sessions.providerBinding.run.status
|
|
196
|
-
},
|
|
203
|
+
nativeTurn: health.nativeTurn,
|
|
204
|
+
health,
|
|
197
205
|
...(role.jobOwner === undefined ? {} : { job: {
|
|
198
206
|
owner: role.jobOwner,
|
|
199
207
|
read: `yui job show ${role.jobOwner.scope.kind === "global" ? "--global" : `--task ${role.jobOwner.scope.taskId}`} --job ${role.jobOwner.jobId} --json`,
|
|
200
208
|
boundary: "Job execution only. This Role is not Operator and owns no Task delivery authority. Read the exact Run from its original message."
|
|
201
209
|
} }),
|
|
202
|
-
retry:
|
|
210
|
+
retry: health.retry,
|
|
203
211
|
// Historical interrupt receipts are available through Session inspection;
|
|
204
212
|
// only this Turn's exact identity is required at entry.
|
|
205
213
|
sessionManifestPath: environment.YUI_SESSION_MANIFEST,
|
|
@@ -292,7 +300,7 @@ function listRoles(args, store) {
|
|
|
292
300
|
for (const name of SYSTEM_ROLE_NAMES) {
|
|
293
301
|
const role = store.getGlobalRole(name);
|
|
294
302
|
rows.set(name, role === null
|
|
295
|
-
? [name, "system", "?", "?", "?", "?"]
|
|
303
|
+
? [name, "system", "?", "?", "?", "?", "?"]
|
|
296
304
|
: roleListRow(role, "system"));
|
|
297
305
|
}
|
|
298
306
|
for (const role of store.listGlobalRoles()) {
|
|
@@ -305,6 +313,7 @@ function listRoles(args, store) {
|
|
|
305
313
|
return `${renderTable("Roles", [
|
|
306
314
|
{ header: "Role", minWidth: 4, maxWidth: 24 },
|
|
307
315
|
{ header: "Kind", minWidth: 6, maxWidth: 8 },
|
|
316
|
+
{ header: "Enabled", minWidth: 7, maxWidth: 8 },
|
|
308
317
|
{ header: "Active Agent", minWidth: 8, maxWidth: 20 },
|
|
309
318
|
{ header: "Model", minWidth: 8, maxWidth: 22 },
|
|
310
319
|
{ header: "Effort", minWidth: 8, maxWidth: 14 },
|
|
@@ -344,6 +353,9 @@ function updateRole(args, store, options) {
|
|
|
344
353
|
const workspace = trimmed(parsed.one("--workspace"));
|
|
345
354
|
const next = store.transaction((tx) => {
|
|
346
355
|
const role = requireRole(name, tx);
|
|
356
|
+
if (parsed.seen.size === 1 && parsed.has("--enabled")
|
|
357
|
+
&& isEnabled(role) === roleProfilePatch(parsed).enabled)
|
|
358
|
+
return role;
|
|
347
359
|
const changesLaunchContext = hasRoleLaunchContextOptions(parsed);
|
|
348
360
|
const changesAgentConfig = hasAgentConfigOptions(parsed);
|
|
349
361
|
if (changesLaunchContext || changesAgentConfig) {
|
|
@@ -499,7 +511,7 @@ function roleSession(args, store, options) {
|
|
|
499
511
|
throw usageError("Usage: yui session retry <role> [show|cancel|disable|enable]");
|
|
500
512
|
}
|
|
501
513
|
return store.transaction(tx => {
|
|
502
|
-
requireRole(name, tx);
|
|
514
|
+
const role = requireRole(name, tx);
|
|
503
515
|
const sessions = tx.getGlobalRoleSessionSet(name);
|
|
504
516
|
let binding = sessions?.providerBinding ?? null;
|
|
505
517
|
if (action !== "show") {
|
|
@@ -509,9 +521,14 @@ function roleSession(args, store, options) {
|
|
|
509
521
|
tx.saveGlobalRoleSessionSet({ ...sessions, providerBinding: binding });
|
|
510
522
|
settleGlobalRetryInput(tx, name, binding, new Date());
|
|
511
523
|
}
|
|
524
|
+
const health = globalSessionHealth(role, sessions);
|
|
525
|
+
const historical = binding?.retry !== undefined && health.retry === null;
|
|
512
526
|
return options.jsonOutput ? JSON.stringify({
|
|
513
|
-
roleName: name, retry: providerRetryProjection(binding), disabled: binding?.retryDisabled ?? false
|
|
514
|
-
|
|
527
|
+
roleName: name, retry: providerRetryProjection(binding), disabled: binding?.retryDisabled ?? false,
|
|
528
|
+
selectedNativeSessionId: health.nativeSessionId,
|
|
529
|
+
retryScope: historical ? "historical-session" : "current-session"
|
|
530
|
+
}) + "\n" : (historical ? "Historical Session evidence (not the selected Session's current failure): " : "")
|
|
531
|
+
+ renderProviderRetry(binding) + "\n";
|
|
515
532
|
});
|
|
516
533
|
}
|
|
517
534
|
if (command !== "record" && command !== "replace") {
|
|
@@ -971,7 +988,7 @@ function presentRole(title, role, store) {
|
|
|
971
988
|
}
|
|
972
989
|
function roleListRow(role, kind) {
|
|
973
990
|
const summary = activeRoleSummary(role);
|
|
974
|
-
return [role.name, kind, summary.agent, summary.model, summary.effort, role.workspace];
|
|
991
|
+
return [role.name, kind, String(isEnabled(role)), summary.agent, summary.model, summary.effort, role.workspace];
|
|
975
992
|
}
|
|
976
993
|
function renderMissingSystemRole(name) {
|
|
977
994
|
return [
|
|
@@ -322,6 +322,7 @@ export function runInboxCommand(args, store, env, now = new Date()) {
|
|
|
322
322
|
if (message.inbox?.ack || message.delivery !== undefined || message.interruptThen !== undefined
|
|
323
323
|
|| message.control !== undefined && message.control.outcome !== "rejected"
|
|
324
324
|
|| turn?.attemptId === `global-input:${current.roleName}/${message.id}`
|
|
325
|
+
&& turn.status !== "rejected"
|
|
325
326
|
|| !foreignTarget && message.notDelivered === undefined) {
|
|
326
327
|
throw new Error("Retirement requires obsolete, proven-unsubmitted input; unknown/accepted/interrupt-owned input must retain its exact handling path.");
|
|
327
328
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { agentAdapterLabel as adapterLabel } from "../agent/adapterCatalog.js";
|
|
2
|
+
import { assertRoleEnabled } from "../role/availability.js";
|
|
3
|
+
import { globalSessionHealth } from "../runtime/globalSessionHealth.js";
|
|
2
4
|
import { usageError } from "../errors/cliError.js";
|
|
3
5
|
import { createRoleSessionSet } from "../executor/agentExecutor.js";
|
|
4
6
|
import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
|
|
@@ -31,7 +33,11 @@ function status(args, store, options) {
|
|
|
31
33
|
if (activeBinding === undefined) {
|
|
32
34
|
throw usageError(`Operator active Agent is not bound: ${role.activeAgentId}.`);
|
|
33
35
|
}
|
|
34
|
-
const
|
|
36
|
+
const sessions = store.getGlobalRoleSessionSet(role.name);
|
|
37
|
+
const projection = {
|
|
38
|
+
...projectOperatorStatus(sessions, role.activeAgentId, activeBinding.adapterId),
|
|
39
|
+
health: globalSessionHealth(role, sessions)
|
|
40
|
+
};
|
|
35
41
|
const writer = projection.writer;
|
|
36
42
|
const writerLines = [
|
|
37
43
|
"Operator status",
|
|
@@ -41,7 +47,10 @@ function status(args, store, options) {
|
|
|
41
47
|
` Adapter: ${adapterLabel(writer.adapterId)}`,
|
|
42
48
|
` Conversation: ${writer.sessionRef ?? "-"}`,
|
|
43
49
|
` Native session: ${writer.nativeSessionId ?? "-"}`,
|
|
44
|
-
` Session state: ${writer.sessionStatus ?? "-"}
|
|
50
|
+
` Session state: ${writer.sessionStatus ?? "-"}`,
|
|
51
|
+
` Configuration: desired r${projection.health.desiredRevision}, effective r${projection.health.effectiveRevision ?? "unknown"}`,
|
|
52
|
+
` Host observation: ${projection.health.hostObservation}`,
|
|
53
|
+
` ${projection.health.note}`
|
|
45
54
|
];
|
|
46
55
|
const history = projection.historicalConversations;
|
|
47
56
|
const historyOutput = history.length === 0
|
|
@@ -222,6 +231,7 @@ function resumeSession(args, store) {
|
|
|
222
231
|
export function applyOperatorSessionControl(control, store, now = new Date()) {
|
|
223
232
|
return store.transaction((tx) => {
|
|
224
233
|
const role = requireOperator(tx);
|
|
234
|
+
assertRoleEnabled(role);
|
|
225
235
|
const binding = role.agentBindings[control.targetAgentId];
|
|
226
236
|
if (binding === undefined) {
|
|
227
237
|
throw usageError(`Operator Agent is not bound: ${control.targetAgentId}.`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { isEnabled, parseEnabled } from "../role/availability.js";
|
|
2
3
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
4
|
import { BUILTIN_PROFILE_IDS, builtinAgentProfileInputs, createAgentProfile, updateAgentProfile } from "../profile/agentProfile.js";
|
|
4
5
|
import { resolveAgentProfileRuntime, resolveAgentProfileView } from "../profile/agentProfileRuntime.js";
|
|
@@ -35,7 +36,7 @@ export function previewProfileAgentConfigurationMutation(args, store) {
|
|
|
35
36
|
else {
|
|
36
37
|
return undefined;
|
|
37
38
|
}
|
|
38
|
-
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
39
|
+
const runtime = resolveAgentProfileRuntime(profile, store, { configurationOnly: true });
|
|
39
40
|
if (runtime.status === "unavailable")
|
|
40
41
|
throw usageError(runtime.reason);
|
|
41
42
|
return {
|
|
@@ -51,6 +52,11 @@ export function runProfileCommand(args, store, now = () => new Date(), options =
|
|
|
51
52
|
case "list": return listProfiles(rest, store);
|
|
52
53
|
case "show": return showProfile(rest, store);
|
|
53
54
|
case "update": return updateProfile(rest, store, now(), options);
|
|
55
|
+
case "enable":
|
|
56
|
+
case "disable": {
|
|
57
|
+
const id = oneArg(rest, `Usage: yui config profile ${command} <id>`);
|
|
58
|
+
return updateProfile([id, "--enabled", String(command === "enable")], store, now(), options);
|
|
59
|
+
}
|
|
54
60
|
case "remove": return removeProfile(rest, store);
|
|
55
61
|
case "reset": return resetProfiles(rest, store, now());
|
|
56
62
|
default:
|
|
@@ -68,6 +74,7 @@ function addProfile(args, store, now, options) {
|
|
|
68
74
|
assertOptionPairs(parsed);
|
|
69
75
|
const profile = createAgentProfile({
|
|
70
76
|
id,
|
|
77
|
+
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
71
78
|
defaultAccess: parseAccess(parsed.one("--access") ?? "read"),
|
|
72
79
|
...profileValues(parsed),
|
|
73
80
|
runtime: addedRuntime(parsed, usage)
|
|
@@ -91,6 +98,7 @@ function listProfiles(args, store) {
|
|
|
91
98
|
? "No Agent Profiles found.\n"
|
|
92
99
|
: `${renderTable("Agent Profiles", [
|
|
93
100
|
{ header: "Profile", minWidth: 7, maxWidth: 24 },
|
|
101
|
+
{ header: "Enabled", minWidth: 7, maxWidth: 8 },
|
|
94
102
|
{ header: "Revision", minWidth: 8, maxWidth: 10 },
|
|
95
103
|
{ header: "Access", minWidth: 6, maxWidth: 8 },
|
|
96
104
|
{ header: "Runtime", minWidth: 9, maxWidth: 14 },
|
|
@@ -109,6 +117,7 @@ function showProfile(args, store) {
|
|
|
109
117
|
return {
|
|
110
118
|
output: `${[
|
|
111
119
|
`Agent Profile: ${profile.id}`,
|
|
120
|
+
`Enabled: ${isEnabled(profile)}`,
|
|
112
121
|
`Revision: ${profile.revision}`,
|
|
113
122
|
`Default access: ${profile.defaultAccess}`,
|
|
114
123
|
`Description: ${profile.description ?? "-"}`,
|
|
@@ -140,8 +149,14 @@ function updateProfile(args, store, now, options) {
|
|
|
140
149
|
throw usageError("At least one Profile option is required.", usage);
|
|
141
150
|
assertOptionPairs(parsed);
|
|
142
151
|
const current = requireProfile(store, id);
|
|
152
|
+
if (parsed.seen.size === 1 && parsed.has("--enabled")
|
|
153
|
+
&& isEnabled(current) === parseEnabled(parsed.one("--enabled"))) {
|
|
154
|
+
return { output: `Agent Profile ${id} is already ${isEnabled(current) ? "enabled" : "disabled"}\n`,
|
|
155
|
+
data: resolveAgentProfileView(current, store) };
|
|
156
|
+
}
|
|
143
157
|
const runtime = updatedRuntime(current, parsed, usage);
|
|
144
158
|
const updated = updateAgentProfile(current, {
|
|
159
|
+
...(parsed.has("--enabled") ? { enabled: parseEnabled(parsed.one("--enabled")) } : {}),
|
|
145
160
|
...(parsed.has("--access") ? { defaultAccess: parseAccess(parsed.one("--access")) } : {}),
|
|
146
161
|
...profilePatch(parsed),
|
|
147
162
|
...(runtime === undefined ? {} : { runtime })
|
|
@@ -187,6 +202,7 @@ function resetProfiles(args, store, now) {
|
|
|
187
202
|
};
|
|
188
203
|
}
|
|
189
204
|
const BASE_OPTIONS = [
|
|
205
|
+
["--enabled", "value"],
|
|
190
206
|
["--access", "value"],
|
|
191
207
|
["--description", "value"],
|
|
192
208
|
["--instructions", "value"],
|
|
@@ -307,7 +323,7 @@ function assertRuntimeSourceOptions(parsed, usage) {
|
|
|
307
323
|
function validateExplicitRuntime(profile, store, options) {
|
|
308
324
|
if (profile.runtime.source !== "explicit")
|
|
309
325
|
return;
|
|
310
|
-
const runtime = resolveAgentProfileRuntime(profile, store);
|
|
326
|
+
const runtime = resolveAgentProfileRuntime(profile, store, { configurationOnly: true });
|
|
311
327
|
if (runtime.status === "unavailable")
|
|
312
328
|
throw usageError(runtime.reason);
|
|
313
329
|
options.validateAgentConfiguration?.({
|
|
@@ -320,6 +336,7 @@ function profileListRow(view) {
|
|
|
320
336
|
const { profile, runtime } = view;
|
|
321
337
|
return [
|
|
322
338
|
profile.id,
|
|
339
|
+
String(isEnabled(profile)),
|
|
323
340
|
String(profile.revision),
|
|
324
341
|
profile.defaultAccess,
|
|
325
342
|
runtime.source,
|