@tangle-network/agent-runtime 0.111.0 → 0.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activation-CsuHCZVN.js → activation-Cdc_tRNR.js} +2 -2
- package/dist/{activation-CsuHCZVN.js.map → activation-Cdc_tRNR.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{environment-provider-CTEMEP9u.d.ts → environment-provider-Cl97VeCv.d.ts} +43 -2
- package/dist/environment-provider.d.ts +1 -1
- package/dist/{improvement-cycle-ryAyCLKN.js → improvement-cycle-BCjD-g_C.js} +3 -3
- package/dist/{improvement-cycle-ryAyCLKN.js.map → improvement-cycle-BCjD-g_C.js.map} +1 -1
- package/dist/{index-DlLPNELY.d.ts → index-B0sAaAO_.d.ts} +144 -17
- package/dist/{index-BmI2uxy5.d.ts → index-B6glsqBp.d.ts} +3 -3
- package/dist/{index-sLuBoGud.d.ts → index-C9vg2af7.d.ts} +4 -4
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/intelligence.d.ts +1 -1
- package/dist/intelligence.js +2 -2
- package/dist/kernel.d.ts +3 -3
- package/dist/kernel.js +5 -5
- package/dist/{knowledge-DBNAteaC.js → knowledge-In_1LsHg.js} +3 -3
- package/dist/{knowledge-DBNAteaC.js.map → knowledge-In_1LsHg.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-vRo5TZt_.d.ts → loop-runner-bin-CHnWCyiB.d.ts} +3 -3
- package/dist/{loop-runner-bin-BLOckrqT.js → loop-runner-bin-DF98VMqr.js} +3 -3
- package/dist/{loop-runner-bin-BLOckrqT.js.map → loop-runner-bin-DF98VMqr.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-st-hERvA.js → openai-tools-g5FS2twc.js} +2 -2
- package/dist/{openai-tools-st-hERvA.js.map → openai-tools-g5FS2twc.js.map} +1 -1
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{runtime-fwz-erxT.js → runtime-6sL2vhTQ.js} +4 -4
- package/dist/{runtime-fwz-erxT.js.map → runtime-6sL2vhTQ.js.map} +1 -1
- package/dist/{structural-rollout-CgnNy7_4.js → structural-rollout-D5GbH3cy.js} +2 -2
- package/dist/{structural-rollout-CgnNy7_4.js.map → structural-rollout-D5GbH3cy.js.map} +1 -1
- package/dist/{supervise-CeZtA1wu.js → supervise-6jSTe0kO.js} +322 -30
- package/dist/supervise-6jSTe0kO.js.map +1 -0
- package/dist/{supervisor-ByCPHcp9.js → supervisor-B4hOPKJV.js} +48 -6
- package/dist/{supervisor-ByCPHcp9.js.map → supervisor-B4hOPKJV.js.map} +1 -1
- package/dist/testing.js +8 -8
- package/package.json +1 -1
- package/dist/supervise-CeZtA1wu.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { c as RuntimeRunStateError, i as ConfigError, o as NotFoundError, t as AgentEvalError$1, u as ValidationError } from "./errors-DEAvWQPy.js";
|
|
2
2
|
import { a as mapWithConcurrency, c as sleep, d as throwIfAborted, f as withTimeout, l as stringifySafe, n as addTokenUsage, o as randomSuffix, p as zeroTokenUsage, r as deleteBoxSafe, s as randomUuid, t as abortError, u as throwAbort } from "./util-Cc9g9Y-o.js";
|
|
3
3
|
import { a as contentAddress, i as InMemorySpawnJournal, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore } from "./spawn-journal-DLhnom3e.js";
|
|
4
|
-
import { A as createWorktree, C as routerChatWithUsage, D as runWorktreeChecks, O as runWorktreeHarness, T as runBrainLoop, a as runFinalizer, i as pickBestDelivered, j as removeWorktree, k as captureWorktreeDiff, l as withDriverExecutor, n as bestDelivered, o as runTree, t as createSupervisor, x as routerBrain, y as createActivityLog } from "./supervisor-
|
|
4
|
+
import { A as createWorktree, C as routerChatWithUsage, D as runWorktreeChecks, O as runWorktreeHarness, T as runBrainLoop, a as runFinalizer, i as pickBestDelivered, j as removeWorktree, k as captureWorktreeDiff, l as withDriverExecutor, n as bestDelivered, o as runTree, t as createSupervisor, x as routerBrain, y as createActivityLog } from "./supervisor-B4hOPKJV.js";
|
|
5
5
|
import { i as notifyRuntimeHookEvent } from "./runtime-hooks-C7iJOWm3.js";
|
|
6
6
|
import { a as notifySandboxEventObserver, n as extractLlmCallEvent } from "./sandbox-events-DeI5xX8P.js";
|
|
7
7
|
import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, s as buildBackendOptions } from "./environment-provider-BsHpW4Dp.js";
|
|
@@ -9,6 +9,7 @@ import { r as buildLoopSpanNodes } from "./otel-export-D34IQW1C.js";
|
|
|
9
9
|
import { t as createStdioToolServer } from "./tool-server-RcWgLIsL.js";
|
|
10
10
|
import { n as UI_LENSES } from "./substrate-B0TYNrXn.js";
|
|
11
11
|
import { argHash, computeFindingId, errorStreakDetector, estimateCost, isModelPriced, makeFinding, observeAll, repeatedActionDetector } from "@tangle-network/agent-eval";
|
|
12
|
+
import { agentProfileSchema } from "@tangle-network/agent-interface";
|
|
12
13
|
import { randomUUID } from "node:crypto";
|
|
13
14
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
14
15
|
import path, { dirname } from "node:path";
|
|
@@ -3956,6 +3957,15 @@ function createEventBus(now = Date.now) {
|
|
|
3956
3957
|
}
|
|
3957
3958
|
//#endregion
|
|
3958
3959
|
//#region src/mcp/tools/coordination.ts
|
|
3960
|
+
/**
|
|
3961
|
+
*
|
|
3962
|
+
* MCP binding for a live `Scope`. A sandbox driver gets the same small verbs
|
|
3963
|
+
* the in-process driver has: spawn, observe, await, steer, ask/answer, analyze,
|
|
3964
|
+
* and stop. Settled outputs remain Scope artifacts; product code can project
|
|
3965
|
+
* them into any UI/report envelope it needs.
|
|
3966
|
+
*
|
|
3967
|
+
* @experimental
|
|
3968
|
+
*/
|
|
3959
3969
|
/** Default ceiling for a single `await_event` block (ms). Chosen well under any reasonable remote
|
|
3960
3970
|
* MCP client request timeout so the call returns a `pending` liveness snapshot instead of erroring;
|
|
3961
3971
|
* the supervisor re-polls until the worker settles. */
|
|
@@ -3981,6 +3991,179 @@ const idArg = {
|
|
|
3981
3991
|
type: "string",
|
|
3982
3992
|
description: "The workerId returned by spawn_agent."
|
|
3983
3993
|
};
|
|
3994
|
+
/**
|
|
3995
|
+
* Strip zod's object-KEY CODEC artifact from a derived JSON Schema, at every depth.
|
|
3996
|
+
*
|
|
3997
|
+
* `z.record(z.string(), …)` converts to `propertyNames: { type: 'string', pattern:
|
|
3998
|
+
* '^u(?:[0-9a-f]{4})*$' }` — a marker of the lossy key round-trip, not a constraint anything
|
|
3999
|
+
* enforces: `agentProfileSchema.safeParse({ name: 'r', tools: { bash: true, Read: false } })`
|
|
4000
|
+
* succeeds with those plain keys. Published verbatim it reads to a model as "every key must be a
|
|
4001
|
+
* run of hex quads", and the model either emits hex-encoded garbage keys or drops the field —
|
|
4002
|
+
* on `tools`, `permissions`, `metadata`, `mcp`, `mcp.*.env`, and `model.metadata`, which is most
|
|
4003
|
+
* of what a parent actually configures.
|
|
4004
|
+
*
|
|
4005
|
+
* Every `propertyNames` in the canonical profile schema is this artifact (25 of 25 at Interface
|
|
4006
|
+
* 0.40), and the canonical schema constrains no key by pattern, so dropping the keyword outright
|
|
4007
|
+
* loses nothing real and cannot be defeated by zod changing the encoding's exact regex.
|
|
4008
|
+
*
|
|
4009
|
+
* Rebuilds rather than mutates: the canonical conversion output must stay untouched for callers
|
|
4010
|
+
* that compare against it.
|
|
4011
|
+
*/
|
|
4012
|
+
const stripKeyCodecArtifacts = (node) => {
|
|
4013
|
+
if (Array.isArray(node)) return node.map(stripKeyCodecArtifacts);
|
|
4014
|
+
if (!node || typeof node !== "object") return node;
|
|
4015
|
+
return Object.fromEntries(Object.entries(node).filter(([key]) => key !== "propertyNames").map(([key, value]) => [key, stripKeyCodecArtifacts(value)]));
|
|
4016
|
+
};
|
|
4017
|
+
/** The canonical profile fields a spawning parent actually sets on a child, in the order a reader
|
|
4018
|
+
* needs them, each with the description published alongside it. Everything else stays legal to
|
|
4019
|
+
* pass — see {@link deriveSpawnProfileArg}.
|
|
4020
|
+
*
|
|
4021
|
+
* Why these eleven, with the measured numbers (serialized JSON bytes, Interface 0.40). The
|
|
4022
|
+
* canonical `properties` map is 10629 bytes, against 2424 bytes for every argument of every other
|
|
4023
|
+
* coordination tool COMBINED — publishing it whole makes one parameter four times the rest of the
|
|
4024
|
+
* surface a driver re-reads each turn. Of the seven omitted fields (tags, connections, subagents,
|
|
4025
|
+
* hooks, modes, confidential, extensions) none is something a parent hands a worker; `permissions`
|
|
4026
|
+
* (321 bytes) IS, so it is published — a child that must not touch the network or the filesystem
|
|
4027
|
+
* is fenced there and nowhere else. `mcp` (2663 bytes) and `resources` (3122 bytes) are the two a
|
|
4028
|
+
* parent is least likely to author inline and were together 85% of the published cost, so they
|
|
4029
|
+
* carry a brief shape plus a description naming the full form instead of the canonical sub-tree. */
|
|
4030
|
+
const spawnProfileFields = [
|
|
4031
|
+
{
|
|
4032
|
+
name: "name",
|
|
4033
|
+
description: "Short identifier for this child, e.g. \"researcher\" or \"patch-writer\". A child normally sets it to its role; it shows up in traces and worker labels."
|
|
4034
|
+
},
|
|
4035
|
+
{
|
|
4036
|
+
name: "description",
|
|
4037
|
+
description: "One line saying what this child is for. Read by humans and by a parent listing its workers."
|
|
4038
|
+
},
|
|
4039
|
+
{
|
|
4040
|
+
name: "version",
|
|
4041
|
+
description: "Optional version string for this profile, so two revisions of the same role are distinguishable in evidence. A child usually omits it."
|
|
4042
|
+
},
|
|
4043
|
+
{
|
|
4044
|
+
name: "harness",
|
|
4045
|
+
description: "Which coding backend runs the child. Omit to inherit the run default; set it only when this child needs a specific backend (e.g. a long refactor on \"claude-code\")."
|
|
4046
|
+
},
|
|
4047
|
+
{
|
|
4048
|
+
name: "model",
|
|
4049
|
+
description: "Model routing: `default` model id, optional `small` for cheap sub-calls, `provider`, and `reasoningEffort`. A child typically sets `default` and `reasoningEffort` — raise effort for a hard reasoning task, lower it for bulk mechanical work."
|
|
4050
|
+
},
|
|
4051
|
+
{
|
|
4052
|
+
name: "prompt",
|
|
4053
|
+
description: "The child's standing instructions: `systemPrompt` (who it is and how it works) and `instructions` (durable rules). This is the role; the separate `task` argument carries what to do right now. A child with no systemPrompt has no role — always set one."
|
|
4054
|
+
},
|
|
4055
|
+
{
|
|
4056
|
+
name: "tools",
|
|
4057
|
+
description: "Per-tool on/off map keyed by tool name, e.g. `{ \"bash\": true, \"webfetch\": false }`. Omit to inherit the backend's default tool set; set it to narrow a child to the tools its task needs. Keys are plain tool names."
|
|
4058
|
+
},
|
|
4059
|
+
{
|
|
4060
|
+
name: "permissions",
|
|
4061
|
+
description: "Per-tool permission decisions: each tool name maps to \"allow\", \"deny\", or \"ask\", or to a nested map of finer-grained rules. This is where a parent fences a child it does not fully trust — a read-only child denies write and network tools here, not in `tools`."
|
|
4062
|
+
},
|
|
4063
|
+
{
|
|
4064
|
+
name: "mcp",
|
|
4065
|
+
description: "MCP tool servers to mount for the child, keyed by server name. Brief form: each value is either `{ transport: \"stdio\", command, args?, env?, cwd? }` or `{ transport: \"sse\" | \"http\", url, headers? }`. A child normally sets this only when it needs a tool server the task requires; the canonical AgentProfile schema carries the full form (secret-ref values for env/headers, per-server metadata) and governs validation.",
|
|
4066
|
+
brief: {
|
|
4067
|
+
type: "object",
|
|
4068
|
+
additionalProperties: { type: "object" }
|
|
4069
|
+
}
|
|
4070
|
+
},
|
|
4071
|
+
{
|
|
4072
|
+
name: "resources",
|
|
4073
|
+
description: "Files, tools, skills, and agents materialized into the child workspace before it starts. Brief form: `{ files?, tools?, skills?, agents? }`, each an array whose entries are `{ kind: \"inline\", name, content }` or `{ kind: \"github\", repository?, path, ref? }` (`files` entries wrap that as `{ path, resource, executable? }`). A child typically gets `files` for seed inputs and `skills` for a procedure it must follow; the canonical AgentProfile schema carries the full form and governs validation.",
|
|
4074
|
+
brief: {
|
|
4075
|
+
type: "object",
|
|
4076
|
+
properties: {
|
|
4077
|
+
files: {
|
|
4078
|
+
type: "array",
|
|
4079
|
+
items: { type: "object" }
|
|
4080
|
+
},
|
|
4081
|
+
tools: {
|
|
4082
|
+
type: "array",
|
|
4083
|
+
items: { type: "object" }
|
|
4084
|
+
},
|
|
4085
|
+
skills: {
|
|
4086
|
+
type: "array",
|
|
4087
|
+
items: { type: "object" }
|
|
4088
|
+
},
|
|
4089
|
+
agents: {
|
|
4090
|
+
type: "array",
|
|
4091
|
+
items: { type: "object" }
|
|
4092
|
+
}
|
|
4093
|
+
},
|
|
4094
|
+
additionalProperties: true
|
|
4095
|
+
}
|
|
4096
|
+
},
|
|
4097
|
+
{
|
|
4098
|
+
name: "metadata",
|
|
4099
|
+
description: "Free-form key/value bag carried with the profile (plain string keys). Use it for run bookkeeping a reader will want later; it does not change how the child executes."
|
|
4100
|
+
}
|
|
4101
|
+
];
|
|
4102
|
+
/**
|
|
4103
|
+
* Build the published shape of `spawn_agent`'s `profile` argument from the canonical
|
|
4104
|
+
* `agentProfileSchema` conversion's `properties` map, so it cannot drift from the profile the
|
|
4105
|
+
* runtime materializes.
|
|
4106
|
+
*
|
|
4107
|
+
* DEGRADES, never throws. A canonical field that is absent — renamed or removed upstream — is
|
|
4108
|
+
* simply omitted from the published shape, and a canonical schema that is no longer an object
|
|
4109
|
+
* publishes no properties at all. This function is reached from a statically-imported module, so a
|
|
4110
|
+
* throw here bricks `import '@tangle-network/agent-runtime/kernel'` for every consumer over an
|
|
4111
|
+
* upstream rename that costs them, at worst, one advisory field. The drift itself is still caught
|
|
4112
|
+
* loudly — as a test assertion in `tests/kernel/coordination.test.ts`, in CI, where it is our
|
|
4113
|
+
* problem rather than at a consumer's import, where it is theirs.
|
|
4114
|
+
*
|
|
4115
|
+
* Permissive on purpose: `additionalProperties: true` with no `required` list, so every canonical
|
|
4116
|
+
* field this shape omits stays legal to pass. This tool layer performs no profile validation.
|
|
4117
|
+
*
|
|
4118
|
+
* @internal exported for the drift and degradation tests; not part of the package's public API.
|
|
4119
|
+
*/
|
|
4120
|
+
function deriveSpawnProfileArg(canonicalProperties) {
|
|
4121
|
+
const published = [];
|
|
4122
|
+
for (const field of spawnProfileFields) {
|
|
4123
|
+
const canonical = canonicalProperties?.[field.name];
|
|
4124
|
+
if (canonical === void 0) continue;
|
|
4125
|
+
const shape = field.brief ?? stripKeyCodecArtifacts(canonical);
|
|
4126
|
+
published.push([field.name, {
|
|
4127
|
+
...shape,
|
|
4128
|
+
description: field.description
|
|
4129
|
+
}]);
|
|
4130
|
+
}
|
|
4131
|
+
return {
|
|
4132
|
+
type: "object",
|
|
4133
|
+
description: "The child agent profile to run — this is the shape the DEFAULT worker seam accepts; a run wired with a custom makeWorkerAgent may accept a different one. The properties below are derived from the canonical AgentProfile schema and reduced to what a spawning parent sets (`mcp` and `resources` in a brief form); every other canonical field — tags, connections, subagents, hooks, modes, confidential, extensions — may still be passed. This tool does not validate the profile: the canonical AgentProfile schema governs validation downstream.",
|
|
4134
|
+
properties: Object.fromEntries(published),
|
|
4135
|
+
additionalProperties: true
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
spawnProfileFields.map((f) => f.name);
|
|
4139
|
+
let spawnProfileArgCache;
|
|
4140
|
+
/** The published `profile` shape, computed on FIRST tool-definition access and memoized — not at
|
|
4141
|
+
* module load. The conversion walks the whole canonical profile tree, and the strip walk rebuilds
|
|
4142
|
+
* it: 3.5ms on the first `createCoordinationTools`, 0.017ms on every later one (measured, Interface
|
|
4143
|
+
* 0.40, node 24). `src/runtime/index.ts` imports this module statically, so paying that at import
|
|
4144
|
+
* taxes every consumer of the kernel entrypoint — including the ones that never build a
|
|
4145
|
+
* coordination toolbox. The memo keeps it at once per process for the ones that do.
|
|
4146
|
+
*
|
|
4147
|
+
* Conversion choices. `io: 'input'` is the CALLER's view — pre-default, pre-transform — which is
|
|
4148
|
+
* what a spawning parent may pass, not what the runtime ends up holding. `unrepresentable: 'any'`
|
|
4149
|
+
* keeps the conversion total: the canonical schema contains transforms with no JSON Schema form,
|
|
4150
|
+
* and zod's default is to throw on them, which would leave the tool with no published shape. */
|
|
4151
|
+
function spawnProfileArg() {
|
|
4152
|
+
if (!spawnProfileArgCache) spawnProfileArgCache = deepFreeze(deriveSpawnProfileArg(agentProfileSchema.toJSONSchema({
|
|
4153
|
+
io: "input",
|
|
4154
|
+
target: "draft-07",
|
|
4155
|
+
unrepresentable: "any"
|
|
4156
|
+
}).properties));
|
|
4157
|
+
return spawnProfileArgCache;
|
|
4158
|
+
}
|
|
4159
|
+
/** Freeze a JSON-Schema tree. The derived schema is plain JSON data with no cycles. */
|
|
4160
|
+
function deepFreeze(value) {
|
|
4161
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
4162
|
+
Object.freeze(value);
|
|
4163
|
+
for (const nested of Object.values(value)) deepFreeze(nested);
|
|
4164
|
+
}
|
|
4165
|
+
return value;
|
|
4166
|
+
}
|
|
3984
4167
|
/** Build the driver's MCP tools over a live scope. */
|
|
3985
4168
|
function createCoordinationTools(opts) {
|
|
3986
4169
|
const deliverable = opts.deliverable;
|
|
@@ -4311,7 +4494,7 @@ function createCoordinationTools(opts) {
|
|
|
4311
4494
|
inputSchema: {
|
|
4312
4495
|
type: "object",
|
|
4313
4496
|
properties: {
|
|
4314
|
-
profile:
|
|
4497
|
+
profile: spawnProfileArg(),
|
|
4315
4498
|
task: { description: "The task the worker should perform." },
|
|
4316
4499
|
label: {
|
|
4317
4500
|
type: "string",
|
|
@@ -7340,9 +7523,21 @@ function createInProcessTransport() {
|
|
|
7340
7523
|
*
|
|
7341
7524
|
* @experimental
|
|
7342
7525
|
*/
|
|
7526
|
+
/** Hosts that reach only this machine, including the IPv4-mapped and bracketed IPv6 spellings a
|
|
7527
|
+
* caller may pass through from config. A name that is not recognizably loopback counts as REMOTE:
|
|
7528
|
+
* whether it resolves to a loopback interface is not knowable here, and the safe direction of that
|
|
7529
|
+
* doubt is "exposed". */
|
|
7530
|
+
function isLoopbackHost(host) {
|
|
7531
|
+
const h = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
7532
|
+
if (h === "localhost" || h === "::1" || h === "0:0:0:0:0:0:0:1" || h === "::ffff:127.0.0.1") return true;
|
|
7533
|
+
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
|
|
7534
|
+
}
|
|
7343
7535
|
/** Stand up the coordination MCP over a live scope. The HOST address is `127.0.0.1` (the bridge runs
|
|
7344
|
-
* opencode locally, same host); pass `host` to bind elsewhere when the harness is remote
|
|
7536
|
+
* opencode locally, same host); pass `host` to bind elsewhere when the harness is remote — a
|
|
7537
|
+
* non-loopback host additionally requires `allowUnauthenticatedRemote`. */
|
|
7345
7538
|
async function serveCoordinationMcp(opts) {
|
|
7539
|
+
const host = opts.host ?? "127.0.0.1";
|
|
7540
|
+
if (!isLoopbackHost(host) && opts.allowUnauthenticatedRemote !== true) throw new ConfigError(`coordination host=${JSON.stringify(host)} is not a loopback address and the coordination MCP has no authentication: any client that can reach the port could call spawn_agent/steer_agent and spend this run's budget. Bind a loopback host ("127.0.0.1", "localhost", "::1"), or set allowUnauthenticatedRemote: true to accept that exposure explicitly.`);
|
|
7346
7541
|
const coord = createCoordinationTools({
|
|
7347
7542
|
scope: opts.scope,
|
|
7348
7543
|
blobs: opts.blobs,
|
|
@@ -7363,7 +7558,6 @@ async function serveCoordinationMcp(opts) {
|
|
|
7363
7558
|
extraTools: coord.tools,
|
|
7364
7559
|
serverName: "coordination"
|
|
7365
7560
|
});
|
|
7366
|
-
const host = opts.host ?? "127.0.0.1";
|
|
7367
7561
|
const server = createServer((req, res) => {
|
|
7368
7562
|
if (req.method !== "POST") {
|
|
7369
7563
|
res.writeHead(405, { allow: "POST" });
|
|
@@ -7423,21 +7617,6 @@ async function serveCoordinationMcp(opts) {
|
|
|
7423
7617
|
}
|
|
7424
7618
|
//#endregion
|
|
7425
7619
|
//#region src/runtime/supervise/supervisor-agent.ts
|
|
7426
|
-
/**
|
|
7427
|
-
* `supervisorAgent` — build a supervisor `Agent` FROM its profile. The brain is resolved from
|
|
7428
|
-
* `profile.harness` exactly as `createExecutor({ backend })` resolves a worker: backend-as-data,
|
|
7429
|
-
* no hand-built brain. The supervisor stops being special — it's one profile, materialized by the
|
|
7430
|
-
* same resolution rule as every other agent.
|
|
7431
|
-
*
|
|
7432
|
-
* - `harness` null/undefined → the in-process router tool-loop: `driverAgent` over the
|
|
7433
|
-
* canonical `ToolLoopChat`, built by `routerBrain` from the profile's model + the router seam.
|
|
7434
|
-
* - `harness` a coding CLI (`claude-code`/`opencode`/`codex`/…) → a SANDBOXED harness drives the
|
|
7435
|
-
* coordination verbs: `serveCoordinationMcp` exposes spawn/await/steer/stop over the live scope,
|
|
7436
|
-
* and the caller's `driveHarness` runs the harness with that MCP mounted. The harness IS the brain.
|
|
7437
|
-
*
|
|
7438
|
-
* Both arms spawn children through the SAME `makeWorkerAgent` seam and apply the SAME independent
|
|
7439
|
-
* deliverable check to direct submissions. Raw driver prose is never eligible.
|
|
7440
|
-
*/
|
|
7441
7620
|
/** The standing strategy a router-brained supervisor runs with when its profile names no
|
|
7442
7621
|
* `systemPrompt`. The brain's competence IS this prompt: without it the brain has the coordination
|
|
7443
7622
|
* verbs but no policy for WHEN to use them, and either over-spawns or stalls. A profile may override
|
|
@@ -7459,11 +7638,101 @@ const defaultSupervisorPrompt = [
|
|
|
7459
7638
|
"Wait on real signals (await a settle, answer a blocking question), integrate the result, and stop",
|
|
7460
7639
|
"as soon as the deliverable is met."
|
|
7461
7640
|
].join("\n");
|
|
7641
|
+
/** Longest prompt excerpt an error message may carry. A supervisor system prompt is routinely
|
|
7642
|
+
* thousands of characters; two of them interpolated whole turn a configuration fault into an
|
|
7643
|
+
* unreadable wall, so a fault reports each prompt's LENGTH plus a leading excerpt instead. */
|
|
7644
|
+
const PROMPT_EXCERPT_CHARS = 60;
|
|
7645
|
+
/** `<n> chars starting "<first 60>…"` — enough to tell two prompts apart without printing either. */
|
|
7646
|
+
function describePrompt(value) {
|
|
7647
|
+
const head = value.slice(0, PROMPT_EXCERPT_CHARS);
|
|
7648
|
+
return `${value.length} chars starting ${JSON.stringify(head)}${value.length > PROMPT_EXCERPT_CHARS ? "…" : ""}`;
|
|
7649
|
+
}
|
|
7650
|
+
/**
|
|
7651
|
+
* The instruction lines a canonical `resources.instructions` contributes. A plain string and an
|
|
7652
|
+
* `inline` resource are their own text; a `github` reference names bytes that live elsewhere and
|
|
7653
|
+
* cannot be fetched while building a supervisor synchronously — that fails loud rather than
|
|
7654
|
+
* dropping instructions the profile says the agent runs under (the same rule
|
|
7655
|
+
* `improve()`'s memory surface applies to the same field).
|
|
7656
|
+
*/
|
|
7657
|
+
function resourceInstructionLines(instructions) {
|
|
7658
|
+
if (instructions === void 0) return [];
|
|
7659
|
+
if (typeof instructions === "string") return instructions.length > 0 ? [instructions] : [];
|
|
7660
|
+
if (instructions.kind === "inline") return instructions.content.length > 0 ? [instructions.content] : [];
|
|
7661
|
+
throw new ConfigError(`supervisorAgent: profile.resources.instructions is a github resource reference (${JSON.stringify(instructions.path)}), which cannot be fetched while the supervisor is built — pass the instruction text as a string or an inline resource`);
|
|
7662
|
+
}
|
|
7663
|
+
/**
|
|
7664
|
+
* The standing instruction both arms run under, assembled from every canonical spelling that
|
|
7665
|
+
* carries one: the system prompt (`prompt.systemPrompt` or the top-level `systemPrompt`), then the
|
|
7666
|
+
* `prompt.instructions` lines, then `resources.instructions` — each on its own line, in that order.
|
|
7667
|
+
* `undefined` only when the profile names none at all.
|
|
7668
|
+
*
|
|
7669
|
+
* Two disagreeing system prompts throw: they are the same standing instruction in two spellings, so
|
|
7670
|
+
* picking one silently changes what the supervisor runs and there is no defensible winner.
|
|
7671
|
+
*/
|
|
7672
|
+
function resolveSupervisorSystemPrompt(profile, activePrompt) {
|
|
7673
|
+
const promptSystem = profile.prompt?.systemPrompt;
|
|
7674
|
+
const topSystem = profile.systemPrompt;
|
|
7675
|
+
if (promptSystem !== void 0 && topSystem !== void 0 && promptSystem !== topSystem) throw new ValidationError(`supervisorAgent: profile.prompt.systemPrompt and profile.systemPrompt are both set and differ — they are the same standing instruction, so keep exactly one (prompt.systemPrompt: ${describePrompt(promptSystem)}; systemPrompt: ${describePrompt(topSystem)})`);
|
|
7676
|
+
const base = promptSystem ?? topSystem ?? activePrompt;
|
|
7677
|
+
const lines = [...profile.prompt?.instructions ?? [], ...resourceInstructionLines(profile.resources?.instructions)];
|
|
7678
|
+
if (lines.length === 0) return base;
|
|
7679
|
+
return (base !== void 0 ? [base, ...lines] : lines).join("\n");
|
|
7680
|
+
}
|
|
7681
|
+
/**
|
|
7682
|
+
* The router model id, or `undefined` when the profile names none. A string `model` IS the id; an
|
|
7683
|
+
* object `model` is canonical model hints and `default` is the id. `AgentProfileModelHints.default`
|
|
7684
|
+
* is OPTIONAL upstream (`{ provider: 'anthropic' }` is a valid canonical profile), so hints without
|
|
7685
|
+
* a resolvable id are the documented "profile names no model" case: the router config's own model
|
|
7686
|
+
* applies, exactly as when `model` is absent.
|
|
7687
|
+
*/
|
|
7688
|
+
function resolveSupervisorModelId(profile) {
|
|
7689
|
+
if (typeof profile.model === "string") return profile.model;
|
|
7690
|
+
const fromHints = profile.model?.default;
|
|
7691
|
+
return typeof fromHints === "string" && fromHints.length > 0 ? fromHints : void 0;
|
|
7692
|
+
}
|
|
7693
|
+
/**
|
|
7694
|
+
* Reduce either profile spelling — a hand-written `SupervisorProfile` or a canonical `AgentProfile`
|
|
7695
|
+
* — to the scalars the brain arms consume:
|
|
7696
|
+
*
|
|
7697
|
+
* - `modelId`: a string `model` verbatim, else `model.default`. Absent or unresolvable → the
|
|
7698
|
+
* router config's own model applies unchanged.
|
|
7699
|
+
* - `systemPrompt`: the system prompt plus the `prompt.instructions` and `resources.instructions`
|
|
7700
|
+
* lines, one per line.
|
|
7701
|
+
*
|
|
7702
|
+
* `supervisorAgent` resolves each piece only where it is consumed (the model id on the router arm
|
|
7703
|
+
* only); this whole-profile reduction is the caller-facing view of the same rules.
|
|
7704
|
+
*/
|
|
7705
|
+
function resolveSupervisorProfile(profile) {
|
|
7706
|
+
const systemPrompt = resolveSupervisorSystemPrompt(profile);
|
|
7707
|
+
const modelId = resolveSupervisorModelId(profile);
|
|
7708
|
+
return {
|
|
7709
|
+
name: profile.name ?? "supervisor",
|
|
7710
|
+
harness: profile.harness ?? null,
|
|
7711
|
+
...modelId !== void 0 ? { modelId } : {},
|
|
7712
|
+
...systemPrompt !== void 0 ? { systemPrompt } : {}
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
/**
|
|
7716
|
+
* Fail closed on a non-loopback coordination bind. `serveCoordinationMcp` mounts spawn_agent /
|
|
7717
|
+
* steer_agent / stop with NO authentication of any kind (it is a bare JSON-RPC-over-HTTP handler),
|
|
7718
|
+
* so a non-loopback bind lets anyone who can reach the port spawn agents and spend the run's
|
|
7719
|
+
* conserved budget. There is no token to require yet, so the only honest options are loopback or an
|
|
7720
|
+
* explicit, recorded acknowledgment — never a silent bind.
|
|
7721
|
+
*/
|
|
7722
|
+
function assertCoordinationBinding(binding) {
|
|
7723
|
+
const host = binding?.host;
|
|
7724
|
+
if (host === void 0 || isLoopbackHost(host)) return;
|
|
7725
|
+
if (binding?.allowUnauthenticatedRemote === true) return;
|
|
7726
|
+
throw new ConfigError(`supervisorAgent: coordination.host=${JSON.stringify(host)} is not a loopback address and the coordination MCP has no authentication: any client that can reach the port could call spawn_agent/steer_agent and spend this run's budget. Bind a loopback host ("127.0.0.1", "localhost", "::1"), or set coordination.allowUnauthenticatedRemote: true to accept that exposure explicitly.`);
|
|
7727
|
+
}
|
|
7462
7728
|
/** Build a supervisor `Agent` from its profile: the brain resolves from `profile.harness` (backend-as-data), the same resolution rule as every worker. */
|
|
7463
7729
|
function supervisorAgent(profile, deps) {
|
|
7464
7730
|
const name = profile.name ?? "supervisor";
|
|
7465
|
-
const systemPrompt = profile.systemPrompt ?? defaultSupervisorPrompt;
|
|
7466
7731
|
const harness = profile.harness ?? null;
|
|
7732
|
+
const profilePrompt = resolveSupervisorSystemPrompt(profile);
|
|
7733
|
+
const coordination = deps.coordination ? { ...deps.coordination } : void 0;
|
|
7734
|
+
assertCoordinationBinding(coordination);
|
|
7735
|
+
if (harness === null && coordination !== void 0) throw new ConfigError("supervisorAgent: coordination binding is only meaningful for a harness-brained supervisor (profile.harness set). A router-brained supervisor calls the coordination verbs in process and serves no MCP, so this binding would be silently ignored.");
|
|
7467
7736
|
if (harness !== null && deps.compaction) throw new ValidationError("supervisorAgent: compaction is only supported for router-brained supervisors (profile.harness null)");
|
|
7468
7737
|
if (harness === null) return driverAgent({
|
|
7469
7738
|
name,
|
|
@@ -7471,7 +7740,7 @@ function supervisorAgent(profile, deps) {
|
|
|
7471
7740
|
blobs: deps.blobs,
|
|
7472
7741
|
makeWorkerAgent: deps.makeWorkerAgent,
|
|
7473
7742
|
perWorker: deps.perWorker,
|
|
7474
|
-
systemPrompt,
|
|
7743
|
+
systemPrompt: resolveSupervisorSystemPrompt(profile, defaultSupervisorPrompt) ?? defaultSupervisorPrompt,
|
|
7475
7744
|
...deps.deliverable ? { deliverable: deps.deliverable } : {},
|
|
7476
7745
|
...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {},
|
|
7477
7746
|
...deps.extraTools ? { extraTools: deps.extraTools } : {},
|
|
@@ -7498,6 +7767,9 @@ function supervisorAgent(profile, deps) {
|
|
|
7498
7767
|
blobs: deps.blobs,
|
|
7499
7768
|
makeWorkerAgent: deps.makeWorkerAgent,
|
|
7500
7769
|
perWorker: deps.perWorker,
|
|
7770
|
+
...coordination?.host !== void 0 ? { host: coordination.host } : {},
|
|
7771
|
+
...coordination?.port !== void 0 ? { port: coordination.port } : {},
|
|
7772
|
+
...coordination?.allowUnauthenticatedRemote === true ? { allowUnauthenticatedRemote: true } : {},
|
|
7501
7773
|
...deps.deliverable ? { deliverable: deps.deliverable } : {},
|
|
7502
7774
|
...deps.maxLiveWorkers !== void 0 ? { maxLiveWorkers: deps.maxLiveWorkers } : {},
|
|
7503
7775
|
...deps.analysts ? { analysts: deps.analysts } : {},
|
|
@@ -7511,6 +7783,7 @@ function supervisorAgent(profile, deps) {
|
|
|
7511
7783
|
try {
|
|
7512
7784
|
await driveHarness({
|
|
7513
7785
|
profile,
|
|
7786
|
+
...profilePrompt !== void 0 ? { systemPrompt: profilePrompt } : {},
|
|
7514
7787
|
task,
|
|
7515
7788
|
scope,
|
|
7516
7789
|
coordinationMcpUrl: mcp.url
|
|
@@ -7535,9 +7808,10 @@ function supervisorAgent(profile, deps) {
|
|
|
7535
7808
|
}
|
|
7536
7809
|
function routerBrainFromProfile(profile, deps) {
|
|
7537
7810
|
if (!deps.router) throw new ValidationError("supervisorAgent: a router-brained supervisor (harness null) needs deps.router (or deps.brain)");
|
|
7811
|
+
const modelId = resolveSupervisorModelId(profile);
|
|
7538
7812
|
return routerBrain({
|
|
7539
7813
|
...deps.router,
|
|
7540
|
-
model:
|
|
7814
|
+
...modelId !== void 0 ? { model: modelId } : {}
|
|
7541
7815
|
});
|
|
7542
7816
|
}
|
|
7543
7817
|
//#endregion
|
|
@@ -7569,6 +7843,18 @@ function workerFromBackend(backend, deliverable) {
|
|
|
7569
7843
|
};
|
|
7570
7844
|
};
|
|
7571
7845
|
}
|
|
7846
|
+
/** Resolve one option that may be given as a value OR as a name into `opts.registry`. Both failure
|
|
7847
|
+
* modes name the option, the requested name, and the table it was looked up in — a typo must not
|
|
7848
|
+
* degrade into a silently unconfigured run (which for `deliverable` means "no run can ever
|
|
7849
|
+
* deliver"). A resolver port cannot enumerate its names, so the message names the table instead of
|
|
7850
|
+
* listing what was in it. */
|
|
7851
|
+
function resolveNamed(option, table, value, registry) {
|
|
7852
|
+
if (typeof value !== "string") return value;
|
|
7853
|
+
if (!registry) throw new ConfigError(`supervise: opts.${option} = ${JSON.stringify(value)} names a registry entry, but no opts.registry.${table} was provided to resolve it against`);
|
|
7854
|
+
const entry = registry.resolve(value);
|
|
7855
|
+
if (entry === void 0) throw new ConfigError(`supervise: opts.${option} = ${JSON.stringify(value)} is not in opts.registry.${table} — the table resolved no entry under that name`);
|
|
7856
|
+
return entry;
|
|
7857
|
+
}
|
|
7572
7858
|
/** A quarter of the token pool per worker → ~4 workers fit before `poolStarved` halts spawning. */
|
|
7573
7859
|
function defaultPerWorker(budget) {
|
|
7574
7860
|
return {
|
|
@@ -7580,15 +7866,20 @@ function defaultPerWorker(budget) {
|
|
|
7580
7866
|
function supervise(profile, task, opts) {
|
|
7581
7867
|
const backendModel = opts.backend?.model;
|
|
7582
7868
|
assertModelAllowed(opts.router?.model, opts.allowedModels);
|
|
7583
|
-
assertModelAllowed(profile
|
|
7869
|
+
assertModelAllowed(resolveSupervisorModelId(profile), opts.allowedModels);
|
|
7584
7870
|
assertModelAllowed(typeof backendModel === "string" ? backendModel : void 0, opts.allowedModels);
|
|
7871
|
+
const deliverable = resolveNamed("deliverable", "deliverables", opts.deliverable, opts.registry?.deliverables);
|
|
7872
|
+
const finalizer = resolveNamed("finalizer", "finalizers", opts.finalizer, opts.registry?.finalizers);
|
|
7873
|
+
const analysts = resolveNamed("analysts", "analysts", opts.analysts, opts.registry?.analysts);
|
|
7874
|
+
const probes = resolveNamed("probes", "probes", opts.probes, opts.registry?.probes);
|
|
7875
|
+
assertCoordinationBinding(opts.coordination);
|
|
7585
7876
|
const ctx = opts.runDir !== void 0 ? createFileRunContext(opts.runDir, { withDriver: true }) : createInMemoryRunContext({ withDriver: true });
|
|
7586
7877
|
const blobs = opts.blobs ?? ctx.blobs;
|
|
7587
7878
|
const perWorker = opts.perWorker ?? defaultPerWorker(opts.budget);
|
|
7588
7879
|
let makeWorkerAgent = opts.makeWorkerAgent;
|
|
7589
7880
|
if (!makeWorkerAgent) {
|
|
7590
7881
|
if (!opts.backend) throw new ValidationError("supervise: provide opts.backend (where workers run) or opts.makeWorkerAgent");
|
|
7591
|
-
makeWorkerAgent = workerFromBackend(opts.backend,
|
|
7882
|
+
makeWorkerAgent = workerFromBackend(opts.backend, deliverable);
|
|
7592
7883
|
}
|
|
7593
7884
|
const workerFactory = makeWorkerAgent;
|
|
7594
7885
|
const runId = opts.runId ?? "supervise";
|
|
@@ -7600,17 +7891,18 @@ function supervise(profile, task, opts) {
|
|
|
7600
7891
|
blobs,
|
|
7601
7892
|
makeWorkerAgent: workerFactory,
|
|
7602
7893
|
perWorker,
|
|
7603
|
-
...
|
|
7894
|
+
...deliverable ? { deliverable } : {},
|
|
7604
7895
|
...log ? { onEvent: (ev) => log.append(runId, ev, new Date(now()).toISOString()) } : {},
|
|
7605
7896
|
...priorCoordination && (priorCoordination.questions.length > 0 || priorCoordination.findings.length > 0) ? { priorCoordination } : {},
|
|
7606
|
-
...
|
|
7897
|
+
...finalizer ? { finalizer } : {},
|
|
7898
|
+
...opts.coordination ? { coordination: opts.coordination } : {},
|
|
7607
7899
|
...opts.maxLiveWorkers !== void 0 ? { maxLiveWorkers: opts.maxLiveWorkers } : {},
|
|
7608
7900
|
...opts.router ? { router: opts.router } : {},
|
|
7609
7901
|
...opts.brain ? { brain: opts.brain } : {},
|
|
7610
7902
|
...opts.driveHarness ? { driveHarness: opts.driveHarness } : {},
|
|
7611
7903
|
...opts.extraTools ? { extraTools: opts.extraTools } : {},
|
|
7612
7904
|
...opts.executeExtraTool ? { executeExtraTool: opts.executeExtraTool } : {},
|
|
7613
|
-
...
|
|
7905
|
+
...analysts ? { analysts } : {},
|
|
7614
7906
|
...opts.analyzeOnSettle ? { analyzeOnSettle: opts.analyzeOnSettle } : {},
|
|
7615
7907
|
...opts.watchWorkers ? { watchWorkers: opts.watchWorkers } : {},
|
|
7616
7908
|
...opts.stallAfterMs !== void 0 ? { stallAfterMs: opts.stallAfterMs } : {},
|
|
@@ -7626,7 +7918,7 @@ function supervise(profile, task, opts) {
|
|
|
7626
7918
|
blobs,
|
|
7627
7919
|
executors: ctx.executors,
|
|
7628
7920
|
maxDepth: opts.maxDepth ?? 8,
|
|
7629
|
-
...
|
|
7921
|
+
...probes ? { probes } : {},
|
|
7630
7922
|
...ctx.resume === true ? { resume: true } : {},
|
|
7631
7923
|
...opts.now ? { now: opts.now } : {}
|
|
7632
7924
|
});
|
|
@@ -7634,6 +7926,6 @@ function supervise(profile, task, opts) {
|
|
|
7634
7926
|
return start();
|
|
7635
7927
|
}
|
|
7636
7928
|
//#endregion
|
|
7637
|
-
export {
|
|
7929
|
+
export { InMemoryDelegationStore as $, DELEGATE_INPUT_SCHEMA as A, createWorktreeCliExecutor as At, profileRichnessFinding as B, createSandboxForSpec as Bt, validateDelegateUiAuditArgs as C, defaultToolDetectors as Ct, createDelegateFeedbackHandler as D, cliWorktreeExecutor as Dt, DELEGATE_FEEDBACK_TOOL_NAME as E, createInMemoryRunContext as Et, delegate as F, piSeamKey as Ft, DELEGATION_TRACE_MAX_SPANS as G, probeSandboxCapabilities as Gt, DelegationTaskQueue as H, runAgentRounds as Ht, asAuthoredProfile as I, createPushTraceSource as It, composeLoopTraceEmitters as J, assertModelAllowed as Jt, buildDelegationTraceSpans as K, acquireSandbox as Kt, assessAuthoredProfile as L, decodeToolPart as Lt, createDelegateHandler as M, createSteerableSandboxSession as Mt, validateDelegateArgs as N, PI_RUNTIME as Nt, validateDelegateFeedbackArgs as O, createExecutor as Ot, defaultDelegateBudget as P, piExecutor as Pt, FileDelegationStore as Q, authoredWorker as R, sandboxSessionTraceSource as Rt, createDelegateUiAuditHandler as S, rollingDispatch as St, DELEGATE_FEEDBACK_INPUT_SCHEMA as T, createFileRunContext as Tt, hashIdempotencyInput as U, runLoop as Ut, supervisorInstructions as V, defaultSelectWinner as Vt, DELEGATION_TRACE_MAX_BYTES as W, createSandboxLineage as Wt, DelegationPersistenceError as X, createDelegationTraceCollector as Y, gateOnDeliverable as Yt, DelegationStateCorruptError as Z, createDelegationHistoryHandler as _, createCoordinationTools as _t, supervisorAgent as a, allWorkersStalled as at, DELEGATE_UI_AUDIT_INPUT_SCHEMA as b, freeSlots as bt, createMcpServer as c, noProgressFor as ct, DELEGATION_STATUS_TOOL_NAME as d, anytimeReport as dt, InMemoryFeedbackStore as et, createDelegationStatusHandler as f, areaUnderCurve as ft, DELEGATION_HISTORY_TOOL_NAME as g, DEFAULT_AWAIT_EVENT_TIMEOUT_MS as gt, DELEGATION_HISTORY_INPUT_SCHEMA as h, renderAnytimeTable as ht, resolveSupervisorProfile as i, allOf as it, DELEGATE_TOOL_NAME as j, DEFAULT_SANDBOX_STEERING_MAX_TURNS as jt, DELEGATE_DESCRIPTION as k, createExecutorRegistry as kt, DELEGATION_STATUS_DESCRIPTION as l, plateau as lt, DELEGATION_HISTORY_DESCRIPTION as m, plateauLength as mt, workerFromBackend as n, driverAgent as nt, serveCoordinationMcp as o, anyOf as ot, validateDelegationStatusArgs as p, bestSoFar as pt, capDelegationTrace as q, FileCoordinationLog as qt, assertCoordinationBinding as r, finalizeBestDelivered as rt, createInProcessTransport as s, createProgressTracker as st, supervise as t, eventToSnapshot as tt, DELEGATION_STATUS_INPUT_SCHEMA as u, sampleFromSettled as ut, validateDelegationHistoryArgs as v, createEventBus as vt, DELEGATE_FEEDBACK_DESCRIPTION as w, watchTrace as wt, DELEGATE_UI_AUDIT_TOOL_NAME as x, queueOf as xt, DELEGATE_UI_AUDIT_DESCRIPTION as y, effectiveConcurrency as yt, defaultProfileRichnessThresholds as z, createInbox as zt };
|
|
7638
7930
|
|
|
7639
|
-
//# sourceMappingURL=supervise-
|
|
7931
|
+
//# sourceMappingURL=supervise-6jSTe0kO.js.map
|