@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.
- package/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- 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-
|
|
31
|
-
["--clear-
|
|
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
|
-
...
|
|
46
|
-
...
|
|
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:
|
|
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
|
-
|
|
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
|
-
["--
|
|
169
|
-
["--
|
|
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", "--
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
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
|
+
}
|