@tangle-network/agent-runtime 0.113.0 → 0.114.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-DGozpA_7.js → activation-DlKl7-1o.js} +3 -3
- package/dist/{activation-DGozpA_7.js.map → activation-DlKl7-1o.js.map} +1 -1
- package/dist/agent.d.ts +18 -7
- package/dist/agent.js +123 -64
- package/dist/agent.js.map +1 -1
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-Co3sRC_7.js → candidate-execution-CfpJrd3o.js} +4 -4
- package/dist/{candidate-execution-Co3sRC_7.js.map → candidate-execution-CfpJrd3o.js.map} +1 -1
- package/dist/{environment-provider-BsHpW4Dp.js → environment-provider-CCaEhA-l.js} +38 -4
- package/dist/environment-provider-CCaEhA-l.js.map +1 -0
- package/dist/{environment-provider-DG-cSx89.d.ts → environment-provider-Cygthiy3.d.ts} +40 -1
- package/dist/environment-provider.d.ts +1 -1
- package/dist/environment-provider.js +1 -1
- package/dist/{improvement-cycle-DxBZsG33.js → improvement-cycle-vLye8pYb.js} +4 -4
- package/dist/{improvement-cycle-DxBZsG33.js.map → improvement-cycle-vLye8pYb.js.map} +1 -1
- package/dist/{index-DmXPCQ2X.d.ts → index-BLf-SZ2b.d.ts} +6 -6
- package/dist/{index-BdnhmGUH.d.ts → index-D-lNUfea.d.ts} +147 -9
- package/dist/{index-B-DkjCqQ.d.ts → index-YpvLKzIX.d.ts} +3 -3
- package/dist/index.d.ts +7 -7
- package/dist/index.js +11 -11
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +5 -5
- package/dist/kernel.d.ts +4 -4
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-h7sDHsnd.js → knowledge-BTsA9n6L.js} +5 -5
- package/dist/{knowledge-h7sDHsnd.js.map → knowledge-BTsA9n6L.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{local-harness-Dh8PJ0ot.d.ts → local-harness-BnB1E9nM.d.ts} +92 -2
- package/dist/{loop-runner-bin-DN8491Y_.d.ts → loop-runner-bin-Cg_8UKZR.d.ts} +3 -3
- package/dist/{loop-runner-bin-D1TTr39X.js → loop-runner-bin-CuBMmlMJ.js} +3 -3
- package/dist/{loop-runner-bin-D1TTr39X.js.map → loop-runner-bin-CuBMmlMJ.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 +2 -2
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-B1lRpTZr.js → openai-tools-VAbzps5G.js} +2 -2
- package/dist/{openai-tools-B1lRpTZr.js.map → openai-tools-VAbzps5G.js.map} +1 -1
- package/dist/{prepare-BKVdOn0w.js → prepare-BHQBb02e.js} +2 -2
- package/dist/{prepare-BKVdOn0w.js.map → prepare-BHQBb02e.js.map} +1 -1
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{protected-model-port-BtldAE9j.js → protected-model-port-DqAH1Z2M.js} +2 -2
- package/dist/{protected-model-port-BtldAE9j.js.map → protected-model-port-DqAH1Z2M.js.map} +1 -1
- package/dist/{redact-BkasKlyd.d.ts → redact-BRaHUFMf.d.ts} +2 -2
- package/dist/{runtime-Cs2e63LL.js → runtime-DQ5seHEu.js} +8 -5
- package/dist/runtime-DQ5seHEu.js.map +1 -0
- package/dist/{spawn-journal-DLhnom3e.js → spawn-journal-DCPbicXB.js} +2 -1
- package/dist/{spawn-journal-DLhnom3e.js.map → spawn-journal-DCPbicXB.js.map} +1 -1
- package/dist/{structural-rollout-DXmSoA9q.js → structural-rollout-C7vF83fV.js} +3 -3
- package/dist/{structural-rollout-DXmSoA9q.js.map → structural-rollout-C7vF83fV.js.map} +1 -1
- package/dist/{supervise-S7glOjJH.js → supervise-tdxMJYem.js} +476 -31
- package/dist/supervise-tdxMJYem.js.map +1 -0
- package/dist/{supervisor-zebR4ICs.js → supervisor-DKqolBGI.js} +234 -23
- package/dist/supervisor-DKqolBGI.js.map +1 -0
- package/dist/testing.js +9 -9
- package/dist/{workspace-archive-CYYxZyA7.js → workspace-archive-DXzJq7WP.js} +2 -2
- package/dist/{workspace-archive-CYYxZyA7.js.map → workspace-archive-DXzJq7WP.js.map} +1 -1
- package/package.json +1 -1
- package/dist/environment-provider-BsHpW4Dp.js.map +0 -1
- package/dist/runtime-Cs2e63LL.js.map +0 -1
- package/dist/supervise-S7glOjJH.js.map +0 -1
- package/dist/supervisor-zebR4ICs.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
import { a as contentAddress, i as InMemorySpawnJournal, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore } from "./spawn-journal-
|
|
4
|
-
import { A as
|
|
3
|
+
import { a as contentAddress, i as InMemorySpawnJournal, n as FileSpawnJournal, r as InMemoryResultBlobStore, t as FileResultBlobStore } from "./spawn-journal-DCPbicXB.js";
|
|
4
|
+
import { A as captureWorktreeDiff, C as routerChatWithUsage, E as runBrainLoop, M as removeWorktree, O as runWorktreeChecks, a as runFinalizer, i as pickBestDelivered, j as createWorktree, k as runWorktreeHarness, l as withDriverExecutor, n as bestDelivered, o as runTree, t as createSupervisor, x as routerBrain, y as createActivityLog } from "./supervisor-DKqolBGI.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
|
-
import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, s as buildBackendOptions } from "./environment-provider-
|
|
7
|
+
import { i as resolveAgentEnvironmentProvider, n as providerAsExecutor, s as buildBackendOptions } from "./environment-provider-CCaEhA-l.js";
|
|
8
8
|
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";
|
|
@@ -12,8 +12,10 @@ import { argHash, computeFindingId, errorStreakDetector, estimateCost, isModelPr
|
|
|
12
12
|
import { agentProfileSchema } from "@tangle-network/agent-interface";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
14
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
15
|
-
import
|
|
15
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
+
import path, { dirname, isAbsolute, join, sep } from "node:path";
|
|
16
17
|
import { spawn } from "node:child_process";
|
|
18
|
+
import { homedir, tmpdir } from "node:os";
|
|
17
19
|
import { createServer, request } from "node:http";
|
|
18
20
|
import { request as request$1 } from "node:https";
|
|
19
21
|
import { Readable, Writable } from "node:stream";
|
|
@@ -1273,6 +1275,386 @@ function createInbox() {
|
|
|
1273
1275
|
};
|
|
1274
1276
|
}
|
|
1275
1277
|
//#endregion
|
|
1278
|
+
//#region src/runtime/supervise/pi-mcp.ts
|
|
1279
|
+
/**
|
|
1280
|
+
*
|
|
1281
|
+
* MCP for the `pi` harness — the mount `piExecutor` performs before it spawns pi.
|
|
1282
|
+
*
|
|
1283
|
+
* pi ships NO MCP support of its own. MCP arrives through the `pi-mcp-adapter` extension, and the
|
|
1284
|
+
* adapter takes its config from a path the CALLER names: it registers a `--mcp-config <path>` flag
|
|
1285
|
+
* (`pi-mcp-adapter` `index.ts:252`, read back at `init.ts:101` and off raw argv at `utils.ts:55`).
|
|
1286
|
+
* So "give pi an MCP server" means: write the canonical `{ mcpServers: … }` object to a path this
|
|
1287
|
+
* run owns, pass that path with `--mcp-config`, make sure the adapter is loaded, and delete the
|
|
1288
|
+
* file afterwards. This module does exactly that and nothing else.
|
|
1289
|
+
*
|
|
1290
|
+
* Three rules carry the whole design:
|
|
1291
|
+
*
|
|
1292
|
+
* 1. **Fail loud, never fail safe.** If the profile declares MCP servers and `pi-mcp-adapter` is not
|
|
1293
|
+
* installed, `preparePiMcp` throws BEFORE any file is written and before pi is spawned. A run
|
|
1294
|
+
* whose tools never existed must fail, not score zero for the wrong reason.
|
|
1295
|
+
* 2. **`--no-extensions` is a trap.** When `extensions.pi.load` is an array, pi runs
|
|
1296
|
+
* `--no-extensions`, which suppresses ALL auto-discovery — cwd `.pi/extensions`, global
|
|
1297
|
+
* `~/.pi/agent/extensions`, AND the `settings.json` `packages` list that would otherwise have
|
|
1298
|
+
* auto-loaded the adapter. So `load: ['pi-memory']` alongside `mcp` kills MCP dead even though
|
|
1299
|
+
* the adapter IS installed. When MCP is declared, this module adds `pi-mcp-adapter` to the
|
|
1300
|
+
* resolved load list and REPORTS that it did (`PiMcpReceipt.adapterInjected`). The caller's
|
|
1301
|
+
* profile object is never mutated.
|
|
1302
|
+
* 3. **One config file per worker EXECUTION, never one per workspace.** `PiSeam.cwd` comes from the
|
|
1303
|
+
* `ExecutorConfig` and is therefore shared by every worker the factory builds, so a config
|
|
1304
|
+
* discovered by cwd is a file two concurrent siblings fight over. `mountPiMcpConfig` instead
|
|
1305
|
+
* creates a fresh `mkdtemp` directory — OS-guaranteed unique, so no counter, lock, merge or
|
|
1306
|
+
* restore is needed — and hands its path to `--mcp-config`. Concurrent workers built from ONE
|
|
1307
|
+
* seam get ONE config each and never collide.
|
|
1308
|
+
*
|
|
1309
|
+
* Why the flag is safe to emit here even though pi only lists it under `Extension CLI Flags`: it is
|
|
1310
|
+
* emitted ONLY when the profile declares at least one usable MCP server, and that case has already
|
|
1311
|
+
* passed the `piMcpAdapterAvailable()` gate above, so the extension that registers the flag is
|
|
1312
|
+
* guaranteed loadable (auto-discovered, or force-loaded by the `--extension` this module appends).
|
|
1313
|
+
* A machine without the adapter never reaches the flag — it fails at the gate with an install
|
|
1314
|
+
* command instead.
|
|
1315
|
+
*
|
|
1316
|
+
* Where the run's servers sit in pi's config layering, stated plainly because it is a real
|
|
1317
|
+
* behavioral edge: `--mcp-config` REPLACES pi's user-global override file (`config.ts:310`,
|
|
1318
|
+
* `getPiGlobalConfigPath(overridePath)`), so the operator's own `~/.pi/agent/mcp.json` servers no
|
|
1319
|
+
* longer leak into a worker — good. It does NOT suppress project discovery: the adapter still
|
|
1320
|
+
* layers `<cwd>/.mcp.json` and `<cwd>/.pi/mcp.json` on top (`config.ts:352-373`), and those are
|
|
1321
|
+
* later sources, so a workspace that ships a server under the SAME NAME overrides this run's entry.
|
|
1322
|
+
* The consequence is worth naming: an A/B whose arms differ only in `profile.mcp` is confounded
|
|
1323
|
+
* when the shared workspace carries its own MCP config. Deleting a file the run did not create
|
|
1324
|
+
* would be worse — a run cannot quietly edit the repo it was pointed at.
|
|
1325
|
+
*
|
|
1326
|
+
* Upstream note: `@tangle-network/agent-profile-materialize` is the shared owner of profile → harness
|
|
1327
|
+
* mapping and already declares `pi` a first-class `HarnessId`, but as of 0.9.4 its pi arm answers
|
|
1328
|
+
* `unsupported('mcp', 'pi MCP requires a loaded MCP extension; the base Pi runtime has no MCP
|
|
1329
|
+
* control')` and its pi extensions arm answers `unsupported('extensions', …)`. That refusal is now
|
|
1330
|
+
* factually stale — the shape is exactly its `kimi-code` arm's (write a config file, push a config
|
|
1331
|
+
* flag, because the harness has no cwd discovery worth trusting). Teaching the materializer these
|
|
1332
|
+
* two rows is the durable home for this mapping; until it lands, this module is the only thing in
|
|
1333
|
+
* the `backend: 'pi'` path that can honor `profile.mcp` at all.
|
|
1334
|
+
*
|
|
1335
|
+
* Verified against pi 0.83.0 and pi-mcp-adapter 2.15.0.
|
|
1336
|
+
*
|
|
1337
|
+
* @experimental
|
|
1338
|
+
*/
|
|
1339
|
+
/** The pi extension that gives pi MCP at all. Everything here is gated on it being loadable. */
|
|
1340
|
+
const PI_MCP_ADAPTER = "pi-mcp-adapter";
|
|
1341
|
+
/** Overrides adapter detection for installs whose path does not carry the adapter's name. */
|
|
1342
|
+
const PI_MCP_ADAPTER_ENV = "AGENT_RUNTIME_PI_MCP_ADAPTER";
|
|
1343
|
+
/** The adapter-registered flag that names this run's config file (`pi-mcp-adapter` `index.ts:252`). */
|
|
1344
|
+
const PI_MCP_CONFIG_FLAG = "--mcp-config";
|
|
1345
|
+
/** pi's agent home. Holds `settings.json` and `npm/node_modules/<extension>`. */
|
|
1346
|
+
function piAgentDir() {
|
|
1347
|
+
return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
|
|
1348
|
+
}
|
|
1349
|
+
/** `<agentDir>/npm`, whose `node_modules/<pkg>` holds installed pi extensions. */
|
|
1350
|
+
function piNpmRoot() {
|
|
1351
|
+
return join(piAgentDir(), "npm");
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* True when pi can actually consume MCP config — i.e. `pi-mcp-adapter` is installed. Ported from
|
|
1355
|
+
* cli-bridge `pi.ts:189`: an env override first (for vendored installs whose path does not carry the
|
|
1356
|
+
* adapter's name), then the npm install directory, then a scan of `settings.json` `packages` in
|
|
1357
|
+
* which a local-path entry is resolved by reading the target's `package.json` `name`. An
|
|
1358
|
+
* unreadable or absent `settings.json` reports "not detected"; it never throws and never assumes.
|
|
1359
|
+
*/
|
|
1360
|
+
function piMcpAdapterAvailable() {
|
|
1361
|
+
const override = process.env[PI_MCP_ADAPTER_ENV];
|
|
1362
|
+
if (override === "1" || override === "true") return true;
|
|
1363
|
+
if (override === "0" || override === "false") return false;
|
|
1364
|
+
const agentDir = piAgentDir();
|
|
1365
|
+
if (existsSync(join(agentDir, "npm", "node_modules", "pi-mcp-adapter"))) return true;
|
|
1366
|
+
try {
|
|
1367
|
+
const settings = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf-8"));
|
|
1368
|
+
if (!Array.isArray(settings.packages)) return false;
|
|
1369
|
+
return settings.packages.some((entry) => packageEntryIsAdapter(entry, agentDir));
|
|
1370
|
+
} catch {
|
|
1371
|
+
return false;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
/** One `settings.json` `packages[]` entry: a name containing the adapter, or a local path to it. */
|
|
1375
|
+
function packageEntryIsAdapter(entry, agentDir) {
|
|
1376
|
+
if (typeof entry !== "string") return false;
|
|
1377
|
+
if (entry.includes("pi-mcp-adapter")) return true;
|
|
1378
|
+
const spec = entry.replace(/^(file|path):(\/\/)?/, "");
|
|
1379
|
+
const windowsAbsolute = /^[A-Za-z]:[\\/]/.test(spec);
|
|
1380
|
+
if (!isAbsolute(spec) && !windowsAbsolute && !spec.startsWith(".")) return false;
|
|
1381
|
+
return readPackageName(isAbsolute(spec) || windowsAbsolute ? spec : join(agentDir, spec)) === PI_MCP_ADAPTER;
|
|
1382
|
+
}
|
|
1383
|
+
function readPackageName(packageDir) {
|
|
1384
|
+
try {
|
|
1385
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf-8"));
|
|
1386
|
+
return typeof manifest.name === "string" ? manifest.name : void 0;
|
|
1387
|
+
} catch {
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Read one `AgentProfileConfigValue` as public bytes. A `secret-ref` is REFUSED rather than
|
|
1393
|
+
* stringified: this executor has no secret provider, so the only alternatives are writing a
|
|
1394
|
+
* placeholder into the config (an auth failure disguised as a tool bug) or writing the literal
|
|
1395
|
+
* reference object (nonsense to the adapter). Plain strings, numbers and booleans are accepted
|
|
1396
|
+
* because profiles authored as JSON commonly carry them where the type says `{kind:'public'}`.
|
|
1397
|
+
*/
|
|
1398
|
+
function publicConfigString(value, where) {
|
|
1399
|
+
if (typeof value === "string") return value;
|
|
1400
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1401
|
+
if (value && typeof value === "object" && value.kind === "public") return value.value;
|
|
1402
|
+
if (value && typeof value === "object" && value.kind === "secret-ref") throw new ConfigError(`piExecutor: ${where} is a secret-ref ("${value.key}") and piExecutor has no secret provider; resolve it before handing the profile to the executor, or declare a public value`);
|
|
1403
|
+
throw new ValidationError(`piExecutor: ${where} is not a public configuration value`);
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Build the canonical `{ mcpServers }` body the adapter reads, from `AgentProfile.mcp`.
|
|
1407
|
+
*
|
|
1408
|
+
* This is cli-bridge's `buildCanonicalMcpServers`
|
|
1409
|
+
* (`/home/drew/code/cli-bridge/src/backends/profile-support.ts:253`) reproduced field for field and
|
|
1410
|
+
* in the same key order — same drop of `enabled:false`, same stdio-versus-remote decision
|
|
1411
|
+
* (`isStdioMcpSpec`, `profile-support.ts:177`), same `{command, args, env}` and `{type, url,
|
|
1412
|
+
* headers}` bodies, same silent drop of an entry with nothing to connect to. For any server both
|
|
1413
|
+
* input types can express, the emitted JSON is byte-identical.
|
|
1414
|
+
*
|
|
1415
|
+
* The two input types are not the same type, so three differences remain and each is a field one
|
|
1416
|
+
* side cannot express:
|
|
1417
|
+
*
|
|
1418
|
+
* 1. **`cwd` (emitted here, absent there).** `AgentProfileLocalMcpServer` has `cwd`; cli-bridge's
|
|
1419
|
+
* `McpServerSpec` (`src/backends/types.ts:53`) has no such field, so it can never emit one. The
|
|
1420
|
+
* adapter honors it (`pi-mcp-adapter` `types.ts:325`, `ServerEntry.cwd`), so dropping it would
|
|
1421
|
+
* silently discard a declared setting. It is emitted where cli-bridge emits `timeout`.
|
|
1422
|
+
* 2. **`timeout` (emitted there, absent here).** `AgentProfileMcpServer` has no timeout field, so
|
|
1423
|
+
* there is nothing to emit. (The adapter's own knob is `requestTimeoutMs`, not `timeout`.)
|
|
1424
|
+
* 3. **A `url` with no declared transport.** cli-bridge DROPS it — its remote branch requires an
|
|
1425
|
+
* explicit `spec.type` — even though its own `McpServerSpec` docstring says "http is implied if
|
|
1426
|
+
* `url` is set" (`types.ts:53-56`). `transport` is optional on `AgentProfileRemoteMcpServer`, so
|
|
1427
|
+
* dropping such a server here would be exactly the silent-drop bug this module exists to fix.
|
|
1428
|
+
* We honor the documented convention and emit `type: 'http'`.
|
|
1429
|
+
*
|
|
1430
|
+
* `sse` is ACCEPTED and forwarded verbatim, as cli-bridge forwards it. The adapter connects a URL
|
|
1431
|
+
* server by probing StreamableHTTP and falling back to `SSEClientTransport` when the probe fails
|
|
1432
|
+
* (`pi-mcp-adapter` `server-manager.ts:765`), so an SSE-only endpoint does connect. Its
|
|
1433
|
+
* `ServerEntry` (`types.ts:319`) carries no transport discriminator at all, which means the `type`
|
|
1434
|
+
* key is inert to the adapter either way — but it is what cli-bridge writes, and a config file that
|
|
1435
|
+
* round-trips the caller's declared transport is the one that stays readable by both.
|
|
1436
|
+
*/
|
|
1437
|
+
function buildPiMcpServers(mcp) {
|
|
1438
|
+
const out = {};
|
|
1439
|
+
if (!mcp || typeof mcp !== "object") return out;
|
|
1440
|
+
for (const [name, raw] of Object.entries(mcp)) {
|
|
1441
|
+
if (!name || !raw || typeof raw !== "object") continue;
|
|
1442
|
+
if (raw.enabled === false) continue;
|
|
1443
|
+
const transport = raw.transport;
|
|
1444
|
+
const command = typeof raw.command === "string" && raw.command.length > 0 ? raw.command : null;
|
|
1445
|
+
const url = typeof raw.url === "string" && raw.url.length > 0 ? raw.url : null;
|
|
1446
|
+
if ((transport === "http" || transport === "sse" ? false : command !== null) && command !== null) {
|
|
1447
|
+
const args = Array.isArray(raw.args) ? raw.args.map((a, i) => publicConfigString(a, `mcp["${name}"].args[${i}]`)) : [];
|
|
1448
|
+
const env = readConfigRecord(raw.env, `mcp["${name}"].env`);
|
|
1449
|
+
out[name] = {
|
|
1450
|
+
command,
|
|
1451
|
+
...args.length > 0 ? { args } : {},
|
|
1452
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
1453
|
+
...typeof raw.cwd === "string" && raw.cwd.length > 0 ? { cwd: raw.cwd } : {}
|
|
1454
|
+
};
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
if (url !== null && transport !== "stdio") {
|
|
1458
|
+
const headers = readConfigRecord(raw.headers, `mcp["${name}"].headers`);
|
|
1459
|
+
out[name] = {
|
|
1460
|
+
type: transport === "sse" ? "sse" : "http",
|
|
1461
|
+
url,
|
|
1462
|
+
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return out;
|
|
1467
|
+
}
|
|
1468
|
+
function readConfigRecord(record, where) {
|
|
1469
|
+
if (!record || typeof record !== "object") return {};
|
|
1470
|
+
const out = {};
|
|
1471
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1472
|
+
if (value === void 0) continue;
|
|
1473
|
+
out[key] = publicConfigString(value, `${where}["${key}"]`);
|
|
1474
|
+
}
|
|
1475
|
+
return out;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Resolve one `extensions.pi.load` entry to an absolute extension entry file. Ported from cli-bridge
|
|
1479
|
+
* `resolvePiExtensionEntry` (`pi.ts:123`): an absolute path is used verbatim; a bare package name
|
|
1480
|
+
* resolves to `<npmRoot>/node_modules/<pkg>/<package.json main>`, `main` defaulting to `index.ts`.
|
|
1481
|
+
* The resolution is required because pi's per-file loader does NOT resolve bare specifiers against
|
|
1482
|
+
* `~/.pi/agent/npm/node_modules`. Fail-closed: an unreadable manifest or a missing entry file
|
|
1483
|
+
* rejects the run rather than letting an ablation arm run without the extension it declared.
|
|
1484
|
+
*/
|
|
1485
|
+
function resolvePiExtensionEntry(spec, npmRoot) {
|
|
1486
|
+
let entry;
|
|
1487
|
+
if (isAbsolute(spec)) entry = spec;
|
|
1488
|
+
else {
|
|
1489
|
+
const packageDir = join(npmRoot, "node_modules", spec);
|
|
1490
|
+
let main = "index.ts";
|
|
1491
|
+
try {
|
|
1492
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf-8"));
|
|
1493
|
+
if (typeof manifest.main === "string" && manifest.main) main = manifest.main;
|
|
1494
|
+
} catch (err) {
|
|
1495
|
+
throw new ConfigError(`piExecutor: cannot load pi extension "${spec}": ${join(packageDir, "package.json")} is not readable (install it with \`pi install npm:${spec}\`): ${err instanceof Error ? err.message : String(err)}`);
|
|
1496
|
+
}
|
|
1497
|
+
entry = join(packageDir, main);
|
|
1498
|
+
}
|
|
1499
|
+
if (!existsSync(entry)) throw new ConfigError(`piExecutor: cannot load pi extension "${spec}": resolved entry ${entry} does not exist`);
|
|
1500
|
+
return entry;
|
|
1501
|
+
}
|
|
1502
|
+
/** Does this `load` entry name the MCP adapter, before resolution? */
|
|
1503
|
+
function entryNamesAdapter(spec) {
|
|
1504
|
+
if (!isAbsolute(spec)) return spec === PI_MCP_ADAPTER;
|
|
1505
|
+
if (spec.split(/[\\/]/).includes("pi-mcp-adapter")) return true;
|
|
1506
|
+
const parent = spec.slice(0, spec.lastIndexOf(sep) + 1);
|
|
1507
|
+
return parent.length > 0 && readPackageName(parent) === "pi-mcp-adapter";
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Lower `AgentProfile.extensions.pi.load` into pi argv, honoring the `--no-extensions` trap.
|
|
1511
|
+
*
|
|
1512
|
+
* `load` absent (not an array) → no flags at all, so pi keeps its default discovery and the adapter
|
|
1513
|
+
* auto-loads from `settings.json` `packages`. `load` present → `--no-extensions` plus one
|
|
1514
|
+
* `--extension <absolute entry>` per listed extension, which is what makes an ablation arm
|
|
1515
|
+
* reproducible and leak-free.
|
|
1516
|
+
*
|
|
1517
|
+
* When `mcpRequested` is true and an explicit `load` array does not already name the adapter, the
|
|
1518
|
+
* adapter is APPENDED — otherwise `--no-extensions` would suppress the very extension that turns
|
|
1519
|
+
* the `--mcp-config` file into tools. The caller's profile is not touched; the addition is reported
|
|
1520
|
+
* through `adapterInjected` so it reaches the executor receipt and the live progress channel.
|
|
1521
|
+
*
|
|
1522
|
+
* A non-string or blank entry is a schema error here, not a silent skip: an ablation arm that
|
|
1523
|
+
* quietly drops a typo'd extension is the same class of structural false-null this module exists to
|
|
1524
|
+
* eliminate.
|
|
1525
|
+
*/
|
|
1526
|
+
function derivePiExtensionArgs(profile, mcpRequested) {
|
|
1527
|
+
const load = profile?.extensions?.pi?.load;
|
|
1528
|
+
if (!Array.isArray(load)) return {
|
|
1529
|
+
args: [],
|
|
1530
|
+
entries: [],
|
|
1531
|
+
adapterInjected: false
|
|
1532
|
+
};
|
|
1533
|
+
const declared = [];
|
|
1534
|
+
for (const [index, entry] of load.entries()) {
|
|
1535
|
+
if (typeof entry !== "string" || !entry.trim()) throw new ValidationError(`piExecutor: extensions.pi.load[${index}] must be a non-empty string (got ${entry === null ? "null" : typeof entry})`);
|
|
1536
|
+
declared.push(entry.trim());
|
|
1537
|
+
}
|
|
1538
|
+
const adapterInjected = mcpRequested && !declared.some(entryNamesAdapter);
|
|
1539
|
+
const resolvedSpecs = adapterInjected ? [...declared, PI_MCP_ADAPTER] : declared;
|
|
1540
|
+
const npmRoot = piNpmRoot();
|
|
1541
|
+
const entries = resolvedSpecs.map((spec) => resolvePiExtensionEntry(spec, npmRoot));
|
|
1542
|
+
const args = ["--no-extensions"];
|
|
1543
|
+
for (const entry of entries) args.push("--extension", entry);
|
|
1544
|
+
return {
|
|
1545
|
+
args,
|
|
1546
|
+
entries,
|
|
1547
|
+
adapterInjected
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
/** Directory-name-safe form of a run id; the `mkdtemp` suffix supplies the uniqueness. */
|
|
1551
|
+
function runIdSlug(runId) {
|
|
1552
|
+
const slug = runId.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1553
|
+
return slug.length > 0 ? slug.slice(0, 60) : "worker";
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Write this worker execution's MCP config to a directory only it owns, and hand back the removal.
|
|
1557
|
+
*
|
|
1558
|
+
* Every property of the old cwd-discovered mount — a cross-process lock, a merge under whatever the
|
|
1559
|
+
* workspace already had, a verbatim byte restore, a symlink guard on `.pi` — existed because two
|
|
1560
|
+
* workers sharing a `PiSeam` shared one file path. `mkdtemp` deletes the shared path, and with it
|
|
1561
|
+
* the entire class of problem: the directory did not exist a moment ago, nothing else can name it,
|
|
1562
|
+
* `cleanup()` removes it whole, and two siblings started in the same millisecond get two configs.
|
|
1563
|
+
*
|
|
1564
|
+
* Returns `null` when there are no usable servers, so the no-MCP path writes nothing at all.
|
|
1565
|
+
*/
|
|
1566
|
+
function mountPiMcpConfig(mcpServers, options) {
|
|
1567
|
+
const serverNames = Object.keys(mcpServers);
|
|
1568
|
+
if (serverNames.length === 0) return null;
|
|
1569
|
+
const slug = runIdSlug(options.runId);
|
|
1570
|
+
const base = tmpdir();
|
|
1571
|
+
const prefix = `agent-runtime-pi-mcp-${slug}-`;
|
|
1572
|
+
let dir;
|
|
1573
|
+
try {
|
|
1574
|
+
dir = mkdtempSync(join(base, prefix));
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
throw new ConfigError(`piExecutor: cannot create a private MCP config directory under ${base}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1577
|
+
}
|
|
1578
|
+
const configPath = join(dir, "mcp.json");
|
|
1579
|
+
try {
|
|
1580
|
+
writeFileSync(configPath, JSON.stringify({ mcpServers }, null, 2), {
|
|
1581
|
+
flag: "wx",
|
|
1582
|
+
mode: 384
|
|
1583
|
+
});
|
|
1584
|
+
} catch (err) {
|
|
1585
|
+
rmSync(dir, {
|
|
1586
|
+
recursive: true,
|
|
1587
|
+
force: true
|
|
1588
|
+
});
|
|
1589
|
+
throw new ConfigError(`piExecutor: failed to write the MCP config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1590
|
+
}
|
|
1591
|
+
let cleaned = false;
|
|
1592
|
+
return {
|
|
1593
|
+
configPath,
|
|
1594
|
+
serverNames,
|
|
1595
|
+
cleanup: () => {
|
|
1596
|
+
if (cleaned) return;
|
|
1597
|
+
cleaned = true;
|
|
1598
|
+
rmSync(dir, {
|
|
1599
|
+
recursive: true,
|
|
1600
|
+
force: true
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* The one call `piExecutor` makes before spawning pi: decide what pi must be told, fail loud if it
|
|
1607
|
+
* cannot work, then mount.
|
|
1608
|
+
*
|
|
1609
|
+
* Order matters and is the point:
|
|
1610
|
+
*
|
|
1611
|
+
* 1. Canonicalize `profile.mcp` first, so the adapter gate keys off servers that would ACTUALLY be
|
|
1612
|
+
* written — a block of `enabled:false` entries must not trip it.
|
|
1613
|
+
* 2. If any server survives and `pi-mcp-adapter` is not installed, throw. No file has been written
|
|
1614
|
+
* and pi has not been spawned.
|
|
1615
|
+
* 3. Lower `extensions.pi.load` (which may itself throw on an unresolvable extension) — still before
|
|
1616
|
+
* any file exists.
|
|
1617
|
+
* 4. Only then create this execution's private config directory and emit `--mcp-config`.
|
|
1618
|
+
*
|
|
1619
|
+
* A profile with no usable MCP server is a pure no-op: no adapter check, no directory, no flags, no
|
|
1620
|
+
* throw. `extensions.pi.load` on its own still produces flags, because reproducible ablation arms
|
|
1621
|
+
* need them whether or not MCP is involved.
|
|
1622
|
+
*
|
|
1623
|
+
* The caller MUST call `mount.cleanup()` on every exit path — settle, throw, abort, timeout, and
|
|
1624
|
+
* spawn failure — so the directory does not outlive the worker that owns it.
|
|
1625
|
+
*/
|
|
1626
|
+
function preparePiMcp(profile, options) {
|
|
1627
|
+
const mcpServers = buildPiMcpServers(profile?.mcp);
|
|
1628
|
+
const requested = Object.keys(mcpServers);
|
|
1629
|
+
if (requested.length > 0 && !piMcpAdapterAvailable()) throw new ConfigError(`piExecutor: cannot mount MCP servers for pi (${requested.join(", ")}): the ${PI_MCP_ADAPTER} extension is not installed. pi ships no MCP support of its own, so these servers would never connect and the worker would run tool-less. Looked in ${join(piNpmRoot(), "node_modules", PI_MCP_ADAPTER)} and in the \`packages\` list of ${join(piAgentDir(), "settings.json")}. Install it with \`pi install npm:${PI_MCP_ADAPTER}\`, or set ${PI_MCP_ADAPTER_ENV}=1 if it is vendored somewhere this check cannot see.`);
|
|
1630
|
+
const extensions = derivePiExtensionArgs(profile, requested.length > 0);
|
|
1631
|
+
if (requested.length === 0) return {
|
|
1632
|
+
args: extensions.args,
|
|
1633
|
+
mount: null,
|
|
1634
|
+
receipt: void 0
|
|
1635
|
+
};
|
|
1636
|
+
const mount = mountPiMcpConfig(mcpServers, options);
|
|
1637
|
+
if (!mount) return {
|
|
1638
|
+
args: extensions.args,
|
|
1639
|
+
mount: null,
|
|
1640
|
+
receipt: void 0
|
|
1641
|
+
};
|
|
1642
|
+
return {
|
|
1643
|
+
args: [
|
|
1644
|
+
...extensions.args,
|
|
1645
|
+
PI_MCP_CONFIG_FLAG,
|
|
1646
|
+
mount.configPath
|
|
1647
|
+
],
|
|
1648
|
+
mount,
|
|
1649
|
+
receipt: {
|
|
1650
|
+
servers: mount.serverNames,
|
|
1651
|
+
configPath: mount.configPath,
|
|
1652
|
+
extensions: extensions.entries,
|
|
1653
|
+
adapterInjected: extensions.adapterInjected
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
//#endregion
|
|
1276
1658
|
//#region src/runtime/supervise/trace-source.ts
|
|
1277
1659
|
/** Project a normalized tool step into the canonical agent-eval `ToolSpan`. When the step carries
|
|
1278
1660
|
* real wall-clock (`startedAt`/`endedAt`) the span gets a true duration; otherwise it collapses to
|
|
@@ -1510,6 +1892,32 @@ function sandboxSessionTraceSource(box, sessionId, opts = {}) {
|
|
|
1510
1892
|
* usage whose dollar price is absent or zero stays explicitly unknown through the live usage
|
|
1511
1893
|
* stream and terminal artifact.
|
|
1512
1894
|
*
|
|
1895
|
+
* ## What of the `AgentProfile` this executor honors, and what it does not
|
|
1896
|
+
*
|
|
1897
|
+
* This adapter is NOT a profile materializer. It reads a deliberately small part of
|
|
1898
|
+
* `spec.profile`, and a caller who needs the rest must lower it before handing the profile over.
|
|
1899
|
+
* Stated exhaustively so a silent drop is never a surprise:
|
|
1900
|
+
*
|
|
1901
|
+
* | Field | Status |
|
|
1902
|
+
* | --- | --- |
|
|
1903
|
+
* | `profile.name` | honored — trace `runId` label only, no behavioral effect |
|
|
1904
|
+
* | `profile.prompt.systemPrompt` | honored — prepended to the task text (pi RPC takes no separate system-prompt channel) |
|
|
1905
|
+
* | `profile.mcp` | honored — written to this execution's own file and passed as `--mcp-config` for `pi-mcp-adapter`; see `pi-mcp.ts` |
|
|
1906
|
+
* | `profile.extensions.pi.load` | honored — lowered to `--no-extensions` + `--extension <abs>` |
|
|
1907
|
+
* | `profile.prompt.instructions` | DROPPED — fold into `systemPrompt` before calling |
|
|
1908
|
+
* | `profile.model` | DROPPED — the seam's `model` is the only model channel; a profile that disagrees with the seam is silently overridden by the seam |
|
|
1909
|
+
* | `profile.model.reasoningEffort` | DROPPED — no `--thinking` flag is emitted, so pi's configured `defaultThinkingLevel` applies |
|
|
1910
|
+
* | `profile.tools` | DROPPED — no `--no-tools` / allow-deny mapping; pi runs its full builtin tool set |
|
|
1911
|
+
* | `profile.permissions` | DROPPED |
|
|
1912
|
+
* | `profile.resources` (context / skills / commands / subagents / instructions) | DROPPED — nothing is written into the run cwd |
|
|
1913
|
+
* | `profile.hooks` | DROPPED |
|
|
1914
|
+
* | `profile.subagents`, `profile.connections`, `profile.modes`, `profile.confidential` | DROPPED |
|
|
1915
|
+
* | `profile.resources.failOnError` | not consulted — the MCP path above is unconditionally fail-closed, which is the stricter reading |
|
|
1916
|
+
* | every other `profile.extensions.<ns>` | DROPPED — only the `pi` namespace is read |
|
|
1917
|
+
*
|
|
1918
|
+
* Closing those gaps belongs in `@tangle-network/agent-profile-materialize`, whose plan this
|
|
1919
|
+
* executor would then apply, rather than in a second mapping grown here.
|
|
1920
|
+
*
|
|
1513
1921
|
* @experimental
|
|
1514
1922
|
*/
|
|
1515
1923
|
/** The runtime name `piExecutor` registers under. */
|
|
@@ -1521,7 +1929,9 @@ const piExecutor = (spec, ctx) => {
|
|
|
1521
1929
|
const seam = readPiSeam(ctx);
|
|
1522
1930
|
const inbox = createInbox();
|
|
1523
1931
|
const activity = createActivityLog(seam.activityWindow ?? 12);
|
|
1524
|
-
const
|
|
1932
|
+
const runId = `pi-${spec.profile.name ?? "worker"}-${Date.now()}`;
|
|
1933
|
+
const trace = createPushTraceSource({ runId });
|
|
1934
|
+
const derived = [];
|
|
1525
1935
|
const controller = new AbortController();
|
|
1526
1936
|
const cascade = () => controller.abort();
|
|
1527
1937
|
if (ctx.signal.aborted) controller.abort();
|
|
@@ -1540,6 +1950,7 @@ const piExecutor = (spec, ctx) => {
|
|
|
1540
1950
|
turns: state.turns,
|
|
1541
1951
|
pendingMessages: inbox.pending(),
|
|
1542
1952
|
recentActivity: activity.read(),
|
|
1953
|
+
...derived.length > 0 ? { derived: [...derived] } : {},
|
|
1543
1954
|
note: state.note
|
|
1544
1955
|
}),
|
|
1545
1956
|
traceSource: () => trace.source,
|
|
@@ -1550,8 +1961,10 @@ const piExecutor = (spec, ctx) => {
|
|
|
1550
1961
|
controller,
|
|
1551
1962
|
seam,
|
|
1552
1963
|
spec,
|
|
1964
|
+
runId,
|
|
1553
1965
|
inbox,
|
|
1554
1966
|
activity,
|
|
1967
|
+
derived,
|
|
1555
1968
|
record: (step) => {
|
|
1556
1969
|
trace.record(step);
|
|
1557
1970
|
},
|
|
@@ -1588,7 +2001,28 @@ async function* streamPiSession(args) {
|
|
|
1588
2001
|
let usdKnown = true;
|
|
1589
2002
|
throwIfAborted(args.signal);
|
|
1590
2003
|
throwIfAborted(args.controller.signal);
|
|
1591
|
-
const
|
|
2004
|
+
const piMcp = preparePiMcp(args.spec.profile, {
|
|
2005
|
+
...seam.cwd !== void 0 ? { cwd: seam.cwd } : {},
|
|
2006
|
+
runId: args.runId
|
|
2007
|
+
});
|
|
2008
|
+
if (piMcp.receipt) {
|
|
2009
|
+
const injected = piMcp.receipt.adapterInjected ? ` (+${PI_MCP_ADAPTER} added)` : "";
|
|
2010
|
+
activity.push({
|
|
2011
|
+
at: Date.now(),
|
|
2012
|
+
kind: "note",
|
|
2013
|
+
label: "mcp",
|
|
2014
|
+
detail: `${piMcp.receipt.servers.join(", ")}${injected}`
|
|
2015
|
+
});
|
|
2016
|
+
args.derived.push(`mcp: mounted ${piMcp.receipt.servers.join(", ")} via --mcp-config ${piMcp.receipt.configPath}`);
|
|
2017
|
+
if (piMcp.receipt.adapterInjected) args.derived.push(`extensions: added ${PI_MCP_ADAPTER} to extensions.pi.load — the profile's own list would have run under --no-extensions and suppressed it, mounting zero servers`);
|
|
2018
|
+
}
|
|
2019
|
+
let proc;
|
|
2020
|
+
try {
|
|
2021
|
+
proc = spawnPi(seam, piMcp.args);
|
|
2022
|
+
} catch (spawnFailure) {
|
|
2023
|
+
piMcp.mount?.cleanup();
|
|
2024
|
+
throw spawnFailure;
|
|
2025
|
+
}
|
|
1592
2026
|
state.proc = proc;
|
|
1593
2027
|
state.note = "connected";
|
|
1594
2028
|
const events = [];
|
|
@@ -1704,6 +2138,7 @@ async function* streamPiSession(args) {
|
|
|
1704
2138
|
args.signal.removeEventListener("abort", abortAll);
|
|
1705
2139
|
args.controller.signal.removeEventListener("abort", abortAll);
|
|
1706
2140
|
await killPi(proc, 2e3).catch(() => ({ destroyed: false }));
|
|
2141
|
+
piMcp.mount?.cleanup();
|
|
1707
2142
|
}
|
|
1708
2143
|
if (args.signal.aborted || args.controller.signal.aborted) throw abortError();
|
|
1709
2144
|
if (!lastAssistant) throw new ValidationError("piExecutor: agent_settled without an assistant turn");
|
|
@@ -1717,12 +2152,14 @@ async function* streamPiSession(args) {
|
|
|
1717
2152
|
...usdKnown ? {} : { usdKnown: false },
|
|
1718
2153
|
ms: Date.now() - started
|
|
1719
2154
|
};
|
|
2155
|
+
const out = {
|
|
2156
|
+
content: state.lastText,
|
|
2157
|
+
turns: state.turns,
|
|
2158
|
+
...piMcp.receipt ? { mcp: piMcp.receipt } : {}
|
|
2159
|
+
};
|
|
1720
2160
|
state.artifact = {
|
|
1721
2161
|
outRef: `pi:${hash(state.lastText)}`,
|
|
1722
|
-
out
|
|
1723
|
-
content: state.lastText,
|
|
1724
|
-
turns: state.turns
|
|
1725
|
-
},
|
|
2162
|
+
out,
|
|
1726
2163
|
spent
|
|
1727
2164
|
};
|
|
1728
2165
|
}
|
|
@@ -1896,8 +2333,13 @@ function taskText(task) {
|
|
|
1896
2333
|
return String(task);
|
|
1897
2334
|
}
|
|
1898
2335
|
}
|
|
1899
|
-
/**
|
|
1900
|
-
|
|
2336
|
+
/**
|
|
2337
|
+
* Launch `pi --mode rpc`, plus provider/model flags derived from the seam's `provider/model` and
|
|
2338
|
+
* the profile-derived extension flags (`--no-extensions` / `--extension`). Seam args go LAST so an
|
|
2339
|
+
* operator's explicit flag wins over a derived one under pi's last-flag-wins parsing; RPC mode has
|
|
2340
|
+
* no positional prompt, so nothing here has to precede an argument.
|
|
2341
|
+
*/
|
|
2342
|
+
function spawnPi(seam, profileArgs = []) {
|
|
1901
2343
|
const bin = seam.bin ?? "pi";
|
|
1902
2344
|
const argv = ["--mode", "rpc"];
|
|
1903
2345
|
if (seam.model) {
|
|
@@ -1905,6 +2347,7 @@ function spawnPi(seam) {
|
|
|
1905
2347
|
if (slash > 0) argv.push("--provider", seam.model.slice(0, slash), "--model", seam.model.slice(slash + 1));
|
|
1906
2348
|
else argv.push("--model", seam.model);
|
|
1907
2349
|
}
|
|
2350
|
+
argv.push(...profileArgs);
|
|
1908
2351
|
if (seam.args) argv.push(...seam.args);
|
|
1909
2352
|
return spawn(bin, argv, {
|
|
1910
2353
|
...seam.cwd ? { cwd: seam.cwd } : {},
|
|
@@ -4570,7 +5013,7 @@ function createCoordinationTools(opts) {
|
|
|
4570
5013
|
},
|
|
4571
5014
|
{
|
|
4572
5015
|
name: "observe_agent",
|
|
4573
|
-
description: "Inspect a worker WHILE IT RUNS, not only after it finishes: status, spend so far, and `progress` — how long since it last did anything (`idleMs`), whether that counts as stalled, how many turns it has taken, the last tools/files it touched (`recentActivity`), whether a steer can even reach it (`steerable`), and how many steers it has not yet read (`pendingMessages`). Returns the settled output artifact once it exists. Use this BEFORE steer_agent: a steer is only worth sending when the progress says the worker is on the wrong path or has stopped making any.",
|
|
5016
|
+
description: "Inspect a worker WHILE IT RUNS, not only after it finishes: status, spend so far, and `progress` — how long since it last did anything (`idleMs`), whether that counts as stalled, how many turns it has taken, the last tools/files it touched (`recentActivity`), what its executor CHANGED about the profile you gave it (`derived` — an MCP config it materialized, an extension it had to add), whether a steer can even reach it (`steerable`), and how many steers it has not yet read (`pendingMessages`). Returns the settled output artifact once it exists. Use this BEFORE steer_agent: a steer is only worth sending when the progress says the worker is on the wrong path or has stopped making any.",
|
|
4574
5017
|
inputSchema: {
|
|
4575
5018
|
type: "object",
|
|
4576
5019
|
properties: { workerId: idArg },
|
|
@@ -5435,22 +5878,24 @@ function driverAgent(opts) {
|
|
|
5435
5878
|
let driverTurn = 0;
|
|
5436
5879
|
const meteredBrain = async (messages, tools, detail) => {
|
|
5437
5880
|
const res = await opts.brain(messages, tools);
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
}
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5881
|
+
const nothingReported = res.usage === void 0 && res.costUsd === void 0;
|
|
5882
|
+
const turnSpend = {
|
|
5883
|
+
iterations: 0,
|
|
5884
|
+
tokens: {
|
|
5885
|
+
input: res.usage?.input ?? 0,
|
|
5886
|
+
output: res.usage?.output ?? 0
|
|
5887
|
+
},
|
|
5888
|
+
...res.usage === void 0 ? { tokensKnown: false } : {},
|
|
5889
|
+
usd: res.costUsd ?? 0,
|
|
5890
|
+
...nothingReported ? { usdKnown: false } : {},
|
|
5891
|
+
ms: 0
|
|
5892
|
+
};
|
|
5893
|
+
await scope.meter(turnSpend, {
|
|
5894
|
+
driver: opts.name,
|
|
5895
|
+
toolCalls: (res.toolCalls ?? []).map((c) => c.name),
|
|
5896
|
+
...res.usageUnknown === true ? { streamUsageMissing: true } : {},
|
|
5897
|
+
...detail
|
|
5898
|
+
});
|
|
5454
5899
|
return res;
|
|
5455
5900
|
};
|
|
5456
5901
|
const chat = async (messages, tools) => {
|
|
@@ -7927,6 +8372,6 @@ function supervise(profile, task, opts) {
|
|
|
7927
8372
|
return start();
|
|
7928
8373
|
}
|
|
7929
8374
|
//#endregion
|
|
7930
|
-
export { InMemoryDelegationStore as $, DELEGATE_INPUT_SCHEMA as A, createWorktreeCliExecutor as At, profileRichnessFinding as B,
|
|
8375
|
+
export { InMemoryDelegationStore as $, FileCoordinationLog as $t, DELEGATE_INPUT_SCHEMA as A, createWorktreeCliExecutor as At, profileRichnessFinding as B, PI_MCP_ADAPTER_ENV 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, createInbox as Gt, DelegationTaskQueue as H, buildPiMcpServers as Ht, asAuthoredProfile as I, createPushTraceSource as It, composeLoopTraceEmitters as J, runAgentRounds as Jt, buildDelegationTraceSpans as K, createSandboxForSpec 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, acquireSandbox as Qt, authoredWorker as R, sandboxSessionTraceSource as Rt, createDelegateUiAuditHandler as S, rollingDispatch as St, DELEGATE_FEEDBACK_INPUT_SCHEMA as T, createFileRunContext as Tt, hashIdempotencyInput as U, piMcpAdapterAvailable as Ut, supervisorInstructions as V, PI_MCP_CONFIG_FLAG as Vt, DELEGATION_TRACE_MAX_BYTES as W, preparePiMcp as Wt, DelegationPersistenceError as X, createSandboxLineage as Xt, createDelegationTraceCollector as Y, runLoop as Yt, DelegationStateCorruptError as Z, probeSandboxCapabilities as Zt, 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, assertModelAllowed as en, 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, defaultSelectWinner as qt, assertCoordinationBinding as r, finalizeBestDelivered as rt, createInProcessTransport as s, createProgressTracker as st, supervise as t, gateOnDeliverable as tn, 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, PI_MCP_ADAPTER as zt };
|
|
7931
8376
|
|
|
7932
|
-
//# sourceMappingURL=supervise-
|
|
8377
|
+
//# sourceMappingURL=supervise-tdxMJYem.js.map
|