@tangle-network/agent-runtime 0.88.0 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +51 -18
  2. package/dist/agent.d.ts +53 -2
  3. package/dist/agent.js +166 -4
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/{chunk-22HPUH77.js → chunk-4IBAMGBE.js} +170 -269
  7. package/dist/chunk-4IBAMGBE.js.map +1 -0
  8. package/dist/chunk-5QOB7H74.js +387 -0
  9. package/dist/chunk-5QOB7H74.js.map +1 -0
  10. package/dist/chunk-74SBMDTO.js +214 -0
  11. package/dist/chunk-74SBMDTO.js.map +1 -0
  12. package/dist/{chunk-HBE77SWV.js → chunk-MHK62APK.js} +5 -5
  13. package/dist/{chunk-LRNRPJAV.js → chunk-QSNSMJSZ.js} +2622 -2842
  14. package/dist/chunk-QSNSMJSZ.js.map +1 -0
  15. package/dist/{chunk-VNOOH22O.js → chunk-XMOU4WEY.js} +74 -3
  16. package/dist/chunk-XMOU4WEY.js.map +1 -0
  17. package/dist/{chunk-JHULWWQD.js → chunk-XQEISTK2.js} +2 -2
  18. package/dist/{worktree-harness-CX_McRLp.d.ts → completion-gate-BDaBP8dd.d.ts} +44 -44
  19. package/dist/{coordination-CuDLO8wj.d.ts → coordination-DxHduZg7.d.ts} +414 -226
  20. package/dist/environment-provider.d.ts +3 -3
  21. package/dist/index.d.ts +156 -13
  22. package/dist/index.js +316 -70
  23. package/dist/index.js.map +1 -1
  24. package/dist/intelligence.d.ts +2 -2
  25. package/dist/knowledge.d.ts +120 -0
  26. package/dist/knowledge.js +28 -0
  27. package/dist/knowledge.js.map +1 -0
  28. package/dist/lifecycle.js +1 -1
  29. package/dist/{loop-runner-bin-hQDUL8Ld.d.ts → loop-runner-bin-D3GmB2Ru.d.ts} +2 -2
  30. package/dist/loop-runner-bin.d.ts +5 -5
  31. package/dist/loop-runner-bin.js +5 -5
  32. package/dist/loops.d.ts +46 -324
  33. package/dist/loops.js +3 -15
  34. package/dist/mcp/bin.js +2 -2
  35. package/dist/mcp/index.d.ts +8 -9
  36. package/dist/mcp/index.js +7 -7
  37. package/dist/{openai-tools-BnrOmGjN.d.ts → openai-tools-D0xmzo0y.d.ts} +1 -1
  38. package/dist/profiles.d.ts +196 -5
  39. package/dist/profiles.js +8 -0
  40. package/dist/profiles.js.map +1 -1
  41. package/dist/{router-client-r8y_VFVM.d.ts → router-client-DJImUDlm.d.ts} +1 -1
  42. package/dist/supervise-BpCdssu0.d.ts +131 -0
  43. package/dist/{types-Driepl87.d.ts → types-DAJQRIUD.d.ts} +2 -2
  44. package/dist/{types-ESeMOj94.d.ts → types-Dnk189QA.d.ts} +1 -1
  45. package/dist/{worktree-fanout-D6xR2CIA.d.ts → worktree-fanout-CfRXYmgV.d.ts} +3 -3
  46. package/package.json +12 -4
  47. package/skills/agent-runtime-adoption/SKILL.md +74 -24
  48. package/skills/build-with-agent-runtime/SKILL.md +3 -9
  49. package/skills/loop-writer/SKILL.md +6 -78
  50. package/skills/supervise/SKILL.md +2 -2
  51. package/dist/chunk-22HPUH77.js.map +0 -1
  52. package/dist/chunk-LRNRPJAV.js.map +0 -1
  53. package/dist/chunk-VNOOH22O.js.map +0 -1
  54. package/dist/otel-export-BKmNwiCb.d.ts +0 -180
  55. /package/dist/{chunk-HBE77SWV.js.map → chunk-MHK62APK.js.map} +0 -0
  56. /package/dist/{chunk-JHULWWQD.js.map → chunk-XQEISTK2.js.map} +0 -0
@@ -15,7 +15,7 @@ import {
15
15
  settledToIteration,
16
16
  supervise,
17
17
  withDriverExecutor
18
- } from "./chunk-LRNRPJAV.js";
18
+ } from "./chunk-QSNSMJSZ.js";
19
19
  import {
20
20
  addTokenUsage,
21
21
  isAbortError,
@@ -1405,263 +1405,6 @@ function inProcessSandboxClient(options) {
1405
1405
  };
1406
1406
  }
1407
1407
 
1408
- // src/runtime/loop-author.ts
1409
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
1410
- import { join as join4 } from "path";
1411
-
1412
- // src/runtime/strategy-author.ts
1413
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
1414
- import { join as join3 } from "path";
1415
- var strategyAuthorContract = `
1416
- You author an OPTIMIZATION STRATEGY for an agentic loop system. A strategy decides how to
1417
- spend a compute budget to beat a task's deployable check. You compose exactly two steps:
1418
-
1419
- shot(spec?: { handle?, messages?, steer?, persona?, tools? }): Promise<ShotResult | null>
1420
- Runs ONE worker attempt (a bounded tool loop) over an artifact.
1421
- - omit handle => the shot opens its OWN fresh artifact and closes it after (a sample).
1422
- - pass handle => the shot CONTINUES that artifact (state accumulates across shots).
1423
- - messages => the carried conversation (pass the previous ShotResult.messages to continue).
1424
- - steer => a corrective instruction injected before the shot.
1425
- - persona => { systemPrompt?, model? } \u2014 give THIS shot its own role and/or model
1426
- (multi-agent strategies: a researcher shot then an engineer shot, a panel of k
1427
- personas over one budget). On a fresh shot the systemPrompt replaces the task's; on
1428
- a carried conversation it arrives as a hand-off message. Same conserved budget.
1429
- - tools => string[] \u2014 restrict THIS shot to a subset of the task's tools by
1430
- name (focus an explore shot on read-only tools, an execute shot on write tools).
1431
- Restriction-only; unknown names make the shot fail. ALWAYS select from
1432
- await listTools(handle) \u2014 never hardcode. Omitted => the shot sees every tool.
1433
- ShotResult = { messages, score (0..1 on the task's check), passes, total, completions, toolErrors }
1434
- Returns null if the attempt failed infra-wise.
1435
-
1436
- critique(messages): Promise<string | null>
1437
- A firewalled trace-analyst reads the attempt's trajectory and returns ONE corrective
1438
- instruction (or null when it judges the work complete). Costs ~1 completion.
1439
-
1440
- consult(messages, instruction): Promise<string | null>
1441
- The RAW analyst channel: the same firewalled critic answers YOUR instruction over the
1442
- trajectory verbatim (no reformatting) \u2014 use it when you need a specific reply format
1443
- (a decision, a prediction). Costs ~1 completion.
1444
-
1445
- surface.open(task) / surface.close(handle)
1446
- Open a persistent artifact you manage yourself (remember to close in a finally).
1447
- close is idempotent \u2014 closing an already-closed handle is a safe no-op.
1448
-
1449
- listTools(handle): Promise<Array<{ name, description? }>>
1450
- The tools THIS task actually offers. TOOL SETS VARY PER TASK \u2014 if you restrict a
1451
- shot with \`tools\`, you MUST pick names from await listTools(handle); hardcoding
1452
- names from an example kills your shots on every task whose tools differ.
1453
-
1454
- Rules:
1455
- - ALWAYS await every shot/critique/surface call \u2014 a floating promise that rejects
1456
- crashes the whole benchmark run.
1457
- - Stay within ~budget total shots; every shot/critique spends from a conserved pool.
1458
- - For a FRESH attempt OMIT \`messages\` entirely (never pass \`[]\` \u2014 an empty array is a
1459
- fresh conversation too, but be explicit). To CONTINUE, pass the previous
1460
- ShotResult.messages unchanged.
1461
- - Return { score, resolved, completions, progression, shots } \u2014 score = the BEST checkpoint
1462
- you reached (keep-best, never final-state), progression = score after each shot.
1463
- - The module must be EXACTLY this shape (no other imports, no commentary outside code):
1464
-
1465
- import { defineStrategy } from '@tangle-network/agent-runtime/loops'
1466
- export default defineStrategy('your-strategy-name', async ({ surface, task, budget, shot, critique, listTools }) => {
1467
- // your composition (listTools comes from the destructured context \u2014 it is NOT a global)
1468
- })
1469
- `;
1470
- function assertStrategyContract(code) {
1471
- const allowedImport = /^\s*import\s+\{[^}]*\}\s+from\s+['"]@tangle-network\/agent-runtime\/loops['"]/;
1472
- for (const line of code.split("\n")) {
1473
- if (/^\s*import\s/.test(line) && !allowedImport.test(line)) {
1474
- throw new Error(`authored code rejected: foreign import \u2014 ${line.trim().slice(0, 120)}`);
1475
- }
1476
- }
1477
- const banned = [
1478
- [/\brequire\s*\(/, "require()"],
1479
- [/\bimport\s*\(/, "dynamic import()"],
1480
- [/\beval\s*\(/, "eval()"],
1481
- [/new\s+Function\s*\(/, "new Function()"],
1482
- [/\bprocess\s*[.[]/, "process access"],
1483
- [/\bglobalThis\s*[.[]/, "globalThis access"],
1484
- [/\bfetch\s*\(/, "network access"],
1485
- [/child_process|node:fs|node:net|node:http|worker_threads/, "node builtin access"]
1486
- ];
1487
- for (const [re, what] of banned) {
1488
- if (re.test(code)) throw new Error(`authored code rejected: ${what}`);
1489
- }
1490
- }
1491
- async function requestAuthoredCode(opts, model) {
1492
- const res = await opts.chat.chat(
1493
- {
1494
- ...model ? { model } : {},
1495
- ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
1496
- ...opts.maxTokens !== void 0 ? { maxTokens: opts.maxTokens } : {},
1497
- messages: [
1498
- {
1499
- role: "system",
1500
- content: "You are a senior engineer authoring optimization strategies for agent loops. Output exactly one fenced ```ts code block and nothing else."
1501
- },
1502
- {
1503
- role: "user",
1504
- content: `${opts.contract ?? strategyAuthorContract}
1505
-
1506
- BASELINE RESULTS on the "${opts.environmentName}" environment (budget=${opts.budget}):
1507
- ${opts.lossesJson}
1508
-
1509
- Author ONE new strategy that you expect to beat the baselines on THIS environment at the same budget. Use the losses to target the observed failure mode. Output only the module code block.`
1510
- }
1511
- ]
1512
- },
1513
- { ...opts.signal ? { signal: opts.signal } : {} }
1514
- );
1515
- const match = res.content.match(/```(?:ts|typescript)?\s*\n([\s\S]*?)```/);
1516
- if (!match?.[1]) {
1517
- throw new Error(
1518
- `authorStrategy: no code block in the author's reply (model=${model ?? "default"}): ${res.content.slice(0, 300)}`
1519
- );
1520
- }
1521
- return match[1];
1522
- }
1523
- async function authorStrategy(opts) {
1524
- let code;
1525
- try {
1526
- code = await requestAuthoredCode(opts, opts.model);
1527
- } catch (primaryError) {
1528
- if (!opts.fallbackModel) throw primaryError;
1529
- code = await requestAuthoredCode(opts, opts.fallbackModel);
1530
- }
1531
- assertStrategyContract(code);
1532
- mkdirSync2(opts.outDir, { recursive: true });
1533
- const file = join3(opts.outDir, `authored-${Date.now()}.mts`);
1534
- writeFileSync2(file, code);
1535
- const mod = await import(`file://${file}`);
1536
- if (!mod.default || typeof mod.default.driver !== "function" || !mod.default.name) {
1537
- throw new Error(`authorStrategy: ${file} does not export a default Strategy`);
1538
- }
1539
- return { strategy: mod.default, file, code };
1540
- }
1541
-
1542
- // src/runtime/loop-author.ts
1543
- var loopAuthorContract = `
1544
- You author a LOOP ATOM for an agent supervisor. A loop runs a bounded, multi-round journey
1545
- toward a deployable check; a supervisor spawns / observes / steers it exactly like a worker.
1546
- You write ONE round; the runtime owns the round ceiling, the conserved budget, the gate, and
1547
- folding a supervisor's steer into the next round. So write real control flow inside a round,
1548
- but never write the loop's stop condition as "hope the model stops" \u2014 that is the runtime's job.
1549
-
1550
- You export ONE module of EXACTLY this shape (no other imports, no commentary outside the code):
1551
-
1552
- import { defineLoop } from '@tangle-network/agent-runtime/loops'
1553
- export default defineLoop('your-loop-name', {
1554
- maxRounds: 3,
1555
- round: async ({ task, scope, round, maxRounds, steer, budget, signal }) => {
1556
- // arbitrary code for ONE round. Do real work by spawning children on the nested scope:
1557
- // const w = scope.spawn(childAgent, subtask, { budget: perRound, label: \`r\${round}\` })
1558
- // if (!w.ok) throw new Error(w.reason) // fail loud: budget-exhausted | depth-exceeded
1559
- // const settled = await scope.next() // conserved child work; settles gated
1560
- // 'steer' is the supervisor's messages since the last round (fold them into what you do next).
1561
- // 'signal' aborts if a forceful steer arrives mid-round \u2014 pass it to your awaited work.
1562
- return { out: /* the running result */ undefined, done: false } // done:true stops early
1563
- },
1564
- check: (out) => /* the deployable completion oracle */ Boolean(out),
1565
- })
1566
-
1567
- For a MULTI-AGENT loop (a proposer then a verifier, a researcher then an engineer), do NOT
1568
- hand-write the pipeline in 'round' \u2014 use 'agents' instead: an ordered list of named agents piped
1569
- each round (task -> agents[0] -> agents[1] -> ... -> out). "How many agents" is then self-evident.
1570
-
1571
- import { defineLoop } from '@tangle-network/agent-runtime/loops'
1572
- export default defineLoop('your-loop-name', {
1573
- maxRounds: 3,
1574
- agents: [
1575
- { name: 'proposer', run: async (ctx, prior) => { /* spawn a worker, produce a draft */ return draft } },
1576
- { name: 'verifier', run: async (ctx, prior) => { /* verify/refine the proposer's draft */ return checked } },
1577
- ],
1578
- check: (out) => Boolean(out),
1579
- })
1580
-
1581
- Provide EXACTLY one of 'round' or 'agents'. The round context (for the 'round' form, and passed to
1582
- each agent's run as its first arg):
1583
- task the spawn task, verbatim.
1584
- scope the NESTED conserved scope. scope.spawn(agent, task, { budget, label }) reserves
1585
- budget and fails closed; scope.next() awaits one child settlement. Budget NESTS \u2014
1586
- the pool reserves each spawn's full ceiling until it settles, so give children a
1587
- per-round budget smaller than the loop's own.
1588
- round 1-based round index. maxRounds is the declared ceiling.
1589
- steer readonly string[] \u2014 supervisor steer_agent messages that arrived since last round.
1590
- budget conserved-pool readouts (tokensLeft, usdLeft, deadlineMs) for in-body awareness.
1591
- signal AbortSignal \u2014 the spawn signal + a fresh per-round interrupt; honor it in awaits.
1592
-
1593
- The round result: { out: unknown; done?: boolean }. 'out' is the running result the gate reads;
1594
- 'done: true' is YOUR early stop (distinct from the runtime's gate 'check').
1595
-
1596
- check(out): boolean | Promise<boolean>. The DEPLOYABLE oracle \u2014 an executable test, a state
1597
- verifier, a readiness score threshold \u2014 read off 'out', never the model judging itself. The loop
1598
- settles valid IFF check passes, and the runtime polls it after each round to stop the instant the
1599
- loop has delivered. A loop that exhausts maxRounds without check passing settles valid:false.
1600
-
1601
- Rules:
1602
- - ALWAYS await every scope.spawn drain / async call \u2014 a floating rejection crashes the run.
1603
- - Do real work by SPAWNING children; raw un-metered inference in the body is not budget-conserved.
1604
- - Give 'check' a real oracle. A loop whose check is a self-judged score cannot be trusted.
1605
- - The only import allowed is '@tangle-network/agent-runtime/loops'. No require/eval/fetch/process/
1606
- node builtins \u2014 the runtime meters and gates you; out-of-band compute breaks that.
1607
- `;
1608
- async function requestAuthoredCode2(opts, model) {
1609
- const res = await opts.chat.chat(
1610
- {
1611
- ...model ? { model } : {},
1612
- ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
1613
- ...opts.maxTokens !== void 0 ? { maxTokens: opts.maxTokens } : {},
1614
- messages: [
1615
- {
1616
- role: "system",
1617
- content: "You are a senior engineer authoring coded loop atoms for an agent supervisor. Output exactly one fenced ```ts code block and nothing else."
1618
- },
1619
- {
1620
- role: "user",
1621
- content: `${opts.contract ?? loopAuthorContract}
1622
-
1623
- GOAL: ${opts.goal}
1624
- MAX ROUNDS: ${opts.maxRounds}${opts.context ? `
1625
-
1626
- CONTEXT:
1627
- ${opts.context}` : ""}
1628
-
1629
- Author ONE loop that reaches the goal within the round ceiling, gated on a real check. Output only the module code block.`
1630
- }
1631
- ]
1632
- },
1633
- { ...opts.signal ? { signal: opts.signal } : {} }
1634
- );
1635
- const match = res.content.match(/```(?:ts|typescript)?\s*\n([\s\S]*?)```/);
1636
- if (!match?.[1]) {
1637
- throw new Error(
1638
- `authorLoop: no code block in the author's reply (model=${model ?? "default"}): ${res.content.slice(0, 300)}`
1639
- );
1640
- }
1641
- return match[1];
1642
- }
1643
- function isLoopDef(value) {
1644
- return typeof value === "object" && value !== null && typeof value.round === "function" && typeof value.name === "string" && Number.isInteger(value.maxRounds);
1645
- }
1646
- async function authorLoop(opts) {
1647
- let code;
1648
- try {
1649
- code = await requestAuthoredCode2(opts, opts.model);
1650
- } catch (primaryError) {
1651
- if (!opts.fallbackModel) throw primaryError;
1652
- code = await requestAuthoredCode2(opts, opts.fallbackModel);
1653
- }
1654
- assertStrategyContract(code);
1655
- mkdirSync3(opts.outDir, { recursive: true });
1656
- const file = join4(opts.outDir, `authored-loop-${Date.now()}.mts`);
1657
- writeFileSync3(file, code);
1658
- const mod = await import(`file://${file}`);
1659
- if (!isLoopDef(mod.default)) {
1660
- throw new Error(`authorLoop: ${file} does not default-export a LoopDef (defineLoop(...))`);
1661
- }
1662
- return { loop: mod.default, file, code };
1663
- }
1664
-
1665
1408
  // src/runtime/mcp-environment.ts
1666
1409
  async function rpc(endpoint, body) {
1667
1410
  let lastErr;
@@ -3054,6 +2797,30 @@ async function analyze(task, messages, opts) {
3054
2797
  const steer = obs.findings.map((f) => f.recommended_action).filter((a) => typeof a === "string" && a.trim().length > 0).join("\n").trim();
3055
2798
  return { steer: steer || "COMPLETE", tokens };
3056
2799
  }
2800
+ async function renderCorpusReadback(opts) {
2801
+ if (!opts.corpus || !opts.corpusReadback) return "";
2802
+ const maxFacts = opts.corpusReadback.maxFacts ?? 3;
2803
+ if (!Number.isInteger(maxFacts) || maxFacts < 0) {
2804
+ throw new Error(`corpusReadback.maxFacts must be a non-negative integer, got ${maxFacts}`);
2805
+ }
2806
+ if (maxFacts === 0) return "";
2807
+ const tags = [
2808
+ ...opts.corpusTags ?? [],
2809
+ ...opts.corpusReadback.tags ?? [],
2810
+ ...opts.corpusReadback.includeOperatorFacts ? [] : ["audience:agent"]
2811
+ ];
2812
+ const facts = await opts.corpus.query({
2813
+ ...tags.length > 0 ? { tags } : {},
2814
+ minConfidence: opts.corpusReadback.minConfidence ?? 0.7,
2815
+ limit: maxFacts
2816
+ });
2817
+ if (facts.length === 0) return "";
2818
+ const rendered = facts.map(
2819
+ (fact) => fact.rationale ? `- ${fact.claim} (${fact.rationale})` : `- ${fact.claim}`
2820
+ );
2821
+ return `Relevant learned facts from prior attempts:
2822
+ ${rendered.join("\n")}`;
2823
+ }
3057
2824
  function shotExecutor(surface, opts) {
3058
2825
  let artifact;
3059
2826
  return {
@@ -3198,7 +2965,8 @@ function depthStrategy(surface, task, opts, cfg) {
3198
2965
  try {
3199
2966
  for (shots = 0; shots < cfg.maxShots; shots += 1) {
3200
2967
  const child = leaf(`shot:${shots}`, "shot");
3201
- const steer = shots === 0 ? void 0 : pendingSteer;
2968
+ const memorySteer = await renderCorpusReadback(opts);
2969
+ const steer = [shots === 0 ? void 0 : pendingSteer, memorySteer].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n\n");
3202
2970
  const res = scope.spawn(child, { task, handle, messages, steer }, {
3203
2971
  budget: perChild(innerTurns),
3204
2972
  label: `shot:${shots}`
@@ -3793,6 +3561,11 @@ async function openSandboxRun(client, options, deliverable) {
3793
3561
  options.promptOptions
3794
3562
  );
3795
3563
  handle = r.handle;
3564
+ await options.beforeStart?.({
3565
+ box: handle.box,
3566
+ sessionId: handle.sessionId,
3567
+ signal: options.signal
3568
+ });
3796
3569
  const result = await settle2(handle.box, r.events);
3797
3570
  turnCount += 1;
3798
3571
  emit({
@@ -3903,8 +3676,138 @@ function errorMessage(error) {
3903
3676
  return error instanceof Error ? error.message : String(error);
3904
3677
  }
3905
3678
 
3679
+ // src/runtime/strategy-author.ts
3680
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3681
+ import { join as join3 } from "path";
3682
+ var strategyAuthorContract = `
3683
+ You author an OPTIMIZATION STRATEGY for an agentic loop system. A strategy decides how to
3684
+ spend a compute budget to beat a task's deployable check. You compose exactly two steps:
3685
+
3686
+ shot(spec?: { handle?, messages?, steer?, persona?, tools? }): Promise<ShotResult | null>
3687
+ Runs ONE worker attempt (a bounded tool loop) over an artifact.
3688
+ - omit handle => the shot opens its OWN fresh artifact and closes it after (a sample).
3689
+ - pass handle => the shot CONTINUES that artifact (state accumulates across shots).
3690
+ - messages => the carried conversation (pass the previous ShotResult.messages to continue).
3691
+ - steer => a corrective instruction injected before the shot.
3692
+ - persona => { systemPrompt?, model? } \u2014 give THIS shot its own role and/or model
3693
+ (multi-agent strategies: a researcher shot then an engineer shot, a panel of k
3694
+ personas over one budget). On a fresh shot the systemPrompt replaces the task's; on
3695
+ a carried conversation it arrives as a hand-off message. Same conserved budget.
3696
+ - tools => string[] \u2014 restrict THIS shot to a subset of the task's tools by
3697
+ name (focus an explore shot on read-only tools, an execute shot on write tools).
3698
+ Restriction-only; unknown names make the shot fail. ALWAYS select from
3699
+ await listTools(handle) \u2014 never hardcode. Omitted => the shot sees every tool.
3700
+ ShotResult = { messages, score (0..1 on the task's check), passes, total, completions, toolErrors }
3701
+ Returns null if the attempt failed infra-wise.
3702
+
3703
+ critique(messages): Promise<string | null>
3704
+ A firewalled trace-analyst reads the attempt's trajectory and returns ONE corrective
3705
+ instruction (or null when it judges the work complete). Costs ~1 completion.
3706
+
3707
+ consult(messages, instruction): Promise<string | null>
3708
+ The RAW analyst channel: the same firewalled critic answers YOUR instruction over the
3709
+ trajectory verbatim (no reformatting) \u2014 use it when you need a specific reply format
3710
+ (a decision, a prediction). Costs ~1 completion.
3711
+
3712
+ surface.open(task) / surface.close(handle)
3713
+ Open a persistent artifact you manage yourself (remember to close in a finally).
3714
+ close is idempotent \u2014 closing an already-closed handle is a safe no-op.
3715
+
3716
+ listTools(handle): Promise<Array<{ name, description? }>>
3717
+ The tools THIS task actually offers. TOOL SETS VARY PER TASK \u2014 if you restrict a
3718
+ shot with \`tools\`, you MUST pick names from await listTools(handle); hardcoding
3719
+ names from an example kills your shots on every task whose tools differ.
3720
+
3721
+ Rules:
3722
+ - ALWAYS await every shot/critique/surface call \u2014 a floating promise that rejects
3723
+ crashes the whole benchmark run.
3724
+ - Stay within ~budget total shots; every shot/critique spends from a conserved pool.
3725
+ - For a FRESH attempt OMIT \`messages\` entirely (never pass \`[]\` \u2014 an empty array is a
3726
+ fresh conversation too, but be explicit). To CONTINUE, pass the previous
3727
+ ShotResult.messages unchanged.
3728
+ - Return { score, resolved, completions, progression, shots } \u2014 score = the BEST checkpoint
3729
+ you reached (keep-best, never final-state), progression = score after each shot.
3730
+ - The module must be EXACTLY this shape (no other imports, no commentary outside code):
3731
+
3732
+ import { defineStrategy } from '@tangle-network/agent-runtime/loops'
3733
+ export default defineStrategy('your-strategy-name', async ({ surface, task, budget, shot, critique, listTools }) => {
3734
+ // your composition (listTools comes from the destructured context \u2014 it is NOT a global)
3735
+ })
3736
+ `;
3737
+ function assertStrategyContract(code) {
3738
+ const allowedImport = /^\s*import\s+\{[^}]*\}\s+from\s+['"]@tangle-network\/agent-runtime\/loops['"]/;
3739
+ for (const line of code.split("\n")) {
3740
+ if (/^\s*import\s/.test(line) && !allowedImport.test(line)) {
3741
+ throw new Error(`authored code rejected: foreign import \u2014 ${line.trim().slice(0, 120)}`);
3742
+ }
3743
+ }
3744
+ const banned = [
3745
+ [/\brequire\s*\(/, "require()"],
3746
+ [/\bimport\s*\(/, "dynamic import()"],
3747
+ [/\beval\s*\(/, "eval()"],
3748
+ [/new\s+Function\s*\(/, "new Function()"],
3749
+ [/\bprocess\s*[.[]/, "process access"],
3750
+ [/\bglobalThis\s*[.[]/, "globalThis access"],
3751
+ [/\bfetch\s*\(/, "network access"],
3752
+ [/child_process|node:fs|node:net|node:http|worker_threads/, "node builtin access"]
3753
+ ];
3754
+ for (const [re, what] of banned) {
3755
+ if (re.test(code)) throw new Error(`authored code rejected: ${what}`);
3756
+ }
3757
+ }
3758
+ async function requestAuthoredCode(opts, model) {
3759
+ const res = await opts.chat.chat(
3760
+ {
3761
+ ...model ? { model } : {},
3762
+ ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {},
3763
+ ...opts.maxTokens !== void 0 ? { maxTokens: opts.maxTokens } : {},
3764
+ messages: [
3765
+ {
3766
+ role: "system",
3767
+ content: "You are a senior engineer authoring optimization strategies for agent loops. Output exactly one fenced ```ts code block and nothing else."
3768
+ },
3769
+ {
3770
+ role: "user",
3771
+ content: `${opts.contract ?? strategyAuthorContract}
3772
+
3773
+ BASELINE RESULTS on the "${opts.environmentName}" environment (budget=${opts.budget}):
3774
+ ${opts.lossesJson}
3775
+
3776
+ Author ONE new strategy that you expect to beat the baselines on THIS environment at the same budget. Use the losses to target the observed failure mode. Output only the module code block.`
3777
+ }
3778
+ ]
3779
+ },
3780
+ { ...opts.signal ? { signal: opts.signal } : {} }
3781
+ );
3782
+ const match = res.content.match(/```(?:ts|typescript)?\s*\n([\s\S]*?)```/);
3783
+ if (!match?.[1]) {
3784
+ throw new Error(
3785
+ `authorStrategy: no code block in the author's reply (model=${model ?? "default"}): ${res.content.slice(0, 300)}`
3786
+ );
3787
+ }
3788
+ return match[1];
3789
+ }
3790
+ async function authorStrategy(opts) {
3791
+ let code;
3792
+ try {
3793
+ code = await requestAuthoredCode(opts, opts.model);
3794
+ } catch (primaryError) {
3795
+ if (!opts.fallbackModel) throw primaryError;
3796
+ code = await requestAuthoredCode(opts, opts.fallbackModel);
3797
+ }
3798
+ assertStrategyContract(code);
3799
+ mkdirSync2(opts.outDir, { recursive: true });
3800
+ const file = join3(opts.outDir, `authored-${Date.now()}.mts`);
3801
+ writeFileSync2(file, code);
3802
+ const mod = await import(`file://${file}`);
3803
+ if (!mod.default || typeof mod.default.driver !== "function" || !mod.default.name) {
3804
+ throw new Error(`authorStrategy: ${file} does not export a default Strategy`);
3805
+ }
3806
+ return { strategy: mod.default, file, code };
3807
+ }
3808
+
3906
3809
  // src/runtime/strategy-evolution.ts
3907
- import { existsSync, readFileSync, writeFileSync as writeFileSync4 } from "fs";
3810
+ import { existsSync, readFileSync, writeFileSync as writeFileSync3 } from "fs";
3908
3811
  import { gzipSync } from "zlib";
3909
3812
  function discriminatingMeans(report, fieldOrder) {
3910
3813
  const rows = report.perTask.filter((r) => {
@@ -4016,7 +3919,7 @@ async function runStrategyEvolution(cfg) {
4016
3919
  }
4017
3920
  const save = (state) => {
4018
3921
  if (cfg.checkpoint)
4019
- writeFileSync4(cfg.checkpoint.path, JSON.stringify({ ...state, fingerprint }, null, 1));
3922
+ writeFileSync3(cfg.checkpoint.path, JSON.stringify({ ...state, fingerprint }, null, 1));
4020
3923
  };
4021
3924
  const bench = async (phase, tasks, strategies) => {
4022
3925
  await cfg.onPhase?.(phase);
@@ -5914,8 +5817,8 @@ function jjWorkspace(opts) {
5914
5817
  async function runInWorkspace(ws, body, opts = {}) {
5915
5818
  const { mkdtempSync: mkdtempSync2, rmSync: rmSync2 } = await import("fs");
5916
5819
  const { tmpdir: tmpdir3 } = await import("os");
5917
- const { join: join5 } = await import("path");
5918
- const dir = mkdtempSync2(join5(tmpdir3(), opts.tmpPrefix ?? "ws-run-"));
5820
+ const { join: join4 } = await import("path");
5821
+ const dir = mkdtempSync2(join4(tmpdir3(), opts.tmpPrefix ?? "ws-run-"));
5919
5822
  try {
5920
5823
  await ws.materialize(dir);
5921
5824
  const r = await body(dir);
@@ -5969,11 +5872,6 @@ export {
5969
5872
  renderReport,
5970
5873
  harvestCorpus,
5971
5874
  inProcessSandboxClient,
5972
- strategyAuthorContract,
5973
- assertStrategyContract,
5974
- authorStrategy,
5975
- loopAuthorContract,
5976
- authorLoop,
5977
5875
  createMcpEnvironment,
5978
5876
  assertTraceDerivedFindings,
5979
5877
  createScopeAnalyst,
@@ -6010,6 +5908,9 @@ export {
6010
5908
  printBenchmarkReport,
6011
5909
  SandboxRunAbortError,
6012
5910
  openSandboxRun,
5911
+ strategyAuthorContract,
5912
+ assertStrategyContract,
5913
+ authorStrategy,
6013
5914
  discriminatingMeans,
6014
5915
  pickChampion,
6015
5916
  selectChampion,
@@ -6036,4 +5937,4 @@ export {
6036
5937
  computeFindingId,
6037
5938
  makeFinding2 as makeFinding
6038
5939
  };
6039
- //# sourceMappingURL=chunk-22HPUH77.js.map
5940
+ //# sourceMappingURL=chunk-4IBAMGBE.js.map