@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.
Files changed (118) hide show
  1. package/dist/cli/commandCatalog.js +19 -12
  2. package/dist/cli/interactionCandidates.js +20 -5
  3. package/dist/cli/interactionPolicy.js +5 -4
  4. package/dist/cli/roleWizard.js +7 -0
  5. package/dist/commands/controllerCommands.js +13 -4
  6. package/dist/commands/durableJobCommands.js +50 -3
  7. package/dist/commands/globalRoleCommands.js +31 -14
  8. package/dist/commands/inboxCommands.js +1 -0
  9. package/dist/commands/operatorCommands.js +12 -2
  10. package/dist/commands/profileCommands.js +19 -2
  11. package/dist/commands/roleConfiguration.js +3 -0
  12. package/dist/commands/taskCommands.js +16 -0
  13. package/dist/commands/taskRoleRuntimeStatus.js +3 -0
  14. package/dist/controlPlaneCli.js +21 -4
  15. package/dist/controller/agentJobControl.js +9 -1
  16. package/dist/controller/clientRuntime.js +34 -13
  17. package/dist/controller/controller.js +35 -22
  18. package/dist/controller/diagnosticJournal.js +214 -0
  19. package/dist/controller/fileSchedulerStoreAdapter.js +174 -105
  20. package/dist/controller/globalInputDelivery.js +19 -9
  21. package/dist/controller/globalRuntimeAttention.js +11 -3
  22. package/dist/controller/jobControl.js +1 -1
  23. package/dist/controller/jobDefinitionControl.js +24 -13
  24. package/dist/controller/jobSupervisor.js +4 -0
  25. package/dist/controller/providerRetryAdmission.js +9 -0
  26. package/dist/controller/providerRetryDelivery.js +16 -0
  27. package/dist/controller/runtime.js +80 -4
  28. package/dist/controller/runtimeLaunchCoordinator.js +3 -1
  29. package/dist/controller/workerSchedulerStore.js +24 -0
  30. package/dist/core/boundedRpc.js +4 -0
  31. package/dist/core/controllerServer.js +1 -1
  32. package/dist/core/controllerTelemetry.js +92 -19
  33. package/dist/executor/fileRoleLaunchPlanner.js +3 -0
  34. package/dist/job/globalShell.js +51 -0
  35. package/dist/job/jobBusinessResult.js +4 -3
  36. package/dist/job/jobDefinition.js +6 -1
  37. package/dist/job/jobRunner.js +9 -0
  38. package/dist/job/scheduleService.js +2 -0
  39. package/dist/message/globalInboxRecovery.js +30 -19
  40. package/dist/message/globalProviderRetry.js +7 -1
  41. package/dist/message/messageContinuation.js +4 -0
  42. package/dist/output/rolePresentation.js +3 -0
  43. package/dist/profile/agentProfile.js +8 -0
  44. package/dist/profile/agentProfileRuntime.js +16 -1
  45. package/dist/review/reviewDecision.js +6 -0
  46. package/dist/role/availability.js +28 -0
  47. package/dist/role/role.js +6 -0
  48. package/dist/runtime/builtinAgentErrorMappers.js +4 -0
  49. package/dist/runtime/codexNativeAccess.js +21 -4
  50. package/dist/runtime/globalSessionHealth.js +39 -0
  51. package/dist/runtime/tmuxAdapters.js +5 -0
  52. package/dist/scheduler/activeRoleRunDelivery.js +9 -0
  53. package/dist/scheduler/leaderWakeupProcessor.js +8 -1
  54. package/dist/scheduler/roleRunLiveness.js +3 -0
  55. package/dist/scheduler/roleRunStall.js +10 -10
  56. package/dist/storage/persistenceWorker.js +41 -19
  57. package/dist/storage/sqliteSchema.js +43 -9
  58. package/dist/storage/sqliteStore.js +86 -8
  59. package/dist/storage/storageVersions.js +1 -1
  60. package/dist/storage/storeRpc.js +25 -11
  61. package/dist/storage/taskReadRevisionSchema.js +39 -0
  62. package/dist/web/assets/client/app/catalog.js +0 -29
  63. package/dist/web/assets/client/app/jobs.js +115 -0
  64. package/dist/web/assets/client/app/main.js +32 -28
  65. package/dist/web/assets/client/app/manage.js +1 -0
  66. package/dist/web/assets/client/app/settings.js +1 -5
  67. package/dist/web/assets/client/app/taskView.js +7 -11
  68. package/dist/web/assets/client/i18n/en.js +265 -36
  69. package/dist/web/assets/client/i18n/zh-CN.js +264 -35
  70. package/dist/web/assets/client/index.js +4 -0
  71. package/dist/web/assets/client/layout/manageShell.js +61 -15
  72. package/dist/web/assets/client/layout/router.js +2 -1
  73. package/dist/web/assets/client/layout/shortcuts.js +2 -2
  74. package/dist/web/assets/client/layout/workspace.js +99 -112
  75. package/dist/web/assets/client/lib/prefs.js +1 -1
  76. package/dist/web/assets/client/ui/primitives.js +15 -2
  77. package/dist/web/assets/client/views/dock/conversation.js +421 -123
  78. package/dist/web/assets/client/views/dock/session.js +229 -0
  79. package/dist/web/assets/client/views/dock/terminal.js +48 -39
  80. package/dist/web/assets/client/views/jobs.js +1042 -160
  81. package/dist/web/assets/client/views/overview.js +2 -2
  82. package/dist/web/assets/client/views/sidebar.js +3 -0
  83. package/dist/web/assets/client/views/task/page.js +2 -12
  84. package/dist/web/assets/shared/manageNav.js +3 -1
  85. package/dist/web/assets/shell/appRail.js +2 -1
  86. package/dist/web/assets/shell/dock.js +9 -27
  87. package/dist/web/assets/shell/manage.js +7 -2
  88. package/dist/web/assets/shell/models.js +4 -3
  89. package/dist/web/assets/shell/settingsContent.js +0 -3
  90. package/dist/web/assets/shell/sidebar.js +0 -1
  91. package/dist/web/assets/styles/views/conversation.js +96 -17
  92. package/dist/web/assets/styles/views/dock.js +17 -5
  93. package/dist/web/assets/styles/views/jobs.js +301 -12
  94. package/dist/web/assets/styles/views/sidebar.js +1 -2
  95. package/dist/web/assets/styles/views/task.js +1 -2
  96. package/dist/web/assets/styles/views/terminal.js +12 -8
  97. package/dist/web/webConversation.js +1 -0
  98. package/dist/web/webServer.js +28 -1
  99. package/dist/web/webSettings.js +10 -2
  100. package/docs/global-shell-jobs.md +78 -0
  101. package/docs/job-runs.md +6 -3
  102. package/docs/managed-turn-and-session-runtime.md +84 -0
  103. package/docs/managed-turn-and-session-runtime.zh-CN.md +57 -0
  104. package/docs/release-workflow.md +67 -0
  105. package/docs/release-workflow.zh-CN.md +48 -0
  106. package/docs/roles-and-configuration.md +45 -1
  107. package/docs/roles-and-configuration.zh-CN.md +35 -0
  108. package/docs/storage-baseline.md +19 -1
  109. package/docs/storage-baseline.zh-CN.md +10 -1
  110. package/docs/testing/review-evaluation.md +35 -1
  111. package/docs/testing/review-evaluation.zh-CN.md +25 -1
  112. package/docs/testing/verification-levels.md +2 -2
  113. package/docs/testing/verification-levels.zh-CN.md +2 -2
  114. package/package.json +1 -1
  115. package/skills/yui-leader/VERSION +1 -1
  116. package/skills/yui-leader/references/execution.md +19 -8
  117. package/skills/yui-reviewer/SKILL.md +13 -2
  118. 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" || name === "update" ? " --title <text> --input <configuration-json>" : "")
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", {}), ["id", "revision", "defaultAccess", "agentId"]);
25
- case "global-roles":
26
- return entities("global role", "Select global Role", await list(ports, "role.list", {}), ["name", "activeAgentId", "workspace"]);
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 },
@@ -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
- const queueDepth = (typeof routes?.inFlight === "number" ? routes.inFlight : 0)
167
- + (typeof dispatcher?.inFlight === "number" ? dispatcher.inFlight : 0);
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)})`, ` Command queue depth ${runtime.queueDepth ?? UNSUPPORTED} · oldest ${runtime.oldestInFlightAgeMs === undefined
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 (!["--global", "--task", "--job", "--title", "--input", "--revision", "--request-id", "--retry-of", "--run", "--artifact", "--reference", "--offset", "--result", "--expected-session", "--expected-attempt"].includes(flag)
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, providerRetrySummary, renderProviderRetry } from "../runtime/providerRetry.js";
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
- effectiveRevision: role.launchRevision,
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: sessions?.providerBinding?.run == null ? null : {
193
- attemptId: sessions.providerBinding.run.attemptId,
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: providerRetrySummary(sessions?.providerBinding),
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
- }) + "\n" : renderProviderRetry(binding) + "\n";
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 projection = projectOperatorStatus(store.getGlobalRoleSessionSet(role.name), role.activeAgentId, activeBinding.adapterId);
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,