@zq-silk/yui 0.2.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/ARCHITECTURE.md +603 -133
  2. package/README.md +806 -31
  3. package/dist/agent/agent.js +2 -1
  4. package/dist/agent/argumentPolicy.js +3 -1
  5. package/dist/agent/launchEnvironment.js +106 -0
  6. package/dist/agent/managedRuntimeEnvironment.js +34 -0
  7. package/dist/brief/taskBrief.js +11 -1
  8. package/dist/cli/agentConfigurationPicker.js +287 -0
  9. package/dist/cli/commandCatalog.js +488 -60
  10. package/dist/cli/completion.js +146 -22
  11. package/dist/cli/helpRenderer.js +3 -1
  12. package/dist/cli/interactionCandidates.js +53 -15
  13. package/dist/cli/interactionPolicy.js +267 -30
  14. package/dist/cli/interactiveSelection.js +6 -2
  15. package/dist/cli/invocationRouter.js +5 -1
  16. package/dist/cli/operatorWizard.js +87 -0
  17. package/dist/cli/roleOptionCatalog.js +1 -0
  18. package/dist/cli/roleWizard.js +185 -21
  19. package/dist/cli/updateCommand.js +62 -19
  20. package/dist/cli/updateOrchestrator.js +539 -0
  21. package/dist/cli/updatePorts.js +1119 -0
  22. package/dist/cli/upgradeCommand.js +112 -0
  23. package/dist/cli.js +1420 -86
  24. package/dist/commands/agentCommands.js +146 -3
  25. package/dist/commands/configCommands.js +126 -0
  26. package/dist/commands/controllerCommands.js +365 -0
  27. package/dist/commands/globalRoleCommands.js +168 -126
  28. package/dist/commands/jobCommands.js +18 -8
  29. package/dist/commands/operatorCommands.js +159 -9
  30. package/dist/commands/profileCommands.js +203 -0
  31. package/dist/commands/projectCommands.js +650 -0
  32. package/dist/commands/roleConfiguration.js +85 -24
  33. package/dist/commands/roleRuntimeGuard.js +12 -0
  34. package/dist/commands/roleSkillValidation.js +47 -0
  35. package/dist/commands/taskActor.js +127 -0
  36. package/dist/commands/taskCommands.js +4201 -313
  37. package/dist/commands/taskCompletionGate.js +131 -0
  38. package/dist/commands/taskContextCommand.js +244 -30
  39. package/dist/commands/taskInputCommands.js +177 -59
  40. package/dist/commands/taskIntegrationCommands.js +303 -0
  41. package/dist/commands/taskOverviewCommand.js +363 -0
  42. package/dist/commands/taskRoleRuntimeStatus.js +125 -19
  43. package/dist/commands/textInput.js +15 -0
  44. package/dist/completion/completionInstaller.js +26 -22
  45. package/dist/config/yuiConfig.js +4 -3
  46. package/dist/context/dispatchContext.js +90 -38
  47. package/dist/context/roleSessionContext.js +119 -0
  48. package/dist/controller/claudeLifecycleHook.js +203 -0
  49. package/dist/controller/clientRuntime.js +408 -56
  50. package/dist/controller/codexLifecycleHook.js +108 -0
  51. package/dist/controller/controller.js +1089 -32
  52. package/dist/controller/domainIdentity.js +505 -0
  53. package/dist/controller/ephemeralResourceReaper.js +131 -0
  54. package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
  55. package/dist/controller/providerHookRunFence.js +127 -0
  56. package/dist/controller/resourceCleanupLinux.js +286 -0
  57. package/dist/controller/resourceInventory.js +531 -0
  58. package/dist/controller/resourceInventoryLinux.js +610 -0
  59. package/dist/controller/runtime.js +629 -10
  60. package/dist/controller/runtimeEventInbox.js +564 -0
  61. package/dist/controller/runtimeEventProcessor.js +248 -0
  62. package/dist/controller/runtimeLaunchCoordinator.js +477 -0
  63. package/dist/controller/sessionNotify.js +121 -78
  64. package/dist/coordination/deadlineScheduler.js +15 -0
  65. package/dist/coordination/mailboxScheduler.js +108 -0
  66. package/dist/coordination/workMailbox.js +329 -0
  67. package/dist/coordination/workMailboxQueue.js +86 -0
  68. package/dist/core/controllerClient.js +19 -5
  69. package/dist/core/controllerEndpoint.js +37 -0
  70. package/dist/core/controllerServer.js +218 -10
  71. package/dist/core/protocol.js +6 -2
  72. package/dist/decision/decision.js +2 -1
  73. package/dist/doctor/doctor.js +681 -32
  74. package/dist/domain/validation.js +53 -0
  75. package/dist/errors/cliError.js +5 -3
  76. package/dist/event/taskEvent.js +7 -3
  77. package/dist/execution/codexThreadNaming.js +160 -0
  78. package/dist/execution/executionGroup.js +579 -0
  79. package/dist/executor/agentAdapter.js +255 -40
  80. package/dist/executor/agentConfigurationCatalog.js +326 -0
  81. package/dist/executor/agentConfigurationProbe.js +506 -0
  82. package/dist/executor/agentExecutor.js +625 -10
  83. package/dist/executor/codexConfigConflict.js +290 -0
  84. package/dist/executor/effectiveLaunch.js +340 -0
  85. package/dist/executor/executorRegistry.js +238 -36
  86. package/dist/executor/fileRoleLaunchPlanner.js +550 -40
  87. package/dist/executor/turnCompletion.js +126 -0
  88. package/dist/input/inputRequest.js +30 -9
  89. package/dist/integration/changeSet.js +36 -0
  90. package/dist/integration/checkResult.js +24 -0
  91. package/dist/integration/gitIntegrationService.js +695 -0
  92. package/dist/integration/integrationAttempt.js +142 -0
  93. package/dist/interaction/operatorPresentation.js +96 -0
  94. package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
  95. package/dist/lifecycle/exactRunTerminalization.js +572 -0
  96. package/dist/lifecycle/providerLifecycleMapping.js +190 -0
  97. package/dist/lifecycle/taskRoleSessionReset.js +124 -0
  98. package/dist/message/message.js +23 -7
  99. package/dist/milestone/milestone.js +2 -1
  100. package/dist/operator/operatorSessionHistory.js +124 -0
  101. package/dist/output/agentConfigurationPresentation.js +43 -0
  102. package/dist/output/rolePresentation.js +34 -10
  103. package/dist/output/terminal.js +8 -0
  104. package/dist/output/timePresentation.js +55 -0
  105. package/dist/profile/agentProfile.js +128 -0
  106. package/dist/repository/gitWorkspace.js +578 -24
  107. package/dist/repository/project.js +213 -0
  108. package/dist/repository/taskWorkspaceCoordinator.js +392 -0
  109. package/dist/repository/taskWorkspacePreparer.js +1688 -191
  110. package/dist/review/reviewConfig.js +11 -0
  111. package/dist/review/reviewRound.js +399 -0
  112. package/dist/review/taskFinalReviewContract.js +90 -0
  113. package/dist/role/role.js +124 -23
  114. package/dist/run/agentRun.js +155 -12
  115. package/dist/run/runIdentity.js +82 -0
  116. package/dist/runtime/exactControlPlane.js +472 -0
  117. package/dist/runtime/index.js +8 -0
  118. package/dist/runtime/lifecycleReservation.js +38 -0
  119. package/dist/runtime/ports.js +11 -0
  120. package/dist/runtime/preallocatedNativeSession.js +13 -0
  121. package/dist/runtime/promptEnvelope.js +30 -0
  122. package/dist/runtime/runtimeBinding.js +31 -0
  123. package/dist/runtime/runtimeOwner.js +14 -0
  124. package/dist/runtime/sessionLaunchRequest.js +62 -0
  125. package/dist/runtime/sessionTitle.js +54 -0
  126. package/dist/runtime/taskRuntimeIsolation.js +643 -0
  127. package/dist/runtime/tmuxAdapters.js +315 -0
  128. package/dist/runtime/turnCompletion.js +3 -0
  129. package/dist/runtime/validation.js +23 -0
  130. package/dist/scheduler/activeRoleRunDelivery.js +342 -32
  131. package/dist/scheduler/activeTaskProgress.js +63 -0
  132. package/dist/scheduler/leaderFailure.js +2 -1
  133. package/dist/scheduler/leaderWakeupProcessor.js +307 -66
  134. package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
  135. package/dist/scheduler/operatorNotification.js +44 -2
  136. package/dist/scheduler/ports.js +28 -1
  137. package/dist/scheduler/roleRunLiveness.js +131 -25
  138. package/dist/scheduler/roleRunStall.js +951 -0
  139. package/dist/scheduler/taskExecutionProjection.js +544 -0
  140. package/dist/scheduler/wakeupQueue.js +3 -0
  141. package/dist/setup/setupCommand.js +302 -52
  142. package/dist/storage/compatibleTaskStore.js +102 -0
  143. package/dist/storage/migration/baseline.js +78 -0
  144. package/dist/storage/migration/classifier.js +51 -0
  145. package/dist/storage/migration/compatibleCodec.js +53 -0
  146. package/dist/storage/migration/engine.js +147 -0
  147. package/dist/storage/migration/index.js +33 -0
  148. package/dist/storage/migration/planner.js +154 -0
  149. package/dist/storage/migration/productionRegistry.js +486 -0
  150. package/dist/storage/migration/registry.js +169 -0
  151. package/dist/storage/migration/report.js +54 -0
  152. package/dist/storage/migration/types.js +31 -0
  153. package/dist/storage/storageSchema.js +147 -123
  154. package/dist/storage/storageVersions.js +11 -0
  155. package/dist/storage/taskStore.js +1793 -197
  156. package/dist/storage/upgrade/homeClassification.js +156 -0
  157. package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
  158. package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
  159. package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
  160. package/dist/storage/upgrade/recordVersionScan.js +176 -0
  161. package/dist/storage/upgrade/recordVersions.js +159 -0
  162. package/dist/storage/upgrade/switchProgress.js +80 -0
  163. package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
  164. package/dist/storage/upgrade/upgradeReceipt.js +161 -0
  165. package/dist/storage/upgradeCoordination.js +186 -0
  166. package/dist/storage/upgradeFence.js +366 -0
  167. package/dist/task/task.js +132 -26
  168. package/dist/task/taskRecordReference.js +66 -0
  169. package/dist/tmux/commandExecutor.js +75 -2
  170. package/dist/tmux/tmuxManager.js +747 -49
  171. package/dist/version.js +23 -0
  172. package/dist/web/assets/assetManifest.js +62 -0
  173. package/dist/web/assets/client/app.js +631 -0
  174. package/dist/web/assets/client/components.js +605 -0
  175. package/dist/web/assets/client/dom.js +14 -0
  176. package/dist/web/assets/client/format.js +28 -0
  177. package/dist/web/assets/client/i18n.js +494 -0
  178. package/dist/web/assets/client/markdown.js +114 -0
  179. package/dist/web/assets/client/theme.js +32 -0
  180. package/dist/web/assets/client/view.js +458 -0
  181. package/dist/web/assets/fontData.js +12 -0
  182. package/dist/web/assets/fonts.js +12 -0
  183. package/dist/web/assets/shell.js +114 -0
  184. package/dist/web/assets/styles/cards.js +135 -0
  185. package/dist/web/assets/styles/layout.js +47 -0
  186. package/dist/web/assets/styles/markdown.js +29 -0
  187. package/dist/web/assets/styles/responsive.js +39 -0
  188. package/dist/web/assets/styles/tokens.js +101 -0
  189. package/dist/web/assets/styles/widgets.js +147 -0
  190. package/dist/web/tmuxWebTerminal.js +158 -0
  191. package/dist/web/webServer.js +463 -0
  192. package/dist/web/webSnapshot.js +148 -0
  193. package/dist/workItem/workItem.js +642 -23
  194. package/dist/workspace/gitChangeSetCapture.js +86 -0
  195. package/dist/workspace/workItemChangeSetManager.js +445 -0
  196. package/dist/worktree/managedWorkspace.js +202 -0
  197. package/docs/task-local-identity.md +62 -0
  198. package/i18n/README.zh-CN.md +406 -31
  199. package/package.json +10 -2
  200. package/skills/yui-leader/SKILL.md +601 -39
  201. package/skills/yui-operator/SKILL.md +255 -34
  202. package/skills/yui-reviewer/SKILL.md +57 -0
  203. package/skills/yui-worker/SKILL.md +214 -17
  204. package/dist/commands/repositoryCommands.js +0 -86
  205. package/dist/operator/operatorContext.js +0 -66
  206. package/dist/repository/repository.js +0 -55
  207. package/dist/scheduler/archivedTaskRuntime.js +0 -12
  208. package/dist/worktree/roleWorkspace.js +0 -62
@@ -1,5 +1,5 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
- import { resolveAgentAdapter } from "../executor/agentAdapter.js";
2
+ import { defaultRoleAgentConfig, resolveAgentAdapter } from "../executor/agentAdapter.js";
3
3
  const PROFILE_OPTIONS = [
4
4
  ["--description", "value"],
5
5
  ["--responsibility", "repeatable"],
@@ -19,31 +19,48 @@ const PROFILE_CLEAR_OPTIONS = [
19
19
  const AGENT_VALUE_OPTIONS = [
20
20
  ["--model", "value"],
21
21
  ["--effort", "value"],
22
+ ["--permission-strategy", "value"],
22
23
  ["--sandbox", "value"],
23
24
  ["--approval", "value"],
24
25
  ["--permission-mode", "value"],
25
26
  ["--search", "value"]
26
27
  ];
28
+ const AGENT_REPEATABLE_OPTIONS = [
29
+ ["--allowed-tool", "repeatable"],
30
+ ["--disallowed-tool", "repeatable"]
31
+ ];
27
32
  const AGENT_CLEAR_OPTIONS = [
28
33
  ["--clear-model", "flag"],
29
34
  ["--clear-effort", "flag"],
30
- ["--clear-sandbox", "flag"],
31
- ["--clear-approval", "flag"],
32
- ["--clear-permission-mode", "flag"],
35
+ ["--clear-allowed-tools", "flag"],
36
+ ["--clear-disallowed-tools", "flag"],
33
37
  ["--clear-search", "flag"],
34
38
  ["--clear-agent-config", "flag"]
35
39
  ];
36
40
  const AGENT_CONFIG_OPTIONS = new Set([
37
41
  ...AGENT_VALUE_OPTIONS.map(([option]) => option),
42
+ ...AGENT_REPEATABLE_OPTIONS.map(([option]) => option),
38
43
  ...AGENT_CLEAR_OPTIONS.map(([option]) => option)
39
44
  ]);
40
45
  export function roleOptionSpecs(input) {
46
+ const agentValueOptions = input.agentOptions === "execution"
47
+ ? AGENT_VALUE_OPTIONS.filter(([option]) => option === "--model" || option === "--effort")
48
+ : AGENT_VALUE_OPTIONS;
49
+ const agentClearOptions = input.agentOptions === "execution"
50
+ ? AGENT_CLEAR_OPTIONS.filter(([option]) => (option === "--clear-model"
51
+ || option === "--clear-effort"
52
+ || option === "--clear-agent-config"))
53
+ : AGENT_CLEAR_OPTIONS;
54
+ const agentRepeatableOptions = input.agentOptions === "execution"
55
+ ? []
56
+ : AGENT_REPEATABLE_OPTIONS;
41
57
  return new Map([
42
58
  ...(input.includeAgent === true ? [["--agent", "value"]] : []),
43
59
  ...(input.includeWorkspace === true ? [["--workspace", "value"]] : []),
44
60
  ...PROFILE_OPTIONS,
45
- ...AGENT_VALUE_OPTIONS,
46
- ...(input.update ? [...PROFILE_CLEAR_OPTIONS, ...AGENT_CLEAR_OPTIONS] : [])
61
+ ...agentValueOptions,
62
+ ...agentRepeatableOptions,
63
+ ...(input.update ? [...PROFILE_CLEAR_OPTIONS, ...agentClearOptions] : [])
47
64
  ]);
48
65
  }
49
66
  export function parseRoleOptions(args, specs, usage) {
@@ -124,21 +141,12 @@ export function patchRoleAgentBinding(binding, parsed) {
124
141
  assertAgentOptionConflicts(parsed);
125
142
  assertAdapterOptions(binding.adapterId, parsed);
126
143
  if (parsed.has("--clear-agent-config")) {
127
- return { ...binding, config: { adapterId: binding.adapterId } };
144
+ return { ...binding, config: defaultRoleAgentConfig(binding.adapterId) };
128
145
  }
129
146
  const config = structuredClone(binding.config);
130
147
  patchText(config, parsed, "--model", "--clear-model", "model");
131
148
  patchText(config, parsed, "--effort", "--clear-effort", "effort");
132
- const permission = {
133
- ...(config.permission ?? {})
134
- };
135
- patchText(permission, parsed, "--sandbox", "--clear-sandbox", "sandbox");
136
- patchText(permission, parsed, "--approval", "--clear-approval", "approval");
137
- patchText(permission, parsed, "--permission-mode", "--clear-permission-mode", "mode");
138
- if (Object.keys(permission).length === 0)
139
- delete config.permission;
140
- else
141
- config.permission = permission;
149
+ config.permission = permissionPatch(binding, parsed);
142
150
  if (parsed.has("--search")) {
143
151
  if (parsed.one("--search") !== "true") {
144
152
  throw usageError("--search supports true only; use --clear-search to follow the CLI default.");
@@ -155,19 +163,69 @@ export function patchRoleAgentBinding(binding, parsed) {
155
163
  throw usageError(error instanceof Error ? error.message : String(error));
156
164
  }
157
165
  }
166
+ function permissionPatch(binding, parsed) {
167
+ const selected = parsed.has("--permission-strategy")
168
+ ? permissionStrategy(parsed.one("--permission-strategy"))
169
+ : undefined;
170
+ const nativeOptions = [
171
+ "--sandbox", "--approval", "--permission-mode",
172
+ "--allowed-tool", "--clear-allowed-tools",
173
+ "--disallowed-tool", "--clear-disallowed-tools"
174
+ ].some((option) => parsed.has(option));
175
+ if (selected === "default" || selected === "bypass") {
176
+ if (nativeOptions) {
177
+ throw usageError(`--permission-strategy ${selected} cannot be combined with provider permission options.`);
178
+ }
179
+ return { strategy: selected };
180
+ }
181
+ const current = binding.config.permission;
182
+ if (selected !== "configured" && !nativeOptions) {
183
+ return structuredClone(current ?? { strategy: "bypass" });
184
+ }
185
+ const permission = current?.strategy === "configured"
186
+ ? structuredClone(current)
187
+ : { strategy: "configured" };
188
+ permission.strategy = "configured";
189
+ if (binding.adapterId === "codex") {
190
+ if (parsed.has("--sandbox"))
191
+ permission.sandbox = requiredText(parsed.one("--sandbox"));
192
+ if (parsed.has("--approval"))
193
+ permission.approval = requiredText(parsed.one("--approval"));
194
+ }
195
+ else {
196
+ if (parsed.has("--permission-mode")) {
197
+ permission.mode = requiredText(parsed.one("--permission-mode"));
198
+ }
199
+ patchTexts(permission, parsed, "--allowed-tool", "--clear-allowed-tools", "allowedTools");
200
+ patchTexts(permission, parsed, "--disallowed-tool", "--clear-disallowed-tools", "disallowedTools");
201
+ }
202
+ return permission;
203
+ }
204
+ function permissionStrategy(value) {
205
+ const strategy = requiredText(value);
206
+ if (strategy !== "default" && strategy !== "bypass" && strategy !== "configured") {
207
+ throw usageError("--permission-strategy must be default, bypass, or configured.");
208
+ }
209
+ return strategy;
210
+ }
158
211
  function patchText(target, parsed, valueOption, clearOption, key) {
159
212
  if (parsed.has(valueOption))
160
213
  target[key] = requiredText(parsed.one(valueOption));
161
214
  if (parsed.has(clearOption))
162
215
  delete target[key];
163
216
  }
217
+ function patchTexts(target, parsed, valueOption, clearOption, key) {
218
+ if (parsed.has(valueOption))
219
+ target[key] = parsed.many(valueOption).map(requiredText);
220
+ if (parsed.has(clearOption))
221
+ delete target[key];
222
+ }
164
223
  function assertAgentOptionConflicts(parsed) {
165
224
  assertPairs(parsed, [
166
225
  ["--model", "--clear-model"],
167
226
  ["--effort", "--clear-effort"],
168
- ["--sandbox", "--clear-sandbox"],
169
- ["--approval", "--clear-approval"],
170
- ["--permission-mode", "--clear-permission-mode"],
227
+ ["--allowed-tool", "--clear-allowed-tools"],
228
+ ["--disallowed-tool", "--clear-disallowed-tools"],
171
229
  ["--search", "--clear-search"]
172
230
  ]);
173
231
  if (parsed.has("--clear-agent-config")
@@ -176,14 +234,17 @@ function assertAgentOptionConflicts(parsed) {
176
234
  }
177
235
  }
178
236
  function assertAdapterOptions(adapterId, parsed) {
179
- const codex = ["--sandbox", "--clear-sandbox", "--approval", "--clear-approval",
180
- "--search", "--clear-search"];
181
- const claude = ["--permission-mode", "--clear-permission-mode"];
237
+ const codex = ["--sandbox", "--approval", "--search", "--clear-search"];
238
+ const claude = [
239
+ "--permission-mode",
240
+ "--allowed-tool", "--clear-allowed-tools",
241
+ "--disallowed-tool", "--clear-disallowed-tools"
242
+ ];
182
243
  if (adapterId !== "codex" && codex.some((option) => parsed.has(option))) {
183
244
  throw usageError("Sandbox, approval, and search settings are only supported by Codex.");
184
245
  }
185
246
  if (adapterId !== "claude" && claude.some((option) => parsed.has(option))) {
186
- throw usageError("Permission mode is only supported by Claude.");
247
+ throw usageError("Permission mode and tool rules are only supported by Claude.");
187
248
  }
188
249
  }
189
250
  function assertPairs(parsed, pairs) {
@@ -0,0 +1,12 @@
1
+ import { usageError } from "../errors/cliError.js";
2
+ import { hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
3
+ /**
4
+ * Runtime launch reservation and cleanup are ownership obligations for a Role
5
+ * identity. Mutating or recreating that identity while either obligation is
6
+ * queued can make the controller act on a different launch configuration.
7
+ */
8
+ export function assertRoleRuntimeMutationAllowed(store, owner, action) {
9
+ if (!hasRuntimeLifecycleWork(store.getWorkMailbox(runtimeLifecycleTarget(owner))))
10
+ return;
11
+ throw usageError(`Role ${action} is blocked while a runtime lifecycle transition is pending or processing.`);
12
+ }
@@ -0,0 +1,47 @@
1
+ import { accessSync, constants, statSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { usageError } from "../errors/cliError.js";
4
+ const ROLE_LAUNCH_CONTEXT_OPTIONS = new Set([
5
+ "--workspace",
6
+ "--description",
7
+ "--clear-description",
8
+ "--responsibility",
9
+ "--clear-responsibilities",
10
+ "--constraint",
11
+ "--clear-constraints",
12
+ "--expected-output",
13
+ "--clear-expected-output",
14
+ "--system-prompt",
15
+ "--clear-system-prompt",
16
+ "--skill",
17
+ "--clear-skills"
18
+ ]);
19
+ const SAFE_SKILL_ID = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
20
+ export function hasRoleLaunchContextOptions(parsed) {
21
+ return [...parsed.seen].some((option) => ROLE_LAUNCH_CONTEXT_OPTIONS.has(option));
22
+ }
23
+ /**
24
+ * Role Skills are launch-time policy. Reject invalid references before the
25
+ * durable Role is changed so a typo cannot make future enters or wakeups fail.
26
+ */
27
+ export function validateConfiguredRoleSkills(yuiHome, skillIds) {
28
+ if (skillIds.length === 0)
29
+ return;
30
+ if (yuiHome === undefined || yuiHome.trim().length === 0) {
31
+ throw usageError("YUI_HOME is required to configure Role Skills.");
32
+ }
33
+ for (const id of new Set(skillIds)) {
34
+ if (!SAFE_SKILL_ID.test(id)) {
35
+ throw usageError(`Configured Skill id is invalid: ${id}.`);
36
+ }
37
+ const path = resolve(yuiHome, "skills", id, "SKILL.md");
38
+ try {
39
+ if (!statSync(path).isFile())
40
+ throw new Error("not a file");
41
+ accessSync(path, constants.R_OK);
42
+ }
43
+ catch {
44
+ throw usageError(`Configured Skill not found or unreadable: ${id}.`);
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,127 @@
1
+ import { usageError } from "../errors/cliError.js";
2
+ import { activeRoleAgentBinding } from "../role/role.js";
3
+ import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
4
+ const LEADER_ROLE = "leader";
5
+ const LEADER_ACTION_RUN_ENV = "YUI_LEADER_ACTION_RUN_ID";
6
+ const LEADER_ACTION_RECEIPT_ENV = "YUI_LEADER_ACTION_RECEIPT_ID";
7
+ export function taskActor(environment, taskId) {
8
+ const env = environment ?? {};
9
+ if (env.YUI_SESSION_SCOPE === "task"
10
+ && env.YUI_TASK_ID === taskId
11
+ && env.YUI_ROLE === "leader") {
12
+ return "leader";
13
+ }
14
+ if (env.YUI_SESSION_SCOPE === "task") {
15
+ throw usageError(`A managed Task Session may perform this action only as the matching Leader: ${taskId}.`);
16
+ }
17
+ if (env.YUI_SESSION_SCOPE === "global") {
18
+ if (env.YUI_ROLE === "operator")
19
+ return "operator";
20
+ throw usageError("A managed global Session may perform this action only as Operator.");
21
+ }
22
+ if (env.YUI_ROLE !== undefined
23
+ || env.YUI_AGENT_ID !== undefined
24
+ || env.YUI_RUN_ID !== undefined
25
+ || env.YUI_NATIVE_SESSION_ID !== undefined) {
26
+ throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
27
+ }
28
+ return "user";
29
+ }
30
+ /**
31
+ * Resolve an exact current Task Leader Run for event attribution.
32
+ *
33
+ * Task commands may be invoked by a managed process whose environment is
34
+ * stale, incomplete, or copied from another Run. A bare YUI_RUN_ID is never
35
+ * enough: the active durable Run, Role, in-flight receipt, native Session,
36
+ * launch generation, and (when supplied) Home must all agree before an
37
+ * allowlisted lifecycle event can buy a Leader action window. A fixed native
38
+ * Session may keep an earlier Run/launch in its process environment; in that
39
+ * case the current Leader turn must explicitly carry the exact durable
40
+ * Run/receipt pair through the two command-only assertion variables below.
41
+ */
42
+ export function taskLeaderActionRunId(store, taskId, environment, yuiHome) {
43
+ const env = environment ?? {};
44
+ if (env.YUI_SESSION_SCOPE !== "task" || env.YUI_ROLE !== LEADER_ROLE)
45
+ return undefined;
46
+ if (identity(env.YUI_TASK_ID) !== taskId)
47
+ return undefined;
48
+ const environmentAssertion = leaderActionAssertion(env);
49
+ if (environmentAssertion === "invalid")
50
+ return undefined;
51
+ const explicitAssertion = environmentAssertion === undefined ? undefined : environmentAssertion;
52
+ const runId = explicitAssertion?.runId ?? identity(env.YUI_RUN_ID);
53
+ const agentId = identity(env.YUI_AGENT_ID);
54
+ const adapterId = identity(env.YUI_ADAPTER_ID);
55
+ const launchId = identity(env.YUI_LAUNCH_ID);
56
+ const home = identity(env.YUI_HOME);
57
+ if (runId === undefined
58
+ || agentId === undefined
59
+ || adapterId === undefined
60
+ || launchId === undefined
61
+ || home === undefined) {
62
+ return undefined;
63
+ }
64
+ const storeHome = typeof store.rootDirectory === "function"
65
+ ? store.rootDirectory()
66
+ : undefined;
67
+ if (yuiHome !== undefined && home !== yuiHome)
68
+ return undefined;
69
+ if (yuiHome === undefined && storeHome !== undefined && home !== storeHome)
70
+ return undefined;
71
+ const role = store.getRole(taskId, LEADER_ROLE);
72
+ const run = store.getActiveAgentRun(taskId, LEADER_ROLE);
73
+ const sessions = store.getTaskRoleSessionSet(taskId, LEADER_ROLE);
74
+ if (role === null
75
+ || run === null
76
+ || run.status !== "active"
77
+ || run.id !== runId
78
+ || run.taskId !== taskId
79
+ || run.roleName !== LEADER_ROLE
80
+ || run.effective.agentId !== agentId
81
+ || run.effective.adapterId !== adapterId
82
+ || role.activeAgentId !== agentId
83
+ || activeRoleAgentBinding(role).adapterId !== adapterId
84
+ || sessions === null
85
+ || sessions.owner.scope !== "task"
86
+ || sessions.owner.taskId !== taskId
87
+ || sessions.owner.roleName !== LEADER_ROLE
88
+ || sessions.activeAgentId !== agentId
89
+ || sessions.inFlight === null
90
+ || sessions.inFlight.runId !== runId
91
+ || sessions.inFlight.agentId !== agentId
92
+ || sessions.inFlight.receiptId !== formatAgentRunReceiptId(taskId, runId))
93
+ return undefined;
94
+ if (explicitAssertion !== undefined
95
+ && explicitAssertion.receiptId !== formatAgentRunReceiptId(taskId, runId))
96
+ return undefined;
97
+ const session = sessions.sessions[agentId];
98
+ if (session === undefined
99
+ || identity(session.nativeSessionId) === undefined
100
+ || identity(session.launchId) === undefined
101
+ || session.status === "stopped"
102
+ || session.status === "broken"
103
+ || session.adapterId !== adapterId
104
+ || (explicitAssertion === undefined && session.launchId !== launchId))
105
+ return undefined;
106
+ if (env.YUI_NATIVE_SESSION_ID !== undefined
107
+ && env.YUI_NATIVE_SESSION_ID !== session.nativeSessionId)
108
+ return undefined;
109
+ return run.id;
110
+ }
111
+ function leaderActionAssertion(environment) {
112
+ const runId = environment[LEADER_ACTION_RUN_ENV];
113
+ const receiptId = environment[LEADER_ACTION_RECEIPT_ENV];
114
+ if (runId === undefined && receiptId === undefined)
115
+ return undefined;
116
+ const normalizedRunId = identity(runId);
117
+ const normalizedReceiptId = identity(receiptId);
118
+ return normalizedRunId === undefined || normalizedReceiptId === undefined
119
+ ? "invalid"
120
+ : { runId: normalizedRunId, receiptId: normalizedReceiptId };
121
+ }
122
+ function identity(value) {
123
+ if (typeof value !== "string" || value.includes("\0"))
124
+ return undefined;
125
+ const normalized = value.trim();
126
+ return normalized.length === 0 || normalized !== value ? undefined : normalized;
127
+ }