@duckcodeailabs/dql-cli 1.13.5 → 1.14.1
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/args.d.ts +17 -0
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +30 -0
- package/dist/args.js.map +1 -1
- package/dist/assets/dql-notebook/assets/{AgentLogPage-BzQOKjyV.js → AgentLogPage-BPz-UWFh.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildDialog-CTxha499.js → AiBuildDialog-BEl53WA_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiBuildResult--MuD_I6g.js → AiBuildResult-B4yfGTTZ.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AiSidePanel-DcI4PMJ1.js → AiSidePanel-CSZAAvuD.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AnalyticsHome-Dq54vjNz.js → AnalyticsHome-D5P6Ujwi.js} +1 -1
- package/dist/assets/dql-notebook/assets/{AppsView-0SlwWYex.js → AppsView-DQOwU9Cg.js} +4 -4
- package/dist/assets/dql-notebook/assets/{BlockStudio-CxxXZD3M.js → BlockStudio-B4ap0GdY.js} +1 -1
- package/dist/assets/dql-notebook/assets/{BusinessArtifactView-DxdAmeUN.js → BusinessArtifactView-BIfNI-0S.js} +1 -1
- package/dist/assets/dql-notebook/assets/{DbtFirstModelingPage-y81gFg_b.js → DbtFirstModelingPage-D72byb2g.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GitPage-GQtcwncb.js → GitPage-lQb1uXH2.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GlobalAiRail-DM4wkxR_.js → GlobalAiRail-CVECf6Xj.js} +1 -1
- package/dist/assets/dql-notebook/assets/{GovernedContextPage-B8Ft6JES.js → GovernedContextPage-trOyMCY6.js} +1 -1
- package/dist/assets/dql-notebook/assets/{HelpDocsPage-DnD4nMuF.js → HelpDocsPage-D8hLS5lE.js} +1 -1
- package/dist/assets/dql-notebook/assets/{HomePage-Ehb0ITj-.js → HomePage-eIkfBIep.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDAG-Lvjc2AQX.js → LineageDAG-CSqcbDrE.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDetailView-DX32pfbp.js → LineageDetailView-DJZZjZu-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineageDrawer-CFq3jPfk.js → LineageDrawer-BcIipIc3.js} +1 -1
- package/dist/assets/dql-notebook/assets/{LineagePathBreadcrumb-CVHXuHls.js → LineagePathBreadcrumb-CviIf8PN.js} +1 -1
- package/dist/assets/dql-notebook/assets/{MiniLineageGraph-BKaeT5Kt.js → MiniLineageGraph-vH_MY_Ju.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewBlockModal-DI-JDzFH.js → NewBlockModal-DbCQg-pj.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NewNotebookModal-Csw6eF74.js → NewNotebookModal-5Vp6XiuK.js} +1 -1
- package/dist/assets/dql-notebook/assets/{NotebookEditor-FEqs3789.js → NotebookEditor-DjqJS44s.js} +1 -1
- package/dist/assets/dql-notebook/assets/{ReadinessPage-Bk0S_MEA.js → ReadinessPage-BHGrC5ho.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SetupOnboarding-DyLaPFGn.js → SetupOnboarding-BfS9Tdx-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{SkillsPage-DLHyvhci.js → SkillsPage-BFH21wSj.js} +1 -1
- package/dist/assets/dql-notebook/assets/{TrustBadge-CIDLj2A6.js → TrustBadge-zm6g_SxZ.js} +1 -1
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel--oxmjlgr.js +88 -0
- package/dist/assets/dql-notebook/assets/{answer-to-notebook-CFEJHLvs.js → answer-to-notebook-DPhxIEzF.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-left-B-Zdcyvm.js → arrow-left-DNEb86Xc.js} +1 -1
- package/dist/assets/dql-notebook/assets/{arrow-right-Cn8TM7cp.js → arrow-right-DpPWbwaD.js} +1 -1
- package/dist/assets/dql-notebook/assets/{book-open-text-Bko2NNUs.js → book-open-text-D7s5jo4X.js} +1 -1
- package/dist/assets/dql-notebook/assets/{circle-x-AWJAmUBB.js → circle-x-Db3dXKg7.js} +1 -1
- package/dist/assets/dql-notebook/assets/{dagre.esm-Cl_ucrRh.js → dagre.esm-C7pppQ1a.js} +1 -1
- package/dist/assets/dql-notebook/assets/{external-link-DN57tb5f.js → external-link-BxwXitO_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{grip-vertical-BOWwFFva.js → grip-vertical-Dt4lkWRi.js} +1 -1
- package/dist/assets/dql-notebook/assets/{index-Ck-wqvV2.js → index-DKo-bwNw.js} +4 -4
- package/dist/assets/dql-notebook/assets/{link-2-xUhbfBs8.js → link-2-Dfo2P6wi.js} +1 -1
- package/dist/assets/dql-notebook/assets/{list-tree-BDWcBr67.js → list-tree-DiTmIWAL.js} +1 -1
- package/dist/assets/dql-notebook/assets/{minimize-2-CEeNoMTS.js → minimize-2-CMTAkPzL.js} +1 -1
- package/dist/assets/dql-notebook/assets/{panel-right-open-DnSeUvOY.js → panel-right-open-DwYr7FW4.js} +1 -1
- package/dist/assets/dql-notebook/assets/{play-EoDj8-fa.js → play-BXhHYQ4x.js} +1 -1
- package/dist/assets/dql-notebook/assets/{rotate-ccw-BAMxWT9A.js → rotate-ccw-BNi6F8pl.js} +1 -1
- package/dist/assets/dql-notebook/assets/{semantic-fields-BwLOs1kl.js → semantic-fields-CNOGysAy.js} +1 -1
- package/dist/assets/dql-notebook/assets/{sliders-horizontal-BYkm8SWW.js → sliders-horizontal-Ec5MUMUW.js} +1 -1
- package/dist/assets/dql-notebook/assets/{star-sVexqNCs.js → star-B9leDkp_.js} +1 -1
- package/dist/assets/dql-notebook/assets/{triangle-alert-D2njv4o4.js → triangle-alert-BefTYCzx.js} +1 -1
- package/dist/assets/dql-notebook/assets/{upload-CXTMxIA8.js → upload-SPiOM2tQ.js} +1 -1
- package/dist/assets/dql-notebook/assets/{usePersistedAgentThreadId-CPDeaAgL.js → usePersistedAgentThreadId-C4foXeiQ.js} +4 -4
- package/dist/assets/dql-notebook/assets/{user-round-zVsI_uud.js → user-round-comGmyw-.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wand-sparkles-BMIwIzXT.js → wand-sparkles-BffR4dF8.js} +1 -1
- package/dist/assets/dql-notebook/assets/{workflow-Dj9y1sNk.js → workflow-ChmPTEzH.js} +1 -1
- package/dist/assets/dql-notebook/assets/{wrench-DPQi6zrs.js → wrench-DovaG_ze.js} +1 -1
- package/dist/assets/dql-notebook/assets/{x-XhhbtinL.js → x-nRx91AgW.js} +1 -1
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/commands/agent-eval-cassette.d.ts +73 -0
- package/dist/commands/agent-eval-cassette.d.ts.map +1 -0
- package/dist/commands/agent-eval-cassette.js +170 -0
- package/dist/commands/agent-eval-cassette.js.map +1 -0
- package/dist/commands/agent-eval-runtime.d.ts +97 -0
- package/dist/commands/agent-eval-runtime.d.ts.map +1 -0
- package/dist/commands/agent-eval-runtime.js +155 -0
- package/dist/commands/agent-eval-runtime.js.map +1 -0
- package/dist/commands/agent.d.ts +115 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +289 -62
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/eval.d.ts +15 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +32 -3
- package/dist/commands/eval.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/analyst-loop-tools.d.ts +19 -0
- package/dist/llm/analyst-loop-tools.d.ts.map +1 -0
- package/dist/llm/analyst-loop-tools.js +56 -0
- package/dist/llm/analyst-loop-tools.js.map +1 -0
- package/dist/llm/providers/dql-agent-provider.d.ts +24 -1
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +343 -10
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/llm/types.d.ts +19 -1
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/local-runtime.d.ts +132 -2
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +1120 -184
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/package.json +10 -10
- package/dist/assets/dql-notebook/assets/UnifiedAgentRunPanel-C0oKTU6G.js +0 -88
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&family=Kalam:wght@400;700&family=Caveat:wght@400;600;700&display=swap" rel="stylesheet" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-DKo-bwNw.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/react-CRB3T2We.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-B3shyZsg.css">
|
|
13
13
|
</head>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import type { AgentMessage, AgentProvider } from '@duckcodeailabs/dql-agent';
|
|
3
|
+
export type CassetteMode = 'record' | 'replay' | 'live';
|
|
4
|
+
/** One recorded tool call, so replay can re-run it against the live tools. */
|
|
5
|
+
export interface CassetteToolCall {
|
|
6
|
+
name: string;
|
|
7
|
+
args: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface CassetteEntry {
|
|
10
|
+
key: string;
|
|
11
|
+
operation: 'generate' | 'generate_with_tools';
|
|
12
|
+
/** Non-secret provenance, so a stale cassette is identifiable by eye. */
|
|
13
|
+
recordedAt: string;
|
|
14
|
+
providerName: string;
|
|
15
|
+
/** The model's final text. */
|
|
16
|
+
text: string;
|
|
17
|
+
/** Tool calls the model made, in order (generate_with_tools only). */
|
|
18
|
+
toolCalls?: CassetteToolCall[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Stable key for one provider dispatch.
|
|
22
|
+
*
|
|
23
|
+
* Deliberately includes the full message list: the prompt IS the input, and two
|
|
24
|
+
* turns that differ only in retrieved context are genuinely different calls.
|
|
25
|
+
* Tool NAMES are included but not their schemas — a description edit should not
|
|
26
|
+
* invalidate every cassette in the suite.
|
|
27
|
+
*/
|
|
28
|
+
export declare function cassetteKey(input: {
|
|
29
|
+
providerName: string;
|
|
30
|
+
operation: CassetteEntry['operation'];
|
|
31
|
+
messages: AgentMessage[];
|
|
32
|
+
toolNames?: string[];
|
|
33
|
+
options?: {
|
|
34
|
+
reasoningEffort?: unknown;
|
|
35
|
+
maxTokens?: unknown;
|
|
36
|
+
temperature?: unknown;
|
|
37
|
+
} | undefined;
|
|
38
|
+
}): string;
|
|
39
|
+
export declare class CassetteStore {
|
|
40
|
+
private readonly dir;
|
|
41
|
+
private readonly loaded;
|
|
42
|
+
constructor(dir: string);
|
|
43
|
+
get(key: string): CassetteEntry | undefined;
|
|
44
|
+
put(entry: CassetteEntry): void;
|
|
45
|
+
size(): number;
|
|
46
|
+
}
|
|
47
|
+
export declare class CassetteMissError extends Error {
|
|
48
|
+
readonly key: string;
|
|
49
|
+
constructor(key: string, operation: string);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Wrap a provider so its dispatches are recorded or replayed.
|
|
53
|
+
*
|
|
54
|
+
* On replay of a tool loop the recorded tool calls are RE-INVOKED against the
|
|
55
|
+
* live tools rather than stubbed. The model's choices stay frozen (that is the
|
|
56
|
+
* point), but compilation, validation, and execution still really happen — so
|
|
57
|
+
* the suite keeps catching a broken compiler or a wrong number, which a fully
|
|
58
|
+
* stubbed replay would sail straight past.
|
|
59
|
+
*/
|
|
60
|
+
export declare function withCassette(provider: AgentProvider, store: CassetteStore, mode: CassetteMode): AgentProvider;
|
|
61
|
+
/**
|
|
62
|
+
* Pick the cassette mode from the environment.
|
|
63
|
+
*
|
|
64
|
+
* Defaults to `replay` for ANY value that is not explicitly `record` or `live`,
|
|
65
|
+
* including a missing or misspelled one. If someone sets the cassette directory
|
|
66
|
+
* in CI and forgets the mode, the safe outcome is "never call a live model",
|
|
67
|
+
* not a surprise bill and a non-deterministic suite.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveCassetteModeFromEnv(env: Record<string, string | undefined>): CassetteMode;
|
|
70
|
+
/** Where a project's eval cassettes live. */
|
|
71
|
+
export declare function cassetteDirFor(projectRoot: string, suiteName: string): string;
|
|
72
|
+
export { dirname as _dirname };
|
|
73
|
+
//# sourceMappingURL=agent-eval-cassette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-eval-cassette.d.ts","sourceRoot":"","sources":["../../src/commands/agent-eval-cassette.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,OAAO,EAAQ,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EAId,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExD,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,UAAU,GAAG,qBAAqB,CAAC;IAC9C,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACtC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;CACjG,GAAG,MAAM,CAWT;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;gBAE/C,GAAG,EAAE,MAAM;IAevB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI3C,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAM/B,IAAI,IAAI,MAAM;CAGf;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBACT,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAQ3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,YAAY,GACjB,aAAa,CAiEf;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,YAAY,CAGhG;AAED,6CAA6C;AAC7C,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider cassettes — record a suite once against a real model, replay it
|
|
3
|
+
* offline forever.
|
|
4
|
+
*
|
|
5
|
+
* A provider-backed eval is the only kind that measures the product users get:
|
|
6
|
+
* without one, `mayAssumeInterpretation` is false and every ambiguous question
|
|
7
|
+
* clarifies by design (AGT-017), which makes a clarification rate an artifact.
|
|
8
|
+
* But calling a real model on every PR is slow, costly, and non-deterministic —
|
|
9
|
+
* so the suite could never gate anything.
|
|
10
|
+
*
|
|
11
|
+
* Recording once and replaying fixes all three. Model drift then shows up as a
|
|
12
|
+
* reviewable diff when cassettes are re-recorded, instead of as a mystery
|
|
13
|
+
* regression on an unrelated pull request.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, join } from 'node:path';
|
|
18
|
+
/**
|
|
19
|
+
* Stable key for one provider dispatch.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately includes the full message list: the prompt IS the input, and two
|
|
22
|
+
* turns that differ only in retrieved context are genuinely different calls.
|
|
23
|
+
* Tool NAMES are included but not their schemas — a description edit should not
|
|
24
|
+
* invalidate every cassette in the suite.
|
|
25
|
+
*/
|
|
26
|
+
export function cassetteKey(input) {
|
|
27
|
+
const canonical = JSON.stringify({
|
|
28
|
+
p: input.providerName,
|
|
29
|
+
o: input.operation,
|
|
30
|
+
m: input.messages.map((message) => ({ r: message.role, c: message.content })),
|
|
31
|
+
t: [...(input.toolNames ?? [])].sort(),
|
|
32
|
+
e: input.options?.reasoningEffort ?? null,
|
|
33
|
+
x: input.options?.maxTokens ?? null,
|
|
34
|
+
z: input.options?.temperature ?? null,
|
|
35
|
+
});
|
|
36
|
+
return createHash('sha256').update(canonical).digest('hex').slice(0, 32);
|
|
37
|
+
}
|
|
38
|
+
export class CassetteStore {
|
|
39
|
+
dir;
|
|
40
|
+
loaded = new Map();
|
|
41
|
+
constructor(dir) {
|
|
42
|
+
this.dir = dir;
|
|
43
|
+
if (!existsSync(dir))
|
|
44
|
+
return;
|
|
45
|
+
for (const file of readdirSync(dir)) {
|
|
46
|
+
if (!file.endsWith('.json'))
|
|
47
|
+
continue;
|
|
48
|
+
try {
|
|
49
|
+
const entry = JSON.parse(readFileSync(join(dir, file), 'utf-8'));
|
|
50
|
+
if (entry?.key)
|
|
51
|
+
this.loaded.set(entry.key, entry);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// A corrupt cassette is skipped, not fatal: it re-records on the next
|
|
55
|
+
// --record run, and a replay miss reports the key that is missing.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
get(key) {
|
|
60
|
+
return this.loaded.get(key);
|
|
61
|
+
}
|
|
62
|
+
put(entry) {
|
|
63
|
+
this.loaded.set(entry.key, entry);
|
|
64
|
+
mkdirSync(this.dir, { recursive: true });
|
|
65
|
+
writeFileSync(join(this.dir, `${entry.key}.json`), `${JSON.stringify(entry, null, 2)}\n`, 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
size() {
|
|
68
|
+
return this.loaded.size;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export class CassetteMissError extends Error {
|
|
72
|
+
key;
|
|
73
|
+
constructor(key, operation) {
|
|
74
|
+
super(`No cassette for ${operation} (${key}). CI replays recorded provider responses and never calls a live model; `
|
|
75
|
+
+ 're-record with --record against a real provider and commit the new cassettes.');
|
|
76
|
+
this.name = 'CassetteMissError';
|
|
77
|
+
this.key = key;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wrap a provider so its dispatches are recorded or replayed.
|
|
82
|
+
*
|
|
83
|
+
* On replay of a tool loop the recorded tool calls are RE-INVOKED against the
|
|
84
|
+
* live tools rather than stubbed. The model's choices stay frozen (that is the
|
|
85
|
+
* point), but compilation, validation, and execution still really happen — so
|
|
86
|
+
* the suite keeps catching a broken compiler or a wrong number, which a fully
|
|
87
|
+
* stubbed replay would sail straight past.
|
|
88
|
+
*/
|
|
89
|
+
export function withCassette(provider, store, mode) {
|
|
90
|
+
if (mode === 'live')
|
|
91
|
+
return provider;
|
|
92
|
+
const wrapped = {
|
|
93
|
+
name: provider.name,
|
|
94
|
+
available: () => (mode === 'replay' ? Promise.resolve(true) : provider.available()),
|
|
95
|
+
async generate(messages, options) {
|
|
96
|
+
const key = cassetteKey({ providerName: provider.name, operation: 'generate', messages, options });
|
|
97
|
+
const hit = store.get(key);
|
|
98
|
+
if (hit)
|
|
99
|
+
return hit.text;
|
|
100
|
+
if (mode === 'replay')
|
|
101
|
+
throw new CassetteMissError(key, 'generate');
|
|
102
|
+
const text = await provider.generate(messages, options);
|
|
103
|
+
store.put({
|
|
104
|
+
key, operation: 'generate', text,
|
|
105
|
+
providerName: provider.name, recordedAt: new Date().toISOString(),
|
|
106
|
+
});
|
|
107
|
+
return text;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
if (provider.generateWithTools) {
|
|
111
|
+
wrapped.generateWithTools = async (messages, tools, options) => {
|
|
112
|
+
const key = cassetteKey({
|
|
113
|
+
providerName: provider.name,
|
|
114
|
+
operation: 'generate_with_tools',
|
|
115
|
+
messages,
|
|
116
|
+
toolNames: tools.map((tool) => tool.name),
|
|
117
|
+
options: options,
|
|
118
|
+
});
|
|
119
|
+
const hit = store.get(key);
|
|
120
|
+
if (hit) {
|
|
121
|
+
const byName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
122
|
+
for (const call of hit.toolCalls ?? []) {
|
|
123
|
+
const tool = byName.get(call.name);
|
|
124
|
+
// A cassette recorded against a tool set that no longer has this tool
|
|
125
|
+
// is stale in a way the run should surface, not paper over.
|
|
126
|
+
if (!tool)
|
|
127
|
+
continue;
|
|
128
|
+
await tool.run(call.args).catch(() => undefined);
|
|
129
|
+
}
|
|
130
|
+
return hit.text;
|
|
131
|
+
}
|
|
132
|
+
if (mode === 'replay')
|
|
133
|
+
throw new CassetteMissError(key, 'generate_with_tools');
|
|
134
|
+
const observed = [];
|
|
135
|
+
const recordingTools = tools.map((tool) => ({
|
|
136
|
+
...tool,
|
|
137
|
+
run: async (args) => {
|
|
138
|
+
observed.push({ name: tool.name, args });
|
|
139
|
+
return tool.run(args);
|
|
140
|
+
},
|
|
141
|
+
}));
|
|
142
|
+
const text = await provider.generateWithTools(messages, recordingTools, options);
|
|
143
|
+
store.put({
|
|
144
|
+
key, operation: 'generate_with_tools', text, toolCalls: observed,
|
|
145
|
+
providerName: provider.name, recordedAt: new Date().toISOString(),
|
|
146
|
+
});
|
|
147
|
+
return text;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return wrapped;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Pick the cassette mode from the environment.
|
|
154
|
+
*
|
|
155
|
+
* Defaults to `replay` for ANY value that is not explicitly `record` or `live`,
|
|
156
|
+
* including a missing or misspelled one. If someone sets the cassette directory
|
|
157
|
+
* in CI and forgets the mode, the safe outcome is "never call a live model",
|
|
158
|
+
* not a surprise bill and a non-deterministic suite.
|
|
159
|
+
*/
|
|
160
|
+
export function resolveCassetteModeFromEnv(env) {
|
|
161
|
+
const raw = env.DQL_EVAL_CASSETTE_MODE;
|
|
162
|
+
return raw === 'record' || raw === 'live' ? raw : 'replay';
|
|
163
|
+
}
|
|
164
|
+
/** Where a project's eval cassettes live. */
|
|
165
|
+
export function cassetteDirFor(projectRoot, suiteName) {
|
|
166
|
+
const safe = suiteName.replace(/[^a-z0-9._-]+/gi, '-').replace(/\.(ya?ml)$/i, '');
|
|
167
|
+
return join(projectRoot, 'test-cassettes', safe);
|
|
168
|
+
}
|
|
169
|
+
export { dirname as _dirname };
|
|
170
|
+
//# sourceMappingURL=agent-eval-cassette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-eval-cassette.js","sourceRoot":"","sources":["../../src/commands/agent-eval-cassette.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime driver for `dql agent eval`.
|
|
3
|
+
*
|
|
4
|
+
* The in-process driver calls `answer()` directly, which skips
|
|
5
|
+
* `createHybridRouter`, `AgentRunEngine`, `enforceOrdinaryAnalyticalPlanBoundary`
|
|
6
|
+
* and `defaultAgentRunGates`. Every dead-end this harness exists to measure lives
|
|
7
|
+
* in that skipped half — the router can block before the cascade runs, the engine
|
|
8
|
+
* synthesizes its own modeling-gap block, and the gates declare refusal codes
|
|
9
|
+
* terminal. Measuring the answer loop alone reports a cleaner picture than users
|
|
10
|
+
* experience.
|
|
11
|
+
*
|
|
12
|
+
* This driver posts the same body the Ask panel posts and scores the persisted
|
|
13
|
+
* `AgentRun`, so the suite exercises routing, gates, and the transport projection
|
|
14
|
+
* as a real end-to-end contract.
|
|
15
|
+
*/
|
|
16
|
+
import type { AgentAnswer, AgentRun, AgentRunRoute, AgentRunStatus, AgentRunTrustState } from '@duckcodeailabs/dql-agent';
|
|
17
|
+
/** Eval-facing view of a run, shaped like the fields the scorer already reads. */
|
|
18
|
+
export interface RuntimeDrivenRun {
|
|
19
|
+
kind: 'certified' | 'uncertified' | 'no_answer';
|
|
20
|
+
route?: 'certified' | 'generated_sql' | 'research' | 'clarify';
|
|
21
|
+
runRoute: AgentRunRoute;
|
|
22
|
+
status: AgentRunStatus;
|
|
23
|
+
trustState: AgentRunTrustState;
|
|
24
|
+
answer?: string;
|
|
25
|
+
proposedSql?: string;
|
|
26
|
+
rows?: unknown[];
|
|
27
|
+
clarificationOptionCount: number;
|
|
28
|
+
refusalCode?: string;
|
|
29
|
+
runId: string;
|
|
30
|
+
/**
|
|
31
|
+
* A conversational reply (greeting, capability, polite redirect). It asserts
|
|
32
|
+
* nothing about the data, so it is neither an answer nor a refusal — scoring it
|
|
33
|
+
* as either misreads the correct outcome for an out-of-scope question.
|
|
34
|
+
*/
|
|
35
|
+
conversational: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Did the meaning resolver actually run for this turn?
|
|
38
|
+
*
|
|
39
|
+
* With no provider configured it cannot, and `mayAssumeInterpretation` goes
|
|
40
|
+
* false (AGT-017) — DQL deliberately refuses to settle `booked_revenue` vs
|
|
41
|
+
* `billed_revenue` by lexical rank with semantic judgment switched off. Every
|
|
42
|
+
* ambiguous question then clarifies. That is correct behaviour, but it makes a
|
|
43
|
+
* clarification rate measured without a provider meaningless, so the harness
|
|
44
|
+
* has to be able to say so.
|
|
45
|
+
*/
|
|
46
|
+
meaningResolved: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Did this run decline to produce a usable answer?
|
|
50
|
+
*
|
|
51
|
+
* `needs_review` is deliberately NOT a refusal: a review-required generated
|
|
52
|
+
* answer is the intended outcome when no governed plan froze, and counting it as
|
|
53
|
+
* a refusal would make the false-refusal metric punish the very behaviour the
|
|
54
|
+
* cascade is supposed to produce.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runtimeRunRefused(run: Pick<AgentRun, 'route' | 'status'>): boolean;
|
|
57
|
+
/** Map an engine route onto the vocabulary the eval cases already use. */
|
|
58
|
+
export declare function evalRouteForRun(route: AgentRunRoute): RuntimeDrivenRun['route'];
|
|
59
|
+
/**
|
|
60
|
+
* Collapse a run into the coarse `kind` the existing expectations assert.
|
|
61
|
+
* Certification follows TRUST, not route: a certified route that degraded to a
|
|
62
|
+
* review-required answer must not be scored as certified.
|
|
63
|
+
*/
|
|
64
|
+
export declare function evalKindForRun(run: Pick<AgentRun, 'route' | 'status' | 'trustState'>): RuntimeDrivenRun['kind'];
|
|
65
|
+
/** Pull SQL and result rows out of the run's artifacts, when it produced any. */
|
|
66
|
+
export declare function runtimeRunOutputs(run: AgentRun): {
|
|
67
|
+
proposedSql?: string;
|
|
68
|
+
rows?: unknown[];
|
|
69
|
+
};
|
|
70
|
+
export declare function projectRuntimeRun(run: AgentRun): RuntimeDrivenRun;
|
|
71
|
+
export interface RuntimeDriverOptions {
|
|
72
|
+
runtimeBase: string;
|
|
73
|
+
question: string;
|
|
74
|
+
requestedMode?: 'ask' | 'auto' | 'research';
|
|
75
|
+
threadId?: string;
|
|
76
|
+
timeoutMs?: number;
|
|
77
|
+
fetchImpl?: typeof fetch;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Post one question to a running `dql serve` and return the persisted run.
|
|
81
|
+
*
|
|
82
|
+
* Failures are surfaced rather than swallowed: a harness that silently scores a
|
|
83
|
+
* transport error as a refusal would report a false-refusal spike that no code
|
|
84
|
+
* change caused.
|
|
85
|
+
*/
|
|
86
|
+
export declare function driveViaRuntime(options: RuntimeDriverOptions): Promise<AgentRun>;
|
|
87
|
+
/**
|
|
88
|
+
* Adapt a persisted run into the `AgentAnswer` shape the existing scorer reads,
|
|
89
|
+
* so both drivers share one set of assertions.
|
|
90
|
+
*
|
|
91
|
+
* Fields the run genuinely does not carry are left UNDEFINED rather than
|
|
92
|
+
* defaulted. An expectation that references one then fails loudly instead of
|
|
93
|
+
* passing against a fabricated value — a harness that quietly invents
|
|
94
|
+
* `sourceTier` would report agreement it never observed.
|
|
95
|
+
*/
|
|
96
|
+
export declare function answerFromRuntimeRun(run: AgentRun): AgentAnswer;
|
|
97
|
+
//# sourceMappingURL=agent-eval-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-eval-runtime.d.ts","sourceRoot":"","sources":["../../src/commands/agent-eval-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE1H,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,KAAK,CAAC,EAAE,WAAW,GAAG,eAAe,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/D,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,wBAAwB,EAAE,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;;;;;OASG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,OAAO,CAKlF;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAU/E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAI/G;AAYD,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CAe3F;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,gBAAgB,CAgBjE;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8BtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,GAAG,WAAW,CAuB/D"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did this run decline to produce a usable answer?
|
|
3
|
+
*
|
|
4
|
+
* `needs_review` is deliberately NOT a refusal: a review-required generated
|
|
5
|
+
* answer is the intended outcome when no governed plan froze, and counting it as
|
|
6
|
+
* a refusal would make the false-refusal metric punish the very behaviour the
|
|
7
|
+
* cascade is supposed to produce.
|
|
8
|
+
*/
|
|
9
|
+
export function runtimeRunRefused(run) {
|
|
10
|
+
return run.route === 'blocked'
|
|
11
|
+
|| run.route === 'clarify'
|
|
12
|
+
|| run.status === 'blocked'
|
|
13
|
+
|| run.status === 'needs_clarification';
|
|
14
|
+
}
|
|
15
|
+
/** Map an engine route onto the vocabulary the eval cases already use. */
|
|
16
|
+
export function evalRouteForRun(route) {
|
|
17
|
+
switch (route) {
|
|
18
|
+
case 'certified_answer': return 'certified';
|
|
19
|
+
case 'semantic_answer': return 'certified';
|
|
20
|
+
case 'generated_answer': return 'generated_sql';
|
|
21
|
+
case 'research': return 'research';
|
|
22
|
+
case 'clarify':
|
|
23
|
+
case 'blocked': return 'clarify';
|
|
24
|
+
default: return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Collapse a run into the coarse `kind` the existing expectations assert.
|
|
29
|
+
* Certification follows TRUST, not route: a certified route that degraded to a
|
|
30
|
+
* review-required answer must not be scored as certified.
|
|
31
|
+
*/
|
|
32
|
+
export function evalKindForRun(run) {
|
|
33
|
+
if (runtimeRunRefused(run))
|
|
34
|
+
return 'no_answer';
|
|
35
|
+
if (run.trustState === 'certified')
|
|
36
|
+
return 'certified';
|
|
37
|
+
return 'uncertified';
|
|
38
|
+
}
|
|
39
|
+
function firstArtifactPayload(run, kinds) {
|
|
40
|
+
for (const artifact of run.artifacts ?? []) {
|
|
41
|
+
if (!kinds.includes(String(artifact.kind)))
|
|
42
|
+
continue;
|
|
43
|
+
if (artifact.payload && typeof artifact.payload === 'object') {
|
|
44
|
+
return artifact.payload;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/** Pull SQL and result rows out of the run's artifacts, when it produced any. */
|
|
50
|
+
export function runtimeRunOutputs(run) {
|
|
51
|
+
const payload = firstArtifactPayload(run, ['sql_cell', 'dql_block', 'generated_answer', 'answer', 'result']);
|
|
52
|
+
const sql = typeof payload?.sql === 'string'
|
|
53
|
+
? payload.sql
|
|
54
|
+
: typeof payload?.compiledSql === 'string' ? payload.compiledSql : undefined;
|
|
55
|
+
const resultRecord = payload?.result && typeof payload.result === 'object'
|
|
56
|
+
? payload.result
|
|
57
|
+
: undefined;
|
|
58
|
+
const rows = Array.isArray(resultRecord?.rows)
|
|
59
|
+
? resultRecord.rows
|
|
60
|
+
: Array.isArray(payload?.rows) ? payload.rows : undefined;
|
|
61
|
+
return {
|
|
62
|
+
...(sql ? { proposedSql: sql } : {}),
|
|
63
|
+
...(rows ? { rows } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function projectRuntimeRun(run) {
|
|
67
|
+
const refusalEvaluation = (run.evaluations ?? []).find((evaluation) => !evaluation.passed);
|
|
68
|
+
return {
|
|
69
|
+
kind: evalKindForRun(run),
|
|
70
|
+
route: evalRouteForRun(run.route),
|
|
71
|
+
runRoute: run.route,
|
|
72
|
+
status: run.status,
|
|
73
|
+
trustState: run.trustState,
|
|
74
|
+
clarificationOptionCount: run.clarificationOptions?.length ?? 0,
|
|
75
|
+
runId: run.id,
|
|
76
|
+
conversational: run.route === 'conversation' || run.answerKind === 'conversational',
|
|
77
|
+
meaningResolved: Boolean(run.routeDecision?.meaningResolution),
|
|
78
|
+
...(run.answer ? { answer: run.answer } : {}),
|
|
79
|
+
...(refusalEvaluation?.id ? { refusalCode: refusalEvaluation.id } : {}),
|
|
80
|
+
...runtimeRunOutputs(run),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Post one question to a running `dql serve` and return the persisted run.
|
|
85
|
+
*
|
|
86
|
+
* Failures are surfaced rather than swallowed: a harness that silently scores a
|
|
87
|
+
* transport error as a refusal would report a false-refusal spike that no code
|
|
88
|
+
* change caused.
|
|
89
|
+
*/
|
|
90
|
+
export async function driveViaRuntime(options) {
|
|
91
|
+
const base = options.runtimeBase.replace(/\/$/, '');
|
|
92
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 120_000);
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetchImpl(`${base}/api/agent-runs`, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
question: options.question,
|
|
101
|
+
requestedMode: options.requestedMode ?? 'ask',
|
|
102
|
+
...(options.threadId ? { threadId: options.threadId } : {}),
|
|
103
|
+
}),
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error(`Runtime returned ${response.status}: ${(await response.text()).slice(0, 400)}`);
|
|
108
|
+
}
|
|
109
|
+
// The endpoint wraps the record (`{ run }`); older/streamed paths return it
|
|
110
|
+
// bare. Accept both rather than guessing one and failing the whole suite.
|
|
111
|
+
const body = await response.json();
|
|
112
|
+
const run = body.run ?? body;
|
|
113
|
+
if (!run || typeof run.id !== 'string') {
|
|
114
|
+
throw new Error(`Runtime response was not an agent run: ${JSON.stringify(body).slice(0, 200)}`);
|
|
115
|
+
}
|
|
116
|
+
return run;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Adapt a persisted run into the `AgentAnswer` shape the existing scorer reads,
|
|
124
|
+
* so both drivers share one set of assertions.
|
|
125
|
+
*
|
|
126
|
+
* Fields the run genuinely does not carry are left UNDEFINED rather than
|
|
127
|
+
* defaulted. An expectation that references one then fails loudly instead of
|
|
128
|
+
* passing against a fabricated value — a harness that quietly invents
|
|
129
|
+
* `sourceTier` would report agreement it never observed.
|
|
130
|
+
*/
|
|
131
|
+
export function answerFromRuntimeRun(run) {
|
|
132
|
+
const projected = projectRuntimeRun(run);
|
|
133
|
+
const certification = projected.kind === 'no_answer'
|
|
134
|
+
? 'analyst_review_required'
|
|
135
|
+
: run.trustState === 'certified'
|
|
136
|
+
? 'certified'
|
|
137
|
+
: run.trustState === 'review_required'
|
|
138
|
+
? 'analyst_review_required'
|
|
139
|
+
: 'ai_generated';
|
|
140
|
+
return {
|
|
141
|
+
kind: projected.kind,
|
|
142
|
+
certification,
|
|
143
|
+
reviewStatus: run.trustState === 'review_required' ? 'analyst_review_required' : 'none',
|
|
144
|
+
text: run.answer ?? run.summary,
|
|
145
|
+
citations: [],
|
|
146
|
+
considered: [],
|
|
147
|
+
...(projected.proposedSql ? { proposedSql: projected.proposedSql } : {}),
|
|
148
|
+
...(projected.rows ? { result: { columns: [], rows: projected.rows, rowCount: projected.rows.length } } : {}),
|
|
149
|
+
...(projected.refusalCode ? { refusalCode: projected.refusalCode } : {}),
|
|
150
|
+
// Carried so the scorer can tell an answerable clarification from a dead end.
|
|
151
|
+
...(run.clarificationOptions?.length ? { clarificationOptions: run.clarificationOptions } : {}),
|
|
152
|
+
...(projected.conversational ? { answerKind: 'conversational' } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=agent-eval-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-eval-runtime.js","sourceRoot":"","sources":["../../src/commands/agent-eval-runtime.ts"],"names":[],"mappings":"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"}
|