@tangle-network/agent-runtime 0.83.0 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +2 -2
- package/dist/{chunk-QDVLTRCP.js → chunk-DLAEEF26.js} +1330 -230
- package/dist/chunk-DLAEEF26.js.map +1 -0
- package/dist/{chunk-J3N6GODQ.js → chunk-G7HQI6DB.js} +2 -2
- package/dist/{chunk-UJ3ONGHR.js → chunk-QSYPMMWS.js} +3 -3
- package/dist/{chunk-SSA6OERV.js → chunk-XN5TIJGP.js} +1006 -1006
- package/dist/{chunk-SSA6OERV.js.map → chunk-XN5TIJGP.js.map} +1 -1
- package/dist/index.js +16 -625
- package/dist/index.js.map +1 -1
- package/dist/loop-runner-bin.js +3 -3
- package/dist/loops.d.ts +286 -4
- package/dist/loops.js +8 -2
- package/dist/mcp/bin.js +1 -1
- package/dist/mcp/index.js +3 -3
- package/package.json +3 -3
- package/dist/chunk-QDVLTRCP.js.map +0 -1
- /package/dist/{chunk-J3N6GODQ.js.map → chunk-G7HQI6DB.js.map} +0 -0
- /package/dist/{chunk-UJ3ONGHR.js.map → chunk-QSYPMMWS.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
settledToIteration,
|
|
16
16
|
supervise,
|
|
17
17
|
withDriverExecutor
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-XN5TIJGP.js";
|
|
19
19
|
import {
|
|
20
20
|
addTokenUsage,
|
|
21
21
|
isAbortError,
|
|
@@ -24,8 +24,12 @@ import {
|
|
|
24
24
|
stringifySafe,
|
|
25
25
|
zeroTokenUsage
|
|
26
26
|
} from "./chunk-BZF3KQ6G.js";
|
|
27
|
+
import {
|
|
28
|
+
mapSandboxEvent
|
|
29
|
+
} from "./chunk-4J6RBI3K.js";
|
|
27
30
|
import {
|
|
28
31
|
AnalystError,
|
|
32
|
+
BackendTransportError,
|
|
29
33
|
PlannerError,
|
|
30
34
|
ValidationError
|
|
31
35
|
} from "./chunk-YEJR7IXO.js";
|
|
@@ -604,6 +608,487 @@ function deterministicCompletion(check) {
|
|
|
604
608
|
};
|
|
605
609
|
}
|
|
606
610
|
|
|
611
|
+
// src/runtime/define-leaderboard.ts
|
|
612
|
+
import { execFileSync } from "child_process";
|
|
613
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
614
|
+
import { tmpdir } from "os";
|
|
615
|
+
import { join } from "path";
|
|
616
|
+
import {
|
|
617
|
+
CODING_HARNESSES,
|
|
618
|
+
expandProfileAxes,
|
|
619
|
+
harnessAxisOf
|
|
620
|
+
} from "@tangle-network/agent-eval";
|
|
621
|
+
import {
|
|
622
|
+
runProfileMatrix
|
|
623
|
+
} from "@tangle-network/agent-eval/campaign";
|
|
624
|
+
import { collectAgentResponseText } from "@tangle-network/sandbox";
|
|
625
|
+
|
|
626
|
+
// src/runtime/report-usage.ts
|
|
627
|
+
function reportLoopUsage(cost, result, source = "loop") {
|
|
628
|
+
cost.observe(result.costUsd, source);
|
|
629
|
+
cost.observeTokens(result.tokenUsage);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/runtime/loop-dispatch.ts
|
|
633
|
+
function campaignTraceToLoopEmitter(trace) {
|
|
634
|
+
return {
|
|
635
|
+
emit(event) {
|
|
636
|
+
trace.span(event.kind, { runId: event.runId, timestamp: event.timestamp, ...event.payload }).end();
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
async function runLoopForCell(opts, scenario, profile, ctx) {
|
|
641
|
+
const loopOptions = opts.toLoopOptions(scenario, profile);
|
|
642
|
+
return runLoopWithCampaignContext(opts, loopOptions, ctx);
|
|
643
|
+
}
|
|
644
|
+
async function runLoopWithCampaignContext(opts, loopOptions, ctx) {
|
|
645
|
+
const result = await runLoop({
|
|
646
|
+
...loopOptions,
|
|
647
|
+
ctx: {
|
|
648
|
+
sandboxClient: opts.sandboxClient,
|
|
649
|
+
signal: ctx.signal,
|
|
650
|
+
traceEmitter: opts.forwardTrace === false ? void 0 : campaignTraceToLoopEmitter(ctx.trace)
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
reportLoopUsage(ctx.cost, result, opts.costSource ?? "loop");
|
|
654
|
+
const toArtifact = opts.toArtifact ?? ((r) => r.winner?.output);
|
|
655
|
+
return toArtifact(result);
|
|
656
|
+
}
|
|
657
|
+
function loopCampaignDispatch(opts) {
|
|
658
|
+
return (scenario, ctx) => runLoopWithCampaignContext(opts, opts.toLoopOptions(scenario), ctx);
|
|
659
|
+
}
|
|
660
|
+
function loopDispatch(opts) {
|
|
661
|
+
return (profile, scenario, ctx) => runLoopForCell(opts, scenario, profile, ctx);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/runtime/inline-sandbox-client.ts
|
|
665
|
+
function isAsyncIterable(v) {
|
|
666
|
+
return typeof v === "object" && v !== null && Symbol.asyncIterator in v;
|
|
667
|
+
}
|
|
668
|
+
async function settle(exec, task, signal) {
|
|
669
|
+
const r = exec.execute(task, signal);
|
|
670
|
+
if (isAsyncIterable(r)) {
|
|
671
|
+
for await (const _ of r) {
|
|
672
|
+
}
|
|
673
|
+
return exec.resultArtifact();
|
|
674
|
+
}
|
|
675
|
+
return r;
|
|
676
|
+
}
|
|
677
|
+
function inlineSandboxClient(factory) {
|
|
678
|
+
let seq = 0;
|
|
679
|
+
return {
|
|
680
|
+
async create(options) {
|
|
681
|
+
const id = `inline-${seq++}`;
|
|
682
|
+
const createOptions = options;
|
|
683
|
+
return {
|
|
684
|
+
id,
|
|
685
|
+
async *streamPrompt(message, opts) {
|
|
686
|
+
const controller = new AbortController();
|
|
687
|
+
const callerSignal = opts?.signal;
|
|
688
|
+
const onAbort = () => controller.abort(callerSignal?.reason ?? new Error("prompt aborted"));
|
|
689
|
+
if (callerSignal) {
|
|
690
|
+
if (callerSignal.aborted) onAbort();
|
|
691
|
+
else callerSignal.addEventListener("abort", onAbort, { once: true });
|
|
692
|
+
}
|
|
693
|
+
const spec = { profile: { name: id }, harness: null };
|
|
694
|
+
const exec = factory(spec, { signal: controller.signal, seams: { createOptions } });
|
|
695
|
+
try {
|
|
696
|
+
const artifact = await settle(exec, message, controller.signal);
|
|
697
|
+
const out = artifact.out;
|
|
698
|
+
const tokensIn = artifact.spent.tokens.input;
|
|
699
|
+
const tokensOut = artifact.spent.tokens.output;
|
|
700
|
+
const costUsd = artifact.spent.usd;
|
|
701
|
+
if (tokensIn || tokensOut || costUsd) {
|
|
702
|
+
yield {
|
|
703
|
+
type: "llm_call",
|
|
704
|
+
data: { tokensIn, tokensOut, costUsd }
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
yield {
|
|
708
|
+
type: "result",
|
|
709
|
+
data: {
|
|
710
|
+
finalText: out?.content ?? "",
|
|
711
|
+
tokenUsage: {
|
|
712
|
+
inputTokens: tokensIn,
|
|
713
|
+
outputTokens: tokensOut
|
|
714
|
+
},
|
|
715
|
+
costUsd
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
} finally {
|
|
719
|
+
callerSignal?.removeEventListener("abort", onAbort);
|
|
720
|
+
await exec.teardown("brutalKill").catch(() => {
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
async delete() {
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/runtime/resolve-sandbox-client.ts
|
|
732
|
+
function resolveSandboxClient(opts) {
|
|
733
|
+
switch (opts.backend) {
|
|
734
|
+
case "sandbox": {
|
|
735
|
+
if (!opts.sandboxClient) {
|
|
736
|
+
throw new Error("resolveSandboxClient: backend 'sandbox' requires opts.sandboxClient");
|
|
737
|
+
}
|
|
738
|
+
return opts.sandboxClient;
|
|
739
|
+
}
|
|
740
|
+
case "bridge": {
|
|
741
|
+
const bridge = opts.bridge;
|
|
742
|
+
if (!bridge?.bearer || !bridge.model) {
|
|
743
|
+
throw new Error(
|
|
744
|
+
"resolveSandboxClient: backend 'bridge' requires bridge.bearer and bridge.model"
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
return inlineSandboxClient(
|
|
748
|
+
createExecutor({
|
|
749
|
+
backend: "bridge",
|
|
750
|
+
bridgeUrl: bridge.url ?? "http://127.0.0.1:3355",
|
|
751
|
+
bridgeBearer: bridge.bearer,
|
|
752
|
+
model: bridge.model,
|
|
753
|
+
timeoutMs: bridge.timeoutMs
|
|
754
|
+
})
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
case "router": {
|
|
758
|
+
const router = opts.router;
|
|
759
|
+
if (!router?.baseUrl || !router.key || !router.model) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
"resolveSandboxClient: backend 'router' requires router.baseUrl, router.key and router.model"
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
return inlineSandboxClient(
|
|
765
|
+
createExecutor({
|
|
766
|
+
backend: "router",
|
|
767
|
+
routerBaseUrl: router.baseUrl,
|
|
768
|
+
routerKey: router.key,
|
|
769
|
+
model: router.model
|
|
770
|
+
})
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/runtime/steering-drivers.ts
|
|
777
|
+
function decideUntilValidOrCapped(history, maxIterations) {
|
|
778
|
+
if (history.some((it) => it.verdict?.valid)) return "pick-winner";
|
|
779
|
+
return history.length < maxIterations ? "refine" : "fail";
|
|
780
|
+
}
|
|
781
|
+
function naiveDriver(options) {
|
|
782
|
+
const { continuation, applyContinuation, maxIterations, name = "naive" } = options;
|
|
783
|
+
return {
|
|
784
|
+
name,
|
|
785
|
+
plan(task, history) {
|
|
786
|
+
if (history.length === 0) return Promise.resolve([task]);
|
|
787
|
+
const last = history[history.length - 1];
|
|
788
|
+
if (last?.verdict?.valid) return Promise.resolve([]);
|
|
789
|
+
if (history.length >= maxIterations) return Promise.resolve([]);
|
|
790
|
+
return Promise.resolve([applyContinuation(task, continuation)]);
|
|
791
|
+
},
|
|
792
|
+
decide(history) {
|
|
793
|
+
return decideUntilValidOrCapped(history, maxIterations);
|
|
794
|
+
},
|
|
795
|
+
// Pure refine topology (one task per round) — render the steer move.
|
|
796
|
+
describePlan() {
|
|
797
|
+
return { kind: "refine", rationale: "naive fixed continuation (no grade signal)" };
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function dumbDriver(options) {
|
|
802
|
+
const { onPass, onFail, applyContinuation, maxIterations, name = "dumb" } = options;
|
|
803
|
+
return {
|
|
804
|
+
name,
|
|
805
|
+
plan(task, history) {
|
|
806
|
+
if (history.length === 0) return Promise.resolve([task]);
|
|
807
|
+
const last = history[history.length - 1];
|
|
808
|
+
const passed = last?.verdict?.valid === true;
|
|
809
|
+
if (passed) return Promise.resolve([]);
|
|
810
|
+
if (history.length >= maxIterations) return Promise.resolve([]);
|
|
811
|
+
const continuation = passed ? onPass : onFail;
|
|
812
|
+
return Promise.resolve([applyContinuation(task, continuation)]);
|
|
813
|
+
},
|
|
814
|
+
decide(history) {
|
|
815
|
+
return decideUntilValidOrCapped(history, maxIterations);
|
|
816
|
+
},
|
|
817
|
+
describePlan() {
|
|
818
|
+
return { kind: "refine", rationale: "dumb pass/fail-only continuation (no grader findings)" };
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// src/runtime/define-leaderboard.ts
|
|
824
|
+
function argOf(argv, name) {
|
|
825
|
+
const i = argv.indexOf(`--${name}`);
|
|
826
|
+
if (i >= 0 && i + 1 < argv.length) return argv[i + 1];
|
|
827
|
+
return void 0;
|
|
828
|
+
}
|
|
829
|
+
function splitList(v) {
|
|
830
|
+
if (v === void 0) return void 0;
|
|
831
|
+
const parts = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
832
|
+
return parts.length > 0 ? parts : void 0;
|
|
833
|
+
}
|
|
834
|
+
function withSnapshot(model, snapshot) {
|
|
835
|
+
return model.includes("@") ? model : `${model}@${snapshot}`;
|
|
836
|
+
}
|
|
837
|
+
function bareModel(model) {
|
|
838
|
+
return model.split("@")[0] ?? model;
|
|
839
|
+
}
|
|
840
|
+
function gitSha() {
|
|
841
|
+
try {
|
|
842
|
+
return execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
843
|
+
} catch {
|
|
844
|
+
return "unknown";
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function normalizeScore(s) {
|
|
848
|
+
return typeof s === "number" ? { composite: s } : s;
|
|
849
|
+
}
|
|
850
|
+
function defineLeaderboard(spec) {
|
|
851
|
+
const caseId = (c) => {
|
|
852
|
+
const id = spec.caseId ? spec.caseId(c) : c.id;
|
|
853
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
854
|
+
throw new Error(
|
|
855
|
+
`defineLeaderboard(${spec.name}): every case needs a stable string id \u2014 give cases an \`id\` property or supply spec.caseId`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
return id;
|
|
859
|
+
};
|
|
860
|
+
const selectCases = (ids) => {
|
|
861
|
+
if (!ids) return spec.cases;
|
|
862
|
+
const byId = new Map(spec.cases.map((c) => [caseId(c), c]));
|
|
863
|
+
return ids.map((id) => {
|
|
864
|
+
const c = byId.get(id);
|
|
865
|
+
if (c === void 0) {
|
|
866
|
+
throw new Error(
|
|
867
|
+
`defineLeaderboard(${spec.name}): unknown case "${id}" (have: ${[...byId.keys()].join(", ")})`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
return c;
|
|
871
|
+
});
|
|
872
|
+
};
|
|
873
|
+
const scoreJudge = {
|
|
874
|
+
name: `${spec.name}-score`,
|
|
875
|
+
dimensions: [{ key: "composite", description: `${spec.name} case score` }],
|
|
876
|
+
score({ artifact, scenario }) {
|
|
877
|
+
const s = normalizeScore(spec.score(artifact, scenario.case));
|
|
878
|
+
return {
|
|
879
|
+
composite: s.composite,
|
|
880
|
+
dimensions: { composite: s.composite, ...s.dimensions },
|
|
881
|
+
notes: s.notes ?? ""
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
async function run(argv = process.argv.slice(2)) {
|
|
886
|
+
const args = {};
|
|
887
|
+
for (const name of [
|
|
888
|
+
"backend",
|
|
889
|
+
"harnesses",
|
|
890
|
+
"models",
|
|
891
|
+
"cases",
|
|
892
|
+
"shots",
|
|
893
|
+
"reps",
|
|
894
|
+
"model-snapshot",
|
|
895
|
+
"run-dir",
|
|
896
|
+
"export-dir"
|
|
897
|
+
]) {
|
|
898
|
+
args[name] = argOf(argv, name);
|
|
899
|
+
}
|
|
900
|
+
for (const [name, flag] of Object.entries(spec.flags ?? {})) {
|
|
901
|
+
args[name] = argOf(argv, name) ?? flag.default;
|
|
902
|
+
}
|
|
903
|
+
const backendName = args.backend ?? "sandbox";
|
|
904
|
+
const shots = Number(args.shots ?? spec.shots ?? 1);
|
|
905
|
+
const reps = Number(args.reps ?? spec.reps ?? 1);
|
|
906
|
+
const snapshot = args["model-snapshot"] ?? "leaderboard";
|
|
907
|
+
const runDir = args["run-dir"] ?? join(tmpdir(), `leaderboard-${spec.name}-${Date.now()}-${process.pid}`);
|
|
908
|
+
const exportDir = args["export-dir"] ?? join(runDir, "export");
|
|
909
|
+
mkdirSync(runDir, { recursive: true });
|
|
910
|
+
const cases = selectCases(splitList(args.cases));
|
|
911
|
+
if (cases.length === 0) throw new Error(`defineLeaderboard(${spec.name}): no cases to run`);
|
|
912
|
+
const scenarios = cases.map((c) => ({
|
|
913
|
+
id: caseId(c),
|
|
914
|
+
kind: spec.name,
|
|
915
|
+
case: c
|
|
916
|
+
}));
|
|
917
|
+
const harnesses = splitList(args.harnesses) ?? spec.axis?.harnesses ?? CODING_HARNESSES;
|
|
918
|
+
const rawModels = splitList(args.models) ?? spec.axis?.models ?? (spec.baseProfile?.model?.default !== void 0 ? [spec.baseProfile.model.default] : void 0);
|
|
919
|
+
if (!rawModels || rawModels.length === 0) {
|
|
920
|
+
throw new Error(
|
|
921
|
+
`defineLeaderboard(${spec.name}): no models \u2014 pass --models, set spec.axis.models, or give spec.baseProfile a model.default`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
const models = rawModels.map((m) => withSnapshot(m, snapshot));
|
|
925
|
+
const base = spec.baseProfile ?? { name: spec.name, model: { default: bareModel(models[0] ?? "") } };
|
|
926
|
+
const profiles = expandProfileAxes({ base, harnesses, models });
|
|
927
|
+
const ctx = {
|
|
928
|
+
name: spec.name,
|
|
929
|
+
backend: backendName,
|
|
930
|
+
runDir,
|
|
931
|
+
exportDir,
|
|
932
|
+
args,
|
|
933
|
+
harnesses,
|
|
934
|
+
models,
|
|
935
|
+
caseIds: scenarios.map((s) => s.id),
|
|
936
|
+
shots,
|
|
937
|
+
reps
|
|
938
|
+
};
|
|
939
|
+
const backends = {
|
|
940
|
+
sandbox: () => {
|
|
941
|
+
throw new Error(
|
|
942
|
+
`defineLeaderboard(${spec.name}): the 'sandbox' backend needs your product's real SandboxClient \u2014 supply spec.backends.sandbox (e.g. () => new SandboxClient({ apiKey, baseUrl }))`
|
|
943
|
+
);
|
|
944
|
+
},
|
|
945
|
+
"cli-bridge": () => {
|
|
946
|
+
const bearer = process.env.BRIDGE_BEARER ?? process.env.CLI_BRIDGE_BEARER;
|
|
947
|
+
if (!bearer) {
|
|
948
|
+
throw new Error(
|
|
949
|
+
`defineLeaderboard(${spec.name}): backend 'cli-bridge' needs BRIDGE_BEARER or CLI_BRIDGE_BEARER set`
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
return resolveSandboxClient({
|
|
953
|
+
backend: "bridge",
|
|
954
|
+
bridge: {
|
|
955
|
+
url: process.env.CLI_BRIDGE_URL,
|
|
956
|
+
bearer,
|
|
957
|
+
model: bareModel(models[0] ?? ""),
|
|
958
|
+
timeoutMs: 9e5
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
},
|
|
962
|
+
...spec.backends
|
|
963
|
+
};
|
|
964
|
+
const makeClient = backends[backendName];
|
|
965
|
+
if (!makeClient) {
|
|
966
|
+
throw new Error(
|
|
967
|
+
`defineLeaderboard(${spec.name}): unknown backend "${backendName}" (have: ${Object.keys(backends).join(", ")})`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
const sandboxClient = makeClient();
|
|
971
|
+
const promptById = /* @__PURE__ */ new Map();
|
|
972
|
+
for (const s of scenarios) promptById.set(s.id, await spec.prompt(s.case));
|
|
973
|
+
const promptOf = (s) => {
|
|
974
|
+
const p = promptById.get(s.id);
|
|
975
|
+
if (p === void 0)
|
|
976
|
+
throw new Error(`defineLeaderboard(${spec.name}): no prompt for case "${s.id}"`);
|
|
977
|
+
return p;
|
|
978
|
+
};
|
|
979
|
+
let shotNonce = 0;
|
|
980
|
+
const dispatch = spec.dispatch ?? loopDispatch({
|
|
981
|
+
sandboxClient,
|
|
982
|
+
toLoopOptions: (scenario, profile) => {
|
|
983
|
+
const axis = harnessAxisOf(profile);
|
|
984
|
+
const modelId = bareModel(axis?.model ?? models[0] ?? "");
|
|
985
|
+
return {
|
|
986
|
+
// naiveDriver = the no-signal retry floor: re-run the same case as
|
|
987
|
+
// an independent attempt until one scores (>0) or the shot cap.
|
|
988
|
+
driver: naiveDriver({
|
|
989
|
+
continuation: "",
|
|
990
|
+
applyContinuation: (task) => task,
|
|
991
|
+
maxIterations: shots
|
|
992
|
+
}),
|
|
993
|
+
agentRun: {
|
|
994
|
+
profile,
|
|
995
|
+
taskToPrompt: (s) => `${promptOf(s)}
|
|
996
|
+
|
|
997
|
+
<!-- independent-attempt:${shotNonce++} -->`,
|
|
998
|
+
...axis ? {
|
|
999
|
+
sandboxOverrides: {
|
|
1000
|
+
backend: {
|
|
1001
|
+
type: axis.harness,
|
|
1002
|
+
model: { ...spec.modelBackend, model: modelId }
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
} : {}
|
|
1006
|
+
},
|
|
1007
|
+
output: {
|
|
1008
|
+
parse: (events) => {
|
|
1009
|
+
spec.onCellEvents?.(events, scenario.case);
|
|
1010
|
+
return spec.parseOutput ? spec.parseOutput(events, scenario.case) : collectAgentResponseText(events) ?? "";
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
validator: {
|
|
1014
|
+
validate: async (output) => {
|
|
1015
|
+
const s = normalizeScore(spec.score(output, scenario.case));
|
|
1016
|
+
return { valid: s.composite > 0, score: s.composite };
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
task: scenario,
|
|
1020
|
+
maxIterations: shots
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
await spec.setup?.(ctx);
|
|
1025
|
+
try {
|
|
1026
|
+
const result = await runProfileMatrix({
|
|
1027
|
+
profiles,
|
|
1028
|
+
scenarios,
|
|
1029
|
+
dispatch,
|
|
1030
|
+
judges: spec.judges ?? [scoreJudge],
|
|
1031
|
+
runDir,
|
|
1032
|
+
commitSha: gitSha(),
|
|
1033
|
+
reps,
|
|
1034
|
+
...spec.matrix
|
|
1035
|
+
});
|
|
1036
|
+
if (spec.export) {
|
|
1037
|
+
await spec.export(result, ctx);
|
|
1038
|
+
} else {
|
|
1039
|
+
mkdirSync(exportDir, { recursive: true });
|
|
1040
|
+
writeFileSync(join(runDir, "matrix-result.json"), `${JSON.stringify(result, null, 2)}
|
|
1041
|
+
`);
|
|
1042
|
+
const table = renderLeaderboardMarkdown(
|
|
1043
|
+
leaderboard(result.records, { title: spec.name, meta: { backend: backendName } })
|
|
1044
|
+
);
|
|
1045
|
+
writeFileSync(join(exportDir, "leaderboard.md"), table);
|
|
1046
|
+
console.log(table);
|
|
1047
|
+
}
|
|
1048
|
+
return result;
|
|
1049
|
+
} finally {
|
|
1050
|
+
await spec.teardown?.(ctx);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
function toBenchmarkAdapter() {
|
|
1054
|
+
return {
|
|
1055
|
+
name: spec.name,
|
|
1056
|
+
async preflight() {
|
|
1057
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1058
|
+
for (const c of spec.cases) {
|
|
1059
|
+
const id = caseId(c);
|
|
1060
|
+
if (seen.has(id)) {
|
|
1061
|
+
throw new Error(`defineLeaderboard(${spec.name}): duplicate case id "${id}"`);
|
|
1062
|
+
}
|
|
1063
|
+
seen.add(id);
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
async loadTasks(opts) {
|
|
1067
|
+
const selected = selectCases(opts?.ids);
|
|
1068
|
+
const limited = opts?.limit !== void 0 ? selected.slice(0, opts.limit) : selected;
|
|
1069
|
+
return Promise.all(
|
|
1070
|
+
limited.map(async (c) => ({
|
|
1071
|
+
id: caseId(c),
|
|
1072
|
+
prompt: await spec.prompt(c),
|
|
1073
|
+
metadata: { case: c }
|
|
1074
|
+
}))
|
|
1075
|
+
);
|
|
1076
|
+
},
|
|
1077
|
+
async judge(task, artifact) {
|
|
1078
|
+
const [c] = selectCases([task.id]);
|
|
1079
|
+
if (c === void 0)
|
|
1080
|
+
throw new Error(`defineLeaderboard(${spec.name}): no case "${task.id}"`);
|
|
1081
|
+
const s = normalizeScore(spec.score(artifact, c));
|
|
1082
|
+
return { resolved: s.composite > 0, score: s.composite, detail: s.notes };
|
|
1083
|
+
},
|
|
1084
|
+
async goldArtifact() {
|
|
1085
|
+
return void 0;
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
return { run, toBenchmarkAdapter };
|
|
1090
|
+
}
|
|
1091
|
+
|
|
607
1092
|
// src/runtime/observe.ts
|
|
608
1093
|
import { makeFinding } from "@tangle-network/agent-eval";
|
|
609
1094
|
var observerId = "observe/trace";
|
|
@@ -815,9 +1300,9 @@ async function harvestCorpus(opts) {
|
|
|
815
1300
|
// src/runtime/in-process-sandbox-client.ts
|
|
816
1301
|
import { mkdtempSync, rmSync } from "fs";
|
|
817
1302
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
818
|
-
import { tmpdir } from "os";
|
|
819
|
-
import { dirname, join } from "path";
|
|
820
|
-
function
|
|
1303
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1304
|
+
import { dirname, join as join2 } from "path";
|
|
1305
|
+
function isAsyncIterable2(v) {
|
|
821
1306
|
return typeof v === "object" && v !== null && Symbol.asyncIterator in v;
|
|
822
1307
|
}
|
|
823
1308
|
function inProcessSandboxClient(options) {
|
|
@@ -827,15 +1312,15 @@ function inProcessSandboxClient(options) {
|
|
|
827
1312
|
async create(_options) {
|
|
828
1313
|
const current = seq++;
|
|
829
1314
|
const id = typeof idOption === "function" ? idOption(current) : idOption ?? `in-process-${current}`;
|
|
830
|
-
const boxWorkdir = workdirPrefix !== void 0 ? mkdtempSync(
|
|
1315
|
+
const boxWorkdir = workdirPrefix !== void 0 ? mkdtempSync(join2(tmpdir2(), `${workdirPrefix}-`)) : void 0;
|
|
831
1316
|
let round = 0;
|
|
832
1317
|
const fsMembers = boxWorkdir !== void 0 ? {
|
|
833
1318
|
fs: {
|
|
834
1319
|
async read(path) {
|
|
835
|
-
return readFile(
|
|
1320
|
+
return readFile(join2(boxWorkdir, path), "utf8");
|
|
836
1321
|
},
|
|
837
1322
|
async write(path, content) {
|
|
838
|
-
const abs =
|
|
1323
|
+
const abs = join2(boxWorkdir, path);
|
|
839
1324
|
await mkdir(dirname(abs), { recursive: true });
|
|
840
1325
|
await writeFile(abs, content, "utf8");
|
|
841
1326
|
}
|
|
@@ -868,7 +1353,7 @@ function inProcessSandboxClient(options) {
|
|
|
868
1353
|
const ctx = { round, workdir: boxWorkdir, signal };
|
|
869
1354
|
round += 1;
|
|
870
1355
|
const produced = await onPrompt(prompt, ctx);
|
|
871
|
-
if (
|
|
1356
|
+
if (isAsyncIterable2(produced)) {
|
|
872
1357
|
for await (const ev of produced) yield ev;
|
|
873
1358
|
} else {
|
|
874
1359
|
for (const ev of produced) yield ev;
|
|
@@ -884,104 +1369,6 @@ function inProcessSandboxClient(options) {
|
|
|
884
1369
|
};
|
|
885
1370
|
}
|
|
886
1371
|
|
|
887
|
-
// src/runtime/inline-sandbox-client.ts
|
|
888
|
-
function isAsyncIterable2(v) {
|
|
889
|
-
return typeof v === "object" && v !== null && Symbol.asyncIterator in v;
|
|
890
|
-
}
|
|
891
|
-
async function settle(exec, task, signal) {
|
|
892
|
-
const r = exec.execute(task, signal);
|
|
893
|
-
if (isAsyncIterable2(r)) {
|
|
894
|
-
for await (const _ of r) {
|
|
895
|
-
}
|
|
896
|
-
return exec.resultArtifact();
|
|
897
|
-
}
|
|
898
|
-
return r;
|
|
899
|
-
}
|
|
900
|
-
function inlineSandboxClient(factory) {
|
|
901
|
-
let seq = 0;
|
|
902
|
-
return {
|
|
903
|
-
async create(options) {
|
|
904
|
-
const id = `inline-${seq++}`;
|
|
905
|
-
const createOptions = options;
|
|
906
|
-
return {
|
|
907
|
-
id,
|
|
908
|
-
async *streamPrompt(message) {
|
|
909
|
-
const controller = new AbortController();
|
|
910
|
-
const spec = { profile: { name: id }, harness: null };
|
|
911
|
-
const exec = factory(spec, { signal: controller.signal, seams: { createOptions } });
|
|
912
|
-
try {
|
|
913
|
-
const artifact = await settle(exec, message, controller.signal);
|
|
914
|
-
const out = artifact.out;
|
|
915
|
-
const tokensIn = artifact.spent.tokens.input;
|
|
916
|
-
const tokensOut = artifact.spent.tokens.output;
|
|
917
|
-
const costUsd = artifact.spent.usd;
|
|
918
|
-
if (tokensIn || tokensOut || costUsd) {
|
|
919
|
-
yield {
|
|
920
|
-
type: "llm_call",
|
|
921
|
-
data: { tokensIn, tokensOut, costUsd }
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
yield {
|
|
925
|
-
type: "result",
|
|
926
|
-
data: {
|
|
927
|
-
finalText: out?.content ?? "",
|
|
928
|
-
tokenUsage: {
|
|
929
|
-
inputTokens: tokensIn,
|
|
930
|
-
outputTokens: tokensOut
|
|
931
|
-
},
|
|
932
|
-
costUsd
|
|
933
|
-
}
|
|
934
|
-
};
|
|
935
|
-
} finally {
|
|
936
|
-
await exec.teardown("brutalKill").catch(() => {
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
},
|
|
940
|
-
async delete() {
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// src/runtime/report-usage.ts
|
|
948
|
-
function reportLoopUsage(cost, result, source = "loop") {
|
|
949
|
-
cost.observe(result.costUsd, source);
|
|
950
|
-
cost.observeTokens(result.tokenUsage);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// src/runtime/loop-dispatch.ts
|
|
954
|
-
function campaignTraceToLoopEmitter(trace) {
|
|
955
|
-
return {
|
|
956
|
-
emit(event) {
|
|
957
|
-
trace.span(event.kind, { runId: event.runId, timestamp: event.timestamp, ...event.payload }).end();
|
|
958
|
-
}
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
async function runLoopForCell(opts, scenario, profile, ctx) {
|
|
962
|
-
const loopOptions = opts.toLoopOptions(scenario, profile);
|
|
963
|
-
return runLoopWithCampaignContext(opts, loopOptions, ctx);
|
|
964
|
-
}
|
|
965
|
-
async function runLoopWithCampaignContext(opts, loopOptions, ctx) {
|
|
966
|
-
const result = await runLoop({
|
|
967
|
-
...loopOptions,
|
|
968
|
-
ctx: {
|
|
969
|
-
sandboxClient: opts.sandboxClient,
|
|
970
|
-
signal: ctx.signal,
|
|
971
|
-
traceEmitter: opts.forwardTrace === false ? void 0 : campaignTraceToLoopEmitter(ctx.trace)
|
|
972
|
-
}
|
|
973
|
-
});
|
|
974
|
-
reportLoopUsage(ctx.cost, result, opts.costSource ?? "loop");
|
|
975
|
-
const toArtifact = opts.toArtifact ?? ((r) => r.winner?.output);
|
|
976
|
-
return toArtifact(result);
|
|
977
|
-
}
|
|
978
|
-
function loopCampaignDispatch(opts) {
|
|
979
|
-
return (scenario, ctx) => runLoopWithCampaignContext(opts, opts.toLoopOptions(scenario), ctx);
|
|
980
|
-
}
|
|
981
|
-
function loopDispatch(opts) {
|
|
982
|
-
return (profile, scenario, ctx) => runLoopForCell(opts, scenario, profile, ctx);
|
|
983
|
-
}
|
|
984
|
-
|
|
985
1372
|
// src/runtime/mcp-environment.ts
|
|
986
1373
|
async function rpc(endpoint, body) {
|
|
987
1374
|
let lastErr;
|
|
@@ -2204,65 +2591,20 @@ function promotionGate(opts) {
|
|
|
2204
2591
|
promoted: false,
|
|
2205
2592
|
reason: "not-cheaper",
|
|
2206
2593
|
mode,
|
|
2207
|
-
n: scoreSig.n,
|
|
2208
|
-
lift,
|
|
2209
|
-
costSavings,
|
|
2210
|
-
latency
|
|
2211
|
-
};
|
|
2212
|
-
return {
|
|
2213
|
-
promoted: true,
|
|
2214
|
-
reason: "non-inferior-and-cheaper",
|
|
2215
|
-
mode,
|
|
2216
|
-
n: scoreSig.n,
|
|
2217
|
-
lift,
|
|
2218
|
-
costSavings,
|
|
2219
|
-
latency
|
|
2220
|
-
};
|
|
2221
|
-
}
|
|
2222
|
-
|
|
2223
|
-
// src/runtime/resolve-sandbox-client.ts
|
|
2224
|
-
function resolveSandboxClient(opts) {
|
|
2225
|
-
switch (opts.backend) {
|
|
2226
|
-
case "sandbox": {
|
|
2227
|
-
if (!opts.sandboxClient) {
|
|
2228
|
-
throw new Error("resolveSandboxClient: backend 'sandbox' requires opts.sandboxClient");
|
|
2229
|
-
}
|
|
2230
|
-
return opts.sandboxClient;
|
|
2231
|
-
}
|
|
2232
|
-
case "bridge": {
|
|
2233
|
-
const bridge = opts.bridge;
|
|
2234
|
-
if (!bridge?.bearer || !bridge.model) {
|
|
2235
|
-
throw new Error(
|
|
2236
|
-
"resolveSandboxClient: backend 'bridge' requires bridge.bearer and bridge.model"
|
|
2237
|
-
);
|
|
2238
|
-
}
|
|
2239
|
-
return inlineSandboxClient(
|
|
2240
|
-
createExecutor({
|
|
2241
|
-
backend: "bridge",
|
|
2242
|
-
bridgeUrl: bridge.url ?? "http://127.0.0.1:3355",
|
|
2243
|
-
bridgeBearer: bridge.bearer,
|
|
2244
|
-
model: bridge.model,
|
|
2245
|
-
timeoutMs: bridge.timeoutMs
|
|
2246
|
-
})
|
|
2247
|
-
);
|
|
2248
|
-
}
|
|
2249
|
-
case "router": {
|
|
2250
|
-
const router = opts.router;
|
|
2251
|
-
if (!router?.baseUrl || !router.key || !router.model) {
|
|
2252
|
-
throw new Error(
|
|
2253
|
-
"resolveSandboxClient: backend 'router' requires router.baseUrl, router.key and router.model"
|
|
2254
|
-
);
|
|
2255
|
-
}
|
|
2256
|
-
return inlineSandboxClient(
|
|
2257
|
-
createExecutor({
|
|
2258
|
-
backend: "router",
|
|
2259
|
-
routerBaseUrl: router.baseUrl,
|
|
2260
|
-
routerKey: router.key,
|
|
2261
|
-
model: router.model
|
|
2262
|
-
})
|
|
2263
|
-
);
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2594
|
+
n: scoreSig.n,
|
|
2595
|
+
lift,
|
|
2596
|
+
costSavings,
|
|
2597
|
+
latency
|
|
2598
|
+
};
|
|
2599
|
+
return {
|
|
2600
|
+
promoted: true,
|
|
2601
|
+
reason: "non-inferior-and-cheaper",
|
|
2602
|
+
mode,
|
|
2603
|
+
n: scoreSig.n,
|
|
2604
|
+
lift,
|
|
2605
|
+
costSavings,
|
|
2606
|
+
latency
|
|
2607
|
+
};
|
|
2266
2608
|
}
|
|
2267
2609
|
|
|
2268
2610
|
// src/runtime/run-benchmark.ts
|
|
@@ -3267,56 +3609,9 @@ function errorMessage(error) {
|
|
|
3267
3609
|
return error instanceof Error ? error.message : String(error);
|
|
3268
3610
|
}
|
|
3269
3611
|
|
|
3270
|
-
// src/runtime/steering-drivers.ts
|
|
3271
|
-
function decideUntilValidOrCapped(history, maxIterations) {
|
|
3272
|
-
if (history.some((it) => it.verdict?.valid)) return "pick-winner";
|
|
3273
|
-
return history.length < maxIterations ? "refine" : "fail";
|
|
3274
|
-
}
|
|
3275
|
-
function naiveDriver(options) {
|
|
3276
|
-
const { continuation, applyContinuation, maxIterations, name = "naive" } = options;
|
|
3277
|
-
return {
|
|
3278
|
-
name,
|
|
3279
|
-
plan(task, history) {
|
|
3280
|
-
if (history.length === 0) return Promise.resolve([task]);
|
|
3281
|
-
const last = history[history.length - 1];
|
|
3282
|
-
if (last?.verdict?.valid) return Promise.resolve([]);
|
|
3283
|
-
if (history.length >= maxIterations) return Promise.resolve([]);
|
|
3284
|
-
return Promise.resolve([applyContinuation(task, continuation)]);
|
|
3285
|
-
},
|
|
3286
|
-
decide(history) {
|
|
3287
|
-
return decideUntilValidOrCapped(history, maxIterations);
|
|
3288
|
-
},
|
|
3289
|
-
// Pure refine topology (one task per round) — render the steer move.
|
|
3290
|
-
describePlan() {
|
|
3291
|
-
return { kind: "refine", rationale: "naive fixed continuation (no grade signal)" };
|
|
3292
|
-
}
|
|
3293
|
-
};
|
|
3294
|
-
}
|
|
3295
|
-
function dumbDriver(options) {
|
|
3296
|
-
const { onPass, onFail, applyContinuation, maxIterations, name = "dumb" } = options;
|
|
3297
|
-
return {
|
|
3298
|
-
name,
|
|
3299
|
-
plan(task, history) {
|
|
3300
|
-
if (history.length === 0) return Promise.resolve([task]);
|
|
3301
|
-
const last = history[history.length - 1];
|
|
3302
|
-
const passed = last?.verdict?.valid === true;
|
|
3303
|
-
if (passed) return Promise.resolve([]);
|
|
3304
|
-
if (history.length >= maxIterations) return Promise.resolve([]);
|
|
3305
|
-
const continuation = passed ? onPass : onFail;
|
|
3306
|
-
return Promise.resolve([applyContinuation(task, continuation)]);
|
|
3307
|
-
},
|
|
3308
|
-
decide(history) {
|
|
3309
|
-
return decideUntilValidOrCapped(history, maxIterations);
|
|
3310
|
-
},
|
|
3311
|
-
describePlan() {
|
|
3312
|
-
return { kind: "refine", rationale: "dumb pass/fail-only continuation (no grader findings)" };
|
|
3313
|
-
}
|
|
3314
|
-
};
|
|
3315
|
-
}
|
|
3316
|
-
|
|
3317
3612
|
// src/runtime/strategy-author.ts
|
|
3318
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
3319
|
-
import { join as
|
|
3613
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3614
|
+
import { join as join3 } from "path";
|
|
3320
3615
|
var strategyAuthorContract = `
|
|
3321
3616
|
You author an OPTIMIZATION STRATEGY for an agentic loop system. A strategy decides how to
|
|
3322
3617
|
spend a compute budget to beat a task's deployable check. You compose exactly two steps:
|
|
@@ -3434,9 +3729,9 @@ async function authorStrategy(opts) {
|
|
|
3434
3729
|
code = await requestAuthoredCode(opts, opts.fallbackModel);
|
|
3435
3730
|
}
|
|
3436
3731
|
assertStrategyContract(code);
|
|
3437
|
-
|
|
3438
|
-
const file =
|
|
3439
|
-
|
|
3732
|
+
mkdirSync2(opts.outDir, { recursive: true });
|
|
3733
|
+
const file = join3(opts.outDir, `authored-${Date.now()}.mts`);
|
|
3734
|
+
writeFileSync2(file, code);
|
|
3440
3735
|
const mod = await import(`file://${file}`);
|
|
3441
3736
|
if (!mod.default || typeof mod.default.driver !== "function" || !mod.default.name) {
|
|
3442
3737
|
throw new Error(`authorStrategy: ${file} does not export a default Strategy`);
|
|
@@ -3445,7 +3740,7 @@ async function authorStrategy(opts) {
|
|
|
3445
3740
|
}
|
|
3446
3741
|
|
|
3447
3742
|
// src/runtime/strategy-evolution.ts
|
|
3448
|
-
import { existsSync, readFileSync, writeFileSync as
|
|
3743
|
+
import { existsSync, readFileSync, writeFileSync as writeFileSync3 } from "fs";
|
|
3449
3744
|
import { gzipSync } from "zlib";
|
|
3450
3745
|
function discriminatingMeans(report, fieldOrder) {
|
|
3451
3746
|
const rows = report.perTask.filter((r) => {
|
|
@@ -3557,7 +3852,7 @@ async function runStrategyEvolution(cfg) {
|
|
|
3557
3852
|
}
|
|
3558
3853
|
const save = (state) => {
|
|
3559
3854
|
if (cfg.checkpoint)
|
|
3560
|
-
|
|
3855
|
+
writeFileSync3(cfg.checkpoint.path, JSON.stringify({ ...state, fingerprint }, null, 1));
|
|
3561
3856
|
};
|
|
3562
3857
|
const bench = async (phase, tasks, strategies) => {
|
|
3563
3858
|
await cfg.onPhase?.(phase);
|
|
@@ -3854,6 +4149,800 @@ ${summary}`,
|
|
|
3854
4149
|
};
|
|
3855
4150
|
}
|
|
3856
4151
|
|
|
4152
|
+
// src/runtime/stream-agent-turn.ts
|
|
4153
|
+
import { scoreKnowledgeReadiness } from "@tangle-network/agent-eval";
|
|
4154
|
+
|
|
4155
|
+
// src/sessions.ts
|
|
4156
|
+
function newRuntimeSession(backend, requestedId, metadata) {
|
|
4157
|
+
const now = nowIso();
|
|
4158
|
+
return {
|
|
4159
|
+
id: requestedId || crypto.randomUUID(),
|
|
4160
|
+
backend,
|
|
4161
|
+
status: "active",
|
|
4162
|
+
createdAt: now,
|
|
4163
|
+
updatedAt: now,
|
|
4164
|
+
metadata
|
|
4165
|
+
};
|
|
4166
|
+
}
|
|
4167
|
+
function touchSession(session) {
|
|
4168
|
+
return { ...session, updatedAt: nowIso() };
|
|
4169
|
+
}
|
|
4170
|
+
function nowIso() {
|
|
4171
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4172
|
+
}
|
|
4173
|
+
var InMemoryRuntimeSessionStore = class {
|
|
4174
|
+
sessions = /* @__PURE__ */ new Map();
|
|
4175
|
+
events = /* @__PURE__ */ new Map();
|
|
4176
|
+
get(sessionId) {
|
|
4177
|
+
return this.sessions.get(sessionId);
|
|
4178
|
+
}
|
|
4179
|
+
put(session) {
|
|
4180
|
+
this.sessions.set(session.id, session);
|
|
4181
|
+
}
|
|
4182
|
+
appendEvent(sessionId, event) {
|
|
4183
|
+
const existing = this.events.get(sessionId) ?? [];
|
|
4184
|
+
existing.push(event);
|
|
4185
|
+
this.events.set(sessionId, existing);
|
|
4186
|
+
}
|
|
4187
|
+
listEvents(sessionId) {
|
|
4188
|
+
return [...this.events.get(sessionId) ?? []];
|
|
4189
|
+
}
|
|
4190
|
+
};
|
|
4191
|
+
|
|
4192
|
+
// src/backends.ts
|
|
4193
|
+
function createIterableBackend(options) {
|
|
4194
|
+
return options;
|
|
4195
|
+
}
|
|
4196
|
+
function createSandboxPromptBackend(options) {
|
|
4197
|
+
const kind = options.kind ?? "sandbox";
|
|
4198
|
+
return {
|
|
4199
|
+
kind,
|
|
4200
|
+
async start(input, context) {
|
|
4201
|
+
const box = await options.getBox(input, context);
|
|
4202
|
+
return newRuntimeSession(
|
|
4203
|
+
kind,
|
|
4204
|
+
options.getSessionId?.(box, input) ?? context.requestedSessionId,
|
|
4205
|
+
{ resumable: true }
|
|
4206
|
+
);
|
|
4207
|
+
},
|
|
4208
|
+
resume(session) {
|
|
4209
|
+
return touchSession({ ...session, status: "active" });
|
|
4210
|
+
},
|
|
4211
|
+
async *stream(input, context) {
|
|
4212
|
+
const box = await options.getBox(input, context);
|
|
4213
|
+
const message = input.message ?? input.messages?.at(-1)?.content ?? context.task.intent;
|
|
4214
|
+
for await (const event of options.streamPrompt(box, message, context)) {
|
|
4215
|
+
const mapped = options.mapEvent?.(event, context) ?? mapCommonBackendEvent(event, context);
|
|
4216
|
+
if (mapped) yield mapped;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
};
|
|
4220
|
+
}
|
|
4221
|
+
var DEFAULT_RETRY_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
4222
|
+
function pickRetryDelayMs(attempt, policy) {
|
|
4223
|
+
const exp = policy.initialBackoffMs * 2 ** (attempt - 1);
|
|
4224
|
+
const capped = Math.min(exp, policy.maxBackoffMs);
|
|
4225
|
+
const jitter = capped * policy.jitter * (Math.random() * 2 - 1);
|
|
4226
|
+
return Math.max(0, Math.round(capped + jitter));
|
|
4227
|
+
}
|
|
4228
|
+
function withTimeout(callerSignal, timeoutMs) {
|
|
4229
|
+
if (timeoutMs <= 0) {
|
|
4230
|
+
return { signal: callerSignal ?? new AbortController().signal, dispose: () => void 0 };
|
|
4231
|
+
}
|
|
4232
|
+
const controller = new AbortController();
|
|
4233
|
+
const timer = setTimeout(
|
|
4234
|
+
() => controller.abort(new Error(`request timed out after ${timeoutMs}ms`)),
|
|
4235
|
+
timeoutMs
|
|
4236
|
+
);
|
|
4237
|
+
if (typeof timer.unref === "function") {
|
|
4238
|
+
;
|
|
4239
|
+
timer.unref();
|
|
4240
|
+
}
|
|
4241
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason ?? new Error("aborted"));
|
|
4242
|
+
if (callerSignal) {
|
|
4243
|
+
if (callerSignal.aborted) onCallerAbort();
|
|
4244
|
+
else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
4245
|
+
}
|
|
4246
|
+
return {
|
|
4247
|
+
signal: controller.signal,
|
|
4248
|
+
dispose: () => {
|
|
4249
|
+
clearTimeout(timer);
|
|
4250
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
4251
|
+
}
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
function sleep2(ms, signal) {
|
|
4255
|
+
return new Promise((resolve, reject) => {
|
|
4256
|
+
if (signal?.aborted) {
|
|
4257
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
4260
|
+
const t = setTimeout(() => {
|
|
4261
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4262
|
+
resolve();
|
|
4263
|
+
}, ms);
|
|
4264
|
+
const onAbort = () => {
|
|
4265
|
+
clearTimeout(t);
|
|
4266
|
+
reject(signal?.reason ?? new Error("aborted"));
|
|
4267
|
+
};
|
|
4268
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4269
|
+
});
|
|
4270
|
+
}
|
|
4271
|
+
function createOpenAICompatibleBackend(options) {
|
|
4272
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
4273
|
+
const kind = options.kind ?? "tcloud";
|
|
4274
|
+
const retryPolicy = {
|
|
4275
|
+
maxAttempts: options.retry?.maxAttempts ?? 5,
|
|
4276
|
+
initialBackoffMs: options.retry?.initialBackoffMs ?? 1e3,
|
|
4277
|
+
maxBackoffMs: options.retry?.maxBackoffMs ?? 3e4,
|
|
4278
|
+
jitter: options.retry?.jitter ?? 0.25,
|
|
4279
|
+
retryStatuses: options.retry?.retryStatuses ?? DEFAULT_RETRY_STATUSES,
|
|
4280
|
+
requestTimeoutMs: options.retry?.requestTimeoutMs ?? 12e4
|
|
4281
|
+
};
|
|
4282
|
+
return {
|
|
4283
|
+
kind,
|
|
4284
|
+
start(_input, context) {
|
|
4285
|
+
return newRuntimeSession(kind, context.requestedSessionId);
|
|
4286
|
+
},
|
|
4287
|
+
async *stream(input, context) {
|
|
4288
|
+
const url = `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
4289
|
+
const bodyPayload = {
|
|
4290
|
+
model: options.model,
|
|
4291
|
+
stream: true,
|
|
4292
|
+
stream_options: { include_usage: true },
|
|
4293
|
+
messages: input.messages ?? [
|
|
4294
|
+
{ role: "user", content: input.message ?? context.task.intent }
|
|
4295
|
+
]
|
|
4296
|
+
};
|
|
4297
|
+
if (options.tools && options.tools.length > 0) {
|
|
4298
|
+
bodyPayload.tools = options.tools;
|
|
4299
|
+
if (options.toolChoice !== void 0) bodyPayload.tool_choice = options.toolChoice;
|
|
4300
|
+
}
|
|
4301
|
+
if (options.responseFormat !== void 0) {
|
|
4302
|
+
bodyPayload.response_format = options.responseFormat;
|
|
4303
|
+
}
|
|
4304
|
+
const requestBody = JSON.stringify(bodyPayload);
|
|
4305
|
+
let response;
|
|
4306
|
+
let lastStatus = 0;
|
|
4307
|
+
let lastThrown;
|
|
4308
|
+
for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt++) {
|
|
4309
|
+
lastThrown = void 0;
|
|
4310
|
+
const attemptSignal = withTimeout(context.signal, retryPolicy.requestTimeoutMs);
|
|
4311
|
+
try {
|
|
4312
|
+
response = await fetcher(url, {
|
|
4313
|
+
method: "POST",
|
|
4314
|
+
headers: {
|
|
4315
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
4316
|
+
"Content-Type": "application/json",
|
|
4317
|
+
// Cross-gateway forwarding: when this call is part of a
|
|
4318
|
+
// multi-agent conversation, the runner stamps run/turn/
|
|
4319
|
+
// depth/forwarded-auth headers onto the context. They flow
|
|
4320
|
+
// through to the downstream gateway verbatim so the original
|
|
4321
|
+
// user gets billed, the recursion depth stays bounded, and
|
|
4322
|
+
// the trace correlates across hops.
|
|
4323
|
+
...context.propagatedHeaders ?? {}
|
|
4324
|
+
},
|
|
4325
|
+
body: requestBody,
|
|
4326
|
+
signal: attemptSignal.signal
|
|
4327
|
+
});
|
|
4328
|
+
} catch (err) {
|
|
4329
|
+
attemptSignal.dispose();
|
|
4330
|
+
if (context.signal?.aborted) throw err;
|
|
4331
|
+
lastThrown = err;
|
|
4332
|
+
response = void 0;
|
|
4333
|
+
if (attempt === retryPolicy.maxAttempts) break;
|
|
4334
|
+
await sleep2(pickRetryDelayMs(attempt, retryPolicy), context.signal);
|
|
4335
|
+
continue;
|
|
4336
|
+
}
|
|
4337
|
+
attemptSignal.dispose();
|
|
4338
|
+
if (response.ok) break;
|
|
4339
|
+
lastStatus = response.status;
|
|
4340
|
+
if (!retryPolicy.retryStatuses.includes(response.status)) break;
|
|
4341
|
+
if (attempt === retryPolicy.maxAttempts) break;
|
|
4342
|
+
try {
|
|
4343
|
+
await response.body?.cancel();
|
|
4344
|
+
} catch {
|
|
4345
|
+
}
|
|
4346
|
+
const delayMs = pickRetryDelayMs(attempt, retryPolicy);
|
|
4347
|
+
await sleep2(delayMs, context.signal);
|
|
4348
|
+
}
|
|
4349
|
+
if (!response) {
|
|
4350
|
+
const reason = lastThrown instanceof Error ? lastThrown.message : String(lastThrown);
|
|
4351
|
+
throw new BackendTransportError(
|
|
4352
|
+
kind,
|
|
4353
|
+
`chat backend unreachable after ${retryPolicy.maxAttempts} attempts: ${reason}`,
|
|
4354
|
+
{ status: 0 }
|
|
4355
|
+
);
|
|
4356
|
+
}
|
|
4357
|
+
if (!response.ok) {
|
|
4358
|
+
let body;
|
|
4359
|
+
try {
|
|
4360
|
+
const raw = await response.text();
|
|
4361
|
+
body = raw.length > MAX_ERROR_BODY_BYTES ? `${raw.slice(0, MAX_ERROR_BODY_BYTES)}\u2026` : raw;
|
|
4362
|
+
} catch {
|
|
4363
|
+
body = void 0;
|
|
4364
|
+
}
|
|
4365
|
+
throw new BackendTransportError(kind, `chat backend returned ${lastStatus || "unknown"}`, {
|
|
4366
|
+
status: lastStatus || 0,
|
|
4367
|
+
body
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
yield* streamResponseEvents(response, context, options.model);
|
|
4371
|
+
}
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
var MAX_ERROR_BODY_BYTES = 2048;
|
|
4375
|
+
function normalizeBackendStreamEvent(event, task, session) {
|
|
4376
|
+
if ("task" in event && event.task && "session" in event && event.session && "timestamp" in event && event.timestamp) {
|
|
4377
|
+
return event;
|
|
4378
|
+
}
|
|
4379
|
+
return {
|
|
4380
|
+
...event,
|
|
4381
|
+
task: "task" in event && event.task ? event.task : task,
|
|
4382
|
+
session: "session" in event && event.session ? event.session : session,
|
|
4383
|
+
timestamp: "timestamp" in event && event.timestamp ? event.timestamp : nowIso()
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
function mapCommonBackendEvent(event, context) {
|
|
4387
|
+
if (!event || typeof event !== "object") return void 0;
|
|
4388
|
+
const record = event;
|
|
4389
|
+
const type = String(record.type ?? "");
|
|
4390
|
+
const data = record.data && typeof record.data === "object" ? record.data : record;
|
|
4391
|
+
if (type === "message.part.updated" || type === "text_delta" || type === "delta") {
|
|
4392
|
+
const part = data.part;
|
|
4393
|
+
const partText = part !== void 0 && typeof part === "object" && (part.type === "text" || part.type === void 0) ? stringValue(part.text) : void 0;
|
|
4394
|
+
const text = stringValue(data.text) ?? stringValue(data.delta) ?? stringValue(record.text) ?? partText;
|
|
4395
|
+
return text ? {
|
|
4396
|
+
type: "text_delta",
|
|
4397
|
+
task: context.task,
|
|
4398
|
+
session: context.session,
|
|
4399
|
+
text,
|
|
4400
|
+
timestamp: nowIso()
|
|
4401
|
+
} : void 0;
|
|
4402
|
+
}
|
|
4403
|
+
if (type === "reasoning_delta") {
|
|
4404
|
+
const text = stringValue(data.text) ?? stringValue(record.text);
|
|
4405
|
+
return text ? {
|
|
4406
|
+
type: "reasoning_delta",
|
|
4407
|
+
task: context.task,
|
|
4408
|
+
session: context.session,
|
|
4409
|
+
text,
|
|
4410
|
+
timestamp: nowIso()
|
|
4411
|
+
} : void 0;
|
|
4412
|
+
}
|
|
4413
|
+
if (type === "tool_call") {
|
|
4414
|
+
return {
|
|
4415
|
+
type: "tool_call",
|
|
4416
|
+
task: context.task,
|
|
4417
|
+
session: context.session,
|
|
4418
|
+
toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
|
|
4419
|
+
toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
|
|
4420
|
+
args: data.args ?? data.input ?? record.args,
|
|
4421
|
+
timestamp: nowIso()
|
|
4422
|
+
};
|
|
4423
|
+
}
|
|
4424
|
+
if (type === "tool_result") {
|
|
4425
|
+
return {
|
|
4426
|
+
type: "tool_result",
|
|
4427
|
+
task: context.task,
|
|
4428
|
+
session: context.session,
|
|
4429
|
+
toolName: stringValue(data.name) ?? stringValue(record.toolName) ?? "tool",
|
|
4430
|
+
toolCallId: stringValue(data.id) ?? stringValue(record.toolCallId),
|
|
4431
|
+
result: data.result ?? data.output ?? record.result,
|
|
4432
|
+
timestamp: nowIso()
|
|
4433
|
+
};
|
|
4434
|
+
}
|
|
4435
|
+
if (type === "artifact") {
|
|
4436
|
+
const artifactId = stringValue(data.artifactId) ?? stringValue(data.id) ?? stringValue(record.artifactId);
|
|
4437
|
+
if (!artifactId) return void 0;
|
|
4438
|
+
return {
|
|
4439
|
+
type: "artifact",
|
|
4440
|
+
task: context.task,
|
|
4441
|
+
session: context.session,
|
|
4442
|
+
artifactId,
|
|
4443
|
+
name: stringValue(data.name) ?? stringValue(record.name),
|
|
4444
|
+
mimeType: stringValue(data.mimeType) ?? stringValue(record.mimeType),
|
|
4445
|
+
uri: stringValue(data.uri) ?? stringValue(record.uri),
|
|
4446
|
+
content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
|
|
4447
|
+
metadata: data.metadata && typeof data.metadata === "object" ? data.metadata : void 0,
|
|
4448
|
+
timestamp: nowIso()
|
|
4449
|
+
};
|
|
4450
|
+
}
|
|
4451
|
+
if (type === "proposal_created" || type === "proposal" || type === "filing") {
|
|
4452
|
+
const proposalId = stringValue(data.proposalId) ?? stringValue(data.id) ?? stringValue(record.proposalId);
|
|
4453
|
+
if (!proposalId) return void 0;
|
|
4454
|
+
const status = stringValue(data.status) ?? stringValue(record.status);
|
|
4455
|
+
return {
|
|
4456
|
+
type: "proposal_created",
|
|
4457
|
+
task: context.task,
|
|
4458
|
+
session: context.session,
|
|
4459
|
+
proposalId,
|
|
4460
|
+
title: stringValue(data.title) ?? stringValue(record.title) ?? proposalId,
|
|
4461
|
+
status: status === "pending" || status === "approved" || status === "rejected" ? status : void 0,
|
|
4462
|
+
content: stringValue(data.content) ?? stringValue(data.body) ?? stringValue(record.content),
|
|
4463
|
+
timestamp: nowIso()
|
|
4464
|
+
};
|
|
4465
|
+
}
|
|
4466
|
+
if (type === "result" || type === "final") {
|
|
4467
|
+
const text = stringValue(data.finalText) ?? stringValue(data.text) ?? stringValue(record.text);
|
|
4468
|
+
return text ? {
|
|
4469
|
+
type: "text_delta",
|
|
4470
|
+
task: context.task,
|
|
4471
|
+
session: context.session,
|
|
4472
|
+
text,
|
|
4473
|
+
timestamp: nowIso()
|
|
4474
|
+
} : void 0;
|
|
4475
|
+
}
|
|
4476
|
+
return void 0;
|
|
4477
|
+
}
|
|
4478
|
+
async function* streamResponseEvents(response, context, requestedModel) {
|
|
4479
|
+
const body = response.body;
|
|
4480
|
+
if (!body) return;
|
|
4481
|
+
const reader = body.getReader();
|
|
4482
|
+
const decoder = new TextDecoder();
|
|
4483
|
+
let buffer = "";
|
|
4484
|
+
const usage = { saw: false };
|
|
4485
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
4486
|
+
const startedAt = Date.now();
|
|
4487
|
+
for (; ; ) {
|
|
4488
|
+
const { done, value } = await reader.read();
|
|
4489
|
+
if (done) break;
|
|
4490
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
4491
|
+
for (const event of drainStreamBuffer(false)) yield event;
|
|
4492
|
+
}
|
|
4493
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
4494
|
+
for (const event of drainStreamBuffer(true)) yield event;
|
|
4495
|
+
if (buffer.trim()) {
|
|
4496
|
+
for (const event of parseStreamChunk(buffer, context, usage, toolCalls)) yield event;
|
|
4497
|
+
}
|
|
4498
|
+
for (const event of flushPendingToolCalls(toolCalls, context)) yield event;
|
|
4499
|
+
if (usage.saw) {
|
|
4500
|
+
yield {
|
|
4501
|
+
type: "llm_call",
|
|
4502
|
+
task: context.task,
|
|
4503
|
+
session: context.session,
|
|
4504
|
+
model: usage.model ?? requestedModel,
|
|
4505
|
+
tokensIn: usage.tokensIn,
|
|
4506
|
+
tokensOut: usage.tokensOut,
|
|
4507
|
+
// `costUsd` is intentionally absent — pricing tables live in consumers
|
|
4508
|
+
// (agent-eval's `estimateCost`, MetricsCollector). Emitting a wrong
|
|
4509
|
+
// number here is worse than emitting none.
|
|
4510
|
+
latencyMs: Date.now() - startedAt,
|
|
4511
|
+
finishReason: usage.finishReason,
|
|
4512
|
+
timestamp: nowIso()
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
4515
|
+
function* drainStreamBuffer(flush) {
|
|
4516
|
+
for (; ; ) {
|
|
4517
|
+
const sseBoundary = buffer.indexOf("\n\n");
|
|
4518
|
+
if (sseBoundary >= 0) {
|
|
4519
|
+
const chunk = buffer.slice(0, sseBoundary);
|
|
4520
|
+
buffer = buffer.slice(sseBoundary + 2);
|
|
4521
|
+
for (const event of parseStreamChunk(chunk, context, usage, toolCalls)) yield event;
|
|
4522
|
+
continue;
|
|
4523
|
+
}
|
|
4524
|
+
const newline = buffer.indexOf("\n");
|
|
4525
|
+
if (newline >= 0 && !buffer.slice(0, newline).startsWith("data:")) {
|
|
4526
|
+
const line = buffer.slice(0, newline);
|
|
4527
|
+
buffer = buffer.slice(newline + 1);
|
|
4528
|
+
for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
|
|
4529
|
+
continue;
|
|
4530
|
+
}
|
|
4531
|
+
if (flush && buffer.trim() && !buffer.trimStart().startsWith("data:")) {
|
|
4532
|
+
const line = buffer;
|
|
4533
|
+
buffer = "";
|
|
4534
|
+
for (const event of parseStreamChunk(line, context, usage, toolCalls)) yield event;
|
|
4535
|
+
continue;
|
|
4536
|
+
}
|
|
4537
|
+
break;
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
function* parseStreamChunk(chunk, context, usage, toolCalls) {
|
|
4542
|
+
const lines = chunk.split(/\r?\n/);
|
|
4543
|
+
const dataLines = lines.filter((line) => line.startsWith("data:"));
|
|
4544
|
+
if (dataLines.length === 0 && lines.every((line) => {
|
|
4545
|
+
const trimmed = line.trim();
|
|
4546
|
+
return trimmed.length === 0 || trimmed.startsWith(":");
|
|
4547
|
+
})) {
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4550
|
+
const data = dataLines.length > 0 ? dataLines.map((line) => line.slice(5).trimStart()).join("\n") : chunk.trim();
|
|
4551
|
+
if (!data || data === "[DONE]") return;
|
|
4552
|
+
let parsed;
|
|
4553
|
+
try {
|
|
4554
|
+
parsed = JSON.parse(data);
|
|
4555
|
+
} catch {
|
|
4556
|
+
yield {
|
|
4557
|
+
type: "text_delta",
|
|
4558
|
+
task: context.task,
|
|
4559
|
+
session: context.session,
|
|
4560
|
+
text: data,
|
|
4561
|
+
timestamp: nowIso()
|
|
4562
|
+
};
|
|
4563
|
+
return;
|
|
4564
|
+
}
|
|
4565
|
+
captureStreamUsage(parsed, usage);
|
|
4566
|
+
const choices = parsed.choices;
|
|
4567
|
+
const choice = Array.isArray(choices) ? choices[0] : void 0;
|
|
4568
|
+
const delta = choice?.delta;
|
|
4569
|
+
const message = choice?.message;
|
|
4570
|
+
const deltaToolCalls = delta?.tool_calls;
|
|
4571
|
+
if (Array.isArray(deltaToolCalls)) {
|
|
4572
|
+
for (const tc of deltaToolCalls) {
|
|
4573
|
+
if (!tc || typeof tc !== "object") continue;
|
|
4574
|
+
const rec = tc;
|
|
4575
|
+
const idx = numberValue(rec.index) ?? 0;
|
|
4576
|
+
const key = `openai:${idx}`;
|
|
4577
|
+
const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
|
|
4578
|
+
const id = stringValue(rec.id);
|
|
4579
|
+
if (id) acc.id = id;
|
|
4580
|
+
const fn = rec.function;
|
|
4581
|
+
const name = stringValue(fn?.name);
|
|
4582
|
+
if (name) acc.name = name;
|
|
4583
|
+
const args = stringValue(fn?.arguments);
|
|
4584
|
+
if (args) acc.argsRaw += args;
|
|
4585
|
+
toolCalls.set(key, acc);
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
const messageToolCalls = message?.tool_calls;
|
|
4589
|
+
if (Array.isArray(messageToolCalls)) {
|
|
4590
|
+
for (const tc of messageToolCalls) {
|
|
4591
|
+
if (!tc || typeof tc !== "object") continue;
|
|
4592
|
+
const rec = tc;
|
|
4593
|
+
const fn = rec.function;
|
|
4594
|
+
const idx = numberValue(rec.index) ?? messageToolCalls.indexOf(tc);
|
|
4595
|
+
const key = `openai:${idx}`;
|
|
4596
|
+
const acc = toolCalls.get(key) ?? { argsRaw: "", source: "openai", finalized: false };
|
|
4597
|
+
const id = stringValue(rec.id);
|
|
4598
|
+
if (id) acc.id = id;
|
|
4599
|
+
const name = stringValue(fn?.name);
|
|
4600
|
+
if (name) acc.name = name;
|
|
4601
|
+
const args = stringValue(fn?.arguments);
|
|
4602
|
+
if (args) acc.argsRaw += args;
|
|
4603
|
+
acc.finalized = true;
|
|
4604
|
+
toolCalls.set(key, acc);
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
const finishReason = stringValue(choice?.finish_reason);
|
|
4608
|
+
if (finishReason === "tool_calls") {
|
|
4609
|
+
for (const [key, acc] of toolCalls) {
|
|
4610
|
+
if (acc.source === "openai" && !acc.finalized) acc.finalized = true;
|
|
4611
|
+
toolCalls.set(key, acc);
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
const eventType = stringValue(parsed.type);
|
|
4615
|
+
if (eventType === "content_block_start") {
|
|
4616
|
+
const block = parsed.content_block;
|
|
4617
|
+
if (block && stringValue(block.type) === "tool_use") {
|
|
4618
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4619
|
+
const key = `anthropic:${idx}`;
|
|
4620
|
+
toolCalls.set(key, {
|
|
4621
|
+
id: stringValue(block.id),
|
|
4622
|
+
name: stringValue(block.name),
|
|
4623
|
+
argsRaw: "",
|
|
4624
|
+
source: "anthropic",
|
|
4625
|
+
finalized: false
|
|
4626
|
+
});
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
if (eventType === "content_block_delta") {
|
|
4630
|
+
const d = parsed.delta;
|
|
4631
|
+
const dType = stringValue(d?.type);
|
|
4632
|
+
if (dType === "input_json_delta") {
|
|
4633
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4634
|
+
const key = `anthropic:${idx}`;
|
|
4635
|
+
const acc = toolCalls.get(key);
|
|
4636
|
+
if (acc) {
|
|
4637
|
+
const partial = stringValue(d?.partial_json) ?? "";
|
|
4638
|
+
acc.argsRaw += partial;
|
|
4639
|
+
toolCalls.set(key, acc);
|
|
4640
|
+
}
|
|
4641
|
+
} else {
|
|
4642
|
+
const text2 = stringValue(d?.text);
|
|
4643
|
+
if (text2) {
|
|
4644
|
+
yield {
|
|
4645
|
+
type: "text_delta",
|
|
4646
|
+
task: context.task,
|
|
4647
|
+
session: context.session,
|
|
4648
|
+
text: text2,
|
|
4649
|
+
timestamp: nowIso()
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (eventType === "content_block_stop") {
|
|
4655
|
+
const idx = numberValue(parsed.index) ?? 0;
|
|
4656
|
+
const key = `anthropic:${idx}`;
|
|
4657
|
+
const acc = toolCalls.get(key);
|
|
4658
|
+
if (acc) {
|
|
4659
|
+
acc.finalized = true;
|
|
4660
|
+
toolCalls.set(key, acc);
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
for (const event of drainFinalizedToolCalls(toolCalls, context)) yield event;
|
|
4664
|
+
const text = stringValue(delta?.content) ?? stringValue(message?.content) ?? stringValue(parsed.text);
|
|
4665
|
+
if (text) {
|
|
4666
|
+
yield {
|
|
4667
|
+
type: "text_delta",
|
|
4668
|
+
task: context.task,
|
|
4669
|
+
session: context.session,
|
|
4670
|
+
text,
|
|
4671
|
+
timestamp: nowIso()
|
|
4672
|
+
};
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
const mapped = mapCommonBackendEvent(parsed, context);
|
|
4676
|
+
if (mapped) yield mapped;
|
|
4677
|
+
}
|
|
4678
|
+
function* drainFinalizedToolCalls(toolCalls, context) {
|
|
4679
|
+
for (const [key, acc] of toolCalls) {
|
|
4680
|
+
if (!acc.finalized) continue;
|
|
4681
|
+
toolCalls.delete(key);
|
|
4682
|
+
yield buildToolCallEvent(acc, context);
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
function* flushPendingToolCalls(toolCalls, context) {
|
|
4686
|
+
for (const [key, acc] of toolCalls) {
|
|
4687
|
+
toolCalls.delete(key);
|
|
4688
|
+
yield buildToolCallEvent(acc, context);
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
function buildToolCallEvent(acc, context) {
|
|
4692
|
+
let args = acc.argsRaw;
|
|
4693
|
+
if (acc.argsRaw.length > 0) {
|
|
4694
|
+
try {
|
|
4695
|
+
args = JSON.parse(acc.argsRaw);
|
|
4696
|
+
} catch {
|
|
4697
|
+
args = acc.argsRaw;
|
|
4698
|
+
}
|
|
4699
|
+
} else {
|
|
4700
|
+
args = {};
|
|
4701
|
+
}
|
|
4702
|
+
return {
|
|
4703
|
+
type: "tool_call",
|
|
4704
|
+
task: context.task,
|
|
4705
|
+
session: context.session,
|
|
4706
|
+
toolName: acc.name ?? "tool",
|
|
4707
|
+
toolCallId: acc.id,
|
|
4708
|
+
args,
|
|
4709
|
+
timestamp: nowIso()
|
|
4710
|
+
};
|
|
4711
|
+
}
|
|
4712
|
+
function captureStreamUsage(parsed, usage) {
|
|
4713
|
+
const model = stringValue(parsed.model);
|
|
4714
|
+
if (model && !usage.model) usage.model = model;
|
|
4715
|
+
const openAiUsage = parsed.usage;
|
|
4716
|
+
if (openAiUsage && typeof openAiUsage === "object") {
|
|
4717
|
+
const promptTokens = numberValue(openAiUsage.prompt_tokens);
|
|
4718
|
+
const completionTokens = numberValue(openAiUsage.completion_tokens);
|
|
4719
|
+
const inputTokens = numberValue(openAiUsage.input_tokens);
|
|
4720
|
+
const outputTokens = numberValue(openAiUsage.output_tokens);
|
|
4721
|
+
if (promptTokens !== void 0) {
|
|
4722
|
+
usage.tokensIn = promptTokens;
|
|
4723
|
+
usage.saw = true;
|
|
4724
|
+
} else if (inputTokens !== void 0) {
|
|
4725
|
+
usage.tokensIn = (usage.tokensIn ?? 0) + inputTokens;
|
|
4726
|
+
usage.saw = true;
|
|
4727
|
+
}
|
|
4728
|
+
if (completionTokens !== void 0) {
|
|
4729
|
+
usage.tokensOut = completionTokens;
|
|
4730
|
+
usage.saw = true;
|
|
4731
|
+
} else if (outputTokens !== void 0) {
|
|
4732
|
+
usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
|
|
4733
|
+
usage.saw = true;
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
const type = stringValue(parsed.type);
|
|
4737
|
+
if (type === "message_start") {
|
|
4738
|
+
const message = parsed.message;
|
|
4739
|
+
const messageModel = stringValue(message?.model);
|
|
4740
|
+
if (messageModel && !usage.model) usage.model = messageModel;
|
|
4741
|
+
const messageUsage = message?.usage;
|
|
4742
|
+
const inputTokens = numberValue(messageUsage?.input_tokens);
|
|
4743
|
+
if (inputTokens !== void 0) {
|
|
4744
|
+
usage.tokensIn = inputTokens;
|
|
4745
|
+
usage.saw = true;
|
|
4746
|
+
}
|
|
4747
|
+
const outputTokens = numberValue(messageUsage?.output_tokens);
|
|
4748
|
+
if (outputTokens !== void 0) {
|
|
4749
|
+
usage.tokensOut = (usage.tokensOut ?? 0) + outputTokens;
|
|
4750
|
+
usage.saw = true;
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
if (type === "message_delta") {
|
|
4754
|
+
const delta = parsed.delta;
|
|
4755
|
+
const stopReason = stringValue(delta?.stop_reason);
|
|
4756
|
+
if (stopReason) usage.finishReason = stopReason;
|
|
4757
|
+
}
|
|
4758
|
+
const choices = parsed.choices;
|
|
4759
|
+
if (Array.isArray(choices)) {
|
|
4760
|
+
const finishReason = stringValue(
|
|
4761
|
+
choices[0]?.finish_reason
|
|
4762
|
+
);
|
|
4763
|
+
if (finishReason) usage.finishReason = finishReason;
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
function numberValue(value) {
|
|
4767
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4768
|
+
}
|
|
4769
|
+
function stringValue(value) {
|
|
4770
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
// src/runtime/stream-agent-turn.ts
|
|
4774
|
+
async function* streamAgentTurn(backend, prompt, opts = {}) {
|
|
4775
|
+
const label = backend.kind === "chat" ? backend.backend.kind : backend.kind;
|
|
4776
|
+
const task = { id: `turn-${crypto.randomUUID()}`, intent: prompt };
|
|
4777
|
+
const acc = { deltaText: "", input: 0, output: 0, costUsd: 0 };
|
|
4778
|
+
const deadline = deriveTurnSignal(opts.signal, opts.timeoutMs ?? 0);
|
|
4779
|
+
let session;
|
|
4780
|
+
try {
|
|
4781
|
+
session = await startTurnSession(backend, task, prompt, deadline.signal, label);
|
|
4782
|
+
yield { type: "backend_start", task, session, backend: label, timestamp: nowIso() };
|
|
4783
|
+
const inner = backend.kind === "chat" ? driveChatTurn(backend.backend, task, session, prompt, deadline.signal, acc) : driveBoxTurn(
|
|
4784
|
+
backend.kind === "box" ? backend.box : await inlineSandboxClient(backend.factory).create(),
|
|
4785
|
+
prompt,
|
|
4786
|
+
deadline.signal,
|
|
4787
|
+
backend.agentRunName ?? "agent",
|
|
4788
|
+
acc
|
|
4789
|
+
);
|
|
4790
|
+
for await (const event of inner) {
|
|
4791
|
+
yield event;
|
|
4792
|
+
throwIfAborted(deadline.signal);
|
|
4793
|
+
}
|
|
4794
|
+
yield buildFinalEvent(task, session, acc, { status: "completed", reason: "turn completed" });
|
|
4795
|
+
} catch (err) {
|
|
4796
|
+
const callerAborted = opts.signal?.aborted === true;
|
|
4797
|
+
const status = callerAborted ? "aborted" : "failed";
|
|
4798
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4799
|
+
const error = err instanceof BackendTransportError ? { kind: "transport", message, status: err.status, body: err.body } : { kind: "backend", message };
|
|
4800
|
+
yield {
|
|
4801
|
+
type: "backend_error",
|
|
4802
|
+
task,
|
|
4803
|
+
...session ? { session } : {},
|
|
4804
|
+
backend: label,
|
|
4805
|
+
message,
|
|
4806
|
+
recoverable: !callerAborted,
|
|
4807
|
+
error,
|
|
4808
|
+
timestamp: nowIso()
|
|
4809
|
+
};
|
|
4810
|
+
yield buildFinalEvent(task, session, acc, { status, reason: message, error });
|
|
4811
|
+
} finally {
|
|
4812
|
+
deadline.dispose();
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
async function collectAgentTurn(stream) {
|
|
4816
|
+
const events = [];
|
|
4817
|
+
for await (const event of stream) events.push(event);
|
|
4818
|
+
const final = events.at(-1);
|
|
4819
|
+
if (!final || final.type !== "final") {
|
|
4820
|
+
throw new Error(
|
|
4821
|
+
`collectAgentTurn: stream ended without a terminal 'final' event (last: ${final ? final.type : "none"})`
|
|
4822
|
+
);
|
|
4823
|
+
}
|
|
4824
|
+
const metadata = final.metadata ?? {};
|
|
4825
|
+
const tokenUsage = metadata.tokenUsage && typeof metadata.tokenUsage === "object" ? metadata.tokenUsage : {};
|
|
4826
|
+
const usage = {
|
|
4827
|
+
input: finiteNumber(tokenUsage.input) ?? 0,
|
|
4828
|
+
output: finiteNumber(tokenUsage.output) ?? 0
|
|
4829
|
+
};
|
|
4830
|
+
const costUsd = finiteNumber(metadata.costUsd);
|
|
4831
|
+
if (costUsd !== void 0) usage.costUsd = costUsd;
|
|
4832
|
+
if (typeof metadata.model === "string" && metadata.model.length > 0) {
|
|
4833
|
+
usage.model = metadata.model;
|
|
4834
|
+
}
|
|
4835
|
+
return {
|
|
4836
|
+
finalText: final.text ?? "",
|
|
4837
|
+
usage,
|
|
4838
|
+
events,
|
|
4839
|
+
status: final.status,
|
|
4840
|
+
...final.error ? { error: final.error } : {}
|
|
4841
|
+
};
|
|
4842
|
+
}
|
|
4843
|
+
async function startTurnSession(backend, task, prompt, signal, label) {
|
|
4844
|
+
if (backend.kind === "chat" && backend.backend.start) {
|
|
4845
|
+
return backend.backend.start(
|
|
4846
|
+
{ task, message: prompt },
|
|
4847
|
+
{ task, knowledge: emptyReadiness(task), signal }
|
|
4848
|
+
);
|
|
4849
|
+
}
|
|
4850
|
+
return newRuntimeSession(label);
|
|
4851
|
+
}
|
|
4852
|
+
async function* driveBoxTurn(box, prompt, signal, agentRunName, acc) {
|
|
4853
|
+
for await (const event of box.streamPrompt(prompt, { signal })) {
|
|
4854
|
+
const terminalText = terminalTextFromSandboxEvent(event);
|
|
4855
|
+
if (terminalText !== void 0) acc.terminalText = terminalText;
|
|
4856
|
+
const mapped = mapSandboxEvent(event, { agentRunName });
|
|
4857
|
+
if (!mapped) continue;
|
|
4858
|
+
foldEvent(mapped, acc, agentRunName);
|
|
4859
|
+
yield mapped;
|
|
4860
|
+
}
|
|
4861
|
+
}
|
|
4862
|
+
async function* driveChatTurn(backend, task, session, prompt, signal, acc) {
|
|
4863
|
+
const input = { task, message: prompt };
|
|
4864
|
+
const context = { task, knowledge: emptyReadiness(task), session, signal };
|
|
4865
|
+
for await (const raw of backend.stream(input, context)) {
|
|
4866
|
+
const event = normalizeBackendStreamEvent(raw, task, session);
|
|
4867
|
+
foldEvent(event, acc);
|
|
4868
|
+
yield event;
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
function foldEvent(event, acc, fallbackModelLabel) {
|
|
4872
|
+
if (event.type === "text_delta") {
|
|
4873
|
+
acc.deltaText += event.text;
|
|
4874
|
+
return;
|
|
4875
|
+
}
|
|
4876
|
+
if (event.type === "llm_call") {
|
|
4877
|
+
acc.input += event.tokensIn ?? 0;
|
|
4878
|
+
acc.output += event.tokensOut ?? 0;
|
|
4879
|
+
acc.costUsd += event.costUsd ?? 0;
|
|
4880
|
+
if (event.model && event.model !== fallbackModelLabel) acc.model = event.model;
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
function terminalTextFromSandboxEvent(event) {
|
|
4884
|
+
if (!event || typeof event !== "object") return void 0;
|
|
4885
|
+
const type = String(event.type ?? "");
|
|
4886
|
+
if (type !== "result" && type !== "done" && type !== "final") return void 0;
|
|
4887
|
+
const data = event.data && typeof event.data === "object" ? event.data : {};
|
|
4888
|
+
for (const key of ["finalText", "text", "response", "content"]) {
|
|
4889
|
+
const value = data[key];
|
|
4890
|
+
if (typeof value === "string") return value;
|
|
4891
|
+
}
|
|
4892
|
+
return void 0;
|
|
4893
|
+
}
|
|
4894
|
+
function buildFinalEvent(task, session, acc, outcome) {
|
|
4895
|
+
const finalText = acc.terminalText ?? acc.deltaText;
|
|
4896
|
+
return {
|
|
4897
|
+
type: "final",
|
|
4898
|
+
task,
|
|
4899
|
+
...session ? { session } : {},
|
|
4900
|
+
status: outcome.status,
|
|
4901
|
+
reason: outcome.reason,
|
|
4902
|
+
...finalText ? { text: finalText } : {},
|
|
4903
|
+
metadata: {
|
|
4904
|
+
tokenUsage: { input: acc.input, output: acc.output },
|
|
4905
|
+
...acc.costUsd > 0 ? { costUsd: acc.costUsd } : {},
|
|
4906
|
+
...acc.model ? { model: acc.model } : {}
|
|
4907
|
+
},
|
|
4908
|
+
...outcome.error ? { error: outcome.error } : {},
|
|
4909
|
+
timestamp: nowIso()
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
function emptyReadiness(task) {
|
|
4913
|
+
return scoreKnowledgeReadiness({ taskId: task.id, requirements: [] });
|
|
4914
|
+
}
|
|
4915
|
+
function finiteNumber(value) {
|
|
4916
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4917
|
+
}
|
|
4918
|
+
function throwIfAborted(signal) {
|
|
4919
|
+
if (!signal.aborted) return;
|
|
4920
|
+
throw signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason));
|
|
4921
|
+
}
|
|
4922
|
+
function deriveTurnSignal(callerSignal, timeoutMs) {
|
|
4923
|
+
const controller = new AbortController();
|
|
4924
|
+
const timer = timeoutMs > 0 ? setTimeout(
|
|
4925
|
+
() => controller.abort(new Error(`agent turn timed out after ${timeoutMs}ms`)),
|
|
4926
|
+
timeoutMs
|
|
4927
|
+
) : void 0;
|
|
4928
|
+
if (timer && typeof timer.unref === "function") {
|
|
4929
|
+
;
|
|
4930
|
+
timer.unref();
|
|
4931
|
+
}
|
|
4932
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason ?? new Error("agent turn aborted"));
|
|
4933
|
+
if (callerSignal) {
|
|
4934
|
+
if (callerSignal.aborted) onCallerAbort();
|
|
4935
|
+
else callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
4936
|
+
}
|
|
4937
|
+
return {
|
|
4938
|
+
signal: controller.signal,
|
|
4939
|
+
dispose: () => {
|
|
4940
|
+
if (timer) clearTimeout(timer);
|
|
4941
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
4942
|
+
}
|
|
4943
|
+
};
|
|
4944
|
+
}
|
|
4945
|
+
|
|
3857
4946
|
// src/runtime/supervise/detector-monitor.ts
|
|
3858
4947
|
import {
|
|
3859
4948
|
argHash,
|
|
@@ -4641,9 +5730,9 @@ function jjWorkspace(opts) {
|
|
|
4641
5730
|
}
|
|
4642
5731
|
async function runInWorkspace(ws, body, opts = {}) {
|
|
4643
5732
|
const { mkdtempSync: mkdtempSync2, rmSync: rmSync2 } = await import("fs");
|
|
4644
|
-
const { tmpdir:
|
|
4645
|
-
const { join:
|
|
4646
|
-
const dir = mkdtempSync2(
|
|
5733
|
+
const { tmpdir: tmpdir3 } = await import("os");
|
|
5734
|
+
const { join: join4 } = await import("path");
|
|
5735
|
+
const dir = mkdtempSync2(join4(tmpdir3(), opts.tmpPrefix ?? "ws-run-"));
|
|
4647
5736
|
try {
|
|
4648
5737
|
await ws.materialize(dir);
|
|
4649
5738
|
const r = await body(dir);
|
|
@@ -4662,6 +5751,14 @@ function tail(s) {
|
|
|
4662
5751
|
}
|
|
4663
5752
|
|
|
4664
5753
|
export {
|
|
5754
|
+
newRuntimeSession,
|
|
5755
|
+
touchSession,
|
|
5756
|
+
nowIso,
|
|
5757
|
+
InMemoryRuntimeSessionStore,
|
|
5758
|
+
createIterableBackend,
|
|
5759
|
+
createSandboxPromptBackend,
|
|
5760
|
+
createOpenAICompatibleBackend,
|
|
5761
|
+
normalizeBackendStreamEvent,
|
|
4665
5762
|
anytimeReport,
|
|
4666
5763
|
renderAnytimeTable,
|
|
4667
5764
|
defaultAuditorInstruction,
|
|
@@ -4676,15 +5773,19 @@ export {
|
|
|
4676
5773
|
stopSentinel,
|
|
4677
5774
|
sentinelCompletion,
|
|
4678
5775
|
deterministicCompletion,
|
|
5776
|
+
reportLoopUsage,
|
|
5777
|
+
loopCampaignDispatch,
|
|
5778
|
+
loopDispatch,
|
|
5779
|
+
inlineSandboxClient,
|
|
5780
|
+
resolveSandboxClient,
|
|
5781
|
+
naiveDriver,
|
|
5782
|
+
dumbDriver,
|
|
5783
|
+
defineLeaderboard,
|
|
4679
5784
|
defaultAnalystInstruction,
|
|
4680
5785
|
observe,
|
|
4681
5786
|
renderReport,
|
|
4682
5787
|
harvestCorpus,
|
|
4683
5788
|
inProcessSandboxClient,
|
|
4684
|
-
inlineSandboxClient,
|
|
4685
|
-
reportLoopUsage,
|
|
4686
|
-
loopCampaignDispatch,
|
|
4687
|
-
loopDispatch,
|
|
4688
5789
|
createMcpEnvironment,
|
|
4689
5790
|
assertTraceDerivedFindings,
|
|
4690
5791
|
createScopeAnalyst,
|
|
@@ -4709,7 +5810,6 @@ export {
|
|
|
4709
5810
|
trajectoryReport,
|
|
4710
5811
|
equalKOnCost,
|
|
4711
5812
|
promotionGate,
|
|
4712
|
-
resolveSandboxClient,
|
|
4713
5813
|
depthStrategy,
|
|
4714
5814
|
breadthStrategy,
|
|
4715
5815
|
sample,
|
|
@@ -4722,8 +5822,6 @@ export {
|
|
|
4722
5822
|
printBenchmarkReport,
|
|
4723
5823
|
SandboxRunAbortError,
|
|
4724
5824
|
openSandboxRun,
|
|
4725
|
-
naiveDriver,
|
|
4726
|
-
dumbDriver,
|
|
4727
5825
|
strategyAuthorContract,
|
|
4728
5826
|
assertStrategyContract,
|
|
4729
5827
|
authorStrategy,
|
|
@@ -4731,6 +5829,8 @@ export {
|
|
|
4731
5829
|
pickChampion,
|
|
4732
5830
|
selectChampion,
|
|
4733
5831
|
runStrategyEvolution,
|
|
5832
|
+
streamAgentTurn,
|
|
5833
|
+
collectAgentTurn,
|
|
4734
5834
|
defaultToolDetectors,
|
|
4735
5835
|
watchTrace,
|
|
4736
5836
|
runCoderChecks,
|
|
@@ -4751,4 +5851,4 @@ export {
|
|
|
4751
5851
|
computeFindingId,
|
|
4752
5852
|
makeFinding2 as makeFinding
|
|
4753
5853
|
};
|
|
4754
|
-
//# sourceMappingURL=chunk-
|
|
5854
|
+
//# sourceMappingURL=chunk-DLAEEF26.js.map
|