@zq-silk/yui 0.15.12 → 0.16.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 (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -1,11 +1,11 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
- import { agentNotFound, usageError } from "../errors/cliError.js";
3
- import { defaultTableWidth, renderTable } from "../output/table.js";
4
2
  import { isAgentAdapterId, supportedAgentAdapterIds } from "../agent/adapterCatalog.js";
5
- import { adapterIdForExecutionComponent, agentExecutionComponentLabel, defaultExecutionComponentForAdapter, isAgentExecutionComponentId, supportedAgentExecutionComponentIds } from "../agent/executionComponents.js";
6
3
  import { createConfiguredAgent, validateConfiguredAgent } from "../agent/agent.js";
7
- import { LIVE_SESSION_ACKNOWLEDGEMENT_OPTION } from "./roleRuntimeGuard.js";
4
+ import { adapterIdForExecutionComponent, agentExecutionComponentLabel, defaultExecutionComponentForAdapter, isAgentExecutionComponentId, supportedAgentExecutionComponentIds } from "../agent/executionComponents.js";
5
+ import { agentNotFound, usageError } from "../errors/cliError.js";
6
+ import { defaultTableWidth, renderTable } from "../output/table.js";
8
7
  import { hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
8
+ import { LIVE_SESSION_ACKNOWLEDGEMENT_OPTION } from "./roleRuntimeGuard.js";
9
9
  export function runAgentCommand(args, store) {
10
10
  const [command, ...rest] = args;
11
11
  switch (command) {
@@ -1,5 +1,5 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
- import { resolveJobCaller } from "./taskActor.js";
2
+ import { resolveJobCaller } from "../task/taskAuthority.js";
3
3
  import { callFileTaskController } from "../controller/clientRuntime.js";
4
4
  export async function runCapabilityCommand(args, home, environment) {
5
5
  const [action, ...rest] = args;
@@ -1,15 +1,14 @@
1
1
  import { mkdirSync, realpathSync } from "node:fs";
2
2
  import { isAbsolute, relative, resolve } from "node:path";
3
- import { usageError } from "../errors/cliError.js";
4
- import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_LEADER_SEMANTIC_BUDGET_RUNS, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, DEFAULT_TMUX_HISTORY_LIMIT, LEADER_NEXT_ACTION_MODES, reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetRuns, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
5
3
  import { CONFIG_DEFINITIONS, CONFIG_KEYS, configDefinition, configDefinitionsForDomain } from "../config/configCatalog.js";
6
- import { resolveTimeZone } from "../output/timePresentation.js";
4
+ import { resolveTimeZone } from "../config/timeZone.js";
5
+ import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, DEFAULT_TMUX_HISTORY_LIMIT, reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
6
+ import { usageError } from "../errors/cliError.js";
7
7
  import { defaultTableWidth, renderTable } from "../output/table.js";
8
8
  import { REVIEW_TRIGGERS } from "../review/reviewConfig.js";
9
9
  /**
10
10
  * One uniform key model for every durable setting. Each catalog domain uses
11
- * the same show/set/clear contract; strategy settings such as `review` and
12
- * `leader-next-action` are keys like any other.
11
+ * the same show/set/clear contract, including the explicit review policy.
13
12
  */
14
13
  export { CONFIG_KEYS } from "../config/configCatalog.js";
15
14
  const CONFIG_PROPERTIES = Object.fromEntries(CONFIG_DEFINITIONS.map(({ key, property }) => [
@@ -20,7 +19,6 @@ const TIME_ZONE_SET_USAGE = "System config set usage: yui config system set time
20
19
  const RECONCILIATION_SET_USAGE = "Runtime config set usage: yui config runtime set reconciliation-interval-seconds <5-300>.";
21
20
  const RESOURCES_GC_MODE_SET_USAGE = "Resources config set usage: yui config resources set resources-gc-mode <report|quarantine>.";
22
21
  const RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE = "Resources config set usage: yui config resources set resources-gc-auto-quarantine <true|false>.";
23
- const LEADER_NEXT_ACTION_SET_USAGE = `Workflow config set usage: yui config workflow set leader-next-action <${LEADER_NEXT_ACTION_MODES.join("|")}>.`;
24
22
  const REVIEW_SET_USAGE = "Workflow config set usage: yui config workflow set review --role <global-role> --trigger <always|leader|final>.";
25
23
  export function runConfigCommand(domain, args, store) {
26
24
  const keys = configDefinitionsForDomain(domain).map(({ key }) => key);
@@ -65,7 +63,6 @@ export function effectiveConfigData(config, domain) {
65
63
  lastTaskId: config.lastTaskId ?? null,
66
64
  reconciliationIntervalSeconds: config.reconciliationIntervalSeconds
67
65
  ?? DEFAULT_RECONCILIATION_INTERVAL_SECONDS,
68
- leaderNextActionMode: resolveLeaderNextActionMode(config.leaderNextActionMode),
69
66
  resourcesGcMode: resolveResourcesGcMode(config.resourcesGcMode),
70
67
  resourcesGcAutoQuarantine: resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine),
71
68
  resourcesQuarantineTtlHours: resolveResourcesQuarantineTtlHours(config.resourcesQuarantineTtlHours),
@@ -77,7 +74,6 @@ export function effectiveConfigData(config, domain) {
77
74
  controllerTaskConcurrency: resolveControllerTaskConcurrency(config.controllerTaskConcurrency),
78
75
  agentLaunchInactivityTimeoutSeconds: resolveAgentLaunchInactivityTimeoutSeconds(config.agentLaunchInactivityTimeoutSeconds),
79
76
  deliveryTimeoutSeconds: resolveDeliveryTimeoutSeconds(config.deliveryTimeoutSeconds),
80
- leaderSemanticBudgetRuns: resolveLeaderSemanticBudgetRuns(config.leaderSemanticBudgetRuns),
81
77
  tmuxBin: resolveTmuxBin(config.tmuxBin),
82
78
  tmuxHistoryLimit: resolveTmuxHistoryLimit(config.tmuxHistoryLimit),
83
79
  telemetryEnabled: resolveTelemetryEnabled(config.telemetryEnabled),
@@ -164,7 +160,7 @@ const CONFIG_KEY_HANDLERS = [
164
160
  throw usageError("System config set usage: yui config system set default-agent <agent-id>.");
165
161
  }
166
162
  const defaultAgent = args[0].trim();
167
- if (store.getConfiguredAgent?.(defaultAgent) === null) {
163
+ if (store.getConfiguredAgent(defaultAgent) === null) {
168
164
  throw usageError(`Configured Agent not found: ${defaultAgent}.`);
169
165
  }
170
166
  saveConfigKey(store, (config) => ({ ...config, defaultAgent }));
@@ -237,51 +233,6 @@ const CONFIG_KEY_HANDLERS = [
237
233
  return `Reconciliation interval reset to ${DEFAULT_RECONCILIATION_INTERVAL_SECONDS} seconds\n`;
238
234
  }
239
235
  },
240
- {
241
- key: "leader-next-action",
242
- showLabel: "Leader next-action mode",
243
- showValue: (config) => resolveLeaderNextActionMode(config.leaderNextActionMode),
244
- set(args, store) {
245
- if (args.length !== 1)
246
- throw usageError(LEADER_NEXT_ACTION_SET_USAGE);
247
- let mode;
248
- try {
249
- mode = resolveLeaderNextActionMode(args[0]);
250
- }
251
- catch (error) {
252
- throw usageError(error instanceof Error ? error.message : String(error), LEADER_NEXT_ACTION_SET_USAGE);
253
- }
254
- saveConfigKey(store, (config) => ({ ...config, leaderNextActionMode: mode }));
255
- return `Leader next-action mode set to ${mode}\n`;
256
- },
257
- clear(store) {
258
- saveConfigKey(store, (config) => {
259
- const { leaderNextActionMode: _removed, ...rest } = config;
260
- return rest;
261
- });
262
- return `Leader next-action mode reset to ${DEFAULT_LEADER_NEXT_ACTION_MODE}\n`;
263
- }
264
- },
265
- {
266
- key: "leader-semantic-budget-runs",
267
- showLabel: "Leader semantic budget",
268
- showValue: (config) => `${resolveLeaderSemanticBudgetRuns(config.leaderSemanticBudgetRuns)} runs`,
269
- set(args, store) {
270
- const usage = "Workflow config set usage: yui config workflow set leader-semantic-budget-runs <1-20>.";
271
- if (args.length !== 1)
272
- throw usageError(usage);
273
- const leaderSemanticBudgetRuns = validatedConfigValue(() => resolveLeaderSemanticBudgetRuns(Number(args[0])), usage);
274
- saveConfigKey(store, (config) => ({ ...config, leaderSemanticBudgetRuns }));
275
- return `Leader semantic budget set to ${leaderSemanticBudgetRuns} runs\n`;
276
- },
277
- clear(store) {
278
- saveConfigKey(store, (config) => {
279
- const { leaderSemanticBudgetRuns: _removed, ...rest } = config;
280
- return rest;
281
- });
282
- return `Leader semantic budget reset to ${DEFAULT_LEADER_SEMANTIC_BUDGET_RUNS} runs\n`;
283
- }
284
- },
285
236
  {
286
237
  key: "resources-gc-mode",
287
238
  showLabel: "Resources GC mode",
@@ -565,7 +516,7 @@ const CONFIG_KEY_HANDLERS = [
565
516
  || !REVIEW_TRIGGERS.includes(rawTrigger)) {
566
517
  throw usageError(REVIEW_SET_USAGE);
567
518
  }
568
- if (store.getGlobalRole?.(roleName) === null) {
519
+ if (store.getGlobalRole(roleName) === null) {
569
520
  throw usageError(`Global Role not found: ${roleName}.`);
570
521
  }
571
522
  const trigger = rawTrigger;
@@ -598,8 +549,6 @@ if (missingConfigHandlers.length > 0 || duplicateConfigHandlers.length > 0) {
598
549
  }
599
550
  function resolveDefaultWorkspace(value, store) {
600
551
  const requested = resolve(value);
601
- if (store.rootDirectory === undefined)
602
- return requested;
603
552
  const requestedHome = resolve(store.rootDirectory());
604
553
  assertWorkspaceOutsideHome(requested, requestedHome);
605
554
  mkdirSync(requested, { recursive: true, mode: 0o700 });
@@ -12,7 +12,7 @@ import { CONFIG_DOMAINS } from "../config/configCatalog.js";
12
12
  * therefore explain what the user sees without reconstructing configuration
13
13
  * from help text or implementation defaults.
14
14
  */
15
- export function runConfigOverview(args, store, environment, identity, roleOptions) {
15
+ export function runConfigOverview(args, store, environment, roleOptions) {
16
16
  if (args.length !== 0)
17
17
  throw usageError("Config show usage: yui config show.");
18
18
  const domains = Object.fromEntries(CONFIG_DOMAINS.map((domain) => [
@@ -27,7 +27,7 @@ export function runConfigOverview(args, store, environment, identity, roleOption
27
27
  ]));
28
28
  const profileResult = runProfileCommand(["list"], store);
29
29
  const profiles = profileResult.data.profiles;
30
- const completion = inspectCompletionStates(store.getConfig(), environment, identity);
30
+ const completion = inspectCompletionStates(store.getConfig(), environment);
31
31
  const roleOutput = runGlobalRoleCommand(["list"], store, roleOptions);
32
32
  if (typeof roleOutput !== "string") {
33
33
  throw new Error("Config overview received an invalid Role control result.");
@@ -1,7 +1,7 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
2
  import { ensureFileTaskController } from "../controller/clientRuntime.js";
3
3
  import { acknowledgeDurableJob, cancelDurableJob, getDurableJob, startDurableJob } from "../controller/jobClient.js";
4
- import { resolveJobCaller, taskLocalActor } from "./taskActor.js";
4
+ import { resolveJobCaller, taskLocalActor } from "../task/taskAuthority.js";
5
5
  /**
6
6
  * The textual `--owner` forms accepted by `job start`. The public help text in
7
7
  * the command catalog must document exactly these forms.
@@ -25,7 +25,7 @@ export async function runDurableJobCommand(args, options) {
25
25
  throw usageError(command === undefined
26
26
  ? "Job command is required: start, get, cancel, or acknowledge."
27
27
  : `Unknown command: job ${command}`, "yui job start --task <id> --project <project> --head <sha> --workspace <dir> "
28
- + "--step <name>=<command> [--step ...] [--env K=V ...] "
28
+ + "--request-id <id> --step <name>=<command> [--step ...] [--env K=V ...] "
29
29
  + `[--owner ${JOB_OWNER_USAGE}] [--retry-of <job-id>]\n`
30
30
  + "yui job get --task <id> --job <job-id>\n"
31
31
  + "yui job cancel --task <id> --job <job-id>\n"
@@ -36,6 +36,7 @@ async function startJob(args, options) {
36
36
  await ensureFileTaskController(options.home, { environment: options.environment });
37
37
  const caller = resolveJobCaller(options.environment, parsed.taskId);
38
38
  const params = {
39
+ requestId: parsed.requestId,
39
40
  taskId: parsed.taskId,
40
41
  owner: parsed.owner,
41
42
  projectId: parsed.projectId,
@@ -54,8 +55,9 @@ async function startJob(args, options) {
54
55
  }
55
56
  async function getJob(args, options) {
56
57
  const ref = parseRefArgs(args, "get");
58
+ const caller = resolveJobCaller(options.environment, ref.taskId);
57
59
  await ensureFileTaskController(options.home, { environment: options.environment });
58
- const job = await getDurableJob(options.home, ref.taskId, ref.jobId);
60
+ const job = await getDurableJob(options.home, ref.taskId, ref.jobId, caller);
59
61
  if (options.json === true)
60
62
  return `${JSON.stringify(job, null, 2)}\n`;
61
63
  const lines = [
@@ -111,6 +113,7 @@ async function acknowledgeJob(args, options) {
111
113
  }
112
114
  function parseStartArgs(args) {
113
115
  let taskId;
116
+ let requestId;
114
117
  let projectId;
115
118
  let head;
116
119
  let workspace;
@@ -129,6 +132,9 @@ function parseStartArgs(args) {
129
132
  case "--task":
130
133
  taskId = value;
131
134
  break;
135
+ case "--request-id":
136
+ requestId = value;
137
+ break;
132
138
  case "--project":
133
139
  projectId = value;
134
140
  break;
@@ -165,10 +171,10 @@ function parseStartArgs(args) {
165
171
  }
166
172
  }
167
173
  if (taskId === undefined || projectId === undefined || head === undefined
168
- || workspace === undefined || steps.length === 0) {
169
- throw usageError("job start requires --task, --project, --head, --workspace, and at least one --step.", "yui job start ...");
174
+ || workspace === undefined || requestId === undefined || requestId.trim().length === 0 || steps.length === 0) {
175
+ throw usageError("job start requires --request-id, --task, --project, --head, --workspace, and at least one --step.", "yui job start ...");
170
176
  }
171
- return { taskId, owner, projectId, head, workspace, env, steps, retryOf };
177
+ return { taskId, requestId, owner, projectId, head, workspace, env, steps, retryOf };
172
178
  }
173
179
  export function parseJobOwner(value) {
174
180
  if (value === "task")
@@ -14,30 +14,41 @@ import { isSystemRoleName, SYSTEM_ROLE_NAMES, systemRoleDescription } from "../r
14
14
  import { hasAgentConfigOptions, hasNoRoleMutation, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfileFrom, roleProfilePatch } from "./roleConfiguration.js";
15
15
  import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
16
16
  import { assertLiveRoleSessionAcknowledged, assertRoleRuntimeMutationAllowed, LIVE_SESSION_ACKNOWLEDGEMENT_OPTION } from "./roleRuntimeGuard.js";
17
- export function runGlobalRoleCommand(args, store, options = {}) {
18
- const [command, ...rest] = args;
19
- if (command === "message" || command === "interrupt" || command === "session" && rest[0] === "retry") {
20
- const env = options.env ?? process.env;
21
- const target = command === "message" || command === "session" ? rest[1] : rest[0];
22
- if (env.YUI_SESSION_SCOPE !== undefined && env.YUI_SESSION_SCOPE !== "global"
23
- || env.YUI_SESSION_SCOPE === "global" && env.YUI_ROLE !== "operator"
24
- && env.YUI_ROLE !== target) {
25
- throw usageError("Global input mutation is outside the exact Session authority.");
26
- }
27
- if (env.YUI_SESSION_SCOPE === "global") {
28
- const caller = store.getGlobalRoleSessionSet(env.YUI_ROLE ?? "");
29
- const session = caller?.sessions[caller.activeAgentId];
30
- if (session === undefined || session.status !== "active" || env.YUI_AGENT_ID !== session.agentId
31
- || env.YUI_ADAPTER_ID !== session.adapterId
32
- || (env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID) !== session.nativeSessionId) {
33
- throw usageError("Global input mutation requires the current native caller Session.");
34
- }
35
- }
36
- else if (env.YUI_ROLE !== undefined || env.YUI_AGENT_ID !== undefined
37
- || env.YUI_NATIVE_SESSION_ID !== undefined) {
38
- throw usageError("Incomplete managed identity cannot acquire user authority.");
17
+ export function applyGlobalInputControl(name, input, store, options = {}) {
18
+ assertGlobalInputAuthority(store, options, name);
19
+ return input.action === "interrupt"
20
+ ? interruptGlobalInput(name, input, store)
21
+ : messageGlobalInput(name, input, store, options);
22
+ }
23
+ function inputOutput(output, data) {
24
+ return { kind: "output", output, data };
25
+ }
26
+ function renderGlobalInput(result, options) {
27
+ return result.kind === "output" ? jsonOrText(options, result.output, result.data) : result;
28
+ }
29
+ function assertGlobalInputAuthority(store, options, target) {
30
+ const env = options.env ?? process.env;
31
+ if (env.YUI_SESSION_SCOPE !== undefined && env.YUI_SESSION_SCOPE !== "global"
32
+ || env.YUI_SESSION_SCOPE === "global" && env.YUI_ROLE !== "operator" && env.YUI_ROLE !== target) {
33
+ throw usageError("Global input mutation is outside the exact Session authority.");
34
+ }
35
+ if (env.YUI_SESSION_SCOPE === "global") {
36
+ const caller = store.getGlobalRoleSessionSet(env.YUI_ROLE ?? "");
37
+ const session = caller?.sessions[caller.activeAgentId];
38
+ if (session === undefined || session.status !== "active" || env.YUI_AGENT_ID !== session.agentId
39
+ || env.YUI_ADAPTER_ID !== session.adapterId
40
+ || (env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID) !== session.nativeSessionId) {
41
+ throw usageError("Global input mutation requires the current native caller Session.");
39
42
  }
40
43
  }
44
+ else if (env.YUI_ROLE !== undefined || env.YUI_AGENT_ID !== undefined || env.YUI_NATIVE_SESSION_ID !== undefined) {
45
+ throw usageError("Incomplete managed identity cannot acquire user authority.");
46
+ }
47
+ }
48
+ export function runGlobalRoleCommand(args, store, options = {}) {
49
+ const [command, ...rest] = args;
50
+ if (command === "session" && rest[0] === "retry")
51
+ assertGlobalInputAuthority(store, options, rest[1]);
41
52
  switch (command) {
42
53
  case "add": return addRole(rest, store, options);
43
54
  case "list": return listRoles(rest, store);
@@ -551,6 +562,13 @@ function globalRoleMessage(args, store, options) {
551
562
  const expectedTarget = action === "steer"
552
563
  ? required(parsed.one("--expected-target"), "--expected-target")
553
564
  : undefined;
565
+ return renderGlobalInput(applyGlobalInputControl(name, action === "steer"
566
+ ? { action, body, requestId, expectedTarget: expectedTarget, to: name }
567
+ : { action, body, requestId }, store, options), options);
568
+ }
569
+ function messageGlobalInput(name, input, store, options) {
570
+ const { action, body, requestId } = input;
571
+ const expectedTarget = input.action === "steer" ? input.expectedTarget : undefined;
554
572
  const now = new Date();
555
573
  // The durable Global input is authored as an operator input: a Global Role has
556
574
  // no Task Leader, and the operator is the human authority over Global Roles.
@@ -563,14 +581,14 @@ function globalRoleMessage(args, store, options) {
563
581
  });
564
582
  if (action === "queue") {
565
583
  const state = persisted.idempotentReplay ? "idempotent-replay" : "queued";
566
- return jsonOrText(options, `Queued Global message ${persisted.message.id} to ${name} (${state}).\n`, { roleName: name, message: persisted.message, delivery: { state } });
584
+ return inputOutput(`Queued Global message ${persisted.message.id} to ${name} (${state}).\n`, { roleName: name, message: persisted.message, delivery: { state } });
567
585
  }
568
586
  if (persisted.idempotentReplay) {
569
- return jsonOrText(options, `Steer Global message ${persisted.message.id} already recorded (idempotent-replay).\n`, { roleName: name, message: persisted.message, steer: { state: "idempotent-replay" } });
587
+ return inputOutput(`Steer Global message ${persisted.message.id} already recorded (idempotent-replay).\n`, { roleName: name, message: persisted.message, steer: { state: "idempotent-replay" } });
570
588
  }
571
589
  const resolution = resolveGlobalInputControl(store, name, "steer", expectedTarget);
572
590
  if (resolution.outcome !== "ready") {
573
- return jsonOrText(options, `Steer Global message ${persisted.message.id} saved but not delivered (${resolution.code}: ${resolution.detail}).\n`, { roleName: name, message: persisted.message,
591
+ return inputOutput(`Steer Global message ${persisted.message.id} saved but not delivered (${resolution.code}: ${resolution.detail}).\n`, { roleName: name, message: persisted.message,
574
592
  steer: { state: "not-steered", code: resolution.code, detail: resolution.detail } });
575
593
  }
576
594
  store.updateGlobalRoleMessage({ ...persisted.message, control: {
@@ -596,7 +614,6 @@ function globalRoleMessage(args, store, options) {
596
614
  * fourth action and not an auto-fallback (decision-3 §4).
597
615
  */
598
616
  function globalRoleInterrupt(args, store, options) {
599
- const usage = "Role interrupt usage: yui role interrupt <role> --expected-target <turn> [--then-message <global-message-id>] [--request-id <id>].";
600
617
  const [rawName, ...tail] = args;
601
618
  const name = roleName(rawName);
602
619
  const parsed = parseOptions(tail, new Map([
@@ -607,12 +624,19 @@ function globalRoleInterrupt(args, store, options) {
607
624
  const expectedTarget = required(parsed.one("--expected-target"), "--expected-target");
608
625
  const thenMessageId = trimmed(parsed.one("--then-message"));
609
626
  const requestId = trimmed(parsed.one("--request-id")) ?? `cancel:${encodeURIComponent(expectedTarget)}`;
627
+ return renderGlobalInput(applyGlobalInputControl(name, {
628
+ action: "interrupt", role: name, expectedTarget, thenMessage: thenMessageId, requestId
629
+ }, store, options), options);
630
+ }
631
+ function interruptGlobalInput(name, input, store) {
632
+ const { expectedTarget, thenMessage: thenMessageId } = input;
633
+ const requestId = input.requestId ?? `cancel:${encodeURIComponent(expectedTarget)}`;
610
634
  const fingerprint = JSON.stringify({ expectedTarget, thenMessageId });
611
635
  const prior = store.getGlobalRoleSessionSet(name)?.interrupts?.[requestId];
612
636
  if (prior !== undefined) {
613
637
  if (prior.fingerprint !== fingerprint)
614
638
  throw usageError("Interrupt requestId already names another control.");
615
- return jsonOrText(options, `Interrupt ${requestId} already recorded; no native control repeated.\n`, { roleName: name, interrupt: prior.receipt ?? { state: "interrupt-unknown" }, idempotentReplay: true });
639
+ return inputOutput(`Interrupt ${requestId} already recorded; no native control repeated.\n`, { roleName: name, interrupt: prior.receipt ?? { state: "interrupt-unknown" }, idempotentReplay: true });
616
640
  }
617
641
  const resolved = store.transaction((tx) => {
618
642
  requireRole(name, tx);
@@ -645,10 +669,10 @@ function globalRoleInterrupt(args, store, options) {
645
669
  return resolution;
646
670
  });
647
671
  if (resolved.outcome === "then-conflict") {
648
- return jsonOrText(options, `Interrupt not delivered (${resolved.code}: ${resolved.detail}).\n`, { roleName: name, interrupt: { state: "not-interrupted", code: resolved.code, detail: resolved.detail } });
672
+ return inputOutput(`Interrupt not delivered (${resolved.code}: ${resolved.detail}).\n`, { roleName: name, interrupt: { state: "not-interrupted", code: resolved.code, detail: resolved.detail } });
649
673
  }
650
674
  if (resolved.outcome !== "ready") {
651
- return jsonOrText(options, `Interrupt not delivered (${resolved.code}: ${resolved.detail}).\n`, { roleName: name, interrupt: { state: "not-interrupted", code: resolved.code, detail: resolved.detail } });
675
+ return inputOutput(`Interrupt not delivered (${resolved.code}: ${resolved.detail}).\n`, { roleName: name, interrupt: { state: "not-interrupted", code: resolved.code, detail: resolved.detail } });
652
676
  }
653
677
  return {
654
678
  kind: "input-interrupt",
@@ -286,9 +286,6 @@ function requireTask(store, taskId) {
286
286
  function recordTaskEvent(store, taskId, type, payload, now) {
287
287
  store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now));
288
288
  }
289
- function requiredOption(options, name) {
290
- return requiredText(options.get(name), name);
291
- }
292
289
  function optionalOption(options, name) {
293
290
  if (!options.has(name))
294
291
  return undefined;
@@ -93,9 +93,6 @@ function retryJob(args, store, options) {
93
93
  enqueueWork(tx, { kind: "role", taskId: task.id, roleName: "leader" }, "recovery-retry", now, [{ type: "task", id: task.id }]);
94
94
  });
95
95
  const target = { kind: "role", taskId, roleName: "leader" };
96
- if (runtime.notifyMailboxChanged !== undefined)
97
- runtime.notifyMailboxChanged(target);
98
- else
99
- runtime.notifyStateChanged(taskId);
96
+ runtime.notifyMailboxChanged(target);
100
97
  return `Retry requested for ${id}\n`;
101
98
  }
@@ -1,11 +1,11 @@
1
- import { usageError } from "../errors/cliError.js";
2
1
  import { agentAdapterLabel as adapterLabel } from "../agent/adapterCatalog.js";
2
+ import { usageError } from "../errors/cliError.js";
3
3
  import { createRoleSessionSet } from "../executor/agentExecutor.js";
4
- import { listOperatorSessions, projectOperatorStatus, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
4
+ import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
5
+ import { listOperatorSessions, prepareOperatorNewSession, prepareOperatorResumeSession, projectOperatorStatus } from "../operator/operatorSessionHistory.js";
5
6
  import { defaultTableWidth, renderTable } from "../output/table.js";
6
7
  import { formatRelativeTimestamp } from "../output/timePresentation.js";
7
8
  import { updateGlobalRole } from "../role/role.js";
8
- import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
9
9
  import { submitOperatorMessage } from "./taskCommands.js";
10
10
  import { readCommandText } from "./textInput.js";
11
11
  /** Operator command parsing never launches or attaches a native process. */
@@ -8,7 +8,7 @@ import { GitWorkspaceRefreshError, NodeGitWorkspace } from "../repository/gitWor
8
8
  import { acquireProjectMaintenanceLock } from "../repository/projectMaintenanceLock.js";
9
9
  import { addProjectKnowledge, addKnowledgeProposal, assertProjectActive, decideKnowledgeProposal, createProject, findKnowledgeProposal, findKnowledgeProposalByFingerprint, knowledgeEvidenceDigest, knowledgeProposalFingerprint, managedProjectPath, planKnowledgeAcceptance, retireProject, retireProjectKnowledge, resolveProject, updateProjectKnowledge, updateProjectMetadata, validateProject, validateProjectName } from "../repository/project.js";
10
10
  import { managedWorkspacesRoot } from "../storage/homeLayout.js";
11
- import { projectActor } from "./taskActor.js";
11
+ import { projectActor } from "../task/taskAuthority.js";
12
12
  export async function runProjectCommand(args, store, options = {}) {
13
13
  const [command, ...rest] = args;
14
14
  if (command === "add" || command === "clone") {
@@ -1497,9 +1497,6 @@ function proposeKnowledge(args, store, options) {
1497
1497
  * digest at proposal time, so a later Agent can verify the source record.
1498
1498
  */
1499
1499
  function requireProposalEvidence(store, parsed) {
1500
- if (store.getTask === undefined) {
1501
- throw usageError("Knowledge promotion requires Task evidence, which this store does not provide.");
1502
- }
1503
1500
  const task = store.getTask(parsed.task);
1504
1501
  if (task === null)
1505
1502
  throw usageError(`Task not found: ${parsed.task}.`);
@@ -1511,9 +1508,6 @@ function requireProposalEvidence(store, parsed) {
1511
1508
  };
1512
1509
  let evidence;
1513
1510
  if (parsed.decision !== undefined) {
1514
- if (store.getDecision === undefined) {
1515
- throw usageError("Knowledge promotion requires Decision evidence, which this store does not provide.");
1516
- }
1517
1511
  const decision = store.getDecision(task.id, parsed.decision);
1518
1512
  if (decision === null) {
1519
1513
  throw usageError(`Decision not found: ${parsed.decision} in Task ${task.id}.`);
@@ -1521,9 +1515,6 @@ function requireProposalEvidence(store, parsed) {
1521
1515
  evidence = `${decision.title}\n${decision.rationale}`;
1522
1516
  }
1523
1517
  else if (parsed.milestone !== undefined) {
1524
- if (store.getMilestone === undefined) {
1525
- throw usageError("Knowledge promotion requires Milestone evidence, which this store does not provide.");
1526
- }
1527
1518
  const milestone = store.getMilestone(task.id, parsed.milestone);
1528
1519
  if (milestone === null) {
1529
1520
  throw usageError(`Milestone not found: ${parsed.milestone} in Task ${task.id}.`);
@@ -7,12 +7,11 @@
7
7
  * every quarantined resource to its original path.
8
8
  */
9
9
  import { resolve } from "node:path";
10
+ import { resolveResourcesGcMode, resolveResourcesQuarantineTtlHours } from "../config/yuiConfig.js";
10
11
  import { usageError } from "../errors/cliError.js";
11
12
  import { defaultTableWidth, renderTable } from "../output/table.js";
12
- import { applyResourceGc, planResourceGc, purgeResourceQuarantine, restoreAllResourceGc } from "../resources/resourceGc.js";
13
- import { createResourceRegistryStore } from "../resources/resourceRegistryStore.js";
14
13
  import { resourceKindLabel, resourceOwnerLabel } from "../resources/resourceDiscovery.js";
15
- import { resolveResourcesGcMode, resolveResourcesQuarantineTtlHours } from "../config/yuiConfig.js";
14
+ import { applyResourceGc, planResourceGc, readResourceGcState, purgeResourceQuarantine, restoreAllResourceGc } from "../resources/resourceGc.js";
16
15
  export async function runResourcesCommand(args, store, options = {}) {
17
16
  const [command, ...rest] = args;
18
17
  if (command !== "gc") {
@@ -27,10 +26,7 @@ async function runGcCommand(args, store, options) {
27
26
  const now = options.now?.() ?? new Date();
28
27
  const home = resolve(store.rootDirectory());
29
28
  const mode = resolveGcMode(store);
30
- const projects = store.listProjects();
31
- const managedWorkspaces = collectManagedWorkspaces(store);
32
- const taskStatusById = collectTaskStatuses(store);
33
- const activeWorkspaceOwnerPaths = collectActiveWorkspaceOwnerPaths(store);
29
+ const { projects, managedWorkspaces, taskStatusById, activeWorkspaceOwnerPaths, sessionOwners } = readResourceGcState(store);
34
30
  if (action === "restore") {
35
31
  const result = await restoreAllResourceGc(home, { now });
36
32
  return {
@@ -46,7 +42,7 @@ async function runGcCommand(args, store, options) {
46
42
  data: { mode, action: "purge", skipped: true }
47
43
  };
48
44
  }
49
- const result = await purgeResourceQuarantine(home, { now, ttlHours, managedWorkspaces });
45
+ const result = await purgeResourceQuarantine(home, { now, ttlHours, managedWorkspaces }, store);
50
46
  return {
51
47
  output: renderPurgeResult(result),
52
48
  data: result
@@ -54,7 +50,7 @@ async function runGcCommand(args, store, options) {
54
50
  }
55
51
  const plan = await planResourceGc({
56
52
  home,
57
- registryStore: createResourceRegistryStore(home),
53
+ sessionOwners,
58
54
  projects,
59
55
  managedWorkspaces,
60
56
  taskStatusById,
@@ -66,7 +62,7 @@ async function runGcCommand(args, store, options) {
66
62
  if (action === "apply" && mode === "quarantine") {
67
63
  const result = await applyResourceGc({
68
64
  home,
69
- registryStore: createResourceRegistryStore(home),
65
+ sessionOwners,
70
66
  projects,
71
67
  managedWorkspaces,
72
68
  taskStatusById,
@@ -74,7 +70,7 @@ async function runGcCommand(args, store, options) {
74
70
  now,
75
71
  quarantineTtlHours: ttlHours,
76
72
  activeWorkspaceOwnerPaths
77
- }, plan);
73
+ }, plan, store);
78
74
  return {
79
75
  output: renderApplyResult(result),
80
76
  data: result
@@ -109,35 +105,6 @@ function parseTtlHours(args, store) {
109
105
  function resolveGcMode(store) {
110
106
  return resolveResourcesGcMode(store.getConfig().resourcesGcMode);
111
107
  }
112
- function collectManagedWorkspaces(store) {
113
- const workspaces = [];
114
- for (const task of store.listTasks()) {
115
- workspaces.push(...store.listManagedWorkspaces(task.id));
116
- }
117
- return workspaces;
118
- }
119
- function collectTaskStatuses(store) {
120
- const statuses = new Map();
121
- for (const task of store.listTasks()) {
122
- statuses.set(task.id, task.status);
123
- }
124
- return statuses;
125
- }
126
- /** Workspace paths claimed by active durable Jobs (AgentRuns). */
127
- function collectActiveWorkspaceOwnerPaths(store) {
128
- const paths = [];
129
- for (const task of store.listTasks()) {
130
- for (const run of store.listRuns(task.id)) {
131
- if (run.status !== "active")
132
- continue;
133
- const workspace = run.workspace;
134
- if (workspace === undefined)
135
- continue;
136
- paths.push(workspace.root, ...workspace.entries.map((entry) => entry.path));
137
- }
138
- }
139
- return paths;
140
- }
141
108
  function renderPlan(plan, action) {
142
109
  const lines = [];
143
110
  const modeNotice = plan.mode === "report" && action === "apply"
@@ -207,6 +174,9 @@ function renderApplyResult(result) {
207
174
  if (result.restored.length > 0) {
208
175
  lines.push(`Restored ${result.restored.length} resource(s) from quarantine.`);
209
176
  }
177
+ for (const record of result.planned.retained) {
178
+ lines.push(`Retained ${record.id} ${record.path}: ${record.blocker ?? "current ownership or live reference"}`);
179
+ }
210
180
  if (result.applied.length === 0 && result.failed.length === 0 && result.restored.length === 0) {
211
181
  lines.push("No releasable resources.");
212
182
  }
@@ -187,12 +187,8 @@ function permissionPatch(binding, parsed) {
187
187
  }
188
188
  const current = binding.config.permission;
189
189
  if (selected !== "configured" && !nativeOptions) {
190
- // Nothing about permission was named, so the binding keeps what it already
191
- // had. The fallback applies only to a binding that somehow carries no
192
- // permission at all, and it must be the adapter's own default rather than a
193
- // fixed `bypass`: ACP defaults to `default`, and defaulting it to bypass
194
- // here would widen authority the user never granted.
195
- return structuredClone(current ?? defaultRoleAgentConfig(binding.adapterId).permission);
190
+ // The validated binding always has its explicit permission strategy.
191
+ return structuredClone(current);
196
192
  }
197
193
  const permission = current?.strategy === "configured"
198
194
  ? structuredClone(current)
@@ -1,11 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
- import { usageError } from "../errors/cliError.js";
3
- import { currentManagedRuntime } from "../runtime/managedCaller.js";
4
2
  import { enqueueWork } from "../coordination/workMailboxQueue.js";
3
+ import { usageError } from "../errors/cliError.js";
5
4
  import { SYSTEM_LEADER_ROLE } from "../role/systemRoles.js";
5
+ import { currentManagedRuntime } from "../runtime/managedCaller.js";
6
6
  import { describeEnvironmentPlan } from "../task/taskActivation.js";
7
7
  import { cancelTaskActivation, requestTaskActivation, taskActivationOperationRef } from "../task/taskActivationService.js";
8
- import { taskLocalActor } from "./taskActor.js";
8
+ import { taskLocalActor } from "../task/taskAuthority.js";
9
9
  /**
10
10
  * Explicit Activation requests.
11
11
  *
@@ -52,10 +52,6 @@ function requestActivation(args, store, options) {
52
52
  actorId: caller.actorId,
53
53
  authorityRef: caller.authorityRef,
54
54
  environmentPlan,
55
- // The activation-request command is the explicit activation boundary, so
56
- // every request it records carries an explicit provable origin (task-32
57
- // §2.4). A develop submission records its own request with `submit-develop`.
58
- origin: "explicit",
59
55
  ...(caller.planningRunId === undefined ? {} : { callerRunId: caller.planningRunId })
60
56
  }, now));
61
57
  // An immediate request has nothing left to wait for, so ask the Controller to
@@ -65,7 +61,7 @@ function requestActivation(args, store, options) {
65
61
  store.transaction((tx) => {
66
62
  enqueueWork(tx, { kind: "task", taskId }, "activation-requested", now, [{ type: "task", id: taskId }]);
67
63
  });
68
- void options.runtime?.notifyMailboxChanged?.({ kind: "task", taskId });
64
+ void options.runtime?.notifyMailboxChanged({ kind: "task", taskId });
69
65
  }
70
66
  const request = result.request;
71
67
  const lines = [
@@ -0,0 +1,9 @@
1
+ import { exactPositionals, parseTail } from "./taskCommandSupport.js";
2
+ export function parseTaskAgentCapabilityQuery(args) {
3
+ const usage = "Usage: yui task role capabilities <task> <role> [--error <event-id>] [--refresh]";
4
+ const parsed = parseTail(args, new Set(["--error"]), usage, new Set(["--refresh"]));
5
+ exactPositionals(parsed.positionals, 2, usage);
6
+ const errorId = parsed.options.get("--error");
7
+ return { taskId: parsed.positionals[0], roleName: parsed.positionals[1],
8
+ ...(errorId === undefined ? {} : { errorId }), refresh: parsed.options.has("--refresh") };
9
+ }