@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
package/ARCHITECTURE.md CHANGED
@@ -93,8 +93,9 @@ meaning of the report. See [result consumption](docs/agent-result-consumption.md
93
93
  ## Planning, workspaces and resources
94
94
 
95
95
  Draft contains planning facts and Project bindings, not an adopted delivery
96
- workspace. Its private planning directory is outside the control Home and
97
- delivery trees. Planning Sessions capture `planning` authority. The Leader can
96
+ workspace. Its private planning directory is under
97
+ `<YUI_HOME>/runtime/task-runtimes/planning`, separate from durable control data
98
+ and delivery trees. Planning Sessions capture `planning` authority. The Leader can
98
99
  persist an activation request during a Run or ordinary discussion and return;
99
100
  native quiescence releases the intent for current-authority and resource checks.
100
101
 
@@ -72,8 +72,9 @@ Reviewer 输出留在那个 Run 上。审查策略和显式的 Task-final 合同
72
72
 
73
73
  ## 规划、工作区与资源
74
74
 
75
- Draft 包含规划事实和 Project 绑定,而不是一个已采用的交付工作区。它私有的规划目录在
76
- 控制 Home 和交付树之外。规划 Session 捕获 `planning` 权限。Leader 可以在一个 Run 或
75
+ Draft 包含规划事实和 Project 绑定,而不是一个已采用的交付工作区。它私有的规划目录位于
76
+ `<YUI_HOME>/runtime/task-runtimes/planning`,与持久控制数据和交付树分开。
77
+ 规划 Session 捕获 `planning` 权限。Leader 可以在一个 Run 或
77
78
  普通讨论中持久化一个激活请求并返回;原生静止会把该意图释放出来,进行当前权限和资源
78
79
  检查。
79
80
 
@@ -1,5 +1,6 @@
1
1
  import { mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { currentFileLockOwner, fileLockOwnerIsLive } from "../core/fileLockOwner.js";
3
4
  /**
4
5
  * Short per-Task commit fence for artifact repositories.
5
6
  *
@@ -9,9 +10,9 @@ import { join } from "node:path";
9
10
  * into the wrong commit, or race the expected-HEAD check. This fence serializes
10
11
  * the commit critical section for one Task.
11
12
  *
12
- * Unlike the Project maintenance fence (which guards long operations and fails
13
- * fast so the CLI is not blocked), an artifact save is a short operation, so a
14
- * contended fence briefly RETRIES before giving up. It is otherwise the same
13
+ * The Project maintenance fence waits asynchronously for longer operations.
14
+ * An artifact save is short, so this synchronous critical section briefly
15
+ * retries a contended fence before giving up. It otherwise shares the same
15
16
  * crash-safe design: an exclusive directory under the Home's `locks/` area
16
17
  * (O_EXCL via mkdir), an owner file recording PID + process start time so a
17
18
  * recycled PID can never pass for the original holder, and reclaim of a stale
@@ -47,12 +48,13 @@ export function artifactCommitLockPath(home, taskId) {
47
48
  */
48
49
  export function acquireArtifactCommitLock(home, taskId) {
49
50
  const lock = artifactCommitLockPath(home, taskId);
51
+ const ownerIdentity = currentFileLockOwner();
50
52
  mkdirSync(join(home, "locks", "task-artifacts"), { recursive: true, mode: 0o700 });
51
53
  const deadline = Date.now() + ARTIFACT_COMMIT_LOCK_TIMEOUT_MS;
52
54
  while (true) {
53
55
  try {
54
56
  mkdirSync(lock, { mode: 0o700 });
55
- const ownerIdentity = writeOwnerIdentity(lock);
57
+ writeOwnerIdentity(lock, ownerIdentity);
56
58
  let released = false;
57
59
  return () => {
58
60
  if (released)
@@ -72,8 +74,7 @@ export function acquireArtifactCommitLock(home, taskId) {
72
74
  }
73
75
  }
74
76
  /** Write this process's owner identity and return the exact bytes recorded. */
75
- function writeOwnerIdentity(lock) {
76
- const identity = `${process.pid}:${processStartIdentity() ?? ""}`;
77
+ function writeOwnerIdentity(lock, identity = currentFileLockOwner()) {
77
78
  writeFileSync(join(lock, "owner"), `${identity}\n`, { mode: 0o600 });
78
79
  return identity;
79
80
  }
@@ -109,8 +110,8 @@ function reclaimStaleArtifactCommitLock(lock) {
109
110
  catch (error) {
110
111
  if (!isEnoent(error))
111
112
  throw error;
112
- // Ownerless lock: a crash between mkdir and owner publication. Reclaim only
113
- // when old enough that the creator is not still initializing.
113
+ // Allow publication its initial grace period; an older missing owner is
114
+ // diagnosed as unverified below, never treated as a dead creator.
114
115
  try {
115
116
  if (Date.now() - statSync(lock).mtimeMs < STALE_ARTIFACT_COMMIT_LOCK_AGE_MS)
116
117
  return;
@@ -131,7 +132,7 @@ function reclaimStaleArtifactCommitLock(lock) {
131
132
  return;
132
133
  throw error;
133
134
  }
134
- if (expectedOwner !== null && lockOwnerIsAlive(lock))
135
+ if (fileLockOwnerIsLive(lock))
135
136
  return;
136
137
  const reclaimLock = `${lock}.reclaim`;
137
138
  for (let attempt = 0; attempt < 2; attempt += 1) {
@@ -171,7 +172,7 @@ function reclaimStaleArtifactCommitLock(lock) {
171
172
  }
172
173
  if (currentOwner !== expectedOwner)
173
174
  return;
174
- if (currentOwner !== null && lockOwnerIsAlive(lock))
175
+ if (fileLockOwnerIsLive(lock))
175
176
  return;
176
177
  rmSync(lock, { recursive: true, force: true });
177
178
  }
@@ -182,63 +183,23 @@ function reclaimStaleArtifactCommitLock(lock) {
182
183
  }
183
184
  }
184
185
  /**
185
- * Reclaim a reclaim-lock directory only when provably orphaned: dead (or
186
- * unrecorded) owner AND older than the age bound. Returns true when it removed
186
+ * Reclaim a reclaim-lock directory only with a proven dead generation and
187
+ * after the age bound. Returns true when it removed
187
188
  * the lock (caller retries), false otherwise.
188
189
  */
189
190
  function reclaimOrphanedReclaimLock(reclaimLock) {
190
191
  try {
191
192
  if (Date.now() - statSync(reclaimLock).mtimeMs < STALE_ARTIFACT_COMMIT_LOCK_AGE_MS)
192
193
  return false;
193
- if (lockOwnerIsAlive(reclaimLock))
194
+ if (fileLockOwnerIsLive(reclaimLock))
194
195
  return false;
195
196
  rmSync(reclaimLock, { recursive: true, force: true });
196
197
  return true;
197
198
  }
198
- catch (error) {
199
- return isEnoent(error);
200
- }
201
- }
202
- function lockOwnerIsAlive(lock) {
203
- let owner;
204
- try {
205
- owner = readFileSync(join(lock, "owner"), "utf8").trim();
206
- }
207
199
  catch (error) {
208
200
  if (isEnoent(error))
209
- return false;
210
- return true; // Unreadable owner fails closed: treat the fence as held.
211
- }
212
- const separator = owner.indexOf(":");
213
- const pid = Number.parseInt(separator < 0 ? owner : owner.slice(0, separator), 10);
214
- if (!Number.isInteger(pid) || pid <= 0)
215
- return false;
216
- const recordedIdentity = separator < 0 ? "" : owner.slice(separator + 1);
217
- const currentIdentity = processStartIdentity(pid);
218
- if (currentIdentity !== undefined) {
219
- return recordedIdentity !== "" && currentIdentity === recordedIdentity;
220
- }
221
- return processIsAlive(pid);
222
- }
223
- /** Linux process start time (clock ticks since boot) for a PID; undefined off /proc. */
224
- function processStartIdentity(pid = process.pid) {
225
- try {
226
- const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
227
- const close = stat.lastIndexOf(")");
228
- const fields = stat.slice(close + 2).split(" ");
229
- return fields[19] ?? "0"; // field 22: starttime
230
- }
231
- catch {
232
- return undefined;
233
- }
234
- }
235
- function processIsAlive(pid) {
236
- try {
237
- process.kill(pid, 0);
238
- return true;
239
- }
240
- catch (error) {
241
- return error instanceof Error && "code" in error && error.code === "EPERM";
201
+ return true;
202
+ throw error;
242
203
  }
243
204
  }
244
205
  function isEnoent(error) {
@@ -1,10 +1,10 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import { existsSync } from "node:fs";
3
1
  import { createHash } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
- import { ManagedGitError, externalProgramConfigViolations, managedGit, managedGitBuffer, managedGitSucceeds, requireCommitId } from "./managedGit.js";
6
5
  import { acquireArtifactCommitLock } from "./artifactCommitLock.js";
7
6
  import { resolveContainedArtifactPath, safeRelativeArtifactPath, taskArtifactRepoPath } from "./artifactPaths.js";
7
+ import { ManagedGitError, externalProgramConfigViolations, managedGit, managedGitBuffer, managedGitSucceeds, requireCommitId } from "./managedGit.js";
8
8
  /**
9
9
  * Per-Task local-only Git artifact repository.
10
10
  *
@@ -106,7 +106,7 @@ export async function selectAgentPermission(resolved, io, current) {
106
106
  export function renderAgentConfigurationResolutionNotice(resolved) {
107
107
  const lines = [];
108
108
  if (resolved.source === "cache") {
109
- lines.push(`! Runtime capability request failed (${resolved.failure?.message ?? "unknown failure"}). `
109
+ lines.push((resolved.failure === undefined ? "" : `! Runtime capability request failed (${resolved.failure.message}). `)
110
110
  + `Showing cached options from ${resolved.fetchedAt ?? "an earlier request"}; they may be stale.`);
111
111
  }
112
112
  else if (resolved.source === "fallback") {
@@ -116,7 +116,8 @@ const agentChildren = [
116
116
  {
117
117
  name: "capabilities",
118
118
  summary: "Probe one Agent CLI for runtime configuration options.",
119
- usage: "yui config agent capabilities <id>"
119
+ usage: "yui config agent capabilities <id> [--refresh]",
120
+ options: ["--refresh"]
120
121
  },
121
122
  {
122
123
  name: "update",
@@ -293,7 +294,7 @@ const taskChildren = [
293
294
  "--type": ["feature", "bugfix"]
294
295
  }
295
296
  },
296
- { name: "activate", summary: "Activate a Draft Task.", usage: "yui task activate <id>" },
297
+ { name: "activate", summary: "Adopt an existing Activation request for a Draft Task.", usage: "yui task activate <id>" },
297
298
  {
298
299
  name: "activation",
299
300
  summary: "Request, cancel or inspect explicit Task Activation.",
@@ -378,9 +379,9 @@ const taskChildren = [
378
379
  },
379
380
  {
380
381
  name: "list",
381
- summary: "List Task overviews or a bounded discovery catalog.",
382
- usage: "yui task list [--all] [--verbose] | --view compact [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>]",
383
- options: ["--all", "--verbose", "--view", "--status", "--project", "--search", "--attention", "--limit", "--cursor"]
382
+ summary: "Discover Tasks through a bounded, filterable catalog.",
383
+ usage: "yui task list [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>]",
384
+ options: ["--all", "--status", "--project", "--search", "--attention", "--limit", "--cursor"]
384
385
  },
385
386
  { name: "show", summary: "Show a Task.", usage: "yui task show <id>" },
386
387
  {
@@ -432,8 +433,8 @@ const taskChildren = [
432
433
  {
433
434
  name: "integrate",
434
435
  summary: "Rebase Task changes onto the remote development head through Integration.",
435
- usage: "yui task upstream integrate <task> (--latest|--project <project>) [--check <command> ...]",
436
- options: ["--latest", "--project", "--check"]
436
+ usage: "yui task upstream integrate <task> (--latest|--project <project>) [--check <command> ...] [--rerun-checks]",
437
+ options: ["--latest", "--project", "--check", "--rerun-checks"]
437
438
  }
438
439
  ]
439
440
  },
@@ -457,18 +458,17 @@ const taskChildren = [
457
458
  {
458
459
  name: "send",
459
460
  summary: "Send a Task message. An unaddressed user/operator message carries a submission intent (record|discuss|develop) and an optional idempotency key.",
460
- usage: "yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--wake-policy leader|none] [--to <role> --work-item <id>|--review-round <id>]",
461
- options: ["--body-file", "--intent", "--request-id", "--wake-policy", "--to", "--work-item", "--review-round"],
461
+ usage: "yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--to <role> --work-item <id>|--review-round <id>]",
462
+ options: ["--body-file", "--intent", "--request-id", "--to", "--work-item", "--review-round"],
462
463
  optionValues: {
463
- "--intent": ["record", "discuss", "develop"],
464
- "--wake-policy": ["leader", "none"]
464
+ "--intent": ["record", "discuss", "develop"]
465
465
  },
466
466
  fileOptions: ["--body-file"]
467
467
  },
468
468
  {
469
469
  name: "queue",
470
- summary: "Queue an input for delivery at the recipient's next legal opportunity (idempotent by request id).",
471
- usage: "yui task message queue <id> (<body>|--body-file <path|->) --request-id <id> [--to <role> --work-item <id>|--review-round <id>]",
470
+ summary: "Queue ordinary Leader input without --to; an explicit Role requires an existing WorkItem/ReviewRound Assignment. Idempotent by request id.",
471
+ usage: "yui task message queue <id> (<body>|--body-file <path|->) --request-id <id> [--to <role> (--work-item <id>|--review-round <id>)]",
472
472
  options: ["--body-file", "--request-id", "--to", "--work-item", "--review-round"],
473
473
  fileOptions: ["--body-file"]
474
474
  },
@@ -492,12 +492,9 @@ const taskChildren = [
492
492
  },
493
493
  {
494
494
  name: "update",
495
- summary: "Replace the mutable body and wake policy of a Draft user/operator Message.",
496
- usage: "yui task message update <task>/<message> (<body>|--body-file <path|->) [--wake-policy leader|none]",
497
- options: ["--body-file", "--wake-policy"],
498
- optionValues: {
499
- "--wake-policy": ["leader", "none"]
500
- },
495
+ summary: "Edit unkeyed Draft context without changing intent; inputs with request identities require a new Message.",
496
+ usage: "yui task message update <task>/<message> (<body>|--body-file <path|->)",
497
+ options: ["--body-file"],
501
498
  fileOptions: ["--body-file"]
502
499
  },
503
500
  {
@@ -659,7 +656,7 @@ const taskChildren = [
659
656
  summary: "Manage Roles within a Task.",
660
657
  sections: [{ id: "manage", title: "Commands", entries: [
661
658
  "add", "list", "status", "show", "update", "remove", "bind", "unbind",
662
- "session", "interrupt", "view", "takeover", "release"
659
+ "capabilities", "session", "interrupt", "view", "takeover", "release"
663
660
  ] }],
664
661
  children: [
665
662
  {
@@ -676,6 +673,9 @@ const taskChildren = [
676
673
  usage: "yui task role status <task> <role>"
677
674
  },
678
675
  { name: "show", summary: "Show one Task Role.", usage: "yui task role show <task> <role>" },
676
+ { name: "capabilities", summary: "Read native options using this Role's desired or exact failed launch configuration.",
677
+ usage: "yui task role capabilities <task> <role> [--error <event-id>] [--refresh]",
678
+ options: ["--error", "--refresh"] },
679
679
  {
680
680
  name: "update",
681
681
  summary: "Update a Task Role; Agent settings target the named or active binding without switching it.",
@@ -947,13 +947,13 @@ const taskChildren = [
947
947
  {
948
948
  name: "integration",
949
949
  summary: "Integrate WorkItem results and upstream commits with Leader-owned decisions.",
950
- sections: [{ id: "manage", title: "Commands", entries: ["start", "continue", "resolve", "abort", "supersede", "list", "show", "cleanup", "queue"] }],
950
+ sections: [{ id: "manage", title: "Commands", entries: ["start", "continue", "resolve", "abort", "supersede", "list", "show", "cleanup"] }],
951
951
  children: [
952
952
  {
953
953
  name: "start",
954
954
  summary: "Build, validate, and CAS-commit an integration candidate.",
955
- usage: "yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...]",
956
- options: ["--work-item", "--strategy", "--project", "--target", "--check"],
955
+ usage: "yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...] [--rerun-checks]",
956
+ options: ["--work-item", "--strategy", "--project", "--target", "--check", "--rerun-checks"],
957
957
  optionValues: { "--strategy": ["ff", "cherry-pick", "merge", "manual"] }
958
958
  },
959
959
  {
@@ -982,21 +982,7 @@ const taskChildren = [
982
982
  },
983
983
  { name: "list", summary: "List Integration Attempts.", usage: "yui task integration list <task>" },
984
984
  { name: "show", summary: "Show one Integration Attempt.", usage: "yui task integration show <task>/<integration>" },
985
- { name: "cleanup", summary: "Remove a terminal Integration worktree and branch.", usage: "yui task integration cleanup <task>/<integration>" },
986
- {
987
- name: "queue",
988
- summary: "Manage the serialized integration queue.",
989
- sections: [{ id: "queue", title: "Commands", entries: ["enqueue", "list", "show", "process", "supersede", "requeue", "reconcile"] }],
990
- children: [
991
- { name: "enqueue", summary: "Enqueue a ChangeSet for serialized integration.", usage: "yui task integration queue enqueue <task> --project <project> --change-set <id> [--target <ref>] [--check <command> ...]", options: ["--project", "--change-set", "--target", "--check"] },
992
- { name: "list", summary: "List integration queue entries.", usage: "yui task integration queue list <task> [--project <project>]", options: ["--project"] },
993
- { name: "show", summary: "Show one integration queue entry.", usage: "yui task integration queue show <task>/<entry>" },
994
- { name: "process", summary: "Process queued integration entries.", usage: "yui task integration queue process <task> [--project <project>] [--limit <n>]", options: ["--project", "--limit"] },
995
- { name: "supersede", summary: "Supersede a queued entry.", usage: "yui task integration queue supersede <task>/<entry> --reason <text>", options: ["--reason"] },
996
- { name: "requeue", summary: "Requeue a conflicted entry.", usage: "yui task integration queue requeue <task>/<entry>" },
997
- { name: "reconcile", summary: "Reconcile a blocked entry.", usage: "yui task integration queue reconcile <task>/<entry>" }
998
- ]
999
- }
985
+ { name: "cleanup", summary: "Remove a terminal Integration worktree and branch.", usage: "yui task integration cleanup <task>/<integration>" }
1000
986
  ]
1001
987
  },
1002
988
  {
@@ -1085,10 +1071,12 @@ const taskChildren = [
1085
1071
  {
1086
1072
  name: "wake",
1087
1073
  summary: "Inspect the durable Leader wake ledger and its delta content.",
1088
- sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "resolve"] }],
1074
+ sections: [{ id: "manage", title: "Commands", entries: ["list", "show", "retry", "resolve"] }],
1089
1075
  children: [
1090
1076
  { name: "list", summary: "List recorded Leader wakes.", usage: "yui task wake list <task>" },
1091
1077
  { name: "show", summary: "Show one wake and its delta content.", usage: "yui task wake show <task> <wake>" },
1078
+ { name: "retry", summary: "Retry a rejected notification after correcting its cause; preserve original input.",
1079
+ usage: "yui task wake retry <task> <wake> --reason <correction>", options: ["--reason"] },
1092
1080
  { name: "resolve", summary: "Release an unknown notification claim after explicit quiescence evidence, without replay.",
1093
1081
  usage: "yui task wake resolve <task> <wake> --reason <quiescence-evidence>", options: ["--reason"] }
1094
1082
  ]
@@ -1117,13 +1105,41 @@ export const ROOT_COMMAND = buildNode({
1117
1105
  { id: "general", title: "General", entries: [
1118
1106
  "help", "version", "update", "upgrade", "setup", "doctor"
1119
1107
  ] },
1120
- { id: "workflow", title: "Workflow", entries: ["operator", "project", "task"] },
1108
+ { id: "workflow", title: "Workflow", entries: ["operator", "role", "project", "task"] },
1121
1109
  { id: "configuration", title: "Configuration", entries: ["config"] },
1122
1110
  { id: "operations", title: "Operations", entries: ["web", "controller", "session", "execution", "capability", "job", "jobs", "telemetry", "release"] },
1123
1111
  { id: "resources", title: "Resources", entries: ["resources"] },
1124
1112
  { id: "internal", title: "Internal", entries: ["internal"] }
1125
1113
  ],
1126
1114
  children: [
1115
+ {
1116
+ name: "role",
1117
+ summary: "Queue input or control an exact current Global Role Turn.",
1118
+ sections: [{ id: "input", title: "Input control", entries: ["message", "interrupt"] }],
1119
+ children: [
1120
+ {
1121
+ name: "message", summary: "Send durable input to a Global Role.",
1122
+ sections: [{ id: "input", title: "Input control", entries: ["queue", "steer"] }],
1123
+ children: [
1124
+ {
1125
+ name: "queue", summary: "Queue input for the next legal delivery opportunity.",
1126
+ usage: "yui role message queue <role> (<text>|--body-file <path|->) --request-id <id>",
1127
+ options: ["--request-id", "--body-file"], fileOptions: ["--body-file"]
1128
+ },
1129
+ {
1130
+ name: "steer", summary: "Steer the exact current native Turn.",
1131
+ usage: "yui role message steer <role> (<text>|--body-file <path|->) --request-id <id> --expected-target <turn>",
1132
+ options: ["--request-id", "--expected-target", "--body-file"], fileOptions: ["--body-file"]
1133
+ }
1134
+ ]
1135
+ },
1136
+ {
1137
+ name: "interrupt", summary: "Request cancellation of the exact current native Turn.",
1138
+ usage: "yui role interrupt <role> --expected-target <turn> [--request-id <id>] [--then-message <message>]",
1139
+ options: ["--request-id", "--expected-target", "--then-message"]
1140
+ }
1141
+ ]
1142
+ },
1127
1143
  { name: "help", summary: "Show root or scoped command help.", usage: "yui help [command ...]", commandPathArguments: true },
1128
1144
  { name: "version", summary: "Print the installed Yui version." },
1129
1145
  { name: "update", summary: "Install the latest published Yui package globally." },
@@ -1540,7 +1556,7 @@ export const ROOT_COMMAND = buildNode({
1540
1556
  summary: "Start, inspect, cancel, or acknowledge a Controller-managed DurableJob.",
1541
1557
  sections: [{ id: "manage", title: "Commands", entries: ["start", "get", "cancel", "acknowledge"] }],
1542
1558
  children: [
1543
- { name: "start", summary: "Start a DurableJob for build, test, package, or Integration checks.", usage: "yui job start --task <id> --project <project> --head <sha> --workspace <dir> --step <name>=<command> [--step ...] [--owner task|work-item:<id>|integration-attempt:<id>] [--env <k=v>...]" },
1559
+ { name: "start", summary: "Start a DurableJob under an explicit request identity.", usage: "yui job start --request-id <id> --task <id> --project <project> --head <sha> --workspace <dir> --step <name>=<command> [--step ...] [--owner task|work-item:<id>|integration-attempt:<id>] [--env <k=v>...]" },
1544
1560
  { name: "get", summary: "Show a DurableJob record and its terminal result.", usage: "yui job get --task <id> --job <job-id>" },
1545
1561
  { name: "cancel", summary: "Request cancellation of a running or queued DurableJob.", usage: "yui job cancel --task <id> --job <job-id>" },
1546
1562
  { name: "acknowledge", summary: "Acknowledge an unknown-needs-attention DurableJob so Task lifecycle gates can proceed.", usage: "yui job acknowledge --task <id> --job <job-id>" }
@@ -1,11 +1,11 @@
1
1
  import { ROOT_COMMAND, listPublicCommandPaths, orderedImmediateTokens, visibleChildren } from "./commandCatalog.js";
2
2
  import { findInteractionPolicy } from "./interactionPolicy.js";
3
- export function renderCompletion(shell, identity = "yui") {
3
+ export function renderCompletion(shell) {
4
4
  const entries = collectEntries(ROOT_COMMAND);
5
5
  switch (shell) {
6
- case "bash": return renderBash(entries, identity);
7
- case "zsh": return renderZsh(entries, identity);
8
- case "fish": return renderFish(entries, identity);
6
+ case "bash": return renderBash(entries);
7
+ case "zsh": return renderZsh(entries);
8
+ case "fish": return renderFish(entries);
9
9
  default: throw new Error("Completion shell must be one of bash, zsh, fish.");
10
10
  }
11
11
  }
@@ -30,8 +30,8 @@ function collectEntries(root) {
30
30
  function manifest(prefix) {
31
31
  return listPublicCommandPaths().map((path) => `${prefix} ${path}`).join("\n");
32
32
  }
33
- function renderBash(entries, identity) {
34
- const functionName = identity === "yui" ? "_yui" : "_yui_dev";
33
+ function renderBash(entries) {
34
+ const functionName = "_yui";
35
35
  return `${manifest("# yui command:")}
36
36
  ${functionName}() {
37
37
  local current path previous dynamic_candidate dynamic=false
@@ -55,7 +55,7 @@ ${bashDynamicOptionCases(entries)}
55
55
  if [[ "$dynamic" == true ]]; then
56
56
  while IFS= read -r dynamic_candidate; do
57
57
  [[ -n "$dynamic_candidate" ]] && dynamic_candidates+=("$dynamic_candidate")
58
- done < <(command ${identity} config completion candidates "$current" -- "\${COMP_WORDS[@]:1:COMP_CWORD-1}" 2>/dev/null)
58
+ done < <(command yui config completion candidates "$current" -- "\${COMP_WORDS[@]:1:COMP_CWORD-1}" 2>/dev/null)
59
59
  candidates=("\${dynamic_candidates[@]}")
60
60
  elif [[ "$current" == -* ]]; then
61
61
  case "$path" in
@@ -70,11 +70,11 @@ ${entries.map((entry) => ` ${bashPattern(entry.path)}) candidates=(${[...en
70
70
  fi
71
71
  COMPREPLY=( $(compgen -W "\${candidates[*]}" -- "$current") )
72
72
  }
73
- complete -F ${functionName} ${identity}
73
+ complete -F ${functionName} yui
74
74
  `;
75
75
  }
76
- function renderZsh(entries, identity) {
77
- return `#compdef ${identity}
76
+ function renderZsh(entries) {
77
+ return `#compdef yui
78
78
  ${manifest("# yui command:")}
79
79
  local current command_path previous dynamic_output dynamic=false
80
80
  local -a candidates
@@ -95,7 +95,7 @@ ${zshDynamicOptionCases(entries)}
95
95
  esac
96
96
  fi
97
97
  if [[ "$dynamic" == true ]]; then
98
- dynamic_output="$(command ${identity} config completion candidates "$current" -- "\${(@)words[2,CURRENT-1]}" 2>/dev/null)"
98
+ dynamic_output="$(command yui config completion candidates "$current" -- "\${(@)words[2,CURRENT-1]}" 2>/dev/null)"
99
99
  candidates=("\${(@f)dynamic_output}")
100
100
  elif [[ "$current" == -* ]]; then
101
101
  case "$command_path" in
@@ -111,8 +111,8 @@ fi
111
111
  (( \${#candidates[@]} > 0 )) && compadd -- "$candidates[@]"
112
112
  `;
113
113
  }
114
- function renderFish(entries, identity) {
115
- const prefix = `__${identity.replaceAll("-", "_")}`;
114
+ function renderFish(entries) {
115
+ const prefix = "__yui";
116
116
  const lines = [
117
117
  manifest("# yui command:"),
118
118
  `function ${prefix}_at_path`,
@@ -151,17 +151,17 @@ function renderFish(entries, identity) {
151
151
  "end",
152
152
  `function ${prefix}_dynamic`,
153
153
  " set -l words (commandline -opc)",
154
- ` ${identity} config completion candidates (commandline -ct) -- $words[2..-1] 2>/dev/null`,
154
+ " yui config completion candidates (commandline -ct) -- $words[2..-1] 2>/dev/null",
155
155
  "end"
156
156
  ];
157
157
  for (const entry of entries) {
158
158
  for (const argument of entry.dynamicArguments) {
159
159
  const condition = `${prefix}_needs_dynamic argument ${argument} ${fishPath(entry.path)}`;
160
- lines.push(`complete -c ${identity} -f -n ${fishQuote(condition)} -a '(${prefix}_dynamic)'`);
160
+ lines.push(`complete -c yui -f -n ${fishQuote(condition)} -a '(${prefix}_dynamic)'`);
161
161
  }
162
162
  for (const option of entry.dynamicOptions) {
163
163
  const condition = `${prefix}_needs_dynamic option ${option} ${fishPath(entry.path)}`;
164
- lines.push(`complete -c ${identity} -f -n ${fishQuote(condition)} -a '(${prefix}_dynamic)'`);
164
+ lines.push(`complete -c yui -f -n ${fishQuote(condition)} -a '(${prefix}_dynamic)'`);
165
165
  }
166
166
  }
167
167
  for (const entry of entries) {
@@ -169,13 +169,13 @@ function renderFish(entries, identity) {
169
169
  const condition = entry.path.length === 0
170
170
  ? "__fish_use_subcommand"
171
171
  : `${prefix}_at_path ${fishPath(entry.path)}${fishInitialDynamicExclusion(entry, prefix)}`;
172
- lines.push(`complete -c ${identity} -f -n ${fishQuote(condition)} -a '${escapeSingle(entry.immediate.join(" "))}'`);
172
+ lines.push(`complete -c yui -f -n ${fishQuote(condition)} -a '${escapeSingle(entry.immediate.join(" "))}'`);
173
173
  }
174
174
  if (entry.options.length > 0) {
175
175
  const path = fishPath(entry.path);
176
176
  const exact = `${prefix}_at_path ${path}${fishInitialDynamicExclusion(entry, prefix)}`;
177
177
  const condition = `begin; ${exact}; end; or ${prefix}_at_command_options ${path}`;
178
- lines.push(`complete -c ${identity} -f -n ${fishQuote(condition)} -a '${escapeSingle(entry.options.join(" "))}'`);
178
+ lines.push(`complete -c yui -f -n ${fishQuote(condition)} -a '${escapeSingle(entry.options.join(" "))}'`);
179
179
  }
180
180
  }
181
181
  return `${lines.join("\n")}\n`;
@@ -41,7 +41,7 @@ export async function runCompletionWizard(port, io, options = {}) {
41
41
  let activate = false;
42
42
  let activationReady = automatic;
43
43
  if (!automatic) {
44
- const activationAnswer = normalize(await io.question(`${activationBlock(state.shell, installation, overview.identity)}\n`
44
+ const activationAnswer = normalize(await io.question(`${activationBlock(state.shell, installation)}\n`
45
45
  + `Update ${installation.activationPath} with this managed Yui block? [Y/n]: `));
46
46
  if (activationAnswer !== ""
47
47
  && activationAnswer !== "y"
@@ -2,7 +2,7 @@ import { findChild, ROOT_COMMAND, visibleChildren } from "./commandCatalog.js";
2
2
  import { getSelectionCandidates } from "./interactionCandidates.js";
3
3
  import { findInteractionPolicy } from "./interactionPolicy.js";
4
4
  export async function resolveCompletionCandidates(input) {
5
- const words = input.words[0] === "yui" || input.words[0] === "yui-dev"
5
+ const words = input.words[0] === "yui"
6
6
  ? input.words.slice(1)
7
7
  : [...input.words];
8
8
  const { node, consumed } = resolveCommand(words);
@@ -223,7 +223,10 @@ export const INTERACTION_POLICIES = Object.freeze([
223
223
  {
224
224
  commandPath: ["task", "list"],
225
225
  selectors: [],
226
- trailingOptions: { "--all": "flag", "--verbose": "flag" }
226
+ trailingOptions: {
227
+ "--all": "flag", "--status": "value", "--project": "value",
228
+ "--search": "value", "--attention": "value", "--limit": "value", "--cursor": "value"
229
+ }
227
230
  },
228
231
  {
229
232
  ...taskTarget("remote-delivery"),
@@ -276,7 +279,7 @@ export const INTERACTION_POLICIES = Object.freeze([
276
279
  provider: "messages",
277
280
  actionTarget: true
278
281
  }],
279
- trailingOptions: { "--body-file": "value", "--wake-policy": "value" }
282
+ trailingOptions: { "--body-file": "value" }
280
283
  },
281
284
  {
282
285
  commandPath: ["task", "message", "retire"],
@@ -554,7 +557,8 @@ export const INTERACTION_POLICIES = Object.freeze([
554
557
  "--strategy": "value",
555
558
  "--project": "value",
556
559
  "--target": "value",
557
- "--check": "value"
560
+ "--check": "value",
561
+ "--rerun-checks": "flag"
558
562
  }
559
563
  },
560
564
  {
@@ -0,0 +1,64 @@
1
+ import { usageError } from "../errors/cliError.js";
2
+ import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
3
+ import { routeInvocation } from "./invocationRouter.js";
4
+ import { findInteractionPolicy } from "./interactionPolicy.js";
5
+ import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
6
+ import { taskDiagnosticTarget } from "./managedDiagnostics.js";
7
+ /** Configuration reads remain available; every Home configuration mutation
8
+ * has one owner, irrespective of which config subcommand parses its flags. */
9
+ export function assertConfigurationAuthority(args, store, env) {
10
+ const configurationWrite = args[0] === "config"
11
+ && !["show", "describe"].includes(args[1] ?? "")
12
+ && !["show", "list", "capabilities", "status", "candidates"].includes(args[2] ?? "");
13
+ const resourceWrite = args[0] === "resources" && args.some(arg => ["--apply", "--purge", "--restore"].includes(arg));
14
+ if (!configurationWrite && !resourceWrite)
15
+ return;
16
+ if (env.YUI_SESSION_SCOPE === "global" && env.YUI_ROLE === "operator") {
17
+ requireManagedGlobalCaller(store, env);
18
+ return;
19
+ }
20
+ if (env.YUI_SESSION_SCOPE !== undefined || env.YUI_ROLE !== undefined
21
+ || env.YUI_AGENT_ID !== undefined || env.YUI_NATIVE_SESSION_ID !== undefined) {
22
+ throw usageError("Home configuration and resource mutations require the user or current Operator, not a Task Assignment.");
23
+ }
24
+ }
25
+ /** The parsed command path identifies the first target argument, never a
26
+ * string found in a body/flag value. Record-local references retain their
27
+ * normal command-specific parser; an explicit foreign Task cannot pass it. */
28
+ export function assertTaskInvocationScope(args, env) {
29
+ if (env.YUI_SESSION_SCOPE !== "task")
30
+ return;
31
+ if (args[0] === "jobs" || (args[0] === "task" && ["create", "overlap"].includes(args[1] ?? ""))) {
32
+ throw usageError("This global operation is outside the caller's Task; use its scoped Context.");
33
+ }
34
+ if (args[0] !== "task" || args[1] === "list")
35
+ return;
36
+ const diagnostic = taskDiagnosticTarget(args);
37
+ if (diagnostic !== undefined) {
38
+ if (diagnostic !== env.YUI_TASK_ID)
39
+ throw usageError("Command target is outside the caller's Task.");
40
+ return;
41
+ }
42
+ const invocation = routeInvocation([...args]);
43
+ if (invocation.kind !== "execute")
44
+ return;
45
+ let targetIndex = invocation.node.path.length - 1;
46
+ const optionSyntax = findInteractionPolicy(invocation.node)?.trailingOptions;
47
+ while (args[targetIndex]?.startsWith("--")) {
48
+ // Reuse known option arity, not any picker/confirmation authority. An
49
+ // unclassified prefix cannot turn a missing target into permission.
50
+ const kind = optionSyntax?.[args[targetIndex]];
51
+ if (kind === undefined)
52
+ throw usageError("Place this command's Task target before its options so its scope is explicit.");
53
+ targetIndex += kind === "flag" ? 1 : 2;
54
+ }
55
+ const target = args[targetIndex];
56
+ if (target === undefined)
57
+ return; // The domain parser still requires its target.
58
+ const taskId = target.split("/")[0];
59
+ const local = !target.includes("/") && Object.values(TASK_RECORD_ID_PREFIXES)
60
+ .some(prefix => target.startsWith(`${prefix}-`));
61
+ if (!local && taskId !== env.YUI_TASK_ID) {
62
+ throw usageError("Command target is outside the caller's Task.");
63
+ }
64
+ }
@@ -4,7 +4,7 @@ export function taskDiagnosticTarget(args) {
4
4
  if (args[0] !== "task")
5
5
  return undefined;
6
6
  if (["show", "context", "next-action", "archive-preflight"].includes(args[1] ?? "")) {
7
- return args[1] === "context" && ["inspect", "delta"].includes(args[2] ?? "")
7
+ return args[1] === "context" && ["read", "inspect", "delta"].includes(args[2] ?? "")
8
8
  ? args[3] : args[2];
9
9
  }
10
10
  if (args[1] === "role" && ["list", "status", "show"].includes(args[2] ?? ""))