@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
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
6
|
+
import { resolveAgentAdapter } from "./agentAdapter.js";
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 8_000;
|
|
8
|
+
export class AgentConfigurationCatalogService {
|
|
9
|
+
yuiHome;
|
|
10
|
+
#environment;
|
|
11
|
+
#timeoutMs;
|
|
12
|
+
#now;
|
|
13
|
+
#discover;
|
|
14
|
+
#requests = new Map();
|
|
15
|
+
constructor(yuiHome, options = {}) {
|
|
16
|
+
this.yuiHome = yuiHome;
|
|
17
|
+
this.#environment = options.environment ?? process.env;
|
|
18
|
+
this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
19
|
+
this.#now = options.now ?? (() => new Date());
|
|
20
|
+
this.#discover = options.discover ?? ((input) => resolveAgentAdapter(input.agent.adapterId).discoverConfiguration(input));
|
|
21
|
+
}
|
|
22
|
+
resolve(input) {
|
|
23
|
+
const fingerprint = catalogFingerprint(input, this.#environment);
|
|
24
|
+
const existing = this.#requests.get(fingerprint);
|
|
25
|
+
if (existing !== undefined)
|
|
26
|
+
return existing;
|
|
27
|
+
const request = this.#resolve(input, fingerprint);
|
|
28
|
+
this.#requests.set(fingerprint, request);
|
|
29
|
+
return request;
|
|
30
|
+
}
|
|
31
|
+
async #resolve(input, fingerprint) {
|
|
32
|
+
const attemptedAt = this.#now().toISOString();
|
|
33
|
+
const controller = new AbortController();
|
|
34
|
+
let timeout;
|
|
35
|
+
try {
|
|
36
|
+
const discovered = await Promise.race([
|
|
37
|
+
this.#discover({
|
|
38
|
+
...input,
|
|
39
|
+
environment: this.#environment,
|
|
40
|
+
signal: controller.signal
|
|
41
|
+
}),
|
|
42
|
+
new Promise((_resolve, reject) => {
|
|
43
|
+
timeout = setTimeout(() => {
|
|
44
|
+
controller.abort();
|
|
45
|
+
reject(Object.assign(new Error(`Agent capability discovery timed out after ${this.#timeoutMs}ms.`), { code: "ETIMEDOUT" }));
|
|
46
|
+
}, this.#timeoutMs);
|
|
47
|
+
})
|
|
48
|
+
]);
|
|
49
|
+
const catalog = validateCatalog(discovered, input.agent);
|
|
50
|
+
const fetchedAt = this.#now().toISOString();
|
|
51
|
+
try {
|
|
52
|
+
writeTextFileAtomically(cachePath(this.yuiHome, input.agent.id, fingerprint), `${JSON.stringify({
|
|
53
|
+
schemaVersion: 1,
|
|
54
|
+
fingerprint,
|
|
55
|
+
fetchedAt,
|
|
56
|
+
catalog
|
|
57
|
+
}, null, 2)}\n`);
|
|
58
|
+
return { source: "live", attemptedAt, fetchedAt, catalog };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {
|
|
62
|
+
source: "live",
|
|
63
|
+
attemptedAt,
|
|
64
|
+
fetchedAt,
|
|
65
|
+
catalog: {
|
|
66
|
+
...catalog,
|
|
67
|
+
warnings: [
|
|
68
|
+
...catalog.warnings,
|
|
69
|
+
"The runtime catalog cache could not be updated."
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const failure = catalogFailure(error);
|
|
77
|
+
const cached = readCachedCatalog(cachePath(this.yuiHome, input.agent.id, fingerprint), fingerprint, input.agent);
|
|
78
|
+
if (cached !== null) {
|
|
79
|
+
return {
|
|
80
|
+
source: "cache",
|
|
81
|
+
attemptedAt,
|
|
82
|
+
fetchedAt: cached.fetchedAt,
|
|
83
|
+
catalog: cached.catalog,
|
|
84
|
+
failure
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
source: "fallback",
|
|
89
|
+
attemptedAt,
|
|
90
|
+
catalog: fallbackAgentConfigurationCatalog(input.agent),
|
|
91
|
+
failure
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (timeout !== undefined)
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
controller.abort();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function fallbackAgentConfigurationCatalog(agent) {
|
|
102
|
+
const choice = (value) => ({ value, label: value });
|
|
103
|
+
const common = [
|
|
104
|
+
field("model", [], true),
|
|
105
|
+
field("effort", [], true)
|
|
106
|
+
];
|
|
107
|
+
return agent.adapterId === "codex"
|
|
108
|
+
? {
|
|
109
|
+
schemaVersion: 1,
|
|
110
|
+
agentId: agent.id,
|
|
111
|
+
adapterId: "codex",
|
|
112
|
+
models: [],
|
|
113
|
+
fields: [
|
|
114
|
+
...common,
|
|
115
|
+
field("permission.strategy", ["default", "bypass", "configured"].map(choice), false),
|
|
116
|
+
field("permission.sandbox", [
|
|
117
|
+
"read-only", "workspace-write", "danger-full-access"
|
|
118
|
+
].map(choice), false),
|
|
119
|
+
field("permission.approval", [
|
|
120
|
+
"untrusted", "on-request", "never"
|
|
121
|
+
].map(choice), false),
|
|
122
|
+
field("search", [choice("true")], false),
|
|
123
|
+
field("profile", [], true),
|
|
124
|
+
field("additionalDirectories", [], true)
|
|
125
|
+
],
|
|
126
|
+
warnings: ["Runtime configuration catalog is unavailable."]
|
|
127
|
+
}
|
|
128
|
+
: {
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
agentId: agent.id,
|
|
131
|
+
adapterId: "claude",
|
|
132
|
+
models: [],
|
|
133
|
+
fields: [
|
|
134
|
+
...common,
|
|
135
|
+
field("permission.strategy", ["default", "bypass", "configured"].map(choice), false),
|
|
136
|
+
field("permission.mode", [
|
|
137
|
+
"acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"
|
|
138
|
+
].map(choice), true),
|
|
139
|
+
field("permission.allowedTools", [], true),
|
|
140
|
+
field("permission.disallowedTools", [], true),
|
|
141
|
+
field("settingsSources", ["user", "project", "local"].map(choice), false),
|
|
142
|
+
field("settingsFile", [], true),
|
|
143
|
+
field("additionalDirectories", [], true)
|
|
144
|
+
],
|
|
145
|
+
warnings: ["Runtime configuration catalog is unavailable."]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function configurationField(catalog, key) {
|
|
149
|
+
return catalog.fields.find((candidate) => candidate.key === key);
|
|
150
|
+
}
|
|
151
|
+
export function defaultModel(catalog) {
|
|
152
|
+
return catalog.models.find((model) => model.isDefault);
|
|
153
|
+
}
|
|
154
|
+
export function modelChoice(catalog, value) {
|
|
155
|
+
return value === undefined
|
|
156
|
+
? defaultModel(catalog)
|
|
157
|
+
: catalog.models.find((model) => model.value === value);
|
|
158
|
+
}
|
|
159
|
+
function field(key, choices, allowCustom) {
|
|
160
|
+
return { key, choices, allowCustom };
|
|
161
|
+
}
|
|
162
|
+
function catalogFingerprint(input, environment) {
|
|
163
|
+
const bindings = input.agent.environment.map((binding) => ({
|
|
164
|
+
target: binding.target,
|
|
165
|
+
sourceName: binding.sourceName,
|
|
166
|
+
value: environment[binding.sourceName] ?? null
|
|
167
|
+
}));
|
|
168
|
+
const nativeRoot = input.agent.adapterId === "codex"
|
|
169
|
+
? environment.CODEX_HOME ?? join(environment.HOME ?? homedir(), ".codex")
|
|
170
|
+
: environment.CLAUDE_CONFIG_DIR ?? join(environment.HOME ?? homedir(), ".claude");
|
|
171
|
+
const context = input.config?.adapterId === "codex"
|
|
172
|
+
? { profile: input.config.profile ?? null }
|
|
173
|
+
: input.config?.adapterId === "claude"
|
|
174
|
+
? {
|
|
175
|
+
settingsFile: input.config.settingsFile ?? null,
|
|
176
|
+
settingsSources: input.config.settingsSources ?? null
|
|
177
|
+
}
|
|
178
|
+
: null;
|
|
179
|
+
return createHash("sha256").update(JSON.stringify({
|
|
180
|
+
adapterId: input.agent.adapterId,
|
|
181
|
+
command: input.agent.command,
|
|
182
|
+
baseArgs: input.agent.baseArgs,
|
|
183
|
+
bindings,
|
|
184
|
+
nativeRoot,
|
|
185
|
+
cwd: input.cwd,
|
|
186
|
+
context
|
|
187
|
+
})).digest("hex");
|
|
188
|
+
}
|
|
189
|
+
function cachePath(yuiHome, agentId, fingerprint) {
|
|
190
|
+
return join(yuiHome, "cache", "agent-capabilities", "v1", agentId, `${fingerprint}.json`);
|
|
191
|
+
}
|
|
192
|
+
function readCachedCatalog(path, fingerprint, agent) {
|
|
193
|
+
let parsed;
|
|
194
|
+
try {
|
|
195
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
if (!record(parsed)
|
|
201
|
+
|| parsed.schemaVersion !== 1
|
|
202
|
+
|| parsed.fingerprint !== fingerprint
|
|
203
|
+
|| typeof parsed.fetchedAt !== "string") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return {
|
|
208
|
+
schemaVersion: 1,
|
|
209
|
+
fingerprint,
|
|
210
|
+
fetchedAt: parsed.fetchedAt,
|
|
211
|
+
catalog: validateCatalog(parsed.catalog, agent)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function validateCatalog(value, agent) {
|
|
219
|
+
if (!record(value)
|
|
220
|
+
|| value.schemaVersion !== 1
|
|
221
|
+
|| value.agentId !== agent.id
|
|
222
|
+
|| value.adapterId !== agent.adapterId
|
|
223
|
+
|| !Array.isArray(value.models)
|
|
224
|
+
|| value.models.length === 0
|
|
225
|
+
|| !Array.isArray(value.fields)
|
|
226
|
+
|| !Array.isArray(value.warnings)) {
|
|
227
|
+
throw new Error("Agent configuration model catalog is incomplete.");
|
|
228
|
+
}
|
|
229
|
+
const models = value.models.map(validateModel);
|
|
230
|
+
unique(models.map(({ value: model }) => model), "model");
|
|
231
|
+
const fields = value.fields.map(validateField);
|
|
232
|
+
unique(fields.map(({ key }) => key), "configuration field");
|
|
233
|
+
const warnings = value.warnings.map((warning) => text(warning, "catalog warning"));
|
|
234
|
+
return {
|
|
235
|
+
schemaVersion: 1,
|
|
236
|
+
agentId: agent.id,
|
|
237
|
+
adapterId: agent.adapterId,
|
|
238
|
+
...(typeof value.cliVersion === "string"
|
|
239
|
+
? { cliVersion: text(value.cliVersion, "CLI version") } : {}),
|
|
240
|
+
models,
|
|
241
|
+
fields,
|
|
242
|
+
warnings
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function validateModel(value) {
|
|
246
|
+
if (!record(value) || typeof value.isDefault !== "boolean" || !Array.isArray(value.efforts)) {
|
|
247
|
+
throw new Error("Agent configuration model entry is invalid.");
|
|
248
|
+
}
|
|
249
|
+
const efforts = value.efforts.map(validateChoice);
|
|
250
|
+
unique(efforts.map(({ value: effort }) => effort), "effort");
|
|
251
|
+
const serviceTiers = value.serviceTiers === undefined
|
|
252
|
+
? undefined
|
|
253
|
+
: array(value.serviceTiers, "service tiers").map(validateChoice);
|
|
254
|
+
if (serviceTiers !== undefined) {
|
|
255
|
+
unique(serviceTiers.map(({ value: tier }) => tier), "service tier");
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
value: text(value.value, "model value"),
|
|
259
|
+
label: text(value.label, "model label"),
|
|
260
|
+
...(typeof value.description === "string"
|
|
261
|
+
? { description: text(value.description, "model description") } : {}),
|
|
262
|
+
...(typeof value.resolvedModel === "string"
|
|
263
|
+
? { resolvedModel: text(value.resolvedModel, "resolved model") } : {}),
|
|
264
|
+
isDefault: value.isDefault,
|
|
265
|
+
...(typeof value.defaultEffort === "string"
|
|
266
|
+
? { defaultEffort: text(value.defaultEffort, "default effort") } : {}),
|
|
267
|
+
efforts,
|
|
268
|
+
...(serviceTiers === undefined ? {} : { serviceTiers }),
|
|
269
|
+
...(typeof value.defaultServiceTier === "string"
|
|
270
|
+
? { defaultServiceTier: text(value.defaultServiceTier, "default service tier") } : {})
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function validateField(value) {
|
|
274
|
+
if (!record(value) || !Array.isArray(value.choices) || typeof value.allowCustom !== "boolean") {
|
|
275
|
+
throw new Error("Agent configuration field entry is invalid.");
|
|
276
|
+
}
|
|
277
|
+
const choices = value.choices.map(validateChoice);
|
|
278
|
+
unique(choices.map(({ value: choice }) => choice), "field choice");
|
|
279
|
+
return {
|
|
280
|
+
key: text(value.key, "field key"),
|
|
281
|
+
choices,
|
|
282
|
+
allowCustom: value.allowCustom,
|
|
283
|
+
...(typeof value.available === "boolean" ? { available: value.available } : {}),
|
|
284
|
+
...(typeof value.reason === "string"
|
|
285
|
+
? { reason: text(value.reason, "field reason") } : {})
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function validateChoice(value) {
|
|
289
|
+
if (!record(value))
|
|
290
|
+
throw new Error("Agent configuration choice is invalid.");
|
|
291
|
+
return {
|
|
292
|
+
value: text(value.value, "choice value"),
|
|
293
|
+
label: text(value.label, "choice label"),
|
|
294
|
+
...(typeof value.description === "string"
|
|
295
|
+
? { description: text(value.description, "choice description") } : {})
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function catalogFailure(error) {
|
|
299
|
+
const candidate = error instanceof Error ? error : new Error(String(error));
|
|
300
|
+
const code = "code" in candidate ? String(candidate.code) : "";
|
|
301
|
+
return {
|
|
302
|
+
code: code === "ETIMEDOUT" || candidate.name === "AbortError"
|
|
303
|
+
? "timeout"
|
|
304
|
+
: code === "ENOENT" ? "missing-command" : "probe-failed",
|
|
305
|
+
message: candidate.message || "Agent capability discovery failed."
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function record(value) {
|
|
309
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
310
|
+
}
|
|
311
|
+
function array(value, label) {
|
|
312
|
+
if (!Array.isArray(value))
|
|
313
|
+
throw new Error(`Agent configuration ${label} are invalid.`);
|
|
314
|
+
return value;
|
|
315
|
+
}
|
|
316
|
+
function text(value, label) {
|
|
317
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.includes("\0")) {
|
|
318
|
+
throw new Error(`Agent configuration ${label} is invalid.`);
|
|
319
|
+
}
|
|
320
|
+
return value.trim();
|
|
321
|
+
}
|
|
322
|
+
function unique(values, label) {
|
|
323
|
+
if (new Set(values).size !== values.length) {
|
|
324
|
+
throw new Error(`Agent configuration ${label} entries contain duplicates.`);
|
|
325
|
+
}
|
|
326
|
+
}
|