@sparkelf/dsh-plugin-subagent-settings 0.1.0-rc.10
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +63 -0
- package/README.zh.md +63 -0
- package/lib/client.js +835 -0
- package/lib/index.js +371 -0
- package/lib/invariant.js +23 -0
- package/lib/startup.js +28 -0
- package/lib/types/client/SubagentCard.d.ts +20 -0
- package/lib/types/client/index.d.ts +17 -0
- package/lib/types/client/locales.d.ts +7 -0
- package/lib/types/client/subagent-store.d.ts +74 -0
- package/lib/types/index.d.ts +100 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/startup.d.ts +20 -0
- package/package.json +94 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
4
|
+
import { assertSubagentMaxDepth, settleRun } from "@deepseek-ai/dsh-subagent";
|
|
5
|
+
//#region lib/types/index.js
|
|
6
|
+
/**
|
|
7
|
+
* Model-facing delegation through one configured `ctx.subagents` provider.
|
|
8
|
+
* Provider lifecycle controls tool registration and context-sensitive schema
|
|
9
|
+
* wording. Foreground calls always dispose the run after collection.
|
|
10
|
+
* Background policy is selected by this plugin's configuration: one-shot
|
|
11
|
+
* calls own a plain Task, while continuable calls use
|
|
12
|
+
* `ctx.subagents.startContinuable()`.
|
|
13
|
+
* @module @sparkelf/dsh-plugin-subagent-settings
|
|
14
|
+
*/
|
|
15
|
+
const name = "tool-subagent";
|
|
16
|
+
const inject = [
|
|
17
|
+
"tools",
|
|
18
|
+
"subagents",
|
|
19
|
+
"systemPrompt"
|
|
20
|
+
];
|
|
21
|
+
/** Prompt order after bounded delegation policy and before child reporting. */
|
|
22
|
+
const SUBAGENT_SECTION_ORDER = 116.5;
|
|
23
|
+
/** Build a fresh tool-filter schema before each owner applies its omission default. */
|
|
24
|
+
function toolFilterSchema() {
|
|
25
|
+
return z.object({
|
|
26
|
+
allow: z.array(z.string()).default(void 0),
|
|
27
|
+
deny: z.array(z.string()).default(void 0)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const Config = z.object({
|
|
31
|
+
provider: z.string().required(),
|
|
32
|
+
settingsNamespace: z.string(),
|
|
33
|
+
enabled: z.boolean().default(true),
|
|
34
|
+
toolName: z.string().default("subagent"),
|
|
35
|
+
enableRunInBackground: z.boolean().default(true),
|
|
36
|
+
backgroundMode: z.union(["one-shot", "continuable"]).default("one-shot"),
|
|
37
|
+
agentOptions: z.object({
|
|
38
|
+
provider: z.string(),
|
|
39
|
+
model: z.string(),
|
|
40
|
+
maxTokens: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER)
|
|
41
|
+
}).default(void 0),
|
|
42
|
+
persona: z.string(),
|
|
43
|
+
toolFilter: toolFilterSchema().default(void 0),
|
|
44
|
+
maxDepth: z.union([z.natural().max(Number.MAX_SAFE_INTEGER), z.const("provider-managed")]).default(0)
|
|
45
|
+
});
|
|
46
|
+
/** Schema for the live user-owned child defaults. */
|
|
47
|
+
const SUBAGENT_SETTINGS_SCHEMA = z.object({
|
|
48
|
+
enabled: z.boolean().default(false),
|
|
49
|
+
agentOptions: z.object({
|
|
50
|
+
provider: z.string().default(void 0),
|
|
51
|
+
model: z.string().default(void 0),
|
|
52
|
+
maxTokens: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(void 0)
|
|
53
|
+
}).default(void 0),
|
|
54
|
+
persona: z.string().default(void 0),
|
|
55
|
+
toolFilter: toolFilterSchema().default(void 0),
|
|
56
|
+
maxDepth: z.union([z.natural().max(Number.MAX_SAFE_INTEGER), z.const("provider-managed")]).default(0)
|
|
57
|
+
});
|
|
58
|
+
/** Render text blocks from the canonical JSON block array without trusting arbitrary values. */
|
|
59
|
+
function outputValueText(values) {
|
|
60
|
+
return values.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value) && value.type === "text" && typeof value.text === "string").map((value) => value.text).join("");
|
|
61
|
+
}
|
|
62
|
+
/** Settle pending startup without rejecting the task producer contract. */
|
|
63
|
+
async function settleStart(start, signal) {
|
|
64
|
+
try {
|
|
65
|
+
return await settleRun(await start);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return signal.aborted && !(error instanceof AggregateError) ? { status: "killed" } : {
|
|
68
|
+
status: "failed",
|
|
69
|
+
detail: String(error)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** A non-`completed` stop reason means the child did not finish cleanly. */
|
|
74
|
+
function stopReasonError(result) {
|
|
75
|
+
switch (result.stopReason) {
|
|
76
|
+
case "completed": return;
|
|
77
|
+
case "aborted": return "subagent run was cancelled";
|
|
78
|
+
case "error": return "subagent run failed";
|
|
79
|
+
case "max-tokens": return "subagent run hit its token limit before finishing";
|
|
80
|
+
case "refusal": return "subagent declined the task";
|
|
81
|
+
default: return `subagent run ended abnormally (${String(result.stopReason)})`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Append provider-authored failure detail and the child's preserved partial
|
|
86
|
+
* answer to a stop-reason error, keeping diagnostic text separate from the
|
|
87
|
+
* child's assistant output.
|
|
88
|
+
* @param error - the stop-reason headline.
|
|
89
|
+
* @param result - the child's terminal result.
|
|
90
|
+
* @returns the headline, diagnostic, and partial text that are present.
|
|
91
|
+
*/
|
|
92
|
+
function withDiagnosticAndPartialText(error, result) {
|
|
93
|
+
const diagnostic = result.diagnostic === void 0 ? "" : `\nDiagnostic: ${result.diagnostic}`;
|
|
94
|
+
const text = result.output.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
95
|
+
return `${error}${diagnostic}${text.length === 0 ? "" : `\nPartial output before the run ended:\n${text}`}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Collect and release one foreground run without letting disposal replace an
|
|
99
|
+
* independent result failure.
|
|
100
|
+
*/
|
|
101
|
+
async function settleForegroundRun(run) {
|
|
102
|
+
const [execution] = await Promise.allSettled([run.result.then((result) => {
|
|
103
|
+
const error = stopReasonError(result);
|
|
104
|
+
if (error !== void 0) throw new Error(withDiagnosticAndPartialText(error, result));
|
|
105
|
+
return {
|
|
106
|
+
kind: "foreground",
|
|
107
|
+
runId: run.id,
|
|
108
|
+
output: result.output
|
|
109
|
+
};
|
|
110
|
+
})]);
|
|
111
|
+
const [disposal] = await Promise.allSettled([Promise.resolve().then(() => run.dispose())]);
|
|
112
|
+
if (execution.status === "rejected") {
|
|
113
|
+
if (disposal.status === "rejected") throw new AggregateError([execution.reason, disposal.reason], `subagent run failed: ${String(execution.reason)}; dispose failed: ${String(disposal.reason)}`);
|
|
114
|
+
throw execution.reason;
|
|
115
|
+
}
|
|
116
|
+
if (disposal.status === "rejected") throw disposal.reason;
|
|
117
|
+
return execution.value;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Model-facing wording from the provider's conversation-history descriptor
|
|
121
|
+
* ({@link SubagentProvider.inheritsParentContext}).
|
|
122
|
+
* A fresh child needs a standalone prompt; a forked child already sees the
|
|
123
|
+
* conversation's completed turns — telling the model to restate everything
|
|
124
|
+
* (or, worse, that the child "does not see this conversation") would be false
|
|
125
|
+
* for a fork.
|
|
126
|
+
* @param inheritsConversation - whether the child's conversation is seeded
|
|
127
|
+
* with the parent's completed turns; this says nothing about tool, service,
|
|
128
|
+
* scope, or authority inheritance.
|
|
129
|
+
* @returns the tool `description` and the `prompt` parameter description.
|
|
130
|
+
*/
|
|
131
|
+
function providerWording(inheritsConversation) {
|
|
132
|
+
if (inheritsConversation) return {
|
|
133
|
+
description: "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn). Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive its result, not its intermediate steps.",
|
|
134
|
+
promptDescription: "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new."
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
description: "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation.",
|
|
138
|
+
promptDescription: "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Resolve the model's optional scheduling request into one execution route. */
|
|
142
|
+
function resolveDelegationRun(request, options) {
|
|
143
|
+
if (!options.backgroundEnabled) {
|
|
144
|
+
if (request.run_in_background === true) throw new Error("run_in_background is disabled for this tool instance (enableRunInBackground: false)");
|
|
145
|
+
return { runInBackground: false };
|
|
146
|
+
}
|
|
147
|
+
return { runInBackground: request.run_in_background ?? options.continuable };
|
|
148
|
+
}
|
|
149
|
+
/** Project deployment defaults into the live user-owned settings section.
|
|
150
|
+
* @param config - composition-owned child defaults.
|
|
151
|
+
* @returns the fields a user layer may override.
|
|
152
|
+
*/
|
|
153
|
+
function settingsFromConfig(config) {
|
|
154
|
+
const agentOptions = config.agentOptions;
|
|
155
|
+
return {
|
|
156
|
+
...config.enabled === void 0 ? {} : { enabled: config.enabled },
|
|
157
|
+
...agentOptions === void 0 ? {} : { agentOptions: {
|
|
158
|
+
...agentOptions.provider === void 0 ? {} : { provider: agentOptions.provider },
|
|
159
|
+
...agentOptions.model === void 0 ? {} : { model: agentOptions.model },
|
|
160
|
+
...agentOptions.maxTokens === void 0 ? {} : { maxTokens: agentOptions.maxTokens }
|
|
161
|
+
} },
|
|
162
|
+
...config.persona === void 0 ? {} : { persona: config.persona },
|
|
163
|
+
...config.toolFilter === void 0 ? {} : { toolFilter: config.toolFilter },
|
|
164
|
+
...config.maxDepth === void 0 ? {} : { maxDepth: config.maxDepth }
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** Convert configured nesting generations to the provider's absolute child-depth cap.
|
|
168
|
+
* @param nestingDepth - generations allowed below the first child.
|
|
169
|
+
* @returns the absolute cap enforced by SubagentRuntime.
|
|
170
|
+
*/
|
|
171
|
+
function absoluteChildDepthCap(nestingDepth) {
|
|
172
|
+
return Math.min(nestingDepth + 1, Number.MAX_SAFE_INTEGER);
|
|
173
|
+
}
|
|
174
|
+
/** Validate live defaults at the earliest point the provider can enforce them.
|
|
175
|
+
* @param value - resolved child defaults.
|
|
176
|
+
* @param provider - currently registered provider, when available.
|
|
177
|
+
*/
|
|
178
|
+
function validateSettings(value, provider) {
|
|
179
|
+
if (value.maxDepth !== void 0 && value.maxDepth !== "provider-managed") assertSubagentMaxDepth(value.maxDepth);
|
|
180
|
+
if (value.toolFilter !== void 0 && value.toolFilter.allow === void 0 && value.toolFilter.deny === void 0) throw new Error("tool-subagent: `toolFilter` is configured but names neither `allow` nor `deny` — remove the key or fill the filter");
|
|
181
|
+
if (provider === void 0) return;
|
|
182
|
+
if (typeof value.maxDepth === "number" && !provider.capabilities.depthLimit) throw new Error(`tool-subagent: provider "${provider.name}" cannot enforce maxDepth (no depthLimit capability) — set maxDepth: 'provider-managed' to leave the recursion budget to the provider`);
|
|
183
|
+
if (value.persona !== void 0 && !provider.capabilities.persona) throw new Error("tool-subagent: provider \"" + provider.name + "\" cannot apply persona (no persona capability)");
|
|
184
|
+
if (value.toolFilter !== void 0 && !provider.capabilities.toolFilter) throw new Error("tool-subagent: provider \"" + provider.name + "\" cannot apply toolFilter (no toolFilter capability)");
|
|
185
|
+
}
|
|
186
|
+
function apply(ctx, config) {
|
|
187
|
+
const configuredSettings = settingsFromConfig(config);
|
|
188
|
+
const namespace = config.settingsNamespace === void 0 ? void 0 : settingsNamespace(config.settingsNamespace);
|
|
189
|
+
const settingsSource = () => {
|
|
190
|
+
if (namespace === void 0) return configuredSettings;
|
|
191
|
+
const settings = ctx.get("settings");
|
|
192
|
+
if (settings === void 0) return configuredSettings;
|
|
193
|
+
const value = settings.get(namespace);
|
|
194
|
+
if (value === void 0) throw new Error(`tool-subagent: settings namespace "${namespace}" is not registered by the Host composition`);
|
|
195
|
+
return value;
|
|
196
|
+
};
|
|
197
|
+
validateSettings(settingsSource(), void 0);
|
|
198
|
+
const backgroundEnabled = config.enableRunInBackground !== false;
|
|
199
|
+
const continuable = (config.backgroundMode ?? "one-shot") === "continuable";
|
|
200
|
+
const toolName = config.toolName ?? "subagent";
|
|
201
|
+
let disposeTool;
|
|
202
|
+
const mount = (provider) => {
|
|
203
|
+
validateSettings(settingsSource(), provider);
|
|
204
|
+
const wording = providerWording(provider.inheritsParentContext);
|
|
205
|
+
if (continuable && provider.prepareContinuable === void 0) throw new Error(`tool-subagent: provider "${provider.name}" does not support \`backgroundMode: continuable\``);
|
|
206
|
+
disposeTool = ctx.tools.register(defineTool({
|
|
207
|
+
name: toolName,
|
|
208
|
+
description: wording.description + (backgroundEnabled ? continuable ? " This tool runs in the background by default, immediately returns a durable subagent id, and keeps the child conversation available for later turns. When that run settles, the runtime sends the parent a notice containing its outcome and any final assistant message; `send_message` starts a later turn in the same child conversation. Set `run_in_background: false` only when your next action depends on receiving the result." : " This call waits for the result by default. Set `run_in_background: true` to return a job id; collect with `job_output` and stop with `job_kill`." : " This call waits for the subagent and returns its result."),
|
|
209
|
+
parameters: {
|
|
210
|
+
description: {
|
|
211
|
+
type: "string",
|
|
212
|
+
required: true,
|
|
213
|
+
description: "A short (3-5 word) description of the delegated task, for display."
|
|
214
|
+
},
|
|
215
|
+
prompt: {
|
|
216
|
+
type: "string",
|
|
217
|
+
required: true,
|
|
218
|
+
description: wording.promptDescription
|
|
219
|
+
},
|
|
220
|
+
...backgroundEnabled ? { run_in_background: {
|
|
221
|
+
type: "boolean",
|
|
222
|
+
description: continuable ? "Whether to run in the background and return a durable subagent id immediately. Defaults to true. Set false to wait for the result when your next action depends on it." : "Whether to run as a background job and return its id. Defaults to false; collect with job_output or stop with job_kill."
|
|
223
|
+
} } : {}
|
|
224
|
+
},
|
|
225
|
+
output: {
|
|
226
|
+
schema: { oneOf: [
|
|
227
|
+
{
|
|
228
|
+
type: "object",
|
|
229
|
+
additionalProperties: false,
|
|
230
|
+
properties: {
|
|
231
|
+
kind: {
|
|
232
|
+
type: "string",
|
|
233
|
+
required: true,
|
|
234
|
+
const: "background"
|
|
235
|
+
},
|
|
236
|
+
jobId: {
|
|
237
|
+
type: "string",
|
|
238
|
+
required: true
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
type: "object",
|
|
244
|
+
additionalProperties: false,
|
|
245
|
+
properties: {
|
|
246
|
+
kind: {
|
|
247
|
+
type: "string",
|
|
248
|
+
required: true,
|
|
249
|
+
const: "continuable"
|
|
250
|
+
},
|
|
251
|
+
subagentId: {
|
|
252
|
+
type: "string",
|
|
253
|
+
required: true
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
type: "object",
|
|
259
|
+
additionalProperties: false,
|
|
260
|
+
properties: {
|
|
261
|
+
kind: {
|
|
262
|
+
type: "string",
|
|
263
|
+
required: true,
|
|
264
|
+
const: "foreground"
|
|
265
|
+
},
|
|
266
|
+
runId: {
|
|
267
|
+
type: "string",
|
|
268
|
+
required: true
|
|
269
|
+
},
|
|
270
|
+
output: {
|
|
271
|
+
type: "array",
|
|
272
|
+
required: true,
|
|
273
|
+
items: { type: "json" }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
] },
|
|
278
|
+
render: (_args, value) => [{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: value.kind === "background" ? `started background subagent job ${value.jobId}` : value.kind === "continuable" ? `started subagent ${value.subagentId}` : outputValueText(value.output)
|
|
281
|
+
}]
|
|
282
|
+
},
|
|
283
|
+
isConcurrencySafe: () => true,
|
|
284
|
+
async execute(args, exec) {
|
|
285
|
+
const parent = exec.agent;
|
|
286
|
+
if (!parent) throw new Error("subagent tool requires a calling agent (exec.agent was undefined)");
|
|
287
|
+
const settings = settingsSource();
|
|
288
|
+
const maxDepth = typeof settings.maxDepth === "number" ? absoluteChildDepthCap(settings.maxDepth) : void 0;
|
|
289
|
+
const request = {
|
|
290
|
+
label: args.description,
|
|
291
|
+
prompt: [{
|
|
292
|
+
type: "text",
|
|
293
|
+
text: args.prompt
|
|
294
|
+
}],
|
|
295
|
+
parent,
|
|
296
|
+
...settings.agentOptions !== void 0 ? { agentOptions: settings.agentOptions } : {},
|
|
297
|
+
...settings.persona !== void 0 ? { persona: settings.persona } : {},
|
|
298
|
+
...settings.toolFilter !== void 0 ? { toolFilter: settings.toolFilter } : {},
|
|
299
|
+
...maxDepth !== void 0 ? { maxDepth } : {}
|
|
300
|
+
};
|
|
301
|
+
if (resolveDelegationRun(args, {
|
|
302
|
+
backgroundEnabled,
|
|
303
|
+
continuable
|
|
304
|
+
}).runInBackground) {
|
|
305
|
+
if (continuable) return {
|
|
306
|
+
kind: "continuable",
|
|
307
|
+
subagentId: (await ctx.subagents.startContinuable({
|
|
308
|
+
provider: config.provider,
|
|
309
|
+
label: args.description,
|
|
310
|
+
request,
|
|
311
|
+
signal: exec.signal
|
|
312
|
+
})).childId
|
|
313
|
+
};
|
|
314
|
+
const jobs = ctx.get("jobs");
|
|
315
|
+
if (jobs === void 0) throw new Error("background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs");
|
|
316
|
+
return {
|
|
317
|
+
kind: "background",
|
|
318
|
+
jobId: jobs.start({
|
|
319
|
+
kind: "subagent",
|
|
320
|
+
label: args.description,
|
|
321
|
+
owner: parent,
|
|
322
|
+
run: () => {
|
|
323
|
+
const controller = new AbortController();
|
|
324
|
+
return {
|
|
325
|
+
cancel: (reason) => {
|
|
326
|
+
controller.abort(reason ?? "background subagent task killed");
|
|
327
|
+
},
|
|
328
|
+
done: settleStart(ctx.subagents.start(config.provider, {
|
|
329
|
+
...request,
|
|
330
|
+
signal: controller.signal
|
|
331
|
+
}), controller.signal)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return settleForegroundRun(await ctx.subagents.start(config.provider, {
|
|
338
|
+
...request,
|
|
339
|
+
signal: exec.signal
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
}));
|
|
343
|
+
};
|
|
344
|
+
const reconcile = (provider) => {
|
|
345
|
+
if (!(settingsSource().enabled !== false) || provider === void 0) {
|
|
346
|
+
disposeTool?.();
|
|
347
|
+
disposeTool = void 0;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (disposeTool === void 0) mount(provider);
|
|
351
|
+
};
|
|
352
|
+
ctx.on("subagent/provider-added", (provider) => {
|
|
353
|
+
if (provider.name === config.provider) reconcile(provider);
|
|
354
|
+
});
|
|
355
|
+
ctx.on("subagent/provider-removed", (name) => {
|
|
356
|
+
if (name === config.provider) reconcile(void 0);
|
|
357
|
+
});
|
|
358
|
+
if (namespace !== void 0) ctx.on("settings/updated", (changedNamespace) => {
|
|
359
|
+
if (changedNamespace === namespace) reconcile(ctx.subagents.getProvider(config.provider));
|
|
360
|
+
});
|
|
361
|
+
const present = ctx.subagents.getProvider(config.provider);
|
|
362
|
+
reconcile(present);
|
|
363
|
+
if (present === void 0 && settingsSource().enabled !== false) ctx.logger.info(`subagent provider "${config.provider}" not registered yet; the "${config.toolName ?? "subagent"}" tool will register when it appears`);
|
|
364
|
+
if (backgroundEnabled && continuable) ctx.systemPrompt.section({
|
|
365
|
+
name: `tool:${toolName}`,
|
|
366
|
+
order: SUBAGENT_SECTION_ORDER,
|
|
367
|
+
text: (context) => disposeTool === void 0 || ctx.tools.get(toolName, context.scope) === void 0 ? "" : `Use ${toolName} in the background by default. Start independent delegations together in one assistant message and continue useful work while they run. Set \`run_in_background: false\` only when your next action depends on that subagent's result. When a background run settles, the runtime sends you a notice containing its outcome and any final assistant message.`
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
export { Config, SUBAGENT_SETTINGS_SCHEMA, apply, inject, name, settingsFromConfig, validateSettings };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@sparkelf/dsh-plugin-subagent-settings`.
|
|
4
|
+
* @module @sparkelf/dsh-plugin-subagent-settings/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@sparkelf/dsh-plugin-subagent-settings";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "tool-subagent-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: this model-facing adapter has no independent lifecycle stream; execution
|
|
13
|
+
* relations are owned by the capability seam it calls.
|
|
14
|
+
*/
|
|
15
|
+
const install = () => {};
|
|
16
|
+
/**
|
|
17
|
+
* Register this package's invariant companion.
|
|
18
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
+
*/
|
|
21
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
package/lib/startup.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { SUBAGENT_SETTINGS_SCHEMA, settingsFromConfig, validateSettings } from "./index.js";
|
|
2
|
+
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
//#region lib/types/startup.js
|
|
5
|
+
const name = "tool-subagent-settings";
|
|
6
|
+
const inject = ["subagents"];
|
|
7
|
+
const Config = z.intersect([z.object({
|
|
8
|
+
settingsNamespace: z.string().required(),
|
|
9
|
+
provider: z.string().required()
|
|
10
|
+
}), SUBAGENT_SETTINGS_SCHEMA]);
|
|
11
|
+
/** Register one Subagent settings namespace at Host lifetime.
|
|
12
|
+
* @param ctx - Host plugin context carrying Settings and Subagent services.
|
|
13
|
+
* @param config - namespace, provider, and composition defaults.
|
|
14
|
+
*/
|
|
15
|
+
function apply(ctx, config) {
|
|
16
|
+
const namespace = settingsNamespace(config.settingsNamespace);
|
|
17
|
+
const base = settingsFromConfig(config);
|
|
18
|
+
ctx.inject(["settings"], (sctx) => {
|
|
19
|
+
sctx.settings.register(namespace, SUBAGENT_SETTINGS_SCHEMA, {
|
|
20
|
+
base,
|
|
21
|
+
validate: (value) => {
|
|
22
|
+
validateSettings(value, ctx.subagents.getProvider(config.provider));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Config, apply, inject, name };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
4
|
+
import type { SubagentEntryView, SubagentSettingsState, SubagentSettingsValue } from './subagent-store.ts';
|
|
5
|
+
/** Browser-side actions and snapshot supplied by the section registration. */
|
|
6
|
+
export interface SubagentSettingsSectionInjected {
|
|
7
|
+
hooks: {
|
|
8
|
+
subagentSettings: SnapshotStore<SubagentSettingsState>;
|
|
9
|
+
};
|
|
10
|
+
ensure: () => void;
|
|
11
|
+
stage: (entry: SubagentEntryView, value: SubagentSettingsValue) => void;
|
|
12
|
+
save: (entry: SubagentEntryView) => Promise<void>;
|
|
13
|
+
reset: (entry: SubagentEntryView) => void;
|
|
14
|
+
discard: (entry: SubagentEntryView) => void;
|
|
15
|
+
}
|
|
16
|
+
/** Props for the unified subagent settings section. */
|
|
17
|
+
export type SubagentSettingsSectionProps = PropsRuntime<'settings.section'> & PropsLocale<'settings.subagents'> & InjectFace<SubagentSettingsSectionInjected>;
|
|
18
|
+
/** Render one card whose mode tabs preserve independent settings namespaces. */
|
|
19
|
+
export declare function SubagentSettingsSection(props: SubagentSettingsSectionProps): ReactNode;
|
|
20
|
+
//# sourceMappingURL=SubagentCard.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Browser Settings section for the two shipped settings-backed delegation entries. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import { type SubagentSettingsLocaleKey } from './locales.ts';
|
|
4
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
5
|
+
interface LocaleNamespaceMap {
|
|
6
|
+
/** Product copy for the Subagent Settings section. */
|
|
7
|
+
'settings.subagents': SubagentSettingsLocaleKey;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Required browser services. */
|
|
11
|
+
export declare const inject: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Mount the dedicated Subagents Settings section.
|
|
14
|
+
* @param ctx - Browser plugin context.
|
|
15
|
+
*/
|
|
16
|
+
export declare function apply(ctx: Context): void;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Typed dictionaries owned by the Subagent settings capability. */
|
|
2
|
+
export type SubagentSettingsLocaleKey = 'nav' | 'reset' | 'readOnly' | 'save' | 'saving' | 'discard' | 'unsaved' | 'saveFailed' | 'subagentTitle' | 'subagentIntro' | 'subagentModes' | 'subagentContinuous' | 'subagentOneShot' | 'subagentContinuousDescription' | 'subagentOneShotDescription' | 'subagentEnableContinuous' | 'subagentEnableOneShot' | 'subagentModel' | 'subagentFollowParent' | 'subagentFixedModel' | 'subagentProvider' | 'subagentModelId' | 'subagentMaxTokens' | 'subagentPersona' | 'subagentInheritPersona' | 'subagentOverridePersona' | 'subagentPersonaPlaceholder' | 'subagentTools' | 'subagentAllTools' | 'subagentAllowTools' | 'subagentDenyTools' | 'subagentToolNamesPlaceholder' | 'subagentDepth' | 'subagentProviderManaged';
|
|
3
|
+
/** English product copy. */
|
|
4
|
+
export declare const en: Record<SubagentSettingsLocaleKey, string>;
|
|
5
|
+
/** Simplified Chinese product copy. */
|
|
6
|
+
export declare const zh: Record<SubagentSettingsLocaleKey, string>;
|
|
7
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Shared Settings-scope state for the unified subagent settings section. */
|
|
2
|
+
import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
3
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
4
|
+
/** One editable value from a tool-subagent settings namespace. */
|
|
5
|
+
export interface SubagentSettingsValue {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
agentOptions?: Partial<{
|
|
8
|
+
provider: string;
|
|
9
|
+
model: string;
|
|
10
|
+
maxTokens: number;
|
|
11
|
+
}>;
|
|
12
|
+
persona?: string;
|
|
13
|
+
toolFilter?: {
|
|
14
|
+
allow?: string[];
|
|
15
|
+
deny?: string[];
|
|
16
|
+
};
|
|
17
|
+
maxDepth?: number | 'provider-managed';
|
|
18
|
+
}
|
|
19
|
+
/** Settings namespaces owned by the two shipped delegation entries. */
|
|
20
|
+
export type SubagentSettingsNamespace = 'subagent' | 'subagent-fork';
|
|
21
|
+
/** One shipped delegation entry and its effective settings. */
|
|
22
|
+
export interface SubagentEntryView {
|
|
23
|
+
ns: SubagentSettingsNamespace;
|
|
24
|
+
kind: 'spawn' | 'fork';
|
|
25
|
+
label: string;
|
|
26
|
+
context: 'fresh' | 'forked';
|
|
27
|
+
background: 'continuable' | 'one-shot';
|
|
28
|
+
value: SubagentSettingsValue;
|
|
29
|
+
}
|
|
30
|
+
/** State rendered by the unified settings section. */
|
|
31
|
+
export interface SubagentSettingsState {
|
|
32
|
+
writable: boolean;
|
|
33
|
+
entries: readonly SubagentEntryView[];
|
|
34
|
+
drafts: Readonly<Partial<Record<SubagentSettingsNamespace, SubagentSettingsValue>>>;
|
|
35
|
+
saving: boolean;
|
|
36
|
+
saveErrors: Readonly<Partial<Record<SubagentSettingsNamespace, true>>>;
|
|
37
|
+
}
|
|
38
|
+
type SubagentScopes = Record<SubagentSettingsNamespace, SettingsScope<SubagentSettingsValue>>;
|
|
39
|
+
/** Settings controller shared by both mode panels. */
|
|
40
|
+
export declare class SubagentSettingsStore {
|
|
41
|
+
private readonly scopes;
|
|
42
|
+
/** Reactive state shared by both mode panels. */
|
|
43
|
+
readonly store: SnapshotStore<SubagentSettingsState>;
|
|
44
|
+
private readonly unsubscribers;
|
|
45
|
+
private started;
|
|
46
|
+
/**
|
|
47
|
+
* @param scopes - namespace scopes derived from the browser's shared Settings mirror.
|
|
48
|
+
*/
|
|
49
|
+
constructor(scopes: SubagentScopes);
|
|
50
|
+
/** Start following both scopes and load model choices once. */
|
|
51
|
+
ensure(): void;
|
|
52
|
+
/** Stop following the two Settings scopes. */
|
|
53
|
+
dispose(): void;
|
|
54
|
+
/** Stage one entry until its plugin card is saved.
|
|
55
|
+
* @param entry - entry being edited.
|
|
56
|
+
* @param value - complete staged user section.
|
|
57
|
+
*/
|
|
58
|
+
stage(entry: SubagentEntryView, value: SubagentSettingsValue): void;
|
|
59
|
+
/** Persist one staged entry through its Settings scope.
|
|
60
|
+
* @param entry - entry whose card initiated the save.
|
|
61
|
+
*/
|
|
62
|
+
save(entry: SubagentEntryView): Promise<void>;
|
|
63
|
+
/** Stage one entry back to its deployment defaults.
|
|
64
|
+
* @param entry - entry being reset.
|
|
65
|
+
*/
|
|
66
|
+
reset(entry: SubagentEntryView): void;
|
|
67
|
+
/** Discard one card's staged edit without writing it.
|
|
68
|
+
* @param entry - entry whose draft is discarded.
|
|
69
|
+
*/
|
|
70
|
+
discard(entry: SubagentEntryView): void;
|
|
71
|
+
private derive;
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=subagent-store.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing delegation through one configured `ctx.subagents` provider.
|
|
3
|
+
* Provider lifecycle controls tool registration and context-sensitive schema
|
|
4
|
+
* wording. Foreground calls always dispose the run after collection.
|
|
5
|
+
* Background policy is selected by this plugin's configuration: one-shot
|
|
6
|
+
* calls own a plain Task, while continuable calls use
|
|
7
|
+
* `ctx.subagents.startContinuable()`.
|
|
8
|
+
* @module @sparkelf/dsh-plugin-subagent-settings
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import z from '@deepseek-ai/schemastery';
|
|
12
|
+
import type { AgentOptions } from '@deepseek-ai/dsh-agent';
|
|
13
|
+
import type { SubagentProvider } from '@deepseek-ai/dsh-subagent';
|
|
14
|
+
export declare const name = "tool-subagent";
|
|
15
|
+
export declare const inject: string[];
|
|
16
|
+
/** Config: which registered provider this tool delegates to, plus child defaults. */
|
|
17
|
+
export interface Config {
|
|
18
|
+
/** The `ctx.subagents` provider name to start runs on (e.g. `spawn`, `acp`). */
|
|
19
|
+
provider: string;
|
|
20
|
+
/** Optional Host-registered settings namespace for live child-default overrides. */
|
|
21
|
+
settingsNamespace?: string;
|
|
22
|
+
/** Whether this model-facing delegation entry is registered (default true). */
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Model-facing tool name (default `subagent`). Each loaded instance must use
|
|
26
|
+
* a distinct name.
|
|
27
|
+
*/
|
|
28
|
+
toolName?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Expose `run_in_background` (default true). Disabled instances omit the
|
|
31
|
+
* parameter and reject forced background calls.
|
|
32
|
+
*/
|
|
33
|
+
enableRunInBackground?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Background execution policy (default `one-shot`). `one-shot` defaults calls
|
|
36
|
+
* to foreground; `continuable` defaults them to background, requires a provider
|
|
37
|
+
* with the `prepareContinuable` capability, and returns the durable child id.
|
|
38
|
+
* Follow-up adapters remain independently optional.
|
|
39
|
+
*/
|
|
40
|
+
backgroundMode?: 'one-shot' | 'continuable';
|
|
41
|
+
/**
|
|
42
|
+
* Agent options applied to every child; omitted fields use child-loop defaults.
|
|
43
|
+
*/
|
|
44
|
+
agentOptions?: AgentOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Per-child persona that shadows `deployment:persona`. Requires the
|
|
47
|
+
* provider's `persona` capability; omission preserves the deployment persona.
|
|
48
|
+
*/
|
|
49
|
+
persona?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Tool filter applied to every child. Filtered tools disappear from its
|
|
52
|
+
* prompt and reject execution. Requires the provider's `toolFilter`
|
|
53
|
+
* capability; unknown names fail startup.
|
|
54
|
+
*/
|
|
55
|
+
toolFilter?: {
|
|
56
|
+
/** Global tool names the child keeps; everything else is removed. */
|
|
57
|
+
allow?: string[];
|
|
58
|
+
/** Global tool names removed from the child. */
|
|
59
|
+
deny?: string[];
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Additional delegation generations below a direct child: a non-negative safe
|
|
63
|
+
* integer (default `0`; `0` permits a child but forbids grandchildren), or
|
|
64
|
+
* `'provider-managed'` to send no cap. A numeric cap
|
|
65
|
+
* requires the provider's `depthLimit` capability (mount fails loud
|
|
66
|
+
* otherwise). The provider checks the calling agent's current depth at every
|
|
67
|
+
* start; the tool remains model-visible so runtime policy owns rejection.
|
|
68
|
+
* `'provider-managed'` is for an out-of-process provider whose recursion
|
|
69
|
+
* budget belongs to the child runtime or its own deployment.
|
|
70
|
+
*/
|
|
71
|
+
maxDepth?: number | 'provider-managed';
|
|
72
|
+
}
|
|
73
|
+
export declare const Config: z<Config>;
|
|
74
|
+
/** User-owned entry state and child defaults that can change without rebuilding the Host. */
|
|
75
|
+
export interface SubagentSettings {
|
|
76
|
+
/** Whether the Host exposes this delegation mode to agents. */
|
|
77
|
+
enabled?: boolean;
|
|
78
|
+
/** Provider and model overrides; omission follows the parent agent. */
|
|
79
|
+
agentOptions?: Partial<Pick<AgentOptions, 'provider' | 'model' | 'maxTokens'>>;
|
|
80
|
+
/** Child persona that shadows the deployment persona when set. */
|
|
81
|
+
persona?: string;
|
|
82
|
+
/** Global tool names the child keeps or removes. */
|
|
83
|
+
toolFilter?: Config['toolFilter'];
|
|
84
|
+
/** Delegation generations below a direct child, or provider-owned depth management. */
|
|
85
|
+
maxDepth?: Config['maxDepth'];
|
|
86
|
+
}
|
|
87
|
+
/** Schema for the live user-owned child defaults. */
|
|
88
|
+
export declare const SUBAGENT_SETTINGS_SCHEMA: z<SubagentSettings>;
|
|
89
|
+
/** Project deployment defaults into the live user-owned settings section.
|
|
90
|
+
* @param config - composition-owned child defaults.
|
|
91
|
+
* @returns the fields a user layer may override.
|
|
92
|
+
*/
|
|
93
|
+
export declare function settingsFromConfig(config: SubagentSettings): SubagentSettings;
|
|
94
|
+
/** Validate live defaults at the earliest point the provider can enforce them.
|
|
95
|
+
* @param value - resolved child defaults.
|
|
96
|
+
* @param provider - currently registered provider, when available.
|
|
97
|
+
*/
|
|
98
|
+
export declare function validateSettings(value: SubagentSettings, provider: SubagentProvider | undefined): void;
|
|
99
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@sparkelf/dsh-plugin-subagent-settings`.
|
|
3
|
+
* @module @sparkelf/dsh-plugin-subagent-settings/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "tool-subagent-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|