@zq-silk/yui 0.7.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/ARCHITECTURE.md +27 -28
  2. package/README.md +79 -71
  3. package/dist/cli/commandCatalog.js +283 -136
  4. package/dist/cli/completion.js +3 -3
  5. package/dist/cli/helpRenderer.js +3 -0
  6. package/dist/cli/interactionPolicy.js +48 -33
  7. package/dist/cli/interactiveSelection.js +1 -1
  8. package/dist/cli/invocationRouter.js +3 -2
  9. package/dist/cli/roleWizard.js +8 -8
  10. package/dist/cli.js +189 -93
  11. package/dist/commands/agentCommands.js +5 -5
  12. package/dist/commands/configCommands.js +351 -104
  13. package/dist/commands/configOverview.js +60 -0
  14. package/dist/commands/deliveryGuardPreflight.js +2 -2
  15. package/dist/commands/globalRoleCommands.js +9 -9
  16. package/dist/commands/profileCommands.js +8 -8
  17. package/dist/commands/resourcesCommands.js +6 -5
  18. package/dist/commands/taskCommands.js +111 -59
  19. package/dist/commands/taskRoleRuntimeStatus.js +3 -1
  20. package/dist/commands/telemetryCommands.js +11 -6
  21. package/dist/config/configCatalog.js +42 -0
  22. package/dist/config/yuiConfig.js +80 -35
  23. package/dist/context/sessionBootstrapManifest.js +1 -1
  24. package/dist/controller/clientRuntime.js +0 -2
  25. package/dist/controller/controller.js +21 -9
  26. package/dist/controller/fileSchedulerStoreAdapter.js +409 -79
  27. package/dist/controller/resourceInventory.js +9 -5
  28. package/dist/controller/runtime.js +112 -25
  29. package/dist/controller/runtimeLaunchCoordinator.js +18 -78
  30. package/dist/controller/structuredProviderObservation.js +273 -0
  31. package/dist/doctor/doctor.js +2 -2
  32. package/dist/executor/agentAdapter.js +40 -0
  33. package/dist/executor/agentExecutor.js +31 -7
  34. package/dist/executor/executorRegistry.js +11 -49
  35. package/dist/executor/fileRoleLaunchPlanner.js +115 -37
  36. package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
  37. package/dist/resources/autoResourceGc.js +3 -1
  38. package/dist/review/reviewConfig.js +0 -2
  39. package/dist/run/agentRun.js +2 -2
  40. package/dist/run/providerRetry.js +29 -16
  41. package/dist/run/providerRetryConfig.js +5 -3
  42. package/dist/runtime/agentHost.js +767 -158
  43. package/dist/runtime/builtinAgentDrivers.js +1 -5
  44. package/dist/runtime/codexAppServerRuntime.js +67 -60
  45. package/dist/runtime/exactControlPlane.js +7 -2
  46. package/dist/runtime/index.js +6 -2
  47. package/dist/runtime/launchBroker.js +30 -8
  48. package/dist/runtime/launchDiagnostics.js +1 -1
  49. package/dist/runtime/providerAuthorityFence.js +24 -0
  50. package/dist/runtime/providerControl.js +63 -0
  51. package/dist/runtime/providerRecoveryDecision.js +55 -0
  52. package/dist/runtime/providerRuntimeIdentity.js +269 -19
  53. package/dist/runtime/runtimeBinding.js +20 -11
  54. package/dist/runtime/structuredProviderHost.js +476 -0
  55. package/dist/runtime/tmuxAdapters.js +143 -42
  56. package/dist/scheduler/activeRoleRunDelivery.js +206 -120
  57. package/dist/scheduler/leaderWakeupProcessor.js +141 -16
  58. package/dist/scheduler/roleRunStall.js +12 -9
  59. package/dist/setup/setupCommand.js +153 -492
  60. package/dist/storage/compatibleTaskStore.js +9 -5
  61. package/dist/storage/migration/productionRegistry.js +169 -0
  62. package/dist/storage/taskStore.js +22 -3
  63. package/dist/telemetry/sqliteTelemetryStore.js +9 -1
  64. package/dist/telemetry/telemetryConfig.js +1 -18
  65. package/dist/telemetry/telemetryStore.js +2 -2
  66. package/dist/telemetry/telemetryWiring.js +6 -5
  67. package/dist/tmux/tmuxManager.js +1 -1
  68. package/dist/web/webSnapshot.js +5 -3
  69. package/i18n/README.zh-CN.md +48 -40
  70. package/package.json +1 -1
  71. package/skills/yui-leader/SKILL.md +12 -5
  72. package/skills/yui-operator/SKILL.md +44 -6
  73. package/skills/yui-runtime/SKILL.md +1 -1
package/dist/cli.js CHANGED
@@ -5,6 +5,7 @@ import { createInterface } from "node:readline/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { isDeepStrictEqual } from "node:util";
7
7
  import { renderCommandHelp } from "./cli/helpRenderer.js";
8
+ import { describeCommandTree, findCommandNode } from "./cli/commandCatalog.js";
8
9
  import { routeInvocation } from "./cli/invocationRouter.js";
9
10
  import { renderCompletion } from "./cli/completion.js";
10
11
  import { resolveCompletionCandidates } from "./cli/dynamicCompletion.js";
@@ -16,12 +17,14 @@ import { resolveOperatorWizardArguments } from "./cli/operatorWizard.js";
16
17
  import { runUpdateCommand } from "./cli/updateCommand.js";
17
18
  import { runUpgradeCommand } from "./cli/upgradeCommand.js";
18
19
  import { formatTimestamp } from "./output/timePresentation.js";
19
- import { resolveTmuxBin } from "./config/yuiConfig.js";
20
+ import { resolveTmuxBin, resolveTmuxHistoryLimit } from "./config/yuiConfig.js";
20
21
  import { renderAgentConfigurationCatalog } from "./output/agentConfigurationPresentation.js";
21
22
  import { nativeAgentEnvironmentNames } from "./agent/launchEnvironment.js";
22
23
  import { runAgentCommand } from "./commands/agentCommands.js";
23
24
  import { runGlobalRoleCommand } from "./commands/globalRoleCommands.js";
24
25
  import { runConfigCommand } from "./commands/configCommands.js";
26
+ import { CONFIG_DOMAINS } from "./config/configCatalog.js";
27
+ import { runConfigOverview } from "./commands/configOverview.js";
25
28
  import { parseControllerCleanupOptions, parseControllerStatusOptions, parseControllerRuntimeSnapshot, renderControllerResourceStatus, renderRuntimeIdentitySection, summarizeDurablePhysicalMismatch, runInteractiveControllerCleanup } from "./commands/controllerCommands.js";
26
29
  import { parseExecutionAuditOptions, runExecutionAuditCommand } from "./commands/executionAuditCommands.js";
27
30
  import { parseSessionReconcileOptions, runSessionReconcileCommand } from "./commands/sessionCommands.js";
@@ -33,7 +36,7 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
33
36
  import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
34
37
  import { runProjectCommand } from "./commands/projectCommands.js";
35
38
  import { runProfileCommand } from "./commands/profileCommands.js";
36
- import { dispatchPreparedReviewRound, failPendingReviewRound, assertTaskRoleWritableAttachAvailable, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
39
+ import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
37
40
  import { taskActor } from "./commands/taskActor.js";
38
41
  import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
39
42
  import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
@@ -58,9 +61,9 @@ import { runSessionNotifyCommand } from "./controller/sessionNotify.js";
58
61
  import { openSchedulerTelemetry } from "./telemetry/telemetryWiring.js";
59
62
  import { runRuntimeObservationHookCommand } from "./controller/runtimeObservationHook.js";
60
63
  import { buildDoctorReport, renderDoctor, runDoctorCommand } from "./doctor/doctor.js";
61
- import { agentNotFound, CliError, usageError } from "./errors/cliError.js";
64
+ import { agentNotFound, CliError, runtimeError, usageError } from "./errors/cliError.js";
62
65
  import { FileRoleLaunchPlanner } from "./executor/fileRoleLaunchPlanner.js";
63
- import { runAgentHost } from "./runtime/agentHost.js";
66
+ import { AGENT_HOST_CONTROL_PROTOCOL, runAgentHost, sendAgentHostAuthorityControl } from "./runtime/agentHost.js";
64
67
  import { TaskWorkspaceCoordinator, WorkspaceCleanupBlockedError } from "./repository/taskWorkspaceCoordinator.js";
65
68
  import { FileTaskWorkspacePreparer, ReviewRoundWorkspaceEvidenceError } from "./repository/taskWorkspacePreparer.js";
66
69
  import { inspectStorageSchema } from "./storage/storageSchema.js";
@@ -168,10 +171,25 @@ export async function main() {
168
171
  }
169
172
  throw usageError("Release usage: yui release install <source-dir> | list | activate [release-id].");
170
173
  }
171
- if (args[0] === "completion") {
174
+ if (args[0] === "config" && args[1] === "completion") {
172
175
  await completionCommand(home, invocation.node);
173
176
  return;
174
177
  }
178
+ if (args[0] === "config" && args[1] === "describe") {
179
+ const describeNode = findCommandNode(["config", "describe"]);
180
+ if (describeNode === undefined)
181
+ throw new Error("Config describe command is missing from the catalog.");
182
+ const domain = args[2];
183
+ const domains = describeNode.argumentValues[0] ?? [];
184
+ if (args.length > 3 || (domain !== undefined && !domains.includes(domain))) {
185
+ throw usageError(`Config describe usage: ${describeNode.usage.join(" | ")}.`);
186
+ }
187
+ const target = findCommandNode(domain === undefined ? ["config"] : ["config", domain]);
188
+ if (target === undefined)
189
+ throw new Error(`Config domain is missing from the catalog: ${domain}.`);
190
+ emit(renderCommandHelp(target, VERSION), false, describeCommandTree(target));
191
+ return;
192
+ }
175
193
  if (args[0] === "setup") {
176
194
  if (jsonOutput)
177
195
  throw usageError("Setup does not support --json.");
@@ -261,13 +279,13 @@ export async function main() {
261
279
  }
262
280
  throw usageError("Internal lifecycle callback usage is invalid.");
263
281
  }
264
- if (args[0] === "session") {
265
- if (args[1] !== "reconcile") {
266
- throw usageError("Session usage: yui session reconcile [--report] [--cleanup].");
267
- }
282
+ if (args[0] === "session" && args[1] === "reconcile") {
268
283
  const options = parseSessionReconcileOptions(args.slice(2));
269
284
  const store = openCompatibleFileTaskStore(home);
270
- const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), new NodeCommandExecutor(), { yuiHome: home });
285
+ const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), new NodeCommandExecutor(), {
286
+ yuiHome: home,
287
+ historyLimit: resolveTmuxHistoryLimit(store.getConfig().tmuxHistoryLimit)
288
+ });
271
289
  const reconciliation = new SessionOwnerReconciliation({
272
290
  home,
273
291
  store,
@@ -474,6 +492,7 @@ export async function main() {
474
492
  const executor = new NodeCommandExecutor();
475
493
  const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), executor, {
476
494
  yuiHome: home,
495
+ historyLimit: resolveTmuxHistoryLimit(store.getConfig().tmuxHistoryLimit),
477
496
  terminalInput: process.stdin,
478
497
  onWarning: (message) => process.stderr.write(`Warning: ${message}\n`)
479
498
  });
@@ -531,77 +550,102 @@ export async function main() {
531
550
  process.stdout.write(`Yui web control room: http://${displayHost}:${options.port}\n`);
532
551
  return;
533
552
  }
534
- if (resolved[0] === "agent") {
535
- const agentArgs = resolved.slice(1);
536
- if (agentArgs[0] === "capabilities") {
537
- if (agentArgs.length !== 2) {
538
- throw usageError("Agent capabilities usage: yui agent capabilities <agent-id>");
553
+ if (resolved[0] === "config") {
554
+ const domain = resolved[1];
555
+ const roleOptions = {
556
+ yuiHome: home,
557
+ env: process.env
558
+ };
559
+ if (domain === "show") {
560
+ const result = runConfigOverview(resolved.slice(2), store, process.env, resolveCliIdentity(process.env), roleOptions);
561
+ emit(result.output, false, result.data);
562
+ return;
563
+ }
564
+ if (CONFIG_DOMAINS.includes(domain ?? "")) {
565
+ const configDomain = domain;
566
+ const domainArgs = resolved.slice(2);
567
+ const result = runConfigCommand(configDomain, domainArgs, store);
568
+ if (domainArgs[0] === "set"
569
+ && domainArgs[1] === "reconciliation-interval-seconds") {
570
+ const refresh = await refreshRunningFileTaskControllerConfiguration(home, { environment: process.env });
571
+ emit(withControllerRefreshWarning(result.output, refresh, "Controller configuration"));
572
+ return;
539
573
  }
540
- const agent = store.getConfiguredAgent(agentArgs[1] ?? "");
541
- if (agent === null)
542
- throw agentNotFound(agentArgs[1] ?? "");
543
- const result = await catalogs.resolve({
544
- agent,
545
- cwd: store.getConfig().defaultWorkspace ?? process.cwd()
546
- });
547
- emit(renderAgentConfigurationCatalog(result), false, result);
574
+ emit(result.output, false, result.data);
548
575
  return;
549
576
  }
550
- const affectedAgentId = agentArgs[1];
551
- const previousAgent = typeof affectedAgentId === "string"
552
- ? store.getConfiguredAgent(affectedAgentId)
553
- : null;
554
- const output = runAgentCommand(agentArgs, store);
555
- if (agentArgs[0] === "add"
556
- || agentArgs[0] === "update"
557
- || agentArgs[0] === "remove") {
558
- const currentAgent = typeof affectedAgentId === "string"
577
+ if (domain === "agent") {
578
+ const agentArgs = resolved.slice(2);
579
+ if (agentArgs[0] === "capabilities") {
580
+ if (agentArgs.length !== 2) {
581
+ throw usageError("Agent capabilities usage: yui config agent capabilities <agent-id>");
582
+ }
583
+ const agent = store.getConfiguredAgent(agentArgs[1] ?? "");
584
+ if (agent === null)
585
+ throw agentNotFound(agentArgs[1] ?? "");
586
+ const result = await catalogs.resolve({
587
+ agent,
588
+ cwd: store.getConfig().defaultWorkspace ?? process.cwd()
589
+ });
590
+ emit(renderAgentConfigurationCatalog(result), false, result);
591
+ return;
592
+ }
593
+ const affectedAgentId = agentArgs[1];
594
+ const previousAgent = typeof affectedAgentId === "string"
559
595
  ? store.getConfiguredAgent(affectedAgentId)
560
596
  : null;
561
- const capabilityNotice = currentAgent !== null
562
- && (agentArgs[0] === "add" || agentArgs[0] === "update")
563
- ? renderAgentConfigurationResolutionNotice(await catalogs.resolve({
564
- agent: currentAgent,
565
- cwd: store.getConfig().defaultWorkspace ?? process.cwd()
566
- }))
567
- : "";
568
- const scope = agentEnvironmentRefreshScope(previousAgent, currentAgent, store.listConfiguredAgents());
569
- const refresh = await refreshRunningFileTaskControllerEnvironment(home, store, process.env, scope);
570
- emit(withControllerRefreshWarning(`${output.trimEnd()}${capabilityNotice.length === 0 ? "\n" : `\n${capabilityNotice}`}`, refresh, "Agent environment"));
597
+ const output = runAgentCommand(agentArgs, store);
598
+ if (agentArgs[0] === "add"
599
+ || agentArgs[0] === "update"
600
+ || agentArgs[0] === "remove") {
601
+ const currentAgent = typeof affectedAgentId === "string"
602
+ ? store.getConfiguredAgent(affectedAgentId)
603
+ : null;
604
+ const capabilityNotice = currentAgent !== null
605
+ && (agentArgs[0] === "add" || agentArgs[0] === "update")
606
+ ? renderAgentConfigurationResolutionNotice(await catalogs.resolve({
607
+ agent: currentAgent,
608
+ cwd: store.getConfig().defaultWorkspace ?? process.cwd()
609
+ }))
610
+ : "";
611
+ const scope = agentEnvironmentRefreshScope(previousAgent, currentAgent, store.listConfiguredAgents());
612
+ const refresh = await refreshRunningFileTaskControllerEnvironment(home, store, process.env, scope);
613
+ emit(withControllerRefreshWarning(`${output.trimEnd()}${capabilityNotice.length === 0 ? "\n" : `\n${capabilityNotice}`}`, refresh, "Agent environment"));
614
+ return;
615
+ }
616
+ emit(output);
571
617
  return;
572
618
  }
573
- emit(output);
574
- return;
575
- }
576
- if (resolved[0] === "config") {
577
- const configArgs = resolved.slice(1);
578
- const result = runConfigCommand(configArgs, store);
579
- if (configArgs[0] === "set"
580
- && configArgs[1] === "reconciliation-interval-seconds") {
581
- const refresh = await refreshRunningFileTaskControllerConfiguration(home, { environment: process.env });
582
- emit(withControllerRefreshWarning(result.output, refresh, "Controller configuration"));
619
+ if (domain === "profile") {
620
+ const result = runProfileCommand(resolved.slice(2), store);
621
+ emit(result.output, false, result.data);
583
622
  return;
584
623
  }
585
- emit(result.output, false, result.data);
586
- return;
624
+ if (domain === "role") {
625
+ const result = runGlobalRoleCommand(resolved.slice(2), store, roleOptions);
626
+ if (typeof result !== "string") {
627
+ throw new Error("Config Role commands cannot enter a runtime Session.");
628
+ }
629
+ emit(result);
630
+ return;
631
+ }
632
+ throw usageError(`Unknown configuration domain: ${domain ?? ""}.`);
587
633
  }
588
634
  if (resolved[0] === "project") {
589
635
  const result = await runProjectCommand(resolved.slice(1), store, { environment: process.env });
590
636
  emit(result.output, false, result.data);
591
637
  return;
592
638
  }
593
- if (resolved[0] === "profile") {
594
- const result = runProfileCommand(resolved.slice(1), store);
595
- emit(result.output, false, result.data);
596
- return;
597
- }
598
- if (resolved[0] === "role") {
639
+ if (resolved[0] === "session") {
599
640
  const roleOptions = {
600
641
  yuiHome: home,
601
642
  env: process.env,
602
643
  jsonOutput
603
644
  };
604
- const result = runGlobalRoleCommand(resolved.slice(1), store, roleOptions);
645
+ const sessionArgs = resolved[1] === "enter" || resolved[1] === "context"
646
+ ? [resolved[1], ...resolved.slice(2)]
647
+ : ["session", resolved[1] ?? "", ...resolved.slice(2)];
648
+ const result = runGlobalRoleCommand(sessionArgs, store, roleOptions);
605
649
  if (typeof result === "string") {
606
650
  emit(result);
607
651
  return;
@@ -1104,30 +1148,82 @@ export async function main() {
1104
1148
  : { command: result.data, ...reviewData });
1105
1149
  return;
1106
1150
  }
1107
- if (result.output !== undefined)
1108
- emit(result.output);
1109
- try {
1110
- tmux.attachRole(result.taskId, result.roleName, result.access, {
1111
- ...(result.access === "read-write"
1112
- ? {
1113
- revalidateWritableAttach: () => {
1114
- assertTaskRoleWritableAttachAvailable(store, result.taskId, result.roleName, {
1115
- isManagedProcessRunning: () => (tmux.probeRoleStatus(result.taskId, result.roleName) === "running")
1116
- });
1117
- }
1151
+ if (jsonOutput) {
1152
+ throw usageError("Task Role view/takeover requires an interactive terminal.");
1153
+ }
1154
+ if (result.kind === "view") {
1155
+ if (result.output !== undefined)
1156
+ emit(result.output);
1157
+ tmux.attachRole(result.taskId, result.roleName, "read-only");
1158
+ return;
1159
+ }
1160
+ const syncAuthority = async (authorityResult) => {
1161
+ let control;
1162
+ try {
1163
+ control = await sendAgentHostAuthorityControl({
1164
+ home,
1165
+ scope: "task",
1166
+ taskId: authorityResult.taskId,
1167
+ roleName: authorityResult.roleName,
1168
+ control: {
1169
+ protocol: AGENT_HOST_CONTROL_PROTOCOL,
1170
+ type: "set-authority",
1171
+ nativeSessionId: authorityResult.nativeSessionId,
1172
+ authority: authorityResult.authority
1118
1173
  }
1119
- : {})
1174
+ });
1175
+ }
1176
+ catch (error) {
1177
+ throw runtimeError(`Agent Host authority synchronization failed at epoch ${authorityResult.authority.epoch}: `
1178
+ + `${error instanceof Error ? error.message : String(error)}. `
1179
+ + `Durable authority is ${authorityResult.authority.owner}-owned; retry `
1180
+ + `'yui task role release ${authorityResult.taskId} ${authorityResult.roleName}' `
1181
+ + "to reconcile the Host.");
1182
+ }
1183
+ if (control.outcome !== "accepted"
1184
+ || control.snapshot.nativeSessionId !== authorityResult.nativeSessionId
1185
+ || control.snapshot.authorityEpoch !== authorityResult.authority.epoch
1186
+ || control.snapshot.authorityOwner !== authorityResult.authority.owner
1187
+ || control.snapshot.authorityHolderId !== authorityResult.authority.holderId) {
1188
+ throw runtimeError(`Agent Host did not accept Provider authority epoch ${authorityResult.authority.epoch}: `
1189
+ + (control.snapshot.detail ?? control.outcome)
1190
+ + `. Durable authority is ${authorityResult.authority.owner}-owned; `
1191
+ + "retry 'yui task role release "
1192
+ + `${authorityResult.taskId} ${authorityResult.roleName}' to reconcile the Host.`);
1193
+ }
1194
+ return control;
1195
+ };
1196
+ await syncAuthority(result);
1197
+ emit(result.output);
1198
+ if (result.action === "release") {
1199
+ runtime.notifyMailboxChanged({
1200
+ kind: "role",
1201
+ taskId: result.taskId,
1202
+ roleName: result.roleName
1120
1203
  });
1204
+ return;
1205
+ }
1206
+ process.stdout.write("Provider input is now routed through the Agent Host PTY gateway. "
1207
+ + "Use tmux detach (Ctrl-b d) to return authority to the Controller.\n");
1208
+ try {
1209
+ tmux.attachRole(result.taskId, result.roleName, "read-write");
1121
1210
  }
1122
1211
  finally {
1123
- if (result.access === "read-write") {
1124
- // A Run claimed while the writer lease was visible is intentionally
1125
- // paused. Releasing the lease only signals that existing durable
1126
- // work may be reconsidered; it never creates or wakes a Run.
1212
+ const currentTask = store.getTask(result.taskId);
1213
+ // Completing or retiring the Task from inside the takeover Turn owns
1214
+ // Provider shutdown and clears the live binding. Do not turn that
1215
+ // successful terminal transition into a failing best-effort release.
1216
+ if (currentTask?.status === "active") {
1217
+ const released = runTaskCommand(["role", "release", result.taskId, result.roleName], store, { runtime, environment: process.env, yuiHome: home });
1218
+ if (released.kind !== "authority" || released.action !== "release") {
1219
+ throw runtimeError("Provider authority release returned an invalid result.");
1220
+ }
1221
+ await syncAuthority(released);
1222
+ emit(released.output);
1127
1223
  runtime.notifyMailboxChanged({
1128
1224
  kind: "role",
1129
- taskId: result.taskId,
1130
- roleName: result.roleName
1225
+ taskId: released.taskId,
1226
+ roleName: released.roleName
1131
1227
  });
1132
1228
  }
1133
1229
  }
@@ -1659,7 +1755,7 @@ async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReview
1659
1755
  const config = store.getReviewConfig();
1660
1756
  if (!deltaRecheckEnabled(config)) {
1661
1757
  throw usageError("Delta-recheck is not enabled for this Project's review policy. "
1662
- + "Set `yui config set review --role <role> --trigger final --delta-recheck enabled` "
1758
+ + "Set `yui config workflow set review --role <role> --trigger final --delta-recheck enabled` "
1663
1759
  + "or request a full Review.");
1664
1760
  }
1665
1761
  const reviewConfig = config;
@@ -1809,7 +1905,7 @@ async function executeOperatorSessionControl(control, home, store, runtime, tmux
1809
1905
  process.stdout.write("Cancelled.\n");
1810
1906
  return;
1811
1907
  }
1812
- const updated = runGlobalRoleCommand(resolution.args.slice(1), store, { yuiHome: home, env: process.env });
1908
+ const updated = runGlobalRoleCommand(resolution.args.slice(2), store, { yuiHome: home, env: process.env });
1813
1909
  if (typeof updated !== "string") {
1814
1910
  throw new Error("Operator Agent configuration returned an invalid control result.");
1815
1911
  }
@@ -1865,11 +1961,11 @@ function renderControllerResult(method, value) {
1865
1961
  : "Controller was already stopped.";
1866
1962
  }
1867
1963
  async function completionCommand(home, node) {
1868
- if (args[1] === "candidates") {
1869
- const separator = args.indexOf("--");
1870
- const prefix = args[2];
1871
- if (prefix === undefined || separator !== 3) {
1872
- throw usageError("Completion candidates usage: yui completion candidates <prefix> -- <words...>");
1964
+ if (args[2] === "candidates") {
1965
+ const separator = args.indexOf("--", 4);
1966
+ const prefix = args[3];
1967
+ if (prefix === undefined || separator !== 4) {
1968
+ throw usageError("Completion candidates usage: yui config completion candidates <prefix> -- <words...>");
1873
1969
  }
1874
1970
  const candidates = await resolveCompletionCandidates({
1875
1971
  current: prefix,
@@ -1884,9 +1980,9 @@ async function completionCommand(home, node) {
1884
1980
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
1885
1981
  throw usageError("Completion configuration requires an interactive terminal.");
1886
1982
  }
1887
- const shell = completionShell(args[1]);
1888
- if (args.length > (shell === undefined ? 1 : 2)) {
1889
- throw usageError("Completion usage: yui completion [bash|zsh|fish]");
1983
+ const shell = completionShell(args[2]);
1984
+ if (args.length > (shell === undefined ? 2 : 3)) {
1985
+ throw usageError("Completion usage: yui config completion [bash|zsh|fish]");
1890
1986
  }
1891
1987
  const store = openCompatibleFileTaskStore(home);
1892
1988
  const ioHandle = terminalIo();
@@ -1921,7 +2017,7 @@ async function resolveTerminalArguments(commandArgs, node, store, catalogs) {
1921
2017
  // Global Role add owns its Agent choice so the configured default can be
1922
2018
  // shown explicitly. Other commands first resolve missing positional
1923
2019
  // targets through the generic selector, then enter the focused Role UI.
1924
- if ((operatorArgs[0] === "role" && operatorArgs[1] === "add")
2020
+ if ((operatorArgs[0] === "config" && operatorArgs[1] === "role" && operatorArgs[2] === "add")
1925
2021
  || (operatorArgs[0] === "task" && operatorArgs[1] === "role" && operatorArgs[2] === "add")) {
1926
2022
  const wizard = await resolveRoleWizardArguments(operatorArgs, ports, handle.io);
1927
2023
  if (wizard.kind === "cancelled")
@@ -1983,7 +2079,7 @@ async function preflightAgentConfigurationMutation(commandArgs, store, catalogs)
1983
2079
  });
1984
2080
  }
1985
2081
  function hasModelOrEffortMutation(args) {
1986
- const operation = args[0] === "role"
2082
+ const operation = (args[0] === "config" && args[1] === "role")
1987
2083
  || (args[0] === "task" && args[1] === "role");
1988
2084
  return operation && [
1989
2085
  "--model", "--effort", "--clear-model", "--clear-effort"
@@ -1993,8 +2089,8 @@ function configurationMutationAgentId(args, store) {
1993
2089
  const explicit = optionValue(args, "--agent");
1994
2090
  if (explicit !== undefined)
1995
2091
  return explicit;
1996
- if (args[0] === "role" && args[1] === "update") {
1997
- return store.getGlobalRole(args[2] ?? "")?.activeAgentId;
2092
+ if (args[0] === "config" && args[1] === "role" && args[2] === "update") {
2093
+ return store.getGlobalRole(args[3] ?? "")?.activeAgentId;
1998
2094
  }
1999
2095
  if (args[0] === "task" && args[1] === "role") {
2000
2096
  if (args[2] === "add")
@@ -15,7 +15,7 @@ export function runAgentCommand(args, store) {
15
15
  default:
16
16
  throw usageError(command === undefined
17
17
  ? "Agent command is required."
18
- : `Unknown command: agent ${command}`);
18
+ : `Unknown command: config agent ${command}`);
19
19
  }
20
20
  }
21
21
  function addAgent(args, store) {
@@ -38,12 +38,12 @@ function addAgent(args, store) {
38
38
  }
39
39
  const created = store.createConfiguredAgentIfAbsent(agent);
40
40
  if (created === null) {
41
- throw usageError(`Agent already exists: ${id}. Use yui agent update to change it.`);
41
+ throw usageError(`Agent already exists: ${id}. Use yui config agent update to change it.`);
42
42
  }
43
43
  return renderAgent(`Added agent ${id}`, created);
44
44
  }
45
45
  function listAgents(args, store) {
46
- assertNoArguments(args, "Agent list usage: yui agent list");
46
+ assertNoArguments(args, "Agent list usage: yui config agent list");
47
47
  const agents = store.listConfiguredAgents();
48
48
  if (agents.length === 0)
49
49
  return "No agents configured.\n";
@@ -62,7 +62,7 @@ function listAgents(args, store) {
62
62
  function showAgent(args, store) {
63
63
  const [rawId, ...rest] = args;
64
64
  const id = agentId(rawId);
65
- assertNoArguments(rest, "Agent show usage: yui agent show <agent-id>");
65
+ assertNoArguments(rest, "Agent show usage: yui config agent show <agent-id>");
66
66
  const agent = store.getConfiguredAgent(id);
67
67
  if (agent === null)
68
68
  throw agentNotFound(id);
@@ -137,7 +137,7 @@ function updateAgent(args, store) {
137
137
  function removeAgent(args, store) {
138
138
  const [rawId, ...rest] = args;
139
139
  const id = agentId(rawId);
140
- assertNoArguments(rest, "Agent remove usage: yui agent remove <agent-id>");
140
+ assertNoArguments(rest, "Agent remove usage: yui config agent remove <agent-id>");
141
141
  const removed = store.transaction((tx) => {
142
142
  if (tx.getConfiguredAgent(id) === null)
143
143
  return false;