@tangle-network/agent-runtime 0.109.1 → 0.110.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-CM-L6TtL.js → activation-CsuHCZVN.js} +3 -3
- package/dist/{activation-CM-L6TtL.js.map → activation-CsuHCZVN.js.map} +1 -1
- package/dist/agent.d.ts +4 -4
- package/dist/agent.js +2 -2
- package/dist/agent.js.map +1 -1
- package/dist/{analyst-loop-C8cGThTW.js → analyst-loop-BoNIG2hA.js} +30 -9
- package/dist/analyst-loop-BoNIG2hA.js.map +1 -0
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/analyst-loop.js +1 -1
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-nYMa-bc-.js → candidate-execution-Co3sRC_7.js} +4 -4
- package/dist/{candidate-execution-nYMa-bc-.js.map → candidate-execution-Co3sRC_7.js.map} +1 -1
- package/dist/{environment-provider-CfLUmKmf.js → environment-provider-BsHpW4Dp.js} +52 -5
- package/dist/environment-provider-BsHpW4Dp.js.map +1 -0
- package/dist/{environment-provider-CMqSAp-O.d.ts → environment-provider-CTEMEP9u.d.ts} +9 -1
- package/dist/environment-provider.d.ts +1 -1
- package/dist/environment-provider.js +1 -1
- package/dist/{improvement-adapter-BaAdfJKT.d.ts → improvement-adapter-D5gwwoXQ.d.ts} +2 -2
- package/dist/{improvement-cycle-9O4iVFOA.js → improvement-cycle-ryAyCLKN.js} +5 -5
- package/dist/{improvement-cycle-9O4iVFOA.js.map → improvement-cycle-ryAyCLKN.js.map} +1 -1
- package/dist/{index-Cn5TtHUG.d.ts → index-C-Zn0VrN.d.ts} +3 -3
- package/dist/{index-bXiMVrUv.d.ts → index-D_OdNLe0.d.ts} +290 -96
- package/dist/{index-Bur6uUF4.d.ts → index-WODlt7iz.d.ts} +6 -6
- package/dist/index.d.ts +5 -5
- package/dist/index.js +11 -11
- package/dist/intelligence.d.ts +2 -2
- package/dist/intelligence.js +39 -9
- package/dist/intelligence.js.map +1 -1
- package/dist/kernel.d.ts +3 -3
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-1a38bcUT.js → knowledge-DBNAteaC.js} +5 -5
- package/dist/{knowledge-1a38bcUT.js.map → knowledge-DBNAteaC.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-CSzrWlgE.d.ts → loop-runner-bin-BUm83y1g.d.ts} +4 -4
- package/dist/{loop-runner-bin-V4EN9aNT.js → loop-runner-bin-zE7DyGop.js} +4 -4
- package/dist/{loop-runner-bin-V4EN9aNT.js.map → loop-runner-bin-zE7DyGop.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 +5 -5
- package/dist/{openai-tools-DT9FH2_t.js → openai-tools-st-hERvA.js} +2 -2
- package/dist/{openai-tools-DT9FH2_t.js.map → openai-tools-st-hERvA.js.map} +1 -1
- package/dist/{prepare-Pr19X93k.js → prepare-BKVdOn0w.js} +15 -14
- package/dist/prepare-BKVdOn0w.js.map +1 -0
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{protected-model-port-uthtDNqu.js → protected-model-port-BtldAE9j.js} +2 -2
- package/dist/{protected-model-port-uthtDNqu.js.map → protected-model-port-BtldAE9j.js.map} +1 -1
- package/dist/{runtime-BVMyqgct.js → runtime-DSkMJb_X.js} +415 -27
- package/dist/runtime-DSkMJb_X.js.map +1 -0
- package/dist/{spawn-journal-CwPvKUTa.js → spawn-journal-DLhnom3e.js} +2 -2
- package/dist/{spawn-journal-CwPvKUTa.js.map → spawn-journal-DLhnom3e.js.map} +1 -1
- package/dist/{structural-rollout-ASQLr4-v.js → structural-rollout-CgnNy7_4.js} +3 -3
- package/dist/{structural-rollout-ASQLr4-v.js.map → structural-rollout-CgnNy7_4.js.map} +1 -1
- package/dist/{supervise-DyPmmhJ6.js → supervise-CeZtA1wu.js} +223 -107
- package/dist/supervise-CeZtA1wu.js.map +1 -0
- package/dist/{supervisor-sTJC9psT.js → supervisor-ByCPHcp9.js} +82 -24
- package/dist/supervisor-ByCPHcp9.js.map +1 -0
- package/dist/testing.js +9 -9
- package/dist/{types-CfGKhIuj.d.ts → types-zWfqDjeL.d.ts} +10 -1
- package/dist/{util-MVgdwuIS.js → util-Cc9g9Y-o.js} +2 -2
- package/dist/{util-MVgdwuIS.js.map → util-Cc9g9Y-o.js.map} +1 -1
- package/dist/{workspace-archive-C9w_rxt6.js → workspace-archive-CYYxZyA7.js} +2 -2
- package/dist/{workspace-archive-C9w_rxt6.js.map → workspace-archive-CYYxZyA7.js.map} +1 -1
- package/package.json +7 -7
- package/dist/analyst-loop-C8cGThTW.js.map +0 -1
- package/dist/environment-provider-CfLUmKmf.js.map +0 -1
- package/dist/prepare-Pr19X93k.js.map +0 -1
- package/dist/runtime-BVMyqgct.js.map +0 -1
- package/dist/supervise-DyPmmhJ6.js.map +0 -1
- package/dist/supervisor-sTJC9psT.js.map +0 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { n as AnalystError, r as BackendTransportError, s as PlannerError, u as ValidationError } from "./errors-DEAvWQPy.js";
|
|
2
2
|
import { i as normalizeBackendStreamEvent, o as newRuntimeSession, s as nowIso } from "./backends-CiOCyRHb.js";
|
|
3
|
-
import {
|
|
4
|
-
import { i as InMemorySpawnJournal, r as InMemoryResultBlobStore } from "./spawn-journal-
|
|
3
|
+
import { c as sleep, i as isAbortError, l as stringifySafe, n as addTokenUsage, o as randomSuffix, p as zeroTokenUsage } from "./util-Cc9g9Y-o.js";
|
|
4
|
+
import { i as InMemorySpawnJournal, r as InMemoryResultBlobStore } from "./spawn-journal-DLhnom3e.js";
|
|
5
5
|
import { i as redactProtectedValue, r as redactProtectedReason } from "./protected-redaction--F3v1oo8.js";
|
|
6
|
-
import { C as routerChatWithUsage, T as runBrainLoop, d as settledToIteration, l as withDriverExecutor, t as createSupervisor, x as routerBrain } from "./supervisor-
|
|
7
|
-
import { C as observe, O as strategyAuthorMethod, b as sample, v as refine, x as sampleThenRefine, y as runAgentic } from "./structural-rollout-
|
|
6
|
+
import { C as routerChatWithUsage, T as runBrainLoop, d as settledToIteration, l as withDriverExecutor, t as createSupervisor, x as routerBrain } from "./supervisor-ByCPHcp9.js";
|
|
7
|
+
import { C as observe, O as strategyAuthorMethod, b as sample, v as refine, x as sampleThenRefine, y as runAgentic } from "./structural-rollout-CgnNy7_4.js";
|
|
8
8
|
import { i as notifyRuntimeHookEvent } from "./runtime-hooks-C7iJOWm3.js";
|
|
9
9
|
import { a as notifySandboxEventObserver, i as mapSandboxToolEvent, r as mapSandboxEvent, t as createSandboxToolPartState } from "./sandbox-events-DeI5xX8P.js";
|
|
10
|
-
import { Bt as runAgentRounds, Dt as createExecutorRegistry, Et as createExecutor, Ht as createSandboxLineage, Ot as createWorktreeCliExecutor, Ut as probeSandboxCapabilities, bt as rollingDispatch, qt as gateOnDeliverable, t as supervise, zt as defaultSelectWinner } from "./supervise-
|
|
10
|
+
import { Bt as runAgentRounds, Dt as createExecutorRegistry, Et as createExecutor, Ht as createSandboxLineage, Ot as createWorktreeCliExecutor, Ut as probeSandboxCapabilities, bt as rollingDispatch, qt as gateOnDeliverable, t as supervise, zt as defaultSelectWinner } from "./supervise-CeZtA1wu.js";
|
|
11
11
|
import { CODING_HARNESSES, InMemoryTraceStore, benjaminiHochberg, buildTrajectory, computeFindingId as computeFindingId$1, confidenceInterval, expandProfileAxes, harnessAxisOf, makeFinding as makeFinding$1, pairedBootstrap, paretoFrontier, scoreKnowledgeReadiness, wilcoxonSignedRank, wilson } from "@tangle-network/agent-eval";
|
|
12
12
|
import { heldoutSignificance, runProfileMatrix } from "@tangle-network/agent-eval/campaign";
|
|
13
13
|
import { canonicalCandidateDigest, validateAgentProfileSecurity } from "@tangle-network/agent-interface";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
14
15
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { dirname, join } from "node:path";
|
|
17
|
-
import { execFileSync, spawn } from "node:child_process";
|
|
16
|
+
import { appendFileSync, chmodSync, constants, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
18
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
18
19
|
import { tmpdir } from "node:os";
|
|
19
20
|
import { createInterface } from "node:readline";
|
|
20
21
|
import { collectAgentResponseText } from "@tangle-network/sandbox";
|
|
@@ -62,6 +63,52 @@ async function resolveSecretEnv(secretEnv, keys, label) {
|
|
|
62
63
|
}
|
|
63
64
|
return resolved;
|
|
64
65
|
}
|
|
66
|
+
/** Apply a secret-ref's declared decoration to its resolved raw value. */
|
|
67
|
+
function formatSecretValue(value, format) {
|
|
68
|
+
return format === "bearer" ? `Bearer ${value}` : value;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a profile MCP server's `args`/`env` config values (interface ≥0.40
|
|
72
|
+
* `AgentProfileConfigValue`) plus the legacy `metadata.secretEnv` channel into
|
|
73
|
+
* the plain strings a spawn needs.
|
|
74
|
+
*
|
|
75
|
+
* Rules, all fail-closed:
|
|
76
|
+
* - `args` must be public values. A secret-ref in argv is refused: argv is
|
|
77
|
+
* readable by every host process (/proc/PID/cmdline) and outside the
|
|
78
|
+
* protected-value redaction channel, so a secret there cannot be contained.
|
|
79
|
+
* - `env` secret-refs resolve through the KeyProvider (missing provider or key
|
|
80
|
+
* throws, naming the KEY NAME only) and land in `protectedEnv`.
|
|
81
|
+
* - An env var declared secret on BOTH channels (env secret-ref and
|
|
82
|
+
* metadata.secretEnv) is ambiguous configuration and throws.
|
|
83
|
+
* - A public `env` entry shadowed by a legacy metadata secret keeps the
|
|
84
|
+
* pre-0.40 spawn precedence: the secret value wins in the child env.
|
|
85
|
+
*/
|
|
86
|
+
async function resolveMcpServerLaunch(server, keys, label) {
|
|
87
|
+
const args = server.args?.map((value, index) => {
|
|
88
|
+
if (value.kind !== "public") throw new ValidationError(`${label}.args[${index}] is a secret-ref ('${value.key}') — argv is readable by every host process and exempt from redaction; declare the secret in env instead`);
|
|
89
|
+
return value.value;
|
|
90
|
+
});
|
|
91
|
+
const publicEnv = {};
|
|
92
|
+
const envSecretRefs = {};
|
|
93
|
+
const envSecretFormats = {};
|
|
94
|
+
for (const [envName, value] of Object.entries(server.env ?? {})) if (value.kind === "public") publicEnv[envName] = value.value;
|
|
95
|
+
else {
|
|
96
|
+
envSecretRefs[envName] = value.key;
|
|
97
|
+
envSecretFormats[envName] = value.format;
|
|
98
|
+
}
|
|
99
|
+
const legacySecretEnv = secretEnvOfMcpServer(server);
|
|
100
|
+
for (const envName of Object.keys(legacySecretEnv ?? {})) if (envName in envSecretRefs) throw new ValidationError(`${label}: env ${envName} is declared secret twice — once as an env secret-ref and once in metadata.${mcpSecretEnvMetadataKey}; keep exactly one declaration`);
|
|
101
|
+
const resolvedRefs = await resolveSecretEnv(envSecretRefs, keys, label);
|
|
102
|
+
const protectedEnv = {
|
|
103
|
+
...legacySecretEnv ? await resolveSecretEnv(legacySecretEnv, keys, label) : {},
|
|
104
|
+
...Object.fromEntries(Object.entries(resolvedRefs).map(([envName, raw]) => [envName, formatSecretValue(raw, envSecretFormats[envName])]))
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
...args ? { args } : {},
|
|
108
|
+
...server.env ? { env: publicEnv } : {},
|
|
109
|
+
...Object.keys(protectedEnv).length > 0 ? { protectedEnv } : {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
65
112
|
//#endregion
|
|
66
113
|
//#region src/runtime/mcp-environment.ts
|
|
67
114
|
/** POST JSON-RPC and parse a JSON body OR the last `data:` line of an SSE stream.
|
|
@@ -385,14 +432,13 @@ async function materializeLocalMcp(profile, opts = {}) {
|
|
|
385
432
|
const transport = server.transport ?? "stdio";
|
|
386
433
|
if (transport !== "stdio") throw new ValidationError(`materializeLocalMcp: profile.mcp['${key}'] has transport '${transport}' — the same-host client only spawns stdio servers; a remote (http/sse) server needs an http MCP client (not yet built here) or the sandbox backend. Failing loud: scoring this profile without its declared server would fake the with/without ablation`);
|
|
387
434
|
if (!server.command || server.command.trim().length === 0) throw new ValidationError(`materializeLocalMcp: profile.mcp['${key}'] declares a stdio server with no command`);
|
|
388
|
-
const
|
|
389
|
-
const provisioned = secretRefs ? await resolveSecretEnv(secretRefs, opts.keys, `materializeLocalMcp: profile.mcp['${key}']`) : void 0;
|
|
435
|
+
const launch = await resolveMcpServerLaunch(server, opts.keys, `materializeLocalMcp: profile.mcp['${key}']`);
|
|
390
436
|
const conn = await connectStdioMcp({
|
|
391
437
|
command: server.command,
|
|
392
|
-
...
|
|
438
|
+
...launch.args ? { args: launch.args } : {},
|
|
393
439
|
...server.cwd ? { cwd: server.cwd } : {},
|
|
394
|
-
...
|
|
395
|
-
...
|
|
440
|
+
...launch.env ? { env: launch.env } : {},
|
|
441
|
+
...launch.protectedEnv ? { protectedEnv: launch.protectedEnv } : {},
|
|
396
442
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {}
|
|
397
443
|
});
|
|
398
444
|
connections.push(conn);
|
|
@@ -3096,13 +3142,12 @@ function requireSpend(rolled, id, root) {
|
|
|
3096
3142
|
/**
|
|
3097
3143
|
* promotionGate — the statistical promotion decision over a holdout benchmark: does the
|
|
3098
3144
|
* candidate strategy beat the incumbent on held-out tasks by a margin the task noise
|
|
3099
|
-
* cannot fake? The statistics
|
|
3100
|
-
*
|
|
3101
|
-
*
|
|
3102
|
-
*
|
|
3103
|
-
* coin-flip rates; this gate is the instrument-grade replacement.
|
|
3145
|
+
* cannot fake? The statistics come from `heldoutSignificance`, which chooses the
|
|
3146
|
+
* decision interval that matches the outcome shape, applies a minimum-evidence floor,
|
|
3147
|
+
* and requires the interval's lower bound to clear `deltaThreshold`. A raw h1>h0 point
|
|
3148
|
+
* comparison on m≈8 holdout tasks certifies false champions at near coin-flip rates.
|
|
3104
3149
|
*/
|
|
3105
|
-
/** Statistical promotion decision over a holdout benchmark
|
|
3150
|
+
/** Statistical promotion decision over a holdout benchmark using the outcome-appropriate interval selected by `heldoutSignificance`. */
|
|
3106
3151
|
function promotionGate(opts) {
|
|
3107
3152
|
const mode = opts.mode ?? "superiority";
|
|
3108
3153
|
if (opts.candidate === opts.incumbent) return {
|
|
@@ -3151,8 +3196,8 @@ function promotionGate(opts) {
|
|
|
3151
3196
|
const lift = {
|
|
3152
3197
|
mean: sig.bootstrap.mean,
|
|
3153
3198
|
median: sig.bootstrap.median,
|
|
3154
|
-
low: sig.
|
|
3155
|
-
high: sig.
|
|
3199
|
+
low: sig.decision.low,
|
|
3200
|
+
high: sig.decision.high
|
|
3156
3201
|
};
|
|
3157
3202
|
const latSig = heldoutSignificance({
|
|
3158
3203
|
before: incMs,
|
|
@@ -3168,8 +3213,8 @@ function promotionGate(opts) {
|
|
|
3168
3213
|
const latency = {
|
|
3169
3214
|
mean: latSig.bootstrap.mean,
|
|
3170
3215
|
median: latSig.bootstrap.median,
|
|
3171
|
-
low: latSig.
|
|
3172
|
-
high: latSig.
|
|
3216
|
+
low: latSig.decision.low,
|
|
3217
|
+
high: latSig.decision.high
|
|
3173
3218
|
};
|
|
3174
3219
|
if (mode === "superiority") {
|
|
3175
3220
|
if (sig.fewRuns) return {
|
|
@@ -3222,8 +3267,8 @@ function promotionGate(opts) {
|
|
|
3222
3267
|
const costSavings = {
|
|
3223
3268
|
mean: costSig.bootstrap.mean,
|
|
3224
3269
|
median: costSig.bootstrap.median,
|
|
3225
|
-
low: costSig.
|
|
3226
|
-
high: costSig.
|
|
3270
|
+
low: costSig.decision.low,
|
|
3271
|
+
high: costSig.decision.high
|
|
3227
3272
|
};
|
|
3228
3273
|
if (scoreSig.fewRuns) return {
|
|
3229
3274
|
promoted: false,
|
|
@@ -4727,6 +4772,123 @@ function signalPass(value, required) {
|
|
|
4727
4772
|
return !required;
|
|
4728
4773
|
}
|
|
4729
4774
|
//#endregion
|
|
4775
|
+
//#region src/runtime/supervise/run-layout.ts
|
|
4776
|
+
/**
|
|
4777
|
+
* The on-disk supervisor-run layout: `<root>/.loops/supervisor/<id>`.
|
|
4778
|
+
*
|
|
4779
|
+
* This is the durable, cross-process face of a supervisor run — the counterpart to the in-process
|
|
4780
|
+
* `Inbox` seam in `./inbox`. A run persists its state under one directory so that any OTHER process
|
|
4781
|
+
* can find it after the fact: `@tangle-network/traces` reads exactly this layout
|
|
4782
|
+
* (`traces analyze --supervisor-run-dir` expects `<runDir>/ws/.loops/supervisor/<id>`), a restarted
|
|
4783
|
+
* host can rehydrate a run it no longer holds handles to, and a human can steer a live worker by
|
|
4784
|
+
* appending one NDJSON line. Until now the layout was defined only in the unpublished `loops` repo
|
|
4785
|
+
* (`src/supervisor-control.ts`) — a published reader depending on an unpublished writer's
|
|
4786
|
+
* convention — so the contract is promoted here, names preserved.
|
|
4787
|
+
*
|
|
4788
|
+
* Layout, relative to `supervisorRunDir(root, id)`:
|
|
4789
|
+
*
|
|
4790
|
+
* workers/<label>.inbox.ndjson down-leg steer/answer requests for one worker (durable inbox);
|
|
4791
|
+
* each line is a {@link WorkerSteerRequest}
|
|
4792
|
+
* workers/<label>.ndjson best-effort per-worker control-event log (delivery bookkeeping)
|
|
4793
|
+
*
|
|
4794
|
+
* Reads are tolerant by contract: a partial trailing line (a writer mid-append) or a corrupt line
|
|
4795
|
+
* never poisons the rest of the file — later valid lines still matter.
|
|
4796
|
+
*
|
|
4797
|
+
* Promoted from `loops/src/supervisor-control.ts`. The one deliberate difference: the loops version
|
|
4798
|
+
* resolved a worker id to its label through the run journal before writing a steer; that resolution
|
|
4799
|
+
* stays with the caller (it is journal-format-specific), so `writeWorkerSteer` here takes the worker
|
|
4800
|
+
* LABEL directly.
|
|
4801
|
+
*
|
|
4802
|
+
* @experimental
|
|
4803
|
+
*/
|
|
4804
|
+
/** The run directory every artifact of one supervisor run lives under. */
|
|
4805
|
+
function supervisorRunDir(rootDir, id) {
|
|
4806
|
+
return join(resolve(rootDir), ".loops", "supervisor", id);
|
|
4807
|
+
}
|
|
4808
|
+
/** A worker label reduced to a safe filename stem. Empty labels get a stable fallback. */
|
|
4809
|
+
function safeWorkerFile(label) {
|
|
4810
|
+
const safe = label.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
4811
|
+
return safe.length > 0 ? safe : "worker";
|
|
4812
|
+
}
|
|
4813
|
+
/** The durable inbox file for one worker of one run. */
|
|
4814
|
+
function workerInboxFile(rootDir, supervisorId, worker) {
|
|
4815
|
+
return workerInboxFileFromEventDir(supervisorRunDir(rootDir, supervisorId), worker);
|
|
4816
|
+
}
|
|
4817
|
+
/** Same, addressed from an already-known run directory (the reader's usual entry point). */
|
|
4818
|
+
function workerInboxFileFromEventDir(eventDir, worker) {
|
|
4819
|
+
return join(eventDir, "workers", `${safeWorkerFile(worker)}.inbox.ndjson`);
|
|
4820
|
+
}
|
|
4821
|
+
/**
|
|
4822
|
+
* Durably append one steer request to a worker's inbox and log the delivery attempt.
|
|
4823
|
+
*
|
|
4824
|
+
* The inbox append is the durable act; the control-event log is best-effort bookkeeping and may
|
|
4825
|
+
* silently fail without voiding the steer.
|
|
4826
|
+
*/
|
|
4827
|
+
function writeWorkerSteer(rootDir, supervisorId, worker, message, source = "human") {
|
|
4828
|
+
const trimmed = message.trim();
|
|
4829
|
+
if (!trimmed) throw new Error("steer message is empty");
|
|
4830
|
+
const dir = supervisorRunDir(rootDir, supervisorId);
|
|
4831
|
+
mkdirSync(join(dir, "workers"), { recursive: true });
|
|
4832
|
+
const request = {
|
|
4833
|
+
id: randomUUID(),
|
|
4834
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4835
|
+
source,
|
|
4836
|
+
worker,
|
|
4837
|
+
message: trimmed
|
|
4838
|
+
};
|
|
4839
|
+
const file = workerInboxFile(rootDir, supervisorId, worker);
|
|
4840
|
+
appendFileSync(file, `${JSON.stringify(request)}\n`, "utf8");
|
|
4841
|
+
appendWorkerControlEvent(dir, worker, {
|
|
4842
|
+
kind: "message",
|
|
4843
|
+
direction: "down",
|
|
4844
|
+
source,
|
|
4845
|
+
requestId: request.id,
|
|
4846
|
+
message: trimmed,
|
|
4847
|
+
queued: true,
|
|
4848
|
+
delivered: false
|
|
4849
|
+
});
|
|
4850
|
+
return {
|
|
4851
|
+
worker,
|
|
4852
|
+
file,
|
|
4853
|
+
request
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
/** Read every valid steer request in a worker's inbox. Corrupt or partial lines are skipped. */
|
|
4857
|
+
function readWorkerSteerRequests(eventDir, worker) {
|
|
4858
|
+
const file = workerInboxFileFromEventDir(eventDir, worker);
|
|
4859
|
+
if (!existsSync(file)) return [];
|
|
4860
|
+
const out = [];
|
|
4861
|
+
let raw = "";
|
|
4862
|
+
try {
|
|
4863
|
+
raw = readFileSync(file, "utf8");
|
|
4864
|
+
} catch {
|
|
4865
|
+
return out;
|
|
4866
|
+
}
|
|
4867
|
+
for (const line of raw.split("\n")) {
|
|
4868
|
+
const trimmed = line.trim();
|
|
4869
|
+
if (!trimmed) continue;
|
|
4870
|
+
try {
|
|
4871
|
+
const parsed = JSON.parse(trimmed);
|
|
4872
|
+
if (isWorkerSteerRequest(parsed)) out.push(parsed);
|
|
4873
|
+
} catch {}
|
|
4874
|
+
}
|
|
4875
|
+
return out;
|
|
4876
|
+
}
|
|
4877
|
+
function appendWorkerControlEvent(eventDir, label, event) {
|
|
4878
|
+
try {
|
|
4879
|
+
const workersDir = join(eventDir, "workers");
|
|
4880
|
+
mkdirSync(workersDir, { recursive: true });
|
|
4881
|
+
appendFileSync(join(workersDir, `${safeWorkerFile(label)}.ndjson`), `${JSON.stringify({
|
|
4882
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4883
|
+
label,
|
|
4884
|
+
...event
|
|
4885
|
+
})}\n`, "utf8");
|
|
4886
|
+
} catch {}
|
|
4887
|
+
}
|
|
4888
|
+
function isWorkerSteerRequest(value) {
|
|
4889
|
+
return typeof value.id === "string" && typeof value.at === "string" && typeof value.source === "string" && typeof value.worker === "string" && typeof value.message === "string" && value.message.trim().length > 0;
|
|
4890
|
+
}
|
|
4891
|
+
//#endregion
|
|
4730
4892
|
//#region src/runtime/supervise/trajectory-recorder.ts
|
|
4731
4893
|
/**
|
|
4732
4894
|
*
|
|
@@ -4789,6 +4951,232 @@ async function analyzeTrace(source, runId = "worker") {
|
|
|
4789
4951
|
};
|
|
4790
4952
|
}
|
|
4791
4953
|
//#endregion
|
|
4954
|
+
//#region src/runtime/supervise/untracked-clone.ts
|
|
4955
|
+
/**
|
|
4956
|
+
* Worker-clone preparation: make each worker's fresh clone a FAITHFUL copy of the
|
|
4957
|
+
* shared workspace's working tree, not just of its git history.
|
|
4958
|
+
*
|
|
4959
|
+
* `runInWorkspace` materializes a worker dir via `git clone`, which carries only
|
|
4960
|
+
* COMMITTED files. Real workspaces (SWE-bench instance images: astropy,
|
|
4961
|
+
* matplotlib) hold compiled extension modules (.so/.pyd) as UNTRACKED build
|
|
4962
|
+
* artifacts — inside a bare clone `import astropy` dies with "cannot import name
|
|
4963
|
+
* '_compiler'" before the worker's first edit, so its verify gate is physically
|
|
4964
|
+
* unpassable regardless of patch quality (observed: 7/7 astropy and 6/6
|
|
4965
|
+
* matplotlib workers failed exactly this way while the shared workspace verified
|
|
4966
|
+
* fine). The fix copies the source tree's untracked files — including git-ignored
|
|
4967
|
+
* build outputs — into the clone right after materialize and before the worker
|
|
4968
|
+
* starts.
|
|
4969
|
+
*
|
|
4970
|
+
* Isolation is preserved: artifacts are COPIED (copy-on-write via FICLONE where
|
|
4971
|
+
* the filesystem supports it — never hardlinked, a worker rebuild opening an
|
|
4972
|
+
* artifact with O_TRUNC on a shared inode would corrupt the source workspace for
|
|
4973
|
+
* every sibling), and an absolute symlink pointing inside the source tree is
|
|
4974
|
+
* rewritten to the clone-local path so no worker holds a live handle back into
|
|
4975
|
+
* shared mutable state.
|
|
4976
|
+
*
|
|
4977
|
+
* Fidelity stops at git history: every copied path is appended to the clone's
|
|
4978
|
+
* `.git/info/exclude`, so the clone-side `git add -A` (both the local delegate's
|
|
4979
|
+
* diff capture and `gitWorkspace.commit`) can never stage pre-existing build
|
|
4980
|
+
* artifacts into the shared ref — in the source they are untracked, and a
|
|
4981
|
+
* delivery must not silently start tracking them.
|
|
4982
|
+
*
|
|
4983
|
+
* Promoted verbatim from `loops/src/worker-clone.ts` (see agent-dev-container#4519): any harness
|
|
4984
|
+
* that materializes workers via `git clone` needs the working tree, not just the history.
|
|
4985
|
+
*
|
|
4986
|
+
* @experimental
|
|
4987
|
+
*/
|
|
4988
|
+
/** Loop-infra dirs living inside a workspace; orchestration state, never build inputs. */
|
|
4989
|
+
const SKIPPED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
4990
|
+
".loops",
|
|
4991
|
+
".evolve",
|
|
4992
|
+
".agent-worktrees",
|
|
4993
|
+
".claude"
|
|
4994
|
+
]);
|
|
4995
|
+
/** Warn (and copy anyway) past this untracked payload size. */
|
|
4996
|
+
const DEFAULT_WARN_BYTES = 2 * 1024 * 1024 * 1024;
|
|
4997
|
+
/**
|
|
4998
|
+
* Copy every untracked file of `sourceDir`'s working tree — including git-ignored
|
|
4999
|
+
* build outputs (`git ls-files --others` with NO exclude flags lists both) — into
|
|
5000
|
+
* `cloneDir`, then shield the copied paths from the clone's `git add -A` via
|
|
5001
|
+
* `.git/info/exclude`. Nested git repos (listed as bare `dir/` entries), any path
|
|
5002
|
+
* containing a `.git` segment, and loop-infra dirs are skipped.
|
|
5003
|
+
*/
|
|
5004
|
+
function copyUntrackedIntoClone(sourceDir, cloneDir, opts = {}) {
|
|
5005
|
+
const warnBytes = opts.warnBytes ?? DEFAULT_WARN_BYTES;
|
|
5006
|
+
const log = opts.log ?? ((message) => console.warn(message));
|
|
5007
|
+
const src = resolve(sourceDir);
|
|
5008
|
+
const dst = resolve(cloneDir);
|
|
5009
|
+
const ls = spawnSync("git", [
|
|
5010
|
+
"-C",
|
|
5011
|
+
src,
|
|
5012
|
+
"ls-files",
|
|
5013
|
+
"--others",
|
|
5014
|
+
"-z"
|
|
5015
|
+
], {
|
|
5016
|
+
encoding: "utf8",
|
|
5017
|
+
maxBuffer: 256 * 1024 * 1024
|
|
5018
|
+
});
|
|
5019
|
+
if (ls.status !== 0) throw new Error(`git ls-files --others failed in ${src}: ${(ls.stderr ?? "").trim() || `exit ${ls.status}`}`);
|
|
5020
|
+
const plan = [];
|
|
5021
|
+
for (const rel of (ls.stdout ?? "").split("\0")) {
|
|
5022
|
+
if (!rel || rel.endsWith("/")) continue;
|
|
5023
|
+
const segments = rel.split("/");
|
|
5024
|
+
if (segments.includes(".git")) continue;
|
|
5025
|
+
if (SKIPPED_TOP_LEVEL.has(segments[0] ?? "")) continue;
|
|
5026
|
+
let stat;
|
|
5027
|
+
try {
|
|
5028
|
+
stat = lstatSync(join(src, rel));
|
|
5029
|
+
} catch {
|
|
5030
|
+
continue;
|
|
5031
|
+
}
|
|
5032
|
+
if (stat.isSymbolicLink()) plan.push({
|
|
5033
|
+
rel,
|
|
5034
|
+
kind: "symlink",
|
|
5035
|
+
size: 0,
|
|
5036
|
+
mode: stat.mode
|
|
5037
|
+
});
|
|
5038
|
+
else if (stat.isFile()) plan.push({
|
|
5039
|
+
rel,
|
|
5040
|
+
kind: "file",
|
|
5041
|
+
size: stat.size,
|
|
5042
|
+
mode: stat.mode
|
|
5043
|
+
});
|
|
5044
|
+
}
|
|
5045
|
+
const bytes = plan.reduce((sum, entry) => sum + entry.size, 0);
|
|
5046
|
+
if (bytes > warnBytes) log(`worker clone: untracked payload in ${src} is ${(bytes / 1024 / 1024).toFixed(0)}MB (> ${(warnBytes / 1024 / 1024).toFixed(0)}MB) — copying anyway so the clone stays runnable`);
|
|
5047
|
+
let copied = 0;
|
|
5048
|
+
const excludeLines = [];
|
|
5049
|
+
for (const entry of plan) {
|
|
5050
|
+
const from = join(src, entry.rel);
|
|
5051
|
+
const to = join(dst, entry.rel);
|
|
5052
|
+
try {
|
|
5053
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
5054
|
+
if (entry.kind === "symlink") {
|
|
5055
|
+
let target = readlinkSync(from);
|
|
5056
|
+
if (isAbsolute(target)) {
|
|
5057
|
+
const resolved = resolve(target);
|
|
5058
|
+
if (resolved === src || resolved.startsWith(src + sep)) target = join(dst, relative(src, resolved));
|
|
5059
|
+
}
|
|
5060
|
+
rmSync(to, { force: true });
|
|
5061
|
+
symlinkSync(target, to);
|
|
5062
|
+
} else {
|
|
5063
|
+
copyFileSync(from, to, constants.COPYFILE_FICLONE);
|
|
5064
|
+
chmodSync(to, entry.mode & 4095);
|
|
5065
|
+
}
|
|
5066
|
+
copied += 1;
|
|
5067
|
+
excludeLines.push(toExcludePattern(entry.rel));
|
|
5068
|
+
} catch (err) {
|
|
5069
|
+
log(`worker clone: failed to copy untracked ${entry.rel}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
if (excludeLines.length > 0) {
|
|
5073
|
+
const infoDir = join(dst, ".git", "info");
|
|
5074
|
+
mkdirSync(infoDir, { recursive: true });
|
|
5075
|
+
appendFileSync(join(infoDir, "exclude"), `# untracked artifacts copied from the shared workspace — never commit them back\n${excludeLines.join("\n")}\n`, "utf8");
|
|
5076
|
+
}
|
|
5077
|
+
return {
|
|
5078
|
+
copied,
|
|
5079
|
+
bytes
|
|
5080
|
+
};
|
|
5081
|
+
}
|
|
5082
|
+
/** Root-anchored literal exclude pattern: glob metachars and trailing spaces escaped. */
|
|
5083
|
+
function toExcludePattern(rel) {
|
|
5084
|
+
return `/${rel.replace(/([\\*?[\]])/g, "\\$1").replace(/ +$/, (spaces) => "\\ ".repeat(spaces.length))}`;
|
|
5085
|
+
}
|
|
5086
|
+
/**
|
|
5087
|
+
* Wrap a `Workspace` so every `materialize` (the per-worker `git clone` inside
|
|
5088
|
+
* `runInWorkspace`) is followed by the untracked-artifact copy above — the clone
|
|
5089
|
+
* the worker starts in matches the source WORKING TREE, not just its history.
|
|
5090
|
+
* `commit`/`head` pass through untouched, so delivery semantics are unchanged.
|
|
5091
|
+
*/
|
|
5092
|
+
function withUntrackedArtifacts(ws, sourceDir, log) {
|
|
5093
|
+
return {
|
|
5094
|
+
ref: ws.ref,
|
|
5095
|
+
materialize: async (dir) => {
|
|
5096
|
+
await ws.materialize(dir);
|
|
5097
|
+
copyUntrackedIntoClone(sourceDir, dir, log ? { log } : {});
|
|
5098
|
+
},
|
|
5099
|
+
commit: (dir, message) => ws.commit(dir, message),
|
|
5100
|
+
head: () => ws.head()
|
|
5101
|
+
};
|
|
5102
|
+
}
|
|
5103
|
+
//#endregion
|
|
5104
|
+
//#region src/runtime/supervise/worker-evidence.ts
|
|
5105
|
+
/**
|
|
5106
|
+
* Evidence-rich worker settlement.
|
|
5107
|
+
*
|
|
5108
|
+
* The supervisor BRAIN decides respawn/steer/stop from what a settled worker
|
|
5109
|
+
* shows it. A bare `finished passed=false patchBytes=6138` starves that
|
|
5110
|
+
* decision: the brain cannot quote the failing assertion, so its next worker
|
|
5111
|
+
* goal is authored blind. This module composes the bounded evidence block a
|
|
5112
|
+
* settling worker appends to its event stream and — on failure — returns as
|
|
5113
|
+
* its output artifact, so `observe_agent` hands the brain the failing verify
|
|
5114
|
+
* tail, a diff summary, and the worker's final note instead of counters.
|
|
5115
|
+
*/
|
|
5116
|
+
/** Hard cap on one worker's evidence block so the brain's context cannot blow up. */
|
|
5117
|
+
const EVIDENCE_MAX_CHARS = 3e3;
|
|
5118
|
+
/** Tail of the verify output — the failing assertion lives at the END of a test log. */
|
|
5119
|
+
const VERIFY_TAIL_CHARS = 1200;
|
|
5120
|
+
const PATCH_HEAD_LINES = 40;
|
|
5121
|
+
const PATCH_LINE_MAX_CHARS = 200;
|
|
5122
|
+
/** Cap on the worker's closing note inside the evidence block. */
|
|
5123
|
+
const NOTE_MAX_CHARS = 300;
|
|
5124
|
+
/**
|
|
5125
|
+
* Compose the settle evidence block. Section order is priority order under the
|
|
5126
|
+
* hard cap: the verify tail (what failed) survives before the diff head (what
|
|
5127
|
+
* was tried) and the worker note — a truncated diff is recoverable from the
|
|
5128
|
+
* persisted patch file, a truncated failing assertion is not recoverable at all.
|
|
5129
|
+
*/
|
|
5130
|
+
function composeWorkerEvidence(input) {
|
|
5131
|
+
const sections = [];
|
|
5132
|
+
const failures = [...input.testPassed ? [] : ["tests failed"], ...input.typecheckPassed ? [] : ["typecheck failed"]];
|
|
5133
|
+
sections.push(input.passed ? "verify PASSED" : `verify FAILED (${failures.join(", ") || "gate not passed"})`);
|
|
5134
|
+
const testTail = input.testOutput.trim().slice(-1200);
|
|
5135
|
+
if (testTail) sections.push(`--- verify output (tail) ---\n${testTail}`);
|
|
5136
|
+
if (!input.typecheckPassed) {
|
|
5137
|
+
const typecheckTail = input.typecheckOutput.trim().slice(-400);
|
|
5138
|
+
if (typecheckTail) sections.push(`--- typecheck output (tail) ---\n${typecheckTail}`);
|
|
5139
|
+
}
|
|
5140
|
+
sections.push(diffSummary(input.patch));
|
|
5141
|
+
const note = input.reviewerNotes?.trim();
|
|
5142
|
+
if (note) sections.push(`--- worker note ---\n${note.slice(0, 300)}`);
|
|
5143
|
+
return sections.join("\n").slice(0, EVIDENCE_MAX_CHARS);
|
|
5144
|
+
}
|
|
5145
|
+
/**
|
|
5146
|
+
* What a settled worker exposes as its output artifact (the blob the brain's
|
|
5147
|
+
* `observe_agent` reads). A passing worker's output is its patch — the
|
|
5148
|
+
* deliverable. A failing worker's output is its evidence block. A passing
|
|
5149
|
+
* worker with NO edits — the post-delivery read-only reviewer: the workspace
|
|
5150
|
+
* already holds a delivered fix, so the gate stays green under an empty diff —
|
|
5151
|
+
* would otherwise settle with an EMPTY output and its review would be lost, so
|
|
5152
|
+
* it exposes its evidence block instead (the worker note carries the review).
|
|
5153
|
+
*/
|
|
5154
|
+
function settledWorkerOut(input) {
|
|
5155
|
+
if (input.evidence.length === 0) return input.patch;
|
|
5156
|
+
if (!input.passed) return input.evidence;
|
|
5157
|
+
return input.patch.trim().length > 0 ? input.patch : input.evidence;
|
|
5158
|
+
}
|
|
5159
|
+
/**
|
|
5160
|
+
* The worker's closing commentary off a local harness run: the TAIL of its
|
|
5161
|
+
* stdout (falling back to stderr), bounded to the note cap so a reviewer's
|
|
5162
|
+
* final verdict line — written last — survives into the evidence block
|
|
5163
|
+
* (`composeWorkerEvidence` keeps the note's FIRST `NOTE_MAX_CHARS` chars).
|
|
5164
|
+
*/
|
|
5165
|
+
function closingWorkerNote(stdout, stderr) {
|
|
5166
|
+
const source = stdout.trim() || stderr.trim();
|
|
5167
|
+
if (!source) return void 0;
|
|
5168
|
+
return source.slice(-300);
|
|
5169
|
+
}
|
|
5170
|
+
function diffSummary(patch) {
|
|
5171
|
+
const trimmed = patch.trim();
|
|
5172
|
+
if (!trimmed) return "--- diff: EMPTY (the worker produced no edits) ---";
|
|
5173
|
+
const lines = trimmed.split("\n");
|
|
5174
|
+
const filesChanged = lines.filter((line) => line.startsWith("diff --git ")).length;
|
|
5175
|
+
const head = lines.slice(0, PATCH_HEAD_LINES).map((line) => line.length > PATCH_LINE_MAX_CHARS ? `${line.slice(0, PATCH_LINE_MAX_CHARS)}~` : line);
|
|
5176
|
+
const omitted = lines.length - head.length;
|
|
5177
|
+
return `${`--- diff: ${filesChanged} file(s) changed, ${Buffer.byteLength(patch)} bytes; first ${head.length} line(s) ---`}\n${head.join("\n")}${omitted > 0 ? `\n... (${omitted} more patch lines; full patch persisted per worker)` : ""}`;
|
|
5178
|
+
}
|
|
5179
|
+
//#endregion
|
|
4792
5180
|
//#region src/runtime/supervise/worktree-fanout.ts
|
|
4793
5181
|
/**
|
|
4794
5182
|
* Build the worktree fanout combinator. Run it with `runPersonified({ persona, shape, task, budget })`
|
|
@@ -5411,6 +5799,6 @@ function tail(s) {
|
|
|
5411
5799
|
return s.slice(-400);
|
|
5412
5800
|
}
|
|
5413
5801
|
//#endregion
|
|
5414
|
-
export {
|
|
5802
|
+
export { fanout as $, streamAgentTurn as A, renderPairwiseMarkdown as At, printBenchmarkReport as B, resolveMcpServerLaunch as Bt, supervisorRunDir as C, sentinelCompletion as Ct, patchDelivered as D, renderLeaderboardHtml as Dt, writeWorkerSteer as E, pairwiseSignificance as Et, assertStrategyContract as F, materializeLocalMcp as Ft, definePersona as G, promotionGate as H, secretEnvOfMcpServer as Ht, authorStrategy as I, createMcpEnvironment as It, createShapeRegistry as J, runPersonified as K, strategyAuthorContract as L, sanitizeMcpToolSchema as Lt, pickChampion as M, defaultAuditorInstruction as Mt, runStrategyEvolution as N, McpSpawnFault as Nt, runCoderChecks as O, renderLeaderboardMarkdown as Ot, selectChampion as P, connectStdioMcp as Pt, renderCorpusToInstructions as Q, SandboxRunAbortError as R, envKeyProvider as Rt, safeWorkerFile as S, deterministicCompletion as St, workerInboxFileFromEventDir as T, leaderboard as Tt, equalKOnCost as U, runBenchmark as V, resolveSecretEnv as Vt, trajectoryReport as W, FileCorpus as X, registerShape as Y, InMemoryCorpus as Z, settledWorkerOut as _, localSandboxClient as _t, localShell as a, verify as at, analyzeTrace as b, loopDispatch as bt, createVerifierEnvironment as c, buildSteerContext as ct, worktreeFanout as d, inProcessSandboxClient as dt, flatWidenGate as et, EVIDENCE_MAX_CHARS as f, harvestCorpus as ft, composeWorkerEvidence as g, resolveSandboxClient as gt, closingWorkerNote as h, naiveDriver as ht, jjWorkspace as i, selectValidWinner as it, discriminatingMeans as j, auditIntent as jt, collectAgentTurn as k, renderLeaderboardSvg as kt, failuresAnalyst as l, createScopeAnalyst as lt, VERIFY_TAIL_CHARS as m, dumbDriver as mt, makeFinding$1 as n, panel as nt, runInWorkspace as o, widen as ot, NOTE_MAX_CHARS as p, defineLeaderboard as pt, builtinShapes as q, gitWorkspace as r, pipeline as rt, createWaterfallCollector as s, assertTraceDerivedFindings as st, computeFindingId$1 as t, loopUntil as tt, superviseSurface as u, registryScopeAnalyst as ut, copyUntrackedIntoClone as v, inlineSandboxClient as vt, workerInboxFile as w, stopSentinel as wt, readWorkerSteerRequests as x, completionAuthorizes as xt, withUntrackedArtifacts as y, loopCampaignDispatch as yt, openSandboxRun as z, mcpSecretEnvMetadataKey as zt };
|
|
5415
5803
|
|
|
5416
|
-
//# sourceMappingURL=runtime-
|
|
5804
|
+
//# sourceMappingURL=runtime-DSkMJb_X.js.map
|