@toddzheng024/dscode-bundle 0.1.0 → 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.
Files changed (34) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/cordis.patch.yml +6 -0
  3. package/package.json +5 -1
  4. package/plugins/dscode/index.mjs +1 -1
  5. package/plugins/memory/content.mjs +57 -0
  6. package/plugins/memory/index.mjs +123 -0
  7. package/plugins/memory/pipeline.mjs +78 -0
  8. package/plugins/memory/store.mjs +93 -0
  9. package/plugins/session-bridge/client.mjs +106 -0
  10. package/plugins/session-bridge/communication.mjs +217 -0
  11. package/plugins/session-bridge/index.mjs +61 -0
  12. package/plugins/session-bridge/mailbox.mjs +212 -0
  13. package/plugins/session-bridge/paths.mjs +21 -0
  14. package/plugins/session-bridge/server.mjs +169 -0
  15. package/plugins/session-cards/content.mjs +64 -0
  16. package/plugins/session-cards/index.mjs +31 -0
  17. package/plugins/session-cards/manager.mjs +131 -0
  18. package/plugins/session-metrics/view.mjs +3 -3
  19. package/plugins/tui-tools/index.mjs +2 -0
  20. package/plugins/tui-tools/shell.mjs +27 -0
  21. package/plugins/ultra/policy.mjs +7 -3
  22. package/presets/dscode/agent.cordis.yml +7 -5
  23. package/vendor/deepseek/index.js +1 -1
  24. package/vendor/subagent/LICENSE +21 -0
  25. package/vendor/subagent/index.js +664 -0
  26. package/vendor/subagent/invariant.js +52 -0
  27. package/vendor/subagent/model-selection-settings.js +94 -0
  28. package/vendor/subagent/types/index.d.ts +81 -0
  29. package/vendor/subagent/types/invariant.d.ts +16 -0
  30. package/vendor/subagent/types/list-models.d.ts +10 -0
  31. package/vendor/subagent/types/model-selection-settings.d.ts +43 -0
  32. package/vendor/subagent/types/model-selection-state.d.ts +48 -0
  33. package/vendor/subagent/types/model-selection.d.ts +81 -0
  34. package/vendor/tui/index.mjs +158 -100
@@ -0,0 +1,664 @@
1
+ // dscode-child-effort-v1
2
+ import z from "@deepseek-ai/schemastery";
3
+ import { scopeChainOf, scopeOf } from "@deepseek-ai/dsh-scope";
4
+ import { defineTool } from "@deepseek-ai/dsh-tools";
5
+ import { SessionSeq } from "@deepseek-ai/dsh-session";
6
+ import { assertSubagentMaxDepth, parentAgentOptionsForDelegation, settleRun } from "@deepseek-ai/dsh-subagent";
7
+ import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
8
+ import { z as z$1 } from "zod";
9
+ z.object({
10
+ provider: z.string().min(1).required(),
11
+ model: z.string().min(1).required()
12
+ });
13
+ /**
14
+ * Stable identity for one provider/model pair.
15
+ * @param route - Exact provider/model route.
16
+ * @returns Opaque key for equality checks.
17
+ */
18
+ function modelRouteKey(route) {
19
+ return `${route.provider}\0${route.model}`;
20
+ }
21
+ /**
22
+ * Reject malformed or duplicate route policy entries at a durable or configuration boundary.
23
+ * @param routes - Candidate exact routes to validate.
24
+ * @returns an assertion that the candidate is a validated exact-route array.
25
+ */
26
+ function assertAllowedModelRoutes(routes) {
27
+ if (!Array.isArray(routes)) throw new Error("subagent model selection requires an array of routes");
28
+ const seen = /* @__PURE__ */ new Set();
29
+ const candidates = routes;
30
+ for (const candidate of candidates) {
31
+ if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate) || !("provider" in candidate) || typeof candidate.provider !== "string" || !("model" in candidate) || typeof candidate.model !== "string" || candidate.provider.length === 0 || candidate.model.length === 0) throw new Error("subagent model selection requires non-empty provider and model ids");
32
+ const route = {
33
+ provider: candidate.provider,
34
+ model: candidate.model
35
+ };
36
+ const key = modelRouteKey(route);
37
+ if (seen.has(key)) throw new Error(`subagent model selection repeats route "${route.provider}/${route.model}"`);
38
+ seen.add(key);
39
+ }
40
+ }
41
+ /**
42
+ * Whether a call explicitly selects any child LLM value.
43
+ * @param request - Model-facing route fields from the tool call.
44
+ * @returns Whether at least one route or effort field is present.
45
+ */
46
+ function hasDelegationModelRequest(request) {
47
+ return request.provider !== void 0 || request.model !== void 0 || request.reasoning_effort !== void 0;
48
+ }
49
+ /** Reject an empty model-facing route value at the tool JSON boundary. */
50
+ function assertNonEmpty(value, field) {
51
+ if (value !== void 0 && value.length === 0) throw new Error(`child LLM \`${field}\` must be non-empty`);
52
+ }
53
+ /**
54
+ * Merge model-supplied selection fields over configured child defaults.
55
+ * Provider and model form one route and must be supplied together. Changing
56
+ * that route without an effort clears the configured route-owned effort.
57
+ * @param parentOptions - Current parent values that supply missing child values.
58
+ * @param configured - Tool-instance child defaults.
59
+ * @param request - Model-facing route override.
60
+ * @param enabled - Whether this tool instance permits model-facing selection.
61
+ * @returns Child Agent options, preserving omission when no layer contributes one.
62
+ */
63
+ function requestedAgentOptions(parentOptions, configured, request, enabled) {
64
+ if (!hasDelegationModelRequest(request)) return configured;
65
+ if (!enabled) throw new Error("child model selection is disabled for this tool instance");
66
+ assertNonEmpty(request.provider, "provider");
67
+ assertNonEmpty(request.model, "model");
68
+ assertNonEmpty(request.reasoning_effort, "reasoning_effort");
69
+ if (request.provider === void 0 !== (request.model === void 0)) throw new Error("child LLM `provider` and `model` must be supplied together");
70
+ const baselineProvider = configured?.provider ?? parentOptions.provider;
71
+ const baselineModel = configured?.model ?? parentOptions.model;
72
+ const routeChanged = request.provider !== void 0 && (request.provider !== baselineProvider || request.model !== baselineModel);
73
+ const { reasoningEffort: _configuredReasoningEffort, ...configuredWithoutReasoning } = configured ?? {};
74
+ return {
75
+ ...routeChanged && request.reasoning_effort === void 0 ? configuredWithoutReasoning : configured,
76
+ ...request.provider === void 0 ? {} : {
77
+ provider: request.provider,
78
+ model: request.model
79
+ },
80
+ ...request.reasoning_effort === void 0 ? {} : { reasoningEffort: ReasoningEffortId(request.reasoning_effort) }
81
+ };
82
+ }
83
+ /**
84
+ * Enforce a settings-owned route list at the operation that creates the child.
85
+ * Pure inheritance remains outside this policy because no model-facing choice
86
+ * occurred; any explicit route or effort field must resolve to an allowed route.
87
+ * @param policy - Selection authority captured for this Session.
88
+ * @param parentOptions - Current parent values that supply missing child values.
89
+ * @param requested - Effective child options after request/config merging.
90
+ * @param request - Model-facing selection fields from the tool call.
91
+ */
92
+ function assertAllowedModelSelection(policy, parentOptions, requested, request) {
93
+ if (policy === void 0 || !hasDelegationModelRequest(request)) return;
94
+ const provider = requested?.provider ?? parentOptions.provider;
95
+ const model = requested?.model ?? parentOptions.model;
96
+ if (provider === void 0 || model === void 0) throw new Error("cannot select child LLM values without an effective provider and model");
97
+ if (policy.routes.some((route) => route.provider === provider && route.model === model)) return;
98
+ throw new Error(`child LLM route "${provider}/${model}" is not allowed for this Session`);
99
+ }
100
+ /**
101
+ * Whether configured Agent options require route validation before delegation.
102
+ * @param options - Tool-instance child defaults.
103
+ * @returns Whether configured provider, model, or effort values must be resolved.
104
+ */
105
+ function hasConfiguredLlmSelection(options) {
106
+ return options?.provider !== void 0 || options?.model !== void 0 || options?.reasoningEffort !== void 0;
107
+ }
108
+ /**
109
+ * Resolve an effective child route through its live adapter before the child is
110
+ * created. The LLM runtime owns provider lookup, exact-model metadata, effort
111
+ * validation, and adapter defaults.
112
+ * @param llm - Live LLM runtime.
113
+ * @param parentOptions - Current parent values whose compatible fields the child inherits.
114
+ * @param requested - Per-child options after request/config merging.
115
+ * @param signal - Tool-call cancellation signal.
116
+ * @param inheritParentReasoningEffort - Whether an omitted effort may inherit from the parent route.
117
+ */
118
+ async function preflightChildLlmRoute(llm, parentOptions, requested, signal, inheritParentReasoningEffort = true) {
119
+ const provider = requested?.provider ?? parentOptions.provider;
120
+ const model = requested?.model ?? parentOptions.model;
121
+ if (provider === void 0 || model === void 0) throw new Error("cannot select child LLM values without an effective provider and model");
122
+ const routeChanged = provider !== parentOptions.provider || model !== parentOptions.model;
123
+ const reasoningEffort = requested?.reasoningEffort ?? (inheritParentReasoningEffort && !routeChanged ? parentOptions.reasoningEffort : void 0);
124
+ await llm.resolveCallConfig({
125
+ provider,
126
+ model,
127
+ ...reasoningEffort === void 0 ? {} : { reasoningEffort }
128
+ }, signal);
129
+ }
130
+ //#endregion
131
+ //#region lib/types/list-models.js
132
+ /** Model-facing discovery of LLM routes available to child Agents. */
133
+ /** Resolve one registered provider with a model-correctable diagnostic. */
134
+ function registeredProvider(llm, policy, providerId) {
135
+ const providers = llm.listProviders();
136
+ const provider = providers.find((candidate) => candidate.id === providerId);
137
+ if (provider !== void 0) return provider;
138
+ const available = providers.filter((candidate) => policy.routes.some((route) => route.provider === candidate.id)).map((candidate) => candidate.id).join(", ") || "(none)";
139
+ throw new Error(`LLM provider "${providerId}" is not registered; available providers: ${available}`);
140
+ }
141
+ /** Render one advertised or resolved model. */
142
+ function modelLine(provider, model) {
143
+ return `${provider}/${model.id} — ${model.name}${model.description === void 0 ? "" : `: ${model.description}`}`;
144
+ }
145
+ /** Read the requested provider, advertised models, or exact-model efforts. */
146
+ async function listSubagentModels(ctx, policy, request, signal) {
147
+ const llm = ctx.get("llm");
148
+ if (llm === void 0) throw new Error("cannot discover child LLM routes because the `llm` service is unavailable");
149
+ if (request.model !== void 0 && request.provider === void 0) throw new Error("`model` requires `provider`");
150
+ if (request.provider === void 0) {
151
+ const providers = llm.listProviders().filter((provider) => policy.routes.some((route) => route.provider === provider.id));
152
+ return providers.length === 0 ? "(no LLM providers)" : providers.map((provider) => `${provider.id} — ${provider.name}`).join("\n");
153
+ }
154
+ if (request.provider.length === 0) throw new Error("`provider` must be non-empty");
155
+ const allowedRoutes = policy.routes.filter((route) => route.provider === request.provider);
156
+ if (allowedRoutes.length === 0) throw new Error(`LLM provider "${request.provider}" is not allowed for this Session`);
157
+ const provider = registeredProvider(llm, policy, request.provider);
158
+ if (request.model === void 0) {
159
+ const models = (await llm.listModels(provider.id)).filter((model) => allowedRoutes.some((route) => route.model === model.id));
160
+ return models.length === 0 ? `(no advertised models for ${provider.id})` : models.map((model) => modelLine(provider.id, model)).join("\n");
161
+ }
162
+ if (request.model.length === 0) throw new Error("`model` must be non-empty");
163
+ if (!allowedRoutes.some((route) => route.model === request.model)) throw new Error(`child LLM route "${provider.id}/${request.model}" is not allowed for this Session`);
164
+ const model = await llm.resolveModelInfo(provider.id, request.model, signal);
165
+ const efforts = model.reasoning?.efforts.map((effort) => `${effort.id}${model.reasoning?.defaultEffort === effort.id ? " (default)" : ""} — ${effort.name}` + (effort.description === void 0 ? "" : `: ${effort.description}`)).join("\n") || "(no advertised reasoning efforts)";
166
+ return `${modelLine(provider.id, model)}\nReasoning efforts:\n${efforts}`;
167
+ }
168
+ /**
169
+ * Register `list_subagent_models` for one owning delegation-tool instance.
170
+ * @param ctx - Context whose tool registry owns the fixed discovery definition.
171
+ * @param policy - Route policy captured for this Session.
172
+ */
173
+ function registerListSubagentModels(ctx, policy) {
174
+ ctx.tools.register(defineTool({
175
+ name: "list_subagent_models",
176
+ description: "Discover LLM routes for subagents without changing the current Agent. Call with no arguments to list registered providers, with `provider` to list its advertised models, or with `provider` and `model` to inspect that exact model and its reasoning efforts. Catalog membership is advisory: an adapter may accept an unlisted model id. Use the returned ids with a delegation tool's `provider`, `model`, and `reasoning_effort` fields.",
177
+ parameters: {
178
+ provider: {
179
+ type: "string",
180
+ description: "Registered LLM provider id. Omit to list providers."
181
+ },
182
+ model: {
183
+ type: "string",
184
+ description: "Exact model id to inspect. Requires provider; omit to list that provider's advertised models."
185
+ }
186
+ },
187
+ output: {
188
+ schema: { type: "string" },
189
+ render: (_args, result) => [{
190
+ type: "text",
191
+ text: result
192
+ }]
193
+ },
194
+ execute(args, exec) {
195
+ return listSubagentModels(ctx, policy, args, exec.signal);
196
+ }
197
+ }));
198
+ }
199
+ /** Host-only projection of the durable model-selection policy. */
200
+ const subagentModelSelectionProjectionDefinition = {
201
+ key: "subagentModelSelectionPolicy",
202
+ stateVersion: 1,
203
+ stateSchema: z$1.array(z$1.object({
204
+ provider: z$1.string().min(1),
205
+ model: z$1.string().min(1)
206
+ }).strict()).min(1).nullable(),
207
+ init: () => null,
208
+ apply: (policy, event) => {
209
+ if (policy !== null || event.type !== "subagent/model-selection-policy") return policy;
210
+ const { allowedModels } = event.data;
211
+ assertAllowedModelRoutes(allowedModels);
212
+ if (allowedModels.length === 0) throw new Error("subagent/model-selection-policy requires at least one route");
213
+ return allowedModels;
214
+ }
215
+ };
216
+ /**
217
+ * Read the exact route list captured for a model-selectable definition.
218
+ * @param projections - registry that owns the policy projection.
219
+ * @param session - session whose durable decision is read.
220
+ * @returns a detached route list, or undefined for the fixed-route definition.
221
+ */
222
+ function subagentModelSelectionPolicy(projections, session) {
223
+ return projections.stateOf(session, "subagentModelSelectionPolicy")?.map((route) => ({ ...route }));
224
+ }
225
+ /**
226
+ * Append the route policy once, before its definition can reach a model request.
227
+ * @param projections - registry that owns the policy projection.
228
+ * @param session - session receiving the model-selectable definition.
229
+ * @param allowedModels - exact routes the definition may select explicitly.
230
+ */
231
+ function recordSubagentModelSelection(projections, session, allowedModels) {
232
+ if (subagentModelSelectionPolicy(projections, session) !== void 0) return;
233
+ session.append("subagent/model-selection-policy", { allowedModels: allowedModels.map((route) => ({ ...route })) });
234
+ }
235
+ //#endregion
236
+ //#region lib/types/index.js
237
+ /**
238
+ * Model-facing delegation through one configured `ctx.subagents` provider.
239
+ * Provider lifecycle controls tool registration and context-sensitive schema
240
+ * wording. Foreground calls always dispose the run after collection.
241
+ * Background policy is selected by this plugin's configuration: one-shot
242
+ * calls own a plain Task, while continuable calls use
243
+ * `ctx.subagents.startContinuable()`.
244
+ * @module @deepseek-ai/dsh-tool-subagent
245
+ */
246
+ const name = "tool-subagent";
247
+ const inject = [
248
+ "tools",
249
+ "subagents",
250
+ "systemPrompt",
251
+ "sessionProjections"
252
+ ];
253
+ const Config = z.object({
254
+ provider: z.string().required(),
255
+ toolName: z.string().default("subagent"),
256
+ modelSelectionSettings: z.boolean().default(false),
257
+ enableRunInBackground: z.boolean().default(true),
258
+ backgroundMode: z.union(["one-shot", "continuable"]).default("one-shot"),
259
+ agentOptions: z.object({
260
+ provider: z.string(),
261
+ model: z.string(),
262
+ reasoningEffort: z.string().min(1),
263
+ maxTokens: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER)
264
+ }).default(void 0),
265
+ persona: z.string(),
266
+ toolFilter: z.object({
267
+ allow: z.array(z.string()).default(void 0),
268
+ deny: z.array(z.string()).default(void 0)
269
+ }).default(void 0),
270
+ maxDepth: z.union([z.natural().max(Number.MAX_SAFE_INTEGER), z.const("provider-managed")]).default(3)
271
+ });
272
+ /** Render text blocks from the canonical JSON block array without trusting arbitrary values. */
273
+ function outputValueText(values) {
274
+ return values.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value) && value.type === "text" && typeof value.text === "string").map((value) => value.text).join("");
275
+ }
276
+ /** Settle pending startup without rejecting the task producer contract. */
277
+ async function settleStart(start, signal) {
278
+ try {
279
+ return await settleRun(await start);
280
+ } catch (error) {
281
+ return signal.aborted && !(error instanceof AggregateError) ? { status: "killed" } : {
282
+ status: "failed",
283
+ detail: String(error)
284
+ };
285
+ }
286
+ }
287
+ /** A non-`completed` stop reason means the child did not finish cleanly. */
288
+ function stopReasonError(result) {
289
+ switch (result.stopReason) {
290
+ case "completed": return;
291
+ case "aborted": return "subagent run was cancelled";
292
+ case "error": return "subagent run failed";
293
+ case "max-tokens": return "subagent run hit its token limit before finishing";
294
+ case "refusal": return "subagent declined the task";
295
+ default: return `subagent run ended abnormally (${String(result.stopReason)})`;
296
+ }
297
+ }
298
+ /**
299
+ * Append provider-authored failure detail and the child's preserved partial
300
+ * answer to a stop-reason error, keeping diagnostic text separate from the
301
+ * child's assistant output.
302
+ * @param error - the stop-reason headline.
303
+ * @param result - the child's terminal result.
304
+ * @returns the headline, diagnostic, and partial text that are present.
305
+ */
306
+ function withDiagnosticAndPartialText(error, result) {
307
+ const diagnostic = result.diagnostic === void 0 ? "" : `\nDiagnostic: ${result.diagnostic}`;
308
+ const text = result.output.filter((block) => block.type === "text").map((block) => block.text).join("");
309
+ return `${error}${diagnostic}${text.length === 0 ? "" : `\nPartial output before the run ended:\n${text}`}`;
310
+ }
311
+ /**
312
+ * Collect and release one foreground run without letting disposal replace an
313
+ * independent result failure.
314
+ */
315
+ async function settleForegroundRun(run) {
316
+ const [execution] = await Promise.allSettled([run.result.then((result) => {
317
+ const error = stopReasonError(result);
318
+ if (error !== void 0) throw new Error(withDiagnosticAndPartialText(error, result));
319
+ return {
320
+ kind: "foreground",
321
+ runId: run.id,
322
+ output: result.output
323
+ };
324
+ })]);
325
+ const [disposal] = await Promise.allSettled([Promise.resolve().then(() => run.dispose())]);
326
+ if (execution.status === "rejected") {
327
+ if (disposal.status === "rejected") throw new AggregateError([execution.reason, disposal.reason], `subagent run failed: ${String(execution.reason)}; dispose failed: ${String(disposal.reason)}`);
328
+ throw execution.reason;
329
+ }
330
+ if (disposal.status === "rejected") throw disposal.reason;
331
+ return execution.value;
332
+ }
333
+ /**
334
+ * Model-facing wording from the provider's conversation-history descriptor
335
+ * ({@link SubagentProvider.inheritsParentContext}).
336
+ * A fresh child needs a standalone prompt; a forked child already sees the
337
+ * conversation's completed turns — telling the model to restate everything
338
+ * (or, worse, that the child "does not see this conversation") would be false
339
+ * for a fork.
340
+ * @param inheritsConversation - whether the child's conversation is seeded
341
+ * with the parent's completed turns; this says nothing about tool, service,
342
+ * scope, or authority inheritance.
343
+ * @returns the tool `description` and the `prompt` parameter description.
344
+ */
345
+ function providerWording(inheritsConversation) {
346
+ if (inheritsConversation) return {
347
+ 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.",
348
+ 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."
349
+ };
350
+ return {
351
+ 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.",
352
+ promptDescription: "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
353
+ };
354
+ }
355
+ /** Resolve the model's optional scheduling request into one execution route. */
356
+ function resolveDelegationRun(request, options) {
357
+ if (!options.backgroundEnabled) {
358
+ if (request.run_in_background === true) throw new Error("run_in_background is disabled for this tool instance (enableRunInBackground: false)");
359
+ return { runInBackground: false };
360
+ }
361
+ return { runInBackground: request.run_in_background ?? options.continuable };
362
+ }
363
+ /**
364
+ * Install one delegation-tool composition.
365
+ * @param ctx - Context that owns the registrations.
366
+ * @param config - delegation-tool configuration.
367
+ * @param session - unpublished Session supplied by a direct Agent setup; omit for a standing composition.
368
+ */
369
+ function apply(ctx, config, session) {
370
+ if (config.maxDepth !== "provider-managed") assertSubagentMaxDepth(config.maxDepth);
371
+ if (config.toolFilter !== void 0 && config.toolFilter.allow === void 0 && config.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");
372
+ const backgroundEnabled = config.enableRunInBackground !== false;
373
+ const continuable = (config.backgroundMode ?? "one-shot") === "continuable";
374
+ const toolName = config.toolName ?? "subagent";
375
+ const modelSelectionCapable = config.modelSelectionSettings === true;
376
+ ctx.sessionProjections.register(subagentModelSelectionProjectionDefinition);
377
+ const assertSubagentProviderConfiguration = (subagentProvider) => {
378
+ if (typeof config.maxDepth === "number" && !subagentProvider.capabilities.depthLimit) throw new Error(`tool-subagent: provider "${subagentProvider.name}" cannot enforce maxDepth (no depthLimit capability) — set maxDepth: 'provider-managed' to leave the recursion budget to the provider`);
379
+ if (config.agentOptions !== void 0 && !subagentProvider.capabilities.agentOptions) throw new Error(`tool-subagent: provider "${subagentProvider.name}" does not support child agentOptions`);
380
+ if (modelSelectionCapable && !subagentProvider.capabilities.agentOptions) throw new Error(`tool-subagent: provider "${subagentProvider.name}" does not support child model selection`);
381
+ if (continuable && subagentProvider.prepareContinuable === void 0) throw new Error(`tool-subagent: provider "${subagentProvider.name}" does not support \`backgroundMode: continuable\``);
382
+ };
383
+ ctx.on("subagent/provider-added", (subagentProvider) => {
384
+ if (subagentProvider.name === config.provider) assertSubagentProviderConfiguration(subagentProvider);
385
+ });
386
+ const initialProvider = ctx.subagents.getProvider(config.provider);
387
+ if (initialProvider !== void 0) assertSubagentProviderConfiguration(initialProvider);
388
+ const install = (runtimeCtx, modelSelectionPolicy) => {
389
+ const modelSelectionEnabled = modelSelectionPolicy !== void 0;
390
+ if (modelSelectionPolicy !== void 0) registerListSubagentModels(runtimeCtx, modelSelectionPolicy);
391
+ let mounted;
392
+ const mount = (subagentProvider) => {
393
+ assertSubagentProviderConfiguration(subagentProvider);
394
+ const wording = providerWording(subagentProvider.inheritsParentContext);
395
+ const providerRouteDefaults = subagentProvider.agentRouteDefaults;
396
+ const choiceDescription = !modelSelectionEnabled ? (subagentProvider.capabilities.agentOptions ? " Optionally set reasoning_effort for this child without changing its provider/model. Omit to inherit. Choose low for bounded tasks, high for difficult work, and max only when needed; use an effort supported by the current model." : "") : (providerRouteDefaults !== void 0 ? " Child LLM selection is optional. Omit `provider`, `model`, and `reasoning_effort` to use configured child defaults and this provider's route defaults. Supply `provider` and `model` together after using `list_subagent_models` to inspect advertised routes and efforts. Changing the effective route without naming an effort uses the selected model's default effort." : " Child LLM selection is optional. Omit `provider`, `model`, and `reasoning_effort` to use configured child defaults and inherit compatible missing values from the parent Agent. Supply `provider` and `model` together after using `list_subagent_models` to inspect advertised routes and efforts. Changing the effective route without naming an effort uses the selected model's default effort.") + (subagentProvider.inheritsParentContext ? " Changing the route can prevent provider-side reuse of the inherited conversation prefix." : "");
397
+ mounted = {
398
+ subagentProvider,
399
+ disposeTool: runtimeCtx.tools.register(defineTool({
400
+ name: toolName,
401
+ 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` steers the child's nearest step while it is running and starts a turn while it is idle. 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.") + choiceDescription,
402
+ parameters: {
403
+ description: {
404
+ type: "string",
405
+ required: true,
406
+ description: "A short (3-5 word) description of the delegated task, for display."
407
+ },
408
+ prompt: {
409
+ type: "string",
410
+ required: true,
411
+ description: wording.promptDescription
412
+ },
413
+ ...modelSelectionEnabled ? {
414
+ provider: {
415
+ type: "string",
416
+ description: providerRouteDefaults !== void 0 ? "LLM provider route for the child. Supply together with model; omit both to use configured child defaults or this provider's route defaults." : "LLM provider route for the child. Supply together with model; omit both to use configured child defaults or inherit the parent route."
417
+ },
418
+ model: {
419
+ type: "string",
420
+ description: providerRouteDefaults !== void 0 ? "Model id interpreted by provider. Supply together with provider; omit both to use configured child defaults or this provider's route defaults." : "Model id interpreted by provider. Supply together with provider; omit both to use configured child defaults or inherit the parent route."
421
+ },
422
+ reasoning_effort: {
423
+ type: "string",
424
+ description: providerRouteDefaults !== void 0 ? "Adapter-owned reasoning effort for the effective child route. Omit to use a compatible configured effort or the selected model's default." : "Adapter-owned reasoning effort for the effective child route. Omit to inherit a compatible configured/parent effort or use a newly selected model's default."
425
+ }
426
+ } : {},
427
+ ...!modelSelectionEnabled && subagentProvider.capabilities.agentOptions ? { reasoning_effort: {
428
+ type: "string",
429
+ description: "Reasoning effort for this child only, validated against its model. Omit to inherit. Prefer low for bounded tasks, high for difficult work, max for exceptional uncertainty. Provider/model remain unchanged."
430
+ } } : {},
431
+ ...backgroundEnabled ? { run_in_background: {
432
+ type: "boolean",
433
+ 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."
434
+ } } : {}
435
+ },
436
+ output: {
437
+ schema: { oneOf: [
438
+ {
439
+ type: "object",
440
+ additionalProperties: false,
441
+ properties: {
442
+ kind: {
443
+ type: "string",
444
+ required: true,
445
+ const: "background"
446
+ },
447
+ jobId: {
448
+ type: "string",
449
+ required: true
450
+ }
451
+ }
452
+ },
453
+ {
454
+ type: "object",
455
+ additionalProperties: false,
456
+ properties: {
457
+ kind: {
458
+ type: "string",
459
+ required: true,
460
+ const: "continuable"
461
+ },
462
+ subagentId: {
463
+ type: "string",
464
+ required: true
465
+ }
466
+ }
467
+ },
468
+ {
469
+ type: "object",
470
+ additionalProperties: false,
471
+ properties: {
472
+ kind: {
473
+ type: "string",
474
+ required: true,
475
+ const: "foreground"
476
+ },
477
+ runId: {
478
+ type: "string",
479
+ required: true
480
+ },
481
+ output: {
482
+ type: "array",
483
+ required: true,
484
+ items: { type: "json" }
485
+ }
486
+ }
487
+ }
488
+ ] },
489
+ render: (_args, value) => [{
490
+ type: "text",
491
+ text: value.kind === "background" ? `started background subagent job ${value.jobId}` : value.kind === "continuable" ? `started subagent ${value.subagentId}` : outputValueText(value.output)
492
+ }]
493
+ },
494
+ isConcurrencySafe: () => true,
495
+ async execute(args, exec) {
496
+ const parent = exec.agent;
497
+ if (!parent) throw new Error("subagent tool requires a calling agent (exec.agent was undefined)");
498
+ const modelRequest = args;
499
+ const parentOptions = parentAgentOptionsForDelegation(parent);
500
+ const requiresRoutePreflight = hasDelegationModelRequest(modelRequest) || hasConfiguredLlmSelection(config.agentOptions);
501
+ const requestedChildAgentOptions = requestedAgentOptions(parentOptions, requiresRoutePreflight && providerRouteDefaults !== void 0 ? {
502
+ ...providerRouteDefaults,
503
+ ...config.agentOptions
504
+ } : config.agentOptions, modelRequest, modelSelectionEnabled || subagentProvider.capabilities.agentOptions && modelRequest.provider === void 0 && modelRequest.model === void 0);
505
+ if (modelRequest.provider !== void 0 || modelRequest.model !== void 0) assertAllowedModelSelection(modelSelectionPolicy, parentOptions, requestedChildAgentOptions, modelRequest);
506
+ if (requiresRoutePreflight) {
507
+ const llm = runtimeCtx.get("llm");
508
+ if (llm === void 0) throw new Error("cannot resolve the selected child LLM route because the `llm` service is unavailable");
509
+ await preflightChildLlmRoute(llm, parentOptions, requestedChildAgentOptions, exec.signal, providerRouteDefaults === void 0);
510
+ if (runtimeCtx.subagents.getProvider(config.provider) !== subagentProvider) throw new Error(`subagent provider "${config.provider}" changed while resolving the child LLM route; retry the delegation`);
511
+ }
512
+ exec.signal.throwIfAborted();
513
+ const maxDepth = typeof config.maxDepth === "number" ? config.maxDepth : void 0;
514
+ const request = {
515
+ label: args.description,
516
+ prompt: [{
517
+ type: "text",
518
+ text: args.prompt
519
+ }],
520
+ parent,
521
+ ...requestedChildAgentOptions !== void 0 ? { agentOptions: requestedChildAgentOptions } : {},
522
+ ...config.persona !== void 0 ? { persona: config.persona } : {},
523
+ ...config.toolFilter !== void 0 ? { toolFilter: config.toolFilter } : {},
524
+ ...maxDepth !== void 0 ? { maxDepth } : {}
525
+ };
526
+ if (resolveDelegationRun(args, {
527
+ backgroundEnabled,
528
+ continuable
529
+ }).runInBackground) {
530
+ if (continuable) return {
531
+ kind: "continuable",
532
+ subagentId: (await runtimeCtx.subagents.startContinuable({
533
+ provider: config.provider,
534
+ label: args.description,
535
+ request,
536
+ signal: exec.signal
537
+ })).childId
538
+ };
539
+ const jobs = runtimeCtx.get("jobs");
540
+ if (jobs === void 0) throw new Error("background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs");
541
+ return {
542
+ kind: "background",
543
+ jobId: jobs.start({
544
+ kind: "subagent",
545
+ label: args.description,
546
+ owner: parent,
547
+ run: () => {
548
+ const controller = new AbortController();
549
+ return {
550
+ cancel: (reason) => {
551
+ controller.abort(reason ?? "background subagent task killed");
552
+ },
553
+ done: settleStart(runtimeCtx.subagents.start(config.provider, {
554
+ ...request,
555
+ signal: controller.signal
556
+ }), controller.signal)
557
+ };
558
+ }
559
+ })
560
+ };
561
+ }
562
+ return settleForegroundRun(await runtimeCtx.subagents.start(config.provider, {
563
+ ...request,
564
+ signal: exec.signal
565
+ }));
566
+ }
567
+ }))
568
+ };
569
+ };
570
+ runtimeCtx.on("subagent/provider-added", (subagentProvider) => {
571
+ if (subagentProvider.name === config.provider && mounted === void 0) mount(subagentProvider);
572
+ });
573
+ runtimeCtx.on("subagent/provider-removed", (name) => {
574
+ if (name !== config.provider || mounted === void 0) return;
575
+ mounted.disposeTool();
576
+ mounted = void 0;
577
+ });
578
+ const present = runtimeCtx.subagents.getProvider(config.provider);
579
+ if (present !== void 0) mount(present);
580
+ else runtimeCtx.logger.info(`subagent provider "${config.provider}" not registered yet; the "${config.toolName ?? "subagent"}" tool will register when it appears`);
581
+ if (backgroundEnabled && continuable) runtimeCtx.systemPrompt.section({
582
+ name: `tool:${toolName}`,
583
+ order: runtimeCtx.systemPrompt.getSectionOrder("TOOL_SUBAGENT"),
584
+ text: (context) => mounted === void 0 || runtimeCtx.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.`
585
+ });
586
+ };
587
+ if (config.modelSelectionSettings !== true) {
588
+ install(ctx, void 0);
589
+ return;
590
+ }
591
+ const settings = ctx.get("subagentModelSelection");
592
+ if (settings === void 0) throw new Error("tool-subagent: `modelSelectionSettings` requires @deepseek-ai/dsh-tool-subagent/model-selection-settings in the Host scope");
593
+ const selectForSession = (target) => {
594
+ const freshSession = target.firstLiveSeq === 0 && target.eventAt(SessionSeq(0))?.type !== "session/end-seed";
595
+ let allowedModels = subagentModelSelectionPolicy(ctx.sessionProjections, target);
596
+ if (allowedModels === void 0) {
597
+ const parentId = target.header.origin === "subagent" ? target.header.parentSession : void 0;
598
+ if (parentId !== void 0) {
599
+ const sessions = ctx.get("sessions");
600
+ if (sessions === void 0) throw new Error("tool-subagent: child model-selection inheritance requires the Session registry");
601
+ const parent = sessions.get(parentId);
602
+ allowedModels = parent === void 0 ? void 0 : subagentModelSelectionPolicy(ctx.sessionProjections, parent);
603
+ } else if (freshSession) {
604
+ const current = settings.current();
605
+ allowedModels = current.enabled ? current.allowedModels : void 0;
606
+ }
607
+ }
608
+ if (allowedModels !== void 0) recordSubagentModelSelection(ctx.sessionProjections, target, allowedModels);
609
+ return allowedModels === void 0 ? void 0 : { routes: allowedModels };
610
+ };
611
+ if (session !== void 0) {
612
+ install(ctx, selectForSession(session));
613
+ return;
614
+ }
615
+ const compositionScope = scopeOf(ctx);
616
+ if (compositionScope === void 0) throw new Error("tool-subagent: standing `modelSelectionSettings` requires a scoped preset Context");
617
+ const agents = ctx.get("agents");
618
+ /* v8 ignore next -- shipped preset compositions always include the Agent registry. */
619
+ if (agents === void 0) throw new Error("tool-subagent: standing `modelSelectionSettings` requires the Agent registry");
620
+ const scopedInstalls = /* @__PURE__ */ new WeakMap();
621
+ const installing = /* @__PURE__ */ new WeakSet();
622
+ const belongsToComposition = (candidate) => scopeChainOf(scopeOf(candidate.ctx)).includes(compositionScope);
623
+ const installScoped = (candidate) => {
624
+ if (scopedInstalls.has(candidate) || installing.has(candidate)) return;
625
+ installing.add(candidate);
626
+ let fiber;
627
+ try {
628
+ const policy = selectForSession(candidate.session);
629
+ fiber = candidate.ctx.inject([
630
+ "tools",
631
+ "subagents",
632
+ "systemPrompt"
633
+ ], (runtimeCtx) => {
634
+ install(runtimeCtx, policy);
635
+ });
636
+ } finally {
637
+ installing.delete(candidate);
638
+ }
639
+ scopedInstalls.set(candidate, fiber);
640
+ };
641
+ const removeScoped = (candidate) => {
642
+ const fiber = scopedInstalls.get(candidate);
643
+ if (fiber === void 0) return;
644
+ scopedInstalls.delete(candidate);
645
+ /* v8 ignore next 3 -- Cordis Fiber disposal contains registration cleanup failures; this is the final diagnostic sink. */
646
+ fiber.dispose().catch((error) => {
647
+ ctx.logger.warn(`tool-subagent: failed to remove recomposed Agent "${candidate.id}" definitions: ${String(error)}`);
648
+ });
649
+ };
650
+ const reconcileComposedAgents = () => {
651
+ for (const candidate of agents.list()) if (belongsToComposition(candidate)) installScoped(candidate);
652
+ else removeScoped(candidate);
653
+ };
654
+ ctx.on("agent/created", ({ agent: created }) => {
655
+ installScoped(created);
656
+ });
657
+ ctx.on("agent/disposed", ({ agent: disposed }) => {
658
+ removeScoped(disposed);
659
+ });
660
+ ctx.on("tools/change", reconcileComposedAgents);
661
+ reconcileComposedAgents();
662
+ }
663
+ //#endregion
664
+ export { Config, apply, inject, name };