@matamt-dbg/dsh-studio 0.0.0-stage → 0.2.0

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/index.js ADDED
@@ -0,0 +1,1195 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
5
+ var __typeError = (msg) => {
6
+ throw TypeError(msg);
7
+ };
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
11
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
12
+ var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
13
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
14
+ var __decoratorMetadata = (array2, target) => __defNormalProp(target, __knownSymbol("metadata"), array2[3]);
15
+ var __runInitializers = (array2, flags, self, value) => {
16
+ for (var i = 0, fns = array2[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
17
+ return value;
18
+ };
19
+ var __decorateElement = (array2, flags, name, decorators, target, extra) => {
20
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
21
+ var j = k > 3 ? array2.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
22
+ var initializers = k > 3 && (array2[j - 1] = []), extraInitializers = array2[j] || (array2[j] = []);
23
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
24
+ return __privateGet(this, extra);
25
+ }, set [name](x) {
26
+ return __privateSet(this, extra, x);
27
+ } }, name));
28
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
29
+ for (var i = decorators.length - 1; i >= 0; i--) {
30
+ ctx = __decoratorContext(k, name, done = {}, array2[3], extraInitializers);
31
+ if (k) {
32
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
33
+ if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
34
+ if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
35
+ }
36
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
37
+ if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
38
+ else if (typeof it !== "object" || it === null) __typeError("Object expected");
39
+ else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
40
+ }
41
+ return k || __decoratorMetadata(array2, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
42
+ };
43
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
44
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
45
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
46
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
47
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
48
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
49
+
50
+ // src/package-name.ts
51
+ var PACKAGE_NAME = "@matamt-dbg/dsh-studio";
52
+ var LEGACY_PACKAGE_NAME = "@local/dsh-studio-shell";
53
+ var POLICY_PLUGIN = `${PACKAGE_NAME}/policy`;
54
+ var ROLE_PLUGIN = `${PACKAGE_NAME}/roles`;
55
+ var BATCH_PLUGIN = `${PACKAGE_NAME}/batch`;
56
+ function currentPluginName(name) {
57
+ return name === LEGACY_PACKAGE_NAME || name.startsWith(`${LEGACY_PACKAGE_NAME}/`) ? PACKAGE_NAME + name.slice(LEGACY_PACKAGE_NAME.length) : name;
58
+ }
59
+
60
+ // src/delegation.ts
61
+ var ROLE_TOOL = "subagent_role";
62
+ var LEGACY_ROLE_TOOL = "studio_role";
63
+ var GENERIC_DELEGATION_TOOLS = ["subagent", "subagent_fork", "list_subagent_models"];
64
+ var SUBAGENT_PLUGIN = "@deepseek-ai/dsh-tool-subagent";
65
+ var WITHDRAWN_TOOLS = ["task_run"];
66
+ function delegationTools(tools, hasRoles = false) {
67
+ const delegates = hasRoles || tools.some((name) => name === ROLE_TOOL || name === LEGACY_ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name));
68
+ const rest = tools.filter((name) => name !== ROLE_TOOL && name !== LEGACY_ROLE_TOOL && !GENERIC_DELEGATION_TOOLS.includes(name) && !WITHDRAWN_TOOLS.includes(name));
69
+ return delegates ? [...rest, ROLE_TOOL] : rest;
70
+ }
71
+ function withoutDelegationPlugins(rows) {
72
+ return rows.filter((row) => row.name !== SUBAGENT_PLUGIN && row.name !== ROLE_PLUGIN).map((row) => row.group && Array.isArray(row.config) ? { ...row, config: withoutDelegationPlugins(row.config) } : row);
73
+ }
74
+ function renameLegacyRoleKey(entries) {
75
+ if (!entries || !Object.hasOwn(entries, LEGACY_ROLE_TOOL)) return entries;
76
+ const { [LEGACY_ROLE_TOOL]: legacy, ...rest } = entries;
77
+ return Object.hasOwn(rest, ROLE_TOOL) ? rest : { ...rest, [ROLE_TOOL]: legacy };
78
+ }
79
+ function migratePolicyConfig(config) {
80
+ const tools = Array.isArray(config.tools) ? config.tools : [];
81
+ if (!tools.includes(LEGACY_ROLE_TOOL)) return config;
82
+ const execution = config.execution;
83
+ return {
84
+ ...config,
85
+ tools: [...new Set(tools.map((name) => name === LEGACY_ROLE_TOOL ? ROLE_TOOL : name))],
86
+ ...config.toolOverrides ? { toolOverrides: renameLegacyRoleKey(config.toolOverrides) } : {},
87
+ ...execution?.defaults ? { execution: { ...execution, defaults: renameLegacyRoleKey(execution.defaults) } } : {}
88
+ };
89
+ }
90
+
91
+ // src/optimized-preset.ts
92
+ var OPTIMIZED_PRESET_ID = "studio-optimized";
93
+ var BUILTIN_TOOL_PRESETS = ["standard", "ptc", "minimal", "cordis"];
94
+ var excluded = /* @__PURE__ */ new Set(["get_goal", "create_goal", "update_goal", "todo_write", "task_run", "cordis_inspect_list", "cordis_inspect_query", "plugin_manager"]);
95
+ var OPTIMIZED_PROMPT = `Use glob to discover paths, grep to search contents, and read to inspect files before editing. Prefer targeted edits.
96
+ Batch independent read-only inspections with read_batch. Start independent tool calls together; keep calls that depend on earlier results sequential. Do not guess unknown arguments.
97
+ Wait for required work to finish before validating it. Use background execution only for independent work or intentionally long-lived services. Do not duplicate running work or busy-poll it.
98
+ Track every job you start. Before reporting completion, collect all relevant results and cancel work that no longer matters. Report failures and unverified behavior honestly.
99
+ Delegate self-contained independent work when useful and collect the result before relying on it.
100
+ Keep changes within the requested scope and preserve user work. Use the smallest sufficient checks for the requested outcome and risk, then stop when the work is complete.`;
101
+ function optimizedTemplate(tools) {
102
+ const selected = delegationTools(tools.filter((name) => !excluded.has(name)));
103
+ const request = {
104
+ preset: "studio-master",
105
+ providers: ["@deepseek-ai/dsh-persona"],
106
+ tools: selected,
107
+ prompt: OPTIMIZED_PROMPT,
108
+ model: "testLLM",
109
+ configOverrides: { "spill-policy": { maxInlineTokens: 2500 } },
110
+ toolOverrides: {
111
+ ...selected.includes("bash") ? { bash: { parameters: {
112
+ timeoutMs: "Foreground wait in milliseconds; defaults to a 600000ms request, subject to the runtime cap. When the next step needs the result, request sufficient waiting initially rather than a short wait followed immediately by polling.",
113
+ run_in_background: "Run in the background immediately only for concrete independent work or intentionally long-lived services. Collect relevant results before relying on success."
114
+ } } } : {},
115
+ ...selected.includes("job_output") ? { job_output: { parameters: {
116
+ wait: "Block until completion or the wait expires. Use true when the result is required and no independent work remains; avoid repeated non-blocking checks. Defaults to false.",
117
+ timeout_ms: "Maximum wait in milliseconds when wait is true. Defaults to a 120000ms request, subject to the runtime cap. Timeout leaves the job running."
118
+ } } } : {}
119
+ },
120
+ execution: { defaults: { ...selected.includes("bash") ? { bash: { timeoutMs: 6e5 } } : {}, ...selected.includes("job_output") ? { job_output: { timeout_ms: 12e4 } } : {} } }
121
+ };
122
+ const missing = ["read_batch"].filter((name) => !selected.includes(name));
123
+ return {
124
+ id: OPTIMIZED_PRESET_ID,
125
+ name: "Optimized",
126
+ description: "Studio optimized default preset",
127
+ nonDeletable: true,
128
+ template: true,
129
+ status: "applied",
130
+ agents: [],
131
+ request,
132
+ ...missing.length ? { applyWarning: `Optional tool providers not installed: ${missing.join(", ")}. The preset does not install or emulate these tools.` } : {}
133
+ };
134
+ }
135
+
136
+ // src/host/shell.ts
137
+ import { createConfigProjector, isNativeConfigSchema, LOADER_EXPRESSION_SCHEMA } from "@deepseek-ai/dsh-app-boot";
138
+
139
+ // src/config-overrides.ts
140
+ var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
141
+ function validateConfigOverrides(value) {
142
+ if (!object(value)) throw new Error("Config overrides must be an object");
143
+ const visit = (v, depth = 0) => {
144
+ if (depth > 32) throw new Error("Config nesting exceeds 32");
145
+ if (typeof v === "number" && !Number.isFinite(v)) throw new Error("Invalid Config number");
146
+ if (v !== null && !["object", "string", "number", "boolean"].includes(typeof v)) throw new Error("Config must be JSON");
147
+ if (Array.isArray(v)) v.forEach((child) => visit(child, depth + 1));
148
+ else if (object(v)) for (const [key, child] of Object.entries(v)) {
149
+ if (["__proto__", "prototype", "constructor"].includes(key)) throw new Error("Unsafe Config key");
150
+ visit(child, depth + 1);
151
+ }
152
+ };
153
+ for (const patch of Object.values(value)) {
154
+ if (!object(patch)) throw new Error("Plugin override must be an object");
155
+ visit(patch);
156
+ }
157
+ }
158
+ function mergeConfig(base, patch) {
159
+ const next = { ...object(base) ? base : {} };
160
+ for (const [key, value] of Object.entries(patch)) next[key] = object(value) ? mergeConfig(next[key], value) : structuredClone(value);
161
+ return next;
162
+ }
163
+ function configEntries(rows, parent = "") {
164
+ return rows.flatMap((row) => {
165
+ if (!row.id) throw new Error("Master entries require stable ids");
166
+ const id = parent ? `${parent}/${row.id}` : row.id;
167
+ return row.group && Array.isArray(row.config) ? configEntries(row.config, id) : [{ id, row }];
168
+ });
169
+ }
170
+ function applyConfigOverrides(rows, overrides) {
171
+ validateConfigOverrides(overrides);
172
+ if (!Object.keys(overrides).length) return structuredClone(rows);
173
+ const entries = new Map(configEntries(rows).map((entry) => [entry.id, entry.row]));
174
+ for (const id of Object.keys(overrides)) if (!entries.has(id)) throw new Error(`Unknown master Config entry: ${id}`);
175
+ const visit = (items, parent = "") => items.map((row) => {
176
+ const id = parent ? `${parent}/${row.id}` : row.id;
177
+ return { ...row, ...row.group && Array.isArray(row.config) ? { config: visit(row.config, id) } : overrides[id] ? { config: mergeConfig(row.config, overrides[id]) } : {} };
178
+ });
179
+ return visit(rows);
180
+ }
181
+
182
+ // src/host/master-composition.ts
183
+ var plugin = (id, config, suffix = id) => ({ id, name: `@deepseek-ai/dsh-${suffix}`, ...config ? { config } : {} });
184
+ function masterComposition() {
185
+ return [
186
+ plugin("persona", { prefix: "You are a coding agent powered by the {{model}} model." }),
187
+ plugin("agent-instructions", { maxBytes: 65536 }),
188
+ plugin("time-context"),
189
+ plugin(process.platform === "win32" ? "tool-pwsh" : "tool-bash"),
190
+ plugin("spill-policy"),
191
+ { id: "studio-batch", name: BATCH_PLUGIN },
192
+ // task_run (the /tasks entry) is withheld from the Master for now; the provider stays packaged.
193
+ plugin("tool-fs"),
194
+ plugin("tool-fs-search", { sampleOverCapGlobResults: false }),
195
+ plugin("tool-jobs"),
196
+ plugin("skill-filesystem"),
197
+ plugin("tool-skill"),
198
+ plugin("command-goal"),
199
+ plugin("tool-goal"),
200
+ { id: "planning", name: "cordis:group", group: true, isolate: { planMode: true }, config: [plugin("plan-mode", { section: "You are in plan mode. Explore with read-only operations and present a complete plan using exit_plan_mode. Do not modify files, execute mutations, or leave plan mode until the user approves the plan or switches modes." })] },
201
+ { id: "compaction", name: "cordis:group", group: true, isolate: { compaction: true, toolResultPruner: true }, config: [plugin("compaction-basic"), plugin("command-compact"), plugin("tool-result-pruner", { thresholdChars: 8192, headChars: 4096, tailChars: 1024 }, "compaction-tool-result-pruner")] },
202
+ { id: "delegation", name: "cordis:group", group: true, isolate: { workflowEngine: true }, config: [
203
+ plugin("tool-subagent-control"),
204
+ plugin("tool-subagent-list-agents", void 0, "tool-subagent-control/list-agents"),
205
+ plugin("tool-subagent", { provider: "spawn", toolName: "subagent", modelSelectionSettings: true, toolFilter: { deny: ["schedule_create", "schedule_delete", "schedule_list", "schedule_update"] } }),
206
+ plugin("tool-subagent-fork", { provider: "fork", toolName: "subagent_fork", toolFilter: { deny: ["schedule_create", "schedule_delete", "schedule_list", "schedule_update"] } }, "tool-subagent"),
207
+ plugin("workflow-ptc", { provider: "spawn" }),
208
+ plugin("tool-workflow"),
209
+ // Catalog source for subagent_role; Studio presets replace the generic subagent tools above with it.
210
+ { id: "studio-master-roles", name: ROLE_PLUGIN, config: { roles: [] } }
211
+ ] },
212
+ plugin("tool-ask-user"),
213
+ plugin("tool-todo", { allowParallelInProgress: true }),
214
+ plugin("tool-web", { fetch: true, searchTimeoutMs: 6e4 }),
215
+ plugin("tool-present"),
216
+ plugin("tool-cordis"),
217
+ plugin("tool-plugin-manager", void 0, "plugin-manager/tools")
218
+ ];
219
+ }
220
+
221
+ // src/host/master-extensions.ts
222
+ import { randomUUID } from "node:crypto";
223
+ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
224
+ import { dirname, resolve } from "node:path";
225
+ var poison = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
226
+ var rowKeys = /* @__PURE__ */ new Set(["id", "name", "group", "config", "disabled", "isolate"]);
227
+ var idPattern = /^[a-z][a-z0-9-]{0,63}$/;
228
+ var packagePattern = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)*$/;
229
+ function record(value) {
230
+ return value !== null && typeof value === "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
231
+ }
232
+ function keys(value) {
233
+ const result = [];
234
+ for (const key of Reflect.ownKeys(value)) {
235
+ if (typeof key !== "string" || poison.has(key)) throw new Error("Unsafe extension key");
236
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
237
+ if (!("value" in descriptor) || !descriptor.enumerable && !(Array.isArray(value) && key === "length")) {
238
+ throw new Error("Extensions must contain JSON data only");
239
+ }
240
+ result.push(key);
241
+ }
242
+ return result;
243
+ }
244
+ function array(value) {
245
+ if (!Array.isArray(value)) throw new Error("Expected an extension array");
246
+ const own3 = keys(value);
247
+ if (own3.length !== value.length + 1 || own3.some((key) => key !== "length" && !/^(0|[1-9][0-9]*)$/.test(key))) {
248
+ throw new Error("Extensions must contain dense JSON arrays");
249
+ }
250
+ }
251
+ function json(value, depth = 0, ancestors = /* @__PURE__ */ new Set()) {
252
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
253
+ if (typeof value === "number" && Number.isFinite(value)) return value;
254
+ if (typeof value !== "object" || value === null || !Array.isArray(value) && !record(value)) {
255
+ throw new Error("Extensions must contain JSON data only");
256
+ }
257
+ if (depth >= 8 || ancestors.has(value)) throw new Error("Extension config exceeds depth 8 or is cyclic");
258
+ ancestors.add(value);
259
+ try {
260
+ if (Array.isArray(value)) {
261
+ array(value);
262
+ return value.map((item) => json(item, depth + 1, ancestors));
263
+ }
264
+ return Object.fromEntries(keys(value).map((key) => [key, json(value[key], depth + 1, ancestors)]));
265
+ } finally {
266
+ ancestors.delete(value);
267
+ }
268
+ }
269
+ function validateExtensions(value) {
270
+ let count = 0;
271
+ function visit(input, depth) {
272
+ array(input);
273
+ if (depth > 8) throw new Error("Extensions exceed depth 8");
274
+ const ids = /* @__PURE__ */ new Set();
275
+ return input.map((value2) => {
276
+ if (++count > 64) throw new Error("Extensions exceed 64 entries");
277
+ if (!record(value2)) throw new Error("Invalid extension row");
278
+ const own3 = keys(value2);
279
+ if (own3.some((key) => !rowKeys.has(key))) throw new Error("Unsupported extension row key");
280
+ if (typeof value2.id !== "string" || !idPattern.test(value2.id)) throw new Error("Invalid extension id");
281
+ if (ids.has(value2.id)) throw new Error("Duplicate sibling extension id");
282
+ ids.add(value2.id);
283
+ if (typeof value2.name !== "string" || value2.name !== "cordis:group" && !packagePattern.test(value2.name)) {
284
+ throw new Error("Invalid extension package name");
285
+ }
286
+ if (own3.includes("group") && typeof value2.group !== "boolean") throw new Error("Invalid extension group");
287
+ if (value2.name === "cordis:group" !== (value2.group === true)) throw new Error("cordis:group requires group: true");
288
+ if (own3.includes("disabled") && typeof value2.disabled !== "boolean") throw new Error("Invalid extension disabled flag");
289
+ if (own3.includes("isolate")) {
290
+ const isolate = value2.isolate;
291
+ if (!record(isolate) || keys(isolate).some((key) => typeof isolate[key] !== "boolean")) {
292
+ throw new Error("Extension isolate must map service names to booleans");
293
+ }
294
+ }
295
+ const row = { id: value2.id, name: value2.name };
296
+ if (own3.includes("group")) row.group = value2.group;
297
+ if (own3.includes("disabled")) row.disabled = value2.disabled;
298
+ if (own3.includes("isolate")) row.isolate = json(value2.isolate);
299
+ if (value2.group === true) row.config = visit(value2.config, depth + 1);
300
+ else if (own3.includes("config")) row.config = json(value2.config);
301
+ return row;
302
+ });
303
+ }
304
+ return visit(value, 1);
305
+ }
306
+ var MasterExtensions = class {
307
+ file;
308
+ tail = Promise.resolve();
309
+ constructor(file) {
310
+ this.file = resolve(file);
311
+ }
312
+ async read() {
313
+ await this.tail;
314
+ let content;
315
+ try {
316
+ content = await readFile(this.file, "utf8");
317
+ } catch (error) {
318
+ if (error.code === "ENOENT") return [];
319
+ throw error;
320
+ }
321
+ const value = JSON.parse(content);
322
+ if (!record(value) || keys(value).some((key) => !["version", "extensions"].includes(key)) || value.version !== 1) {
323
+ throw new Error("Unsupported Studio master extensions store");
324
+ }
325
+ return validateExtensions(value.extensions);
326
+ }
327
+ async save(rows) {
328
+ const content = JSON.stringify({ version: 1, extensions: validateExtensions(rows) }, null, 2);
329
+ const operation = this.tail.then(async () => {
330
+ const directory = dirname(this.file);
331
+ const temp = resolve(`${this.file}.${randomUUID()}.tmp`);
332
+ const verify = () => {
333
+ if (resolve(this.file) !== this.file || dirname(temp) !== directory || temp === this.file) {
334
+ throw new Error("Invalid master extensions persistence paths");
335
+ }
336
+ };
337
+ verify();
338
+ await mkdir(directory, { recursive: true, mode: 448 });
339
+ let created = false;
340
+ try {
341
+ await writeFile(temp, content, { mode: 384, flag: "wx" });
342
+ created = true;
343
+ verify();
344
+ await rename(temp, this.file);
345
+ } finally {
346
+ if (created) {
347
+ verify();
348
+ await unlink(temp).catch((error) => {
349
+ if (error.code !== "ENOENT") throw error;
350
+ });
351
+ }
352
+ }
353
+ });
354
+ this.tail = operation.catch(() => {
355
+ });
356
+ return operation;
357
+ }
358
+ };
359
+
360
+ // src/host/shell.ts
361
+ import { dirname as dirname3, join as join2 } from "node:path";
362
+ import { randomUUID as randomUUID4 } from "node:crypto";
363
+ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
364
+
365
+ // src/tool-prompt-sections.ts
366
+ var toolPromptSections = {
367
+ bash: ["tool:bash"],
368
+ pwsh: ["tool:pwsh"],
369
+ read: ["tool:read"],
370
+ write: ["tool:write"],
371
+ edit: ["tool:edit"],
372
+ glob: ["tool:glob"],
373
+ grep: ["tool:grep"],
374
+ job_list: ["tool:jobs"],
375
+ job_output: ["tool:jobs"],
376
+ job_kill: ["tool:jobs"],
377
+ web_fetch: ["tool:web_fetch"],
378
+ web_search: ["tool:web_search"],
379
+ get_goal: ["tool:goal"],
380
+ create_goal: ["tool:goal"],
381
+ update_goal: ["tool:goal"]
382
+ };
383
+
384
+ // src/preset-catalog.ts
385
+ async function readPresetCatalog(services) {
386
+ const all = new Map(services.tools.schemas().map((tool) => [tool.name, { name: tool.name, description: tool.description, ...tool.parameters ? { parameters: tool.parameters } : {} }]));
387
+ const presets = [];
388
+ for (const preset of await services.agentPresets.list()) {
389
+ let lease;
390
+ try {
391
+ lease = await services.agentPresets.acquireScope(preset.id);
392
+ const assembly = await services.systemPrompt.assemble({ scope: lease.key });
393
+ for (const tool of [...services.tools.schemas(lease.key), ...assembly.tools]) all.set(tool.name, { name: tool.name, description: tool.description, ...tool.parameters ? { parameters: tool.parameters } : {} });
394
+ const declaration = (await services.agentPresets.readDocument(preset.id)).content;
395
+ const tools = new Map([...services.tools.schemas(lease.key), ...assembly.tools].map(({ name, description, parameters }) => [name, { name, description, ...parameters ? { parameters } : {} }]));
396
+ presets.push({ id: preset.id, name: preset.name, declaration, sections: assembly.sections.map(({ name, text }) => ({ name, text })), tools: [...tools.values()] });
397
+ } catch (error) {
398
+ presets.push({ id: preset.id, name: preset.name, sections: [], tools: [], error: String(error) });
399
+ } finally {
400
+ await lease?.[Symbol.asyncDispose]();
401
+ }
402
+ }
403
+ const sections = new Map(presets.flatMap((preset) => preset.sections).map((section) => [section.name, section]));
404
+ return { presets, tools: [...all.values()].map((tool) => ({ ...tool, promptSections: (toolPromptSections[tool.name] ?? []).flatMap((name) => sections.has(name) ? [sections.get(name)] : []) })).sort((a, b) => a.name.localeCompare(b.name)) };
405
+ }
406
+
407
+ // src/host/prompt-preview.ts
408
+ import { createScope } from "@deepseek-ai/dsh-scope";
409
+ import { renderPrompt, renderContextSnapshot } from "@deepseek-ai/dsh-system-prompt";
410
+ import { randomUUID as randomUUID2 } from "node:crypto";
411
+
412
+ // src/master-preset.ts
413
+ var MASTER_PRESET_ID = "studio-master";
414
+ var MASTER_PRESET_NAME = "Studio Master";
415
+
416
+ // src/provider-fields.ts
417
+ var field = (key, type, value, minimum) => ({ key, type, ...value === void 0 ? {} : { value }, ...minimum === void 0 ? {} : { minimum } });
418
+ var providerFields = {
419
+ "@deepseek-ai/dsh-compaction-basic": [field("thresholdRatio", "number", 0.8, 0), field("headroomTokens", "number", 65536, 1), field("retainRatio", "number", 0.16, 0), field("retainTokens", "number", void 0, 0), field("summarizationProvider", "string", ""), field("summarizationModel", "string", ""), field("maxTokens", "number", void 0, 1), field("compactionRetries", "number", 1, 0), field("maxOverflowRetries", "number", 1, 0), field("auto", "boolean", true)],
420
+ "@deepseek-ai/dsh-compaction-tool-result-pruner": [field("thresholdChars", "number", 8192, 1), field("headChars", "number", 4096, 0), field("tailChars", "number", 1024, 0)],
421
+ "@deepseek-ai/dsh-agent-tool-presentation": [{ key: "mode", type: "string", options: ["native", "ptc"] }],
422
+ "@deepseek-ai/dsh-persona": [field("prefix", "string"), field("suffix", "string", ""), field("includeRuntimeContext", "boolean", true)],
423
+ "@deepseek-ai/dsh-agent-instructions": [field("maxBytes", "number", 65536), field("maxSourceBytes", "number", void 0, 1), field("projectRootMarkers", "string[]"), field("instructionFileCandidates", "string[]"), field("localInstructionFileCandidates", "string[]")],
424
+ "@deepseek-ai/dsh-time-context": [field("timeZone", "string"), field("refreshIntervalMs", "number", void 0, 0)],
425
+ "@deepseek-ai/dsh-skill-filesystem": [field("providerName", "string", "filesystem"), field("includeDefaultRoots", "boolean", true), field("dshHome", "string"), field("agentsHome", "string"), field("customSkillDirs", "string[]", []), field("bundledSkillDir", "string"), field("watch", "boolean", true), field("watchUsePolling", "boolean", false), field("watchStabilityThresholdMs", "number", 200, 0), field("watchPollIntervalMs", "number", 100, 1), field("watchMaxProjects", "number", 128, 1), field("watchFollowSymlinks", "boolean", true)]
426
+ };
427
+
428
+ // src/host/composition-plan.ts
429
+ import { load } from "js-yaml";
430
+ import { entryListSchema } from "@deepseek-ai/cordis-plugin-include";
431
+
432
+ // src/execution-defaults.ts
433
+ function batchLimitFields(tool, parameters) {
434
+ if (tool !== "read_batch" || parameters.type !== "object" || !record2(parameters.properties)) return [];
435
+ return Object.entries(parameters.properties).flatMap(([name, schema]) => record2(schema) && schema.type === "array" && Number.isSafeInteger(schema.maxItems) && schema.maxItems > 0 && (schema.minItems === void 0 || Number.isSafeInteger(schema.minItems) && schema.minItems >= 0) ? [name] : []);
436
+ }
437
+ function validateBatchLimit(parameters, batch) {
438
+ if (!batchLimitFields(batch.tool, parameters).includes(batch.parameter)) throw new Error("Unsupported batch limit");
439
+ const schema = parameters.properties[batch.parameter];
440
+ if (!Number.isSafeInteger(batch.maxItems) || batch.maxItems < 1 || batch.maxItems > schema.maxItems || batch.maxItems < (typeof schema.minItems === "number" ? schema.minItems : 0)) throw new Error("Batch limit must tighten the published bounds");
441
+ }
442
+ var record2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
443
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
444
+ var names = /* @__PURE__ */ new Set(["timeoutMs", "timeout_ms"]);
445
+ var annotations = ["description", "title", "default", "examples"];
446
+ var bounds = ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"];
447
+ function executionDefaultFields(parameters) {
448
+ if (parameters.type !== "object" || !record2(parameters.properties) || Object.keys(parameters).some((key) => !["type", "properties", "required", "additionalProperties", ...annotations].includes(key)) || parameters.required !== void 0 && (!Array.isArray(parameters.required) || parameters.required.some((key) => typeof key !== "string"))) return [];
449
+ return Object.entries(parameters.properties).flatMap(([name, schema]) => {
450
+ if (!names.has(name) || parameters.required?.includes(name) || !record2(schema) || !["number", "integer"].includes(String(schema.type)) || Object.keys(schema).some((key) => !["type", ...annotations, ...bounds, "enum", "const"].includes(key)) || bounds.some((key) => own(schema, key) && (typeof schema[key] !== "number" || !Number.isFinite(schema[key]) || key === "multipleOf" && schema[key] <= 0)) || own(schema, "const") && (typeof schema.const !== "number" || !Number.isFinite(schema.const)) || own(schema, "enum") && (!Array.isArray(schema.enum) || !schema.enum.length || schema.enum.some((value) => typeof value !== "number" || !Number.isFinite(value)))) return [];
451
+ return [name];
452
+ });
453
+ }
454
+ function validateExecutionDefaults(value) {
455
+ if (!record2(value) || Object.keys(value).some((key) => !["defaults", "batch"].includes(key)) || !record2(value.defaults)) throw new Error("Invalid execution defaults");
456
+ if (value.batch !== void 0 && (!record2(value.batch) || Object.keys(value.batch).some((key) => !["tool", "parameter", "maxItems"].includes(key)) || value.batch.tool !== "read_batch" || typeof value.batch.parameter !== "string" || !value.batch.parameter || ["__proto__", "prototype", "constructor"].includes(value.batch.parameter) || !Number.isSafeInteger(value.batch.maxItems) || value.batch.maxItems < 1)) throw new Error("Invalid batch limit");
457
+ for (const [tool, fields] of Object.entries(value.defaults)) {
458
+ if (!tool || ["__proto__", "prototype", "constructor"].includes(tool) || !record2(fields)) throw new Error("Invalid execution defaults");
459
+ for (const [field2, number] of Object.entries(fields)) if (!names.has(field2) || typeof number !== "number" || !Number.isFinite(number) || number <= 0) throw new Error(`Invalid execution default: ${tool}.${field2}`);
460
+ }
461
+ }
462
+ function validateExecutionDefault(parameters, field2, value) {
463
+ if (!executionDefaultFields(parameters).includes(field2)) throw new Error(`Unsupported execution default: ${field2}`);
464
+ const schema = parameters.properties[field2];
465
+ if (!Number.isFinite(value) || value <= 0 || schema.type === "integer" && !Number.isInteger(value) || typeof schema.minimum === "number" && value < schema.minimum || typeof schema.maximum === "number" && value > schema.maximum || typeof schema.exclusiveMinimum === "number" && value <= schema.exclusiveMinimum || typeof schema.exclusiveMaximum === "number" && value >= schema.exclusiveMaximum || typeof schema.multipleOf === "number" && !Number.isInteger(value / schema.multipleOf) || Array.isArray(schema.enum) && !schema.enum.includes(value) || own(schema, "const") && value !== schema.const) throw new Error(`Execution default outside schema: ${field2}`);
466
+ }
467
+ function applyExecutionDefaults(args, defaults) {
468
+ if (!record2(args)) return args;
469
+ return { ...Object.fromEntries(Object.entries(defaults).filter(([field2]) => !own(args, field2))), ...args };
470
+ }
471
+
472
+ // src/prompt-composition.ts
473
+ var promptProviderModules = [
474
+ "@deepseek-ai/dsh-persona",
475
+ "@deepseek-ai/dsh-agent-instructions",
476
+ "@deepseek-ai/dsh-time-context",
477
+ "@deepseek-ai/dsh-skill-filesystem",
478
+ "@deepseek-ai/dsh-compaction-basic",
479
+ "@deepseek-ai/dsh-compaction-tool-result-pruner",
480
+ "@deepseek-ai/dsh-agent-tool-presentation"
481
+ ];
482
+ function parsePreviewRequest(value) {
483
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid preview request");
484
+ const row = value;
485
+ const strings = (input) => Array.isArray(input) && input.every((item) => typeof item === "string");
486
+ if (typeof row.preset !== "string" || !strings(row.providers) || !strings(row.tools) || typeof row.prompt !== "string" || typeof row.model !== "string") throw new Error("Invalid preview request");
487
+ const record4 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
488
+ if (row.persona !== void 0 && typeof row.persona !== "string") throw new Error("Invalid persona");
489
+ if (row.sections !== void 0 && (!record4(row.sections) || Object.values(row.sections).some((v) => typeof v !== "string"))) throw new Error("Invalid section overrides");
490
+ if (row.toolOverrides !== void 0 && (!record4(row.toolOverrides) || Object.values(row.toolOverrides).some((v) => !record4(v) || Object.keys(v).some((k) => !["description", "parameters"].includes(k)) || v.description !== void 0 && typeof v.description !== "string" || v.parameters !== void 0 && (!record4(v.parameters) || Object.values(v.parameters).some((p) => typeof p !== "string"))))) throw new Error("Invalid tool overrides");
491
+ if (row.providerConfigs !== void 0 && (!record4(row.providerConfigs) || Object.values(row.providerConfigs).some((v) => !record4(v)))) throw new Error("Invalid provider configs");
492
+ if (row.configOverrides !== void 0) validateConfigOverrides(row.configOverrides);
493
+ if (row.execution !== void 0) validateExecutionDefaults(row.execution);
494
+ return {
495
+ ...row.configOverrides !== void 0 ? { configOverrides: row.configOverrides } : {},
496
+ ...row.execution !== void 0 ? { execution: row.execution } : {},
497
+ preset: row.preset,
498
+ providers: [...row.providers],
499
+ tools: [...row.tools],
500
+ prompt: row.prompt,
501
+ model: row.model,
502
+ ...row.persona !== void 0 ? { persona: row.persona } : {},
503
+ ...row.sections !== void 0 ? { sections: row.sections } : {},
504
+ ...row.toolOverrides !== void 0 ? { toolOverrides: row.toolOverrides } : {},
505
+ ...row.providerConfigs !== void 0 ? { providerConfigs: row.providerConfigs } : {}
506
+ };
507
+ }
508
+ function promptProviders(rows) {
509
+ const found = [];
510
+ for (const row of rows) {
511
+ if (row.disabled === true) continue;
512
+ if (row.group && Array.isArray(row.config)) found.push(...promptProviders(row.config));
513
+ else if (promptProviderModules.includes(row.name)) {
514
+ const config = row.config ?? {};
515
+ found.push({ name: row.name, description: row.name.replace("@deepseek-ai/dsh-", ""), fields: (providerFields[row.name] ?? []).map((field2) => ({ ...field2, ...Object.hasOwn(config, field2.key) ? { value: config[field2.key] } : {} })) });
516
+ }
517
+ }
518
+ return [...new Map(found.map((item) => [item.name, item])).values()];
519
+ }
520
+ function selectPromptProviders(rows, selected) {
521
+ const available = new Set(promptProviders(rows).map((item) => item.name));
522
+ if (selected.some((name) => !available.has(name))) throw new Error("Unknown prompt provider selection");
523
+ const visit = (entries) => entries.flatMap((row) => {
524
+ if (row.name === "@deepseek-ai/dsh-command-compact" && !selected.includes("@deepseek-ai/dsh-compaction-basic")) return [];
525
+ if (promptProviderModules.includes(row.name) && !selected.includes(row.name)) return [];
526
+ return [{ ...row, ...row.group && Array.isArray(row.config) ? { config: visit(row.config) } : {} }];
527
+ });
528
+ return visit(rows);
529
+ }
530
+
531
+ // src/host/composition-plan.ts
532
+ function parseComposition(content) {
533
+ const rows = load(content, { schema: entryListSchema });
534
+ if (!Array.isArray(rows)) throw new Error("Preset declaration must be a plugin list");
535
+ return rows;
536
+ }
537
+ function findProviders(rows) {
538
+ return rows.flatMap((row) => row.disabled === true ? [] : row.group && Array.isArray(row.config) ? findProviders(row.config) : promptProviderModules.includes(row.name) ? [row] : []);
539
+ }
540
+ async function compositionPlan(ctx, input) {
541
+ const request = parsePreviewRequest(input);
542
+ const base = request.preset === MASTER_PRESET_ID ? MASTER_PRESET_ID : "minimal";
543
+ if (request.configOverrides && base !== MASTER_PRESET_ID) throw new Error("Config overrides require Studio Master");
544
+ const minimal = applyConfigOverrides(parseComposition((await ctx.agentPresets.readDocument(base)).content), request.configOverrides ?? {});
545
+ const available = new Map(findProviders(minimal).map((row) => [row.name, row]));
546
+ const presets = await ctx.agentPresets.list();
547
+ for (const preset of presets) {
548
+ if (base === MASTER_PRESET_ID || !["minimal", "standard", "ptc", "cordis"].includes(preset.id)) continue;
549
+ try {
550
+ for (const row of findProviders(parseComposition((await ctx.agentPresets.readDocument(preset.id)).content))) {
551
+ if (!available.has(row.name)) available.set(row.name, row);
552
+ }
553
+ } catch {
554
+ }
555
+ }
556
+ const plugins = [...new Set(request.providers)].map((name, index) => {
557
+ const row = available.get(name);
558
+ if (!row) throw new Error(`Unavailable prompt provider: ${name}`);
559
+ const config = { ...row.config ?? {} };
560
+ const overrides = request.providerConfigs?.[name];
561
+ if (overrides) {
562
+ for (const [key, value] of Object.entries(overrides)) {
563
+ const field2 = providerFields[name]?.find((field3) => field3.key === key);
564
+ const valid = field2 && (field2.type === "string[]" ? Array.isArray(value) && value.every((item) => typeof item === "string") : typeof value === field2.type);
565
+ if (!valid || field2?.options && !field2.options.includes(String(value)) || typeof value === "number" && (!Number.isFinite(value) || field2?.minimum !== void 0 && value < field2.minimum)) throw new Error(`Unsupported provider setting: ${name}.${key}`);
566
+ if (key === "timeZone" && typeof value === "string") new Intl.DateTimeFormat("en", { timeZone: value }).format();
567
+ config[key] = value;
568
+ }
569
+ }
570
+ if (name === "@deepseek-ai/dsh-persona") {
571
+ config.complete = false;
572
+ if (request.persona !== void 0) config.prefix = request.persona;
573
+ }
574
+ if (name === "@deepseek-ai/dsh-compaction-basic") {
575
+ if (overrides?.retainTokens !== void 0 && overrides.retainRatio === void 0) delete config.retainRatio;
576
+ if (overrides?.retainRatio !== void 0 && overrides.retainTokens === void 0) delete config.retainTokens;
577
+ }
578
+ return { ...row, id: `studio-provider-${index}`, config };
579
+ });
580
+ if (request.persona !== void 0 && !request.providers.includes("@deepseek-ai/dsh-persona")) throw new Error("Select persona before editing its prefix");
581
+ for (const name of Object.keys(request.providerConfigs ?? {})) if (!request.providers.includes(name)) throw new Error(`Unselected provider override: ${name}`);
582
+ const globalNames = new Set(ctx.tools.schemas().map((tool) => tool.name));
583
+ const missing = request.tools.filter((name) => !globalNames.has(name));
584
+ if (missing.length || base === MASTER_PRESET_ID) {
585
+ let source;
586
+ const candidates = base === MASTER_PRESET_ID ? [MASTER_PRESET_ID] : ["minimal", ...presets.map((p) => p.id).filter((id) => id !== "minimal" && !id.startsWith("studio-"))];
587
+ for (const id of candidates) {
588
+ let lease;
589
+ try {
590
+ lease = await ctx.agentPresets.acquireScope(id);
591
+ const names2 = new Set(ctx.tools.schemas(lease.key).map((tool) => tool.name));
592
+ if (missing.every((name) => names2.has(name))) {
593
+ source = id === base ? minimal : parseComposition((await ctx.agentPresets.readDocument(id)).content);
594
+ break;
595
+ }
596
+ } catch {
597
+ } finally {
598
+ await lease?.[Symbol.asyncDispose]();
599
+ }
600
+ }
601
+ if (!source) throw new Error(`No compatible installed tool composition supplies: ${missing.join(", ")}. Tools from incompatible source scopes cannot be combined safely.`);
602
+ plugins.push(...selectPromptProviders(source, []));
603
+ }
604
+ const compaction = plugins.filter((row) => ["@deepseek-ai/dsh-compaction-basic", "@deepseek-ai/dsh-compaction-tool-result-pruner"].includes(row.name));
605
+ const isolated = plugins.filter((row) => !compaction.includes(row));
606
+ if (compaction.length) isolated.push({ id: "studio-compaction", name: "cordis:group", group: true, isolate: { compaction: true, toolResultPruner: true }, config: compaction });
607
+ return { request, plugins: isolated };
608
+ }
609
+
610
+ // src/host/preset-policy.ts
611
+ import { scopeOf } from "@deepseek-ai/dsh-scope";
612
+ var POLICY_SECTION_ORDERS = Object.freeze({
613
+ "tool:jobs": "TOOL_JOBS",
614
+ "tool:bash": "TOOL_BASH",
615
+ "tool:pwsh": "TOOL_PWSH",
616
+ "tool:read": "TOOL_READ",
617
+ "tool:write": "TOOL_WRITE",
618
+ "tool:edit": "TOOL_EDIT",
619
+ "tool:glob": "TOOL_GLOB",
620
+ "tool:grep": "TOOL_GREP",
621
+ "tool:web_fetch": "TOOL_WEB_FETCH",
622
+ "tool:web_search": "TOOL_WEB_SEARCH",
623
+ "tool:goal": "TOOL_GOAL"
624
+ });
625
+ var own2 = (object3, key) => Object.prototype.hasOwnProperty.call(object3, key);
626
+ var record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
627
+ function validatePolicy(config) {
628
+ if (!record3(config) || !Array.isArray(config.tools) || config.tools.some((name) => typeof name !== "string" || !name) || new Set(config.tools).size !== config.tools.length || !record3(config.toolOverrides) || !record3(config.sections) || typeof config.prompt !== "string") throw new Error("Invalid Studio policy");
629
+ if (config.execution !== void 0) {
630
+ validateExecutionDefaults(config.execution);
631
+ for (const name of Object.keys(config.execution.defaults)) if (!config.tools.includes(name)) throw new Error(`Execution default requires selected tool: ${name}`);
632
+ if (config.execution.batch && !config.tools.includes(config.execution.batch.tool)) throw new Error("Batch limit requires selected tool");
633
+ }
634
+ for (const [name, override] of Object.entries(config.toolOverrides)) {
635
+ if (!config.tools.includes(name)) throw new Error(`Override requires selected tool: ${name}`);
636
+ if (!record3(override) || Object.keys(override).some((key) => !["description", "parameters"].includes(key)) || override.description !== void 0 && typeof override.description !== "string" || override.parameters !== void 0 && (!record3(override.parameters) || Object.values(override.parameters).some((value) => typeof value !== "string"))) throw new Error(`Invalid override: ${name}`);
637
+ }
638
+ for (const [name, text] of Object.entries(config.sections)) {
639
+ if (!own2(POLICY_SECTION_ORDERS, name) || typeof text !== "string") throw new Error(`Unsupported policy section: ${name}`);
640
+ }
641
+ }
642
+ function patchParameter(parameters, path, description) {
643
+ const segments = path.split(".");
644
+ let node = parameters;
645
+ for (const segment of segments) {
646
+ if (!segment || ["__proto__", "prototype", "constructor"].includes(segment) || !record3(node.properties) || !own2(node.properties, segment) || !record3(node.properties[segment])) throw new Error(`Unknown parameter path: ${path}`);
647
+ node = node.properties[segment];
648
+ }
649
+ node.description = description;
650
+ }
651
+ function installPolicy(ctx, scopeKey, config) {
652
+ validatePolicy(config);
653
+ if (scopeOf(ctx) !== scopeKey) throw new Error("Studio policy requires its owning scoped context");
654
+ const selected = new Set(config.tools);
655
+ const definitions = config.tools.map((name) => {
656
+ const definition = ctx.tools.get(name, scopeKey);
657
+ if (!definition) throw new Error(`Unavailable selected tool: ${name}`);
658
+ const override = config.toolOverrides[name];
659
+ const defaults = config.execution?.defaults[name];
660
+ const batch = config.execution?.batch?.tool === name ? config.execution.batch : void 0;
661
+ if (!override && !defaults && !batch) return void 0;
662
+ const parameters = structuredClone(definition.parameters);
663
+ for (const [path, description] of Object.entries(override?.parameters ?? {})) patchParameter(parameters, path, description);
664
+ for (const [field2, value] of Object.entries(defaults ?? {})) {
665
+ validateExecutionDefault(parameters, field2, value);
666
+ parameters.properties[field2].default = value;
667
+ }
668
+ if (batch) {
669
+ validateBatchLimit(parameters, batch);
670
+ parameters.properties[batch.parameter].maxItems = batch.maxItems;
671
+ }
672
+ const stableDefaults = { ...defaults };
673
+ const stableBatch = batch && { ...batch };
674
+ return {
675
+ ...definition,
676
+ parameters,
677
+ ...override?.description === void 0 ? {} : { description: override.description },
678
+ ...defaults || batch ? { execute(args, exec) {
679
+ const effective = applyExecutionDefaults(args, stableDefaults);
680
+ if (stableBatch && record3(effective) && Array.isArray(effective[stableBatch.parameter]) && effective[stableBatch.parameter].length > stableBatch.maxItems) throw new Error(`Studio batch limit exceeded: ${stableBatch.maxItems}`);
681
+ return definition.execute.call(definition, effective, exec);
682
+ } } : {}
683
+ };
684
+ }).filter((definition) => definition !== void 0);
685
+ const disposers = [];
686
+ const dispose = () => {
687
+ for (const release of disposers.splice(0).reverse()) release();
688
+ };
689
+ try {
690
+ disposers.push(ctx.tools.restrict({ allow: config.tools }));
691
+ const excess = ctx.tools.schemas(scopeKey).filter((tool) => !selected.has(tool.name));
692
+ if (excess.length) throw new Error(`Unselected scoped tools must be removed from preset plugins: ${excess.map((tool) => tool.name).join(", ")}`);
693
+ disposers.push(ctx.tools.guard((execution) => selected.has(execution.name) ? void 0 : "Tool excluded by Studio preset policy"));
694
+ for (const definition of definitions) disposers.push(ctx.tools.register(definition));
695
+ for (const [name, text] of Object.entries(config.sections)) disposers.push(ctx.systemPrompt.section({ name, order: ctx.systemPrompt.getSectionOrder(POLICY_SECTION_ORDERS[name]), text, interpolate: false }));
696
+ disposers.push(ctx.systemPrompt.section({ name: "studio:additional", order: Number.MAX_SAFE_INTEGER, text: config.prompt, interpolate: false }));
697
+ return dispose;
698
+ } catch (error) {
699
+ dispose();
700
+ throw error;
701
+ }
702
+ }
703
+
704
+ // src/host/prompt-preview.ts
705
+ async function assemblePreview(ctx, input) {
706
+ const { request, plugins } = await compositionPlan(ctx, { ...input, tools: delegationTools(input.tools) });
707
+ const id = `studio-preview-${randomUUID2()}`;
708
+ const unregister = await ctx.agentPresets.register({ id, plugins });
709
+ try {
710
+ if (ctx.agentPresets.resolve) {
711
+ const resolved = await ctx.agentPresets.resolve(id);
712
+ if (resolved.broken) throw new Error(resolved.broken);
713
+ }
714
+ const lease = await ctx.agentPresets.acquireScope(id);
715
+ try {
716
+ const key = {};
717
+ const child = createScope(ctx, key, { parent: lease.key });
718
+ try {
719
+ installPolicy(child.ctx, key, { tools: request.tools, toolOverrides: request.toolOverrides ?? {}, sections: request.sections ?? {}, prompt: request.prompt, ...request.execution ? { execution: request.execution } : {} });
720
+ child.ctx.systemPrompt.variable("cwd", () => process.cwd());
721
+ if (request.model.trim()) child.ctx.systemPrompt.variable("model", () => request.model.trim());
722
+ const assembly = await ctx.systemPrompt.assemble({ scope: key });
723
+ const actual = assembly.tools.map((tool) => tool.name);
724
+ if (actual.some((name) => !request.tools.includes(name)) || request.tools.some((name) => !actual.includes(name))) throw new Error("Assembler tool output differs from the selected tools");
725
+ return {
726
+ prompt: renderPrompt(assembly),
727
+ sections: assembly.sections.map((section) => ({ name: section.name, text: renderPrompt({ ...assembly, sections: [section] }) })).filter((section) => section.text),
728
+ context: renderContextSnapshot(assembly),
729
+ tools: assembly.tools
730
+ };
731
+ } finally {
732
+ await child.dispose();
733
+ }
734
+ } finally {
735
+ await lease[Symbol.asyncDispose]();
736
+ }
737
+ } finally {
738
+ await unregister();
739
+ }
740
+ }
741
+
742
+ // src/execution-capabilities.ts
743
+ function executionCapabilities(tools) {
744
+ const waits = [];
745
+ let batch;
746
+ for (const tool of tools) {
747
+ const properties = tool.parameters?.properties;
748
+ for (const [parameter, schema] of Object.entries(properties ?? {})) {
749
+ if (["timeoutMs", "timeout_ms"].includes(parameter) && ["number", "integer"].includes(String(schema.type))) {
750
+ waits.push({ tool: tool.name, parameter, supported: executionDefaultFields(tool.parameters ?? {}).includes(parameter), ...typeof schema.minimum === "number" ? { minimum: schema.minimum } : {}, ...typeof schema.maximum === "number" ? { maximum: schema.maximum } : {}, ...typeof schema.default === "number" ? { defaultValue: schema.default } : {} });
751
+ }
752
+ if (batchLimitFields(tool.name, tool.parameters ?? {}).includes(parameter)) batch = { tool: tool.name, parameter, ...typeof schema.maxItems === "number" ? { maxItems: schema.maxItems } : {} };
753
+ }
754
+ }
755
+ return { waits, ...batch ? { batch } : {}, spill: { supported: false, reason: "policy-integration-unverified" } };
756
+ }
757
+
758
+ // src/host/preset-store.ts
759
+ import { randomUUID as randomUUID3 } from "node:crypto";
760
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, rename as rename2 } from "node:fs/promises";
761
+ import { dirname as dirname2, join } from "node:path";
762
+ import { dump } from "js-yaml";
763
+ import { entryListSchema as entryListSchema2 } from "@deepseek-ai/cordis-plugin-include";
764
+
765
+ // src/role-settings.ts
766
+ var object2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
767
+ function parseRoles(input) {
768
+ if (!Array.isArray(input) || input.length > 50) throw new Error("Studio agents must be an array of at most 50 roles");
769
+ const seen = /* @__PURE__ */ new Set();
770
+ return input.map((value) => {
771
+ if (!object2(value)) throw new Error("Invalid Studio role");
772
+ const text = (key, fallback, max) => {
773
+ const v = value[key] ?? fallback;
774
+ if (typeof v !== "string" || v.length > max) throw new Error(`Invalid role ${key}`);
775
+ return key === "persona" ? v : v.trim();
776
+ };
777
+ const name = text("name", "", 120);
778
+ const role = text("role", name, 120);
779
+ const model = text("model", "inherit", 512);
780
+ const reasoning = text("reasoning", "inherit", 120);
781
+ const spawn = text("spawn", "fresh", 10);
782
+ const persona = text("persona", "", 32e3);
783
+ if (!name || !role || seen.has(role)) throw new Error("Role names and unique role identifiers are required");
784
+ if (model !== "inherit" && !/^[^\s/]+\/\S+$/.test(model)) throw new Error("Role model must be inherit or provider/model");
785
+ if (!/^[a-zA-Z0-9_-]+$/.test(reasoning)) throw new Error("Role reasoning must be inherit or an effort identifier");
786
+ if (spawn !== "fresh" && spawn !== "fork") throw new Error("Role spawn must be fresh or fork");
787
+ seen.add(role);
788
+ return { name, role, model, reasoning, spawn, persona, ..."overrides" in value ? { overrides: structuredClone(value.overrides) } : {} };
789
+ });
790
+ }
791
+ function executableRoles(roles) {
792
+ return roles.map(({ name, role, model, reasoning, spawn, persona }) => ({ name, role, model, reasoning, spawn, persona }));
793
+ }
794
+
795
+ // src/host/role-validation.ts
796
+ import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
797
+ async function validateRoleModel(llm, role, signal) {
798
+ signal?.throwIfAborted();
799
+ if (role.model === "inherit") return;
800
+ if (!llm) throw new Error("Studio role model validation unavailable: LLM service is not mounted.");
801
+ const slash = role.model.indexOf("/");
802
+ const provider = role.model.slice(0, slash);
803
+ const model = role.model.slice(slash + 1);
804
+ if (!llm.listProviders().some((item) => item.id === provider)) {
805
+ throw new Error(`Studio role "${role.role}": provider "${provider}" is not registered. Select an available model in Studio, save and reapply the preset.`);
806
+ }
807
+ try {
808
+ await llm.resolveCallConfig({
809
+ provider,
810
+ model,
811
+ ...role.reasoning === "inherit" ? {} : { reasoningEffort: ReasoningEffortId(role.reasoning) }
812
+ }, signal);
813
+ } catch {
814
+ signal?.throwIfAborted();
815
+ throw new Error(`Studio role "${role.role}": could not validate model "${role.model}" and reasoning settings. Check provider availability and model settings, then retry. No child was started.`);
816
+ }
817
+ signal?.throwIfAborted();
818
+ }
819
+
820
+ // src/host/preset-store.ts
821
+ var owned = (id) => id === OPTIMIZED_PRESET_ID || /^studio-user-[a-f0-9-]{36}$/.test(id);
822
+ var warning = "Work convenience settings are saved but not applied yet. Legacy role overrides are stored only; roles apply only their supported settings.";
823
+ function migrateLegacyNames(row) {
824
+ const request = row.request;
825
+ if (request.toolOverrides) {
826
+ const next = renameLegacyRoleKey(request.toolOverrides);
827
+ if (next) request.toolOverrides = next;
828
+ }
829
+ if (request.execution?.defaults) {
830
+ const next = renameLegacyRoleKey(request.execution.defaults);
831
+ if (next) request.execution.defaults = next;
832
+ }
833
+ if (!row.applied || !row.applied.plugins.includes("studio_role") && !row.applied.plugins.includes(LEGACY_PACKAGE_NAME)) return;
834
+ const visit = (entries) => entries.map((entry) => {
835
+ const name = currentPluginName(entry.name);
836
+ const config = entry.group && Array.isArray(entry.config) ? visit(entry.config) : name === POLICY_PLUGIN && entry.config && typeof entry.config === "object" ? migratePolicyConfig(entry.config) : entry.config;
837
+ return { ...entry, name, ...config === void 0 ? {} : { config } };
838
+ });
839
+ row.applied = { ...row.applied, plugins: dump(visit(parseComposition(row.applied.plugins)), { schema: entryListSchema2, noRefs: true }) };
840
+ }
841
+ function studioStorePath(env = process.env, profile) {
842
+ const directory = profile?.dir ?? env.DSH_PROFILE_DIR;
843
+ if (!directory) throw new Error("Studio preset persistence requires a profile");
844
+ return join(directory, "studio-data", "presets.json");
845
+ }
846
+ var PresetStore = class {
847
+ constructor(ctx, file) {
848
+ this.ctx = ctx;
849
+ this.file = file;
850
+ }
851
+ rows = [];
852
+ disposers = /* @__PURE__ */ new Map();
853
+ ready;
854
+ tail = Promise.resolve();
855
+ initialize() {
856
+ return this.ready ??= (async () => {
857
+ try {
858
+ const value = JSON.parse(await readFile2(this.file, "utf8"));
859
+ if (value.version !== 1 || !Array.isArray(value.presets)) throw new Error("Unsupported Studio preset store");
860
+ this.rows = value.presets;
861
+ for (const row of this.rows) {
862
+ if (!owned(row.id)) throw new Error("Invalid Studio preset ownership");
863
+ parsePreviewRequest(row.request);
864
+ migrateLegacyNames(row);
865
+ }
866
+ } catch (error) {
867
+ if (error.code !== "ENOENT") throw error;
868
+ }
869
+ for (const row of this.rows) if (row.applied) {
870
+ try {
871
+ await this.mount(row.id, row.applied);
872
+ } catch (error) {
873
+ row.applyWarning = `Restore failed: ${String(error)}`;
874
+ row.status = "modified";
875
+ }
876
+ }
877
+ })();
878
+ }
879
+ async mount(id, definition) {
880
+ const dispose = await this.ctx.agentPresets.register({ id, name: definition.name, description: definition.description, plugins: parseComposition(definition.plugins) });
881
+ try {
882
+ const preset = await this.ctx.agentPresets.resolve(id);
883
+ if (preset.broken) throw new Error(preset.broken);
884
+ this.disposers.set(id, dispose);
885
+ } catch (error) {
886
+ await dispose();
887
+ throw error;
888
+ }
889
+ }
890
+ async persist(rows) {
891
+ await mkdir2(dirname2(this.file), { recursive: true, mode: 448 });
892
+ const temp = `${this.file}.tmp`;
893
+ await writeFile2(temp, JSON.stringify({ version: 1, presets: rows }, null, 2), { mode: 384 });
894
+ await rename2(temp, this.file);
895
+ this.rows = rows;
896
+ }
897
+ serial(action) {
898
+ const result = this.tail.then(async () => {
899
+ await this.initialize();
900
+ return action();
901
+ });
902
+ this.tail = result.catch(() => {
903
+ });
904
+ return result;
905
+ }
906
+ public(row) {
907
+ const { applied, ...value } = row;
908
+ return structuredClone({ ...value, ...row.id === OPTIMIZED_PRESET_ID ? { template: true, nonDeletable: true } : {} });
909
+ }
910
+ /** Seed once. Later startups retain user edits and explicit unapplied state. */
911
+ ensureOptimized(tools, plugins) {
912
+ return this.serial(async () => {
913
+ const existing = this.rows.find((row2) => row2.id === OPTIMIZED_PRESET_ID);
914
+ if (existing) return this.public(existing);
915
+ const template = optimizedTemplate(tools);
916
+ const request = template.request;
917
+ const delegates = request.tools.includes(ROLE_TOOL);
918
+ const base = applyConfigOverrides(plugins, request.configOverrides ?? {});
919
+ const applied = { name: template.name, description: template.description, plugins: dump([...delegates ? [...withoutDelegationPlugins(base), { id: "studio-roles", name: ROLE_PLUGIN, config: { roles: [] } }] : base, { id: "studio-optimized-policy", name: POLICY_PLUGIN, config: { tools: request.tools, toolOverrides: request.toolOverrides, sections: {}, prompt: request.prompt, execution: request.execution } }], { schema: entryListSchema2, noRefs: true }) };
920
+ const now = (/* @__PURE__ */ new Date()).toISOString();
921
+ const row = { id: template.id, name: template.name, description: template.description, request: template.request, conveniences: {}, agents: [], updatedAt: now, appliedAt: now, status: "applied", applied, ...template.applyWarning ? { applyWarning: template.applyWarning } : {} };
922
+ await this.mount(row.id, applied);
923
+ try {
924
+ await this.persist([...this.rows, row]);
925
+ } catch (error) {
926
+ await this.disposers.get(row.id)?.();
927
+ this.disposers.delete(row.id);
928
+ throw error;
929
+ }
930
+ return this.public(row);
931
+ });
932
+ }
933
+ list() {
934
+ return this.serial(async () => this.rows.map((row) => this.public(row)));
935
+ }
936
+ save(payload) {
937
+ return this.serial(async () => {
938
+ const value = JSON.parse(payload);
939
+ if (!value || typeof value.name !== "string" || !value.name.trim() || value.name.length > 120 || typeof value.description !== "string") throw new Error("Invalid preset name or description");
940
+ const old = value.id ? this.find(value.id) : void 0;
941
+ const row = {
942
+ ...old,
943
+ id: old?.id ?? `studio-user-${randomUUID3()}`,
944
+ name: value.name.trim(),
945
+ description: value.description,
946
+ request: parsePreviewRequest(value.request),
947
+ conveniences: value.conveniences ?? {},
948
+ agents: parseRoles(value.agents ?? old?.agents ?? []).map((role) => {
949
+ const prior = Array.isArray(old?.agents) ? old.agents.find((item) => item.role === role.role || item.name === role.name) : void 0;
950
+ return prior && "overrides" in prior && !("overrides" in role) ? { ...role, overrides: prior.overrides } : role;
951
+ }),
952
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
953
+ status: old?.applied ? "modified" : "draft"
954
+ };
955
+ await this.persist([...this.rows.filter((item) => item.id !== row.id), row]);
956
+ return this.public(row);
957
+ });
958
+ }
959
+ find(id) {
960
+ if (!owned(id)) throw new Error("Only Studio-owned presets may be managed");
961
+ const row = this.rows.find((row2) => row2.id === id);
962
+ if (!row) throw new Error("Studio preset not found");
963
+ return row;
964
+ }
965
+ apply(id) {
966
+ return this.serial(async () => {
967
+ const old = this.find(id);
968
+ const roles = executableRoles(parseRoles(old.agents));
969
+ const tools = delegationTools(old.request.tools, roles.length > 0);
970
+ const { request, plugins } = await compositionPlan(this.ctx, { ...old.request, tools: tools.filter((name) => name !== ROLE_TOOL) });
971
+ for (const role of roles) await validateRoleModel(this.ctx.get("llm"), role);
972
+ const delegates = tools.includes(ROLE_TOOL);
973
+ const keep = (entries) => Object.fromEntries(Object.entries(entries).filter(([name]) => tools.includes(name)));
974
+ const rolePlugins = delegates ? [{ id: "studio-roles", name: ROLE_PLUGIN, config: { roles } }] : [];
975
+ const execution = request.execution && {
976
+ ...request.execution,
977
+ defaults: keep(request.execution.defaults),
978
+ ...request.execution.batch && !tools.includes(request.execution.batch.tool) ? { batch: void 0 } : {}
979
+ };
980
+ const applied = { name: old.name, description: old.description, plugins: dump([...withoutDelegationPlugins(plugins), ...rolePlugins, { id: "studio-policy", name: POLICY_PLUGIN, config: { tools: [...new Set(tools)], toolOverrides: keep(request.toolOverrides ?? {}), sections: request.sections ?? {}, prompt: request.prompt, ...execution ? { execution } : {} } }], { schema: entryListSchema2, noRefs: true }) };
981
+ await this.disposers.get(id)?.();
982
+ this.disposers.delete(id);
983
+ try {
984
+ await this.mount(id, applied);
985
+ const hasLegacySettings = old.conveniences != null && Object.keys(Object(old.conveniences)).length > 0 || Array.isArray(old.agents) && old.agents.some((role) => role && Object.hasOwn(role, "overrides"));
986
+ const row = { ...old, applied, appliedAt: (/* @__PURE__ */ new Date()).toISOString(), status: "applied" };
987
+ if (hasLegacySettings) row.applyWarning = warning;
988
+ else delete row.applyWarning;
989
+ await this.persist(this.rows.map((item) => item.id === id ? row : item));
990
+ return this.public(row);
991
+ } catch (error) {
992
+ await this.disposers.get(id)?.();
993
+ this.disposers.delete(id);
994
+ if (old.applied) await this.mount(id, old.applied);
995
+ throw error;
996
+ }
997
+ });
998
+ }
999
+ unapply(id) {
1000
+ return this.serial(async () => {
1001
+ const old = this.find(id);
1002
+ await this.disposers.get(id)?.();
1003
+ this.disposers.delete(id);
1004
+ const { applied, appliedAt, applyWarning, ...draft } = old;
1005
+ const row = { ...draft, status: "draft" };
1006
+ try {
1007
+ await this.persist(this.rows.map((item) => item.id === id ? row : item));
1008
+ } catch (error) {
1009
+ if (applied) await this.mount(id, applied);
1010
+ throw error;
1011
+ }
1012
+ return this.public(row);
1013
+ });
1014
+ }
1015
+ delete(id) {
1016
+ return this.serial(async () => {
1017
+ if (id === OPTIMIZED_PRESET_ID) throw new Error("Optimized is a non-deletable built-in preset");
1018
+ const old = this.find(id);
1019
+ await this.disposers.get(id)?.();
1020
+ this.disposers.delete(id);
1021
+ try {
1022
+ await this.persist(this.rows.filter((row) => row.id !== id));
1023
+ } catch (error) {
1024
+ if (old.applied) await this.mount(id, old.applied);
1025
+ throw error;
1026
+ }
1027
+ return { id };
1028
+ });
1029
+ }
1030
+ async dispose() {
1031
+ await this.tail;
1032
+ for (const dispose of this.disposers.values()) await dispose();
1033
+ this.disposers.clear();
1034
+ }
1035
+ };
1036
+
1037
+ // src/host/shell.ts
1038
+ var _preview_dec, _saveExtensions_dec, _readExtensions_dec, _read_dec, _deletePreset_dec, _roleModels_dec, _unapplyPreset_dec, _applyPreset_dec, _savePreset_dec, _listPresets_dec, _a, _init;
1039
+ var StudioCatalog = class extends (_a = TypertRemoteService, _listPresets_dec = [Remote], _savePreset_dec = [Remote], _applyPreset_dec = [Remote], _unapplyPreset_dec = [Remote], _roleModels_dec = [Remote], _deletePreset_dec = [Remote], _read_dec = [Remote], _readExtensions_dec = [Remote], _saveExtensions_dec = [Remote], _preview_dec = [Remote], _a) {
1040
+ constructor(ctx) {
1041
+ super(ctx, "studioCatalog");
1042
+ __runInitializers(_init, 5, this);
1043
+ __publicField(this, "store");
1044
+ __publicField(this, "snapshot");
1045
+ __publicField(this, "disposeMaster");
1046
+ __publicField(this, "stopped", false);
1047
+ __publicField(this, "extensions");
1048
+ __publicField(this, "changes", Promise.resolve());
1049
+ const storePath = studioStorePath(process.env, ctx.get("profileContext"));
1050
+ this.extensions = new MasterExtensions(join2(dirname3(storePath), "master-extensions.json"));
1051
+ this.store = new PresetStore(ctx, storePath);
1052
+ this.snapshot = new Promise((resolve2) => {
1053
+ setImmediate(resolve2);
1054
+ }).then(() => this.capture());
1055
+ void this.snapshot.catch(() => void 0);
1056
+ ctx.effect(() => async () => {
1057
+ this.stopped = true;
1058
+ await this.snapshot.catch(() => {
1059
+ });
1060
+ await this.disposeMaster?.();
1061
+ });
1062
+ ctx.effect(async () => {
1063
+ await this.store.initialize();
1064
+ return () => this.store.dispose();
1065
+ });
1066
+ }
1067
+ async listPresets() {
1068
+ const catalog = await this.snapshot;
1069
+ return [{ id: MASTER_PRESET_ID, name: MASTER_PRESET_NAME, description: "Studio Master \xB7 read-only", readOnly: true, status: "applied", agents: [], request: { preset: MASTER_PRESET_ID, providers: [], tools: catalog.tools.map((tool) => tool.name), prompt: "", model: "testLLM" } }, ...(await this.store.list()).sort((a, b) => Number(b.id === OPTIMIZED_PRESET_ID) - Number(a.id === OPTIMIZED_PRESET_ID))];
1070
+ }
1071
+ async savePreset(payload) {
1072
+ return this.store.save(payload);
1073
+ }
1074
+ async applyPreset(id) {
1075
+ await this.snapshot;
1076
+ return this.store.apply(id);
1077
+ }
1078
+ async unapplyPreset(id) {
1079
+ return this.store.unapply(id);
1080
+ }
1081
+ async roleModels() {
1082
+ const service = this.ctx.get("sessionController");
1083
+ if (!service) throw new Error("Model catalog API unavailable");
1084
+ return service.modelCatalog();
1085
+ }
1086
+ async deletePreset(id) {
1087
+ return this.store.delete(id);
1088
+ }
1089
+ async read() {
1090
+ return structuredClone(await this.snapshot);
1091
+ }
1092
+ async readExtensions() {
1093
+ await this.snapshot;
1094
+ return this.extensions.read();
1095
+ }
1096
+ async saveExtensions(payload) {
1097
+ const rows = validateExtensions(JSON.parse(payload));
1098
+ const operation = this.changes.catch(() => {
1099
+ }).then(async () => {
1100
+ await this.snapshot;
1101
+ const previous = await this.extensions.read();
1102
+ const probe = `studio-probe-${randomUUID4()}`;
1103
+ const dispose = await this.ctx.agentPresets.register({ id: probe, plugins: this.composition(rows) });
1104
+ try {
1105
+ const resolved = await this.ctx.agentPresets.resolve(probe);
1106
+ if (resolved.broken) throw new Error(resolved.broken);
1107
+ } finally {
1108
+ await dispose();
1109
+ }
1110
+ const update = (async () => {
1111
+ await this.disposeMaster?.();
1112
+ try {
1113
+ const catalog = await this.capture(rows);
1114
+ await this.extensions.save(rows);
1115
+ return catalog;
1116
+ } catch (error) {
1117
+ await this.disposeMaster?.();
1118
+ const restored = await this.capture(previous);
1119
+ this.snapshot = Promise.resolve(restored);
1120
+ throw error;
1121
+ }
1122
+ })();
1123
+ this.snapshot = update;
1124
+ try {
1125
+ return await update;
1126
+ } catch (error) {
1127
+ throw error;
1128
+ }
1129
+ });
1130
+ this.changes = operation;
1131
+ return operation;
1132
+ }
1133
+ composition(rows) {
1134
+ return [...masterComposition(), ...rows.length ? [{ id: "studio-added", name: "cordis:group", group: true, config: rows }] : []];
1135
+ }
1136
+ async capture(extra) {
1137
+ const project = await createConfigProjector();
1138
+ await this.ctx.loader.await();
1139
+ const composition = this.composition(extra ?? await this.extensions.read());
1140
+ const plugins = [];
1141
+ for (const { id, row } of configEntries(composition)) {
1142
+ const item = { id, name: row.name, ...id.startsWith("studio-added/") ? {} : { config: structuredClone(row.config ?? {}) } };
1143
+ try {
1144
+ const module = this.ctx.loader.unwrapExports(await this.ctx.loader.import(row.name));
1145
+ if (module?.Config) {
1146
+ if (!isNativeConfigSchema(module.Config)) throw new Error("Unsupported Config schema");
1147
+ const result = project(module.Config, "config");
1148
+ item.schema = JSON.parse(JSON.stringify({ $schema: "https://json-schema.org/draft/2020-12/schema", ...result.schema, $defs: { ...result.definitions, loaderExpression: LOADER_EXPRESSION_SCHEMA } }));
1149
+ }
1150
+ } catch (error) {
1151
+ item.error = String(error);
1152
+ }
1153
+ plugins.push(item);
1154
+ }
1155
+ if (this.stopped) throw new Error("Studio stopped during catalog initialization");
1156
+ this.disposeMaster = await this.ctx.agentPresets.register({ id: MASTER_PRESET_ID, name: MASTER_PRESET_NAME, description: "Studio-managed read-only explicit composition", plugins: composition });
1157
+ const master = await this.ctx.agentPresets.resolve(MASTER_PRESET_ID);
1158
+ if (master.broken) throw new Error(`Studio Master: ${master.broken}`);
1159
+ const registry = this.ctx.get("agentPresets");
1160
+ const catalog = await readPresetCatalog({
1161
+ agentPresets: { list: async () => [{ id: MASTER_PRESET_ID, name: MASTER_PRESET_NAME }, ...(await registry.list()).filter((p) => BUILTIN_TOOL_PRESETS.includes(p.id))], readDocument: (id) => registry.readDocument(id), acquireScope: (id) => registry.acquireScope(id) },
1162
+ systemPrompt: this.ctx.get("systemPrompt"),
1163
+ tools: this.ctx.get("tools")
1164
+ });
1165
+ if (catalog.presets[0]?.error) throw new Error(catalog.presets[0].error);
1166
+ for (const preset of catalog.presets) {
1167
+ if (preset.declaration) preset.promptProviders = promptProviders(parseComposition(preset.declaration));
1168
+ }
1169
+ const baseline = catalog.presets.find((p) => p.id === MASTER_PRESET_ID);
1170
+ await this.store.ensureOptimized(baseline.tools.map((t) => t.name), composition);
1171
+ catalog.plugins = plugins;
1172
+ catalog.execution = executionCapabilities(catalog.tools);
1173
+ return catalog;
1174
+ }
1175
+ async preview(request) {
1176
+ await this.snapshot;
1177
+ return assemblePreview(this.ctx, request);
1178
+ }
1179
+ };
1180
+ _init = __decoratorStart(_a);
1181
+ __decorateElement(_init, 1, "listPresets", _listPresets_dec, StudioCatalog);
1182
+ __decorateElement(_init, 1, "savePreset", _savePreset_dec, StudioCatalog);
1183
+ __decorateElement(_init, 1, "applyPreset", _applyPreset_dec, StudioCatalog);
1184
+ __decorateElement(_init, 1, "unapplyPreset", _unapplyPreset_dec, StudioCatalog);
1185
+ __decorateElement(_init, 1, "roleModels", _roleModels_dec, StudioCatalog);
1186
+ __decorateElement(_init, 1, "deletePreset", _deletePreset_dec, StudioCatalog);
1187
+ __decorateElement(_init, 1, "read", _read_dec, StudioCatalog);
1188
+ __decorateElement(_init, 1, "readExtensions", _readExtensions_dec, StudioCatalog);
1189
+ __decorateElement(_init, 1, "saveExtensions", _saveExtensions_dec, StudioCatalog);
1190
+ __decorateElement(_init, 1, "preview", _preview_dec, StudioCatalog);
1191
+ __decoratorMetadata(_init, StudioCatalog);
1192
+ __publicField(StudioCatalog, "inject", ["agentPresets", "systemPrompt", "tools", "loader"]);
1193
+ export {
1194
+ StudioCatalog as default
1195
+ };