@xrkseek/harness 0.7.6 → 0.7.8
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/node_modules/@xrkseek/a2a/dist/inbound.d.ts.map +1 -1
- package/node_modules/@xrkseek/a2a/dist/inbound.js +72 -2
- package/node_modules/@xrkseek/a2a/dist/inbound.js.map +1 -1
- package/node_modules/@xrkseek/core-tools/dist/subagent-prompt.d.ts.map +1 -1
- package/node_modules/@xrkseek/core-tools/dist/subagent-prompt.js +2 -0
- package/node_modules/@xrkseek/core-tools/dist/subagent-prompt.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/README.md +4 -3
- package/node_modules/@xrkseek/exec-voice/dist/browser/index.d.ts +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/browser/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/browser/index.js +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/browser/index.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/browser/wav-capture.d.ts +91 -0
- package/node_modules/@xrkseek/exec-voice/dist/browser/wav-capture.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/browser/wav-capture.js +269 -0
- package/node_modules/@xrkseek/exec-voice/dist/browser/wav-capture.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/download-http.d.ts +46 -0
- package/node_modules/@xrkseek/exec-voice/dist/download-http.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/download-http.js +223 -0
- package/node_modules/@xrkseek/exec-voice/dist/download-http.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/engine-ids.d.ts +19 -0
- package/node_modules/@xrkseek/exec-voice/dist/engine-ids.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/engine-ids.js +36 -0
- package/node_modules/@xrkseek/exec-voice/dist/engine-ids.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/engines.d.ts +60 -0
- package/node_modules/@xrkseek/exec-voice/dist/engines.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/engines.js +130 -0
- package/node_modules/@xrkseek/exec-voice/dist/engines.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/format.d.ts +13 -0
- package/node_modules/@xrkseek/exec-voice/dist/format.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/format.js +22 -3
- package/node_modules/@xrkseek/exec-voice/dist/format.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/index.d.ts +23 -31
- package/node_modules/@xrkseek/exec-voice/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/index.js +17 -66
- package/node_modules/@xrkseek/exec-voice/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/readiness.d.ts +14 -8
- package/node_modules/@xrkseek/exec-voice/dist/readiness.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/readiness.js +36 -27
- package/node_modules/@xrkseek/exec-voice/dist/readiness.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-assets.d.ts +48 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-assets.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-assets.js +64 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-assets.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-local.d.ts +24 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-local.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-local.js +66 -4
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-local.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-runtime.d.ts +78 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-runtime.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-runtime.js +332 -0
- package/node_modules/@xrkseek/exec-voice/dist/sensevoice-runtime.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/system-tts.d.ts +40 -0
- package/node_modules/@xrkseek/exec-voice/dist/system-tts.d.ts.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/system-tts.js +260 -0
- package/node_modules/@xrkseek/exec-voice/dist/system-tts.js.map +1 -0
- package/node_modules/@xrkseek/exec-voice/dist/tools.d.ts +13 -12
- package/node_modules/@xrkseek/exec-voice/dist/tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/tools.js +12 -6
- package/node_modules/@xrkseek/exec-voice/dist/tools.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/types.d.ts +3 -1
- package/node_modules/@xrkseek/exec-voice/dist/types.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-voice/dist/types.js.map +1 -1
- package/node_modules/@xrkseek/exec-voice/package.json +39 -31
- package/node_modules/@xrkseek/preset-harness/dist/preset.d.ts +4 -3
- package/node_modules/@xrkseek/preset-harness/dist/preset.d.ts.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/preset.js +31 -18
- package/node_modules/@xrkseek/preset-harness/dist/preset.js.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/preset-minimal/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/preset-server/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/protocol/dist/sidebar-previews.d.ts +2 -0
- package/node_modules/@xrkseek/protocol/dist/sidebar-previews.d.ts.map +1 -1
- package/node_modules/@xrkseek/protocol/dist/sidebar-previews.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/adapt/subagent-notice.d.ts +21 -2
- package/node_modules/@xrkseek/server-face/dist/adapt/subagent-notice.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/adapt/subagent-notice.js +56 -7
- package/node_modules/@xrkseek/server-face/dist/adapt/subagent-notice.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js +26 -3
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js +4 -0
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/dispatch.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/dispatch.js +16 -0
- package/node_modules/@xrkseek/server-face/dist/dispatch.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/voice.d.ts +30 -0
- package/node_modules/@xrkseek/server-face/dist/handlers/voice.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/handlers/voice.js +195 -0
- package/node_modules/@xrkseek/server-face/dist/handlers/voice.js.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/model-catalog.d.ts +15 -0
- package/node_modules/@xrkseek/server-face/dist/model-catalog.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/model-catalog.js +44 -10
- package/node_modules/@xrkseek/server-face/dist/model-catalog.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.js +7 -2
- package/node_modules/@xrkseek/server-face/dist/runtime.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/select-session-model.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/select-session-model.js +68 -1
- package/node_modules/@xrkseek/server-face/dist/select-session-model.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-posture.js +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-posture.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts +4 -0
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js +16 -0
- package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-tools.js +2 -0
- package/node_modules/@xrkseek/server-face/dist/session-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-document.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-document.js +56 -1
- package/node_modules/@xrkseek/server-face/dist/settings-document.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.js +42 -12
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.d.ts +43 -0
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.js +293 -24
- package/node_modules/@xrkseek/server-face/dist/subagent-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/wire/paths.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/wire/paths.js +6 -0
- package/node_modules/@xrkseek/server-face/dist/wire/paths.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-orchestrate-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-orchestrate-tools.js +4 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-orchestrate-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/package.json +1 -0
- package/node_modules/@xrkseek/server-host/dist/a2a-inbound-public.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/a2a-inbound-public.js +8 -1
- package/node_modules/@xrkseek/server-host/dist/a2a-inbound-public.js.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.d.ts +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.js +44 -45
- package/node_modules/@xrkseek/server-host/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/sidebar-face-bridge.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/sidebar-face-bridge.js +2 -0
- package/node_modules/@xrkseek/server-host/dist/sidebar-face-bridge.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/auto-review-tool-pre.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/auto-review-tool-pre.js +5 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/auto-review-tool-pre.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.d.ts +15 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.js +61 -3
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.js.map +1 -1
- package/node_modules/sherpa-onnx-node/README.md +11 -0
- package/node_modules/sherpa-onnx-node/addon-static-import.js +76 -0
- package/node_modules/sherpa-onnx-node/addon.js +98 -0
- package/node_modules/sherpa-onnx-node/audio-tagg.js +45 -0
- package/node_modules/sherpa-onnx-node/keyword-spotter.js +68 -0
- package/node_modules/sherpa-onnx-node/non-streaming-asr.js +158 -0
- package/node_modules/sherpa-onnx-node/non-streaming-speaker-diarization.js +38 -0
- package/node_modules/sherpa-onnx-node/non-streaming-speech-denoiser.js +32 -0
- package/node_modules/sherpa-onnx-node/non-streaming-tts.js +117 -0
- package/node_modules/sherpa-onnx-node/online-speech-denoiser.js +46 -0
- package/node_modules/sherpa-onnx-node/package.json +68 -0
- package/node_modules/sherpa-onnx-node/punctuation.js +43 -0
- package/node_modules/sherpa-onnx-node/resampler.js +80 -0
- package/node_modules/sherpa-onnx-node/sherpa-onnx.js +49 -0
- package/node_modules/sherpa-onnx-node/speaker-identification.js +152 -0
- package/node_modules/sherpa-onnx-node/spoken-language-identification.js +38 -0
- package/node_modules/sherpa-onnx-node/streaming-asr.js +181 -0
- package/node_modules/sherpa-onnx-node/types.js +833 -0
- package/node_modules/sherpa-onnx-node/vad.js +134 -0
- package/node_modules/sherpa-onnx-win-x64/README.md +3 -0
- package/node_modules/sherpa-onnx-win-x64/index.js +1 -0
- package/node_modules/sherpa-onnx-win-x64/onnxruntime.dll +0 -0
- package/node_modules/sherpa-onnx-win-x64/onnxruntime_providers_shared.dll +0 -0
- package/node_modules/sherpa-onnx-win-x64/package.json +65 -0
- package/node_modules/sherpa-onnx-win-x64/sherpa-onnx-c-api.dll +0 -0
- package/node_modules/sherpa-onnx-win-x64/sherpa-onnx-cxx-api.dll +0 -0
- package/node_modules/sherpa-onnx-win-x64/sherpa-onnx.node +0 -0
- package/package.json +5 -1
|
@@ -15,12 +15,12 @@ import { ensureSessionWorkingDirectory, resolveSessionCwd, } from "./session-cwd
|
|
|
15
15
|
import { canvasWorkspaceIdForSession } from "./canvas-tools.js";
|
|
16
16
|
import { refuseCrossAgentHomeLane } from "./agent-home-boundary.js";
|
|
17
17
|
import { orchestratorSurface } from "./session-kind.js";
|
|
18
|
-
import { closeAgentDescription, followupTaskDescription, interruptAgentDescription, listAgentsDescription, liveRosterCatalog, sendMessageDescription, sessionToolContext, subagentDescription, teamGraphDescription, waitAgentDescription, } from "./session-tool-copy.js";
|
|
18
|
+
import { closeAgentDescription, followupTaskDescription, interruptAgentDescription, listAgentsDescription, liveRosterCatalog, listModelsDescription, sendMessageDescription, sessionToolContext, setAgentModelDescription, subagentDescription, teamGraphDescription, waitAgentDescription, } from "./session-tool-copy.js";
|
|
19
19
|
import { IM_WORKSPACE_ID } from "./im-workspace.js";
|
|
20
20
|
import { resolveDefaultAgentPreset } from "./settings-document.js";
|
|
21
21
|
import { ExternalAgentError, parseExternalAgentKind, parseExternalAgentProduct, runExternalAgentTurn, supportsExternalContinuable, startExternalContinuable, isChildSessionActive, } from "./external-agent-runtime.js";
|
|
22
22
|
import { createSubagentWorktree, finalizeSubagentWorktree, formatWorktreeResult, worktreeContextNote, worktreeSkippedForRemote, } from "./subagent-worktree.js";
|
|
23
|
-
import { resolveSessionModelSelection } from "./model-catalog.js";
|
|
23
|
+
import { buildFaceModelCatalog, resolveSessionModelSelection, } from "./model-catalog.js";
|
|
24
24
|
import { modelSessionCapProblem, resolveModelSessionQuota, } from "./model-session-cap.js";
|
|
25
25
|
import { selectSessionModel } from "./select-session-model.js";
|
|
26
26
|
import { appendOutputContract, buildOutputSchemaRetryMessage, coerceOutputSchema, validateOutputAgainstSchema, } from "./agent-team-output.js";
|
|
@@ -200,20 +200,67 @@ function taskIdConflict(runtime, taskIdHint) {
|
|
|
200
200
|
? `subagent: task_id ${taskIdHint} is already bound to child ${boundChild}; use a different task_id`
|
|
201
201
|
: undefined;
|
|
202
202
|
}
|
|
203
|
+
/** `provider/model · effort x` for tool receipts. */
|
|
204
|
+
function routeLabel(selection) {
|
|
205
|
+
const route = `${selection.provider}/${selection.model}`;
|
|
206
|
+
return selection.reasoningEffort
|
|
207
|
+
? `${route} · effort ${selection.reasoningEffort}`
|
|
208
|
+
: route;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Receipt line for send_message / followup_task / set_agent_model: what the
|
|
212
|
+
* child is **actually** on after the pin, not what was merely requested.
|
|
213
|
+
*/
|
|
214
|
+
export function formatPinnedRouteReceipt(childSessionId, pin) {
|
|
215
|
+
const routeMoved = pin.before.provider !== pin.after.provider ||
|
|
216
|
+
pin.before.model !== pin.after.model;
|
|
217
|
+
if (routeMoved) {
|
|
218
|
+
return `route: ${pin.before.provider}/${pin.before.model} → ${routeLabel(pin.after)}`;
|
|
219
|
+
}
|
|
220
|
+
if (pin.changed) {
|
|
221
|
+
// Effort-only switch: the route stays, the thinking level moves.
|
|
222
|
+
const from = pin.before.reasoningEffort ?? "default";
|
|
223
|
+
const to = pin.after.reasoningEffort ?? "default";
|
|
224
|
+
return `route: ${pin.after.provider}/${pin.after.model} (unchanged) · effort ${from} → ${to}`;
|
|
225
|
+
}
|
|
226
|
+
return `route: ${childSessionId} already on ${routeLabel(pin.after)} (unchanged)`;
|
|
227
|
+
}
|
|
203
228
|
/**
|
|
204
|
-
*
|
|
229
|
+
* Pin an **existing** child onto `provider/model` + `reasoning_effort`.
|
|
205
230
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
231
|
+
* Hot switch: `createSessionRoutingLlm` re-reads `sessionModels` on every
|
|
232
|
+
* model request, so a running child picks the new route on its next request
|
|
233
|
+
* (the in-flight one is untouched — never restart a turn to switch).
|
|
234
|
+
*
|
|
235
|
+
* Authorization is the lineage link: a session may only repin a child it
|
|
236
|
+
* spawned. `reasoningEffort` alone keeps the child's current provider/model;
|
|
237
|
+
* `model` alone keeps the child's current provider. External runtimes own
|
|
238
|
+
* their model, so any Face-side pin there is refused rather than faked.
|
|
209
239
|
*/
|
|
210
|
-
async function
|
|
240
|
+
export async function pinChildRoute(runtime, args) {
|
|
241
|
+
const childSessionId = args.childSessionId.trim();
|
|
242
|
+
if (!runtime.store.has(childSessionId)) {
|
|
243
|
+
return {
|
|
244
|
+
ok: false,
|
|
245
|
+
message: `subagent: child session ${childSessionId} not found`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (!runtime.subagents.get(args.parentSessionId, childSessionId)) {
|
|
249
|
+
return {
|
|
250
|
+
ok: false,
|
|
251
|
+
message: `subagent: ${childSessionId} is not your child — ` +
|
|
252
|
+
"only a session this one spawned can be repinned",
|
|
253
|
+
};
|
|
254
|
+
}
|
|
211
255
|
const providerRaw = args.provider?.trim() ?? "";
|
|
212
256
|
const modelRaw = args.model?.trim() ?? "";
|
|
213
257
|
const effortRaw = args.reasoningEffort?.trim() ?? "";
|
|
258
|
+
const external = args.skipExternal === true ||
|
|
259
|
+
runtime.externalAgents.has(childSessionId) ||
|
|
260
|
+
runtime.externalAgents.kind(childSessionId) !== undefined;
|
|
214
261
|
// Refuse loudly rather than silently: an explicit model on an external
|
|
215
262
|
// child would otherwise read as honored when the subprocess ignores it.
|
|
216
|
-
if (
|
|
263
|
+
if (external && (providerRaw || modelRaw || effortRaw)) {
|
|
217
264
|
return {
|
|
218
265
|
ok: false,
|
|
219
266
|
message: "subagent: model / provider / reasoning_effort do not apply to an external runtime " +
|
|
@@ -221,22 +268,23 @@ async function applySubagentModelOverride(runtime, args) {
|
|
|
221
268
|
"Drop the argument, or use runtime=in-process.",
|
|
222
269
|
};
|
|
223
270
|
}
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
if (!providerRaw && !modelRaw && !effortRaw)
|
|
227
|
-
return { ok: true };
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
271
|
+
const before = resolveSessionModelSelection(runtime, childSessionId);
|
|
272
|
+
// No pin requested: report the route the child is already on.
|
|
273
|
+
if (!providerRaw && !modelRaw && !effortRaw) {
|
|
274
|
+
return { ok: true, before, after: before, changed: false };
|
|
275
|
+
}
|
|
276
|
+
const provider = providerRaw || before.provider;
|
|
277
|
+
const model = modelRaw || before.model;
|
|
278
|
+
const effort = effortRaw || before.reasoningEffort || "";
|
|
279
|
+
if (!provider || !model) {
|
|
231
280
|
return {
|
|
232
281
|
ok: false,
|
|
233
|
-
message:
|
|
282
|
+
message: `subagent: ${childSessionId} has no pinned provider/model yet — ` +
|
|
283
|
+
"pass both (list_models shows routable pairs)",
|
|
234
284
|
};
|
|
235
285
|
}
|
|
236
|
-
const provider = providerRaw
|
|
237
|
-
|| resolveSessionModelSelection(runtime, args.parentSessionId).provider;
|
|
238
286
|
const selected = await selectSessionModel(runtime, {
|
|
239
|
-
sessionId:
|
|
287
|
+
sessionId: childSessionId,
|
|
240
288
|
provider,
|
|
241
289
|
model,
|
|
242
290
|
...(effort ? { reasoningEffort: effort } : {}),
|
|
@@ -247,7 +295,40 @@ async function applySubagentModelOverride(runtime, args) {
|
|
|
247
295
|
message: `subagent model: ${formatToolError(selected.error)}`,
|
|
248
296
|
};
|
|
249
297
|
}
|
|
250
|
-
|
|
298
|
+
const after = selected.value.selected;
|
|
299
|
+
const changed = before.provider !== after.provider ||
|
|
300
|
+
before.model !== after.model ||
|
|
301
|
+
(before.reasoningEffort ?? "") !== (after.reasoningEffort ?? "");
|
|
302
|
+
return { ok: true, before, after, changed };
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Optional LLM target for a freshly created child (thin spawn wrapper).
|
|
306
|
+
*
|
|
307
|
+
* Precedence: the call's own `provider` / `model` / `reasoning_effort` →
|
|
308
|
+
* the route `session.create` already copied from the parent.
|
|
309
|
+
*/
|
|
310
|
+
async function applySubagentModelOverride(runtime, args) {
|
|
311
|
+
const pinned = await pinChildRoute(runtime, args);
|
|
312
|
+
return pinned.ok ? { ok: true } : { ok: false, message: pinned.message };
|
|
313
|
+
}
|
|
314
|
+
/** Spawn receipt line — the child's **actual** route, never the prediction. */
|
|
315
|
+
function childRouteReceipt(runtime, childSessionId) {
|
|
316
|
+
const selection = resolveSessionModelSelection(runtime, childSessionId);
|
|
317
|
+
return selection.provider && selection.model
|
|
318
|
+
? `route: ${routeLabel(selection)}`
|
|
319
|
+
: "route: unpinned — the child resolves provider/model per request";
|
|
320
|
+
}
|
|
321
|
+
/** Only strings reach the pin; anything else stays an "argument omitted". */
|
|
322
|
+
function routeArgsFrom(a) {
|
|
323
|
+
const out = {};
|
|
324
|
+
if (typeof a.provider === "string")
|
|
325
|
+
out.provider = a.provider;
|
|
326
|
+
if (typeof a.model === "string")
|
|
327
|
+
out.model = a.model;
|
|
328
|
+
if (typeof a.reasoning_effort === "string") {
|
|
329
|
+
out.reasoningEffort = a.reasoning_effort;
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
251
332
|
}
|
|
252
333
|
function createSubagentTool(options) {
|
|
253
334
|
const foregroundWaitMs = options.foregroundWaitMs ?? FOREGROUND_WAIT_MS;
|
|
@@ -609,6 +690,7 @@ function createSubagentTool(options) {
|
|
|
609
690
|
(spawnRole && spawnRole !== "default"
|
|
610
691
|
? ` · role ${spawnRole}`
|
|
611
692
|
: ""),
|
|
693
|
+
`route: external (${runtimeKind}) · child CLI owns the model`,
|
|
612
694
|
childResumeHint(childId),
|
|
613
695
|
].join("\n"),
|
|
614
696
|
};
|
|
@@ -625,7 +707,8 @@ function createSubagentTool(options) {
|
|
|
625
707
|
: {}),
|
|
626
708
|
});
|
|
627
709
|
return {
|
|
628
|
-
content: `[external:${result.kind}]\n${result.text}
|
|
710
|
+
content: `[external:${result.kind}]\n${result.text}\n` +
|
|
711
|
+
`route: external (${result.kind}) · child CLI owns the model`,
|
|
629
712
|
};
|
|
630
713
|
}
|
|
631
714
|
catch (err) {
|
|
@@ -988,6 +1071,7 @@ function createSubagentTool(options) {
|
|
|
988
1071
|
outputSchema
|
|
989
1072
|
? "OUTPUT CONTRACT attached; result will be schema-checked on idle."
|
|
990
1073
|
: undefined,
|
|
1074
|
+
childRouteReceipt(options.runtime, childId),
|
|
991
1075
|
childResumeHint(childId),
|
|
992
1076
|
worktreeLine(true) || undefined,
|
|
993
1077
|
]
|
|
@@ -1111,7 +1195,11 @@ function createSubagentTool(options) {
|
|
|
1111
1195
|
const extra = worktreeLine(isChildSessionActive(options.runtime, childId));
|
|
1112
1196
|
if (!text) {
|
|
1113
1197
|
return {
|
|
1114
|
-
content: [
|
|
1198
|
+
content: [
|
|
1199
|
+
`(subagent ${childId} finished with no assistant text)`,
|
|
1200
|
+
childRouteReceipt(options.runtime, childId),
|
|
1201
|
+
extra,
|
|
1202
|
+
]
|
|
1115
1203
|
.filter(Boolean)
|
|
1116
1204
|
.join("\n"),
|
|
1117
1205
|
};
|
|
@@ -1125,6 +1213,7 @@ function createSubagentTool(options) {
|
|
|
1125
1213
|
content: [
|
|
1126
1214
|
boundChildAnswer(options.parentSessionId, childId, text),
|
|
1127
1215
|
schemaLine,
|
|
1216
|
+
childRouteReceipt(options.runtime, childId),
|
|
1128
1217
|
extra,
|
|
1129
1218
|
]
|
|
1130
1219
|
.filter(Boolean)
|
|
@@ -1204,6 +1293,18 @@ function createSendMessageTool(options) {
|
|
|
1204
1293
|
type: "string",
|
|
1205
1294
|
description: "steer (default) or queue.",
|
|
1206
1295
|
},
|
|
1296
|
+
provider: {
|
|
1297
|
+
type: "string",
|
|
1298
|
+
description: "Optional route repin before the prompt; defaults to the child's current provider.",
|
|
1299
|
+
},
|
|
1300
|
+
model: {
|
|
1301
|
+
type: "string",
|
|
1302
|
+
description: "Optional model repin before the prompt; needs provider unless the child is pinned.",
|
|
1303
|
+
},
|
|
1304
|
+
reasoning_effort: {
|
|
1305
|
+
type: "string",
|
|
1306
|
+
description: "Optional thinking level for the repinned route (keeps provider/model when alone).",
|
|
1307
|
+
},
|
|
1207
1308
|
},
|
|
1208
1309
|
required: ["agent_id", "message"],
|
|
1209
1310
|
},
|
|
@@ -1218,6 +1319,15 @@ function createSendMessageTool(options) {
|
|
|
1218
1319
|
};
|
|
1219
1320
|
}
|
|
1220
1321
|
const delivery = a.delivery === "queue" || a.delivery === "steer" ? a.delivery : "steer";
|
|
1322
|
+
// Repin first: a refused pin must send nothing (the caller believed the
|
|
1323
|
+
// child moved; delivering on the old quota is exactly the waste to avoid).
|
|
1324
|
+
const pin = await pinChildRoute(options.runtime, {
|
|
1325
|
+
parentSessionId: options.parentSessionId,
|
|
1326
|
+
childSessionId: childId,
|
|
1327
|
+
...routeArgsFrom(a),
|
|
1328
|
+
});
|
|
1329
|
+
if (!pin.ok)
|
|
1330
|
+
return { content: pin.message, isError: true };
|
|
1221
1331
|
const prompted = await dispatchFaceMethod(options.runtime, "subagent.prompt", `tool-sa-sm-${childId}`, {
|
|
1222
1332
|
parentSessionId: options.parentSessionId,
|
|
1223
1333
|
childSessionId: childId,
|
|
@@ -1234,7 +1344,12 @@ function createSendMessageTool(options) {
|
|
|
1234
1344
|
}
|
|
1235
1345
|
// Task-board resume is owned by `subagent.prompt` (the SoT for every
|
|
1236
1346
|
// delivery route); calling it again here double-bumped the revision.
|
|
1237
|
-
return {
|
|
1347
|
+
return {
|
|
1348
|
+
content: [
|
|
1349
|
+
`sent to ${childId} (${delivery})`,
|
|
1350
|
+
formatPinnedRouteReceipt(childId, pin),
|
|
1351
|
+
].join("\n"),
|
|
1352
|
+
};
|
|
1238
1353
|
},
|
|
1239
1354
|
};
|
|
1240
1355
|
}
|
|
@@ -1446,6 +1561,18 @@ function createFollowupTaskTool(options) {
|
|
|
1446
1561
|
type: "string",
|
|
1447
1562
|
description: "New task text for the child (triggers a turn).",
|
|
1448
1563
|
},
|
|
1564
|
+
provider: {
|
|
1565
|
+
type: "string",
|
|
1566
|
+
description: "Optional route repin before the turn; defaults to the child's current provider.",
|
|
1567
|
+
},
|
|
1568
|
+
model: {
|
|
1569
|
+
type: "string",
|
|
1570
|
+
description: "Optional model repin before the turn; needs provider unless the child is pinned.",
|
|
1571
|
+
},
|
|
1572
|
+
reasoning_effort: {
|
|
1573
|
+
type: "string",
|
|
1574
|
+
description: "Optional thinking level for the repinned route (keeps provider/model when alone).",
|
|
1575
|
+
},
|
|
1449
1576
|
},
|
|
1450
1577
|
required: ["agent_id", "message"],
|
|
1451
1578
|
},
|
|
@@ -1459,6 +1586,15 @@ function createFollowupTaskTool(options) {
|
|
|
1459
1586
|
isError: true,
|
|
1460
1587
|
};
|
|
1461
1588
|
}
|
|
1589
|
+
// Repin first: an out-of-quota child must not be woken on the dead
|
|
1590
|
+
// route — that is the whole "manager rescues the child" flow.
|
|
1591
|
+
const pin = await pinChildRoute(options.runtime, {
|
|
1592
|
+
parentSessionId: options.parentSessionId,
|
|
1593
|
+
childSessionId: childId,
|
|
1594
|
+
...routeArgsFrom(a),
|
|
1595
|
+
});
|
|
1596
|
+
if (!pin.ok)
|
|
1597
|
+
return { content: pin.message, isError: true };
|
|
1462
1598
|
const prompted = await dispatchFaceMethod(options.runtime, "subagent.prompt", `tool-sa-fu-${childId}`, {
|
|
1463
1599
|
parentSessionId: options.parentSessionId,
|
|
1464
1600
|
childSessionId: childId,
|
|
@@ -1473,7 +1609,138 @@ function createFollowupTaskTool(options) {
|
|
|
1473
1609
|
isError: true,
|
|
1474
1610
|
};
|
|
1475
1611
|
}
|
|
1476
|
-
return {
|
|
1612
|
+
return {
|
|
1613
|
+
content: [
|
|
1614
|
+
`followup_task sent to ${childId} (steer)`,
|
|
1615
|
+
formatPinnedRouteReceipt(childId, pin),
|
|
1616
|
+
].join("\n"),
|
|
1617
|
+
};
|
|
1618
|
+
},
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
/** Total output lines for `list_models` — the catalog can hold hundreds of routes. */
|
|
1622
|
+
const LIST_MODELS_MAX_LINES = 80;
|
|
1623
|
+
/**
|
|
1624
|
+
* Parent repairs a child's route: the escape hatch when the child's model ran
|
|
1625
|
+
* out of quota mid-task. Hot switch — the next model request uses it.
|
|
1626
|
+
*/
|
|
1627
|
+
function createSetAgentModelTool(options) {
|
|
1628
|
+
return {
|
|
1629
|
+
name: "set_agent_model",
|
|
1630
|
+
description: "Repin a child subagent's provider / model / reasoning effort (own children only).",
|
|
1631
|
+
dynamicSchema: () => ({
|
|
1632
|
+
description: setAgentModelDescription(),
|
|
1633
|
+
}),
|
|
1634
|
+
parameters: {
|
|
1635
|
+
type: "object",
|
|
1636
|
+
properties: {
|
|
1637
|
+
agent_id: {
|
|
1638
|
+
type: "string",
|
|
1639
|
+
description: "Child session id from subagent / list_agents.",
|
|
1640
|
+
},
|
|
1641
|
+
provider: {
|
|
1642
|
+
type: "string",
|
|
1643
|
+
description: "Provider id; omit to keep the child's current provider.",
|
|
1644
|
+
},
|
|
1645
|
+
model: {
|
|
1646
|
+
type: "string",
|
|
1647
|
+
description: "Model id; omit to keep the child's current model.",
|
|
1648
|
+
},
|
|
1649
|
+
reasoning_effort: {
|
|
1650
|
+
type: "string",
|
|
1651
|
+
description: "Thinking level id; omit to keep the child's current effort.",
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1654
|
+
required: ["agent_id"],
|
|
1655
|
+
},
|
|
1656
|
+
async execute(args) {
|
|
1657
|
+
const a = args;
|
|
1658
|
+
const childId = String(a.agent_id ?? "").trim();
|
|
1659
|
+
if (!childId) {
|
|
1660
|
+
return { content: "set_agent_model requires agent_id", isError: true };
|
|
1661
|
+
}
|
|
1662
|
+
const route = routeArgsFrom(a);
|
|
1663
|
+
if (!route.provider && !route.model && !route.reasoningEffort) {
|
|
1664
|
+
return {
|
|
1665
|
+
content: "set_agent_model: pass at least one of provider / model / reasoning_effort " +
|
|
1666
|
+
"(list_models shows routable pairs, efforts and free slots)",
|
|
1667
|
+
isError: true,
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
const pin = await pinChildRoute(options.runtime, {
|
|
1671
|
+
parentSessionId: options.parentSessionId,
|
|
1672
|
+
childSessionId: childId,
|
|
1673
|
+
...route,
|
|
1674
|
+
});
|
|
1675
|
+
if (!pin.ok)
|
|
1676
|
+
return { content: pin.message, isError: true };
|
|
1677
|
+
return {
|
|
1678
|
+
content: [
|
|
1679
|
+
formatPinnedRouteReceipt(childId, pin),
|
|
1680
|
+
"applies to the child's next model request — a live turn is not interrupted",
|
|
1681
|
+
].join("\n"),
|
|
1682
|
+
};
|
|
1683
|
+
},
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
/** Compact routable catalog: pick a child route (+ effort) and read slot use. */
|
|
1687
|
+
function createListModelsTool(options) {
|
|
1688
|
+
return {
|
|
1689
|
+
name: "list_models",
|
|
1690
|
+
description: "List routable models with efforts and live session slots (read-only).",
|
|
1691
|
+
dynamicSchema: () => ({
|
|
1692
|
+
description: listModelsDescription(),
|
|
1693
|
+
}),
|
|
1694
|
+
parameters: { type: "object", properties: {} },
|
|
1695
|
+
isConcurrencySafe: () => true,
|
|
1696
|
+
async execute() {
|
|
1697
|
+
const runtime = options.runtime;
|
|
1698
|
+
const here = resolveSessionModelSelection(runtime, options.parentSessionId);
|
|
1699
|
+
const { groups } = buildFaceModelCatalog(runtime);
|
|
1700
|
+
const total = groups.reduce((n, group) => n + group.models.length, 0);
|
|
1701
|
+
const header = [
|
|
1702
|
+
`models ${total}`,
|
|
1703
|
+
...(here.provider && here.model ? [`here ${routeLabel(here)}`] : []),
|
|
1704
|
+
].join(" · ");
|
|
1705
|
+
if (total === 0) {
|
|
1706
|
+
return {
|
|
1707
|
+
content: `${header}\n` +
|
|
1708
|
+
"(no routable models — add a provider in Settings → Models, then retry)",
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
// 1 header line is always shown; a truncated tail line ("… N more")
|
|
1712
|
+
// eats one more, so the whole reply never exceeds the line budget.
|
|
1713
|
+
const rowBudget = LIST_MODELS_MAX_LINES - 1;
|
|
1714
|
+
const truncated = total > rowBudget;
|
|
1715
|
+
const rowCap = truncated ? rowBudget - 1 : rowBudget;
|
|
1716
|
+
const rows = [];
|
|
1717
|
+
outer: for (const group of groups) {
|
|
1718
|
+
for (const entry of group.models) {
|
|
1719
|
+
if (rows.length >= rowCap)
|
|
1720
|
+
break outer;
|
|
1721
|
+
const bits = [];
|
|
1722
|
+
const efforts = entry.reasoning?.efforts.map((effort) => effort.id);
|
|
1723
|
+
if (efforts && efforts.length > 0) {
|
|
1724
|
+
bits.push(`efforts: ${efforts.join("|")}`);
|
|
1725
|
+
}
|
|
1726
|
+
const defaultEffort = entry.reasoning?.defaultEffort;
|
|
1727
|
+
if (defaultEffort)
|
|
1728
|
+
bits.push(`default ${defaultEffort}`);
|
|
1729
|
+
const quota = resolveModelSessionQuota(runtime, {
|
|
1730
|
+
provider: group.id,
|
|
1731
|
+
model: entry.id,
|
|
1732
|
+
});
|
|
1733
|
+
bits.push(`slots ${quota.using}/${quota.max}`);
|
|
1734
|
+
if (here.provider === group.id && here.model === entry.id) {
|
|
1735
|
+
bits.push("here");
|
|
1736
|
+
}
|
|
1737
|
+
rows.push(`- ${group.id}/${entry.id} ${bits.join(" ")}`);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
const lines = [header, ...rows];
|
|
1741
|
+
if (total > rows.length)
|
|
1742
|
+
lines.push(`… ${total - rows.length} more`);
|
|
1743
|
+
return { content: lines.join("\n") };
|
|
1477
1744
|
},
|
|
1478
1745
|
};
|
|
1479
1746
|
}
|
|
@@ -1894,7 +2161,9 @@ export function bindSubagentTools(tools, options) {
|
|
|
1894
2161
|
for (const tool of [
|
|
1895
2162
|
createSubagentTool(options),
|
|
1896
2163
|
createListAgentsTool(options),
|
|
2164
|
+
createListModelsTool(options),
|
|
1897
2165
|
createSendMessageTool(options),
|
|
2166
|
+
createSetAgentModelTool(options),
|
|
1898
2167
|
createTeamGraphTool(options),
|
|
1899
2168
|
createFollowupTaskTool(options),
|
|
1900
2169
|
createWaitAgentTool(options),
|