@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
@@ -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
+ }