@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
|
@@ -3,11 +3,19 @@ import { realpathSync, statSync } from "node:fs";
|
|
|
3
3
|
import { isAbsolute, resolve } from "node:path";
|
|
4
4
|
import { supportedAgentAdapterIds } from "../agent/adapterCatalog.js";
|
|
5
5
|
import { ownedArgumentsForAdapter, validateAgentAdvancedArguments, validateAgentBaseArguments } from "../agent/argumentPolicy.js";
|
|
6
|
+
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
7
|
+
import { inspectCodexDeveloperInstructions } from "./codexConfigConflict.js";
|
|
8
|
+
import { discoverClaudeConfiguration, discoverCodexConfiguration } from "./agentConfigurationProbe.js";
|
|
9
|
+
import { preInputReadinessCapability } from "../lifecycle/providerLifecycleMapping.js";
|
|
6
10
|
const SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
|
|
7
11
|
const APPROVALS = ["untrusted", "on-request", "never"];
|
|
8
12
|
const PROBE_TIMEOUT_MS = 2_000;
|
|
9
13
|
const PROBE_MAX_BYTES = 1024 * 1024;
|
|
14
|
+
const CODEX_TESTED_THROUGH_VERSION = "0.145.0";
|
|
10
15
|
class BaseAdapter {
|
|
16
|
+
launchContextArgs(_input) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
11
19
|
validateConfig(input) {
|
|
12
20
|
if (input.agent.adapterId !== this.id || input.config.adapterId !== this.id) {
|
|
13
21
|
throw new Error(`Agent adapter identity mismatch: expected ${this.id}.`);
|
|
@@ -19,7 +27,12 @@ class BaseAdapter {
|
|
|
19
27
|
this.validateConfig(input);
|
|
20
28
|
const config = this.canonicalizeConfig(input.config);
|
|
21
29
|
return {
|
|
22
|
-
argv: [
|
|
30
|
+
argv: [
|
|
31
|
+
...input.agent.baseArgs,
|
|
32
|
+
...this.structuredArgs(config),
|
|
33
|
+
...this.launchContextArgs(input),
|
|
34
|
+
...(config.advanced?.rawArgs ?? [])
|
|
35
|
+
],
|
|
23
36
|
sessionStrategy: this.capabilities.nativeSessionDiscovery === "runtime"
|
|
24
37
|
? "runtime-discovery"
|
|
25
38
|
: "preallocated"
|
|
@@ -40,7 +53,15 @@ class CodexAdapter extends BaseAdapter {
|
|
|
40
53
|
id = "codex";
|
|
41
54
|
label = "Codex";
|
|
42
55
|
supportedVersion = "0.144.1";
|
|
43
|
-
capabilities = {
|
|
56
|
+
capabilities = {
|
|
57
|
+
recover: true,
|
|
58
|
+
interrupt: true,
|
|
59
|
+
nativeSessionDiscovery: "runtime",
|
|
60
|
+
preInputReadiness: preInputReadinessCapability("codex")
|
|
61
|
+
};
|
|
62
|
+
discoverConfiguration(input) {
|
|
63
|
+
return discoverCodexConfiguration(input);
|
|
64
|
+
}
|
|
44
65
|
validateStructured(config) {
|
|
45
66
|
exact(config, ["adapterId", "model", "effort", "permission", "search", "profile",
|
|
46
67
|
"additionalDirectories", "advanced"], "Codex Agent config");
|
|
@@ -53,33 +74,85 @@ class CodexAdapter extends BaseAdapter {
|
|
|
53
74
|
throw new Error("Codex search must be boolean.");
|
|
54
75
|
}
|
|
55
76
|
validatePaths(config.additionalDirectories, "Codex additional directory");
|
|
56
|
-
if (config.permission
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
if (config.permission === undefined) {
|
|
78
|
+
throw new Error("Codex permission strategy is required.");
|
|
79
|
+
}
|
|
80
|
+
if (config.permission.strategy === "configured") {
|
|
81
|
+
exact(config.permission, ["strategy", "sandbox", "approval"], "Codex permission config");
|
|
82
|
+
if (config.permission.sandbox !== undefined
|
|
83
|
+
&& !SANDBOXES.includes(config.permission.sandbox)) {
|
|
59
84
|
throw new Error("Codex sandbox is invalid.");
|
|
60
85
|
}
|
|
61
|
-
if (config.permission.approval !== undefined
|
|
86
|
+
if (config.permission.approval !== undefined
|
|
87
|
+
&& !APPROVALS.includes(config.permission.approval)) {
|
|
62
88
|
throw new Error("Codex approval is invalid.");
|
|
63
89
|
}
|
|
90
|
+
requireConfiguredPermissionOption(config.permission, "Codex");
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
exact(config.permission, ["strategy"], "Codex permission config");
|
|
94
|
+
validateSimplePermissionStrategy(config.permission.strategy, "Codex permission strategy");
|
|
64
95
|
}
|
|
65
96
|
advanced(this.id, config.advanced);
|
|
66
97
|
}
|
|
67
98
|
structuredArgs(config) {
|
|
68
99
|
return [
|
|
69
|
-
// Yui launches Codex in a detached tmux window
|
|
70
|
-
//
|
|
71
|
-
// interactive prompt, so leaving it enabled can consume the first
|
|
72
|
-
// automated delivery as an update-menu answer.
|
|
100
|
+
// Yui launches Codex in a detached tmux window. Disable the startup
|
|
101
|
+
// updater so an unrelated native prompt cannot consume managed input.
|
|
73
102
|
"--config", "check_for_update_on_startup=false",
|
|
74
103
|
...(config.model === undefined ? [] : ["--model", config.model]),
|
|
75
104
|
...(config.effort === undefined ? [] : ["--config", `model_reasoning_effort=\"${config.effort}\"`]),
|
|
76
|
-
...(config.permission
|
|
77
|
-
|
|
105
|
+
...(config.permission.strategy === "bypass"
|
|
106
|
+
? ["--dangerously-bypass-approvals-and-sandbox"]
|
|
107
|
+
: config.permission.strategy === "configured"
|
|
108
|
+
? [
|
|
109
|
+
...(config.permission.sandbox === undefined
|
|
110
|
+
? [] : ["--sandbox", config.permission.sandbox]),
|
|
111
|
+
...(config.permission.approval === undefined
|
|
112
|
+
? [] : ["--ask-for-approval", config.permission.approval])
|
|
113
|
+
]
|
|
114
|
+
: []),
|
|
78
115
|
...(config.search === true ? ["--search"] : []),
|
|
79
116
|
...(config.profile === undefined ? [] : ["--profile", config.profile]),
|
|
80
117
|
...(config.additionalDirectories ?? []).flatMap((path) => ["--add-dir", path])
|
|
81
118
|
];
|
|
82
119
|
}
|
|
120
|
+
launchContextArgs(input) {
|
|
121
|
+
// Yui has already scoped and authorized this exact workspace. Declare that
|
|
122
|
+
// invocation-local trust explicitly so Codex does not place its interactive
|
|
123
|
+
// directory trust prompt in front of the managed first input. This does not
|
|
124
|
+
// mutate the user's Codex config or trust any parent/sibling directory.
|
|
125
|
+
const workspaceTrust = [
|
|
126
|
+
"--config",
|
|
127
|
+
`projects={${JSON.stringify(resolve(input.workspace))}={trust_level="trusted"}}`
|
|
128
|
+
];
|
|
129
|
+
const instructions = [
|
|
130
|
+
input.developerInstructions,
|
|
131
|
+
...(input.skills === undefined || input.skills.length === 0
|
|
132
|
+
? []
|
|
133
|
+
: [
|
|
134
|
+
"Yui Role Skills are available at the paths below. Before performing work governed by one, read and follow its SKILL.md on demand; do not treat this list as a user message.",
|
|
135
|
+
...input.skills.map((skill) => `- ${skill.id}: ${skill.path}/SKILL.md`)
|
|
136
|
+
])
|
|
137
|
+
].filter((value) => value !== undefined && value.trim().length > 0);
|
|
138
|
+
if (instructions.length === 0)
|
|
139
|
+
return workspaceTrust;
|
|
140
|
+
const nativeInstructions = input.codexDeveloperInstructions
|
|
141
|
+
?? inspectCodexDeveloperInstructions({
|
|
142
|
+
workspace: input.workspace,
|
|
143
|
+
profile: input.config.profile,
|
|
144
|
+
trustWorkspace: true
|
|
145
|
+
});
|
|
146
|
+
if (nativeInstructions.status === "configured") {
|
|
147
|
+
throw new Error("Codex developer_instructions is already configured by "
|
|
148
|
+
+ `${nativeInstructions.source}; Yui refuses to replace native developer instructions.`);
|
|
149
|
+
}
|
|
150
|
+
return [
|
|
151
|
+
...workspaceTrust,
|
|
152
|
+
"--config",
|
|
153
|
+
`developer_instructions=${tomlString(instructions.join("\n"))}`
|
|
154
|
+
];
|
|
155
|
+
}
|
|
83
156
|
compileResume(input) {
|
|
84
157
|
const launch = this.compileNew(input);
|
|
85
158
|
return { ...launch, argv: [...launch.argv, "resume", nativeId(input.nativeSessionId)] };
|
|
@@ -89,7 +162,15 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
89
162
|
id = "claude";
|
|
90
163
|
label = "Claude";
|
|
91
164
|
supportedVersion = "2.1.207";
|
|
92
|
-
capabilities = {
|
|
165
|
+
capabilities = {
|
|
166
|
+
recover: true,
|
|
167
|
+
interrupt: true,
|
|
168
|
+
nativeSessionDiscovery: "preallocated",
|
|
169
|
+
preInputReadiness: preInputReadinessCapability("claude")
|
|
170
|
+
};
|
|
171
|
+
discoverConfiguration(input) {
|
|
172
|
+
return discoverClaudeConfiguration(input);
|
|
173
|
+
}
|
|
93
174
|
validateStructured(config) {
|
|
94
175
|
exact(config, ["adapterId", "model", "effort", "permission", "additionalDirectories",
|
|
95
176
|
"settingsFile", "settingsSources", "advanced"], "Claude Agent config");
|
|
@@ -104,16 +185,23 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
104
185
|
if (config.settingsSources !== undefined && new Set(config.settingsSources).size !== config.settingsSources.length) {
|
|
105
186
|
throw new Error("Claude settings sources contain duplicates.");
|
|
106
187
|
}
|
|
107
|
-
if (config.permission
|
|
108
|
-
|
|
188
|
+
if (config.permission === undefined) {
|
|
189
|
+
throw new Error("Claude permission strategy is required.");
|
|
190
|
+
}
|
|
191
|
+
if (config.permission.strategy === "configured") {
|
|
192
|
+
exact(config.permission, ["strategy", "mode", "allowedTools", "disallowedTools"], "Claude permission config");
|
|
109
193
|
optionalText(config.permission.mode, "Claude permission mode");
|
|
110
194
|
optionalTexts(config.permission.allowedTools, "Claude allowed tool");
|
|
111
195
|
optionalTexts(config.permission.disallowedTools, "Claude disallowed tool");
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
196
|
+
if (config.permission.allowedTools?.length === 0
|
|
197
|
+
|| config.permission.disallowedTools?.length === 0) {
|
|
198
|
+
throw new Error("Claude configured tool lists must not be empty.");
|
|
116
199
|
}
|
|
200
|
+
requireConfiguredPermissionOption(config.permission, "Claude");
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
exact(config.permission, ["strategy"], "Claude permission config");
|
|
204
|
+
validateSimplePermissionStrategy(config.permission.strategy, "Claude permission strategy");
|
|
117
205
|
}
|
|
118
206
|
advanced(this.id, config.advanced);
|
|
119
207
|
}
|
|
@@ -121,22 +209,72 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
121
209
|
return [
|
|
122
210
|
...(config.model === undefined ? [] : ["--model", config.model]),
|
|
123
211
|
...(config.effort === undefined ? [] : ["--effort", config.effort]),
|
|
124
|
-
...(config.permission
|
|
125
|
-
|
|
126
|
-
|
|
212
|
+
...(config.permission.strategy === "bypass"
|
|
213
|
+
? ["--dangerously-skip-permissions"]
|
|
214
|
+
: config.permission.strategy === "configured"
|
|
215
|
+
&& config.permission.mode !== undefined
|
|
216
|
+
? ["--permission-mode", config.permission.mode]
|
|
217
|
+
: []),
|
|
218
|
+
...(config.permission.strategy !== "configured"
|
|
219
|
+
|| config.permission.allowedTools === undefined
|
|
220
|
+
? [] : ["--allowed-tools", ...config.permission.allowedTools]),
|
|
221
|
+
...(config.permission.strategy !== "configured"
|
|
222
|
+
|| config.permission.disallowedTools === undefined
|
|
223
|
+
? [] : ["--disallowed-tools", ...config.permission.disallowedTools]),
|
|
127
224
|
...(config.additionalDirectories ?? []).flatMap((path) => ["--add-dir", path]),
|
|
128
225
|
...(config.settingsFile === undefined ? [] : ["--settings", config.settingsFile]),
|
|
129
226
|
...(config.settingsSources === undefined ? [] : ["--setting-sources", config.settingsSources.join(",")])
|
|
130
227
|
];
|
|
131
228
|
}
|
|
229
|
+
compileNew(input) {
|
|
230
|
+
const launch = super.compileNew(input);
|
|
231
|
+
return input.sessionTitle === undefined
|
|
232
|
+
? launch
|
|
233
|
+
: {
|
|
234
|
+
...launch,
|
|
235
|
+
argv: [...launch.argv, "--name", sessionTitle(input.sessionTitle)]
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
launchContextArgs(input) {
|
|
239
|
+
const sections = [
|
|
240
|
+
input.developerInstructions,
|
|
241
|
+
...(input.skills ?? []).map((skill) => [
|
|
242
|
+
`# Yui Skill: ${skill.id}`,
|
|
243
|
+
skill.content
|
|
244
|
+
].join("\n\n"))
|
|
245
|
+
].filter((value) => value !== undefined && value.trim().length > 0);
|
|
246
|
+
if (sections.length === 0)
|
|
247
|
+
return [];
|
|
248
|
+
const context = sections.join("\n\n");
|
|
249
|
+
if (input.managedContextFile === undefined) {
|
|
250
|
+
throw new Error("Claude session context requires a managed context file under YUI_HOME.");
|
|
251
|
+
}
|
|
252
|
+
writeTextFileAtomically(input.managedContextFile, context);
|
|
253
|
+
return ["--append-system-prompt-file", input.managedContextFile];
|
|
254
|
+
}
|
|
132
255
|
compileResume(input) {
|
|
133
|
-
const launch =
|
|
256
|
+
const launch = super.compileNew(input);
|
|
134
257
|
return { ...launch, argv: [...launch.argv, "--resume", nativeId(input.nativeSessionId)] };
|
|
135
258
|
}
|
|
136
259
|
}
|
|
137
260
|
const ADAPTERS = {
|
|
138
261
|
codex: new CodexAdapter(), claude: new ClaudeAdapter()
|
|
139
262
|
};
|
|
263
|
+
function tomlString(value) {
|
|
264
|
+
if (value.includes("\0"))
|
|
265
|
+
throw new Error("Agent launch context cannot contain NUL bytes.");
|
|
266
|
+
return JSON.stringify(value);
|
|
267
|
+
}
|
|
268
|
+
function sessionTitle(value) {
|
|
269
|
+
if (typeof value !== "string" || value.includes("\0")) {
|
|
270
|
+
throw new Error("Agent session title is invalid.");
|
|
271
|
+
}
|
|
272
|
+
const normalized = value.trim();
|
|
273
|
+
if (normalized.length === 0 || normalized.length > 1_024) {
|
|
274
|
+
throw new Error("Agent session title is invalid.");
|
|
275
|
+
}
|
|
276
|
+
return normalized;
|
|
277
|
+
}
|
|
140
278
|
export { supportedAgentAdapterIds };
|
|
141
279
|
export function findAgentAdapter(id) {
|
|
142
280
|
return id === "codex" || id === "claude" ? ADAPTERS[id] : null;
|
|
@@ -171,24 +309,53 @@ export function inspectAgentCapabilities(agent, optionsOrNow = {}) {
|
|
|
171
309
|
reason: "Agent version probe did not return a semantic version.", probedAt: at
|
|
172
310
|
}, baseline(agent.adapterId), at);
|
|
173
311
|
}
|
|
174
|
-
const supported = supports(version, adapter
|
|
312
|
+
const supported = supports(version, adapter);
|
|
175
313
|
let fields = baseline(agent.adapterId);
|
|
176
|
-
const warnings =
|
|
177
|
-
if (supported) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
314
|
+
const warnings = [];
|
|
315
|
+
if (!supported) {
|
|
316
|
+
return snapshot(agent, adapter, {
|
|
317
|
+
status: "unsupported-version", command: agent.command, version,
|
|
318
|
+
reason: adapter.id === "codex"
|
|
319
|
+
? `Minimum supported version is ${adapter.supportedVersion}.`
|
|
320
|
+
: `Supported version line starts at ${adapter.supportedVersion}.`,
|
|
321
|
+
probedAt: at
|
|
322
|
+
}, fields, at, [`Installed version ${version} is not supported by adapter ${adapter.id}.`]);
|
|
323
|
+
}
|
|
324
|
+
const help = run(agent.command, ["--help"]);
|
|
325
|
+
const helpFailure = failed(help);
|
|
326
|
+
if (adapter.id === "codex" && helpFailure !== undefined) {
|
|
327
|
+
return snapshot(agent, adapter, {
|
|
328
|
+
status: "probe-failed", command: agent.command, version,
|
|
329
|
+
reason: `Required Codex capability probe failed: ${helpFailure}`, probedAt: at
|
|
330
|
+
}, fields, at);
|
|
331
|
+
}
|
|
332
|
+
if (helpFailure === undefined) {
|
|
333
|
+
const helpOutput = output(help.stdout, help.stderr);
|
|
334
|
+
fields = fromHelp(agent.adapterId, helpOutput);
|
|
335
|
+
if (adapter.id === "codex") {
|
|
336
|
+
const missing = missingCodexCapabilities(helpOutput);
|
|
337
|
+
if (missing.length > 0) {
|
|
338
|
+
return snapshot(agent, adapter, {
|
|
339
|
+
status: "unsupported-version", command: agent.command, version,
|
|
340
|
+
reason: `Codex CLI is missing required capabilities: ${missing.join(", ")}.`,
|
|
341
|
+
probedAt: at
|
|
342
|
+
}, fields, at);
|
|
343
|
+
}
|
|
344
|
+
if (compareVersions(version, CODEX_TESTED_THROUGH_VERSION) > 0) {
|
|
345
|
+
warnings.push(`Installed Codex version ${version} is newer than the latest tested version `
|
|
346
|
+
+ `${CODEX_TESTED_THROUGH_VERSION}; required capabilities were detected.`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
181
349
|
}
|
|
182
350
|
return snapshot(agent, adapter, {
|
|
183
|
-
status:
|
|
184
|
-
...(supported ? {} : { reason: `Supported version line starts at ${adapter.supportedVersion}.` }),
|
|
185
|
-
probedAt: at
|
|
351
|
+
status: "installed", command: agent.command, version, probedAt: at
|
|
186
352
|
}, fields, at, warnings);
|
|
187
353
|
}
|
|
188
354
|
function baseline(id) {
|
|
189
355
|
if (id === "codex")
|
|
190
356
|
return [
|
|
191
357
|
field("model", "enum", "degraded", true), field("effort", "enum", "unavailable", true),
|
|
358
|
+
field("permission.strategy", "enum", "available", false, ["default", "bypass", "configured"]),
|
|
192
359
|
field("permission.sandbox", "enum", "available", false, SANDBOXES),
|
|
193
360
|
field("permission.approval", "enum", "available", false, APPROVALS),
|
|
194
361
|
field("profile", "string", "available", true), field("search", "boolean", "available", false, ["true"]),
|
|
@@ -196,7 +363,9 @@ function baseline(id) {
|
|
|
196
363
|
];
|
|
197
364
|
return [
|
|
198
365
|
field("model", "enum", "degraded", true, ["fable", "opus", "sonnet"]),
|
|
199
|
-
field("effort", "enum", "unavailable", true),
|
|
366
|
+
field("effort", "enum", "unavailable", true),
|
|
367
|
+
field("permission.strategy", "enum", "available", false, ["default", "bypass", "configured"]),
|
|
368
|
+
field("permission.mode", "enum", "unavailable", true),
|
|
200
369
|
field("permission.allowedTools", "string-list", "available", true),
|
|
201
370
|
field("permission.disallowedTools", "string-list", "available", true),
|
|
202
371
|
field("additionalDirectories", "path-list", "available", true), field("settingsFile", "path", "available", true),
|
|
@@ -206,15 +375,25 @@ function baseline(id) {
|
|
|
206
375
|
function fromHelp(id, help) {
|
|
207
376
|
const fields = baseline(id);
|
|
208
377
|
const replacements = id === "codex"
|
|
209
|
-
? [
|
|
378
|
+
? [permissionStrategyField(help, "--dangerously-bypass-approvals-and-sandbox"),
|
|
379
|
+
choiceField("permission.sandbox", help, "--sandbox", SANDBOXES),
|
|
210
380
|
choiceField("permission.approval", help, "--ask-for-approval", APPROVALS)]
|
|
211
381
|
: [choiceField("model", help, "--model", ["fable", "opus", "sonnet"], true),
|
|
212
382
|
choiceField("effort", help, "--effort", [], true),
|
|
383
|
+
permissionStrategyField(help, "--dangerously-skip-permissions"),
|
|
213
384
|
choiceField("permission.mode", help, "--permission-mode", [], true),
|
|
214
385
|
choiceField("settingsSources", help, "--setting-sources", ["user", "project", "local"])];
|
|
215
386
|
const byKey = new Map(replacements.map((value) => [value.key, value]));
|
|
216
387
|
return fields.map((value) => byKey.get(value.key) ?? value);
|
|
217
388
|
}
|
|
389
|
+
function permissionStrategyField(help, bypassFlag) {
|
|
390
|
+
const choices = [
|
|
391
|
+
"default",
|
|
392
|
+
...(help.includes(bypassFlag) ? ["bypass"] : []),
|
|
393
|
+
"configured"
|
|
394
|
+
];
|
|
395
|
+
return field("permission.strategy", "enum", choices.includes("bypass") ? "available" : "degraded", false, choices);
|
|
396
|
+
}
|
|
218
397
|
function choiceField(key, help, flag, fallback, custom = false) {
|
|
219
398
|
const choices = helpChoices(help, flag);
|
|
220
399
|
return field(key, key === "settingsSources" ? "string-list" : "enum", choices.length > 0 ? "available" : fallback.length > 0 ? "degraded" : "unavailable", custom, choices.length > 0 ? choices : fallback);
|
|
@@ -238,7 +417,7 @@ function helpChoices(help, flag) {
|
|
|
238
417
|
function snapshot(agent, adapter, installation, fields, at, warnings = []) {
|
|
239
418
|
return { schemaVersion: 1, agentId: agent.id, adapterId: agent.adapterId, installation,
|
|
240
419
|
lifecycle: { start: true, resume: true, nativeSessionDiscovery: adapter.capabilities.nativeSessionDiscovery,
|
|
241
|
-
interrupt: true }, fields, warnings, refreshedAt: at };
|
|
420
|
+
interrupt: true, preInputReadiness: adapter.capabilities.preInputReadiness }, fields, warnings, refreshedAt: at };
|
|
242
421
|
}
|
|
243
422
|
function runProbe(command, args) {
|
|
244
423
|
const result = spawnSync(command, [...args], { encoding: "utf8", shell: false, timeout: PROBE_TIMEOUT_MS,
|
|
@@ -258,25 +437,61 @@ function output(stdout, stderr) {
|
|
|
258
437
|
throw new Error("Agent probe output exceeded 1 MiB.");
|
|
259
438
|
return value;
|
|
260
439
|
}
|
|
261
|
-
function supports(version,
|
|
262
|
-
|
|
440
|
+
function supports(version, adapter) {
|
|
441
|
+
if (adapter.id === "codex")
|
|
442
|
+
return compareVersions(version, adapter.supportedVersion) >= 0;
|
|
443
|
+
const left = version.split(".").map(Number), right = adapter.supportedVersion.split(".").map(Number);
|
|
263
444
|
return left[0] === right[0] && left[1] === right[1] && left[2] >= right[2];
|
|
264
445
|
}
|
|
446
|
+
function compareVersions(leftVersion, rightVersion) {
|
|
447
|
+
const left = leftVersion.split(".").map(Number);
|
|
448
|
+
const right = rightVersion.split(".").map(Number);
|
|
449
|
+
for (let index = 0; index < 3; index += 1) {
|
|
450
|
+
const difference = (left[index] ?? 0) - (right[index] ?? 0);
|
|
451
|
+
if (difference !== 0)
|
|
452
|
+
return difference;
|
|
453
|
+
}
|
|
454
|
+
return 0;
|
|
455
|
+
}
|
|
456
|
+
function missingCodexCapabilities(help) {
|
|
457
|
+
const required = [
|
|
458
|
+
[/(?:^|\s)--config(?:\s|[=<,]|$)/m, "--config"],
|
|
459
|
+
[/^\s*resume(?:\s|$)/m, "resume"]
|
|
460
|
+
];
|
|
461
|
+
return required.flatMap(([pattern, label]) => pattern.test(help) ? [] : [label]);
|
|
462
|
+
}
|
|
265
463
|
function cloneConfig(config, paths) {
|
|
266
464
|
const advancedConfig = config.advanced?.rawArgs === undefined ? config.advanced : { rawArgs: [...config.advanced.rawArgs] };
|
|
267
465
|
if (config.adapterId === "codex")
|
|
268
466
|
return { ...config,
|
|
269
|
-
|
|
467
|
+
permission: { ...config.permission },
|
|
270
468
|
...(paths === undefined ? {} : { additionalDirectories: [...paths] }),
|
|
271
469
|
...(advancedConfig === undefined ? {} : { advanced: advancedConfig }) };
|
|
272
470
|
return { ...config,
|
|
273
|
-
|
|
471
|
+
permission: config.permission.strategy === "configured"
|
|
472
|
+
? { ...config.permission,
|
|
274
473
|
...(config.permission.allowedTools === undefined ? {} : { allowedTools: [...config.permission.allowedTools] }),
|
|
275
|
-
...(config.permission.disallowedTools === undefined ? {} : { disallowedTools: [...config.permission.disallowedTools] }) }
|
|
474
|
+
...(config.permission.disallowedTools === undefined ? {} : { disallowedTools: [...config.permission.disallowedTools] }) }
|
|
475
|
+
: { ...config.permission },
|
|
276
476
|
...(paths === undefined ? {} : { additionalDirectories: [...paths] }),
|
|
277
477
|
...(config.settingsSources === undefined ? {} : { settingsSources: [...config.settingsSources] }),
|
|
278
478
|
...(advancedConfig === undefined ? {} : { advanced: advancedConfig }) };
|
|
279
479
|
}
|
|
480
|
+
export function defaultRoleAgentConfig(adapterId) {
|
|
481
|
+
return adapterId === "codex"
|
|
482
|
+
? { adapterId: "codex", permission: { strategy: "bypass" } }
|
|
483
|
+
: { adapterId: "claude", permission: { strategy: "bypass" } };
|
|
484
|
+
}
|
|
485
|
+
function validateSimplePermissionStrategy(value, label) {
|
|
486
|
+
if (value !== "default" && value !== "bypass") {
|
|
487
|
+
throw new Error(`${label} is invalid.`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function requireConfiguredPermissionOption(permission, provider) {
|
|
491
|
+
if (Object.keys(permission).every((key) => key === "strategy")) {
|
|
492
|
+
throw new Error(`${provider} configured permission requires at least one provider-native option.`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
280
495
|
function advanced(id, value) {
|
|
281
496
|
if (value === undefined)
|
|
282
497
|
return;
|