@tangle-network/agent-runtime 0.52.0 → 0.54.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 (74) hide show
  1. package/README.md +27 -16
  2. package/dist/agent.d.ts +2 -2
  3. package/dist/agent.js +5 -3
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/analyst-loop.js +2 -2
  7. package/dist/{chunk-COAVO6QB.js → chunk-2BDXWZUC.js} +5 -34
  8. package/dist/chunk-2BDXWZUC.js.map +1 -0
  9. package/dist/chunk-A73RADPB.js +281 -0
  10. package/dist/chunk-A73RADPB.js.map +1 -0
  11. package/dist/chunk-FRBWCJLP.js +396 -0
  12. package/dist/chunk-FRBWCJLP.js.map +1 -0
  13. package/dist/chunk-GRAGM4MC.js +72 -0
  14. package/dist/chunk-GRAGM4MC.js.map +1 -0
  15. package/dist/{chunk-2OU7ZQPD.js → chunk-JTH2FPCK.js} +3248 -456
  16. package/dist/chunk-JTH2FPCK.js.map +1 -0
  17. package/dist/{chunk-V2K35HF2.js → chunk-KJH62YEK.js} +2 -2
  18. package/dist/{chunk-HNUXAZIJ.js → chunk-P5OKDSLB.js} +2 -2
  19. package/dist/{chunk-GSUO5QS6.js → chunk-VLF5RHEQ.js} +2 -5
  20. package/dist/{chunk-GSUO5QS6.js.map → chunk-VLF5RHEQ.js.map} +1 -1
  21. package/dist/{chunk-7JITYN6T.js → chunk-XRYEZPR6.js} +18 -2
  22. package/dist/chunk-XRYEZPR6.js.map +1 -0
  23. package/dist/{coder-_YCf3BAK.d.ts → coder-CEkUFv8h.d.ts} +1 -1
  24. package/dist/{types-5MGt5KTY.d.ts → coordination-BMzskrUR.d.ts} +268 -2
  25. package/dist/{kb-gate-CHAyt4aI.d.ts → delegates-BPLIl8EC.d.ts} +491 -566
  26. package/dist/{delegation-profile-1GbW5yA3.d.ts → delegation-profile-Bvfro2m1.d.ts} +28 -2
  27. package/dist/index.d.ts +7 -7
  28. package/dist/index.js +25 -80
  29. package/dist/index.js.map +1 -1
  30. package/dist/intelligence.d.ts +475 -5
  31. package/dist/intelligence.js +547 -3
  32. package/dist/intelligence.js.map +1 -1
  33. package/dist/kb-gate-CuzMYGYM.d.ts +76 -0
  34. package/dist/{loop-runner-bin-DFUNgpeK.d.ts → loop-runner-bin-BGpVVyXp.d.ts} +6 -27
  35. package/dist/loop-runner-bin.d.ts +5 -5
  36. package/dist/loop-runner-bin.js +6 -5
  37. package/dist/loops.d.ts +8 -5
  38. package/dist/loops.js +32 -8
  39. package/dist/mcp/bin.js +94 -30
  40. package/dist/mcp/bin.js.map +1 -1
  41. package/dist/mcp/index.d.ts +10 -273
  42. package/dist/mcp/index.js +45 -365
  43. package/dist/mcp/index.js.map +1 -1
  44. package/dist/{openai-tools-D4HLDWgw.d.ts → openai-tools-CoeLQ7Uo.d.ts} +1 -1
  45. package/dist/profiles.d.ts +2 -2
  46. package/dist/profiles.js +4 -4
  47. package/dist/router-client-30Y_pca8.d.ts +120 -0
  48. package/dist/{run-loop-BIineL1T.d.ts → run-loop-DluzfJ2h.d.ts} +1 -1
  49. package/dist/runtime.d.ts +429 -131
  50. package/dist/runtime.js +32 -8
  51. package/dist/topology.d.ts +54 -1
  52. package/dist/topology.js +222 -0
  53. package/dist/topology.js.map +1 -1
  54. package/dist/{types-BEQsBhOE.d.ts → types-C8rNlxfV.d.ts} +6 -5
  55. package/dist/workflow.d.ts +2 -2
  56. package/dist/workflow.js +4 -2
  57. package/dist/workflow.js.map +1 -1
  58. package/package.json +5 -5
  59. package/skills/agent-runtime-adoption/SKILL.md +54 -46
  60. package/skills/build-with-agent-runtime/SKILL.md +12 -8
  61. package/skills/loop-writer/SKILL.md +9 -1
  62. package/skills/supervise/SKILL.md +24 -0
  63. package/dist/chunk-2OU7ZQPD.js.map +0 -1
  64. package/dist/chunk-4JI4BCBI.js +0 -623
  65. package/dist/chunk-4JI4BCBI.js.map +0 -1
  66. package/dist/chunk-7JITYN6T.js.map +0 -1
  67. package/dist/chunk-7SP2OVYZ.js +0 -908
  68. package/dist/chunk-7SP2OVYZ.js.map +0 -1
  69. package/dist/chunk-BERLUBAP.js +0 -1356
  70. package/dist/chunk-BERLUBAP.js.map +0 -1
  71. package/dist/chunk-COAVO6QB.js.map +0 -1
  72. package/dist/driver-DLI1io57.d.ts +0 -221
  73. /package/dist/{chunk-V2K35HF2.js.map → chunk-KJH62YEK.js.map} +0 -0
  74. /package/dist/{chunk-HNUXAZIJ.js.map → chunk-P5OKDSLB.js.map} +0 -0
@@ -1,10 +1,18 @@
1
1
  import {
2
+ UI_LENSES
3
+ } from "./chunk-WIR4HOOJ.js";
4
+ import {
5
+ AgentEvalError,
2
6
  AnalystError,
7
+ NotFoundError,
3
8
  PlannerError,
4
9
  RuntimeRunStateError,
5
10
  ValidationError,
6
11
  extractLlmCallEvent
7
- } from "./chunk-GSUO5QS6.js";
12
+ } from "./chunk-VLF5RHEQ.js";
13
+ import {
14
+ buildLoopSpanNodes
15
+ } from "./chunk-G3RGMA7C.js";
8
16
 
9
17
  // src/durable/spawn-journal.ts
10
18
  import { createHash } from "crypto";
@@ -206,8 +214,8 @@ var InMemorySpawnJournal = class {
206
214
  }
207
215
  };
208
216
  var FileSpawnJournal = class {
209
- constructor(path) {
210
- this.path = path;
217
+ constructor(path2) {
218
+ this.path = path2;
211
219
  }
212
220
  path;
213
221
  async loadTree(root) {
@@ -277,8 +285,8 @@ var FileSpawnJournal = class {
277
285
  }
278
286
  async appendRecord(record) {
279
287
  const fs = await import("fs/promises");
280
- const path = await import("path");
281
- await fs.mkdir(path.dirname(this.path), { recursive: true });
288
+ const path2 = await import("path");
289
+ await fs.mkdir(path2.dirname(this.path), { recursive: true });
282
290
  const fh = await fs.open(this.path, "a");
283
291
  try {
284
292
  await fh.write(`${JSON.stringify(record)}
@@ -648,246 +656,6 @@ function deterministicCompletion(check) {
648
656
  };
649
657
  }
650
658
 
651
- // src/runtime/personify/analyst.ts
652
- var judgeEvidenceUri = /^(verdict|judge|score)\b/i;
653
- var assertTraceDerivedFindings = (findings) => {
654
- for (const f of findings) {
655
- for (const ref of f.evidence_refs ?? []) {
656
- if (ref.kind === "metric" && judgeEvidenceUri.test(ref.uri)) {
657
- throw new PlannerError(
658
- `steer-firewall: finding ${stringifySafe(f.finding_id)} cites judge-derived evidence (${stringifySafe(ref.uri)}); findings fed to a combinator's steer decision must be trace-derived, not judge-derived (selector \u2260 judge)`
659
- );
660
- }
661
- }
662
- }
663
- };
664
- function createScopeAnalyst(scope, options) {
665
- if (!options.analyst || typeof options.analyst.act !== "function") {
666
- throw new AnalystError("createScopeAnalyst: analyst must be an Agent with an act() method");
667
- }
668
- const label = options.label ?? "analyst";
669
- return {
670
- async analyze(input) {
671
- const task = options.buildTask(input);
672
- const spawned = scope.spawn(options.analyst, task, {
673
- budget: options.budget,
674
- label
675
- });
676
- if (!spawned.ok) {
677
- throw new AnalystError(
678
- `createScopeAnalyst: analyst spawn refused by the conserved pool (${spawned.reason}); cannot steer node ${stringifySafe(input.nodeId)} on an unrun analyst`
679
- );
680
- }
681
- const settled = await drainAnalystSettlement(scope, spawned.handle.id);
682
- const findings = readAnalystFindings(settled);
683
- assertTraceDerivedFindings(findings);
684
- return findings;
685
- }
686
- };
687
- }
688
- async function drainAnalystSettlement(scope, analystId) {
689
- for (; ; ) {
690
- const settled = await scope.next();
691
- if (settled === null) {
692
- throw new AnalystError(
693
- `createScopeAnalyst: scope drained before analyst ${stringifySafe(analystId)} settled`
694
- );
695
- }
696
- if (settled.handle.id === analystId) return settled;
697
- }
698
- }
699
- function readAnalystFindings(settled) {
700
- if (settled.kind === "down") {
701
- throw new AnalystError(
702
- `createScopeAnalyst: analyst ${stringifySafe(settled.handle.id)} settled down (${settled.infra ? "infra" : "result"}): ${stringifySafe(settled.reason)}`
703
- );
704
- }
705
- const out = settled.out;
706
- if (!Array.isArray(out)) {
707
- throw new PlannerError(
708
- `createScopeAnalyst: analyst ${stringifySafe(settled.handle.id)} must return AnalystFinding[], got ${stringifySafe(out)}`
709
- );
710
- }
711
- return out;
712
- }
713
- function registryScopeAnalyst(registry, buildInputs) {
714
- return {
715
- async analyze(input) {
716
- const projection = buildInputs(input);
717
- const result = await registry.run(projection.runId, projection.inputs, projection.opts);
718
- const findings = result.findings;
719
- assertTraceDerivedFindings(findings);
720
- return findings;
721
- }
722
- };
723
- }
724
- function buildSteerContext(findings, settledSoFar) {
725
- assertTraceDerivedFindings(findings);
726
- const lastValidScore = observedBestScore(settledSoFar);
727
- return {
728
- findings,
729
- settledSoFar,
730
- ...lastValidScore !== void 0 ? { lastValidScore } : {}
731
- };
732
- }
733
- function observedBestScore(settledSoFar) {
734
- let best;
735
- for (const s of settledSoFar) {
736
- if (s.kind !== "done") continue;
737
- const v = s.verdict;
738
- if (!v || v.valid !== true || typeof v.score !== "number") continue;
739
- if (best === void 0 || v.score > best) best = v.score;
740
- }
741
- return best;
742
- }
743
-
744
- // src/runtime/driver.ts
745
- function createDriver(options) {
746
- if (typeof options.planner !== "function") {
747
- throw new ValidationError("createDriver: planner must be a function");
748
- }
749
- const maxIterations = options.maxIterations ?? 8;
750
- if (!Number.isFinite(maxIterations) || maxIterations <= 0) {
751
- throw new ValidationError("createDriver: maxIterations must be > 0");
752
- }
753
- const maxFanout = options.maxFanout ?? 4;
754
- if (!Number.isFinite(maxFanout) || maxFanout < 1) {
755
- throw new ValidationError("createDriver: maxFanout must be >= 1");
756
- }
757
- let pending;
758
- return {
759
- name: options.name ?? "dynamic",
760
- async plan(task, history) {
761
- if (history.length >= maxIterations) {
762
- pending = { kind: "stop", rationale: `maxIterations (${maxIterations}) reached` };
763
- return [];
764
- }
765
- const analyses = options.analyze && history.length > 0 ? await runAnalyze(options.analyze, task, history) : void 0;
766
- if (options.complete && history.length > 0) {
767
- const verdict = await runComplete(options.complete, task, history);
768
- if (completionAuthorizes(verdict, options.completionPolicy)) {
769
- pending = {
770
- kind: "stop",
771
- rationale: `complete (${verdict.determinism}): ${verdict.reasons ?? "satisfied"}`
772
- };
773
- return [];
774
- }
775
- }
776
- const move = await options.planner({
777
- task,
778
- history,
779
- iterationsSpent: history.length,
780
- iterationsRemaining: maxIterations - history.length,
781
- ...analyses ? { analyses } : {}
782
- });
783
- pending = validateMove(move, maxFanout);
784
- if (pending.kind === "select") {
785
- const iter = history[pending.index];
786
- if (!iter || iter.output === void 0) {
787
- throw new PlannerError(
788
- `dynamic planner select.index ${pending.index} is not a completed iteration with output (history length ${history.length})`
789
- );
790
- }
791
- }
792
- switch (pending.kind) {
793
- case "refine":
794
- return [pending.task];
795
- case "fanout":
796
- return pending.tasks;
797
- case "stop":
798
- case "select":
799
- return [];
800
- }
801
- },
802
- decide() {
803
- return pending?.kind === "stop" || pending?.kind === "select" ? "done" : "continue";
804
- },
805
- describePlan() {
806
- if (!pending) return void 0;
807
- const out = { kind: pending.kind };
808
- if (pending.rationale !== void 0) out.rationale = pending.rationale;
809
- if ((pending.kind === "refine" || pending.kind === "fanout") && pending.parentIndex !== void 0) {
810
- out.parentIndex = pending.parentIndex;
811
- }
812
- return out;
813
- },
814
- selectWinner(history) {
815
- if (pending?.kind !== "select") return void 0;
816
- const iter = history[pending.index];
817
- if (!iter || iter.output === void 0) return void 0;
818
- return {
819
- task: iter.task,
820
- output: iter.output,
821
- verdict: iter.verdict,
822
- iterationIndex: iter.index,
823
- agentRunName: iter.agentRunName
824
- };
825
- }
826
- };
827
- }
828
- function validateMove(move, maxFanout) {
829
- if (!move || typeof move !== "object" || typeof move.kind !== "string") {
830
- throw new PlannerError(`dynamic planner returned a non-move value: ${stringifySafe(move)}`);
831
- }
832
- switch (move.kind) {
833
- case "refine":
834
- return move;
835
- case "stop":
836
- return move;
837
- case "select": {
838
- if (!Number.isInteger(move.index) || move.index < 0) {
839
- throw new PlannerError(
840
- `dynamic planner select move must carry a non-negative integer index, got ${stringifySafe(move.index)}`
841
- );
842
- }
843
- return move;
844
- }
845
- case "fanout": {
846
- if (!Array.isArray(move.tasks) || move.tasks.length === 0) {
847
- throw new PlannerError("dynamic planner fanout move must carry a non-empty tasks[]");
848
- }
849
- if (move.tasks.length <= maxFanout) return move;
850
- return {
851
- kind: "fanout",
852
- tasks: move.tasks.slice(0, maxFanout),
853
- rationale: `${move.rationale ?? ""} [clamped ${move.tasks.length}\u2192${maxFanout}]`.trim()
854
- };
855
- }
856
- default:
857
- throw new PlannerError(
858
- `dynamic planner returned unknown move kind: ${stringifySafe(move.kind)}`
859
- );
860
- }
861
- }
862
- async function runAnalyze(analyze2, task, history) {
863
- const findings = await analyze2({ task, history });
864
- if (!Array.isArray(findings)) {
865
- throw new PlannerError(
866
- `createDriver: analyze hook must return AnalystFinding[], got ${stringifySafe(findings)}`
867
- );
868
- }
869
- assertTraceDerivedFindings(findings);
870
- return findings;
871
- }
872
- async function runComplete(complete, task, history) {
873
- const verdict = await complete.assess({ task, history });
874
- if (!verdict || typeof verdict.done !== "boolean" || verdict.determinism !== "deterministic" && verdict.determinism !== "probabilistic") {
875
- throw new PlannerError(
876
- `createDriver: complete.assess must return a CompletionVerdict {done, determinism}, got ${stringifySafe(verdict)}`
877
- );
878
- }
879
- return verdict;
880
- }
881
- function renderAnalyses(findings) {
882
- if (findings.length === 0) return "";
883
- const rows = findings.map((f) => {
884
- const action = f.recommended_action ? ` \u2192 ${f.recommended_action}` : "";
885
- return ` - [${f.severity}/${f.area}] ${f.claim}${action} (conf ${f.confidence.toFixed(2)})`;
886
- });
887
- return `Trace-analyst findings (diagnosis of the attempts so far \u2014 steer from these, not the verdict score alone):
888
- ${rows.join("\n")}`;
889
- }
890
-
891
659
  // src/runtime/observe.ts
892
660
  import { makeFinding } from "@tangle-network/agent-eval";
893
661
  var observerId = "observe/trace";
@@ -2231,7 +1999,126 @@ function createMcpEnvironment(opts) {
2231
1999
  };
2232
2000
  }
2233
2001
 
2002
+ // src/runtime/personify/analyst.ts
2003
+ var judgeEvidenceUri = /^(verdict|judge|score)\b/i;
2004
+ var assertTraceDerivedFindings = (findings) => {
2005
+ for (const f of findings) {
2006
+ for (const ref of f.evidence_refs ?? []) {
2007
+ if (ref.kind === "metric" && judgeEvidenceUri.test(ref.uri)) {
2008
+ throw new PlannerError(
2009
+ `steer-firewall: finding ${stringifySafe(f.finding_id)} cites judge-derived evidence (${stringifySafe(ref.uri)}); findings fed to a combinator's steer decision must be trace-derived, not judge-derived (selector \u2260 judge)`
2010
+ );
2011
+ }
2012
+ }
2013
+ }
2014
+ };
2015
+ function createScopeAnalyst(scope, options) {
2016
+ if (!options.analyst || typeof options.analyst.act !== "function") {
2017
+ throw new AnalystError("createScopeAnalyst: analyst must be an Agent with an act() method");
2018
+ }
2019
+ const label = options.label ?? "analyst";
2020
+ return {
2021
+ async analyze(input) {
2022
+ const task = options.buildTask(input);
2023
+ const spawned = scope.spawn(options.analyst, task, {
2024
+ budget: options.budget,
2025
+ label
2026
+ });
2027
+ if (!spawned.ok) {
2028
+ throw new AnalystError(
2029
+ `createScopeAnalyst: analyst spawn refused by the conserved pool (${spawned.reason}); cannot steer node ${stringifySafe(input.nodeId)} on an unrun analyst`
2030
+ );
2031
+ }
2032
+ const settled = await drainAnalystSettlement(scope, spawned.handle.id);
2033
+ const findings = readAnalystFindings(settled);
2034
+ assertTraceDerivedFindings(findings);
2035
+ return findings;
2036
+ }
2037
+ };
2038
+ }
2039
+ async function drainAnalystSettlement(scope, analystId) {
2040
+ for (; ; ) {
2041
+ const settled = await scope.next();
2042
+ if (settled === null) {
2043
+ throw new AnalystError(
2044
+ `createScopeAnalyst: scope drained before analyst ${stringifySafe(analystId)} settled`
2045
+ );
2046
+ }
2047
+ if (settled.handle.id === analystId) return settled;
2048
+ }
2049
+ }
2050
+ function readAnalystFindings(settled) {
2051
+ if (settled.kind === "down") {
2052
+ throw new AnalystError(
2053
+ `createScopeAnalyst: analyst ${stringifySafe(settled.handle.id)} settled down (${settled.infra ? "infra" : "result"}): ${stringifySafe(settled.reason)}`
2054
+ );
2055
+ }
2056
+ const out = settled.out;
2057
+ if (!Array.isArray(out)) {
2058
+ throw new PlannerError(
2059
+ `createScopeAnalyst: analyst ${stringifySafe(settled.handle.id)} must return AnalystFinding[], got ${stringifySafe(out)}`
2060
+ );
2061
+ }
2062
+ return out;
2063
+ }
2064
+ function registryScopeAnalyst(registry, buildInputs) {
2065
+ return {
2066
+ async analyze(input) {
2067
+ const projection = buildInputs(input);
2068
+ const result = await registry.run(projection.runId, projection.inputs, projection.opts);
2069
+ const findings = result.findings;
2070
+ assertTraceDerivedFindings(findings);
2071
+ return findings;
2072
+ }
2073
+ };
2074
+ }
2075
+ function buildSteerContext(findings, settledSoFar) {
2076
+ assertTraceDerivedFindings(findings);
2077
+ const lastValidScore = observedBestScore(settledSoFar);
2078
+ return {
2079
+ findings,
2080
+ settledSoFar,
2081
+ ...lastValidScore !== void 0 ? { lastValidScore } : {}
2082
+ };
2083
+ }
2084
+ function observedBestScore(settledSoFar) {
2085
+ let best;
2086
+ for (const s of settledSoFar) {
2087
+ if (s.kind !== "done") continue;
2088
+ const v = s.verdict;
2089
+ if (!v || v.valid !== true || typeof v.score !== "number") continue;
2090
+ if (best === void 0 || v.score > best) best = v.score;
2091
+ }
2092
+ return best;
2093
+ }
2094
+
2234
2095
  // src/runtime/supervise/scope.ts
2096
+ var nestedScopeSeamKey = "nested-scope";
2097
+ function makeNestedScopeSeam(args, childNodeId) {
2098
+ return {
2099
+ depth: args.depth,
2100
+ ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
2101
+ journalRoot: args.root,
2102
+ mount(nestedRoot, signal) {
2103
+ return createScope({
2104
+ parentId: childNodeId,
2105
+ root: nestedRoot,
2106
+ pool: args.pool,
2107
+ journal: args.journal,
2108
+ blobs: args.blobs,
2109
+ executors: args.executors,
2110
+ // Re-seed the parent's NON-recursion seams (sandbox/router for leaf grandchildren);
2111
+ // the nested scope adds its OWN nested-scope seam per child in `spawn`.
2112
+ seams: args.seams,
2113
+ depth: args.depth + 1,
2114
+ ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
2115
+ signal,
2116
+ ...args.now ? { now: args.now } : {},
2117
+ ...args.hooks ? { hooks: args.hooks } : {}
2118
+ });
2119
+ }
2120
+ };
2121
+ }
2235
2122
  function createScope(args) {
2236
2123
  const children = /* @__PURE__ */ new Map();
2237
2124
  let spawnOrdinal = 0;
@@ -2258,7 +2145,10 @@ function createScope(args) {
2258
2145
  const cascadeAbort = () => childAbort.abort();
2259
2146
  if (args.signal.aborted) childAbort.abort();
2260
2147
  else args.signal.addEventListener("abort", cascadeAbort, { once: true });
2261
- const ctx = { signal: childAbort.signal, seams: args.seams };
2148
+ const ctx = {
2149
+ signal: childAbort.signal,
2150
+ seams: { ...args.seams, [nestedScopeSeamKey]: makeNestedScopeSeam(args, id) }
2151
+ };
2262
2152
  const executor = resolved.value(spec, ctx);
2263
2153
  const handle = {
2264
2154
  id,
@@ -3020,8 +2910,8 @@ var InMemoryCorpus = class {
3020
2910
  }
3021
2911
  };
3022
2912
  var FileCorpus = class {
3023
- constructor(path) {
3024
- this.path = path;
2913
+ constructor(path2) {
2914
+ this.path = path2;
3025
2915
  }
3026
2916
  path;
3027
2917
  async append(record) {
@@ -3085,8 +2975,8 @@ var FileCorpus = class {
3085
2975
  }
3086
2976
  async appendLine(record) {
3087
2977
  const fs = await import("fs/promises");
3088
- const path = await import("path");
3089
- await fs.mkdir(path.dirname(this.path), { recursive: true });
2978
+ const path2 = await import("path");
2979
+ await fs.mkdir(path2.dirname(this.path), { recursive: true });
3090
2980
  const fh = await fs.open(this.path, "a");
3091
2981
  try {
3092
2982
  await fh.write(`${JSON.stringify(record)}
@@ -3135,16 +3025,170 @@ function isNoEntError2(err) {
3135
3025
  return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
3136
3026
  }
3137
3027
 
3138
- // src/runtime/supervise/runtime.ts
3139
- import { spawn } from "child_process";
3140
- import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
3141
-
3142
- // src/runtime/router-client.ts
3143
- import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
3144
- async function routerChatWithUsage(cfg, messages, opts) {
3145
- const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
3146
- const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
3147
- let temperature = opts?.temperature ?? 0.2;
3028
+ // src/runtime/supervise/driver-executor.ts
3029
+ var driverRuntime = "driver";
3030
+ var driverRole = "driver";
3031
+ function driverChild(name, driver, journal) {
3032
+ const spec = {
3033
+ profile: { name, metadata: { role: driverRole } },
3034
+ harness: null,
3035
+ driver,
3036
+ journal
3037
+ };
3038
+ return {
3039
+ name,
3040
+ executorSpec: spec,
3041
+ act() {
3042
+ throw new ValidationError(
3043
+ `driverChild: "${name}" was run directly; a driver child runs through its nested-scope executor`
3044
+ );
3045
+ }
3046
+ };
3047
+ }
3048
+ function isDriverSpec(spec) {
3049
+ const role = spec.profile.metadata?.role;
3050
+ if (role !== driverRole) return false;
3051
+ const driver = spec.driver;
3052
+ if (!isAgent(driver)) {
3053
+ throw new ValidationError(
3054
+ "driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
3055
+ );
3056
+ }
3057
+ return true;
3058
+ }
3059
+ var driverExecutorFactory = (spec, ctx) => {
3060
+ if (!isDriverSpec(spec)) {
3061
+ throw new ValidationError(
3062
+ 'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
3063
+ );
3064
+ }
3065
+ const driver = spec.driver;
3066
+ const journal = spec.journal;
3067
+ const seam = readNestedScopeSeam(ctx);
3068
+ let artifact;
3069
+ return {
3070
+ runtime: driverRuntime,
3071
+ async execute(task, signal) {
3072
+ const nestedRoot = nestedTreeKey(seam, journal);
3073
+ await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
3074
+ const nestedScope = seam.mount(nestedRoot, signal);
3075
+ const out = await driver.act(task, nestedScope);
3076
+ const settled = await loadSettled(journal, nestedRoot);
3077
+ const spent = sumSpend(settled);
3078
+ const verdict = deriveDeliveryVerdict(settled);
3079
+ artifact = {
3080
+ outRef: `${driverRuntime}:${nestedRoot}`,
3081
+ out,
3082
+ spent,
3083
+ ...verdict ? { verdict } : {}
3084
+ };
3085
+ return artifact;
3086
+ },
3087
+ teardown() {
3088
+ return Promise.resolve({ destroyed: true });
3089
+ },
3090
+ resultArtifact() {
3091
+ if (!artifact) {
3092
+ throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
3093
+ }
3094
+ return artifact;
3095
+ }
3096
+ };
3097
+ };
3098
+ function withDriverExecutor(base) {
3099
+ return {
3100
+ register: base.register.bind(base),
3101
+ resolve(spec) {
3102
+ const role = spec.profile.metadata?.role;
3103
+ if (role === driverRole && !spec.executor) {
3104
+ return { succeeded: true, value: driverExecutorFactory };
3105
+ }
3106
+ return base.resolve(spec);
3107
+ }
3108
+ };
3109
+ }
3110
+ function nestedTreeKey(seam, journal) {
3111
+ return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
3112
+ }
3113
+ var nestCounters = /* @__PURE__ */ new WeakMap();
3114
+ function nextNestOrdinal(journal) {
3115
+ let c = nestCounters.get(journal);
3116
+ if (!c) {
3117
+ c = { n: 0 };
3118
+ nestCounters.set(journal, c);
3119
+ }
3120
+ return c.n++;
3121
+ }
3122
+ async function loadSettled(journal, nestedRoot) {
3123
+ const events = await journal.loadTree(nestedRoot);
3124
+ if (events === void 0) {
3125
+ throw new ValidationError(
3126
+ `driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
3127
+ );
3128
+ }
3129
+ return events.filter(
3130
+ (ev) => ev.kind === "settled"
3131
+ );
3132
+ }
3133
+ function sumSpend(settled) {
3134
+ const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
3135
+ for (const ev of settled) {
3136
+ total.iterations += ev.spent.iterations;
3137
+ total.tokens.input += ev.spent.tokens.input;
3138
+ total.tokens.output += ev.spent.tokens.output;
3139
+ total.usd += ev.spent.usd;
3140
+ total.ms += ev.spent.ms;
3141
+ }
3142
+ return total;
3143
+ }
3144
+ function deriveDeliveryVerdict(settled) {
3145
+ let sawChild = false;
3146
+ let anyValid = false;
3147
+ let bestValidScore;
3148
+ let bestDoneScore;
3149
+ for (const ev of settled) {
3150
+ sawChild = true;
3151
+ if (ev.status !== "done") continue;
3152
+ const score = ev.verdict?.score;
3153
+ if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
3154
+ bestDoneScore = score;
3155
+ }
3156
+ if (ev.verdict?.valid === true) {
3157
+ anyValid = true;
3158
+ if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
3159
+ bestValidScore = score;
3160
+ }
3161
+ }
3162
+ }
3163
+ if (!sawChild) return void 0;
3164
+ return {
3165
+ valid: anyValid,
3166
+ score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
3167
+ };
3168
+ }
3169
+ function readNestedScopeSeam(ctx) {
3170
+ const seam = ctx.seams[nestedScopeSeamKey];
3171
+ if (!seam || typeof seam.mount !== "function") {
3172
+ throw new ValidationError(
3173
+ `driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
3174
+ );
3175
+ }
3176
+ return seam;
3177
+ }
3178
+ function isAgent(value) {
3179
+ return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
3180
+ }
3181
+
3182
+ // src/runtime/supervise/runtime.ts
3183
+ import { spawn } from "child_process";
3184
+ import { estimateCost as estimateCost2, isModelPriced as isModelPriced2 } from "@tangle-network/agent-eval";
3185
+
3186
+ // src/runtime/router-client.ts
3187
+ import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
3188
+ async function routerChatWithUsage(cfg, messages, opts) {
3189
+ const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
3190
+ const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
3191
+ let temperature = opts?.temperature ?? 0.2;
3148
3192
  let lastErr = "";
3149
3193
  for (let attempt = 0; attempt < 5; attempt += 1) {
3150
3194
  const res = await fetch(url, {
@@ -3194,7 +3238,8 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
3194
3238
  messages,
3195
3239
  tools,
3196
3240
  tool_choice: opts?.toolChoice ?? "auto",
3197
- temperature: opts?.temperature ?? 0.3
3241
+ temperature: opts?.temperature ?? 0.3,
3242
+ ...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
3198
3243
  }),
3199
3244
  ...opts?.signal ? { signal: opts.signal } : {}
3200
3245
  });
@@ -3218,7 +3263,7 @@ async function routerChatWithTools(cfg, messages, tools, opts) {
3218
3263
  }
3219
3264
  async function routerToolLoop(cfg, system, user, tools, execute, opts) {
3220
3265
  const maxTurns = opts?.maxTurns ?? 4;
3221
- const messages = [
3266
+ const messages = opts?.initialMessages ? [...opts.initialMessages] : [
3222
3267
  { role: "system", content: system },
3223
3268
  { role: "user", content: user }
3224
3269
  ];
@@ -3229,6 +3274,7 @@ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
3229
3274
  for (let turn = 1; turn <= maxTurns; turn += 1) {
3230
3275
  const r = await routerChatWithTools(cfg, messages, tools, {
3231
3276
  ...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
3277
+ ...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
3232
3278
  ...opts?.signal ? { signal: opts.signal } : {}
3233
3279
  });
3234
3280
  if (r.usage) {
@@ -3237,7 +3283,7 @@ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
3237
3283
  }
3238
3284
  if (r.content) lastText = r.content;
3239
3285
  if (r.toolCalls.length === 0)
3240
- return { final: lastText, turns: turn, toolCalls, toolTrace, usage };
3286
+ return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
3241
3287
  messages.push({
3242
3288
  role: "assistant",
3243
3289
  content: r.content ?? "",
@@ -3265,7 +3311,7 @@ async function routerToolLoop(cfg, system, user, tools, execute, opts) {
3265
3311
  toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
3266
3312
  }
3267
3313
  }
3268
- return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage };
3314
+ return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
3269
3315
  }
3270
3316
 
3271
3317
  // src/runtime/supervise/runtime.ts
@@ -4037,14 +4083,22 @@ function createSupervisor() {
4037
4083
  if (actOutcome.ok) {
4038
4084
  pool2.assertNoOpenTickets();
4039
4085
  const out = actOutcome.out;
4040
- const outRef = contentAddress(out);
4041
- await opts.blobs.put(outRef, out);
4086
+ if (out !== void 0) {
4087
+ const outRef = contentAddress(out);
4088
+ await opts.blobs.put(outRef, out);
4089
+ return {
4090
+ kind: "winner",
4091
+ out,
4092
+ outRef,
4093
+ tree,
4094
+ spentTotal: await spentTotalFromJournal(journal, opts.runId)
4095
+ };
4096
+ }
4042
4097
  return {
4043
- kind: "winner",
4044
- out,
4045
- outRef,
4098
+ kind: "no-winner",
4099
+ reason: classifyNoWinner(controller, pool2, opts, breaker),
4046
4100
  tree,
4047
- spentTotal: await spentTotalFromJournal(journal, opts.runId)
4101
+ downCount: breaker.downCount()
4048
4102
  };
4049
4103
  }
4050
4104
  return {
@@ -4245,7 +4299,7 @@ function createShapeContext(persona, budget, analyst) {
4245
4299
  executorSpec: spec,
4246
4300
  act() {
4247
4301
  throw new ValidationError(
4248
- `personify: spawned child "${name}" was run as a driver; its executorSpec drives a leaf`
4302
+ `personify: spawned child "${name}" was run directly; its executorSpec drives it (a leaf, or \u2014 for a driver child \u2014 a nested scope through the recursive driver-executor)`
4249
4303
  );
4250
4304
  }
4251
4305
  };
@@ -4279,7 +4333,8 @@ async function runPersonified(options) {
4279
4333
  ...options.maxRestarts !== void 0 ? { maxRestarts: options.maxRestarts } : {},
4280
4334
  ...options.withinMs !== void 0 ? { withinMs: options.withinMs } : {},
4281
4335
  ...options.now ? { now: options.now } : {},
4282
- ...options.signal ? { signal: options.signal } : {}
4336
+ ...options.signal ? { signal: options.signal } : {},
4337
+ ...options.hooks ? { hooks: options.hooks } : {}
4283
4338
  };
4284
4339
  return supervisor.run(rootAgent, options.task, supervisorOpts);
4285
4340
  }
@@ -4309,13 +4364,13 @@ function resolveShapeBudget(root, over) {
4309
4364
  var defaultFanout = 3;
4310
4365
  function personaRegistry(persona) {
4311
4366
  const { registry, seams } = persona.executors;
4312
- if (registry) return registry;
4367
+ if (registry) return withDriverExecutor(registry);
4313
4368
  if (!seams) {
4314
4369
  throw new ValidationError(
4315
4370
  `personify: persona "${persona.name}" supplies neither a registry nor seams`
4316
4371
  );
4317
4372
  }
4318
- return withSeams(createExecutorRegistry(), seams);
4373
+ return withDriverExecutor(withSeams(createExecutorRegistry(), seams));
4319
4374
  }
4320
4375
  function withSeams(base, seams) {
4321
4376
  return {
@@ -4682,59 +4737,41 @@ import { pairedBootstrap, paretoFrontier } from "@tangle-network/agent-eval";
4682
4737
  import { createChatClient, estimateCost as estimateCost3, isModelPriced as isModelPriced3 } from "@tangle-network/agent-eval";
4683
4738
  var taskNudge = "Use the available tools to bring the artifact to the required final state. Address EVERY distinct change the request implies. After each tool result, check what remains and continue. Re-read the values you set to confirm they took. Reply DONE only once every required change is made and verified.";
4684
4739
  async function runShot(surface, _task, handle, tools, messages, opts, modelOverride) {
4685
- const innerTurns = opts.innerTurns ?? 4;
4686
- let completions = 0;
4687
- let toolCalls = 0;
4688
4740
  let toolErrors = 0;
4689
- const tokens = { input: 0, output: 0 };
4690
- for (let t = 0; t < innerTurns; t += 1) {
4691
- const res = await fetch(`${opts.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
4692
- method: "POST",
4693
- headers: { "content-type": "application/json", authorization: `Bearer ${opts.routerKey}` },
4694
- body: JSON.stringify({
4695
- model: modelOverride ?? opts.model,
4696
- messages,
4697
- tools,
4698
- tool_choice: "auto",
4699
- temperature: opts.temperature ?? 0.7,
4700
- ...opts.maxTokens ? { max_tokens: opts.maxTokens } : {}
4701
- })
4702
- });
4703
- if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
4704
- completions += 1;
4705
- const data = await res.json();
4706
- if (typeof data.usage?.prompt_tokens === "number") tokens.input += data.usage.prompt_tokens;
4707
- if (typeof data.usage?.completion_tokens === "number")
4708
- tokens.output += data.usage.completion_tokens;
4709
- const msg = data.choices?.[0]?.message;
4710
- if (!msg) break;
4711
- const calls = msg.tool_calls ?? [];
4712
- messages.push({
4713
- role: "assistant",
4714
- content: msg.content ?? "",
4715
- ...calls.length ? { tool_calls: calls } : {}
4716
- });
4717
- if (calls.length === 0) break;
4718
- for (const call of calls) {
4719
- toolCalls += 1;
4720
- let args = {};
4721
- try {
4722
- args = JSON.parse(call.function.arguments || "{}");
4723
- } catch {
4724
- toolErrors += 1;
4725
- }
4726
- let out;
4727
- try {
4728
- out = await surface.call(handle, call.function.name, args);
4729
- if (out.startsWith("ERROR:")) toolErrors += 1;
4730
- } catch (e) {
4731
- toolErrors += 1;
4732
- out = `ERROR: ${e instanceof Error ? e.message : String(e)}`;
4733
- }
4734
- messages.push({ role: "tool", tool_call_id: call.id, content: out });
4741
+ const execute = async (name, args) => {
4742
+ try {
4743
+ const out = await surface.call(handle, name, args);
4744
+ if (out.startsWith("ERROR:")) toolErrors += 1;
4745
+ return out;
4746
+ } catch (e) {
4747
+ toolErrors += 1;
4748
+ return `ERROR: ${e instanceof Error ? e.message : String(e)}`;
4735
4749
  }
4736
- }
4737
- return { messages, completions, toolCalls, toolErrors, tokens };
4750
+ };
4751
+ const r = await routerToolLoop(
4752
+ {
4753
+ routerBaseUrl: opts.routerBaseUrl,
4754
+ routerKey: opts.routerKey,
4755
+ model: modelOverride ?? opts.model
4756
+ },
4757
+ "",
4758
+ "",
4759
+ tools,
4760
+ execute,
4761
+ {
4762
+ maxTurns: opts.innerTurns ?? 4,
4763
+ temperature: opts.temperature ?? 0.7,
4764
+ initialMessages: messages,
4765
+ ...opts.maxTokens ? { maxTokens: opts.maxTokens } : {}
4766
+ }
4767
+ );
4768
+ return {
4769
+ messages: r.messages,
4770
+ completions: r.turns,
4771
+ toolCalls: r.toolCalls,
4772
+ toolErrors,
4773
+ tokens: r.usage
4774
+ };
4738
4775
  }
4739
4776
  function compactTrajectory(messages) {
4740
4777
  return messages.filter((m) => m.role === "assistant" || m.role === "tool").map((m) => {
@@ -4915,7 +4952,7 @@ function analystExecutor(opts) {
4915
4952
  };
4916
4953
  }
4917
4954
  function agenticRegistry(surface, opts) {
4918
- return {
4955
+ const leaves = {
4919
4956
  register() {
4920
4957
  throw new Error("agenticRegistry: register unsupported");
4921
4958
  },
@@ -4925,13 +4962,14 @@ function agenticRegistry(surface, opts) {
4925
4962
  return { succeeded: true, value: factory };
4926
4963
  }
4927
4964
  };
4965
+ return withDriverExecutor(leaves);
4928
4966
  }
4929
4967
  function leaf(name, role) {
4930
4968
  const agent = {
4931
4969
  name,
4932
4970
  executorSpec: { profile: { name, metadata: { role } }, harness: null },
4933
4971
  act() {
4934
- throw new Error(`agentic: spawned leaf "${name}" run as a driver`);
4972
+ throw new Error(`agentic: spawned child "${name}" was run directly (the executor drives it)`);
4935
4973
  }
4936
4974
  };
4937
4975
  return agent;
@@ -6186,135 +6224,2812 @@ ${summary}`,
6186
6224
  };
6187
6225
  }
6188
6226
 
6189
- // src/runtime/verifier-environment.ts
6190
- var submitTool = {
6191
- type: "function",
6192
- function: {
6193
- name: "submit_answer",
6194
- description: "Submit your answer for evaluation. You may submit more than once \u2014 the best-scoring submission counts. Submit the COMPLETE final answer, not a fragment.",
6195
- parameters: {
6196
- type: "object",
6197
- properties: { answer: { type: "string", description: "The complete final answer." } },
6198
- required: ["answer"]
6227
+ // src/runtime/supervise/completion-gate.ts
6228
+ function gateOnDeliverable(inner, deliverable) {
6229
+ let gated;
6230
+ const check = async (out, baseScore) => {
6231
+ let delivered;
6232
+ try {
6233
+ delivered = await deliverable.check(out) === true;
6234
+ } catch {
6235
+ delivered = false;
6199
6236
  }
6200
- }
6201
- };
6202
- function createVerifierEnvironment(opts) {
6203
- if (opts.extraTools?.length && !opts.callExtra) {
6204
- throw new Error(`${opts.name}: extraTools requires callExtra`);
6205
- }
6206
- const states = /* @__PURE__ */ new Map();
6207
- let seq = 0;
6237
+ return { valid: delivered, score: baseScore ?? (delivered ? 1 : 0) };
6238
+ };
6208
6239
  return {
6209
- name: opts.name,
6210
- async open(task) {
6211
- seq += 1;
6212
- const handle = { id: `${opts.name}-${seq}`, surface: opts.name };
6213
- states.set(handle.id, { task, submissions: [] });
6214
- return handle;
6215
- },
6216
- async tools() {
6217
- return [submitTool, ...opts.extraTools ?? []];
6218
- },
6219
- async call(handle, name, args) {
6220
- const state = states.get(handle.id);
6221
- if (!state) return "ERROR: workspace closed";
6222
- if (name === "submit_answer") {
6223
- const answer = String(args.answer ?? "").trim();
6224
- if (!answer) return "ERROR: empty answer";
6225
- state.submissions.push(answer);
6226
- return `submission ${state.submissions.length} recorded`;
6227
- }
6228
- if (opts.callExtra && opts.extraTools?.some((t) => t.function.name === name)) {
6229
- try {
6230
- return await opts.callExtra(state.task, name, args);
6231
- } catch (e) {
6232
- return `ERROR: ${e instanceof Error ? e.message : String(e)}`;
6233
- }
6234
- }
6235
- return `ERROR: unknown tool ${name}`;
6236
- },
6237
- // Keep-best across submissions — the measured law (workers reach correct answers,
6238
- // then revise past them; final-state scoring undersells every strategy).
6239
- async score(task, handle) {
6240
- const state = states.get(handle.id);
6241
- if (!state || state.submissions.length === 0) return { passes: 0, total: 1, errored: 0 };
6242
- let best = { passes: 0, total: 1, errored: 0 };
6243
- const ratio = (s) => s.total > 0 ? s.passes / s.total : 0;
6244
- for (const answer of state.submissions) {
6245
- const s = await opts.check(task, answer);
6246
- if (ratio(s) > ratio(best)) best = s;
6240
+ runtime: inner.runtime,
6241
+ ...inner.budgetExempt !== void 0 ? { budgetExempt: inner.budgetExempt } : {},
6242
+ ...inner.deliver ? { deliver: (m) => inner.deliver?.(m) } : {},
6243
+ execute(task, signal) {
6244
+ const r = inner.execute(task, signal);
6245
+ if (isAsyncIterable3(r)) {
6246
+ return (async function* () {
6247
+ for await (const ev of r) yield ev;
6248
+ const art = inner.resultArtifact();
6249
+ gated = await check(art.out, art.verdict?.score);
6250
+ })();
6247
6251
  }
6248
- return best;
6252
+ return (async () => {
6253
+ const res = await r;
6254
+ gated = await check(res.out, res.verdict?.score);
6255
+ return { ...res, verdict: gated };
6256
+ })();
6249
6257
  },
6250
- async close(handle) {
6251
- states.delete(handle.id);
6258
+ teardown: (grace) => inner.teardown(grace),
6259
+ resultArtifact() {
6260
+ const art = inner.resultArtifact();
6261
+ return { ...art, verdict: gated ?? art.verdict };
6252
6262
  }
6253
6263
  };
6254
6264
  }
6265
+ function isAsyncIterable3(v) {
6266
+ return v != null && typeof v[Symbol.asyncIterator] === "function";
6267
+ }
6255
6268
 
6256
- // src/runtime/waterfall.ts
6257
- function createWaterfallCollector() {
6258
- let spans = /* @__PURE__ */ new Map();
6259
- const onEvent = (event) => {
6260
- if (event.target === "agent.spawn") {
6261
- const p = event.payload ?? {};
6262
- const id = p.childId ?? event.id;
6263
- spans.set(id, {
6264
- id,
6265
- label: p.label ?? id,
6266
- runId: event.runId,
6267
- ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
6268
- startMs: event.timestamp,
6269
- status: "running",
6270
- usd: 0,
6271
- tokens: { input: 0, output: 0 }
6272
- });
6273
- return;
6274
- }
6275
- if (event.target === "agent.child") {
6276
- const p = event.payload ?? {};
6277
- const id = p.childId;
6278
- if (!id) return;
6279
- const span = spans.get(id);
6280
- if (!span) return;
6281
- span.endMs = event.timestamp;
6282
- span.status = p.status === "down" ? "down" : "done";
6283
- span.usd = p.spent?.usd ?? 0;
6284
- span.tokens = {
6285
- input: p.spent?.tokens?.input ?? 0,
6286
- output: p.spent?.tokens?.output ?? 0
6269
+ // src/runtime/supervise/authoring.ts
6270
+ function asAuthoredProfile(raw) {
6271
+ const p = raw;
6272
+ if (!p || typeof p.systemPrompt !== "string" || p.systemPrompt.trim().length === 0) return null;
6273
+ return {
6274
+ name: typeof p.name === "string" && p.name.length > 0 ? p.name : "worker",
6275
+ systemPrompt: p.systemPrompt,
6276
+ ...typeof p.model === "string" ? { model: p.model } : {}
6277
+ };
6278
+ }
6279
+ function supervisorSkill(opts) {
6280
+ return [
6281
+ "You are a SUPERVISOR. You do NOT do the work yourself \u2014 your job is to DESIGN and DRIVE specialist worker agents.",
6282
+ "",
6283
+ "For the task you are given:",
6284
+ "1. DECOMPOSE it into the smallest set of sub-tasks a single focused worker can each deliver.",
6285
+ "2. For EACH sub-task, AUTHOR a worker by calling spawn_worker with a COMPLETE `profile`:",
6286
+ " \u2022 name: a short id for the worker.",
6287
+ ' \u2022 systemPrompt: rich, specific instructions for THIS sub-task \u2014 tell the worker exactly what to produce, how to use its tools fully, and what "done" means. Never a one-liner; write the prompt a power-user would write.',
6288
+ " \u2022 model: the model best suited to this sub-task (omit to use the default).",
6289
+ " NEVER spawn a worker with an empty profile. The quality of the worker IS the quality of the profile you write.",
6290
+ "3. await_next to collect each worker. Its result says valid:true only if the deployable check passed.",
6291
+ "4. If a worker did NOT deliver, AUTHOR A NEW worker whose systemPrompt names the SPECIFIC failure and how to fix it \u2014 never just retry the same prompt.",
6292
+ "5. Stop (reply with no tool call) once the work is delivered. You cannot declare done yourself \u2014 only a delivered (valid:true) worker counts.",
6293
+ ...opts?.goal ? ["", `The goal: ${opts.goal}`] : []
6294
+ ].join("\n");
6295
+ }
6296
+ function authoredWorker(profile, opts) {
6297
+ let artifact;
6298
+ const model = profile.model ?? opts.cfg.model;
6299
+ const inner = {
6300
+ runtime: "router",
6301
+ async execute(_t, signal) {
6302
+ const res = await routerChatWithUsage(
6303
+ { ...opts.cfg, model },
6304
+ [
6305
+ { role: "system", content: profile.systemPrompt },
6306
+ { role: "user", content: opts.taskPrompt }
6307
+ ],
6308
+ { temperature: opts.temperature ?? 0.4, ...signal ? { signal } : {} }
6309
+ );
6310
+ artifact = {
6311
+ outRef: contentAddress(res.content),
6312
+ out: res.content,
6313
+ spent: {
6314
+ iterations: 1,
6315
+ tokens: res.usage ?? { input: 0, output: 0 },
6316
+ usd: res.costUsd ?? 0,
6317
+ ms: 0
6318
+ }
6287
6319
  };
6288
- if (typeof p.score === "number") span.score = p.score;
6320
+ return artifact;
6321
+ },
6322
+ teardown: () => Promise.resolve({ destroyed: true }),
6323
+ resultArtifact: () => {
6324
+ if (!artifact) throw new Error("authoredWorker: resultArtifact read before execute");
6325
+ return artifact;
6289
6326
  }
6290
6327
  };
6291
- const report = () => {
6292
- const all = [...spans.values()].sort((a, b) => a.startMs - b.startMs);
6293
- const start = all[0]?.startMs ?? 0;
6294
- const end = Math.max(start, ...all.map((s) => s.endMs ?? s.startMs));
6295
- const byKind = {};
6296
- let totalUsd = 0;
6297
- const totalTokens2 = { input: 0, output: 0 };
6298
- for (const s of all) {
6299
- totalUsd += s.usd;
6300
- totalTokens2.input += s.tokens.input;
6301
- totalTokens2.output += s.tokens.output;
6302
- const kind = s.label.includes(":") ? s.label.split(":")[0] : s.label;
6303
- const k = byKind[kind] ??= { count: 0, ms: 0, usd: 0, tokens: { input: 0, output: 0 } };
6304
- k.count += 1;
6305
- k.ms += (s.endMs ?? s.startMs) - s.startMs;
6306
- k.usd += s.usd;
6307
- k.tokens.input += s.tokens.input;
6308
- k.tokens.output += s.tokens.output;
6309
- }
6310
- return { spans: all, totalMs: end - start, totalUsd, totalTokens: totalTokens2, byKind };
6328
+ const gated = gateOnDeliverable(inner, opts.deliverable);
6329
+ const spec = {
6330
+ profile: { name: profile.name },
6331
+ harness: null,
6332
+ executor: gated
6311
6333
  };
6312
- const render = (opts) => {
6313
- const { spans: all, totalMs, totalUsd, byKind } = report();
6314
- if (all.length === 0) return "(no spans observed)";
6315
- const width = opts?.width ?? 48;
6316
- const maxRows = opts?.maxRows ?? 60;
6317
- const start = all[0]?.startMs ?? 0;
6334
+ return { name: profile.name, act: async () => "", executorSpec: spec };
6335
+ }
6336
+
6337
+ // src/mcp/tools/coordination.ts
6338
+ var idArg = { type: "string", description: "The workerId returned by spawn_worker." };
6339
+ function createCoordinationTools(opts) {
6340
+ let stopped = false;
6341
+ let reason;
6342
+ let questionSeq = 0;
6343
+ const ledger = [];
6344
+ const questions = [];
6345
+ const questionPolicy = opts.questionPolicy ?? "auto";
6346
+ const str = (v, field) => {
6347
+ if (typeof v !== "string" || v.length === 0)
6348
+ throw new Error(`coordination tools: "${field}" must be a non-empty string`);
6349
+ return v;
6350
+ };
6351
+ const obj = (raw) => {
6352
+ if (!raw || typeof raw !== "object")
6353
+ throw new Error("coordination tools: arguments must be an object");
6354
+ return raw;
6355
+ };
6356
+ const level = (v) => {
6357
+ if (v === "worker" || v === "driver" || v === "loop") return v;
6358
+ throw new Error('coordination tools: "level" must be worker, driver, or loop');
6359
+ };
6360
+ const urgency = (v) => {
6361
+ if (v === "continue-without" || v === "blocks-step" || v === "blocks-run") return v;
6362
+ throw new Error(
6363
+ 'coordination tools: "urgency" must be continue-without, blocks-step, or blocks-run'
6364
+ );
6365
+ };
6366
+ const recordSettled = (s) => {
6367
+ const w = s.kind === "done" ? {
6368
+ id: s.handle.id,
6369
+ status: "done",
6370
+ score: s.verdict?.score ?? 0,
6371
+ valid: s.verdict?.valid ?? false,
6372
+ outRef: s.outRef
6373
+ } : { id: s.handle.id, status: "down", reason: s.reason };
6374
+ ledger.push(w);
6375
+ return w;
6376
+ };
6377
+ const nextQuestionId = (from) => `${from}:q${questionSeq++}`;
6378
+ const normalizeQuestion = (q, fallbackFrom) => {
6379
+ const from = str(q.from ?? fallbackFrom, "from");
6380
+ return {
6381
+ id: typeof q.id === "string" && q.id.length > 0 ? q.id : nextQuestionId(from),
6382
+ from,
6383
+ level: level(q.level),
6384
+ question: str(q.question, "question"),
6385
+ reason: str(q.reason, "reason"),
6386
+ ...q.options ? { options: q.options } : {},
6387
+ urgency: urgency(q.urgency)
6388
+ };
6389
+ };
6390
+ const addQuestion = (raw, fallbackFrom, decision) => {
6391
+ const q = normalizeQuestion(raw, fallbackFrom);
6392
+ const existing = questions.find((x) => x.id === q.id);
6393
+ if (existing) return { question: existing, added: false };
6394
+ const effectiveDecision = decision ?? (questionPolicy === "bubble" ? {
6395
+ kind: "escalate",
6396
+ to: "parent",
6397
+ reason: "question policy bubbled to parent"
6398
+ } : void 0);
6399
+ const status = effectiveDecision?.kind === "answer" ? "answered" : effectiveDecision?.kind === "defer" ? "deferred" : effectiveDecision?.kind === "escalate" ? "escalated" : "open";
6400
+ const record = {
6401
+ ...q,
6402
+ status,
6403
+ openedAt: Date.now(),
6404
+ ...effectiveDecision ? { decision: effectiveDecision } : {}
6405
+ };
6406
+ questions.push(record);
6407
+ return { question: record, added: true };
6408
+ };
6409
+ const emitNewQuestion = async (record) => {
6410
+ if (record.added) await opts.onEvent?.({ type: "question", question: record.question });
6411
+ return record.question;
6412
+ };
6413
+ const decideQuestion = (questionId, decision) => {
6414
+ const idx = questions.findIndex((q) => q.id === questionId);
6415
+ if (idx < 0) throw new Error(`unknown questionId ${JSON.stringify(questionId)}`);
6416
+ const prior = questions[idx];
6417
+ const status = decision.kind === "answer" ? "answered" : decision.kind === "defer" ? "deferred" : "escalated";
6418
+ const next = { ...prior, status, decision };
6419
+ questions[idx] = next;
6420
+ return next;
6421
+ };
6422
+ const blockingQuestionsForStop = () => {
6423
+ if (questionPolicy === "auto" || questionPolicy === "bubble") return [];
6424
+ return questions.filter((q) => {
6425
+ const blocking = q.urgency === "blocks-step" || q.urgency === "blocks-run";
6426
+ if (!blocking) return false;
6427
+ if (questionPolicy === "mustDecide") return q.status === "open";
6428
+ return q.status !== "answered" && q.status !== "deferred";
6429
+ });
6430
+ };
6431
+ const tools = [
6432
+ {
6433
+ name: "spawn_worker",
6434
+ description: "Start a worker the driver will drive. `profile` is the worker or another driver; `task` is what it should do. Reserves budget from the conserved pool and fails closed.",
6435
+ inputSchema: {
6436
+ type: "object",
6437
+ properties: {
6438
+ profile: { description: "The worker/driver profile to run." },
6439
+ task: { description: "The task the worker should perform." },
6440
+ label: { type: "string", description: "Optional trace label." }
6441
+ },
6442
+ required: ["profile", "task"]
6443
+ },
6444
+ handler: (raw) => {
6445
+ const a = obj(raw);
6446
+ const agent = opts.makeWorkerAgent(a.profile);
6447
+ const res = opts.scope.spawn(agent, a.task, {
6448
+ budget: opts.perWorker,
6449
+ label: typeof a.label === "string" ? a.label : "worker"
6450
+ });
6451
+ return Promise.resolve(res.ok ? { workerId: res.handle.id } : { error: res.reason });
6452
+ }
6453
+ },
6454
+ {
6455
+ name: "observe_worker",
6456
+ description: "Inspect a worker status, spend, and settled output artifact when available.",
6457
+ inputSchema: { type: "object", properties: { workerId: idArg }, required: ["workerId"] },
6458
+ handler: async (raw) => {
6459
+ const id = str(obj(raw).workerId, "workerId");
6460
+ const node = opts.scope.view.nodes.find((n) => n.id === id);
6461
+ if (!node) return { error: `unknown workerId ${JSON.stringify(id)}` };
6462
+ const output = node.outRef ? await opts.blobs.get(node.outRef) : void 0;
6463
+ return {
6464
+ status: node.status,
6465
+ spent: node.spent,
6466
+ outRef: node.outRef ?? null,
6467
+ output: output ?? null
6468
+ };
6469
+ }
6470
+ },
6471
+ {
6472
+ name: "steer_worker",
6473
+ description: "Deliver an out-of-band instruction to a running worker inbox.",
6474
+ inputSchema: {
6475
+ type: "object",
6476
+ properties: {
6477
+ workerId: idArg,
6478
+ instruction: { type: "string", description: "What the worker should do next." }
6479
+ },
6480
+ required: ["workerId", "instruction"]
6481
+ },
6482
+ handler: (raw) => {
6483
+ const a = obj(raw);
6484
+ const delivered = opts.scope.send(str(a.workerId, "workerId"), {
6485
+ steer: str(a.instruction, "instruction")
6486
+ });
6487
+ return Promise.resolve({ delivered });
6488
+ }
6489
+ },
6490
+ {
6491
+ name: "await_next",
6492
+ description: "Wait for the next spawned worker to settle. Returns { idle: true } when none are live.",
6493
+ inputSchema: { type: "object", properties: {} },
6494
+ handler: async () => {
6495
+ const s = await opts.scope.next();
6496
+ if (!s) return { idle: true };
6497
+ const w = recordSettled(s);
6498
+ return w.status === "done" ? {
6499
+ settled: w.id,
6500
+ status: "done",
6501
+ score: w.score,
6502
+ valid: w.valid,
6503
+ outRef: w.outRef
6504
+ } : { settled: w.id, status: "down", reason: w.reason };
6505
+ }
6506
+ },
6507
+ {
6508
+ name: "list_questions",
6509
+ description: "List questions raised by workers, drivers, or analysts. Blocking stop behavior follows questionPolicy.",
6510
+ inputSchema: { type: "object", properties: {} },
6511
+ handler: () => Promise.resolve({ questions })
6512
+ },
6513
+ {
6514
+ name: "answer_question",
6515
+ description: "Record an answer, deferral, or escalation for a loop question.",
6516
+ inputSchema: {
6517
+ type: "object",
6518
+ properties: {
6519
+ questionId: { type: "string" },
6520
+ answer: { type: "string" },
6521
+ by: { type: "string", description: 'Node id or "user".' },
6522
+ deferReason: { type: "string" },
6523
+ escalateTo: { type: "string", enum: ["parent", "user"] },
6524
+ escalateReason: { type: "string" }
6525
+ },
6526
+ required: ["questionId"]
6527
+ },
6528
+ handler: (raw) => {
6529
+ const a = obj(raw);
6530
+ const questionId = str(a.questionId, "questionId");
6531
+ if (typeof a.answer === "string" && a.answer.length > 0) {
6532
+ return Promise.resolve({
6533
+ question: decideQuestion(questionId, {
6534
+ kind: "answer",
6535
+ answer: a.answer,
6536
+ by: typeof a.by === "string" && a.by.length > 0 ? a.by : "user"
6537
+ })
6538
+ });
6539
+ }
6540
+ if (typeof a.deferReason === "string" && a.deferReason.length > 0) {
6541
+ return Promise.resolve({
6542
+ question: decideQuestion(questionId, {
6543
+ kind: "defer",
6544
+ reason: a.deferReason
6545
+ })
6546
+ });
6547
+ }
6548
+ if (a.escalateTo === "parent" || a.escalateTo === "user") {
6549
+ const escalateReason = typeof a.escalateReason === "string" && a.escalateReason.length > 0 ? a.escalateReason : "driver escalated";
6550
+ return Promise.resolve({
6551
+ question: decideQuestion(questionId, {
6552
+ kind: "escalate",
6553
+ to: a.escalateTo,
6554
+ reason: escalateReason
6555
+ })
6556
+ });
6557
+ }
6558
+ throw new Error("answer_question: provide answer, deferReason, or escalateTo");
6559
+ }
6560
+ },
6561
+ {
6562
+ name: "ask_parent",
6563
+ description: "Raise a question to the parent driver/Pi/user when this driver cannot decide.",
6564
+ inputSchema: {
6565
+ type: "object",
6566
+ properties: {
6567
+ from: { type: "string" },
6568
+ level: { type: "string", enum: ["worker", "driver", "loop"] },
6569
+ question: { type: "string" },
6570
+ reason: { type: "string" },
6571
+ urgency: { type: "string", enum: ["continue-without", "blocks-step", "blocks-run"] }
6572
+ },
6573
+ required: ["from", "level", "question", "reason", "urgency"]
6574
+ },
6575
+ handler: async (raw) => {
6576
+ const a = obj(raw);
6577
+ const from = str(a.from, "from");
6578
+ const q = await emitNewQuestion(
6579
+ addQuestion(
6580
+ {
6581
+ from,
6582
+ level: level(a.level),
6583
+ question: str(a.question, "question"),
6584
+ reason: str(a.reason, "reason"),
6585
+ urgency: urgency(a.urgency)
6586
+ },
6587
+ from,
6588
+ { kind: "escalate", to: "parent", reason: "asked parent" }
6589
+ )
6590
+ );
6591
+ return { question: q };
6592
+ }
6593
+ },
6594
+ {
6595
+ name: "stop",
6596
+ description: "Declare the run complete.",
6597
+ inputSchema: {
6598
+ type: "object",
6599
+ properties: { reason: { type: "string", description: "Why you are stopping." } }
6600
+ },
6601
+ handler: (raw) => {
6602
+ const blocking = blockingQuestionsForStop();
6603
+ if (blocking.length) {
6604
+ return Promise.resolve({
6605
+ stopped: false,
6606
+ error: "unresolved-blocking-questions",
6607
+ questions: blocking
6608
+ });
6609
+ }
6610
+ stopped = true;
6611
+ const r = obj(raw).reason;
6612
+ reason = typeof r === "string" ? r : void 0;
6613
+ return Promise.resolve({ stopped: true });
6614
+ }
6615
+ }
6616
+ ];
6617
+ if (opts.analysts) {
6618
+ tools.push({
6619
+ name: "list_analysts",
6620
+ description: "List trace-analyst lenses available to run over a settled worker.",
6621
+ inputSchema: { type: "object", properties: {} },
6622
+ handler: () => Promise.resolve({ analysts: opts.analysts?.kinds })
6623
+ });
6624
+ tools.push({
6625
+ name: "run_analyst",
6626
+ description: "Apply an analyst lens to a settled worker trace.",
6627
+ inputSchema: {
6628
+ type: "object",
6629
+ properties: {
6630
+ kind: { type: "string", description: "The analyst kind id." },
6631
+ workerId: idArg
6632
+ },
6633
+ required: ["kind", "workerId"]
6634
+ },
6635
+ handler: async (raw) => {
6636
+ const a = obj(raw);
6637
+ const id = str(a.workerId, "workerId");
6638
+ const node = opts.scope.view.nodes.find((n) => n.id === id);
6639
+ if (!node) return { error: `unknown workerId ${JSON.stringify(id)}` };
6640
+ if (!node.outRef)
6641
+ return { error: `worker ${JSON.stringify(id)} has not settled \u2014 no trace to analyze yet` };
6642
+ const trace = await opts.blobs.get(node.outRef);
6643
+ return { findings: await opts.analysts?.run(str(a.kind, "kind"), trace) };
6644
+ }
6645
+ });
6646
+ }
6647
+ return {
6648
+ tools,
6649
+ isStopped: () => stopped,
6650
+ stopReason: () => reason,
6651
+ settled: () => ledger,
6652
+ questions: () => questions
6653
+ };
6654
+ }
6655
+
6656
+ // src/runtime/supervise/coordination-driver.ts
6657
+ function coordinationDriverAgent(opts) {
6658
+ if (typeof opts.chat?.next !== "function") {
6659
+ throw new ValidationError("coordinationDriverAgent: opts.chat.next must be a function");
6660
+ }
6661
+ const maxTurns = opts.maxTurns ?? 16;
6662
+ return {
6663
+ name: opts.name,
6664
+ async act(task, scope) {
6665
+ const coord = createCoordinationTools({
6666
+ scope,
6667
+ blobs: opts.blobs,
6668
+ makeWorkerAgent: opts.makeWorkerAgent,
6669
+ perWorker: opts.perWorker
6670
+ });
6671
+ const byName = new Map(coord.tools.map((t) => [t.name, t]));
6672
+ const toolSpecs = coord.tools.map((t) => ({
6673
+ name: t.name,
6674
+ description: t.description,
6675
+ parameters: t.inputSchema
6676
+ }));
6677
+ const system = typeof opts.systemPrompt === "function" ? opts.systemPrompt(task) : opts.systemPrompt;
6678
+ const messages = [{ role: "user", content: stringifyTask(task) }];
6679
+ for (let turn = 0; turn < maxTurns; turn += 1) {
6680
+ if (coord.isStopped()) break;
6681
+ const res = await opts.chat.next({ system, messages, tools: toolSpecs });
6682
+ const calls = res.toolCalls ?? [];
6683
+ if (calls.length === 0) {
6684
+ return finalize2(coord, opts.blobs);
6685
+ }
6686
+ messages.push({ role: "assistant", content: res.content ?? "", toolCalls: calls });
6687
+ for (const tc of calls) {
6688
+ const tool = byName.get(tc.name);
6689
+ const result = tool ? await runTool(tool, tc.arguments) : { error: `unknown tool: ${tc.name}` };
6690
+ messages.push({
6691
+ role: "tool",
6692
+ ...tc.id ? { toolCallId: tc.id } : {},
6693
+ name: tc.name,
6694
+ content: safeJson(result)
6695
+ });
6696
+ }
6697
+ }
6698
+ return finalize2(coord, opts.blobs);
6699
+ }
6700
+ };
6701
+ }
6702
+ async function runTool(tool, args) {
6703
+ try {
6704
+ return await tool.handler(args);
6705
+ } catch (e) {
6706
+ return { error: e instanceof Error ? e.message : String(e) };
6707
+ }
6708
+ }
6709
+ async function finalize2(coord, blobs) {
6710
+ const delivered = coord.settled().filter((w) => w.status === "done" && w.valid === true);
6711
+ if (delivered.length === 0) return void 0;
6712
+ let best = delivered[0];
6713
+ for (const w of delivered) if ((w.score ?? 0) > (best.score ?? 0)) best = w;
6714
+ return best.outRef ? await blobs.get(best.outRef) : void 0;
6715
+ }
6716
+ function stringifyTask(task) {
6717
+ return typeof task === "string" ? task : safeJson(task);
6718
+ }
6719
+ function safeJson(v) {
6720
+ try {
6721
+ return JSON.stringify(v) ?? String(v);
6722
+ } catch {
6723
+ return String(v);
6724
+ }
6725
+ }
6726
+
6727
+ // src/runtime/supervise/coordination-mcp.ts
6728
+ import { createServer } from "http";
6729
+
6730
+ // src/mcp/server.ts
6731
+ import { createInterface } from "readline";
6732
+ import { Readable, Writable } from "stream";
6733
+
6734
+ // src/mcp/feedback-store.ts
6735
+ var InMemoryFeedbackStore = class {
6736
+ events = [];
6737
+ async put(event) {
6738
+ this.events.push({ ...event });
6739
+ }
6740
+ async list(filter = {}) {
6741
+ let out = this.events;
6742
+ if (filter.namespace !== void 0) {
6743
+ out = out.filter((event) => event.namespace === filter.namespace);
6744
+ }
6745
+ if (filter.refersToRef !== void 0) {
6746
+ out = out.filter((event) => event.refersTo.ref === filter.refersToRef);
6747
+ }
6748
+ return out.map((event) => ({ ...event }));
6749
+ }
6750
+ };
6751
+ function eventToSnapshot(event) {
6752
+ const snap = {
6753
+ id: event.id,
6754
+ score: event.rating.score,
6755
+ by: event.by,
6756
+ notes: event.rating.notes,
6757
+ capturedAt: event.capturedAt
6758
+ };
6759
+ if (event.rating.label) snap.label = event.rating.label;
6760
+ return snap;
6761
+ }
6762
+
6763
+ // src/mcp/delegation-store.ts
6764
+ import { mkdir, readFile, rename, writeFile } from "fs/promises";
6765
+ import { dirname } from "path";
6766
+ var DelegationStateCorruptError = class extends AgentEvalError {
6767
+ constructor(message, options) {
6768
+ super("validation", message, options);
6769
+ }
6770
+ };
6771
+ var DelegationPersistenceError = class extends AgentEvalError {
6772
+ constructor(message, options) {
6773
+ super("config", message, options);
6774
+ }
6775
+ };
6776
+ var InMemoryDelegationStore = class {
6777
+ records = /* @__PURE__ */ new Map();
6778
+ async loadAll() {
6779
+ return [...this.records.values()].map(cloneRecord);
6780
+ }
6781
+ async upsert(record) {
6782
+ this.records.set(record.taskId, cloneRecord(record));
6783
+ }
6784
+ async lookupIdempotencyKey(key) {
6785
+ for (const record of this.records.values()) {
6786
+ if (record.idempotencyKey === key) return record.taskId;
6787
+ }
6788
+ return void 0;
6789
+ }
6790
+ async remove(taskIds) {
6791
+ for (const taskId of taskIds) this.records.delete(taskId);
6792
+ }
6793
+ };
6794
+ var STATE_FORMAT_VERSION = 1;
6795
+ var FileDelegationStore = class {
6796
+ filePath;
6797
+ recoverCorrupt;
6798
+ records = /* @__PURE__ */ new Map();
6799
+ loaded = false;
6800
+ writeTail = Promise.resolve();
6801
+ tmpSeq = 0;
6802
+ constructor(options) {
6803
+ this.filePath = options.filePath;
6804
+ this.recoverCorrupt = options.recoverCorrupt ?? false;
6805
+ }
6806
+ async loadAll() {
6807
+ let raw;
6808
+ try {
6809
+ raw = await readFile(this.filePath, "utf8");
6810
+ } catch (err) {
6811
+ if (err.code === "ENOENT") {
6812
+ this.loaded = true;
6813
+ return [];
6814
+ }
6815
+ throw new DelegationPersistenceError(
6816
+ `FileDelegationStore: failed to read ${this.filePath}: ${errorMessage2(err)}`,
6817
+ { cause: err }
6818
+ );
6819
+ }
6820
+ let state;
6821
+ try {
6822
+ state = parsePersistedState(raw);
6823
+ } catch (err) {
6824
+ if (!this.recoverCorrupt) {
6825
+ throw new DelegationStateCorruptError(
6826
+ `FileDelegationStore: state file ${this.filePath} is corrupt (${errorMessage2(err)}). Repair or archive the file, or opt into automatic recovery (recoverCorrupt / AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1) to archive it and start empty.`,
6827
+ { cause: err }
6828
+ );
6829
+ }
6830
+ const archivePath = `${this.filePath}.corrupt-${Date.now()}`;
6831
+ await rename(this.filePath, archivePath);
6832
+ this.loaded = true;
6833
+ return [];
6834
+ }
6835
+ this.records.clear();
6836
+ for (const record of state.records) this.records.set(record.taskId, record);
6837
+ this.loaded = true;
6838
+ return [...this.records.values()].map(cloneRecord);
6839
+ }
6840
+ async upsert(record) {
6841
+ this.assertLoaded("upsert");
6842
+ this.records.set(record.taskId, cloneRecord(record));
6843
+ await this.enqueueWrite();
6844
+ }
6845
+ async lookupIdempotencyKey(key) {
6846
+ this.assertLoaded("lookupIdempotencyKey");
6847
+ for (const record of this.records.values()) {
6848
+ if (record.idempotencyKey === key) return record.taskId;
6849
+ }
6850
+ return void 0;
6851
+ }
6852
+ async remove(taskIds) {
6853
+ this.assertLoaded("remove");
6854
+ let changed = false;
6855
+ for (const taskId of taskIds) {
6856
+ if (this.records.delete(taskId)) changed = true;
6857
+ }
6858
+ if (changed) await this.enqueueWrite();
6859
+ }
6860
+ assertLoaded(op) {
6861
+ if (this.loaded) return;
6862
+ throw new DelegationPersistenceError(
6863
+ `FileDelegationStore: ${op} called before loadAll() \u2014 the on-disk state has not been read yet`
6864
+ );
6865
+ }
6866
+ enqueueWrite() {
6867
+ const write = this.writeTail.then(() => this.writeSnapshot());
6868
+ this.writeTail = write.catch(() => {
6869
+ });
6870
+ return write;
6871
+ }
6872
+ async writeSnapshot() {
6873
+ const state = {
6874
+ version: STATE_FORMAT_VERSION,
6875
+ records: [...this.records.values()]
6876
+ };
6877
+ const payload = `${JSON.stringify(state)}
6878
+ `;
6879
+ this.tmpSeq += 1;
6880
+ const tmpPath = `${this.filePath}.tmp-${process.pid}-${this.tmpSeq}`;
6881
+ try {
6882
+ await mkdir(dirname(this.filePath), { recursive: true });
6883
+ await writeFile(tmpPath, payload, "utf8");
6884
+ await rename(tmpPath, this.filePath);
6885
+ } catch (err) {
6886
+ throw new DelegationPersistenceError(
6887
+ `FileDelegationStore: failed to write ${this.filePath}: ${errorMessage2(err)}`,
6888
+ { cause: err }
6889
+ );
6890
+ }
6891
+ }
6892
+ };
6893
+ function parsePersistedState(raw) {
6894
+ const parsed = JSON.parse(raw);
6895
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
6896
+ throw new Error("top-level value is not an object");
6897
+ }
6898
+ const state = parsed;
6899
+ if (state.version !== STATE_FORMAT_VERSION) {
6900
+ throw new Error(`unsupported state version ${JSON.stringify(state.version)}`);
6901
+ }
6902
+ if (!Array.isArray(state.records)) {
6903
+ throw new Error("`records` is not an array");
6904
+ }
6905
+ for (const record of state.records) {
6906
+ if (record === null || typeof record !== "object") {
6907
+ throw new Error("a record entry is not an object");
6908
+ }
6909
+ const candidate = record;
6910
+ if (typeof candidate.taskId !== "string" || typeof candidate.status !== "string") {
6911
+ throw new Error("a record entry is missing `taskId`/`status`");
6912
+ }
6913
+ }
6914
+ return { version: STATE_FORMAT_VERSION, records: state.records };
6915
+ }
6916
+ function cloneRecord(record) {
6917
+ return structuredClone(record);
6918
+ }
6919
+ function errorMessage2(err) {
6920
+ return err instanceof Error ? err.message : String(err);
6921
+ }
6922
+
6923
+ // src/mcp/delegation-trace.ts
6924
+ var DELEGATION_TRACE_MAX_SPANS = 512;
6925
+ var DELEGATION_TRACE_MAX_BYTES = 256 * 1024;
6926
+ function buildDelegationTraceSpans(events) {
6927
+ return buildLoopSpanNodes(events).map((node) => ({
6928
+ spanId: node.spanId,
6929
+ ...node.parentSpanId !== void 0 ? { parentSpanId: node.parentSpanId } : {},
6930
+ name: node.name,
6931
+ kind: node.kind,
6932
+ startMs: node.startMs,
6933
+ endMs: node.endMs,
6934
+ ...Object.keys(node.attrs).length > 0 ? { meta: node.attrs } : {}
6935
+ }));
6936
+ }
6937
+ function capDelegationTrace(spans, caps) {
6938
+ const maxSpans = caps?.maxSpans ?? DELEGATION_TRACE_MAX_SPANS;
6939
+ const maxBytes = caps?.maxBytes ?? DELEGATION_TRACE_MAX_BYTES;
6940
+ let start = Math.max(0, spans.length - maxSpans);
6941
+ const sizes = spans.map((span) => JSON.stringify(span).length + 1);
6942
+ let total = 0;
6943
+ for (let i = start; i < sizes.length; i += 1) total += sizes[i];
6944
+ while (start < spans.length - 1 && total > maxBytes) {
6945
+ total -= sizes[start];
6946
+ start += 1;
6947
+ }
6948
+ return { trace: spans.slice(start), truncated: start > 0 };
6949
+ }
6950
+ function createDelegationTraceCollector(onSpans) {
6951
+ const buffers = /* @__PURE__ */ new Map();
6952
+ const flush = (events) => {
6953
+ const spans = buildDelegationTraceSpans(events);
6954
+ if (spans.length > 0) onSpans(spans);
6955
+ };
6956
+ return {
6957
+ emitter: {
6958
+ emit(event) {
6959
+ const buf = buffers.get(event.runId);
6960
+ if (buf) buf.push(event);
6961
+ else buffers.set(event.runId, [event]);
6962
+ if (event.kind === "loop.ended") {
6963
+ const events = buffers.get(event.runId) ?? [event];
6964
+ buffers.delete(event.runId);
6965
+ flush(events);
6966
+ }
6967
+ }
6968
+ },
6969
+ settle() {
6970
+ for (const events of buffers.values()) flush(events);
6971
+ buffers.clear();
6972
+ }
6973
+ };
6974
+ }
6975
+ function generateDelegationSpanId() {
6976
+ const bytes = new Uint8Array(8);
6977
+ if (typeof globalThis.crypto?.getRandomValues === "function") {
6978
+ globalThis.crypto.getRandomValues(bytes);
6979
+ } else {
6980
+ for (let i = 0; i < 8; i += 1) bytes[i] = Math.floor(Math.random() * 256);
6981
+ }
6982
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
6983
+ }
6984
+ function composeLoopTraceEmitters(...emitters) {
6985
+ const live = emitters.filter((e) => e !== void 0);
6986
+ if (live.length === 0) return void 0;
6987
+ if (live.length === 1) return live[0];
6988
+ return {
6989
+ emit(event) {
6990
+ const pending = [];
6991
+ for (const emitter of live) {
6992
+ const result = emitter.emit(event);
6993
+ if (result) pending.push(result);
6994
+ }
6995
+ if (pending.length > 0) return Promise.all(pending).then(() => void 0);
6996
+ }
6997
+ };
6998
+ }
6999
+
7000
+ // src/mcp/task-queue.ts
7001
+ var DelegationTaskQueue = class _DelegationTaskQueue {
7002
+ records = /* @__PURE__ */ new Map();
7003
+ controllers = /* @__PURE__ */ new Map();
7004
+ byIdempotencyKey = /* @__PURE__ */ new Map();
7005
+ generateId;
7006
+ now;
7007
+ store;
7008
+ resumeDelegate;
7009
+ maxTerminalRecords;
7010
+ onPersistError;
7011
+ traceContext;
7012
+ persistTail = Promise.resolve();
7013
+ persistFailure;
7014
+ constructor(options = {}) {
7015
+ this.generateId = options.generateId ?? randomTaskId;
7016
+ this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
7017
+ this.store = options.store ?? new InMemoryDelegationStore();
7018
+ this.resumeDelegate = options.resumeDelegate;
7019
+ if (options.maxTerminalRecords !== void 0) {
7020
+ if (!Number.isInteger(options.maxTerminalRecords) || options.maxTerminalRecords < 1) {
7021
+ throw new ValidationError(
7022
+ `DelegationTaskQueue: maxTerminalRecords must be a positive integer, got ${String(options.maxTerminalRecords)}`
7023
+ );
7024
+ }
7025
+ }
7026
+ this.maxTerminalRecords = options.maxTerminalRecords ?? Number.POSITIVE_INFINITY;
7027
+ this.traceContext = options.traceContext;
7028
+ this.onPersistError = options.onPersistError ?? ((error) => {
7029
+ queueMicrotask(() => {
7030
+ throw error;
7031
+ });
7032
+ });
7033
+ }
7034
+ /**
7035
+ * Construct a queue from previously-persisted state. Loads every record
7036
+ * from `options.store`, rebuilds the idempotency index (so a re-submitted
7037
+ * identical task returns the prior taskId and its terminal state), then:
7038
+ *
7039
+ * - terminal records stay queryable via `status()` / `history()`
7040
+ * - in-flight records with a `detachedSessionRef` re-attach through
7041
+ * `options.resumeDelegate` and report `running`
7042
+ * - other in-flight records settle as failed — their driver died with
7043
+ * the previous process and the result is unrecoverable
7044
+ *
7045
+ * The retention cap applies to the loaded set as well.
7046
+ */
7047
+ static async restore(options = {}) {
7048
+ const queue = new _DelegationTaskQueue(options);
7049
+ const loaded = await queue.store.loadAll();
7050
+ queue.rehydrate(loaded);
7051
+ return queue;
7052
+ }
7053
+ /**
7054
+ * Kick off a delegation in the background. Returns immediately. The
7055
+ * `taskId` is queryable via `status` once this method returns. Throws
7056
+ * the recorded `DelegationPersistenceError` once the store has failed —
7057
+ * the queue does not accept work it cannot journal.
7058
+ */
7059
+ submit(input) {
7060
+ if (this.persistFailure) throw this.persistFailure;
7061
+ if (input.idempotencyKey) {
7062
+ const existing = this.byIdempotencyKey.get(input.idempotencyKey);
7063
+ if (existing && this.records.has(existing)) {
7064
+ return { taskId: existing, reused: true };
7065
+ }
7066
+ }
7067
+ const taskId = this.generateId();
7068
+ const controller = new AbortController();
7069
+ const record = {
7070
+ taskId,
7071
+ profile: input.profile,
7072
+ namespace: input.namespace,
7073
+ args: input.args,
7074
+ status: "pending",
7075
+ startedAt: this.now(),
7076
+ feedback: [],
7077
+ idempotencyKey: input.idempotencyKey,
7078
+ detachedSessionRef: input.detachedSessionRef,
7079
+ ...this.traceContext !== void 0 ? {
7080
+ traceId: this.traceContext.traceId,
7081
+ ...this.traceContext.parentSpanId !== void 0 ? { parentSpanId: this.traceContext.parentSpanId } : {}
7082
+ } : {}
7083
+ };
7084
+ this.records.set(taskId, record);
7085
+ this.controllers.set(taskId, controller);
7086
+ if (input.idempotencyKey) this.byIdempotencyKey.set(input.idempotencyKey, taskId);
7087
+ this.persist(record);
7088
+ queueMicrotask(() => {
7089
+ this.execute(taskId, input, controller);
7090
+ });
7091
+ return { taskId, reused: false };
7092
+ }
7093
+ /**
7094
+ * Snapshot the current state of a delegation. Returns `undefined` for
7095
+ * unknown ids so callers can distinguish missing from terminal.
7096
+ * `includeTrace` attaches the journaled loop-trace span tree — off by
7097
+ * default so status polls stay light.
7098
+ */
7099
+ status(taskId, opts) {
7100
+ const record = this.records.get(taskId);
7101
+ if (!record) return void 0;
7102
+ return toStatusResult(record, opts);
7103
+ }
7104
+ /**
7105
+ * Abort an in-flight delegation. Returns `false` if the task is unknown
7106
+ * or already terminal. The underlying `run` function MUST honor the
7107
+ * abort signal for the cancel to take effect; the queue marks the
7108
+ * record `cancelled` regardless so a misbehaving runner cannot pin the
7109
+ * UI on `running` forever.
7110
+ */
7111
+ cancel(taskId) {
7112
+ const record = this.records.get(taskId);
7113
+ if (!record) return false;
7114
+ if (isTerminal(record.status)) return false;
7115
+ const controller = this.controllers.get(taskId);
7116
+ controller?.abort();
7117
+ record.status = "cancelled";
7118
+ record.completedAt = this.now();
7119
+ record.error = { message: "cancelled by caller", kind: "CancelledError" };
7120
+ this.persist(record);
7121
+ this.enforceRetention();
7122
+ return true;
7123
+ }
7124
+ /**
7125
+ * Append a feedback event to the matching delegation. Returns `false`
7126
+ * when `ref` does not name a known taskId — the caller should still
7127
+ * record the feedback through a different surface (artifact/outcome
7128
+ * kinds are not queue-bound).
7129
+ */
7130
+ attachFeedback(taskId, snapshot) {
7131
+ const record = this.records.get(taskId);
7132
+ if (!record) return false;
7133
+ record.feedback.push(snapshot);
7134
+ this.persist(record);
7135
+ return true;
7136
+ }
7137
+ /**
7138
+ * Query the recorded delegations. Returns entries newest-first (by
7139
+ * `startedAt`), truncated to `limit`.
7140
+ */
7141
+ history(args = {}) {
7142
+ const limit = clampLimit(args.limit);
7143
+ const since = args.since ? Date.parse(args.since) : Number.NEGATIVE_INFINITY;
7144
+ const out = [];
7145
+ for (const record of this.records.values()) {
7146
+ if (args.namespace && record.namespace !== args.namespace) continue;
7147
+ if (args.profile && record.profile !== args.profile) continue;
7148
+ if (Number.isFinite(since) && Date.parse(record.startedAt) < since) continue;
7149
+ out.push(toHistoryEntry(record));
7150
+ }
7151
+ out.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
7152
+ return out.slice(0, limit);
7153
+ }
7154
+ /**
7155
+ * Await every journal write issued so far. Rejects with the recorded
7156
+ * `DelegationPersistenceError` when any of them failed. Call before
7157
+ * handing the store's backing file to another process.
7158
+ */
7159
+ async flush() {
7160
+ await this.persistTail;
7161
+ if (this.persistFailure) throw this.persistFailure;
7162
+ }
7163
+ /** Test-only — number of in-flight (non-terminal) records. */
7164
+ inflightCount() {
7165
+ let n = 0;
7166
+ for (const record of this.records.values()) {
7167
+ if (!isTerminal(record.status)) n += 1;
7168
+ }
7169
+ return n;
7170
+ }
7171
+ async execute(taskId, input, controller) {
7172
+ const record = this.records.get(taskId);
7173
+ if (!record) return;
7174
+ record.status = "running";
7175
+ this.persist(record);
7176
+ const traceCollector = createDelegationTraceCollector((spans) => {
7177
+ if (isTerminal(currentStatus(record))) return;
7178
+ this.appendTrace(record, spans);
7179
+ this.persist(record);
7180
+ });
7181
+ try {
7182
+ const output = await input.run({
7183
+ signal: controller.signal,
7184
+ report: (progress) => {
7185
+ if (record.status === "running") {
7186
+ record.progress = progress;
7187
+ this.persist(record);
7188
+ }
7189
+ },
7190
+ traceEmitter: traceCollector.emitter,
7191
+ ...record.detachedSessionRef !== void 0 ? { detachedSessionRef: record.detachedSessionRef } : {},
7192
+ updateDetachedSessionRef: (ref) => {
7193
+ if (typeof ref !== "string" || ref.length === 0) {
7194
+ throw new ValidationError(
7195
+ "DelegationTaskQueue: updateDetachedSessionRef requires a non-empty ref"
7196
+ );
7197
+ }
7198
+ if (isTerminal(currentStatus(record))) return;
7199
+ record.detachedSessionRef = ref;
7200
+ this.persist(record);
7201
+ }
7202
+ });
7203
+ traceCollector.settle();
7204
+ if (currentStatus(record) === "cancelled") return;
7205
+ record.status = "completed";
7206
+ record.completedAt = this.now();
7207
+ record.result = { profile: input.profile, output };
7208
+ this.persist(record);
7209
+ this.enforceRetention();
7210
+ } catch (err) {
7211
+ traceCollector.settle();
7212
+ if (currentStatus(record) === "cancelled") return;
7213
+ record.status = "failed";
7214
+ record.completedAt = this.now();
7215
+ record.error = errorToShape(err);
7216
+ this.persist(record);
7217
+ this.enforceRetention();
7218
+ } finally {
7219
+ this.controllers.delete(taskId);
7220
+ }
7221
+ }
7222
+ appendTrace(record, spans) {
7223
+ if (spans.length === 0) return;
7224
+ const { trace, truncated } = capDelegationTrace([...record.trace ?? [], ...spans]);
7225
+ record.trace = trace;
7226
+ if (truncated) record.traceTruncated = true;
7227
+ }
7228
+ rehydrate(loaded) {
7229
+ const records = [...loaded].sort((a, b) => a.startedAt.localeCompare(b.startedAt));
7230
+ for (const record of records) {
7231
+ this.records.set(record.taskId, record);
7232
+ if (record.idempotencyKey) this.byIdempotencyKey.set(record.idempotencyKey, record.taskId);
7233
+ }
7234
+ for (const record of this.records.values()) {
7235
+ if (isTerminal(record.status)) continue;
7236
+ if (record.detachedSessionRef && this.resumeDelegate) {
7237
+ record.status = "running";
7238
+ this.persist(record);
7239
+ this.startResume(record, record.detachedSessionRef, this.resumeDelegate);
7240
+ continue;
7241
+ }
7242
+ record.status = "failed";
7243
+ record.completedAt = this.now();
7244
+ record.error = {
7245
+ message: record.detachedSessionRef ? `delegation driver restarted while the task was in flight; detached session "${record.detachedSessionRef}" needs a resumeDelegate to be resumed` : "delegation driver restarted while the task was in flight; the run was not detached and cannot be resumed",
7246
+ kind: "DriverRestartError"
7247
+ };
7248
+ this.persist(record);
7249
+ }
7250
+ this.enforceRetention();
7251
+ }
7252
+ startResume(record, detachedSessionRef, driver) {
7253
+ const controller = new AbortController();
7254
+ this.controllers.set(record.taskId, controller);
7255
+ void this.driveResume(record, detachedSessionRef, driver, controller);
7256
+ }
7257
+ async driveResume(record, detachedSessionRef, driver, controller) {
7258
+ const intervalMs = driver.intervalMs ?? 5e3;
7259
+ const resumeStartMs = Date.parse(this.now());
7260
+ const ctx = {
7261
+ signal: controller.signal,
7262
+ report: (progress) => {
7263
+ if (currentStatus(record) !== "running") return;
7264
+ record.progress = progress;
7265
+ this.persist(record);
7266
+ }
7267
+ };
7268
+ try {
7269
+ while (!controller.signal.aborted && currentStatus(record) === "running") {
7270
+ const tick = await driver.tick({ record: structuredClone(record), detachedSessionRef }, ctx);
7271
+ if (currentStatus(record) === "cancelled") return;
7272
+ if (tick.state === "completed") {
7273
+ this.appendResumeSpan(record, detachedSessionRef, resumeStartMs);
7274
+ record.status = "completed";
7275
+ record.completedAt = this.now();
7276
+ record.result = {
7277
+ profile: record.profile,
7278
+ output: tick.output
7279
+ };
7280
+ if (tick.costUsd !== void 0) record.costUsd = tick.costUsd;
7281
+ this.persist(record);
7282
+ this.enforceRetention();
7283
+ return;
7284
+ }
7285
+ if (tick.state === "failed") {
7286
+ this.appendResumeSpan(record, detachedSessionRef, resumeStartMs, tick.error.message);
7287
+ record.status = "failed";
7288
+ record.completedAt = this.now();
7289
+ record.error = tick.error;
7290
+ this.persist(record);
7291
+ this.enforceRetention();
7292
+ return;
7293
+ }
7294
+ await abortableDelay(intervalMs, controller.signal);
7295
+ }
7296
+ } catch (err) {
7297
+ if (currentStatus(record) === "cancelled") return;
7298
+ this.appendResumeSpan(record, detachedSessionRef, resumeStartMs, errorToShape(err).message);
7299
+ record.status = "failed";
7300
+ record.completedAt = this.now();
7301
+ record.error = errorToShape(err);
7302
+ this.persist(record);
7303
+ this.enforceRetention();
7304
+ } finally {
7305
+ this.controllers.delete(record.taskId);
7306
+ }
7307
+ }
7308
+ /**
7309
+ * Journal the resumed segment of a detached run as one compact span. The
7310
+ * resume driver re-attaches after a process restart, so the original
7311
+ * process's loop events are gone — this span records the post-restart
7312
+ * observation window (re-attach → terminal tick) under the
7313
+ * `'detached-resume'` driver tag, keeping restored delegations observable
7314
+ * in the journal alongside trace-carrying live runs.
7315
+ */
7316
+ appendResumeSpan(record, detachedSessionRef, startMs, error) {
7317
+ this.appendTrace(record, [
7318
+ {
7319
+ spanId: generateDelegationSpanId(),
7320
+ name: "loop",
7321
+ kind: "loop",
7322
+ startMs,
7323
+ endMs: Date.parse(this.now()),
7324
+ meta: {
7325
+ "tangle.loop.driver": "detached-resume",
7326
+ "tangle.loop.detached_session_ref": detachedSessionRef,
7327
+ ...error !== void 0 ? { "tangle.loop.error": error } : {}
7328
+ }
7329
+ }
7330
+ ]);
7331
+ }
7332
+ persist(record) {
7333
+ if (this.persistFailure) return;
7334
+ const snapshot = structuredClone(record);
7335
+ this.persistTail = this.persistTail.then(async () => {
7336
+ if (this.persistFailure) return;
7337
+ try {
7338
+ await this.store.upsert(snapshot);
7339
+ } catch (err) {
7340
+ this.failPersistence(err);
7341
+ }
7342
+ });
7343
+ }
7344
+ persistRemoval(taskIds) {
7345
+ if (this.persistFailure || taskIds.length === 0) return;
7346
+ this.persistTail = this.persistTail.then(async () => {
7347
+ if (this.persistFailure) return;
7348
+ try {
7349
+ await this.store.remove(taskIds);
7350
+ } catch (err) {
7351
+ this.failPersistence(err);
7352
+ }
7353
+ });
7354
+ }
7355
+ failPersistence(cause) {
7356
+ if (this.persistFailure) return;
7357
+ const error = cause instanceof DelegationPersistenceError ? cause : new DelegationPersistenceError(
7358
+ `DelegationTaskQueue: store write failed: ${cause instanceof Error ? cause.message : String(cause)}`,
7359
+ { cause }
7360
+ );
7361
+ this.persistFailure = error;
7362
+ this.onPersistError(error);
7363
+ }
7364
+ enforceRetention() {
7365
+ if (!Number.isFinite(this.maxTerminalRecords)) return;
7366
+ const terminal = [];
7367
+ for (const record of this.records.values()) {
7368
+ if (isTerminal(record.status)) terminal.push(record);
7369
+ }
7370
+ const excess = terminal.length - this.maxTerminalRecords;
7371
+ if (excess <= 0) return;
7372
+ terminal.sort(
7373
+ (a, b) => (a.completedAt ?? a.startedAt).localeCompare(b.completedAt ?? b.startedAt)
7374
+ );
7375
+ const evicted = terminal.slice(0, excess);
7376
+ for (const record of evicted) {
7377
+ this.records.delete(record.taskId);
7378
+ if (record.idempotencyKey && this.byIdempotencyKey.get(record.idempotencyKey) === record.taskId) {
7379
+ this.byIdempotencyKey.delete(record.idempotencyKey);
7380
+ }
7381
+ }
7382
+ this.persistRemoval(evicted.map((record) => record.taskId));
7383
+ }
7384
+ };
7385
+ function isTerminal(status) {
7386
+ return status === "completed" || status === "failed" || status === "cancelled";
7387
+ }
7388
+ function currentStatus(record) {
7389
+ return record.status;
7390
+ }
7391
+ function clampLimit(raw) {
7392
+ if (!Number.isFinite(raw)) return 50;
7393
+ const n = Math.trunc(raw);
7394
+ if (n <= 0) return 50;
7395
+ return Math.min(n, 500);
7396
+ }
7397
+ function abortableDelay(ms, signal) {
7398
+ return new Promise((resolve) => {
7399
+ if (signal.aborted) {
7400
+ resolve();
7401
+ return;
7402
+ }
7403
+ const onAbort = () => {
7404
+ clearTimeout(timer);
7405
+ resolve();
7406
+ };
7407
+ const timer = setTimeout(() => {
7408
+ signal.removeEventListener("abort", onAbort);
7409
+ resolve();
7410
+ }, ms);
7411
+ signal.addEventListener("abort", onAbort, { once: true });
7412
+ });
7413
+ }
7414
+ function toStatusResult(record, opts) {
7415
+ const out = {
7416
+ taskId: record.taskId,
7417
+ profile: record.profile,
7418
+ status: record.status,
7419
+ startedAt: record.startedAt
7420
+ };
7421
+ if (record.progress) out.progress = record.progress;
7422
+ if (record.result) out.result = record.result;
7423
+ if (record.error) out.error = record.error;
7424
+ if (record.costUsd !== void 0) out.costUsd = record.costUsd;
7425
+ if (record.completedAt) out.completedAt = record.completedAt;
7426
+ if (record.traceId !== void 0) out.traceId = record.traceId;
7427
+ if (record.parentSpanId !== void 0) out.parentSpanId = record.parentSpanId;
7428
+ if (opts?.includeTrace === true && record.trace && record.trace.length > 0) {
7429
+ out.trace = record.trace.map((span) => ({ ...span }));
7430
+ if (record.traceTruncated) out.traceTruncated = true;
7431
+ }
7432
+ return out;
7433
+ }
7434
+ function toHistoryEntry(record) {
7435
+ const entry = {
7436
+ taskId: record.taskId,
7437
+ profile: record.profile,
7438
+ args: record.args,
7439
+ status: record.status,
7440
+ startedAt: record.startedAt,
7441
+ hasTrace: record.trace !== void 0 && record.trace.length > 0
7442
+ };
7443
+ if (record.namespace) entry.namespace = record.namespace;
7444
+ if (record.completedAt) entry.completedAt = record.completedAt;
7445
+ if (record.costUsd !== void 0) entry.costUsd = record.costUsd;
7446
+ if (record.feedback.length > 0) entry.feedback = [...record.feedback];
7447
+ if (record.traceId !== void 0) entry.traceId = record.traceId;
7448
+ return entry;
7449
+ }
7450
+ function errorToShape(err) {
7451
+ if (err instanceof Error) {
7452
+ return { message: err.message, kind: err.name || "Error" };
7453
+ }
7454
+ return { message: String(err), kind: "NonError" };
7455
+ }
7456
+ function randomTaskId() {
7457
+ const t = Date.now().toString(36);
7458
+ const r = Math.random().toString(36).slice(2, 10);
7459
+ return `dlg-${t}-${r}`;
7460
+ }
7461
+ function hashIdempotencyInput(value) {
7462
+ let str;
7463
+ try {
7464
+ str = JSON.stringify(canonicalize(value));
7465
+ } catch {
7466
+ str = String(value);
7467
+ }
7468
+ let h = 2166136261;
7469
+ for (let i = 0; i < str.length; i += 1) {
7470
+ h ^= str.charCodeAt(i);
7471
+ h = Math.imul(h, 16777619);
7472
+ }
7473
+ return (h >>> 0).toString(16).padStart(8, "0");
7474
+ }
7475
+ function canonicalize(value) {
7476
+ if (value === null || typeof value !== "object") return value;
7477
+ if (Array.isArray(value)) return value.map(canonicalize);
7478
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a.localeCompare(b));
7479
+ const out = {};
7480
+ for (const [k, v] of entries) out[k] = canonicalize(v);
7481
+ return out;
7482
+ }
7483
+
7484
+ // src/mcp/detached-turn.ts
7485
+ var DEFAULT_TICK_INTERVAL_MS = 5e3;
7486
+ function formatDetachedSessionRef(parts) {
7487
+ assertRefComponent("sessionId", parts.sessionId);
7488
+ if (parts.sandboxId === void 0) return `session=${parts.sessionId}`;
7489
+ assertRefComponent("sandboxId", parts.sandboxId);
7490
+ return `sandbox=${parts.sandboxId};session=${parts.sessionId}`;
7491
+ }
7492
+ function parseDetachedSessionRef(raw) {
7493
+ const fields = /* @__PURE__ */ new Map();
7494
+ for (const pair of raw.split(";")) {
7495
+ const eq = pair.indexOf("=");
7496
+ const key = eq === -1 ? "" : pair.slice(0, eq);
7497
+ const value = eq === -1 ? "" : pair.slice(eq + 1);
7498
+ if (key !== "session" && key !== "sandbox" || value.length === 0 || fields.has(key)) {
7499
+ throw new ValidationError(
7500
+ `parseDetachedSessionRef: malformed detachedSessionRef ${JSON.stringify(raw)} \u2014 expected "session=<id>" or "sandbox=<id>;session=<id>"`
7501
+ );
7502
+ }
7503
+ fields.set(key, value);
7504
+ }
7505
+ const sessionId = fields.get("session");
7506
+ if (!sessionId) {
7507
+ throw new ValidationError(
7508
+ `parseDetachedSessionRef: detachedSessionRef ${JSON.stringify(raw)} carries no session id`
7509
+ );
7510
+ }
7511
+ const sandboxId = fields.get("sandbox");
7512
+ return { sessionId, ...sandboxId !== void 0 ? { sandboxId } : {} };
7513
+ }
7514
+ function assertRefComponent(name, value) {
7515
+ if (value.length === 0 || value.includes(";") || value.includes("=")) {
7516
+ throw new ValidationError(
7517
+ `formatDetachedSessionRef: ${name} ${JSON.stringify(value)} must be non-empty and free of ";" / "="`
7518
+ );
7519
+ }
7520
+ }
7521
+ function detachedTurnEvents(sessionId, turn) {
7522
+ return [
7523
+ {
7524
+ type: "result",
7525
+ id: sessionId,
7526
+ data: {
7527
+ text: turn.text,
7528
+ finalText: turn.text,
7529
+ success: true,
7530
+ result: turn.result
7531
+ }
7532
+ }
7533
+ ];
7534
+ }
7535
+ async function runDetachedTurn(options) {
7536
+ const intervalMs = options.tickIntervalMs ?? DEFAULT_TICK_INTERVAL_MS;
7537
+ const trace = createDetachedTurnTrace(options);
7538
+ trace.started();
7539
+ const box = await createSandboxForSpec(options.client, options.spec, options.signal).catch(
7540
+ (err) => {
7541
+ trace.ended(err instanceof Error ? err.message : String(err));
7542
+ throw err;
7543
+ }
7544
+ );
7545
+ const drive = box;
7546
+ const onAbort = () => {
7547
+ void drive._sessionCancel?.(options.sessionId).catch(() => {
7548
+ });
7549
+ };
7550
+ try {
7551
+ if (typeof drive.driveTurn !== "function") {
7552
+ throw new ValidationError(
7553
+ "runDetachedTurn: the acquired sandbox exposes no driveTurn(message, { sessionId }) \u2014 detached dispatch requires @tangle-network/sandbox >= 0.6 and a session-backed placement (sibling/fleet); disable detached dispatch for this executor."
7554
+ );
7555
+ }
7556
+ const sandboxId = box.id;
7557
+ if (typeof sandboxId !== "string" || sandboxId.length === 0) {
7558
+ throw new ValidationError(
7559
+ "runDetachedTurn: the acquired sandbox carries no id \u2014 without it the detached run cannot be resumed after a restart, so refusing to dispatch detached."
7560
+ );
7561
+ }
7562
+ options.bindSandbox(sandboxId);
7563
+ trace.dispatched(sandboxId);
7564
+ options.signal.addEventListener("abort", onAbort, { once: true });
7565
+ for (; ; ) {
7566
+ throwIfAborted(options.signal);
7567
+ const tick = await drive.driveTurn(options.prompt, {
7568
+ sessionId: options.sessionId,
7569
+ turnId: options.sessionId,
7570
+ ...options.wallCapMs !== void 0 ? { wallCapMs: options.wallCapMs } : {}
7571
+ });
7572
+ throwIfAborted(options.signal);
7573
+ if (tick.state === "completed") {
7574
+ trace.ended();
7575
+ return { text: tick.text, result: tick.result };
7576
+ }
7577
+ if (tick.state === "failed") {
7578
+ throw new Error(`detached turn ${options.sessionId} failed: ${tick.error}`);
7579
+ }
7580
+ options.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) });
7581
+ await sleep(intervalMs, options.signal);
7582
+ }
7583
+ } catch (err) {
7584
+ trace.ended(err instanceof Error ? err.message : String(err));
7585
+ throw err;
7586
+ } finally {
7587
+ options.signal.removeEventListener("abort", onAbort);
7588
+ if (options.signal.aborted) onAbort();
7589
+ await deleteBoxSafe(box);
7590
+ }
7591
+ }
7592
+ function createDetachedTurnTrace(options) {
7593
+ const emitter = options.traceEmitter;
7594
+ if (!emitter) {
7595
+ return { started() {
7596
+ }, dispatched() {
7597
+ }, ended() {
7598
+ } };
7599
+ }
7600
+ const runId = options.sessionId;
7601
+ const agentRunName = options.spec.name ?? options.spec.profile.name ?? "detached-turn";
7602
+ const startMs = Date.now();
7603
+ let done = false;
7604
+ const emit = (event) => {
7605
+ void emitter.emit(event);
7606
+ };
7607
+ return {
7608
+ started() {
7609
+ emit({
7610
+ kind: "loop.started",
7611
+ runId,
7612
+ timestamp: startMs,
7613
+ payload: {
7614
+ driver: "detached-turn",
7615
+ agentRunNames: [agentRunName],
7616
+ maxIterations: 1,
7617
+ maxConcurrency: 1
7618
+ }
7619
+ });
7620
+ emit({
7621
+ kind: "loop.iteration.started",
7622
+ runId,
7623
+ timestamp: startMs,
7624
+ payload: { iterationIndex: 0, agentRunName, taskHash: options.sessionId }
7625
+ });
7626
+ },
7627
+ dispatched(sandboxId) {
7628
+ emit({
7629
+ kind: "loop.iteration.dispatch",
7630
+ runId,
7631
+ timestamp: Date.now(),
7632
+ payload: {
7633
+ iterationIndex: 0,
7634
+ agentRunName,
7635
+ placement: options.placement ?? "sibling",
7636
+ sandboxId
7637
+ }
7638
+ });
7639
+ },
7640
+ ended(error) {
7641
+ if (done) return;
7642
+ done = true;
7643
+ const endMs = Date.now();
7644
+ emit({
7645
+ kind: "loop.iteration.ended",
7646
+ runId,
7647
+ timestamp: endMs,
7648
+ payload: {
7649
+ iterationIndex: 0,
7650
+ agentRunName,
7651
+ costUsd: 0,
7652
+ durationMs: endMs - startMs,
7653
+ ...error !== void 0 ? { error } : {}
7654
+ }
7655
+ });
7656
+ emit({
7657
+ kind: "loop.ended",
7658
+ runId,
7659
+ timestamp: endMs,
7660
+ payload: {
7661
+ ...error === void 0 ? { winnerIterationIndex: 0 } : {},
7662
+ totalCostUsd: 0,
7663
+ durationMs: endMs - startMs,
7664
+ iterations: 1
7665
+ }
7666
+ });
7667
+ }
7668
+ };
7669
+ }
7670
+ function detachedRunningPhase(elapsedMs) {
7671
+ return elapsedMs === void 0 ? "detached-running" : `detached-running ${Math.round(elapsedMs / 1e3)}s`;
7672
+ }
7673
+ function createDriveTurnResumeDriver(options) {
7674
+ const cancelHooked = /* @__PURE__ */ new Set();
7675
+ return {
7676
+ intervalMs: options.intervalMs ?? DEFAULT_TICK_INTERVAL_MS,
7677
+ async tick({ record, detachedSessionRef }, ctx) {
7678
+ const ref = parseDetachedSessionRef(detachedSessionRef);
7679
+ if (ref.sandboxId === void 0) {
7680
+ return {
7681
+ state: "failed",
7682
+ error: {
7683
+ message: `detached session "${ref.sessionId}" was never bound to a sandbox \u2014 the previous process died before the box was acquired, so the turn was never dispatched and cannot be resumed`,
7684
+ kind: "DetachedSessionUnboundError"
7685
+ }
7686
+ };
7687
+ }
7688
+ const box = await options.resolveSandbox(ref.sandboxId);
7689
+ if (!cancelHooked.has(record.taskId)) {
7690
+ cancelHooked.add(record.taskId);
7691
+ ctx.signal.addEventListener(
7692
+ "abort",
7693
+ () => {
7694
+ void box._sessionCancel?.(ref.sessionId).catch(() => {
7695
+ });
7696
+ },
7697
+ { once: true }
7698
+ );
7699
+ }
7700
+ if (ctx.signal.aborted) throwAbort();
7701
+ const tick = await box.driveTurn(options.buildMessage(record), {
7702
+ sessionId: ref.sessionId,
7703
+ turnId: ref.sessionId,
7704
+ ...options.wallCapMs !== void 0 ? { wallCapMs: options.wallCapMs } : {}
7705
+ });
7706
+ if (tick.state === "completed") {
7707
+ const output = await options.settleOutput(
7708
+ { text: tick.text, result: tick.result },
7709
+ record,
7710
+ {
7711
+ signal: ctx.signal
7712
+ }
7713
+ );
7714
+ return { state: "completed", output };
7715
+ }
7716
+ if (tick.state === "failed") {
7717
+ return {
7718
+ state: "failed",
7719
+ error: {
7720
+ message: `detached turn ${ref.sessionId} failed: ${tick.error}`,
7721
+ kind: "DetachedTurnFailedError"
7722
+ }
7723
+ };
7724
+ }
7725
+ ctx.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) });
7726
+ return { state: "running" };
7727
+ }
7728
+ };
7729
+ }
7730
+
7731
+ // src/mcp/tools/delegate-code.ts
7732
+ var DELEGATE_CODE_TOOL_NAME = "delegate_code";
7733
+ var DELEGATE_CODE_DESCRIPTION = [
7734
+ "Delegate a coding task to specialist coder agents that produce a validated patch.",
7735
+ "",
7736
+ "Use when: you need code written, fixed, refactored, or extended to satisfy a",
7737
+ "user goal that touches a real repository. The coder runs in an isolated",
7738
+ "sandbox, opens a fresh branch, keeps the diff minimal, runs the supplied",
7739
+ "test + typecheck commands, and emits a unified-diff patch.",
7740
+ "",
7741
+ "Returns immediately with a taskId. Poll delegation_status to retrieve the",
7742
+ "patch + validator verdict (typically minutes-to-hours, longer for large",
7743
+ "changes). Identical inputs return the same taskId \u2014 safe to retry.",
7744
+ "",
7745
+ "When variants > 1, multiple coder harnesses (claude-code, codex, opencode)",
7746
+ "attempt the task in parallel and the highest-scoring patch wins (smallest",
7747
+ "passing diff). Use variants for high-stakes changes; single variant for",
7748
+ "routine ones.",
7749
+ "",
7750
+ "Capability scope: the coder cannot modify paths outside repoRoot and cannot",
7751
+ "touch paths in config.forbiddenPaths. The validator hard-fails on a",
7752
+ "forbidden-path violation, diff above config.maxDiffLines, test failure, or",
7753
+ "typecheck failure \u2014 none of those make it past the gate."
7754
+ ].join("\n");
7755
+ var DELEGATE_CODE_INPUT_SCHEMA = {
7756
+ type: "object",
7757
+ properties: {
7758
+ goal: {
7759
+ type: "string",
7760
+ description: "Natural-language description of what the coder must accomplish."
7761
+ },
7762
+ repoRoot: {
7763
+ type: "string",
7764
+ description: "Absolute path inside the sandbox where the repo lives."
7765
+ },
7766
+ contextHint: {
7767
+ type: "string",
7768
+ description: "Optional free-form context the coder sees in the prompt prelude."
7769
+ },
7770
+ variants: {
7771
+ type: "integer",
7772
+ minimum: 1,
7773
+ maximum: 8,
7774
+ description: "Number of parallel coder harnesses. Default 1."
7775
+ },
7776
+ config: {
7777
+ type: "object",
7778
+ properties: {
7779
+ testCmd: { type: "string" },
7780
+ typecheckCmd: { type: "string" },
7781
+ forbiddenPaths: { type: "array", items: { type: "string" } },
7782
+ maxDiffLines: { type: "integer", minimum: 1 }
7783
+ },
7784
+ additionalProperties: false
7785
+ },
7786
+ namespace: {
7787
+ type: "string",
7788
+ description: "Multi-tenant scope (customer-id, workspace-id)."
7789
+ }
7790
+ },
7791
+ required: ["goal", "repoRoot"],
7792
+ additionalProperties: false
7793
+ };
7794
+ var SINGLE_VARIANT_ESTIMATE_MS = 6 * 60 * 1e3;
7795
+ var FANOUT_PER_VARIANT_ESTIMATE_MS = 8 * 60 * 1e3;
7796
+ function validateDelegateCodeArgs(raw) {
7797
+ if (raw === null || typeof raw !== "object") {
7798
+ throw new TypeError("delegate_code: arguments must be an object");
7799
+ }
7800
+ const value = raw;
7801
+ const goal = value.goal;
7802
+ if (typeof goal !== "string" || goal.trim().length === 0) {
7803
+ throw new TypeError("delegate_code: `goal` must be a non-empty string");
7804
+ }
7805
+ const repoRoot = value.repoRoot;
7806
+ if (typeof repoRoot !== "string" || repoRoot.trim().length === 0) {
7807
+ throw new TypeError("delegate_code: `repoRoot` must be a non-empty string");
7808
+ }
7809
+ const args = { goal: goal.trim(), repoRoot: repoRoot.trim() };
7810
+ if (typeof value.contextHint === "string") args.contextHint = value.contextHint;
7811
+ if (value.variants !== void 0) {
7812
+ const variants = Number(value.variants);
7813
+ if (!Number.isFinite(variants) || variants < 1 || variants > 8) {
7814
+ throw new RangeError("delegate_code: `variants` must be an integer in [1, 8]");
7815
+ }
7816
+ args.variants = Math.trunc(variants);
7817
+ }
7818
+ if (value.config !== void 0) {
7819
+ args.config = validateConfig(value.config);
7820
+ }
7821
+ if (typeof value.namespace === "string") args.namespace = value.namespace;
7822
+ return args;
7823
+ }
7824
+ function validateConfig(raw) {
7825
+ if (raw === null || typeof raw !== "object") {
7826
+ throw new TypeError("delegate_code: `config` must be an object");
7827
+ }
7828
+ const value = raw;
7829
+ const out = {};
7830
+ if (value.testCmd !== void 0) {
7831
+ if (typeof value.testCmd !== "string") {
7832
+ throw new TypeError("delegate_code: `config.testCmd` must be a string");
7833
+ }
7834
+ out.testCmd = value.testCmd;
7835
+ }
7836
+ if (value.typecheckCmd !== void 0) {
7837
+ if (typeof value.typecheckCmd !== "string") {
7838
+ throw new TypeError("delegate_code: `config.typecheckCmd` must be a string");
7839
+ }
7840
+ out.typecheckCmd = value.typecheckCmd;
7841
+ }
7842
+ if (value.forbiddenPaths !== void 0) {
7843
+ if (!Array.isArray(value.forbiddenPaths)) {
7844
+ throw new TypeError("delegate_code: `config.forbiddenPaths` must be a string array");
7845
+ }
7846
+ out.forbiddenPaths = value.forbiddenPaths.map((entry, i) => {
7847
+ if (typeof entry !== "string") {
7848
+ throw new TypeError(`delegate_code: forbiddenPaths[${i}] must be a string`);
7849
+ }
7850
+ return entry;
7851
+ });
7852
+ }
7853
+ if (value.maxDiffLines !== void 0) {
7854
+ const n = Number(value.maxDiffLines);
7855
+ if (!Number.isFinite(n) || n < 1) {
7856
+ throw new RangeError("delegate_code: `config.maxDiffLines` must be a positive integer");
7857
+ }
7858
+ out.maxDiffLines = Math.trunc(n);
7859
+ }
7860
+ return out;
7861
+ }
7862
+ function createDelegateCodeHandler(options) {
7863
+ const estimateDurationMs = options.estimateDurationMs ?? defaultEstimate;
7864
+ return async (raw) => {
7865
+ const args = validateDelegateCodeArgs(raw);
7866
+ const idempotencyKey = hashIdempotencyInput({
7867
+ profile: "coder",
7868
+ goal: args.goal,
7869
+ repoRoot: args.repoRoot,
7870
+ contextHint: args.contextHint,
7871
+ variants: args.variants ?? 1,
7872
+ config: args.config,
7873
+ namespace: args.namespace
7874
+ });
7875
+ const detached = options.detachedDispatch === true && (args.variants ?? 1) <= 1;
7876
+ const submitted = options.queue.submit({
7877
+ profile: "coder",
7878
+ args,
7879
+ namespace: args.namespace,
7880
+ idempotencyKey,
7881
+ ...detached ? {
7882
+ detachedSessionRef: formatDetachedSessionRef({
7883
+ sessionId: `dlg-turn-coder-${idempotencyKey}`
7884
+ })
7885
+ } : {},
7886
+ run: async (ctx) => options.delegate(args, ctx)
7887
+ });
7888
+ return {
7889
+ taskId: submitted.taskId,
7890
+ estimatedDurationMs: estimateDurationMs(args)
7891
+ };
7892
+ };
7893
+ }
7894
+ function defaultEstimate(args) {
7895
+ const variants = Math.max(1, args.variants ?? 1);
7896
+ if (variants === 1) return SINGLE_VARIANT_ESTIMATE_MS;
7897
+ return FANOUT_PER_VARIANT_ESTIMATE_MS;
7898
+ }
7899
+
7900
+ // src/mcp/tools/delegate-feedback.ts
7901
+ var DELEGATE_FEEDBACK_TOOL_NAME = "delegate_feedback";
7902
+ var DELEGATE_FEEDBACK_DESCRIPTION = [
7903
+ "Record feedback on a delegation, artifact, or outcome. Synchronous \u2014 the",
7904
+ "event is durably stored when this call returns.",
7905
+ "",
7906
+ "Use when: you (the agent), the user, or a downstream judge has formed an",
7907
+ "opinion about a piece of work and want it persisted for calibration,",
7908
+ "pricing, or future routing. Every call is a new event \u2014 multiple ratings",
7909
+ "on the same target are expected and never deduped.",
7910
+ "",
7911
+ "`refersTo.kind`:",
7912
+ ' - "delegation": ref is a taskId returned by delegate_code/delegate_research',
7913
+ ' - "artifact": ref is a URI/path/git-sha \u2014 anything you can dereference',
7914
+ ' - "outcome": ref is a free-form description of a downstream result',
7915
+ "",
7916
+ "`by`:",
7917
+ ' - "agent": the agent itself rated the work',
7918
+ ' - "user": the human user rated it',
7919
+ ' - "downstream-judge": an automated evaluator emitted the rating',
7920
+ "",
7921
+ "When ref names a known taskId, the rating is also attached to the",
7922
+ "delegation record so delegation_history surfaces it inline."
7923
+ ].join("\n");
7924
+ var DELEGATE_FEEDBACK_INPUT_SCHEMA = {
7925
+ type: "object",
7926
+ properties: {
7927
+ refersTo: {
7928
+ type: "object",
7929
+ properties: {
7930
+ kind: { type: "string", enum: ["delegation", "artifact", "outcome"] },
7931
+ ref: { type: "string" }
7932
+ },
7933
+ required: ["kind", "ref"],
7934
+ additionalProperties: false
7935
+ },
7936
+ rating: {
7937
+ type: "object",
7938
+ properties: {
7939
+ score: { type: "number", minimum: 0, maximum: 1 },
7940
+ label: { type: "string", enum: ["good", "bad", "neutral", "mixed"] },
7941
+ notes: { type: "string" }
7942
+ },
7943
+ required: ["score", "notes"],
7944
+ additionalProperties: false
7945
+ },
7946
+ by: { type: "string", enum: ["agent", "user", "downstream-judge"] },
7947
+ capturedAt: { type: "string" },
7948
+ namespace: { type: "string" }
7949
+ },
7950
+ required: ["refersTo", "rating", "by"],
7951
+ additionalProperties: false
7952
+ };
7953
+ function validateDelegateFeedbackArgs(raw) {
7954
+ if (raw === null || typeof raw !== "object") {
7955
+ throw new TypeError("delegate_feedback: arguments must be an object");
7956
+ }
7957
+ const value = raw;
7958
+ const refersTo = validateRefersTo(value.refersTo);
7959
+ const rating = validateRating(value.rating);
7960
+ const by = value.by;
7961
+ if (by !== "agent" && by !== "user" && by !== "downstream-judge") {
7962
+ throw new TypeError(
7963
+ 'delegate_feedback: `by` must be one of "agent" | "user" | "downstream-judge"'
7964
+ );
7965
+ }
7966
+ const args = { refersTo, rating, by };
7967
+ if (value.capturedAt !== void 0) {
7968
+ if (typeof value.capturedAt !== "string" || Number.isNaN(Date.parse(value.capturedAt))) {
7969
+ throw new TypeError("delegate_feedback: `capturedAt` must be an ISO datetime");
7970
+ }
7971
+ args.capturedAt = value.capturedAt;
7972
+ }
7973
+ if (typeof value.namespace === "string") args.namespace = value.namespace;
7974
+ return args;
7975
+ }
7976
+ function validateRefersTo(raw) {
7977
+ if (raw === null || typeof raw !== "object") {
7978
+ throw new TypeError("delegate_feedback: `refersTo` must be an object");
7979
+ }
7980
+ const value = raw;
7981
+ const kind = value.kind;
7982
+ if (kind !== "delegation" && kind !== "artifact" && kind !== "outcome") {
7983
+ throw new TypeError(
7984
+ 'delegate_feedback: `refersTo.kind` must be one of "delegation" | "artifact" | "outcome"'
7985
+ );
7986
+ }
7987
+ const ref = value.ref;
7988
+ if (typeof ref !== "string" || ref.trim().length === 0) {
7989
+ throw new TypeError("delegate_feedback: `refersTo.ref` must be a non-empty string");
7990
+ }
7991
+ return { kind, ref: ref.trim() };
7992
+ }
7993
+ function validateRating(raw) {
7994
+ if (raw === null || typeof raw !== "object") {
7995
+ throw new TypeError("delegate_feedback: `rating` must be an object");
7996
+ }
7997
+ const value = raw;
7998
+ const score = Number(value.score);
7999
+ if (!Number.isFinite(score) || score < 0 || score > 1) {
8000
+ throw new RangeError("delegate_feedback: `rating.score` must be a number in [0, 1]");
8001
+ }
8002
+ const notes = value.notes;
8003
+ if (typeof notes !== "string") {
8004
+ throw new TypeError("delegate_feedback: `rating.notes` must be a string");
8005
+ }
8006
+ const rating = { score, notes };
8007
+ const label = value.label;
8008
+ if (label !== void 0) {
8009
+ if (label !== "good" && label !== "bad" && label !== "neutral" && label !== "mixed") {
8010
+ throw new TypeError(
8011
+ 'delegate_feedback: `rating.label` must be one of "good" | "bad" | "neutral" | "mixed"'
8012
+ );
8013
+ }
8014
+ rating.label = label;
8015
+ }
8016
+ return rating;
8017
+ }
8018
+ function createDelegateFeedbackHandler(options) {
8019
+ const generateId = options.generateId ?? randomFeedbackId;
8020
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
8021
+ return async (raw) => {
8022
+ const args = validateDelegateFeedbackArgs(raw);
8023
+ const id = generateId();
8024
+ const event = {
8025
+ id,
8026
+ refersTo: args.refersTo,
8027
+ rating: args.rating,
8028
+ by: args.by,
8029
+ capturedAt: args.capturedAt ?? now(),
8030
+ namespace: args.namespace
8031
+ };
8032
+ await options.store.put(event);
8033
+ if (args.refersTo.kind === "delegation") {
8034
+ options.queue.attachFeedback(args.refersTo.ref, eventToSnapshot(event));
8035
+ }
8036
+ return { recorded: true, id };
8037
+ };
8038
+ }
8039
+ function randomFeedbackId() {
8040
+ const t = Date.now().toString(36);
8041
+ const r = Math.random().toString(36).slice(2, 10);
8042
+ return `fbk-${t}-${r}`;
8043
+ }
8044
+
8045
+ // src/mcp/tools/delegate-research.ts
8046
+ var DELEGATE_RESEARCH_TOOL_NAME = "delegate_research";
8047
+ var DELEGATE_RESEARCH_DESCRIPTION = [
8048
+ "Delegate a research question to specialist researcher agents that produce",
8049
+ "source-grounded, evidence-bearing knowledge items.",
8050
+ "",
8051
+ "Use when: you need to answer a factual question with external evidence \u2014",
8052
+ "audience research, competitive intelligence, recency-bound web searches,",
8053
+ "corpus / docs lookups. The researcher emits items[] with provenance, a",
8054
+ "citations[] index, and proposedWrites[] you decide whether to persist.",
8055
+ "",
8056
+ "Returns immediately with a taskId. Poll delegation_status to retrieve the",
8057
+ "items + verdict. Identical inputs return the same taskId \u2014 safe to retry.",
8058
+ "",
8059
+ "When variants > 1, multiple researcher harnesses run in parallel and the",
8060
+ "highest-scoring valid output wins (citation density \xD7 source diversity \xD7",
8061
+ "recency match \xD7 gap coverage). Use variants when answers might disagree.",
8062
+ "",
8063
+ "Multi-tenant isolation: every item carries `namespace`. The validator",
8064
+ "hard-fails when any item is scoped outside `namespace`. Never pass another",
8065
+ "tenant's namespace."
8066
+ ].join("\n");
8067
+ var VALID_SOURCES = ["web", "corpus", "twitter", "github", "docs"];
8068
+ var DELEGATE_RESEARCH_INPUT_SCHEMA = {
8069
+ type: "object",
8070
+ properties: {
8071
+ question: {
8072
+ type: "string",
8073
+ description: "The research question to answer."
8074
+ },
8075
+ namespace: {
8076
+ type: "string",
8077
+ description: "Multi-tenant scope (customer-id, workspace-id). REQUIRED."
8078
+ },
8079
+ scope: { type: "string", description: 'Bound, e.g. "audience for cpg-founder ICP".' },
8080
+ sources: {
8081
+ type: "array",
8082
+ items: { type: "string", enum: [...VALID_SOURCES] }
8083
+ },
8084
+ variants: { type: "integer", minimum: 1, maximum: 8 },
8085
+ config: {
8086
+ type: "object",
8087
+ properties: {
8088
+ recencyWindow: {
8089
+ type: "object",
8090
+ properties: {
8091
+ since: { type: "string", description: "ISO datetime" },
8092
+ until: { type: "string", description: "ISO datetime" }
8093
+ },
8094
+ additionalProperties: false
8095
+ },
8096
+ maxItems: { type: "integer", minimum: 1 },
8097
+ minConfidence: { type: "number", minimum: 0, maximum: 1 }
8098
+ },
8099
+ additionalProperties: false
8100
+ }
8101
+ },
8102
+ required: ["question", "namespace"],
8103
+ additionalProperties: false
8104
+ };
8105
+ var SINGLE_VARIANT_ESTIMATE_MS2 = 4 * 60 * 1e3;
8106
+ var FANOUT_PER_VARIANT_ESTIMATE_MS2 = 6 * 60 * 1e3;
8107
+ function validateDelegateResearchArgs(raw) {
8108
+ if (raw === null || typeof raw !== "object") {
8109
+ throw new TypeError("delegate_research: arguments must be an object");
8110
+ }
8111
+ const value = raw;
8112
+ const question = value.question;
8113
+ if (typeof question !== "string" || question.trim().length === 0) {
8114
+ throw new TypeError("delegate_research: `question` must be a non-empty string");
8115
+ }
8116
+ const namespace = value.namespace;
8117
+ if (typeof namespace !== "string" || namespace.trim().length === 0) {
8118
+ throw new TypeError("delegate_research: `namespace` is required");
8119
+ }
8120
+ const args = { question: question.trim(), namespace: namespace.trim() };
8121
+ if (typeof value.scope === "string") args.scope = value.scope;
8122
+ if (value.sources !== void 0) {
8123
+ if (!Array.isArray(value.sources)) {
8124
+ throw new TypeError("delegate_research: `sources` must be a string array");
8125
+ }
8126
+ const sources = value.sources.map((src, i) => {
8127
+ if (typeof src !== "string" || !VALID_SOURCES.includes(src)) {
8128
+ throw new TypeError(
8129
+ `delegate_research: sources[${i}] must be one of ${VALID_SOURCES.join("|")}`
8130
+ );
8131
+ }
8132
+ return src;
8133
+ });
8134
+ args.sources = sources;
8135
+ }
8136
+ if (value.variants !== void 0) {
8137
+ const variants = Number(value.variants);
8138
+ if (!Number.isFinite(variants) || variants < 1 || variants > 8) {
8139
+ throw new RangeError("delegate_research: `variants` must be an integer in [1, 8]");
8140
+ }
8141
+ args.variants = Math.trunc(variants);
8142
+ }
8143
+ if (value.config !== void 0) {
8144
+ args.config = validateConfig2(value.config);
8145
+ }
8146
+ return args;
8147
+ }
8148
+ function validateConfig2(raw) {
8149
+ if (raw === null || typeof raw !== "object") {
8150
+ throw new TypeError("delegate_research: `config` must be an object");
8151
+ }
8152
+ const value = raw;
8153
+ const out = {};
8154
+ if (value.recencyWindow !== void 0) {
8155
+ if (value.recencyWindow === null || typeof value.recencyWindow !== "object") {
8156
+ throw new TypeError("delegate_research: `config.recencyWindow` must be an object");
8157
+ }
8158
+ const window = value.recencyWindow;
8159
+ const windowOut = {};
8160
+ if (window.since !== void 0) {
8161
+ if (typeof window.since !== "string" || Number.isNaN(Date.parse(window.since))) {
8162
+ throw new TypeError("delegate_research: `recencyWindow.since` must be an ISO datetime");
8163
+ }
8164
+ windowOut.since = window.since;
8165
+ }
8166
+ if (window.until !== void 0) {
8167
+ if (typeof window.until !== "string" || Number.isNaN(Date.parse(window.until))) {
8168
+ throw new TypeError("delegate_research: `recencyWindow.until` must be an ISO datetime");
8169
+ }
8170
+ windowOut.until = window.until;
8171
+ }
8172
+ out.recencyWindow = windowOut;
8173
+ }
8174
+ if (value.maxItems !== void 0) {
8175
+ const n = Number(value.maxItems);
8176
+ if (!Number.isFinite(n) || n < 1) {
8177
+ throw new RangeError("delegate_research: `config.maxItems` must be a positive integer");
8178
+ }
8179
+ out.maxItems = Math.trunc(n);
8180
+ }
8181
+ if (value.minConfidence !== void 0) {
8182
+ const n = Number(value.minConfidence);
8183
+ if (!Number.isFinite(n) || n < 0 || n > 1) {
8184
+ throw new RangeError("delegate_research: `config.minConfidence` must be in [0, 1]");
8185
+ }
8186
+ out.minConfidence = n;
8187
+ }
8188
+ return out;
8189
+ }
8190
+ function createDelegateResearchHandler(options) {
8191
+ const estimateDurationMs = options.estimateDurationMs ?? defaultEstimate2;
8192
+ return async (raw) => {
8193
+ const args = validateDelegateResearchArgs(raw);
8194
+ const idempotencyKey = hashIdempotencyInput({
8195
+ profile: "researcher",
8196
+ question: args.question,
8197
+ namespace: args.namespace,
8198
+ scope: args.scope,
8199
+ sources: args.sources,
8200
+ variants: args.variants ?? 1,
8201
+ config: args.config
8202
+ });
8203
+ const detached = options.detachedDispatch === true && (args.variants ?? 1) <= 1;
8204
+ const submitted = options.queue.submit({
8205
+ profile: "researcher",
8206
+ args,
8207
+ namespace: args.namespace,
8208
+ idempotencyKey,
8209
+ ...detached ? {
8210
+ detachedSessionRef: formatDetachedSessionRef({
8211
+ sessionId: `dlg-turn-research-${idempotencyKey}`
8212
+ })
8213
+ } : {},
8214
+ run: async (ctx) => options.delegate(args, ctx)
8215
+ });
8216
+ return {
8217
+ taskId: submitted.taskId,
8218
+ estimatedDurationMs: estimateDurationMs(args)
8219
+ };
8220
+ };
8221
+ }
8222
+ function defaultEstimate2(args) {
8223
+ const variants = Math.max(1, args.variants ?? 1);
8224
+ if (variants === 1) return SINGLE_VARIANT_ESTIMATE_MS2;
8225
+ return FANOUT_PER_VARIANT_ESTIMATE_MS2;
8226
+ }
8227
+
8228
+ // src/mcp/tools/delegate-ui-audit.ts
8229
+ import path from "path";
8230
+ var DELEGATE_UI_AUDIT_TOOL_NAME = "delegate_ui_audit";
8231
+ var DELEGATE_UI_AUDIT_DESCRIPTION = [
8232
+ "Delegate a UI/UX audit to a vision-driven auditor that produces self-contained",
8233
+ "GitHub-issue-ready Markdown findings \u2014 one file per finding, with embedded",
8234
+ "screenshot evidence and a suggested fix.",
8235
+ "",
8236
+ "Use when: you want a thorough pass over a running web app for consistency,",
8237
+ "hierarchy, layout, ux-flow, duplication, accessibility, responsive, states,",
8238
+ "content, interaction, or perceived-performance issues. The auditor iterates",
8239
+ "lens-by-lens so each pass finds new classes of issues; the workspace registry",
8240
+ "deduplicates across iterations.",
8241
+ "",
8242
+ "Returns immediately with a taskId. Poll delegation_status to retrieve the",
8243
+ "workspace path + indexed findings (typically minutes per audited route).",
8244
+ "Identical inputs return the same taskId \u2014 safe to retry.",
8245
+ "",
8246
+ "Output layout under workspaceDir:",
8247
+ " registry.json \u2014 finding index + capture sidecar",
8248
+ " index.md \u2014 human-readable rollup",
8249
+ " issues/NNN--<lens>--<slug>.md \u2014 one self-contained GitHub-issue",
8250
+ " screenshots/<route>--<viewport>.png \u2014 capture archive",
8251
+ "",
8252
+ "Multi-tenant isolation: every finding is scoped to `namespace` when set.",
8253
+ "Never pass another tenant's namespace."
8254
+ ].join("\n");
8255
+ var VIEWPORT_SCHEMA = {
8256
+ type: "object",
8257
+ properties: {
8258
+ width: { type: "integer", minimum: 1 },
8259
+ height: { type: "integer", minimum: 1 }
8260
+ },
8261
+ required: ["width", "height"],
8262
+ additionalProperties: false
8263
+ };
8264
+ var ROUTE_SCHEMA = {
8265
+ type: "object",
8266
+ properties: {
8267
+ name: { type: "string", description: "Stable route name (used in screenshot filenames)." },
8268
+ url: { type: "string", description: "Fully-qualified URL." },
8269
+ viewports: {
8270
+ type: "array",
8271
+ items: VIEWPORT_SCHEMA,
8272
+ description: "Viewports to capture at. Default [{1280, 800}]."
8273
+ },
8274
+ fullPage: { type: "boolean" },
8275
+ waitFor: { type: "string", description: "CSS selector to wait for before capturing." }
8276
+ },
8277
+ required: ["name", "url"],
8278
+ additionalProperties: false
8279
+ };
8280
+ var DELEGATE_UI_AUDIT_INPUT_SCHEMA = {
8281
+ type: "object",
8282
+ properties: {
8283
+ workspaceDir: { type: "string", description: "Absolute path for the audit workspace." },
8284
+ routes: { type: "array", items: ROUTE_SCHEMA, minItems: 1 },
8285
+ namespace: { type: "string", description: "Multi-tenant scope." },
8286
+ config: {
8287
+ type: "object",
8288
+ properties: {
8289
+ lenses: {
8290
+ type: "array",
8291
+ items: { type: "string", enum: [...UI_LENSES] },
8292
+ description: 'Lenses to iterate. Default: every lens except "other".'
8293
+ },
8294
+ maxIterations: { type: "integer", minimum: 1 },
8295
+ maxConcurrency: { type: "integer", minimum: 1 },
8296
+ productContext: { type: "string" }
8297
+ },
8298
+ additionalProperties: false
8299
+ }
8300
+ },
8301
+ required: ["workspaceDir", "routes"],
8302
+ additionalProperties: false
8303
+ };
8304
+ var PER_LENS_PER_ROUTE_ESTIMATE_MS = 45e3;
8305
+ function validateDelegateUiAuditArgs(raw) {
8306
+ if (raw === null || typeof raw !== "object") {
8307
+ throw new TypeError("delegate_ui_audit: arguments must be an object");
8308
+ }
8309
+ const value = raw;
8310
+ const workspaceDir = value.workspaceDir;
8311
+ if (typeof workspaceDir !== "string" || workspaceDir.trim().length === 0) {
8312
+ throw new TypeError("delegate_ui_audit: `workspaceDir` must be a non-empty string");
8313
+ }
8314
+ const trimmedWs = workspaceDir.trim();
8315
+ if (!path.isAbsolute(trimmedWs)) {
8316
+ throw new TypeError(
8317
+ `delegate_ui_audit: \`workspaceDir\` must be an absolute path (got ${JSON.stringify(workspaceDir)})`
8318
+ );
8319
+ }
8320
+ if (trimmedWs.split(path.sep).includes("..")) {
8321
+ throw new TypeError(
8322
+ `delegate_ui_audit: \`workspaceDir\` must not contain '..' segments (got ${JSON.stringify(workspaceDir)})`
8323
+ );
8324
+ }
8325
+ const routesRaw = value.routes;
8326
+ if (!Array.isArray(routesRaw) || routesRaw.length === 0) {
8327
+ throw new TypeError("delegate_ui_audit: `routes` must be a non-empty array");
8328
+ }
8329
+ const routes = routesRaw.map((r, i) => validateRoute(r, i));
8330
+ const args = { workspaceDir: workspaceDir.trim(), routes };
8331
+ if (value.namespace !== void 0) {
8332
+ if (typeof value.namespace !== "string" || value.namespace.trim().length === 0) {
8333
+ throw new TypeError("delegate_ui_audit: `namespace` must be a non-empty string when set");
8334
+ }
8335
+ args.namespace = value.namespace.trim();
8336
+ }
8337
+ if (value.config !== void 0) {
8338
+ args.config = validateConfig3(value.config);
8339
+ }
8340
+ return args;
8341
+ }
8342
+ function validateRoute(raw, index) {
8343
+ if (raw === null || typeof raw !== "object") {
8344
+ throw new TypeError(`delegate_ui_audit: routes[${index}] must be an object`);
8345
+ }
8346
+ const v = raw;
8347
+ if (typeof v.name !== "string" || v.name.trim().length === 0) {
8348
+ throw new TypeError(`delegate_ui_audit: routes[${index}].name must be a non-empty string`);
8349
+ }
8350
+ const trimmedName = v.name.trim();
8351
+ if (/[./\\]/.test(trimmedName) || trimmedName.includes("\0")) {
8352
+ throw new TypeError(
8353
+ `delegate_ui_audit: routes[${index}].name must not contain path separators, dots, or NUL (got ${JSON.stringify(v.name)})`
8354
+ );
8355
+ }
8356
+ if (typeof v.url !== "string" || v.url.trim().length === 0) {
8357
+ throw new TypeError(`delegate_ui_audit: routes[${index}].url must be a non-empty string`);
8358
+ }
8359
+ let parsedUrl;
8360
+ try {
8361
+ parsedUrl = new URL(v.url);
8362
+ } catch {
8363
+ throw new TypeError(
8364
+ `delegate_ui_audit: routes[${index}].url is not a parseable URL (got ${JSON.stringify(v.url)})`
8365
+ );
8366
+ }
8367
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
8368
+ throw new TypeError(
8369
+ `delegate_ui_audit: routes[${index}].url must use http or https (got ${parsedUrl.protocol})`
8370
+ );
8371
+ }
8372
+ const out = { name: v.name.trim(), url: v.url.trim() };
8373
+ if (v.viewports !== void 0) {
8374
+ if (!Array.isArray(v.viewports) || v.viewports.length === 0) {
8375
+ throw new TypeError(
8376
+ `delegate_ui_audit: routes[${index}].viewports must be a non-empty array when set`
8377
+ );
8378
+ }
8379
+ out.viewports = v.viewports.map((vp, j) => validateViewport(vp, index, j));
8380
+ }
8381
+ if (v.fullPage !== void 0) {
8382
+ if (typeof v.fullPage !== "boolean") {
8383
+ throw new TypeError(`delegate_ui_audit: routes[${index}].fullPage must be a boolean`);
8384
+ }
8385
+ out.fullPage = v.fullPage;
8386
+ }
8387
+ if (v.waitFor !== void 0) {
8388
+ if (typeof v.waitFor !== "string" || v.waitFor.trim().length === 0) {
8389
+ throw new TypeError(
8390
+ `delegate_ui_audit: routes[${index}].waitFor must be a non-empty string when set`
8391
+ );
8392
+ }
8393
+ out.waitFor = v.waitFor.trim();
8394
+ }
8395
+ return out;
8396
+ }
8397
+ function validateViewport(raw, routeIndex, viewportIndex) {
8398
+ if (raw === null || typeof raw !== "object") {
8399
+ throw new TypeError(
8400
+ `delegate_ui_audit: routes[${routeIndex}].viewports[${viewportIndex}] must be an object`
8401
+ );
8402
+ }
8403
+ const v = raw;
8404
+ const w = Number(v.width);
8405
+ const h = Number(v.height);
8406
+ if (!Number.isInteger(w) || w <= 0 || !Number.isInteger(h) || h <= 0) {
8407
+ throw new RangeError(
8408
+ `delegate_ui_audit: routes[${routeIndex}].viewports[${viewportIndex}] must have positive integer width/height`
8409
+ );
8410
+ }
8411
+ return { width: w, height: h };
8412
+ }
8413
+ function validateConfig3(raw) {
8414
+ if (raw === null || typeof raw !== "object") {
8415
+ throw new TypeError("delegate_ui_audit: `config` must be an object");
8416
+ }
8417
+ const v = raw;
8418
+ const out = {};
8419
+ if (v.lenses !== void 0) {
8420
+ if (!Array.isArray(v.lenses) || v.lenses.length === 0) {
8421
+ throw new TypeError("delegate_ui_audit: `config.lenses` must be a non-empty array when set");
8422
+ }
8423
+ const knownSet = new Set(UI_LENSES);
8424
+ const lenses = [];
8425
+ for (let i = 0; i < v.lenses.length; i += 1) {
8426
+ const lens = v.lenses[i];
8427
+ if (typeof lens !== "string" || !knownSet.has(lens)) {
8428
+ throw new TypeError(
8429
+ `delegate_ui_audit: config.lenses[${i}] must be one of ${UI_LENSES.join("|")}`
8430
+ );
8431
+ }
8432
+ lenses.push(lens);
8433
+ }
8434
+ out.lenses = lenses;
8435
+ }
8436
+ if (v.maxIterations !== void 0) {
8437
+ const n = Number(v.maxIterations);
8438
+ if (!Number.isInteger(n) || n < 1) {
8439
+ throw new RangeError("delegate_ui_audit: `config.maxIterations` must be a positive integer");
8440
+ }
8441
+ out.maxIterations = n;
8442
+ }
8443
+ if (v.maxConcurrency !== void 0) {
8444
+ const n = Number(v.maxConcurrency);
8445
+ if (!Number.isInteger(n) || n < 1) {
8446
+ throw new RangeError("delegate_ui_audit: `config.maxConcurrency` must be a positive integer");
8447
+ }
8448
+ out.maxConcurrency = n;
8449
+ }
8450
+ if (v.productContext !== void 0) {
8451
+ if (typeof v.productContext !== "string") {
8452
+ throw new TypeError("delegate_ui_audit: `config.productContext` must be a string");
8453
+ }
8454
+ out.productContext = v.productContext;
8455
+ }
8456
+ return out;
8457
+ }
8458
+ function createDelegateUiAuditHandler(options) {
8459
+ const estimateDurationMs = options.estimateDurationMs ?? defaultEstimate3;
8460
+ return async (raw) => {
8461
+ const args = validateDelegateUiAuditArgs(raw);
8462
+ const idempotencyKey = hashIdempotencyInput({
8463
+ profile: "ui-auditor",
8464
+ workspaceDir: args.workspaceDir,
8465
+ routes: args.routes,
8466
+ namespace: args.namespace,
8467
+ config: args.config
8468
+ });
8469
+ const submitted = options.queue.submit({
8470
+ profile: "ui-auditor",
8471
+ args,
8472
+ namespace: args.namespace,
8473
+ idempotencyKey,
8474
+ run: async (ctx) => options.delegate(args, ctx)
8475
+ });
8476
+ return {
8477
+ taskId: submitted.taskId,
8478
+ estimatedDurationMs: estimateDurationMs(args)
8479
+ };
8480
+ };
8481
+ }
8482
+ function defaultEstimate3(args) {
8483
+ const lenses = args.config?.lenses?.length ?? UI_LENSES.length - 1;
8484
+ const routes = args.routes.length;
8485
+ return PER_LENS_PER_ROUTE_ESTIMATE_MS * lenses * routes;
8486
+ }
8487
+
8488
+ // src/mcp/tools/delegation-history.ts
8489
+ var DELEGATION_HISTORY_TOOL_NAME = "delegation_history";
8490
+ var DELEGATION_HISTORY_DESCRIPTION = [
8491
+ "Read past delegations newest-first. Each entry carries the original",
8492
+ "arguments, current status, cost, and any feedback attached via",
8493
+ "delegate_feedback.",
8494
+ "",
8495
+ 'Use when: you want to introspect prior decisions \u2014 "have I asked this',
8496
+ "question before?",
8497
+ "did the last patch land?",
8498
+ "what's the historical",
8499
+ 'success rate of coder delegations on this repo?". Feed the results back',
8500
+ "into your own routing and calibration.",
8501
+ "",
8502
+ "Each entry carries `hasTrace` \u2014 when true, the full loop-trace span tree",
8503
+ "is retrievable via delegation_status { taskId, includeTrace: true }.",
8504
+ "",
8505
+ 'Filters: `namespace` (multi-tenant scope), `profile` ("coder" | "researcher"),',
8506
+ "`since` (ISO date \u2014 only delegations started at-or-after). `limit` defaults",
8507
+ "to 50, capped at 500."
8508
+ ].join("\n");
8509
+ var DELEGATION_HISTORY_INPUT_SCHEMA = {
8510
+ type: "object",
8511
+ properties: {
8512
+ namespace: { type: "string" },
8513
+ profile: { type: "string", enum: ["coder", "researcher"] },
8514
+ since: { type: "string", description: "ISO datetime \u2014 earliest startedAt to include." },
8515
+ limit: { type: "integer", minimum: 1, maximum: 500 }
8516
+ },
8517
+ additionalProperties: false
8518
+ };
8519
+ function validateDelegationHistoryArgs(raw) {
8520
+ if (raw === void 0 || raw === null) return {};
8521
+ if (typeof raw !== "object") {
8522
+ throw new TypeError("delegation_history: arguments must be an object");
8523
+ }
8524
+ const value = raw;
8525
+ const out = {};
8526
+ if (value.namespace !== void 0) {
8527
+ if (typeof value.namespace !== "string") {
8528
+ throw new TypeError("delegation_history: `namespace` must be a string");
8529
+ }
8530
+ out.namespace = value.namespace;
8531
+ }
8532
+ if (value.profile !== void 0) {
8533
+ if (value.profile !== "coder" && value.profile !== "researcher") {
8534
+ throw new TypeError('delegation_history: `profile` must be "coder" or "researcher"');
8535
+ }
8536
+ out.profile = value.profile;
8537
+ }
8538
+ if (value.since !== void 0) {
8539
+ if (typeof value.since !== "string" || Number.isNaN(Date.parse(value.since))) {
8540
+ throw new TypeError("delegation_history: `since` must be an ISO datetime");
8541
+ }
8542
+ out.since = value.since;
8543
+ }
8544
+ if (value.limit !== void 0) {
8545
+ const n = Number(value.limit);
8546
+ if (!Number.isFinite(n) || n < 1 || n > 500) {
8547
+ throw new RangeError("delegation_history: `limit` must be an integer in [1, 500]");
8548
+ }
8549
+ out.limit = Math.trunc(n);
8550
+ }
8551
+ return out;
8552
+ }
8553
+ function createDelegationHistoryHandler(options) {
8554
+ return async (raw) => {
8555
+ const args = validateDelegationHistoryArgs(raw);
8556
+ return { delegations: options.queue.history(args) };
8557
+ };
8558
+ }
8559
+
8560
+ // src/mcp/tools/delegation-status.ts
8561
+ var DELEGATION_STATUS_TOOL_NAME = "delegation_status";
8562
+ var DELEGATION_STATUS_DESCRIPTION = [
8563
+ "Poll the status of an async delegation. Returns the current state",
8564
+ "(pending | running | completed | failed | cancelled), optional progress,",
8565
+ 'and the final result when status === "completed".',
8566
+ "",
8567
+ "Use when: you previously called delegate_code or delegate_research and",
8568
+ "need to know whether the work is done. The agent's right rhythm is to",
8569
+ "call this every minute or two while waiting; do not busy-poll.",
8570
+ "",
8571
+ "For a completed coder task, `result.output` is a CoderOutput with branch,",
8572
+ "patch, test/typecheck results, and diff stats. For a completed research",
8573
+ "task, `result.output` is the items + citations + proposedWrites bundle.",
8574
+ "",
8575
+ "Pass includeTrace: true to also receive the journaled loop-trace span",
8576
+ "tree (loop \u2192 round \u2192 iteration, with placement/cost/verdict metadata).",
8577
+ "Default false \u2014 keep routine polls light.",
8578
+ "",
8579
+ "Throws NotFoundError when taskId is unknown \u2014 never silently returns",
8580
+ "`pending` for a typo."
8581
+ ].join("\n");
8582
+ var DELEGATION_STATUS_INPUT_SCHEMA = {
8583
+ type: "object",
8584
+ properties: {
8585
+ taskId: { type: "string", description: "Returned by delegate_code / delegate_research." },
8586
+ includeTrace: {
8587
+ type: "boolean",
8588
+ description: "Also return the journaled loop-trace span tree for this delegation. Default false."
8589
+ }
8590
+ },
8591
+ required: ["taskId"],
8592
+ additionalProperties: false
8593
+ };
8594
+ function validateDelegationStatusArgs(raw) {
8595
+ if (raw === null || typeof raw !== "object") {
8596
+ throw new TypeError("delegation_status: arguments must be an object");
8597
+ }
8598
+ const value = raw;
8599
+ const taskId = value.taskId;
8600
+ if (typeof taskId !== "string" || taskId.trim().length === 0) {
8601
+ throw new TypeError("delegation_status: `taskId` must be a non-empty string");
8602
+ }
8603
+ const out = { taskId: taskId.trim() };
8604
+ if (value.includeTrace !== void 0) {
8605
+ if (typeof value.includeTrace !== "boolean") {
8606
+ throw new TypeError("delegation_status: `includeTrace` must be a boolean");
8607
+ }
8608
+ out.includeTrace = value.includeTrace;
8609
+ }
8610
+ return out;
8611
+ }
8612
+ function createDelegationStatusHandler(options) {
8613
+ return async (raw) => {
8614
+ const args = validateDelegationStatusArgs(raw);
8615
+ const status = options.queue.status(
8616
+ args.taskId,
8617
+ args.includeTrace !== void 0 ? { includeTrace: args.includeTrace } : void 0
8618
+ );
8619
+ if (!status) {
8620
+ throw new NotFoundError(`delegation_status: unknown taskId "${args.taskId}"`);
8621
+ }
8622
+ return status;
8623
+ };
8624
+ }
8625
+
8626
+ // src/mcp/server.ts
8627
+ var PROTOCOL_VERSION = "2024-11-05";
8628
+ var DEFAULT_SERVER_NAME = "agent-runtime-mcp";
8629
+ var DEFAULT_SERVER_VERSION = "0.22.0";
8630
+ function createMcpServer(options = {}) {
8631
+ const queue = options.queue ?? new DelegationTaskQueue(
8632
+ options.traceContext !== void 0 ? { traceContext: options.traceContext } : {}
8633
+ );
8634
+ const feedbackStore = options.feedbackStore ?? new InMemoryFeedbackStore();
8635
+ const serverName = options.serverName ?? DEFAULT_SERVER_NAME;
8636
+ const serverVersion = options.serverVersion ?? DEFAULT_SERVER_VERSION;
8637
+ const tools = /* @__PURE__ */ new Map();
8638
+ if (options.coderDelegate) {
8639
+ tools.set(DELEGATE_CODE_TOOL_NAME, {
8640
+ name: DELEGATE_CODE_TOOL_NAME,
8641
+ description: DELEGATE_CODE_DESCRIPTION,
8642
+ inputSchema: DELEGATE_CODE_INPUT_SCHEMA,
8643
+ handler: createDelegateCodeHandler({
8644
+ queue,
8645
+ delegate: options.coderDelegate,
8646
+ ...options.detachedDispatch !== void 0 ? { detachedDispatch: options.detachedDispatch } : {}
8647
+ })
8648
+ });
8649
+ }
8650
+ if (options.researcherDelegate) {
8651
+ tools.set(DELEGATE_RESEARCH_TOOL_NAME, {
8652
+ name: DELEGATE_RESEARCH_TOOL_NAME,
8653
+ description: DELEGATE_RESEARCH_DESCRIPTION,
8654
+ inputSchema: DELEGATE_RESEARCH_INPUT_SCHEMA,
8655
+ handler: createDelegateResearchHandler({
8656
+ queue,
8657
+ delegate: options.researcherDelegate,
8658
+ ...options.detachedDispatch !== void 0 ? { detachedDispatch: options.detachedDispatch } : {}
8659
+ })
8660
+ });
8661
+ }
8662
+ if (options.uiAuditorDelegate) {
8663
+ tools.set(DELEGATE_UI_AUDIT_TOOL_NAME, {
8664
+ name: DELEGATE_UI_AUDIT_TOOL_NAME,
8665
+ description: DELEGATE_UI_AUDIT_DESCRIPTION,
8666
+ inputSchema: DELEGATE_UI_AUDIT_INPUT_SCHEMA,
8667
+ handler: createDelegateUiAuditHandler({ queue, delegate: options.uiAuditorDelegate })
8668
+ });
8669
+ }
8670
+ tools.set(DELEGATE_FEEDBACK_TOOL_NAME, {
8671
+ name: DELEGATE_FEEDBACK_TOOL_NAME,
8672
+ description: DELEGATE_FEEDBACK_DESCRIPTION,
8673
+ inputSchema: DELEGATE_FEEDBACK_INPUT_SCHEMA,
8674
+ handler: createDelegateFeedbackHandler({ queue, store: feedbackStore })
8675
+ });
8676
+ tools.set(DELEGATION_STATUS_TOOL_NAME, {
8677
+ name: DELEGATION_STATUS_TOOL_NAME,
8678
+ description: DELEGATION_STATUS_DESCRIPTION,
8679
+ inputSchema: DELEGATION_STATUS_INPUT_SCHEMA,
8680
+ handler: createDelegationStatusHandler({ queue })
8681
+ });
8682
+ tools.set(DELEGATION_HISTORY_TOOL_NAME, {
8683
+ name: DELEGATION_HISTORY_TOOL_NAME,
8684
+ description: DELEGATION_HISTORY_DESCRIPTION,
8685
+ inputSchema: DELEGATION_HISTORY_INPUT_SCHEMA,
8686
+ handler: createDelegationHistoryHandler({ queue })
8687
+ });
8688
+ for (const tool of options.extraTools ?? []) {
8689
+ if (tools.has(tool.name)) {
8690
+ throw new ValidationError(
8691
+ `createMcpServer: extra tool "${tool.name}" shadows a built-in tool`
8692
+ );
8693
+ }
8694
+ tools.set(tool.name, tool);
8695
+ }
8696
+ let stopped = false;
8697
+ let activeReadline;
8698
+ async function handle(message) {
8699
+ if (stopped) {
8700
+ return rpcError(message.id ?? null, -32099, "server stopped");
8701
+ }
8702
+ if (message.method === "initialize") {
8703
+ return rpcResult(message.id ?? null, {
8704
+ protocolVersion: PROTOCOL_VERSION,
8705
+ capabilities: { tools: {} },
8706
+ serverInfo: { name: serverName, version: serverVersion }
8707
+ });
8708
+ }
8709
+ if (message.method === "notifications/initialized") {
8710
+ return null;
8711
+ }
8712
+ if (message.method === "tools/list") {
8713
+ return rpcResult(message.id ?? null, {
8714
+ tools: [...tools.values()].map((tool) => ({
8715
+ name: tool.name,
8716
+ description: tool.description,
8717
+ inputSchema: tool.inputSchema
8718
+ }))
8719
+ });
8720
+ }
8721
+ if (message.method === "tools/call") {
8722
+ const params = message.params ?? {};
8723
+ const name = typeof params.name === "string" ? params.name : "";
8724
+ const tool = tools.get(name);
8725
+ if (!tool) {
8726
+ return rpcError(message.id ?? null, -32601, `unknown tool: ${name}`);
8727
+ }
8728
+ try {
8729
+ const output = await tool.handler(params.arguments ?? {});
8730
+ return rpcResult(message.id ?? null, {
8731
+ content: [{ type: "text", text: JSON.stringify(output) }],
8732
+ structuredContent: output,
8733
+ isError: false
8734
+ });
8735
+ } catch (err) {
8736
+ const reason = err instanceof Error ? err.message : String(err);
8737
+ const code = err instanceof TypeError || err instanceof RangeError ? -32602 : -32e3;
8738
+ return rpcError(message.id ?? null, code, reason);
8739
+ }
8740
+ }
8741
+ if (message.id === void 0 || message.id === null) return null;
8742
+ return rpcError(message.id, -32601, `unknown method: ${message.method}`);
8743
+ }
8744
+ async function serve(transport) {
8745
+ const input = transport?.input ?? process.stdin;
8746
+ const output = transport?.output ?? process.stdout;
8747
+ const rl = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });
8748
+ activeReadline = rl;
8749
+ return new Promise((resolve, reject) => {
8750
+ rl.on("line", (line) => {
8751
+ const trimmed = line.trim();
8752
+ if (!trimmed) return;
8753
+ let parsed;
8754
+ try {
8755
+ parsed = JSON.parse(trimmed);
8756
+ } catch (err) {
8757
+ writeResponse(output, rpcError(null, -32700, `parse error: ${err.message}`));
8758
+ return;
8759
+ }
8760
+ if (!parsed || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
8761
+ writeResponse(output, rpcError(parsed?.id ?? null, -32600, "invalid request"));
8762
+ return;
8763
+ }
8764
+ void handle(parsed).then((response) => {
8765
+ if (response) writeResponse(output, response);
8766
+ });
8767
+ });
8768
+ rl.on("close", () => resolve());
8769
+ rl.on("error", (err) => reject(err));
8770
+ if (stopped) {
8771
+ rl.close();
8772
+ resolve();
8773
+ }
8774
+ });
8775
+ }
8776
+ function stop() {
8777
+ stopped = true;
8778
+ activeReadline?.close();
8779
+ activeReadline = void 0;
8780
+ }
8781
+ return {
8782
+ tools,
8783
+ queue,
8784
+ feedbackStore,
8785
+ handle,
8786
+ serve,
8787
+ stop
8788
+ };
8789
+ }
8790
+ function rpcResult(id, result) {
8791
+ return { jsonrpc: "2.0", id, result };
8792
+ }
8793
+ function rpcError(id, code, message, data) {
8794
+ return {
8795
+ jsonrpc: "2.0",
8796
+ id,
8797
+ error: data === void 0 ? { code, message } : { code, message, data }
8798
+ };
8799
+ }
8800
+ function writeResponse(output, response) {
8801
+ output.write(`${JSON.stringify(response)}
8802
+ `);
8803
+ }
8804
+ function createInProcessTransport() {
8805
+ const responses = [];
8806
+ const input = new Readable({ read() {
8807
+ } });
8808
+ const output = new Writable({
8809
+ write(chunk, _enc, cb) {
8810
+ const text = chunk.toString("utf8");
8811
+ for (const line of text.split("\n")) {
8812
+ const trimmed = line.trim();
8813
+ if (!trimmed) continue;
8814
+ try {
8815
+ responses.push(JSON.parse(trimmed));
8816
+ } catch {
8817
+ }
8818
+ }
8819
+ cb();
8820
+ }
8821
+ });
8822
+ return {
8823
+ transport: { input, output },
8824
+ clientWrite(line) {
8825
+ input.push(`${line}
8826
+ `);
8827
+ },
8828
+ clientClose() {
8829
+ input.push(null);
8830
+ },
8831
+ async readServer() {
8832
+ for (let i = 0; i < 5; i += 1) await new Promise((r) => setImmediate(r));
8833
+ return [...responses];
8834
+ }
8835
+ };
8836
+ }
8837
+
8838
+ // src/runtime/supervise/coordination-mcp.ts
8839
+ async function serveCoordinationMcp(opts) {
8840
+ const coord = createCoordinationTools({
8841
+ scope: opts.scope,
8842
+ blobs: opts.blobs,
8843
+ makeWorkerAgent: opts.makeWorkerAgent,
8844
+ perWorker: opts.perWorker
8845
+ });
8846
+ const mcp = createMcpServer({ extraTools: coord.tools, serverName: "coordination" });
8847
+ const host = opts.host ?? "127.0.0.1";
8848
+ const server = createServer((req, res) => {
8849
+ if (req.method !== "POST") {
8850
+ res.writeHead(405, { allow: "POST" });
8851
+ res.end();
8852
+ return;
8853
+ }
8854
+ let body = "";
8855
+ req.on("data", (c) => {
8856
+ body += c;
8857
+ });
8858
+ req.on("end", () => {
8859
+ void (async () => {
8860
+ try {
8861
+ const message = JSON.parse(body);
8862
+ const response = await mcp.handle(message);
8863
+ if (response === null) {
8864
+ res.writeHead(202).end();
8865
+ return;
8866
+ }
8867
+ res.writeHead(200, { "content-type": "application/json" });
8868
+ res.end(JSON.stringify(response));
8869
+ } catch (e) {
8870
+ res.writeHead(200, { "content-type": "application/json" });
8871
+ res.end(
8872
+ JSON.stringify({
8873
+ jsonrpc: "2.0",
8874
+ id: null,
8875
+ error: { code: -32700, message: e instanceof Error ? e.message : "parse error" }
8876
+ })
8877
+ );
8878
+ }
8879
+ })();
8880
+ });
8881
+ });
8882
+ const port = await new Promise((resolve, reject) => {
8883
+ server.once("error", reject);
8884
+ server.listen(opts.port ?? 0, host, () => {
8885
+ const addr = server.address();
8886
+ resolve(typeof addr === "object" && addr ? addr.port : opts.port ?? 0);
8887
+ });
8888
+ });
8889
+ return {
8890
+ url: `http://${host}:${port}/mcp`,
8891
+ port,
8892
+ settled: () => coord.settled(),
8893
+ isStopped: () => coord.isStopped(),
8894
+ close: () => new Promise((resolve) => {
8895
+ server.close(() => resolve());
8896
+ })
8897
+ };
8898
+ }
8899
+
8900
+ // src/runtime/verifier-environment.ts
8901
+ var submitTool = {
8902
+ type: "function",
8903
+ function: {
8904
+ name: "submit_answer",
8905
+ description: "Submit your answer for evaluation. You may submit more than once \u2014 the best-scoring submission counts. Submit the COMPLETE final answer, not a fragment.",
8906
+ parameters: {
8907
+ type: "object",
8908
+ properties: { answer: { type: "string", description: "The complete final answer." } },
8909
+ required: ["answer"]
8910
+ }
8911
+ }
8912
+ };
8913
+ function createVerifierEnvironment(opts) {
8914
+ if (opts.extraTools?.length && !opts.callExtra) {
8915
+ throw new Error(`${opts.name}: extraTools requires callExtra`);
8916
+ }
8917
+ const states = /* @__PURE__ */ new Map();
8918
+ let seq = 0;
8919
+ return {
8920
+ name: opts.name,
8921
+ async open(task) {
8922
+ seq += 1;
8923
+ const handle = { id: `${opts.name}-${seq}`, surface: opts.name };
8924
+ states.set(handle.id, { task, submissions: [] });
8925
+ return handle;
8926
+ },
8927
+ async tools() {
8928
+ return [submitTool, ...opts.extraTools ?? []];
8929
+ },
8930
+ async call(handle, name, args) {
8931
+ const state = states.get(handle.id);
8932
+ if (!state) return "ERROR: workspace closed";
8933
+ if (name === "submit_answer") {
8934
+ const answer = String(args.answer ?? "").trim();
8935
+ if (!answer) return "ERROR: empty answer";
8936
+ state.submissions.push(answer);
8937
+ return `submission ${state.submissions.length} recorded`;
8938
+ }
8939
+ if (opts.callExtra && opts.extraTools?.some((t) => t.function.name === name)) {
8940
+ try {
8941
+ return await opts.callExtra(state.task, name, args);
8942
+ } catch (e) {
8943
+ return `ERROR: ${e instanceof Error ? e.message : String(e)}`;
8944
+ }
8945
+ }
8946
+ return `ERROR: unknown tool ${name}`;
8947
+ },
8948
+ // Keep-best across submissions — the measured law (workers reach correct answers,
8949
+ // then revise past them; final-state scoring undersells every strategy).
8950
+ async score(task, handle) {
8951
+ const state = states.get(handle.id);
8952
+ if (!state || state.submissions.length === 0) return { passes: 0, total: 1, errored: 0 };
8953
+ let best = { passes: 0, total: 1, errored: 0 };
8954
+ const ratio = (s) => s.total > 0 ? s.passes / s.total : 0;
8955
+ for (const answer of state.submissions) {
8956
+ const s = await opts.check(task, answer);
8957
+ if (ratio(s) > ratio(best)) best = s;
8958
+ }
8959
+ return best;
8960
+ },
8961
+ async close(handle) {
8962
+ states.delete(handle.id);
8963
+ }
8964
+ };
8965
+ }
8966
+
8967
+ // src/runtime/waterfall.ts
8968
+ function createWaterfallCollector() {
8969
+ let spans = /* @__PURE__ */ new Map();
8970
+ const onEvent = (event) => {
8971
+ if (event.target === "agent.spawn") {
8972
+ const p = event.payload ?? {};
8973
+ const id = p.childId ?? event.id;
8974
+ spans.set(id, {
8975
+ id,
8976
+ label: p.label ?? id,
8977
+ runId: event.runId,
8978
+ ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
8979
+ startMs: event.timestamp,
8980
+ status: "running",
8981
+ usd: 0,
8982
+ tokens: { input: 0, output: 0 }
8983
+ });
8984
+ return;
8985
+ }
8986
+ if (event.target === "agent.child") {
8987
+ const p = event.payload ?? {};
8988
+ const id = p.childId;
8989
+ if (!id) return;
8990
+ const span = spans.get(id);
8991
+ if (!span) return;
8992
+ span.endMs = event.timestamp;
8993
+ span.status = p.status === "down" ? "down" : "done";
8994
+ span.usd = p.spent?.usd ?? 0;
8995
+ span.tokens = {
8996
+ input: p.spent?.tokens?.input ?? 0,
8997
+ output: p.spent?.tokens?.output ?? 0
8998
+ };
8999
+ if (typeof p.score === "number") span.score = p.score;
9000
+ }
9001
+ };
9002
+ const report = () => {
9003
+ const all = [...spans.values()].sort((a, b) => a.startMs - b.startMs);
9004
+ const start = all[0]?.startMs ?? 0;
9005
+ const end = Math.max(start, ...all.map((s) => s.endMs ?? s.startMs));
9006
+ const byKind = {};
9007
+ let totalUsd = 0;
9008
+ const totalTokens2 = { input: 0, output: 0 };
9009
+ for (const s of all) {
9010
+ totalUsd += s.usd;
9011
+ totalTokens2.input += s.tokens.input;
9012
+ totalTokens2.output += s.tokens.output;
9013
+ const kind = s.label.includes(":") ? s.label.split(":")[0] : s.label;
9014
+ let k = byKind[kind];
9015
+ if (!k) {
9016
+ k = { count: 0, ms: 0, usd: 0, tokens: { input: 0, output: 0 } };
9017
+ byKind[kind] = k;
9018
+ }
9019
+ k.count += 1;
9020
+ k.ms += (s.endMs ?? s.startMs) - s.startMs;
9021
+ k.usd += s.usd;
9022
+ k.tokens.input += s.tokens.input;
9023
+ k.tokens.output += s.tokens.output;
9024
+ }
9025
+ return { spans: all, totalMs: end - start, totalUsd, totalTokens: totalTokens2, byKind };
9026
+ };
9027
+ const render = (opts) => {
9028
+ const { spans: all, totalMs, totalUsd, byKind } = report();
9029
+ if (all.length === 0) return "(no spans observed)";
9030
+ const width = opts?.width ?? 48;
9031
+ const maxRows = opts?.maxRows ?? 60;
9032
+ const start = all[0]?.startMs ?? 0;
6318
9033
  const scale = totalMs > 0 ? width / totalMs : 0;
6319
9034
  const lines = [];
6320
9035
  const labelWidth = Math.min(24, Math.max(...all.map((s) => s.label.length)) + 1);
@@ -6447,15 +9162,29 @@ function jjWorkspace(opts) {
6447
9162
  }
6448
9163
  };
6449
9164
  }
9165
+ async function runInWorkspace(ws, body, opts = {}) {
9166
+ const { mkdtempSync, rmSync } = await import("fs");
9167
+ const { tmpdir } = await import("os");
9168
+ const { join: join2 } = await import("path");
9169
+ const dir = mkdtempSync(join2(tmpdir(), opts.tmpPrefix ?? "ws-run-"));
9170
+ try {
9171
+ await ws.materialize(dir);
9172
+ const r = await body(dir);
9173
+ if (r.valid || opts.commitOnInvalid) {
9174
+ const message = r.message ?? (r.valid ? "worker: delivered" : "worker: wip");
9175
+ const commit = await ws.commit(dir, message);
9176
+ return { valid: r.valid, value: r.value, commit };
9177
+ }
9178
+ return { valid: r.valid, value: r.value };
9179
+ } finally {
9180
+ rmSync(dir, { recursive: true, force: true });
9181
+ }
9182
+ }
6450
9183
  function tail(s) {
6451
9184
  return s.slice(-400);
6452
9185
  }
6453
9186
 
6454
9187
  export {
6455
- deleteBoxSafe,
6456
- throwAbort,
6457
- throwIfAborted,
6458
- sleep,
6459
9188
  contentAddress,
6460
9189
  InMemoryResultBlobStore,
6461
9190
  FileResultBlobStore,
@@ -6471,12 +9200,6 @@ export {
6471
9200
  stopSentinel,
6472
9201
  sentinelCompletion,
6473
9202
  deterministicCompletion,
6474
- assertTraceDerivedFindings,
6475
- createScopeAnalyst,
6476
- registryScopeAnalyst,
6477
- buildSteerContext,
6478
- createDriver,
6479
- renderAnalyses,
6480
9203
  defaultAnalystInstruction,
6481
9204
  observe,
6482
9205
  renderReport,
@@ -6496,6 +9219,11 @@ export {
6496
9219
  defaultSelectWinner,
6497
9220
  loopDispatch,
6498
9221
  createMcpEnvironment,
9222
+ assertTraceDerivedFindings,
9223
+ createScopeAnalyst,
9224
+ registryScopeAnalyst,
9225
+ buildSteerContext,
9226
+ nestedScopeSeamKey,
6499
9227
  createScope,
6500
9228
  settledToIteration,
6501
9229
  pipeline,
@@ -6508,6 +9236,11 @@ export {
6508
9236
  InMemoryCorpus,
6509
9237
  FileCorpus,
6510
9238
  renderCorpusToInstructions,
9239
+ driverRuntime,
9240
+ driverChild,
9241
+ isDriverSpec,
9242
+ driverExecutorFactory,
9243
+ withDriverExecutor,
6511
9244
  routerChatWithUsage,
6512
9245
  routerChatWithTools,
6513
9246
  routerToolLoop,
@@ -6543,10 +9276,69 @@ export {
6543
9276
  pickChampion,
6544
9277
  selectChampion,
6545
9278
  runStrategyEvolution,
9279
+ gateOnDeliverable,
9280
+ asAuthoredProfile,
9281
+ supervisorSkill,
9282
+ authoredWorker,
9283
+ createCoordinationTools,
9284
+ coordinationDriverAgent,
9285
+ InMemoryFeedbackStore,
9286
+ eventToSnapshot,
9287
+ DelegationStateCorruptError,
9288
+ DelegationPersistenceError,
9289
+ InMemoryDelegationStore,
9290
+ FileDelegationStore,
9291
+ DELEGATION_TRACE_MAX_SPANS,
9292
+ DELEGATION_TRACE_MAX_BYTES,
9293
+ buildDelegationTraceSpans,
9294
+ capDelegationTrace,
9295
+ createDelegationTraceCollector,
9296
+ composeLoopTraceEmitters,
9297
+ DelegationTaskQueue,
9298
+ hashIdempotencyInput,
9299
+ formatDetachedSessionRef,
9300
+ parseDetachedSessionRef,
9301
+ detachedTurnEvents,
9302
+ runDetachedTurn,
9303
+ createDriveTurnResumeDriver,
9304
+ DELEGATE_CODE_TOOL_NAME,
9305
+ DELEGATE_CODE_DESCRIPTION,
9306
+ DELEGATE_CODE_INPUT_SCHEMA,
9307
+ validateDelegateCodeArgs,
9308
+ createDelegateCodeHandler,
9309
+ DELEGATE_FEEDBACK_TOOL_NAME,
9310
+ DELEGATE_FEEDBACK_DESCRIPTION,
9311
+ DELEGATE_FEEDBACK_INPUT_SCHEMA,
9312
+ validateDelegateFeedbackArgs,
9313
+ createDelegateFeedbackHandler,
9314
+ DELEGATE_RESEARCH_TOOL_NAME,
9315
+ DELEGATE_RESEARCH_DESCRIPTION,
9316
+ DELEGATE_RESEARCH_INPUT_SCHEMA,
9317
+ validateDelegateResearchArgs,
9318
+ createDelegateResearchHandler,
9319
+ DELEGATE_UI_AUDIT_TOOL_NAME,
9320
+ DELEGATE_UI_AUDIT_DESCRIPTION,
9321
+ DELEGATE_UI_AUDIT_INPUT_SCHEMA,
9322
+ validateDelegateUiAuditArgs,
9323
+ createDelegateUiAuditHandler,
9324
+ DELEGATION_HISTORY_TOOL_NAME,
9325
+ DELEGATION_HISTORY_DESCRIPTION,
9326
+ DELEGATION_HISTORY_INPUT_SCHEMA,
9327
+ validateDelegationHistoryArgs,
9328
+ createDelegationHistoryHandler,
9329
+ DELEGATION_STATUS_TOOL_NAME,
9330
+ DELEGATION_STATUS_DESCRIPTION,
9331
+ DELEGATION_STATUS_INPUT_SCHEMA,
9332
+ validateDelegationStatusArgs,
9333
+ createDelegationStatusHandler,
9334
+ createMcpServer,
9335
+ createInProcessTransport,
9336
+ serveCoordinationMcp,
6546
9337
  createVerifierEnvironment,
6547
9338
  createWaterfallCollector,
6548
9339
  localShell,
6549
9340
  gitWorkspace,
6550
- jjWorkspace
9341
+ jjWorkspace,
9342
+ runInWorkspace
6551
9343
  };
6552
- //# sourceMappingURL=chunk-2OU7ZQPD.js.map
9344
+ //# sourceMappingURL=chunk-JTH2FPCK.js.map