@moda-ai/cli 1.55.72 → 1.55.74
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/README.md +11 -0
- package/dist/{agent-xesfknb0.js → agent-8jsrjbx5.js} +6 -6
- package/dist/{cli-gqkvypxh.js → cli-4qx5ax0a.js} +7 -7
- package/dist/{cli-ge6ty4hx.js → cli-82dggtv9.js} +2202 -248
- package/dist/{cli-9dwq3vyj.js → cli-s4j2m651.js} +2 -2
- package/dist/{cli-9ww72mrd.js → cli-y333w08f.js} +1 -1
- package/dist/cli.js +20 -20
- package/dist/{doctor-z29f8x00.js → doctor-8tenph7m.js} +4 -4
- package/dist/{harness-qvszhbrt.js → harness-acdx3zgk.js} +4 -4
- package/dist/{harness-github-actions-awcd5ns1.js → harness-github-actions-m26tapfj.js} +4 -4
- package/dist/{harness-report-3n7bg314.js → harness-report-1q7322xg.js} +1 -1
- package/dist/index-0wm7157z.js +33 -0
- package/dist/{index-badpwfw2.js → index-1wxmsrfe.js} +7 -7
- package/dist/{index-tpwztst3.js → index-cpz85prt.js} +6 -4
- package/dist/{production-intelligence-vsjhv4n2.js → production-intelligence-f9sd68te.js} +2 -2
- package/dist/{prompt-attribution-t62yzfxb.js → prompt-attribution-b0ccme35.js} +4 -4
- package/dist/prompt-source-mcp.js +4 -4
- package/dist/{prompts-x74bqatj.js → prompts-wqkgnb1b.js} +3 -3
- package/dist/{skills-pqhvfc1r.js → skills-knpeqryb.js} +3 -3
- package/dist/{sync-kbhbrksr.js → sync-q0rm0s5s.js} +6 -6
- package/package.json +2 -1
- package/skills/integration/index.json +2 -2
- package/dist/{cli-k8hzbxtn.js → cli-1smcq5pf.js} +3 -3
- package/dist/{cli-qq4gd126.js → cli-6hpfw3vf.js} +3 -3
|
@@ -18,10 +18,12 @@ import {
|
|
|
18
18
|
artifactHasBody,
|
|
19
19
|
buildImportGraphReachability,
|
|
20
20
|
createHarnessReport,
|
|
21
|
+
detectBoundRuntimeClasses,
|
|
21
22
|
firstEvidencePath,
|
|
22
23
|
graphForReport,
|
|
23
24
|
hashHarnessReport,
|
|
24
25
|
hashStableJson,
|
|
26
|
+
isTestWorkerConfig,
|
|
25
27
|
loadHarnessReportForSync,
|
|
26
28
|
mergeBoundRuntimeClasses,
|
|
27
29
|
normalizeHarnessReport,
|
|
@@ -29,14 +31,11 @@ import {
|
|
|
29
31
|
requiresArtifactBody,
|
|
30
32
|
resolveHarnessReportPath,
|
|
31
33
|
stampRemoteSnapshotTruncation,
|
|
34
|
+
stripJsonc,
|
|
32
35
|
tagComponentStatus,
|
|
33
36
|
validateHarnessReport,
|
|
34
37
|
writeHarnessReportArtifacts
|
|
35
|
-
} from "./cli-
|
|
36
|
-
import {
|
|
37
|
-
createCommandContext,
|
|
38
|
-
stringOption
|
|
39
|
-
} from "./cli-5ms8rw8x.js";
|
|
38
|
+
} from "./cli-y333w08f.js";
|
|
40
39
|
import {
|
|
41
40
|
assertFlags,
|
|
42
41
|
assertPositionals,
|
|
@@ -46,6 +45,10 @@ import {
|
|
|
46
45
|
ApiError,
|
|
47
46
|
callControlAPI
|
|
48
47
|
} from "./cli-87xy1evx.js";
|
|
48
|
+
import {
|
|
49
|
+
createCommandContext,
|
|
50
|
+
stringOption
|
|
51
|
+
} from "./cli-5ms8rw8x.js";
|
|
49
52
|
import {
|
|
50
53
|
EXIT_DEGRADED,
|
|
51
54
|
embeddedTenantIdFromApiKey,
|
|
@@ -105,8 +108,11 @@ import {
|
|
|
105
108
|
__toESM
|
|
106
109
|
} from "./cli-0v6na3yp.js";
|
|
107
110
|
|
|
111
|
+
// src/harness-extract/enumerators/agents.ts
|
|
112
|
+
var import_typescript14 = __toESM(require_typescript(), 1);
|
|
113
|
+
|
|
108
114
|
// src/harness-extract/index.ts
|
|
109
|
-
var
|
|
115
|
+
var import_typescript13 = __toESM(require_typescript(), 1);
|
|
110
116
|
import { resolve as resolve13 } from "node:path";
|
|
111
117
|
|
|
112
118
|
// src/harness-extract/ast.ts
|
|
@@ -9140,7 +9146,7 @@ function isUnder2(path, root) {
|
|
|
9140
9146
|
return true;
|
|
9141
9147
|
return path === root || path.startsWith(`${root}/`);
|
|
9142
9148
|
}
|
|
9143
|
-
function
|
|
9149
|
+
function stripJsonc2(text) {
|
|
9144
9150
|
let out = "";
|
|
9145
9151
|
let i = 0;
|
|
9146
9152
|
let inString = false;
|
|
@@ -9184,7 +9190,7 @@ function stripJsonc(text) {
|
|
|
9184
9190
|
}
|
|
9185
9191
|
function parseJsonLoose(text) {
|
|
9186
9192
|
try {
|
|
9187
|
-
return JSON.parse(
|
|
9193
|
+
return JSON.parse(stripJsonc2(text));
|
|
9188
9194
|
} catch {
|
|
9189
9195
|
return;
|
|
9190
9196
|
}
|
|
@@ -17635,6 +17641,103 @@ function hasDirective(text) {
|
|
|
17635
17641
|
const plain = text.replace(/\{\{[^}]*\}\}/g, " ").trim();
|
|
17636
17642
|
return DIRECTIVE_ANYWHERE.test(plain) || DIRECTIVE_VERB.test(plain);
|
|
17637
17643
|
}
|
|
17644
|
+
var ERROR_TEXT = /^\s*(?:error\b|failed\b|could not\b|couldn't\b|cannot\b|can't\b|unable to\b|invalid\b|unknown\b|missing\b|not found\b)/i;
|
|
17645
|
+
function toolResultIsPrompt(owner, pieces) {
|
|
17646
|
+
if (!owner)
|
|
17647
|
+
return false;
|
|
17648
|
+
if (import_typescript8.default.isShorthandPropertyAssignment(owner) || import_typescript8.default.isPropertyAssignment(owner) && !isNamedTableEntry(owner))
|
|
17649
|
+
return false;
|
|
17650
|
+
const own = pieces.map((piece) => piece.trim()).filter(Boolean);
|
|
17651
|
+
if (own.length === 0)
|
|
17652
|
+
return false;
|
|
17653
|
+
const text = withoutLabels(own.join(`
|
|
17654
|
+
`));
|
|
17655
|
+
if (own.every((piece) => ERROR_TEXT.test(piece)) && !IMPERATIVE.test(text))
|
|
17656
|
+
return false;
|
|
17657
|
+
if (!hasDirective(text) && !IMPERATIVE.test(text))
|
|
17658
|
+
return false;
|
|
17659
|
+
const fn = import_typescript8.default.isFunctionLike(owner) ? owner : import_typescript8.default.isVariableDeclaration(owner) && owner.initializer && import_typescript8.default.isFunctionLike(unwrapNode(owner.initializer)) ? unwrapNode(owner.initializer) : undefined;
|
|
17660
|
+
return !fn || isTextBuilder(fn);
|
|
17661
|
+
}
|
|
17662
|
+
function isNamedTableEntry(property) {
|
|
17663
|
+
const object = property.parent;
|
|
17664
|
+
if (object.properties.some((entry) => entry.name && import_typescript8.default.isIdentifier(entry.name) && entry.name.text === "type"))
|
|
17665
|
+
return false;
|
|
17666
|
+
let node = object;
|
|
17667
|
+
while (import_typescript8.default.isObjectLiteralExpression(node) || import_typescript8.default.isPropertyAssignment(node) || import_typescript8.default.isParenthesizedExpression(node) || import_typescript8.default.isAsExpression(node) || import_typescript8.default.isSatisfiesExpression(node) || import_typescript8.default.isTypeAssertionExpression(node)) {
|
|
17668
|
+
node = node.parent;
|
|
17669
|
+
}
|
|
17670
|
+
return import_typescript8.default.isVariableDeclaration(node) || import_typescript8.default.isPropertyDeclaration(node) || import_typescript8.default.isExportAssignment(node);
|
|
17671
|
+
}
|
|
17672
|
+
function withoutLabels(text) {
|
|
17673
|
+
return text.replace(/\{\{[^}]*\}\}/g, " ").replace(/(^|[.!?\n]\s*)[A-Za-z][\w -]{0,24}:(?=\s)/g, "$1");
|
|
17674
|
+
}
|
|
17675
|
+
var IMPERATIVE_VERBS = "keep|write|reply|respond|return|answer|use|include|output|list|describe|summari[sz]e|explain|decide|pick|choose|rate|score|label|extract|generate|draft|revise|rewrite|translate|tell|ask|call|check|verify|follow|format|limit|stop|continue|focus|consider|act|identify|compare|judge|review|repair|fix|plan|propose|suggest|recommend|find|search|read|treat|cite|flag|mark|rank|group|merge|drop|skip|wait|confirm|send|offer|share|re-share|lean|vary|retry|resend|retell|replace|unschedule|reschedule|reconnect|connect|attach|pass|move|open|try|run|re-run|rerun|load|save|update|delete|remove|add|create|edit|post|publish|schedule|mention|note|let|give|show|present|announce|surface|ping|text|message|forward|route|hand|escalate|report|log";
|
|
17676
|
+
var IMPERATIVE = new RegExp(`(?:^|[.!?:;\\n]\\s*|\\bif\\b[^.!?\\n]{0,160},\\s*)(?:then\\s+)?(?:${IMPERATIVE_VERBS})\\b`, "i");
|
|
17677
|
+
function unwrapNode(node) {
|
|
17678
|
+
let current = node;
|
|
17679
|
+
while (import_typescript8.default.isParenthesizedExpression(current) || import_typescript8.default.isAsExpression(current) || import_typescript8.default.isSatisfiesExpression(current))
|
|
17680
|
+
current = current.expression;
|
|
17681
|
+
return current;
|
|
17682
|
+
}
|
|
17683
|
+
function isTextBuilder(fn) {
|
|
17684
|
+
const modifiers = import_typescript8.default.canHaveModifiers(fn) ? import_typescript8.default.getModifiers(fn) ?? [] : [];
|
|
17685
|
+
if (modifiers.some((modifier) => modifier.kind === import_typescript8.default.SyntaxKind.AsyncKeyword))
|
|
17686
|
+
return false;
|
|
17687
|
+
const declared = fn.type?.getText().replace(/\s+/g, "");
|
|
17688
|
+
if (declared && !/^(?:Promise<)?(?:(?:readonly)?string(?:\[\])?|null|undefined)(?:\|(?:(?:readonly)?string(?:\[\])?|null|undefined))*>?$/.test(declared))
|
|
17689
|
+
return false;
|
|
17690
|
+
let builder = true;
|
|
17691
|
+
let returns = 0;
|
|
17692
|
+
let failures = 0;
|
|
17693
|
+
const visit = (node) => {
|
|
17694
|
+
if (!builder)
|
|
17695
|
+
return;
|
|
17696
|
+
if (node !== fn && import_typescript8.default.isFunctionLike(node))
|
|
17697
|
+
return;
|
|
17698
|
+
if (import_typescript8.default.isAwaitExpression(node))
|
|
17699
|
+
builder = false;
|
|
17700
|
+
else if (import_typescript8.default.isReturnStatement(node) && node.expression) {
|
|
17701
|
+
if (returnsRecord(node.expression))
|
|
17702
|
+
builder = false;
|
|
17703
|
+
returns += 1;
|
|
17704
|
+
if (ERROR_TEXT.test(leadingText(node.expression)))
|
|
17705
|
+
failures += 1;
|
|
17706
|
+
import_typescript8.default.forEachChild(node, visit);
|
|
17707
|
+
} else
|
|
17708
|
+
import_typescript8.default.forEachChild(node, visit);
|
|
17709
|
+
};
|
|
17710
|
+
const body = fn.body;
|
|
17711
|
+
if (body && !import_typescript8.default.isBlock(body))
|
|
17712
|
+
return !returnsRecord(body);
|
|
17713
|
+
if (body)
|
|
17714
|
+
import_typescript8.default.forEachChild(body, visit);
|
|
17715
|
+
return builder && !(returns > 1 && failures > 0 && takesContext(fn));
|
|
17716
|
+
}
|
|
17717
|
+
function takesContext(fn) {
|
|
17718
|
+
return fn.parameters.some((parameter) => {
|
|
17719
|
+
const name = import_typescript8.default.isIdentifier(parameter.name) ? parameter.name.text : "";
|
|
17720
|
+
return /^(?:ctx|context|env|sql|db|tx)$/.test(name) || /(?:Context|Env)\b/.test(parameter.type?.getText() ?? "");
|
|
17721
|
+
});
|
|
17722
|
+
}
|
|
17723
|
+
function leadingText(expr) {
|
|
17724
|
+
const value = unwrapNode(expr);
|
|
17725
|
+
if (import_typescript8.default.isStringLiteral(value) || import_typescript8.default.isNoSubstitutionTemplateLiteral(value))
|
|
17726
|
+
return value.text;
|
|
17727
|
+
if (import_typescript8.default.isTemplateExpression(value))
|
|
17728
|
+
return value.head.text;
|
|
17729
|
+
if (import_typescript8.default.isBinaryExpression(value) && value.operatorToken.kind === import_typescript8.default.SyntaxKind.PlusToken)
|
|
17730
|
+
return leadingText(value.left);
|
|
17731
|
+
return "";
|
|
17732
|
+
}
|
|
17733
|
+
function returnsRecord(expr) {
|
|
17734
|
+
const value = unwrapNode(expr);
|
|
17735
|
+
if (import_typescript8.default.isObjectLiteralExpression(value))
|
|
17736
|
+
return true;
|
|
17737
|
+
if (import_typescript8.default.isConditionalExpression(value))
|
|
17738
|
+
return returnsRecord(value.whenTrue) || returnsRecord(value.whenFalse);
|
|
17739
|
+
return value.kind === import_typescript8.default.SyntaxKind.TrueKeyword || value.kind === import_typescript8.default.SyntaxKind.FalseKeyword || import_typescript8.default.isNumericLiteral(value);
|
|
17740
|
+
}
|
|
17638
17741
|
function isInstructionDescription(text) {
|
|
17639
17742
|
if (text.length >= 2000)
|
|
17640
17743
|
return true;
|
|
@@ -17786,7 +17889,7 @@ class PromptResolver {
|
|
|
17786
17889
|
const line = `${ref.path}:${ref.line}`;
|
|
17787
17890
|
firstOnLine.set(line, Math.min(firstOnLine.get(line) ?? Infinity, ref.start));
|
|
17788
17891
|
}
|
|
17789
|
-
const refs = [...this.refs.values()].map(({ pieces, start, ...ref }) => {
|
|
17892
|
+
const refs = [...this.refs.values()].filter((ref) => this.keepAll || ref.role !== "tool-result" || toolResultIsPrompt(ref.ownerNode, [...ref.pieces.values()])).map(({ pieces, start, ownerNode: _owner, ...ref }) => {
|
|
17790
17893
|
const later = start > firstOnLine.get(`${ref.path}:${ref.line}`);
|
|
17791
17894
|
const column = later ? this.columnOf(ref.path, start) : undefined;
|
|
17792
17895
|
return {
|
|
@@ -18363,7 +18466,8 @@ class PromptResolver {
|
|
|
18363
18466
|
hops: walk.hops,
|
|
18364
18467
|
via: [...walk.flags],
|
|
18365
18468
|
pieces: new Map,
|
|
18366
|
-
start: anchor.getStart(sf)
|
|
18469
|
+
start: anchor.getStart(sf),
|
|
18470
|
+
...owner ? { ownerNode: owner.node } : {}
|
|
18367
18471
|
};
|
|
18368
18472
|
this.refs.set(key, ref);
|
|
18369
18473
|
}
|
|
@@ -18384,6 +18488,15 @@ class PromptResolver {
|
|
|
18384
18488
|
return;
|
|
18385
18489
|
return this.relative(resolvePath(dirname7(statement.getSourceFile().fileName), spec.replace(/\?raw$/, "")));
|
|
18386
18490
|
}
|
|
18491
|
+
preferModelRole(ref, walk) {
|
|
18492
|
+
const toolPath = walk.role === "tool-result" || walk.role === "tool-description";
|
|
18493
|
+
if (ref.role === "tool-result" && !toolPath) {
|
|
18494
|
+
ref.role = walk.role;
|
|
18495
|
+
ref.hops = walk.hops;
|
|
18496
|
+
ref.via = [...walk.flags];
|
|
18497
|
+
}
|
|
18498
|
+
return ref.role === "tool-result" || walk.role !== "tool-result";
|
|
18499
|
+
}
|
|
18387
18500
|
fileRef(path, walk) {
|
|
18388
18501
|
if (TEST_SOURCE.test(path))
|
|
18389
18502
|
return;
|
|
@@ -18393,9 +18506,11 @@ class PromptResolver {
|
|
|
18393
18506
|
ref = { id, path, line: 1, endLine: 1, start: 0, kind: "file", role: walk.role, text: "", callSiteIds: [], hops: walk.hops, via: [...walk.flags], pieces: new Map };
|
|
18394
18507
|
this.refs.set(id, ref);
|
|
18395
18508
|
}
|
|
18509
|
+
const counts = this.preferModelRole(ref, walk);
|
|
18396
18510
|
if (!ref.callSiteIds.includes(walk.site.id))
|
|
18397
18511
|
ref.callSiteIds.push(walk.site.id);
|
|
18398
|
-
|
|
18512
|
+
if (counts)
|
|
18513
|
+
ref.via = ref.via.filter((f) => walk.flags.includes(f));
|
|
18399
18514
|
}
|
|
18400
18515
|
markUnresolved(node, reason, walk) {
|
|
18401
18516
|
const sf = node.getSourceFile();
|
|
@@ -19031,7 +19146,7 @@ async function runHarnessCommand(context) {
|
|
|
19031
19146
|
throw new CliInputError("Focused remote prompt analysis is not supported yet.", "Use local --analyst=claude|codex|cursor|local-scan, or drop --concern for a full remote report.");
|
|
19032
19147
|
}
|
|
19033
19148
|
if (context.flags["github-actions"] === "true") {
|
|
19034
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
19149
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-m26tapfj.js");
|
|
19035
19150
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
19036
19151
|
writeReport: (report2) => {
|
|
19037
19152
|
const normalized = normalizeHarnessReport(report2);
|
|
@@ -19177,7 +19292,15 @@ async function runHarnessCommand(context) {
|
|
|
19177
19292
|
}
|
|
19178
19293
|
const { artifacts, unknowns } = deterministicGraphItems(rootDir, promote === "all" ? undefined : promote);
|
|
19179
19294
|
tagComponentStatus({ runtimeAgents: [], artifacts }, rootDir);
|
|
19180
|
-
const
|
|
19295
|
+
const { extractHarnessWithContext: extractHarnessWithContext2 } = await import("./index-cpz85prt.js");
|
|
19296
|
+
const { enumerateHarnessComponents, mergeEnumeratedComponents } = await import("./index-0wm7157z.js");
|
|
19297
|
+
let components = { runtimeAgents: [], artifacts: [], warnings: [] };
|
|
19298
|
+
if (promote === "all" || promote === "topology" || promote === "tools") {
|
|
19299
|
+
const { result, ctx } = extractHarnessWithContext2(rootDir);
|
|
19300
|
+
const found = enumerateHarnessComponents(result, ctx);
|
|
19301
|
+
components = promote === "tools" ? { ...found, artifacts: found.artifacts.filter((entry) => entry.type === "tool") } : promote === "topology" ? { ...found, artifacts: found.artifacts.filter((entry) => entry.type !== "tool") } : found;
|
|
19302
|
+
}
|
|
19303
|
+
const promoted = { concern: promote, unknowns, ...mergeEnumeratedComponents(artifacts, components) };
|
|
19181
19304
|
const promoteWritten = [];
|
|
19182
19305
|
if (promoteOut && !context.dryRun) {
|
|
19183
19306
|
const target = resolve9(context.cwd, promoteOut);
|
|
@@ -19204,10 +19327,14 @@ async function runHarnessCommand(context) {
|
|
|
19204
19327
|
}
|
|
19205
19328
|
case "extract": {
|
|
19206
19329
|
writeHumanProgress(context, "Harness extract: tracing model call sites (no LLM, no network)...");
|
|
19207
|
-
const {
|
|
19208
|
-
const
|
|
19330
|
+
const { extractHarnessWithContext: extractHarnessWithContext2, serializeExtractResult } = await import("./index-cpz85prt.js");
|
|
19331
|
+
const { enumerateHarnessComponents, mergeEnumeratedComponents } = await import("./index-0wm7157z.js");
|
|
19332
|
+
const { result, ctx } = extractHarnessWithContext2(rootDir, { fresh: true });
|
|
19333
|
+
const artifacts = deterministicArtifacts(rootDir);
|
|
19334
|
+
tagComponentStatus({ runtimeAgents: [], artifacts }, rootDir);
|
|
19335
|
+
const graph = mergeEnumeratedComponents(artifacts, enumerateHarnessComponents(result, ctx));
|
|
19209
19336
|
writeHumanProgress(context, `Harness extract: ${result.callSites.length} call sites, ${result.roles.length} traced roles across ${result.files} files (${result.programs} programs, ${result.durationMs.toLocaleString()} ms).`);
|
|
19210
|
-
context.output.writeData(serializeExtractResult(result), {
|
|
19337
|
+
context.output.writeData({ ...serializeExtractResult(result), graph }, {
|
|
19211
19338
|
status: result.warnings.length > 0 ? "degraded" : "success",
|
|
19212
19339
|
...result.warnings.length > 0 ? { warnings: result.warnings } : {}
|
|
19213
19340
|
});
|
|
@@ -19987,18 +20114,19 @@ function deterministicGraphItems(rootDir, concern) {
|
|
|
19987
20114
|
hydratePromptAndToolBodies(graph, rootDir);
|
|
19988
20115
|
return { artifacts: graph.artifacts, unknowns: graph.unknowns };
|
|
19989
20116
|
}
|
|
19990
|
-
var extractedModelsCache = new
|
|
20117
|
+
var extractedModelsCache = new WeakMap;
|
|
19991
20118
|
function extractedModelItems(rootDir) {
|
|
19992
20119
|
const empty = { artifacts: [], unknowns: [], rewritten: new Set, enabled: false };
|
|
19993
20120
|
if (process.env.MODA_HARNESS_EXTRACT === "0")
|
|
19994
20121
|
return empty;
|
|
19995
20122
|
const key = resolve9(rootDir);
|
|
19996
|
-
const cached = extractedModelsCache.get(key);
|
|
19997
|
-
if (cached)
|
|
19998
|
-
return cached;
|
|
19999
20123
|
let items = { ...empty, enabled: true };
|
|
20000
20124
|
try {
|
|
20001
|
-
const result =
|
|
20125
|
+
const { result, ctx } = extractHarnessWithContext(key);
|
|
20126
|
+
createAgentScopeResolver(result, ctx);
|
|
20127
|
+
const cached = extractedModelsCache.get(result);
|
|
20128
|
+
if (cached)
|
|
20129
|
+
return cached;
|
|
20002
20130
|
for (const warning of result.warnings)
|
|
20003
20131
|
process.stderr.write(`warning: harness extraction incomplete; ${warning}
|
|
20004
20132
|
`);
|
|
@@ -20008,11 +20136,11 @@ function extractedModelItems(rootDir) {
|
|
|
20008
20136
|
rewritten: new Set((result.rewrittenModels ?? []).map((entry) => normalizedModelId(entry.id))),
|
|
20009
20137
|
enabled: true
|
|
20010
20138
|
};
|
|
20139
|
+
extractedModelsCache.set(result, items);
|
|
20011
20140
|
} catch (error) {
|
|
20012
20141
|
process.stderr.write(`warning: harness extraction failed; model artifacts come from the candidate pre-pass only (${error instanceof Error ? error.message : String(error)})
|
|
20013
20142
|
`);
|
|
20014
20143
|
}
|
|
20015
|
-
extractedModelsCache.set(key, items);
|
|
20016
20144
|
return items;
|
|
20017
20145
|
}
|
|
20018
20146
|
var normalizedModelId = (name) => String(name ?? "").trim().toLowerCase();
|
|
@@ -20180,7 +20308,7 @@ function withCallSitePrompts(rootDir, promoted) {
|
|
|
20180
20308
|
const { result, ctx } = extractHarnessWithContext(rootDir);
|
|
20181
20309
|
const sites = result.callSites.filter((site) => !site.isTest);
|
|
20182
20310
|
const toolDefinitions = result.toolDefinitions ?? [];
|
|
20183
|
-
return promptArtifactsFromCallSites(sites, ctx, promoted, { toolDefinitions }).artifacts;
|
|
20311
|
+
return promptArtifactsFromCallSites(sites, ctx, promoted, { toolDefinitions, agentForScope: createAgentScopeResolver(result, ctx) }).artifacts;
|
|
20184
20312
|
} catch {
|
|
20185
20313
|
return promoted;
|
|
20186
20314
|
}
|
|
@@ -26390,25 +26518,29 @@ var REASON = {
|
|
|
26390
26518
|
config: "model id in model configuration next to a model call"
|
|
26391
26519
|
};
|
|
26392
26520
|
function modelArtifacts(result) {
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
|
|
26401
|
-
|
|
26402
|
-
|
|
26403
|
-
|
|
26404
|
-
|
|
26405
|
-
line:
|
|
26406
|
-
|
|
26407
|
-
|
|
26408
|
-
|
|
26409
|
-
|
|
26410
|
-
|
|
26411
|
-
|
|
26521
|
+
const owners = new Map(result.callSites.map((site) => [site.id, site.agentIds ?? []]));
|
|
26522
|
+
return (result.models ?? []).map((model) => {
|
|
26523
|
+
const agentIds = [...new Set(model.calledFrom.flatMap((site) => owners.get(site.callSite) ?? []))].sort();
|
|
26524
|
+
return {
|
|
26525
|
+
id: `model_config_${slug(model.id)}_${createHash9("sha256").update(model.id).digest("hex").slice(0, 8)}`,
|
|
26526
|
+
type: "model_config",
|
|
26527
|
+
name: model.id,
|
|
26528
|
+
scope: agentIds.length === 1 ? "agent" : "shared",
|
|
26529
|
+
usedByAgentIds: agentIds,
|
|
26530
|
+
confidence: CONFIDENCE[model.resolution],
|
|
26531
|
+
confidenceReason: model.resolution === "config" ? "Read from a model-named setting in source; no call site passes it directly." : "Traced from a model call site's model argument to this literal.",
|
|
26532
|
+
evidence: [
|
|
26533
|
+
{ path: model.definition.path, line: model.definition.line, reason: REASON[model.resolution] },
|
|
26534
|
+
...model.calledFrom.slice(0, MAX_CALL_SITE_EVIDENCE).map((site) => ({
|
|
26535
|
+
path: site.path,
|
|
26536
|
+
line: site.line,
|
|
26537
|
+
reason: `model call site${site.enclosing ? ` in ${site.enclosing}` : ""} (${site.callee})`
|
|
26538
|
+
}))
|
|
26539
|
+
],
|
|
26540
|
+
calledFrom: model.calledFrom.map((site) => ({ path: site.path, line: site.line, ...site.enclosing ? { enclosing: site.enclosing } : {} })),
|
|
26541
|
+
resolution: model.resolution
|
|
26542
|
+
};
|
|
26543
|
+
});
|
|
26412
26544
|
}
|
|
26413
26545
|
function describeSource(source) {
|
|
26414
26546
|
const [kind, ...rest] = source.split(":");
|
|
@@ -26619,210 +26751,1173 @@ function collectModelLiterals(rootDir) {
|
|
|
26619
26751
|
return { literals: [...found].sort(), truncated: capped || skipped };
|
|
26620
26752
|
}
|
|
26621
26753
|
|
|
26622
|
-
// src/harness-extract/
|
|
26623
|
-
var
|
|
26624
|
-
|
|
26625
|
-
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
var
|
|
26629
|
-
|
|
26630
|
-
|
|
26631
|
-
|
|
26632
|
-
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
if (
|
|
26638
|
-
return;
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
|
|
26644
|
-
|
|
26645
|
-
|
|
26646
|
-
|
|
26647
|
-
|
|
26648
|
-
|
|
26649
|
-
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
|
|
26653
|
-
|
|
26654
|
-
}
|
|
26655
|
-
function definitionName(tree) {
|
|
26656
|
-
const fn = member(tree, "function");
|
|
26657
|
-
const fnObject = fn ? objectsOf(fn)[0] : undefined;
|
|
26658
|
-
const fnName = fnObject ? singleLiteral2(member(fnObject, "name")) : undefined;
|
|
26659
|
-
if (fnName && fnObject)
|
|
26660
|
-
return { name: fnName, path: fnObject.path, line: fnObject.line, via: "function.name" };
|
|
26661
|
-
const nameNode = member(tree, "name");
|
|
26662
|
-
const name = singleLiteral2(nameNode);
|
|
26663
|
-
const keys = (tree.children ?? []).map((child) => child.key ?? "");
|
|
26664
|
-
if (name && nameNode && keys.some((key) => DEFINITION_KEYS.has(key)))
|
|
26665
|
-
return { name, path: nameNode.path, line: nameNode.line, via: "name" };
|
|
26666
|
-
return;
|
|
26667
|
-
}
|
|
26668
|
-
function toolNamesOf(tree) {
|
|
26669
|
-
const out = { names: [], unresolved: [] };
|
|
26670
|
-
const visit = (node, inArray, names) => {
|
|
26671
|
-
if (node.kind === "array" || node.kind === "assembled" && node.text === "array") {
|
|
26672
|
-
for (const child of node.children ?? [])
|
|
26673
|
-
visit(child, true, names);
|
|
26674
|
-
return;
|
|
26675
|
-
}
|
|
26676
|
-
if (node.kind === "object") {
|
|
26677
|
-
const definition = definitionName(node);
|
|
26678
|
-
if (definition) {
|
|
26679
|
-
names.push(definition);
|
|
26680
|
-
return;
|
|
26681
|
-
}
|
|
26682
|
-
if (inArray) {
|
|
26683
|
-
out.unresolved.push({ ...node, note: "tool object without a literal name" });
|
|
26684
|
-
return;
|
|
26754
|
+
// src/harness-extract/emit-tools.ts
|
|
26755
|
+
var import_typescript12 = __toESM(require_typescript(), 1);
|
|
26756
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
26757
|
+
import { join as join14, posix as posix3 } from "node:path";
|
|
26758
|
+
|
|
26759
|
+
// src/harness-extract/enumerators/common.ts
|
|
26760
|
+
var import_typescript10 = __toESM(require_typescript(), 1);
|
|
26761
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync15 } from "node:fs";
|
|
26762
|
+
import { dirname as dirname10, join as join13, posix as posix2, relative as relative10 } from "node:path";
|
|
26763
|
+
var cache = new WeakMap;
|
|
26764
|
+
var SKIP = new Set(["node_modules", "dist", "build", "out", "vendor", "coverage", "__pycache__"]);
|
|
26765
|
+
var TEST_PATH4 = /(?:^|\/)(?:test|tests|__tests__|test-fixtures|fixtures?|__fixtures__|__mocks__|e2e)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/i;
|
|
26766
|
+
var EVAL_PATH2 = /(?:^|\/)evals?\/|\.evals?\.[cm]?[jt]sx?$/i;
|
|
26767
|
+
function enumerationRepository(ctx) {
|
|
26768
|
+
const existing = cache.get(ctx);
|
|
26769
|
+
if (existing)
|
|
26770
|
+
return existing;
|
|
26771
|
+
const files = [];
|
|
26772
|
+
const walk = (dir) => {
|
|
26773
|
+
for (const entry of readdirSync8(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
26774
|
+
const abs = join13(dir, entry.name);
|
|
26775
|
+
if (entry.isDirectory()) {
|
|
26776
|
+
if (!entry.name.startsWith(".") && !SKIP.has(entry.name))
|
|
26777
|
+
walk(abs);
|
|
26778
|
+
} else if (entry.isFile() && /\.(?:[cm]?[jt]sx?|py|jsonc?|toml)$/.test(entry.name) && !/\.d\.[cm]?ts$/.test(entry.name)) {
|
|
26779
|
+
const path = relative10(ctx.rootDir, abs).split("\\").join("/");
|
|
26780
|
+
try {
|
|
26781
|
+
const text = readFileSync15(abs, "utf8");
|
|
26782
|
+
if (text.length > 1024 * 1024)
|
|
26783
|
+
continue;
|
|
26784
|
+
const source = ctx.programFor(path)?.getSourceFile(abs) ?? import_typescript10.default.createSourceFile(abs, text, import_typescript10.default.ScriptTarget.Latest, true, path.endsWith("tsx") ? import_typescript10.default.ScriptKind.TSX : import_typescript10.default.ScriptKind.TS);
|
|
26785
|
+
files.push({ path, text, source });
|
|
26786
|
+
} catch {}
|
|
26685
26787
|
}
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26788
|
+
}
|
|
26789
|
+
};
|
|
26790
|
+
walk(ctx.rootDir);
|
|
26791
|
+
const workers = [];
|
|
26792
|
+
const paths = new Set(files.map((file) => file.path));
|
|
26793
|
+
const closure = (main) => {
|
|
26794
|
+
const found = new Set, pending = [main];
|
|
26795
|
+
for (let index = 0;index < pending.length; index++) {
|
|
26796
|
+
const path = pending[index];
|
|
26797
|
+
if (found.has(path))
|
|
26798
|
+
continue;
|
|
26799
|
+
found.add(path);
|
|
26800
|
+
const file = files.find((entry) => entry.path === path);
|
|
26801
|
+
if (!file)
|
|
26802
|
+
continue;
|
|
26803
|
+
for (const ref of import_typescript10.default.preProcessFile(file.text, true, true).importedFiles) {
|
|
26804
|
+
if (!ref.fileName.startsWith("."))
|
|
26805
|
+
continue;
|
|
26806
|
+
const base = posix2.normalize(posix2.join(posix2.dirname(path), ref.fileName)).replace(/\.[cm]?js$/, "");
|
|
26807
|
+
const target = [base, ...[".ts", ".tsx", ".js", ".mjs", "/index.ts", "/index.js"].map((suffix) => base + suffix)].find((candidate) => paths.has(candidate));
|
|
26808
|
+
if (target)
|
|
26809
|
+
pending.push(target);
|
|
26699
26810
|
}
|
|
26700
|
-
names.push(...record.values());
|
|
26701
|
-
return;
|
|
26702
26811
|
}
|
|
26703
|
-
|
|
26704
|
-
|
|
26705
|
-
|
|
26706
|
-
|
|
26812
|
+
return found;
|
|
26813
|
+
};
|
|
26814
|
+
for (const file of files)
|
|
26815
|
+
if (/(?:^|\/)wrangler(?:\.[\w.-]+)?\.(?:jsonc?|toml)$/.test(file.path)) {
|
|
26816
|
+
try {
|
|
26817
|
+
const config2 = file.path.endsWith(".toml") ? parse(file.text) : JSON.parse(stripJsonc(file.text));
|
|
26818
|
+
const variants = [["default", config2], ...Object.entries(config2.env ?? {}).map(([name, section]) => [name, { ...config2, ...section, queues: section.queues, workflows: section.workflows }])];
|
|
26819
|
+
for (const [environment, section] of variants) {
|
|
26820
|
+
if (typeof section.main !== "string" || isTestWorkerConfig({ path: file.path, name: section.name, mains: [section.main] }))
|
|
26821
|
+
continue;
|
|
26822
|
+
const main = posix2.normalize(posix2.join(dirname10(file.path), section.main));
|
|
26823
|
+
workers.push({ id: `${file.path}#${environment}`, main, cron: Boolean(section.triggers?.crons?.length), queueBindings: new Set((section.queues?.producers ?? []).flatMap((producer) => typeof producer.binding === "string" ? [producer.binding] : [])), workflowClasses: (section.workflows ?? []).flatMap((binding) => typeof binding.class_name === "string" && !binding.script_name ? [binding.class_name] : []), files: closure(main) });
|
|
26824
|
+
}
|
|
26825
|
+
} catch {}
|
|
26707
26826
|
}
|
|
26708
|
-
|
|
26827
|
+
const queueBindingsFor = (path) => {
|
|
26828
|
+
const owners = workers.filter((worker) => worker.files.has(path));
|
|
26829
|
+
return new Set(owners[0] ? [...owners[0].queueBindings].filter((binding) => owners.every((worker) => worker.queueBindings.has(binding))) : []);
|
|
26709
26830
|
};
|
|
26710
|
-
|
|
26711
|
-
|
|
26712
|
-
|
|
26713
|
-
|
|
26714
|
-
// src/harness-extract/index.ts
|
|
26715
|
-
var PROMPT_ROLES = new Set(["system", "user", "messages", "prompt", "instructions", "questions"]);
|
|
26716
|
-
var MAX_HELPER_CALLERS = 200;
|
|
26717
|
-
var MAX_DERIVED = 5000;
|
|
26718
|
-
var MAX_CALLER_CHAIN = 3;
|
|
26719
|
-
var cache = new Map;
|
|
26720
|
-
function clearExtractCache() {
|
|
26721
|
-
cache.clear();
|
|
26831
|
+
const reach = buildImportGraphReachability(ctx.rootDir);
|
|
26832
|
+
const repo = { files, workers, queueBindingsFor, cronEntrypoints: new Set(workers.filter((worker) => worker.cron).map((worker) => worker.main)), deployedFiles: reach.inProd, status: (path) => reach.inProd.has(path) || reach.reachable.has(path) ? "reachable" : "defined_only" };
|
|
26833
|
+
cache.set(ctx, repo);
|
|
26834
|
+
return repo;
|
|
26722
26835
|
}
|
|
26723
|
-
function
|
|
26724
|
-
return
|
|
26725
|
-
}
|
|
26726
|
-
function extractHarnessWithContext(rootDir, options = {}) {
|
|
26727
|
-
const root = resolve13(rootDir);
|
|
26728
|
-
const { fresh, ...rest } = options;
|
|
26729
|
-
const key = `${root}\x00${JSON.stringify(rest)}`;
|
|
26730
|
-
const fingerprint = sourceFingerprint(root, rest);
|
|
26731
|
-
const cached = fresh ? undefined : cache.get(key);
|
|
26732
|
-
if (cached && cached.fingerprint === fingerprint)
|
|
26733
|
-
return cached.output;
|
|
26734
|
-
const output = runExtract(root, options);
|
|
26735
|
-
cache.set(key, { output, fingerprint });
|
|
26736
|
-
return output;
|
|
26836
|
+
function lineOf5(node) {
|
|
26837
|
+
return node.getSourceFile().getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
26737
26838
|
}
|
|
26738
|
-
function
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
if (
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
return ref;
|
|
26839
|
+
function nameOf(node) {
|
|
26840
|
+
if (!node)
|
|
26841
|
+
return;
|
|
26842
|
+
if (import_typescript10.default.isComputedPropertyName(node) && import_typescript10.default.isStringLiteralLike(node.expression))
|
|
26843
|
+
return node.expression.text;
|
|
26844
|
+
return import_typescript10.default.isIdentifier(node) || import_typescript10.default.isStringLiteralLike(node) || import_typescript10.default.isNumericLiteral(node) ? node.text : undefined;
|
|
26745
26845
|
}
|
|
26746
|
-
function
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
if (name)
|
|
26750
|
-
return { fn, name };
|
|
26751
|
-
}
|
|
26752
|
-
return;
|
|
26846
|
+
function visit(node, fn) {
|
|
26847
|
+
fn(node);
|
|
26848
|
+
import_typescript10.default.forEachChild(node, (child) => visit(child, fn));
|
|
26753
26849
|
}
|
|
26754
|
-
function
|
|
26755
|
-
|
|
26756
|
-
const owner = cls && (fn.parent === cls || fn.parent && fn.parent.parent === cls) ? className(cls) : undefined;
|
|
26757
|
-
return owner ? `${owner}.${name}` : name;
|
|
26850
|
+
function componentId(kind, path, name) {
|
|
26851
|
+
return `${kind}:${path}:${name}`;
|
|
26758
26852
|
}
|
|
26759
|
-
function
|
|
26760
|
-
|
|
26761
|
-
const object = objectLiteralOf2(fn);
|
|
26762
|
-
if (object) {
|
|
26763
|
-
const holder = object.parent && import_typescript10.default.isVariableDeclaration(object.parent) && import_typescript10.default.isIdentifier(object.parent.name) ? object.parent.name.text : `L${lineOf(object)}`;
|
|
26764
|
-
parts.unshift(holder);
|
|
26765
|
-
}
|
|
26766
|
-
for (let outer = namedEnclosingFunction(object ?? fn);outer; outer = namedEnclosingFunction(outer.fn))
|
|
26767
|
-
parts.unshift(qualifiedName(outer.fn, outer.name));
|
|
26768
|
-
return `${relPath(rootDir, fn.getSourceFile().fileName)}#${parts.join(".")}`;
|
|
26853
|
+
function artifact(type, name, file, node, reason, status) {
|
|
26854
|
+
return { id: componentId(type, file.path, name), type, name, scope: "shared", sourcePath: file.path, usedByAgentIds: [], confidence: 1, evidence: [{ path: file.path, line: lineOf5(node), reason }], status };
|
|
26769
26855
|
}
|
|
26770
|
-
function
|
|
26771
|
-
|
|
26772
|
-
return member2 && import_typescript10.default.isObjectLiteralExpression(member2.parent) ? member2.parent : undefined;
|
|
26856
|
+
function agent(name, file, node, reason, status) {
|
|
26857
|
+
return { id: componentId("agent", file.path, name), name, kind: "runtime_agent", root: ".", entrypointIds: [], channelAdapterIds: [], artifactIds: [], identityNamespaceIds: [], deploymentIds: [], frameworkIds: [], modelProviderIds: [], confidence: 1, evidence: [{ path: file.path, line: lineOf5(node), reason }], status };
|
|
26773
26858
|
}
|
|
26774
|
-
function
|
|
26775
|
-
|
|
26776
|
-
if (named)
|
|
26777
|
-
return functionScope(rootDir, named.fn, named.name);
|
|
26778
|
-
const path = relPath(rootDir, node.getSourceFile().fileName);
|
|
26779
|
-
let outermost;
|
|
26780
|
-
for (let fn = enclosingFunction(node);fn; fn = enclosingFunction(fn))
|
|
26781
|
-
outermost = fn;
|
|
26782
|
-
return outermost ? `${path}#L${lineOf(outermost)}` : path;
|
|
26859
|
+
function emptyResult() {
|
|
26860
|
+
return { runtimeAgents: [], artifacts: [], warnings: [] };
|
|
26783
26861
|
}
|
|
26784
|
-
function
|
|
26785
|
-
const
|
|
26786
|
-
|
|
26787
|
-
return { callers: [], callerScopes: [] };
|
|
26788
|
-
const cached = memo.get(start.fn);
|
|
26789
|
-
if (cached)
|
|
26790
|
-
return cached;
|
|
26791
|
-
const chain = { callers: [], callerScopes: [] };
|
|
26792
|
-
const seen = new Set;
|
|
26793
|
-
let current = start;
|
|
26794
|
-
while (current && !seen.has(current.fn) && chain.callers.length <= MAX_CALLER_CHAIN) {
|
|
26795
|
-
seen.add(current.fn);
|
|
26796
|
-
chain.callers.unshift(qualifiedName(current.fn, current.name));
|
|
26797
|
-
chain.callerScopes.unshift(functionScope(rootDir, current.fn, current.name));
|
|
26798
|
-
const caller = calls.callersOf(current.fn, current.name, 1)[0];
|
|
26799
|
-
current = caller ? namedEnclosingFunction(caller) : undefined;
|
|
26800
|
-
}
|
|
26801
|
-
memo.set(start.fn, chain);
|
|
26802
|
-
return chain;
|
|
26862
|
+
function stableResult(result) {
|
|
26863
|
+
const unique2 = (values) => [...new Map(values.map((x) => [x.id, x])).values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
26864
|
+
return { runtimeAgents: unique2(result.runtimeAgents), artifacts: unique2(result.artifacts), warnings: [...new Set(result.warnings)].sort() };
|
|
26803
26865
|
}
|
|
26804
|
-
|
|
26805
|
-
|
|
26866
|
+
|
|
26867
|
+
// src/harness-extract/tool-owners.ts
|
|
26868
|
+
var import_typescript11 = __toESM(require_typescript(), 1);
|
|
26869
|
+
var AGENT_BASES3 = new Set(["Think", "Agent", "AIChatAgent"]);
|
|
26870
|
+
var ROLE_FIELD = /^(?:role|roleId|roleName|kind|type|id|slug|agentRole|subagentRole)$/i;
|
|
26871
|
+
var MAX_HOPS = 10;
|
|
26872
|
+
var MIN_ALLOWLIST = 3;
|
|
26873
|
+
var TOOL_LIST_PARAM = /^(?:allow(?:list|ed)?|allowedTools|toolNames|tools|requested)$/i;
|
|
26874
|
+
var ALLOWLIST_SHARE = 0.6;
|
|
26875
|
+
function sourceFilesOf(ctx) {
|
|
26806
26876
|
const out = [];
|
|
26807
|
-
for (const
|
|
26808
|
-
|
|
26809
|
-
|
|
26810
|
-
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
|
|
26817
|
-
}
|
|
26818
|
-
visit(source);
|
|
26877
|
+
for (const program of ctx.programs) {
|
|
26878
|
+
for (const source of program.getSourceFiles()) {
|
|
26879
|
+
if (source.isDeclarationFile || /[\\/]node_modules[\\/]/.test(source.fileName))
|
|
26880
|
+
continue;
|
|
26881
|
+
const rel2 = relPath(ctx.rootDir, source.fileName);
|
|
26882
|
+
if (rel2.startsWith(".."))
|
|
26883
|
+
continue;
|
|
26884
|
+
if (ctx.programFor(source.fileName) !== program)
|
|
26885
|
+
continue;
|
|
26886
|
+
out.push({ program, source, rel: rel2 });
|
|
26887
|
+
}
|
|
26819
26888
|
}
|
|
26820
26889
|
return out;
|
|
26821
26890
|
}
|
|
26822
|
-
function
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26891
|
+
function isOneOffScript(rel2, reach) {
|
|
26892
|
+
if (!/^scripts\//.test(rel2))
|
|
26893
|
+
return false;
|
|
26894
|
+
return !reach?.inProd.has(rel2);
|
|
26895
|
+
}
|
|
26896
|
+
|
|
26897
|
+
class ToolOwners {
|
|
26898
|
+
ctx;
|
|
26899
|
+
classes = [];
|
|
26900
|
+
byDeclaration = new Map;
|
|
26901
|
+
references = new Map;
|
|
26902
|
+
edges = new Map;
|
|
26903
|
+
allowlists;
|
|
26904
|
+
constructor(ctx) {
|
|
26905
|
+
this.ctx = ctx;
|
|
26906
|
+
const memo = createFrameworkMemo();
|
|
26907
|
+
for (const { program, source, rel: rel2 } of sourceFilesOf(ctx)) {
|
|
26908
|
+
if (isTestPath(rel2))
|
|
26909
|
+
continue;
|
|
26910
|
+
const checker = program.getTypeChecker();
|
|
26911
|
+
const visit2 = (node) => {
|
|
26912
|
+
if ((import_typescript11.default.isClassDeclaration(node) || import_typescript11.default.isClassExpression(node)) && node.name) {
|
|
26913
|
+
let info;
|
|
26914
|
+
try {
|
|
26915
|
+
info = frameworkBaseOf(node, checker, memo);
|
|
26916
|
+
} catch {
|
|
26917
|
+
info = undefined;
|
|
26918
|
+
}
|
|
26919
|
+
if (info?.base && AGENT_BASES3.has(info.base)) {
|
|
26920
|
+
const agent2 = {
|
|
26921
|
+
name: node.name.text,
|
|
26922
|
+
path: rel2,
|
|
26923
|
+
line: lineOf(node.name),
|
|
26924
|
+
abstract: Boolean(import_typescript11.default.getCombinedModifierFlags(node) & import_typescript11.default.ModifierFlags.Abstract),
|
|
26925
|
+
base: info.base,
|
|
26926
|
+
baseModule: info.module,
|
|
26927
|
+
parent: superclassOf(node, checker),
|
|
26928
|
+
declaration: node,
|
|
26929
|
+
fields: classFields(node),
|
|
26930
|
+
methods: new Set(node.members.filter((member) => import_typescript11.default.isMethodDeclaration(member) || import_typescript11.default.isGetAccessorDeclaration(member)).map((member) => propertyNameText(member.name) ?? ""))
|
|
26931
|
+
};
|
|
26932
|
+
this.classes.push(agent2);
|
|
26933
|
+
this.byDeclaration.set(node, agent2);
|
|
26934
|
+
}
|
|
26935
|
+
}
|
|
26936
|
+
import_typescript11.default.forEachChild(node, visit2);
|
|
26937
|
+
};
|
|
26938
|
+
visit2(source);
|
|
26939
|
+
}
|
|
26940
|
+
}
|
|
26941
|
+
concrete() {
|
|
26942
|
+
return this.classes.filter((agent2) => !agent2.abstract);
|
|
26943
|
+
}
|
|
26944
|
+
fieldOf(name, field) {
|
|
26945
|
+
let current = this.classes.find((agent2) => agent2.name === name);
|
|
26946
|
+
for (let hops = 0;current && hops < 10; hops += 1) {
|
|
26947
|
+
if (field in current.fields)
|
|
26948
|
+
return current.fields[field];
|
|
26949
|
+
current = current.parent ? this.byDeclaration.get(current.parent) : undefined;
|
|
26950
|
+
}
|
|
26951
|
+
return;
|
|
26952
|
+
}
|
|
26953
|
+
inheritors(owner, method) {
|
|
26954
|
+
const out = [];
|
|
26955
|
+
for (const agent2 of this.classes) {
|
|
26956
|
+
if (agent2.abstract)
|
|
26957
|
+
continue;
|
|
26958
|
+
let current = agent2;
|
|
26959
|
+
let ok = false;
|
|
26960
|
+
for (let hops = 0;current && hops < 10; hops += 1) {
|
|
26961
|
+
if (current === owner) {
|
|
26962
|
+
ok = true;
|
|
26963
|
+
break;
|
|
26964
|
+
}
|
|
26965
|
+
if (method && current.methods.has(method))
|
|
26966
|
+
break;
|
|
26967
|
+
current = current.parent ? this.byDeclaration.get(current.parent) : undefined;
|
|
26968
|
+
}
|
|
26969
|
+
if (ok)
|
|
26970
|
+
out.push(agent2.name);
|
|
26971
|
+
}
|
|
26972
|
+
return out;
|
|
26973
|
+
}
|
|
26974
|
+
ownersOf(node, program) {
|
|
26975
|
+
const sets = { direct: new Set, rpc: new Set, open: new Set };
|
|
26976
|
+
const start = this.positionOf(node, program);
|
|
26977
|
+
if (!start)
|
|
26978
|
+
return { direct: [], rpc: [], open: [] };
|
|
26979
|
+
const seen = new Set;
|
|
26980
|
+
let frontier = [{ position: start, via: "direct" }];
|
|
26981
|
+
for (let hops = 0;frontier.length > 0 && hops <= MAX_HOPS; hops += 1) {
|
|
26982
|
+
const next = [];
|
|
26983
|
+
for (const { position, via } of frontier) {
|
|
26984
|
+
for (const agent2 of position.agents)
|
|
26985
|
+
sets[via].add(agent2);
|
|
26986
|
+
const key = `${position.id}|${via}`;
|
|
26987
|
+
if (seen.has(key))
|
|
26988
|
+
continue;
|
|
26989
|
+
seen.add(key);
|
|
26990
|
+
for (const edge of this.edgesOf(position, program))
|
|
26991
|
+
next.push({ position: edge.position, via: combineVia(via, edge.via) });
|
|
26992
|
+
}
|
|
26993
|
+
frontier = next;
|
|
26994
|
+
}
|
|
26995
|
+
const direct = [...sets.direct].sort();
|
|
26996
|
+
return {
|
|
26997
|
+
direct,
|
|
26998
|
+
rpc: [...sets.rpc].filter((name) => !sets.direct.has(name)).sort(),
|
|
26999
|
+
open: [...sets.open].filter((name) => !sets.direct.has(name)).sort()
|
|
27000
|
+
};
|
|
27001
|
+
}
|
|
27002
|
+
positionOf(node, program) {
|
|
27003
|
+
let method;
|
|
27004
|
+
for (let current = node;current; current = current.parent) {
|
|
27005
|
+
if ((import_typescript11.default.isMethodDeclaration(current) || import_typescript11.default.isGetAccessorDeclaration(current) || import_typescript11.default.isPropertyDeclaration(current)) && import_typescript11.default.isClassLike(current.parent) && import_typescript11.default.isIdentifier(current.name)) {
|
|
27006
|
+
method = current.name;
|
|
27007
|
+
}
|
|
27008
|
+
if (import_typescript11.default.isClassLike(current)) {
|
|
27009
|
+
const agent2 = this.byDeclaration.get(current);
|
|
27010
|
+
if (!agent2)
|
|
27011
|
+
return current.name ? this.holderPosition(current.name, program) : undefined;
|
|
27012
|
+
const guards = fieldGuards(node, current);
|
|
27013
|
+
const agents = this.inheritors(agent2, method?.text).filter((name) => guards.every((guard) => guard.holds(this.fieldOf(name, guard.field))));
|
|
27014
|
+
const anchor = method ?? current;
|
|
27015
|
+
return { id: nodeId(anchor), anchor, agents, agent: agent2, method };
|
|
27016
|
+
}
|
|
27017
|
+
}
|
|
27018
|
+
const holder = holderName(node);
|
|
27019
|
+
return holder ? this.holderPosition(holder, program) : undefined;
|
|
27020
|
+
}
|
|
27021
|
+
holderPosition(name, program) {
|
|
27022
|
+
const target = valueDeclaration(resolvedSymbol(name, program.getTypeChecker())) ?? name.parent;
|
|
27023
|
+
return { id: nodeId(target), anchor: target, agents: [], holder: name };
|
|
27024
|
+
}
|
|
27025
|
+
edgesOf(position, program) {
|
|
27026
|
+
const cached = this.edges.get(position.id);
|
|
27027
|
+
if (cached)
|
|
27028
|
+
return cached;
|
|
27029
|
+
const out = [];
|
|
27030
|
+
this.edges.set(position.id, out);
|
|
27031
|
+
const checker = program.getTypeChecker();
|
|
27032
|
+
if (position.holder) {
|
|
27033
|
+
for (const reference of this.referencesOf(program, position.holder.text)) {
|
|
27034
|
+
if (reference === position.holder || isDeclarationName(reference))
|
|
27035
|
+
continue;
|
|
27036
|
+
if (valueDeclaration(resolvedSymbol(reference, checker)) !== position.anchor)
|
|
27037
|
+
continue;
|
|
27038
|
+
const next = this.positionOf(reference, program);
|
|
27039
|
+
if (next)
|
|
27040
|
+
out.push({ position: next, via: "direct" });
|
|
27041
|
+
}
|
|
27042
|
+
} else if (position.agent && position.method) {
|
|
27043
|
+
const owner = position.agent;
|
|
27044
|
+
const name = position.method.text;
|
|
27045
|
+
const unique2 = this.classes.filter((agent2) => agent2.methods.has(name)).length === 1 && owner.methods.has(name);
|
|
27046
|
+
const declaration = position.method.parent;
|
|
27047
|
+
const takesToolList = import_typescript11.default.isMethodDeclaration(declaration) && declaration.parameters.some((param) => import_typescript11.default.isIdentifier(param.name) && TOOL_LIST_PARAM.test(param.name.text));
|
|
27048
|
+
for (const reference of this.referencesOf(program, name)) {
|
|
27049
|
+
const access = reference.parent;
|
|
27050
|
+
if (!access || !import_typescript11.default.isPropertyAccessExpression(access) || access.name !== reference)
|
|
27051
|
+
continue;
|
|
27052
|
+
if (access.expression.kind === import_typescript11.default.SyntaxKind.ThisKeyword) {
|
|
27053
|
+
if (!isWithin3(reference, owner.declaration))
|
|
27054
|
+
continue;
|
|
27055
|
+
const next2 = this.positionOf(reference, program);
|
|
27056
|
+
if (next2)
|
|
27057
|
+
out.push({ position: { ...next2, agents: [] }, via: "direct" });
|
|
27058
|
+
continue;
|
|
27059
|
+
}
|
|
27060
|
+
if (!unique2 || !import_typescript11.default.isCallExpression(access.parent) || access.parent.expression !== access)
|
|
27061
|
+
continue;
|
|
27062
|
+
const resolved = valueDeclaration(resolvedSymbol(reference, checker));
|
|
27063
|
+
if (resolved && resolved.parent !== owner.declaration)
|
|
27064
|
+
continue;
|
|
27065
|
+
const next = this.positionOf(reference, program);
|
|
27066
|
+
if (next)
|
|
27067
|
+
out.push({ position: next, via: takesToolList ? "open" : "rpc" });
|
|
27068
|
+
}
|
|
27069
|
+
}
|
|
27070
|
+
return out;
|
|
27071
|
+
}
|
|
27072
|
+
referencesOf(program, name) {
|
|
27073
|
+
let index = this.references.get(program);
|
|
27074
|
+
if (!index) {
|
|
27075
|
+
index = new Map;
|
|
27076
|
+
for (const source of program.getSourceFiles()) {
|
|
27077
|
+
if (source.isDeclarationFile || /[\\/]node_modules[\\/]/.test(source.fileName))
|
|
27078
|
+
continue;
|
|
27079
|
+
if (isTestPath(relPath(this.ctx.rootDir, source.fileName)))
|
|
27080
|
+
continue;
|
|
27081
|
+
const visit2 = (node) => {
|
|
27082
|
+
if (import_typescript11.default.isIdentifier(node)) {
|
|
27083
|
+
const list = index.get(node.text);
|
|
27084
|
+
if (list)
|
|
27085
|
+
list.push(node);
|
|
27086
|
+
else
|
|
27087
|
+
index.set(node.text, [node]);
|
|
27088
|
+
}
|
|
27089
|
+
import_typescript11.default.forEachChild(node, visit2);
|
|
27090
|
+
};
|
|
27091
|
+
visit2(source);
|
|
27092
|
+
}
|
|
27093
|
+
this.references.set(program, index);
|
|
27094
|
+
}
|
|
27095
|
+
return index.get(name) ?? [];
|
|
27096
|
+
}
|
|
27097
|
+
indexAllowlists(knownTools) {
|
|
27098
|
+
this.allowlists = [];
|
|
27099
|
+
for (const { program, source, rel: rel2 } of sourceFilesOf(this.ctx)) {
|
|
27100
|
+
if (isTestPath(rel2))
|
|
27101
|
+
continue;
|
|
27102
|
+
const checker = program.getTypeChecker();
|
|
27103
|
+
const visit2 = (node) => {
|
|
27104
|
+
if (import_typescript11.default.isArrayLiteralExpression(node) && (!node.parent || !import_typescript11.default.isSpreadElement(node.parent))) {
|
|
27105
|
+
const entries = stringEntries(node, checker);
|
|
27106
|
+
const named = entries.filter((entry) => knownTools.has(entry) || entry.endsWith("*") && entry.length > 2);
|
|
27107
|
+
if (entries.length >= MIN_ALLOWLIST && named.length / entries.length >= ALLOWLIST_SHARE) {
|
|
27108
|
+
const agents = this.allowlistOwners(node, program);
|
|
27109
|
+
if (agents.length > 0)
|
|
27110
|
+
this.allowlists.push({ entries, agents, path: rel2 });
|
|
27111
|
+
}
|
|
27112
|
+
}
|
|
27113
|
+
import_typescript11.default.forEachChild(node, visit2);
|
|
27114
|
+
};
|
|
27115
|
+
visit2(source);
|
|
27116
|
+
}
|
|
27117
|
+
}
|
|
27118
|
+
allowlisted() {
|
|
27119
|
+
return new Set((this.allowlists ?? []).flatMap((list) => list.agents));
|
|
27120
|
+
}
|
|
27121
|
+
agentsFor(tool, owners, allowlistApplies = () => true) {
|
|
27122
|
+
const restricted = this.allowlisted();
|
|
27123
|
+
const out = new Set([...owners.direct, ...owners.open, ...owners.rpc.filter((agent2) => !restricted.has(agent2)), ...this.allowlistAgents(tool, allowlistApplies)]);
|
|
27124
|
+
return [...out].sort();
|
|
27125
|
+
}
|
|
27126
|
+
allowlistAgents(tool, applies = () => true) {
|
|
27127
|
+
const out = new Set;
|
|
27128
|
+
for (const list of this.allowlists ?? []) {
|
|
27129
|
+
if (!applies(list.path))
|
|
27130
|
+
continue;
|
|
27131
|
+
if (list.entries.some((entry) => entry === tool || entry.endsWith("*") && tool.startsWith(entry.slice(0, -1)))) {
|
|
27132
|
+
for (const agent2 of list.agents)
|
|
27133
|
+
out.add(agent2);
|
|
27134
|
+
}
|
|
27135
|
+
}
|
|
27136
|
+
return [...out].sort();
|
|
27137
|
+
}
|
|
27138
|
+
allowlistOwners(list, program) {
|
|
27139
|
+
const keys = new Set;
|
|
27140
|
+
const direct = new Set;
|
|
27141
|
+
const checker = program.getTypeChecker();
|
|
27142
|
+
const visited = new Set;
|
|
27143
|
+
const walk = (node, hops) => {
|
|
27144
|
+
if (hops > 4 || visited.has(node))
|
|
27145
|
+
return;
|
|
27146
|
+
visited.add(node);
|
|
27147
|
+
for (let current = node.parent;current; current = current.parent) {
|
|
27148
|
+
if (import_typescript11.default.isClassLike(current)) {
|
|
27149
|
+
const agent2 = this.byDeclaration.get(current);
|
|
27150
|
+
if (agent2)
|
|
27151
|
+
for (const name of this.inheritors(agent2, undefined))
|
|
27152
|
+
direct.add(name);
|
|
27153
|
+
return;
|
|
27154
|
+
}
|
|
27155
|
+
if (import_typescript11.default.isFunctionLike(current))
|
|
27156
|
+
return;
|
|
27157
|
+
if (import_typescript11.default.isPropertyAssignment(current) || import_typescript11.default.isShorthandPropertyAssignment(current)) {
|
|
27158
|
+
const key = propertyNameText(current.name);
|
|
27159
|
+
if (key)
|
|
27160
|
+
keys.add(key);
|
|
27161
|
+
}
|
|
27162
|
+
if (import_typescript11.default.isObjectLiteralExpression(current)) {
|
|
27163
|
+
for (const property of current.properties) {
|
|
27164
|
+
if (!import_typescript11.default.isPropertyAssignment(property) || !ROLE_FIELD.test(propertyNameText(property.name) ?? ""))
|
|
27165
|
+
continue;
|
|
27166
|
+
const value = unwrap(property.initializer);
|
|
27167
|
+
if (import_typescript11.default.isStringLiteral(value))
|
|
27168
|
+
keys.add(value.text);
|
|
27169
|
+
}
|
|
27170
|
+
}
|
|
27171
|
+
if (import_typescript11.default.isVariableDeclaration(current) && import_typescript11.default.isIdentifier(current.name)) {
|
|
27172
|
+
const target = current;
|
|
27173
|
+
for (const reference of this.referencesOf(program, current.name.text)) {
|
|
27174
|
+
if (reference === current.name || isDeclarationName(reference))
|
|
27175
|
+
continue;
|
|
27176
|
+
if (valueDeclaration(resolvedSymbol(reference, checker)) !== target)
|
|
27177
|
+
continue;
|
|
27178
|
+
walk(reference, hops + 1);
|
|
27179
|
+
}
|
|
27180
|
+
return;
|
|
27181
|
+
}
|
|
27182
|
+
}
|
|
27183
|
+
};
|
|
27184
|
+
walk(list, 0);
|
|
27185
|
+
for (const agent2 of this.concrete()) {
|
|
27186
|
+
if (Object.entries(agent2.fields).some(([field, value]) => ROLE_FIELD.test(field) && keys.has(value)))
|
|
27187
|
+
direct.add(agent2.name);
|
|
27188
|
+
}
|
|
27189
|
+
return [...direct].sort();
|
|
27190
|
+
}
|
|
27191
|
+
}
|
|
27192
|
+
function combineVia(current, edge) {
|
|
27193
|
+
if (current === "open" || edge === "open")
|
|
27194
|
+
return "open";
|
|
27195
|
+
if (current === "rpc" || edge === "rpc")
|
|
27196
|
+
return "rpc";
|
|
27197
|
+
return "direct";
|
|
27198
|
+
}
|
|
27199
|
+
function nodeId(node) {
|
|
27200
|
+
return `${node.getSourceFile().fileName}:${node.getStart()}`;
|
|
27201
|
+
}
|
|
27202
|
+
function fieldGuards(node, cls) {
|
|
27203
|
+
const out = [];
|
|
27204
|
+
let child = node;
|
|
27205
|
+
for (let current = node.parent;current && current !== cls; child = current, current = current.parent) {
|
|
27206
|
+
let condition;
|
|
27207
|
+
let whenTrue;
|
|
27208
|
+
if (import_typescript11.default.isConditionalExpression(current) && child !== current.condition) {
|
|
27209
|
+
condition = current.condition;
|
|
27210
|
+
whenTrue = child === current.whenTrue;
|
|
27211
|
+
} else if (import_typescript11.default.isIfStatement(current) && child !== current.expression) {
|
|
27212
|
+
condition = current.expression;
|
|
27213
|
+
whenTrue = child === current.thenStatement;
|
|
27214
|
+
}
|
|
27215
|
+
if (!condition || whenTrue === undefined)
|
|
27216
|
+
continue;
|
|
27217
|
+
const test = unwrap(condition);
|
|
27218
|
+
if (!import_typescript11.default.isBinaryExpression(test))
|
|
27219
|
+
continue;
|
|
27220
|
+
const op = test.operatorToken.kind;
|
|
27221
|
+
const equal = op === import_typescript11.default.SyntaxKind.EqualsEqualsEqualsToken || op === import_typescript11.default.SyntaxKind.EqualsEqualsToken;
|
|
27222
|
+
const notEqual = op === import_typescript11.default.SyntaxKind.ExclamationEqualsEqualsToken || op === import_typescript11.default.SyntaxKind.ExclamationEqualsToken;
|
|
27223
|
+
if (!equal && !notEqual)
|
|
27224
|
+
continue;
|
|
27225
|
+
const sides = [unwrap(test.left), unwrap(test.right)];
|
|
27226
|
+
const access = sides.find((side) => import_typescript11.default.isPropertyAccessExpression(side) && side.expression.kind === import_typescript11.default.SyntaxKind.ThisKeyword);
|
|
27227
|
+
const literal = sides.find((side) => import_typescript11.default.isStringLiteral(side));
|
|
27228
|
+
if (!access || !literal)
|
|
27229
|
+
continue;
|
|
27230
|
+
const wantEqual = equal === whenTrue;
|
|
27231
|
+
const value = literal.text;
|
|
27232
|
+
out.push({ field: access.name.text, holds: (actual) => actual === undefined || actual === value === wantEqual });
|
|
27233
|
+
}
|
|
27234
|
+
return out;
|
|
27235
|
+
}
|
|
27236
|
+
function isWithin3(node, container) {
|
|
27237
|
+
for (let current = node;current; current = current.parent)
|
|
27238
|
+
if (current === container)
|
|
27239
|
+
return true;
|
|
27240
|
+
return false;
|
|
27241
|
+
}
|
|
27242
|
+
function superclassOf(node, checker) {
|
|
27243
|
+
const heritage = node.heritageClauses?.find((clause) => clause.token === import_typescript11.default.SyntaxKind.ExtendsKeyword)?.types[0];
|
|
27244
|
+
if (!heritage)
|
|
27245
|
+
return;
|
|
27246
|
+
const expression = unwrap(heritage.expression);
|
|
27247
|
+
const declaration = valueDeclaration(resolvedSymbol(import_typescript11.default.isPropertyAccessExpression(expression) ? expression.name : expression, checker));
|
|
27248
|
+
return declaration && (import_typescript11.default.isClassDeclaration(declaration) || import_typescript11.default.isClassExpression(declaration)) ? declaration : undefined;
|
|
27249
|
+
}
|
|
27250
|
+
function classFields(node) {
|
|
27251
|
+
const fields = {};
|
|
27252
|
+
for (const member of node.members) {
|
|
27253
|
+
if (!import_typescript11.default.isPropertyDeclaration(member) || !member.initializer)
|
|
27254
|
+
continue;
|
|
27255
|
+
const name = propertyNameText(member.name);
|
|
27256
|
+
if (!name)
|
|
27257
|
+
continue;
|
|
27258
|
+
const value = unwrap(member.initializer);
|
|
27259
|
+
if (import_typescript11.default.isStringLiteral(value) || import_typescript11.default.isNoSubstitutionTemplateLiteral(value))
|
|
27260
|
+
fields[name] = value.text;
|
|
27261
|
+
else if (value.kind === import_typescript11.default.SyntaxKind.TrueKeyword)
|
|
27262
|
+
fields[name] = "true";
|
|
27263
|
+
else if (value.kind === import_typescript11.default.SyntaxKind.FalseKeyword)
|
|
27264
|
+
fields[name] = "false";
|
|
27265
|
+
}
|
|
27266
|
+
return fields;
|
|
27267
|
+
}
|
|
27268
|
+
function holderName(node) {
|
|
27269
|
+
let found;
|
|
27270
|
+
for (let current = node.parent;current; current = current.parent) {
|
|
27271
|
+
if (import_typescript11.default.isFunctionDeclaration(current) && current.name)
|
|
27272
|
+
return current.name;
|
|
27273
|
+
if (import_typescript11.default.isVariableDeclaration(current) && import_typescript11.default.isIdentifier(current.name)) {
|
|
27274
|
+
found = current.name;
|
|
27275
|
+
const statement = current.parent?.parent;
|
|
27276
|
+
if (statement && import_typescript11.default.isVariableStatement(statement) && import_typescript11.default.isSourceFile(statement.parent))
|
|
27277
|
+
return found;
|
|
27278
|
+
const init = current.initializer ? unwrap(current.initializer) : undefined;
|
|
27279
|
+
if (init && (import_typescript11.default.isArrowFunction(init) || import_typescript11.default.isFunctionExpression(init)))
|
|
27280
|
+
return found;
|
|
27281
|
+
}
|
|
27282
|
+
}
|
|
27283
|
+
return found;
|
|
27284
|
+
}
|
|
27285
|
+
function isDeclarationName(identifier) {
|
|
27286
|
+
const parent = identifier.parent;
|
|
27287
|
+
if (!parent)
|
|
27288
|
+
return false;
|
|
27289
|
+
if (import_typescript11.default.isImportSpecifier(parent) || import_typescript11.default.isImportClause(parent) || import_typescript11.default.isNamespaceImport(parent) || import_typescript11.default.isExportSpecifier(parent))
|
|
27290
|
+
return true;
|
|
27291
|
+
return (import_typescript11.default.isVariableDeclaration(parent) || import_typescript11.default.isFunctionDeclaration(parent) || import_typescript11.default.isClassDeclaration(parent) || import_typescript11.default.isParameter(parent) || import_typescript11.default.isPropertyDeclaration(parent) || import_typescript11.default.isMethodDeclaration(parent)) && parent.name === identifier;
|
|
27292
|
+
}
|
|
27293
|
+
function stringEntries(expression, checker, depth = 0) {
|
|
27294
|
+
if (depth > 4)
|
|
27295
|
+
return [];
|
|
27296
|
+
const value = unwrap(expression);
|
|
27297
|
+
if (import_typescript11.default.isIdentifier(value)) {
|
|
27298
|
+
const declaration = valueDeclaration(resolvedSymbol(value, checker));
|
|
27299
|
+
return declaration && import_typescript11.default.isVariableDeclaration(declaration) && declaration.initializer ? stringEntries(declaration.initializer, checker, depth + 1) : [];
|
|
27300
|
+
}
|
|
27301
|
+
if (!import_typescript11.default.isArrayLiteralExpression(value))
|
|
27302
|
+
return [];
|
|
27303
|
+
const out = [];
|
|
27304
|
+
for (const element of value.elements) {
|
|
27305
|
+
if (import_typescript11.default.isSpreadElement(element)) {
|
|
27306
|
+
out.push(...stringEntries(element.expression, checker, depth + 1));
|
|
27307
|
+
continue;
|
|
27308
|
+
}
|
|
27309
|
+
const text = constantString(element, checker, depth);
|
|
27310
|
+
if (text !== undefined)
|
|
27311
|
+
out.push(text);
|
|
27312
|
+
}
|
|
27313
|
+
return out;
|
|
27314
|
+
}
|
|
27315
|
+
function constantString(expression, checker, depth) {
|
|
27316
|
+
const value = unwrap(expression);
|
|
27317
|
+
if (import_typescript11.default.isStringLiteral(value) || import_typescript11.default.isNoSubstitutionTemplateLiteral(value))
|
|
27318
|
+
return value.text;
|
|
27319
|
+
if (depth > 4)
|
|
27320
|
+
return;
|
|
27321
|
+
if (import_typescript11.default.isIdentifier(value)) {
|
|
27322
|
+
const declaration = valueDeclaration(resolvedSymbol(value, checker));
|
|
27323
|
+
return declaration && import_typescript11.default.isVariableDeclaration(declaration) && declaration.initializer ? constantString(declaration.initializer, checker, depth + 1) : undefined;
|
|
27324
|
+
}
|
|
27325
|
+
if (import_typescript11.default.isTemplateExpression(value)) {
|
|
27326
|
+
let text = value.head.text;
|
|
27327
|
+
for (const span of value.templateSpans) {
|
|
27328
|
+
const part = constantString(span.expression, checker, depth + 1);
|
|
27329
|
+
if (part === undefined)
|
|
27330
|
+
return;
|
|
27331
|
+
text += part + span.literal.text;
|
|
27332
|
+
}
|
|
27333
|
+
return text;
|
|
27334
|
+
}
|
|
27335
|
+
return;
|
|
27336
|
+
}
|
|
27337
|
+
|
|
27338
|
+
// src/harness-extract/tools.ts
|
|
27339
|
+
var PASS_THROUGH2 = new Set(["ref", "call", "param", "oneOf", "lookup"]);
|
|
27340
|
+
var DEFINITION_KEYS = new Set(["description", "input_schema", "inputSchema", "parameters", "schema", "execute", "type", "strict"]);
|
|
27341
|
+
function member(tree, key) {
|
|
27342
|
+
const members = tree.children ?? [];
|
|
27343
|
+
for (let index = members.length - 1;index >= 0; index -= 1)
|
|
27344
|
+
if (members[index].key === key)
|
|
27345
|
+
return members[index];
|
|
27346
|
+
return;
|
|
27347
|
+
}
|
|
27348
|
+
function singleLiteral2(tree) {
|
|
27349
|
+
if (!tree)
|
|
27350
|
+
return;
|
|
27351
|
+
if (tree.kind === "literal")
|
|
27352
|
+
return tree.text;
|
|
27353
|
+
if (PASS_THROUGH2.has(tree.kind)) {
|
|
27354
|
+
const flow = valueChildren(tree);
|
|
27355
|
+
if (flow.length === 1)
|
|
27356
|
+
return singleLiteral2(flow[0]);
|
|
27357
|
+
}
|
|
27358
|
+
return;
|
|
27359
|
+
}
|
|
27360
|
+
function objectsOf(tree) {
|
|
27361
|
+
if (tree.kind === "object")
|
|
27362
|
+
return [tree];
|
|
27363
|
+
if (PASS_THROUGH2.has(tree.kind))
|
|
27364
|
+
return valueChildren(tree).flatMap(objectsOf);
|
|
27365
|
+
return [];
|
|
27366
|
+
}
|
|
27367
|
+
function definitionName(tree) {
|
|
27368
|
+
const fn = member(tree, "function");
|
|
27369
|
+
const fnObject = fn ? objectsOf(fn)[0] : undefined;
|
|
27370
|
+
const fnName = fnObject ? singleLiteral2(member(fnObject, "name")) : undefined;
|
|
27371
|
+
if (fnName && fnObject)
|
|
27372
|
+
return { name: fnName, path: fnObject.path, line: fnObject.line, via: "function.name" };
|
|
27373
|
+
const nameNode = member(tree, "name");
|
|
27374
|
+
const name = singleLiteral2(nameNode);
|
|
27375
|
+
const keys = (tree.children ?? []).map((child) => child.key ?? "");
|
|
27376
|
+
if (name && nameNode && keys.some((key) => DEFINITION_KEYS.has(key)))
|
|
27377
|
+
return { name, path: nameNode.path, line: nameNode.line, via: "name" };
|
|
27378
|
+
return;
|
|
27379
|
+
}
|
|
27380
|
+
function toolNamesOf(tree) {
|
|
27381
|
+
const out = { names: [], unresolved: [] };
|
|
27382
|
+
const visit2 = (node, inArray, names) => {
|
|
27383
|
+
if (node.kind === "array" || node.kind === "assembled" && node.text === "array") {
|
|
27384
|
+
for (const child of node.children ?? [])
|
|
27385
|
+
visit2(child, true, names);
|
|
27386
|
+
return;
|
|
27387
|
+
}
|
|
27388
|
+
if (node.kind === "object") {
|
|
27389
|
+
const definition = definitionName(node);
|
|
27390
|
+
if (definition) {
|
|
27391
|
+
names.push(definition);
|
|
27392
|
+
return;
|
|
27393
|
+
}
|
|
27394
|
+
if (inArray) {
|
|
27395
|
+
out.unresolved.push({ ...node, note: "tool object without a literal name" });
|
|
27396
|
+
return;
|
|
27397
|
+
}
|
|
27398
|
+
const record = new Map;
|
|
27399
|
+
const set = (hit) => {
|
|
27400
|
+
record.delete(hit.name);
|
|
27401
|
+
record.set(hit.name, hit);
|
|
27402
|
+
};
|
|
27403
|
+
for (const child of node.children ?? []) {
|
|
27404
|
+
if (child.key === "...") {
|
|
27405
|
+
const spread = [];
|
|
27406
|
+
visit2(child, false, spread);
|
|
27407
|
+
spread.forEach(set);
|
|
27408
|
+
} else if (child.key) {
|
|
27409
|
+
set({ name: child.key, path: child.path, line: child.line, via: "key" });
|
|
27410
|
+
}
|
|
27411
|
+
}
|
|
27412
|
+
names.push(...record.values());
|
|
27413
|
+
return;
|
|
27414
|
+
}
|
|
27415
|
+
if (PASS_THROUGH2.has(node.kind) && valueChildren(node).length > 0) {
|
|
27416
|
+
for (const child of valueChildren(node))
|
|
27417
|
+
visit2(child, inArray, names);
|
|
27418
|
+
return;
|
|
27419
|
+
}
|
|
27420
|
+
out.unresolved.push(node);
|
|
27421
|
+
};
|
|
27422
|
+
visit2(tree, false, out.names);
|
|
27423
|
+
return out;
|
|
27424
|
+
}
|
|
27425
|
+
|
|
27426
|
+
// src/harness-extract/emit-tools.ts
|
|
27427
|
+
var SCHEMA_KEYS = ["input_schema", "inputSchema", "parameters", "output_schema"];
|
|
27428
|
+
var TOOL_BODY_KEYS = ["execute", "handler"];
|
|
27429
|
+
var TOOL_FACTORY = /^(?:tool|dynamicTool|agentTool|createTool|defineTool)$/;
|
|
27430
|
+
var TOOL_MODULE = /^(?:ai|agents(?:\/.*)?|@cloudflare\/[\w-]+(?:\/.*)?|@mastra\/[\w-]+|@ai-sdk\/[\w-]+|@openai\/agents(?:-core)?|@langchain\/[\w-]+)$/;
|
|
27431
|
+
var MODEL_FACING_NAME = /^[A-Za-z_][\w.:-]{0,127}$/;
|
|
27432
|
+
var MAX_NAMES_PER_SITE = 64;
|
|
27433
|
+
var MAX_TOOLS = 1e4;
|
|
27434
|
+
var MAX_EVIDENCE_PER_TOOL = 64;
|
|
27435
|
+
var MAX_DEFINITIONS_PER_TOOL = 64;
|
|
27436
|
+
var MAX_DEFINITION_BYTES = 8 * 1024 * 1024;
|
|
27437
|
+
var MAX_BODY_BYTES = 64000;
|
|
27438
|
+
function emitTools(result, ctx, options = {}) {
|
|
27439
|
+
const owners = options.owners ?? new ToolOwners(ctx);
|
|
27440
|
+
const agentForScope = createAgentScopeResolver(result, ctx);
|
|
27441
|
+
let reach;
|
|
27442
|
+
try {
|
|
27443
|
+
reach = buildImportGraphReachability(ctx.rootDir);
|
|
27444
|
+
} catch {
|
|
27445
|
+
reach = undefined;
|
|
27446
|
+
}
|
|
27447
|
+
const skipPath = (rel2) => isTestPath(rel2) || isOneOffScript(rel2, reach);
|
|
27448
|
+
const tracers = new Map;
|
|
27449
|
+
const tracerFor = (program) => {
|
|
27450
|
+
let tracer = tracers.get(program);
|
|
27451
|
+
if (!tracer) {
|
|
27452
|
+
const checker = program.getTypeChecker();
|
|
27453
|
+
tracer = new ValueTracer(ctx.rootDir, checker, new CallIndex(program, checker), { maxDepth: 8, maxNodes: 400, maxCallers: 8 });
|
|
27454
|
+
tracers.set(program, tracer);
|
|
27455
|
+
}
|
|
27456
|
+
return tracer;
|
|
27457
|
+
};
|
|
27458
|
+
const packageOf = workspacePackages(ctx.rootDir);
|
|
27459
|
+
const found = new Map;
|
|
27460
|
+
const ownersByTool = new Map;
|
|
27461
|
+
let definitionBytes = 0;
|
|
27462
|
+
const limit = () => {
|
|
27463
|
+
const warning = "tools: bounded tool catalog budget reached; some evidence or bodies omitted";
|
|
27464
|
+
if (!result.warnings.includes(warning))
|
|
27465
|
+
result.warnings.push(warning);
|
|
27466
|
+
};
|
|
27467
|
+
const add = (name, via, evidence, toolOwners, definition, displayName, enclosing) => {
|
|
27468
|
+
if (!MODEL_FACING_NAME.test(name))
|
|
27469
|
+
return;
|
|
27470
|
+
const pkg = packageOf(evidence.path);
|
|
27471
|
+
const key = name;
|
|
27472
|
+
let tool = found.get(key);
|
|
27473
|
+
if (!tool) {
|
|
27474
|
+
if (found.size >= MAX_TOOLS) {
|
|
27475
|
+
limit();
|
|
27476
|
+
return;
|
|
27477
|
+
}
|
|
27478
|
+
tool = { name, status: enumerationRepository(ctx).status(evidence.path) === "defined_only" ? "defined_only" : "reachable", ...pkg ? { package: pkg } : {}, via, evidence: [], agents: [], calledFrom: [], definitions: [] };
|
|
27479
|
+
found.set(key, tool);
|
|
27480
|
+
ownersByTool.set(key, { direct: [], rpc: [], open: [] });
|
|
27481
|
+
}
|
|
27482
|
+
if (enumerationRepository(ctx).status(evidence.path) !== "defined_only")
|
|
27483
|
+
tool.status = "reachable";
|
|
27484
|
+
if (tool.evidence.length < MAX_EVIDENCE_PER_TOOL && !tool.evidence.some((other) => other.path === evidence.path && other.line === evidence.line)) {
|
|
27485
|
+
tool.evidence.push(evidence);
|
|
27486
|
+
tool.calledFrom.push({ path: evidence.path, line: evidence.line, ...enclosing ? { enclosing } : {} });
|
|
27487
|
+
}
|
|
27488
|
+
if (definition) {
|
|
27489
|
+
const source = definition.getSourceFile();
|
|
27490
|
+
const path = relPath(ctx.rootDir, source.fileName), start = definition.getStart(source);
|
|
27491
|
+
if (tool.definitions.length < MAX_DEFINITIONS_PER_TOOL && !tool.definitions.some((other) => other.path === path && other.start === start)) {
|
|
27492
|
+
const text = Buffer.from(definition.getText(source));
|
|
27493
|
+
if (text.length > MAX_BODY_BYTES) {
|
|
27494
|
+
const warning = `tools: ${name} definition at ${path}:${lineOf(definition)} exceeds ${MAX_BODY_BYTES} bytes; its body is truncated`;
|
|
27495
|
+
if (!result.warnings.includes(warning))
|
|
27496
|
+
result.warnings.push(warning);
|
|
27497
|
+
}
|
|
27498
|
+
const body = text.subarray(0, Math.max(0, Math.min(MAX_BODY_BYTES, MAX_DEFINITION_BYTES - definitionBytes))).toString("utf8");
|
|
27499
|
+
definitionBytes += Buffer.byteLength(body);
|
|
27500
|
+
if (definitionBytes >= MAX_DEFINITION_BYTES)
|
|
27501
|
+
limit();
|
|
27502
|
+
tool.definitions.push({ path, start, end: definition.getEnd(), line: lineOf(definition), scope: scopeOf(ctx.rootDir, definition), body });
|
|
27503
|
+
}
|
|
27504
|
+
}
|
|
27505
|
+
const merged = ownersByTool.get(key);
|
|
27506
|
+
for (const agent2 of toolOwners.direct)
|
|
27507
|
+
if (!merged.direct.includes(agent2))
|
|
27508
|
+
merged.direct.push(agent2);
|
|
27509
|
+
for (const agent2 of toolOwners.rpc)
|
|
27510
|
+
if (!merged.rpc.includes(agent2))
|
|
27511
|
+
merged.rpc.push(agent2);
|
|
27512
|
+
for (const agent2 of toolOwners.open)
|
|
27513
|
+
if (!merged.open.includes(agent2))
|
|
27514
|
+
merged.open.push(agent2);
|
|
27515
|
+
tool.displayName ??= displayName;
|
|
27516
|
+
};
|
|
27517
|
+
for (const { program, source, rel: rel2 } of sourceFilesOf(ctx)) {
|
|
27518
|
+
if (skipPath(rel2))
|
|
27519
|
+
continue;
|
|
27520
|
+
const checker = program.getTypeChecker();
|
|
27521
|
+
const tracer = tracerFor(program);
|
|
27522
|
+
const names = (expression) => literalStrings(expression, tracer).slice(0, MAX_NAMES_PER_SITE);
|
|
27523
|
+
const at = (node, reason) => ({ path: rel2, line: lineOf(node), reason });
|
|
27524
|
+
const visitObject = (object) => {
|
|
27525
|
+
const keys = object.properties.map((property) => propertyNameText(property.name) ?? "");
|
|
27526
|
+
const nameExpr = propertyValue(object, "name");
|
|
27527
|
+
if (nameExpr && SCHEMA_KEYS.some((key) => keys.includes(key)) && !isSchemaProperty(object)) {
|
|
27528
|
+
const list = names(nameExpr);
|
|
27529
|
+
if (list.length > 0) {
|
|
27530
|
+
const span = registryEntryOf(object);
|
|
27531
|
+
const agents = owners.ownersOf(span, program);
|
|
27532
|
+
for (const name of list)
|
|
27533
|
+
add(name, "definition", at(nameExpr, "tool definition (name beside its input schema)"), agents, span, displayNameOf(object, tracer), enclosingName(object));
|
|
27534
|
+
}
|
|
27535
|
+
}
|
|
27536
|
+
const toolNameExpr = propertyValue(object, "toolName");
|
|
27537
|
+
if (toolNameExpr && keys.includes("description")) {
|
|
27538
|
+
const list = names(toolNameExpr);
|
|
27539
|
+
if (list.length > 0) {
|
|
27540
|
+
const agents = owners.ownersOf(object, program);
|
|
27541
|
+
for (const name of list)
|
|
27542
|
+
add(name, "role-table", at(toolNameExpr, "role table row naming the tool the model sees"), agents, object, displayNameOf(object, tracer), enclosingName(object));
|
|
27543
|
+
}
|
|
27544
|
+
}
|
|
27545
|
+
for (const property of object.properties) {
|
|
27546
|
+
if (!import_typescript12.default.isPropertyAssignment(property) || !isToolValue(property.initializer, checker))
|
|
27547
|
+
continue;
|
|
27548
|
+
const list = import_typescript12.default.isComputedPropertyName(property.name) ? names(property.name.expression) : [propertyNameText(property.name)].filter((key) => Boolean(key));
|
|
27549
|
+
if (list.length === 0)
|
|
27550
|
+
continue;
|
|
27551
|
+
const agents = owners.ownersOf(property, program);
|
|
27552
|
+
const options2 = toolOptions(property.initializer);
|
|
27553
|
+
const definition = definitionNode(property.initializer, checker);
|
|
27554
|
+
for (const name of list)
|
|
27555
|
+
add(name, "record", at(property, "tools record key"), agents, definition, options2 ? displayNameOf(options2, tracer) : undefined, enclosingName(property));
|
|
27556
|
+
}
|
|
27557
|
+
};
|
|
27558
|
+
const visit2 = (node) => {
|
|
27559
|
+
try {
|
|
27560
|
+
if (import_typescript12.default.isObjectLiteralExpression(node)) {
|
|
27561
|
+
visitObject(node);
|
|
27562
|
+
} else if (import_typescript12.default.isBinaryExpression(node) && node.operatorToken.kind === import_typescript12.default.SyntaxKind.EqualsToken && import_typescript12.default.isElementAccessExpression(node.left) && isToolValue(node.right, checker)) {
|
|
27563
|
+
const list = names(node.left.argumentExpression);
|
|
27564
|
+
if (list.length > 0) {
|
|
27565
|
+
const agents = owners.ownersOf(node, program);
|
|
27566
|
+
const options2 = toolOptions(node.right);
|
|
27567
|
+
const definition = definitionNode(node.right, checker);
|
|
27568
|
+
for (const name of list)
|
|
27569
|
+
add(name, "record", at(node, "tool stored under this key"), agents, definition, options2 ? displayNameOf(options2, tracer) : undefined, enclosingName(node));
|
|
27570
|
+
}
|
|
27571
|
+
}
|
|
27572
|
+
} catch {}
|
|
27573
|
+
import_typescript12.default.forEachChild(node, visit2);
|
|
27574
|
+
};
|
|
27575
|
+
visit2(source);
|
|
27576
|
+
}
|
|
27577
|
+
for (const role of result.roles) {
|
|
27578
|
+
if (role.role !== "tools" || role.sink.isTest || skipPath(role.sink.path))
|
|
27579
|
+
continue;
|
|
27580
|
+
const program = ctx.programFor(role.sink.path);
|
|
27581
|
+
const siteOwners = program ? ownersAtSite(owners, program, role.sink.path, role.sink.line) : { direct: [], rpc: [], open: [] };
|
|
27582
|
+
for (const hit of toolNamesOf(role.tree).names) {
|
|
27583
|
+
if (skipPath(hit.path))
|
|
27584
|
+
continue;
|
|
27585
|
+
add(hit.name, "tools-arg", { path: hit.path, line: hit.line, reason: `named in the tools argument of ${role.sink.callee} at ${role.sink.path}:${role.sink.line}` }, siteOwners, undefined, undefined, role.sink.enclosing);
|
|
27586
|
+
}
|
|
27587
|
+
}
|
|
27588
|
+
owners.indexAllowlists(new Set([...found.values()].map((tool) => tool.name)));
|
|
27589
|
+
const callOwners = new Map;
|
|
27590
|
+
for (const role of result.roles) {
|
|
27591
|
+
if (role.role !== "tools" || role.sink.isTest || skipPath(role.sink.path))
|
|
27592
|
+
continue;
|
|
27593
|
+
const ids = agentForScope(role.sink.scope);
|
|
27594
|
+
for (const hit of toolNamesOf(role.tree).names) {
|
|
27595
|
+
let owners2 = callOwners.get(hit.name);
|
|
27596
|
+
if (!owners2)
|
|
27597
|
+
callOwners.set(hit.name, owners2 = new Set);
|
|
27598
|
+
for (const id of ids)
|
|
27599
|
+
owners2.add(id);
|
|
27600
|
+
}
|
|
27601
|
+
}
|
|
27602
|
+
for (const [key, tool] of found) {
|
|
27603
|
+
const ids = new Set;
|
|
27604
|
+
for (const name of owners.agentsFor(tool.name, ownersByTool.get(key))) {
|
|
27605
|
+
const classes = owners.classes.filter((entry) => entry.name === name);
|
|
27606
|
+
if (classes.length === 1)
|
|
27607
|
+
for (const id of agentForScope(`${classes[0].path}#${name}`))
|
|
27608
|
+
ids.add(id);
|
|
27609
|
+
}
|
|
27610
|
+
for (const id of callOwners.get(tool.name) ?? [])
|
|
27611
|
+
ids.add(id);
|
|
27612
|
+
tool.agents = [...ids].sort();
|
|
27613
|
+
}
|
|
27614
|
+
return [...found.values()].sort((a, b) => a.name.localeCompare(b.name) || (a.package ?? "").localeCompare(b.package ?? ""));
|
|
27615
|
+
}
|
|
27616
|
+
function registryEntryOf(object) {
|
|
27617
|
+
const parent = object.parent;
|
|
27618
|
+
if (parent && import_typescript12.default.isPropertyAssignment(parent) && propertyNameText(parent.name) === "definition" && import_typescript12.default.isObjectLiteralExpression(parent.parent)) {
|
|
27619
|
+
return parent.parent;
|
|
27620
|
+
}
|
|
27621
|
+
return object;
|
|
27622
|
+
}
|
|
27623
|
+
function definitionNode(expression, checker, depth = 0) {
|
|
27624
|
+
const value = unwrap(expression);
|
|
27625
|
+
if (import_typescript12.default.isCallExpression(value) || import_typescript12.default.isObjectLiteralExpression(value))
|
|
27626
|
+
return value;
|
|
27627
|
+
if (import_typescript12.default.isIdentifier(value) && depth < 2) {
|
|
27628
|
+
const declaration = valueDeclaration(resolvedSymbol(value, checker));
|
|
27629
|
+
if (declaration && import_typescript12.default.isVariableDeclaration(declaration) && declaration.initializer)
|
|
27630
|
+
return definitionNode(declaration.initializer, checker, depth + 1);
|
|
27631
|
+
}
|
|
27632
|
+
return;
|
|
27633
|
+
}
|
|
27634
|
+
function ownersAtSite(owners, program, path, line) {
|
|
27635
|
+
const source = program.getSourceFiles().find((file) => file.fileName.endsWith(`/${path}`) || file.fileName === path);
|
|
27636
|
+
if (!source)
|
|
27637
|
+
return { direct: [], rpc: [], open: [] };
|
|
27638
|
+
const position = source.getPositionOfLineAndCharacter(Math.max(0, line - 1), 0);
|
|
27639
|
+
let node = source;
|
|
27640
|
+
const descend = (current) => {
|
|
27641
|
+
for (const child of current.getChildren(source)) {
|
|
27642
|
+
if (child.getStart(source) <= position && position < child.getEnd()) {
|
|
27643
|
+
node = child;
|
|
27644
|
+
descend(child);
|
|
27645
|
+
return;
|
|
27646
|
+
}
|
|
27647
|
+
}
|
|
27648
|
+
};
|
|
27649
|
+
descend(source);
|
|
27650
|
+
return owners.ownersOf(node, program);
|
|
27651
|
+
}
|
|
27652
|
+
function literalStrings(expression, tracer) {
|
|
27653
|
+
const value = unwrap(expression);
|
|
27654
|
+
if (import_typescript12.default.isStringLiteral(value) || import_typescript12.default.isNoSubstitutionTemplateLiteral(value))
|
|
27655
|
+
return [value.text];
|
|
27656
|
+
let tree;
|
|
27657
|
+
try {
|
|
27658
|
+
tree = tracer.trace(value);
|
|
27659
|
+
} catch {
|
|
27660
|
+
return [];
|
|
27661
|
+
}
|
|
27662
|
+
return [...new Set(renderings(tree, MAX_NAMES_PER_SITE).filter((text) => text.length > 0 && !text.includes("${...}")))];
|
|
27663
|
+
}
|
|
27664
|
+
function displayNameOf(object, tracer) {
|
|
27665
|
+
for (const key of ["displayName", "title"]) {
|
|
27666
|
+
const value = propertyValue(object, key);
|
|
27667
|
+
if (!value)
|
|
27668
|
+
continue;
|
|
27669
|
+
const [text] = literalStrings(value, tracer);
|
|
27670
|
+
if (text)
|
|
27671
|
+
return text;
|
|
27672
|
+
}
|
|
27673
|
+
return;
|
|
27674
|
+
}
|
|
27675
|
+
function toolOptions(expression) {
|
|
27676
|
+
const value = unwrap(expression);
|
|
27677
|
+
if (import_typescript12.default.isObjectLiteralExpression(value))
|
|
27678
|
+
return value;
|
|
27679
|
+
if (import_typescript12.default.isCallExpression(value)) {
|
|
27680
|
+
for (const argument of value.arguments) {
|
|
27681
|
+
const inner = unwrap(argument);
|
|
27682
|
+
if (import_typescript12.default.isObjectLiteralExpression(inner))
|
|
27683
|
+
return inner;
|
|
27684
|
+
}
|
|
27685
|
+
}
|
|
27686
|
+
return;
|
|
27687
|
+
}
|
|
27688
|
+
function enclosingName(node) {
|
|
27689
|
+
const parts = [];
|
|
27690
|
+
for (let current = node.parent;current; current = current.parent) {
|
|
27691
|
+
if ((import_typescript12.default.isFunctionDeclaration(current) || import_typescript12.default.isMethodDeclaration(current)) && current.name && parts.length === 0) {
|
|
27692
|
+
parts.unshift(propertyNameText(current.name) ?? "");
|
|
27693
|
+
} else if (import_typescript12.default.isVariableDeclaration(current) && import_typescript12.default.isIdentifier(current.name) && parts.length === 0) {
|
|
27694
|
+
parts.unshift(current.name.text);
|
|
27695
|
+
} else if (import_typescript12.default.isClassLike(current) && current.name) {
|
|
27696
|
+
parts.unshift(current.name.text);
|
|
27697
|
+
break;
|
|
27698
|
+
}
|
|
27699
|
+
}
|
|
27700
|
+
const joined = parts.filter(Boolean).join(".");
|
|
27701
|
+
return joined || undefined;
|
|
27702
|
+
}
|
|
27703
|
+
function isSchemaProperty(object) {
|
|
27704
|
+
const parent = object.parent;
|
|
27705
|
+
if (!parent || !import_typescript12.default.isPropertyAssignment(parent))
|
|
27706
|
+
return false;
|
|
27707
|
+
const holder = parent.parent;
|
|
27708
|
+
return import_typescript12.default.isObjectLiteralExpression(holder) && import_typescript12.default.isPropertyAssignment(holder.parent) && propertyNameText(holder.parent.name) === "properties";
|
|
27709
|
+
}
|
|
27710
|
+
function isToolValue(expression, checker, depth = 0) {
|
|
27711
|
+
const value = unwrap(expression);
|
|
27712
|
+
if (import_typescript12.default.isCallExpression(value)) {
|
|
27713
|
+
const callee = unwrap(value.expression);
|
|
27714
|
+
const name = import_typescript12.default.isIdentifier(callee) ? callee : import_typescript12.default.isPropertyAccessExpression(callee) ? callee.name : undefined;
|
|
27715
|
+
if (!name)
|
|
27716
|
+
return false;
|
|
27717
|
+
if (TOOL_FACTORY.test(name.text))
|
|
27718
|
+
return true;
|
|
27719
|
+
if (import_typescript12.default.isIdentifier(callee)) {
|
|
27720
|
+
const origin = importOrigin(callee, checker);
|
|
27721
|
+
if (origin && TOOL_MODULE.test(origin.module) && /tool/i.test(origin.imported))
|
|
27722
|
+
return true;
|
|
27723
|
+
}
|
|
27724
|
+
return false;
|
|
27725
|
+
}
|
|
27726
|
+
if (import_typescript12.default.isObjectLiteralExpression(value)) {
|
|
27727
|
+
const keys = value.properties.map((property) => propertyNameText(property.name) ?? "");
|
|
27728
|
+
return SCHEMA_KEYS.some((key) => keys.includes(key)) && TOOL_BODY_KEYS.some((key) => keys.includes(key)) && keys.includes("description") && !keys.includes("name");
|
|
27729
|
+
}
|
|
27730
|
+
if (import_typescript12.default.isIdentifier(value) && depth < 2) {
|
|
27731
|
+
const declaration = valueDeclaration(resolvedSymbol(value, checker));
|
|
27732
|
+
if (declaration && import_typescript12.default.isVariableDeclaration(declaration) && declaration.initializer && declaration.parent.flags & import_typescript12.default.NodeFlags.Const) {
|
|
27733
|
+
return isToolValue(declaration.initializer, checker, depth + 1);
|
|
27734
|
+
}
|
|
27735
|
+
}
|
|
27736
|
+
return false;
|
|
27737
|
+
}
|
|
27738
|
+
var emitted = new WeakMap;
|
|
27739
|
+
function emitToolsCached(result, ctx) {
|
|
27740
|
+
let tools = emitted.get(result);
|
|
27741
|
+
if (!tools) {
|
|
27742
|
+
tools = emitTools(result, ctx);
|
|
27743
|
+
emitted.set(result, tools);
|
|
27744
|
+
}
|
|
27745
|
+
return tools;
|
|
27746
|
+
}
|
|
27747
|
+
function toolArtifactId(name, pkg) {
|
|
27748
|
+
const slug2 = (text) => text.replace(/[^A-Za-z0-9_.:-]+/g, "_");
|
|
27749
|
+
return `tool.extract.${slug2(name)}`;
|
|
27750
|
+
}
|
|
27751
|
+
function workspacePackages(rootDir) {
|
|
27752
|
+
const cache2 = new Map;
|
|
27753
|
+
const lookup = (dir) => {
|
|
27754
|
+
if (dir === "" || dir === ".")
|
|
27755
|
+
return "";
|
|
27756
|
+
const cached = cache2.get(dir);
|
|
27757
|
+
if (cached !== undefined)
|
|
27758
|
+
return cached;
|
|
27759
|
+
const found = existsSync9(join14(rootDir, dir, "package.json")) ? dir : lookup(posix3.dirname(dir) === "." ? "" : posix3.dirname(dir));
|
|
27760
|
+
cache2.set(dir, found);
|
|
27761
|
+
return found;
|
|
27762
|
+
};
|
|
27763
|
+
return (rel2) => lookup(posix3.dirname(rel2) === "." ? "" : posix3.dirname(rel2));
|
|
27764
|
+
}
|
|
27765
|
+
function toolDefinitionRefs(tools) {
|
|
27766
|
+
const out = [];
|
|
27767
|
+
for (const tool of tools) {
|
|
27768
|
+
const definition = tool.definitions[0];
|
|
27769
|
+
if (!definition)
|
|
27770
|
+
continue;
|
|
27771
|
+
out.push({
|
|
27772
|
+
toolId: toolArtifactId(tool.name, tool.package),
|
|
27773
|
+
name: tool.name,
|
|
27774
|
+
definition: { path: definition.path, start: definition.start, end: definition.end, line: definition.line },
|
|
27775
|
+
...tool.agents.length > 0 ? { agentIds: [...tool.agents] } : {},
|
|
27776
|
+
scope: definition.scope
|
|
27777
|
+
});
|
|
27778
|
+
}
|
|
27779
|
+
return out;
|
|
27780
|
+
}
|
|
27781
|
+
function toolArtifacts(tools, runtimeAgents = []) {
|
|
27782
|
+
const knownIds = new Set(runtimeAgents.map((agent2) => agent2.id));
|
|
27783
|
+
return tools.map((tool) => {
|
|
27784
|
+
const usedByAgentIds = [...new Set(tool.agents.filter((id) => knownIds.has(id)))];
|
|
27785
|
+
const artifact2 = {
|
|
27786
|
+
id: toolArtifactId(tool.name, tool.package),
|
|
27787
|
+
type: "tool",
|
|
27788
|
+
name: tool.name,
|
|
27789
|
+
status: tool.status,
|
|
27790
|
+
scope: usedByAgentIds.length === 1 ? "agent" : "shared",
|
|
27791
|
+
...tool.definitions[0] ? { sourcePath: tool.definitions[0].path } : {},
|
|
27792
|
+
...tool.definitions[0] ? { body: tool.definitions[0].body } : {},
|
|
27793
|
+
...tool.definitions.length > 1 ? { definitionVariants: tool.definitions.map((definition) => ({ sourcePath: definition.path, body: definition.body, evidence: [{ path: definition.path, line: definition.line }] })) } : {},
|
|
27794
|
+
usedByAgentIds,
|
|
27795
|
+
confidence: 0.9,
|
|
27796
|
+
confidenceReason: `Deterministic: model-facing tool name read from ${tool.via === "tools-arg" ? "a traced tools argument" : "its definition"} (call-site extractor).`,
|
|
27797
|
+
evidence: tool.evidence.map((entry) => ({ path: entry.path, line: entry.line, reason: entry.reason })),
|
|
27798
|
+
calledFrom: tool.calledFrom,
|
|
27799
|
+
agentClasses: [...tool.agents],
|
|
27800
|
+
...tool.displayName ? { displayName: tool.displayName } : {}
|
|
27801
|
+
};
|
|
27802
|
+
return artifact2;
|
|
27803
|
+
});
|
|
27804
|
+
}
|
|
27805
|
+
|
|
27806
|
+
// src/harness-extract/types.ts
|
|
27807
|
+
var EXTRACT_SCHEMA_VERSION = 1;
|
|
27808
|
+
|
|
27809
|
+
// src/harness-extract/index.ts
|
|
27810
|
+
var PROMPT_ROLES = new Set(["system", "user", "messages", "prompt", "instructions", "questions"]);
|
|
27811
|
+
var MAX_HELPER_CALLERS = 200;
|
|
27812
|
+
var MAX_DERIVED = 5000;
|
|
27813
|
+
var MAX_CALLER_CHAIN = 3;
|
|
27814
|
+
var cache2 = new Map;
|
|
27815
|
+
function clearExtractCache() {
|
|
27816
|
+
cache2.clear();
|
|
27817
|
+
}
|
|
27818
|
+
function extractHarness(rootDir, options = {}) {
|
|
27819
|
+
return extractHarnessWithContext(rootDir, options).result;
|
|
27820
|
+
}
|
|
27821
|
+
function extractHarnessWithContext(rootDir, options = {}) {
|
|
27822
|
+
const root = resolve13(rootDir);
|
|
27823
|
+
const { fresh, ...rest } = options;
|
|
27824
|
+
const key = `${root}\x00${JSON.stringify(rest)}`;
|
|
27825
|
+
const fingerprint = sourceFingerprint(root, rest);
|
|
27826
|
+
const cached = fresh ? undefined : cache2.get(key);
|
|
27827
|
+
if (cached && cached.fingerprint === fingerprint)
|
|
27828
|
+
return cached.output;
|
|
27829
|
+
const output = runExtract(root, options);
|
|
27830
|
+
cache2.set(key, { output, fingerprint });
|
|
27831
|
+
return output;
|
|
27832
|
+
}
|
|
27833
|
+
function argRef(rootDir, node, key) {
|
|
27834
|
+
const source = node.getSourceFile();
|
|
27835
|
+
const ref = { path: relPath(rootDir, source.fileName), start: node.getStart(source), end: node.getEnd(), line: lineOf(node) };
|
|
27836
|
+
if (key !== undefined)
|
|
27837
|
+
ref.key = key;
|
|
27838
|
+
Object.defineProperty(ref, "node", { value: node, enumerable: false });
|
|
27839
|
+
return ref;
|
|
27840
|
+
}
|
|
27841
|
+
function namedEnclosingFunction(node) {
|
|
27842
|
+
for (let fn = enclosingFunction(node);fn; fn = enclosingFunction(fn)) {
|
|
27843
|
+
const name = functionName(fn);
|
|
27844
|
+
if (name)
|
|
27845
|
+
return { fn, name };
|
|
27846
|
+
}
|
|
27847
|
+
return;
|
|
27848
|
+
}
|
|
27849
|
+
function qualifiedName(fn, name) {
|
|
27850
|
+
const cls = enclosingClass(fn);
|
|
27851
|
+
const owner = cls && (fn.parent === cls || fn.parent && fn.parent.parent === cls) ? className(cls) : undefined;
|
|
27852
|
+
return owner ? `${owner}.${name}` : name;
|
|
27853
|
+
}
|
|
27854
|
+
function functionScope(rootDir, fn, name) {
|
|
27855
|
+
const parts = [qualifiedName(fn, name)];
|
|
27856
|
+
const object = objectLiteralOf2(fn);
|
|
27857
|
+
if (object) {
|
|
27858
|
+
const holder = object.parent && import_typescript13.default.isVariableDeclaration(object.parent) && import_typescript13.default.isIdentifier(object.parent.name) ? object.parent.name.text : `L${lineOf(object)}`;
|
|
27859
|
+
parts.unshift(holder);
|
|
27860
|
+
}
|
|
27861
|
+
for (let outer = namedEnclosingFunction(object ?? fn);outer; outer = namedEnclosingFunction(outer.fn))
|
|
27862
|
+
parts.unshift(qualifiedName(outer.fn, outer.name));
|
|
27863
|
+
return `${relPath(rootDir, fn.getSourceFile().fileName)}#${parts.join(".")}`;
|
|
27864
|
+
}
|
|
27865
|
+
function objectLiteralOf2(fn) {
|
|
27866
|
+
const member2 = import_typescript13.default.isMethodDeclaration(fn) || import_typescript13.default.isGetAccessorDeclaration(fn) || import_typescript13.default.isSetAccessorDeclaration(fn) ? fn : import_typescript13.default.isPropertyAssignment(fn.parent) ? fn.parent : undefined;
|
|
27867
|
+
return member2 && import_typescript13.default.isObjectLiteralExpression(member2.parent) ? member2.parent : undefined;
|
|
27868
|
+
}
|
|
27869
|
+
function scopeOf(rootDir, node) {
|
|
27870
|
+
const named = namedEnclosingFunction(node);
|
|
27871
|
+
if (named)
|
|
27872
|
+
return functionScope(rootDir, named.fn, named.name);
|
|
27873
|
+
const path = relPath(rootDir, node.getSourceFile().fileName);
|
|
27874
|
+
let outermost;
|
|
27875
|
+
for (let fn = enclosingFunction(node);fn; fn = enclosingFunction(fn))
|
|
27876
|
+
outermost = fn;
|
|
27877
|
+
return outermost ? `${path}#L${lineOf(outermost)}` : path;
|
|
27878
|
+
}
|
|
27879
|
+
function callerChain(rootDir, anchor, calls, memo) {
|
|
27880
|
+
const start = namedEnclosingFunction(anchor);
|
|
27881
|
+
if (!start)
|
|
27882
|
+
return { callers: [], callerScopes: [] };
|
|
27883
|
+
const cached = memo.get(start.fn);
|
|
27884
|
+
if (cached)
|
|
27885
|
+
return cached;
|
|
27886
|
+
const chain = { callers: [], callerScopes: [] };
|
|
27887
|
+
const seen = new Set;
|
|
27888
|
+
let current = start;
|
|
27889
|
+
while (current && !seen.has(current.fn) && chain.callers.length <= MAX_CALLER_CHAIN) {
|
|
27890
|
+
seen.add(current.fn);
|
|
27891
|
+
chain.callers.unshift(qualifiedName(current.fn, current.name));
|
|
27892
|
+
chain.callerScopes.unshift(functionScope(rootDir, current.fn, current.name));
|
|
27893
|
+
const caller = calls.callersOf(current.fn, current.name, 1)[0];
|
|
27894
|
+
current = caller ? namedEnclosingFunction(caller) : undefined;
|
|
27895
|
+
}
|
|
27896
|
+
memo.set(start.fn, chain);
|
|
27897
|
+
return chain;
|
|
27898
|
+
}
|
|
27899
|
+
var AGENT_CLASS_BASES = new Set(["Think", "Agent", "AIChatAgent"]);
|
|
27900
|
+
function findAgentClasses(program, checker, memo) {
|
|
27901
|
+
const out = [];
|
|
27902
|
+
for (const source of program.getSourceFiles()) {
|
|
27903
|
+
if (source.isDeclarationFile || /[\\/]node_modules[\\/]/.test(source.fileName))
|
|
27904
|
+
continue;
|
|
27905
|
+
const visit2 = (node) => {
|
|
27906
|
+
if ((import_typescript13.default.isClassDeclaration(node) || import_typescript13.default.isClassExpression(node)) && node.heritageClauses) {
|
|
27907
|
+
const base = frameworkBaseOf(node, checker, memo).base;
|
|
27908
|
+
if (base && AGENT_CLASS_BASES.has(base))
|
|
27909
|
+
out.push(node);
|
|
27910
|
+
}
|
|
27911
|
+
import_typescript13.default.forEachChild(node, visit2);
|
|
27912
|
+
};
|
|
27913
|
+
visit2(source);
|
|
27914
|
+
}
|
|
27915
|
+
return out;
|
|
27916
|
+
}
|
|
27917
|
+
function describeEnclosing(rootDir, anchor, checker, memo, ownClass) {
|
|
27918
|
+
const fn = ownClass ? undefined : namedEnclosingFunction(anchor);
|
|
27919
|
+
const cls = ownClass ?? enclosingClass(anchor);
|
|
27920
|
+
let enclosingCls;
|
|
26826
27921
|
if (cls) {
|
|
26827
27922
|
const info = frameworkBaseOf(cls, checker, memo);
|
|
26828
27923
|
const at = cls.name ?? cls;
|
|
@@ -26857,35 +27952,35 @@ function siteFields(sources, rootDir) {
|
|
|
26857
27952
|
return out;
|
|
26858
27953
|
}
|
|
26859
27954
|
function paramFlow(node, fn, checker, hops = 0) {
|
|
26860
|
-
if (!
|
|
27955
|
+
if (!import_typescript13.default.isExpression(node) || hops > 2)
|
|
26861
27956
|
return;
|
|
26862
27957
|
const expr = unwrap(node);
|
|
26863
|
-
if (
|
|
27958
|
+
if (import_typescript13.default.isBinaryExpression(expr) && (expr.operatorToken.kind === import_typescript13.default.SyntaxKind.QuestionQuestionToken || expr.operatorToken.kind === import_typescript13.default.SyntaxKind.BarBarToken)) {
|
|
26864
27959
|
return paramFlow(expr.left, fn, checker, hops);
|
|
26865
27960
|
}
|
|
26866
|
-
if (
|
|
27961
|
+
if (import_typescript13.default.isPropertyAccessExpression(expr) && import_typescript13.default.isIdentifier(unwrap(expr.expression))) {
|
|
26867
27962
|
const inner = paramFlow(expr.expression, fn, checker, hops);
|
|
26868
27963
|
return inner && inner.key === undefined ? { param: inner.param, key: expr.name.text } : undefined;
|
|
26869
27964
|
}
|
|
26870
|
-
if (!
|
|
27965
|
+
if (!import_typescript13.default.isIdentifier(expr))
|
|
26871
27966
|
return;
|
|
26872
27967
|
const declaration = valueDeclaration(resolvedSymbol(expr, checker));
|
|
26873
27968
|
if (!declaration)
|
|
26874
27969
|
return;
|
|
26875
|
-
if (
|
|
27970
|
+
if (import_typescript13.default.isParameter(declaration) && declaration.parent === fn && !declaration.dotDotDotToken)
|
|
26876
27971
|
return { param: declaration };
|
|
26877
|
-
if (
|
|
27972
|
+
if (import_typescript13.default.isBindingElement(declaration) && import_typescript13.default.isObjectBindingPattern(declaration.parent) && import_typescript13.default.isParameter(declaration.parent.parent) && declaration.parent.parent.parent === fn && !declaration.dotDotDotToken) {
|
|
26878
27973
|
const key = propertyNameText(declaration.propertyName ?? declaration.name);
|
|
26879
27974
|
return key ? { param: declaration.parent.parent, key } : undefined;
|
|
26880
27975
|
}
|
|
26881
|
-
if (
|
|
27976
|
+
if (import_typescript13.default.isVariableDeclaration(declaration) && declaration.initializer && enclosingFunction(declaration) === fn && declaration.parent.flags & import_typescript13.default.NodeFlags.Const) {
|
|
26882
27977
|
return paramFlow(declaration.initializer, fn, checker, hops + 1);
|
|
26883
27978
|
}
|
|
26884
27979
|
return;
|
|
26885
27980
|
}
|
|
26886
27981
|
function roleParamFlow(arg, fn, checker) {
|
|
26887
27982
|
const object = unwrap(arg.node);
|
|
26888
|
-
if (arg.key !== undefined && !arg.key.includes(".") &&
|
|
27983
|
+
if (arg.key !== undefined && !arg.key.includes(".") && import_typescript13.default.isObjectLiteralExpression(object)) {
|
|
26889
27984
|
const value = propertyValue(object, arg.key);
|
|
26890
27985
|
const flow = value ? paramFlow(value, fn, checker) : undefined;
|
|
26891
27986
|
return flow ? { ...flow, viaProperty: true } : undefined;
|
|
@@ -26897,9 +27992,9 @@ function descendArg(argument, path) {
|
|
|
26897
27992
|
let index = 0;
|
|
26898
27993
|
for (;index < path.length; index += 1) {
|
|
26899
27994
|
let value = unwrap(node);
|
|
26900
|
-
if (
|
|
27995
|
+
if (import_typescript13.default.isCallExpression(value) && value.expression.getText() === "JSON.stringify" && value.arguments[0])
|
|
26901
27996
|
value = unwrap(value.arguments[0]);
|
|
26902
|
-
const next =
|
|
27997
|
+
const next = import_typescript13.default.isObjectLiteralExpression(value) ? propertyValue(value, path[index]) : undefined;
|
|
26903
27998
|
if (!next)
|
|
26904
27999
|
break;
|
|
26905
28000
|
node = next;
|
|
@@ -26976,8 +28071,8 @@ function runExtract(rootDir, options) {
|
|
|
26976
28071
|
};
|
|
26977
28072
|
sites.push({ site, entry, anchor, sources, level: 0 });
|
|
26978
28073
|
};
|
|
26979
|
-
const
|
|
26980
|
-
if (
|
|
28074
|
+
const visit2 = (node) => {
|
|
28075
|
+
if (import_typescript13.default.isCallExpression(node) || import_typescript13.default.isNewExpression(node)) {
|
|
26981
28076
|
for (const recognizer of CALL_SINKS) {
|
|
26982
28077
|
let match;
|
|
26983
28078
|
const begin = performance.now();
|
|
@@ -26992,7 +28087,7 @@ function runExtract(rootDir, options) {
|
|
|
26992
28087
|
break;
|
|
26993
28088
|
}
|
|
26994
28089
|
}
|
|
26995
|
-
} else if (
|
|
28090
|
+
} else if (import_typescript13.default.isClassDeclaration(node) || import_typescript13.default.isClassExpression(node)) {
|
|
26996
28091
|
for (const recognizer of classRecognizers) {
|
|
26997
28092
|
try {
|
|
26998
28093
|
for (const match of recognizer.match(node, ctx2))
|
|
@@ -27002,9 +28097,9 @@ function runExtract(rootDir, options) {
|
|
|
27002
28097
|
}
|
|
27003
28098
|
}
|
|
27004
28099
|
}
|
|
27005
|
-
|
|
28100
|
+
import_typescript13.default.forEachChild(node, visit2);
|
|
27006
28101
|
};
|
|
27007
|
-
|
|
28102
|
+
visit2(source);
|
|
27008
28103
|
}
|
|
27009
28104
|
} catch (error) {
|
|
27010
28105
|
warnings.push(`sinks ${entry.configPath ?? "(fallback)"}: ${error.message}`);
|
|
@@ -27054,7 +28149,7 @@ function runExtract(rootDir, options) {
|
|
|
27054
28149
|
const sources = [];
|
|
27055
28150
|
for (const { source: role, flow } of flowing) {
|
|
27056
28151
|
const argument = call.arguments[named.fn.parameters.indexOf(flow.param)];
|
|
27057
|
-
if (!argument ||
|
|
28152
|
+
if (!argument || import_typescript13.default.isSpreadElement(argument))
|
|
27058
28153
|
continue;
|
|
27059
28154
|
const arg = descendArg(argument, [flow.key, flow.viaProperty ? undefined : role.arg.key].filter((part) => Boolean(part)));
|
|
27060
28155
|
sources.push({ ...role, bindings: extendBindings(role.bindings, callerBindings), arg });
|
|
@@ -27183,11 +28278,21 @@ function runExtract(rootDir, options) {
|
|
|
27183
28278
|
timings.models = Date.now() - begin;
|
|
27184
28279
|
result.durationMs = Date.now() - started;
|
|
27185
28280
|
}
|
|
28281
|
+
if (options.tools ?? true) {
|
|
28282
|
+
const begin = Date.now();
|
|
28283
|
+
try {
|
|
28284
|
+
result.toolDefinitions = toolDefinitionRefs(emitToolsCached(result, ctx));
|
|
28285
|
+
} catch (error) {
|
|
28286
|
+
warnings.push(`tools: ${error.message}`);
|
|
28287
|
+
}
|
|
28288
|
+
timings.tools = Date.now() - begin;
|
|
28289
|
+
result.durationMs = Date.now() - started;
|
|
28290
|
+
}
|
|
27186
28291
|
return { result, ctx };
|
|
27187
28292
|
}
|
|
27188
28293
|
function nodeSpanning(source, start, end) {
|
|
27189
28294
|
let best;
|
|
27190
|
-
const
|
|
28295
|
+
const visit2 = (node) => {
|
|
27191
28296
|
const nodeStart = node.getStart(source);
|
|
27192
28297
|
if (nodeStart > start || node.getEnd() < end)
|
|
27193
28298
|
return;
|
|
@@ -27196,9 +28301,9 @@ function nodeSpanning(source, start, end) {
|
|
|
27196
28301
|
return;
|
|
27197
28302
|
}
|
|
27198
28303
|
best = node;
|
|
27199
|
-
|
|
28304
|
+
import_typescript13.default.forEachChild(node, visit2);
|
|
27200
28305
|
};
|
|
27201
|
-
|
|
28306
|
+
visit2(source);
|
|
27202
28307
|
return best;
|
|
27203
28308
|
}
|
|
27204
28309
|
function serializeExtractResult(result) {
|
|
@@ -27242,4 +28347,853 @@ function assignAgentIds(result, agentForScope) {
|
|
|
27242
28347
|
}
|
|
27243
28348
|
}
|
|
27244
28349
|
|
|
27245
|
-
|
|
28350
|
+
// src/harness-extract/enumerators/agents.ts
|
|
28351
|
+
function agentForScope(scope, enumeration) {
|
|
28352
|
+
return enumeration.agentForScope(scope);
|
|
28353
|
+
}
|
|
28354
|
+
function createAgentScopeResolver(result, ctx) {
|
|
28355
|
+
return enumerateAgents(result, ctx).agentForScope;
|
|
28356
|
+
}
|
|
28357
|
+
var enumerations = new WeakMap;
|
|
28358
|
+
var AGENT_BASES4 = new Set(["Think", "Agent", "AIChatAgent", "McpAgent"]);
|
|
28359
|
+
var HTTP_METHODS2 = new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]);
|
|
28360
|
+
function routeOf(fn, file) {
|
|
28361
|
+
const name = functionName(fn);
|
|
28362
|
+
if (name && HTTP_METHODS2.has(name) && /(?:^|\/)(?:route|\+server)\.[cm]?[jt]sx?$/.test(file.path))
|
|
28363
|
+
return `${name} ${file.path}`;
|
|
28364
|
+
const parent = fn.parent;
|
|
28365
|
+
if (parent && import_typescript14.default.isCallExpression(parent) && import_typescript14.default.isPropertyAccessExpression(parent.expression) && /^(get|post|put|patch|delete|all|on)$/.test(parent.expression.name.text)) {
|
|
28366
|
+
const path = parent.arguments.find(import_typescript14.default.isStringLiteralLike);
|
|
28367
|
+
if (path)
|
|
28368
|
+
return `${parent.expression.name.text.toUpperCase()} ${path.text} (${file.path})`;
|
|
28369
|
+
}
|
|
28370
|
+
return;
|
|
28371
|
+
}
|
|
28372
|
+
function enumerateAgents(result, ctx) {
|
|
28373
|
+
const cached = enumerations.get(ctx);
|
|
28374
|
+
if (cached)
|
|
28375
|
+
return cached;
|
|
28376
|
+
const repo = enumerationRepository(ctx);
|
|
28377
|
+
const scopes = new Map;
|
|
28378
|
+
const byNode = new Map;
|
|
28379
|
+
const classes = [];
|
|
28380
|
+
const bound = new Set(detectBoundRuntimeClasses(ctx.rootDir).filter((x) => x.kind === "durable_object").map((x) => x.className));
|
|
28381
|
+
const memo = createFrameworkMemo();
|
|
28382
|
+
const keyOf = (node) => `${node.getSourceFile().fileName}:${node.getStart()}`;
|
|
28383
|
+
for (const file of repo.files) {
|
|
28384
|
+
if (!/\.[cm]?[jt]sx?$/.test(file.path) || TEST_PATH4.test(file.path) || EVAL_PATH2.test(file.path) || /^scripts\//.test(file.path) && !repo.deployedFiles.has(file.path))
|
|
28385
|
+
continue;
|
|
28386
|
+
const checker = ctx.checkerFor(file.path);
|
|
28387
|
+
visit(file.source, (node) => {
|
|
28388
|
+
if ((import_typescript14.default.isClassDeclaration(node) || import_typescript14.default.isClassExpression(node)) && node.name) {
|
|
28389
|
+
const name2 = node.name.text;
|
|
28390
|
+
const info = checker ? frameworkBaseOf(node, checker, memo) : undefined;
|
|
28391
|
+
const directBase = node.heritageClauses?.flatMap((h) => h.types).map((x) => x.expression.getText()) ?? [];
|
|
28392
|
+
const protocolEvents = new Set;
|
|
28393
|
+
visit(node, (member2) => {
|
|
28394
|
+
if (import_typescript14.default.isStringLiteralLike(member2))
|
|
28395
|
+
protocolEvents.add(member2.text);
|
|
28396
|
+
});
|
|
28397
|
+
const realtime = protocolEvents.has("session.update") && protocolEvents.has("response.create") && [...protocolEvents].some((event) => event.includes("function_call"));
|
|
28398
|
+
const exported = import_typescript14.default.getCombinedModifierFlags(node) & import_typescript14.default.ModifierFlags.Export;
|
|
28399
|
+
const abstract = import_typescript14.default.getCombinedModifierFlags(node) & import_typescript14.default.ModifierFlags.Abstract;
|
|
28400
|
+
if (!abstract && (exported || bound.has(name2)) && (info?.base && AGENT_BASES4.has(info.base) || directBase.some((x) => AGENT_BASES4.has(x)) || exported && realtime)) {
|
|
28401
|
+
classes.push({ file, node, name: name2, chain: info?.chain ?? [name2, ...directBase] });
|
|
28402
|
+
}
|
|
28403
|
+
}
|
|
28404
|
+
if (!isFunctionLike(node) || !node.body)
|
|
28405
|
+
return;
|
|
28406
|
+
const name = functionName(node);
|
|
28407
|
+
const cls = enclosingClass(node)?.name?.text;
|
|
28408
|
+
const route = routeOf(node, file);
|
|
28409
|
+
const label = cls && name ? `${cls}.${name}` : name ?? route ?? `${file.path}:${lineOf5(node)}`;
|
|
28410
|
+
const scope = {
|
|
28411
|
+
key: keyOf(node),
|
|
28412
|
+
label,
|
|
28413
|
+
aliases: [label, ...name ? [name] : [], ...route ? [route] : [], `${file.path}::${label}`, `${file.path}#${label}`],
|
|
28414
|
+
file,
|
|
28415
|
+
node,
|
|
28416
|
+
className: cls,
|
|
28417
|
+
route,
|
|
28418
|
+
calls: new Set,
|
|
28419
|
+
virtualCalls: new Map,
|
|
28420
|
+
deployedCalls: new Set,
|
|
28421
|
+
callsByWorker: new Map(repo.workers.map((worker) => [worker.id, new Set])),
|
|
28422
|
+
callers: new Set,
|
|
28423
|
+
hasModel: false,
|
|
28424
|
+
modelWorkers: new Set,
|
|
28425
|
+
hasTools: false,
|
|
28426
|
+
toolFeedback: false,
|
|
28427
|
+
configuredSteps: false,
|
|
28428
|
+
hasLoop: false,
|
|
28429
|
+
forcedOutput: false
|
|
28430
|
+
};
|
|
28431
|
+
const qualified = (fn, fnName) => {
|
|
28432
|
+
const owner = enclosingClass(fn);
|
|
28433
|
+
return owner?.name && (fn.parent === owner || fn.parent?.parent === owner) ? `${owner.name.text}.${fnName}` : fnName;
|
|
28434
|
+
};
|
|
28435
|
+
if (name) {
|
|
28436
|
+
const parts = [qualified(node, name)];
|
|
28437
|
+
const member2 = import_typescript14.default.isMethodDeclaration(node) || import_typescript14.default.isGetAccessorDeclaration(node) || import_typescript14.default.isSetAccessorDeclaration(node) ? node : import_typescript14.default.isPropertyAssignment(node.parent) ? node.parent : undefined;
|
|
28438
|
+
const object = member2 && import_typescript14.default.isObjectLiteralExpression(member2.parent) ? member2.parent : undefined;
|
|
28439
|
+
if (object)
|
|
28440
|
+
parts.unshift(import_typescript14.default.isVariableDeclaration(object.parent) && import_typescript14.default.isIdentifier(object.parent.name) ? object.parent.name.text : `L${lineOf5(object)}`);
|
|
28441
|
+
for (let outer = (object ?? node).parent;outer; outer = outer.parent) {
|
|
28442
|
+
if (isFunctionLike(outer)) {
|
|
28443
|
+
const outerName = functionName(outer);
|
|
28444
|
+
if (outerName)
|
|
28445
|
+
parts.unshift(qualified(outer, outerName));
|
|
28446
|
+
}
|
|
28447
|
+
}
|
|
28448
|
+
scope.aliases.push(`${file.path}#${parts.join(".")}`);
|
|
28449
|
+
}
|
|
28450
|
+
scope.aliases.push(scope.key);
|
|
28451
|
+
scopes.set(scope.key, scope);
|
|
28452
|
+
byNode.set(node, scope);
|
|
28453
|
+
});
|
|
28454
|
+
}
|
|
28455
|
+
const classChain = (node, seen = new Set) => {
|
|
28456
|
+
if (seen.has(keyOf(node)))
|
|
28457
|
+
return [];
|
|
28458
|
+
seen.add(keyOf(node));
|
|
28459
|
+
const checker = ctx.checkerFor(node.getSourceFile().fileName.slice(ctx.rootDir.length + 1));
|
|
28460
|
+
const bases = node.heritageClauses?.flatMap((clause) => clause.types).flatMap((base) => {
|
|
28461
|
+
const declaration = checker && valueDeclaration(resolvedSymbol(base.expression, checker));
|
|
28462
|
+
return declaration && import_typescript14.default.isClassLike(declaration) ? classChain(declaration, seen) : [];
|
|
28463
|
+
}) ?? [];
|
|
28464
|
+
return [node, ...bases];
|
|
28465
|
+
};
|
|
28466
|
+
for (const cls of classes)
|
|
28467
|
+
cls.nodes = classChain(cls.node);
|
|
28468
|
+
const virtualEdges = new Map;
|
|
28469
|
+
const nearest = (node) => {
|
|
28470
|
+
for (let current = node;current; current = current.parent) {
|
|
28471
|
+
const scope = byNode.get(current);
|
|
28472
|
+
if (scope)
|
|
28473
|
+
return scope;
|
|
28474
|
+
}
|
|
28475
|
+
return;
|
|
28476
|
+
};
|
|
28477
|
+
const bindingTruth = (condition, worker) => {
|
|
28478
|
+
if (condition.kind === import_typescript14.default.SyntaxKind.TrueKeyword)
|
|
28479
|
+
return true;
|
|
28480
|
+
if (condition.kind === import_typescript14.default.SyntaxKind.FalseKeyword)
|
|
28481
|
+
return false;
|
|
28482
|
+
if (import_typescript14.default.isPrefixUnaryExpression(condition) && condition.operator === import_typescript14.default.SyntaxKind.ExclamationToken) {
|
|
28483
|
+
const value = bindingTruth(condition.operand, worker);
|
|
28484
|
+
return value === undefined ? undefined : !value;
|
|
28485
|
+
}
|
|
28486
|
+
if (import_typescript14.default.isPropertyAccessExpression(condition) && /^(?:this\.)?env$/.test(condition.expression.getText()) && (worker ? repo.workers.find((entry) => entry.id === worker)?.queueBindings : repo.queueBindingsFor(condition.getSourceFile().fileName.slice(ctx.rootDir.length + 1)))?.has(condition.name.text))
|
|
28487
|
+
return true;
|
|
28488
|
+
return;
|
|
28489
|
+
};
|
|
28490
|
+
const returns = (node) => Boolean(node && (import_typescript14.default.isReturnStatement(node) || import_typescript14.default.isBlock(node) && node.statements.length && import_typescript14.default.isReturnStatement(node.statements[node.statements.length - 1])));
|
|
28491
|
+
const deployed = (node, worker) => {
|
|
28492
|
+
for (let current = node;current.parent; current = current.parent) {
|
|
28493
|
+
const parent = current.parent;
|
|
28494
|
+
if (import_typescript14.default.isIfStatement(parent)) {
|
|
28495
|
+
const truth = bindingTruth(parent.expression, worker);
|
|
28496
|
+
if (truth !== undefined && (current === parent.thenStatement ? !truth : current === parent.elseStatement ? truth : false))
|
|
28497
|
+
return false;
|
|
28498
|
+
}
|
|
28499
|
+
if (import_typescript14.default.isBlock(parent))
|
|
28500
|
+
for (const prior of parent.statements) {
|
|
28501
|
+
if (prior.getStart() >= current.getStart())
|
|
28502
|
+
break;
|
|
28503
|
+
if (import_typescript14.default.isIfStatement(prior)) {
|
|
28504
|
+
const truth = bindingTruth(prior.expression, worker);
|
|
28505
|
+
if (truth !== undefined && returns(truth ? prior.thenStatement : prior.elseStatement))
|
|
28506
|
+
return false;
|
|
28507
|
+
}
|
|
28508
|
+
}
|
|
28509
|
+
}
|
|
28510
|
+
return true;
|
|
28511
|
+
};
|
|
28512
|
+
const markModel = (scope, node) => {
|
|
28513
|
+
scope.hasModel = true;
|
|
28514
|
+
for (const worker of repo.workers)
|
|
28515
|
+
if (deployed(node, worker.id))
|
|
28516
|
+
scope.modelWorkers.add(worker.id);
|
|
28517
|
+
};
|
|
28518
|
+
for (const site of result.callSites) {
|
|
28519
|
+
if (site.isTest || site.isEval)
|
|
28520
|
+
continue;
|
|
28521
|
+
const file = repo.files.find((x) => x.path === site.path);
|
|
28522
|
+
if (!file)
|
|
28523
|
+
continue;
|
|
28524
|
+
const offset = file.source.getPositionOfLineAndCharacter(Math.max(0, site.line - 1), Math.max(0, site.col - 1));
|
|
28525
|
+
let anchor = file.source;
|
|
28526
|
+
const descend = (node) => {
|
|
28527
|
+
if (node.getStart() <= offset && node.getEnd() >= offset) {
|
|
28528
|
+
anchor = node;
|
|
28529
|
+
import_typescript14.default.forEachChild(node, descend);
|
|
28530
|
+
}
|
|
28531
|
+
};
|
|
28532
|
+
descend(file.source);
|
|
28533
|
+
const scope = nearest(anchor);
|
|
28534
|
+
if (!scope)
|
|
28535
|
+
continue;
|
|
28536
|
+
markModel(scope, anchor);
|
|
28537
|
+
const tools = site.toolsArg ? ctx.nodeAt(site.toolsArg) : undefined;
|
|
28538
|
+
scope.hasTools ||= Boolean(tools && !(import_typescript14.default.isObjectLiteralExpression(tools) && tools.properties.length === 0) && !(import_typescript14.default.isArrayLiteralExpression(tools) && tools.elements.length === 0));
|
|
28539
|
+
scope.aliases.push(site.scope);
|
|
28540
|
+
}
|
|
28541
|
+
const targetsOf = (node, file) => {
|
|
28542
|
+
const checker = ctx.checkerFor(file.path);
|
|
28543
|
+
if (!checker)
|
|
28544
|
+
return [];
|
|
28545
|
+
const at = import_typescript14.default.isPropertyAccessExpression(node) ? node.name : node;
|
|
28546
|
+
const declaration = valueDeclaration(resolvedSymbol(at, checker));
|
|
28547
|
+
if (!declaration)
|
|
28548
|
+
return [];
|
|
28549
|
+
const direct = byNode.get(declaration) ?? scopes.get(keyOf(declaration));
|
|
28550
|
+
if (direct)
|
|
28551
|
+
return [direct];
|
|
28552
|
+
if (import_typescript14.default.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
28553
|
+
const fn = byNode.get(declaration.initializer) ?? scopes.get(keyOf(declaration.initializer));
|
|
28554
|
+
if (fn)
|
|
28555
|
+
return [fn];
|
|
28556
|
+
}
|
|
28557
|
+
return [];
|
|
28558
|
+
};
|
|
28559
|
+
for (const scope of scopes.values()) {
|
|
28560
|
+
const inspect = (node) => {
|
|
28561
|
+
if (node !== scope.node && isFunctionLike(node)) {
|
|
28562
|
+
return;
|
|
28563
|
+
}
|
|
28564
|
+
if (import_typescript14.default.isWhileStatement(node) || import_typescript14.default.isDoStatement(node) || import_typescript14.default.isForStatement(node) || import_typescript14.default.isForOfStatement(node))
|
|
28565
|
+
scope.hasLoop = true;
|
|
28566
|
+
if ((import_typescript14.default.isIdentifier(node) || import_typescript14.default.isStringLiteralLike(node)) && /^(?:tool_calls|toolCalls|tool_use|tool_result|tool_results|toolResults)$/.test(node.text)) {
|
|
28567
|
+
scope.hasTools = true;
|
|
28568
|
+
scope.toolFeedback = true;
|
|
28569
|
+
}
|
|
28570
|
+
if (import_typescript14.default.isObjectLiteralExpression(node)) {
|
|
28571
|
+
const fields = new Map(node.properties.filter(import_typescript14.default.isPropertyAssignment).map((p2) => [nameOf(p2.name), p2.initializer]));
|
|
28572
|
+
const type = fields.get("type");
|
|
28573
|
+
if (type && import_typescript14.default.isStringLiteralLike(type) && type.text === "tool" && fields.has("name"))
|
|
28574
|
+
scope.forcedOutput = true;
|
|
28575
|
+
}
|
|
28576
|
+
if (import_typescript14.default.isCallExpression(node) || import_typescript14.default.isNewExpression(node)) {
|
|
28577
|
+
const callee = node.expression.getText();
|
|
28578
|
+
if (/(?:chat\.)?completions\.create$|messages\.(?:create|stream)$|responses\.(?:create|stream)$/.test(callee))
|
|
28579
|
+
markModel(scope, node);
|
|
28580
|
+
if (/\.runTurn$/.test(callee) && scope.node.parameters.some((param) => /tools/i.test(param.name.getText())))
|
|
28581
|
+
markModel(scope, node);
|
|
28582
|
+
const connect = (at) => {
|
|
28583
|
+
for (const target of byNode.has(at) ? [byNode.get(at)] : targetsOf(at, scope.file)) {
|
|
28584
|
+
if (import_typescript14.default.isPropertyAccessExpression(at) && at.expression.kind === import_typescript14.default.SyntaxKind.ThisKeyword) {
|
|
28585
|
+
virtualEdges.set(`${scope.key}|${target.key}`, at.name.text);
|
|
28586
|
+
scope.virtualCalls.set(target.key, at.name.text);
|
|
28587
|
+
}
|
|
28588
|
+
scope.calls.add(target.key);
|
|
28589
|
+
if (deployed(at))
|
|
28590
|
+
scope.deployedCalls.add(target.key);
|
|
28591
|
+
for (const [worker, calls] of scope.callsByWorker)
|
|
28592
|
+
if (deployed(at, worker))
|
|
28593
|
+
calls.add(target.key);
|
|
28594
|
+
target.callers.add(scope.key);
|
|
28595
|
+
}
|
|
28596
|
+
};
|
|
28597
|
+
connect(node.expression);
|
|
28598
|
+
const connectArgument = (at) => {
|
|
28599
|
+
if (isFunctionLike(at)) {
|
|
28600
|
+
connect(at);
|
|
28601
|
+
return;
|
|
28602
|
+
}
|
|
28603
|
+
if (import_typescript14.default.isIdentifier(at) || import_typescript14.default.isPropertyAccessExpression(at))
|
|
28604
|
+
connect(at);
|
|
28605
|
+
import_typescript14.default.forEachChild(at, connectArgument);
|
|
28606
|
+
};
|
|
28607
|
+
for (const argument of node.arguments ?? [])
|
|
28608
|
+
connectArgument(argument);
|
|
28609
|
+
for (const argument of node.arguments ?? [])
|
|
28610
|
+
if (import_typescript14.default.isObjectLiteralExpression(argument)) {
|
|
28611
|
+
const fields = new Set(argument.properties.map((p2) => nameOf(p2.name)));
|
|
28612
|
+
if (fields.has("model") && fields.has("messages") && fields.has("tools")) {
|
|
28613
|
+
markModel(scope, node);
|
|
28614
|
+
scope.hasTools = true;
|
|
28615
|
+
}
|
|
28616
|
+
}
|
|
28617
|
+
if (/(?:^|\.)(?:stepCountIs|isStepCount)$/.test(node.expression.getText()) && node.arguments?.[0] && import_typescript14.default.isNumericLiteral(node.arguments?.[0]) && Number(node.arguments?.[0].text) > 1) {
|
|
28618
|
+
scope.hasLoop = true;
|
|
28619
|
+
scope.configuredSteps = true;
|
|
28620
|
+
}
|
|
28621
|
+
}
|
|
28622
|
+
if (import_typescript14.default.isPropertyAssignment(node) && nameOf(node.name) === "maxSteps" && import_typescript14.default.isNumericLiteral(node.initializer) && Number(node.initializer.text) > 1) {
|
|
28623
|
+
scope.hasLoop = true;
|
|
28624
|
+
scope.configuredSteps = true;
|
|
28625
|
+
}
|
|
28626
|
+
import_typescript14.default.forEachChild(node, inspect);
|
|
28627
|
+
};
|
|
28628
|
+
inspect(scope.node);
|
|
28629
|
+
}
|
|
28630
|
+
const modelQueue = [...scopes.values()].filter((scope) => scope.hasModel);
|
|
28631
|
+
for (let index = 0;index < modelQueue.length; index++) {
|
|
28632
|
+
const scope = modelQueue[index];
|
|
28633
|
+
if (scope.reachesModel)
|
|
28634
|
+
continue;
|
|
28635
|
+
scope.reachesModel = true;
|
|
28636
|
+
for (const caller of scope.callers) {
|
|
28637
|
+
const parent = scopes.get(caller);
|
|
28638
|
+
if (parent && !parent.reachesModel)
|
|
28639
|
+
modelQueue.push(parent);
|
|
28640
|
+
}
|
|
28641
|
+
}
|
|
28642
|
+
const agents = new Map;
|
|
28643
|
+
const owners = new Map;
|
|
28644
|
+
const assign = (key, id) => {
|
|
28645
|
+
let values = owners.get(key);
|
|
28646
|
+
if (!values)
|
|
28647
|
+
owners.set(key, values = new Set);
|
|
28648
|
+
values.add(id);
|
|
28649
|
+
};
|
|
28650
|
+
for (const cls of classes) {
|
|
28651
|
+
const emitted2 = agent(cls.name, cls.file, cls.node, "Concrete agent/framework class or realtime model/tool bridge", repo.status(cls.file.path));
|
|
28652
|
+
agents.set(emitted2.id, emitted2);
|
|
28653
|
+
}
|
|
28654
|
+
for (const scope of scopes.values()) {
|
|
28655
|
+
let nestedInFunction = false;
|
|
28656
|
+
for (let parent = scope.node.parent;parent && !import_typescript14.default.isClassLike(parent); parent = parent.parent)
|
|
28657
|
+
if (isFunctionLike(parent)) {
|
|
28658
|
+
nestedInFunction = true;
|
|
28659
|
+
break;
|
|
28660
|
+
}
|
|
28661
|
+
if (scope.className && !nestedInFunction) {
|
|
28662
|
+
let member2 = scope.node;
|
|
28663
|
+
while (member2.parent && !import_typescript14.default.isClassLike(member2.parent))
|
|
28664
|
+
member2 = member2.parent;
|
|
28665
|
+
const memberName = "name" in member2 ? nameOf(member2.name) : undefined;
|
|
28666
|
+
for (const cls of classes) {
|
|
28667
|
+
const ownerClass = enclosingClass(scope.node);
|
|
28668
|
+
const baseIndex = cls.nodes.findIndex((node) => ownerClass && keyOf(node) === keyOf(ownerClass));
|
|
28669
|
+
if (baseIndex < 0)
|
|
28670
|
+
continue;
|
|
28671
|
+
const overridden = memberName && cls.nodes.slice(0, baseIndex).some((node) => node.members.some((candidate) => nameOf(candidate.name) === memberName));
|
|
28672
|
+
if (!overridden)
|
|
28673
|
+
assign(scope.key, componentId("agent", cls.file.path, cls.name));
|
|
28674
|
+
}
|
|
28675
|
+
}
|
|
28676
|
+
if (!scope.className && !scope.forcedOutput && scope.hasLoop && scope.hasTools && (scope.toolFeedback || scope.configuredSteps) && scope.reachesModel) {
|
|
28677
|
+
let boundary = scope;
|
|
28678
|
+
for (let parent = scope.node.parent;parent; parent = parent.parent) {
|
|
28679
|
+
const outer = byNode.get(parent);
|
|
28680
|
+
if (outer?.route) {
|
|
28681
|
+
boundary = outer;
|
|
28682
|
+
break;
|
|
28683
|
+
}
|
|
28684
|
+
}
|
|
28685
|
+
const name = boundary.route ?? functionName(boundary.node);
|
|
28686
|
+
if (!name)
|
|
28687
|
+
continue;
|
|
28688
|
+
const emitted2 = agent(name, boundary.file, scope.node, "Function or route owns a multi-step model/tool feedback loop", repo.status(boundary.file.path));
|
|
28689
|
+
agents.set(emitted2.id, emitted2);
|
|
28690
|
+
assign(boundary.key, emitted2.id);
|
|
28691
|
+
}
|
|
28692
|
+
}
|
|
28693
|
+
let changed = true;
|
|
28694
|
+
while (changed) {
|
|
28695
|
+
changed = false;
|
|
28696
|
+
for (const scope of scopes.values())
|
|
28697
|
+
for (const callee of scope.calls)
|
|
28698
|
+
for (const id of owners.get(scope.key) ?? []) {
|
|
28699
|
+
let targetKey = callee;
|
|
28700
|
+
const virtual = virtualEdges.get(`${scope.key}|${callee}`), concrete = classes.find((cls) => componentId("agent", cls.file.path, cls.name) === id);
|
|
28701
|
+
if (virtual && concrete) {
|
|
28702
|
+
const implementation = concrete.nodes.flatMap((node) => node.members.filter((member2) => nameOf(member2.name) === virtual))[0];
|
|
28703
|
+
if (implementation && scopes.has(keyOf(implementation)))
|
|
28704
|
+
targetKey = keyOf(implementation);
|
|
28705
|
+
}
|
|
28706
|
+
const before = owners.get(targetKey)?.size ?? 0;
|
|
28707
|
+
assign(targetKey, id);
|
|
28708
|
+
changed ||= (owners.get(targetKey)?.size ?? 0) !== before;
|
|
28709
|
+
}
|
|
28710
|
+
}
|
|
28711
|
+
const scopeOwners = new Map;
|
|
28712
|
+
const mergeAlias = (label, ids) => {
|
|
28713
|
+
let values = scopeOwners.get(label);
|
|
28714
|
+
if (!values)
|
|
28715
|
+
scopeOwners.set(label, values = new Set);
|
|
28716
|
+
for (const id of ids)
|
|
28717
|
+
values.add(id);
|
|
28718
|
+
};
|
|
28719
|
+
const classAliases = new Map;
|
|
28720
|
+
for (const cls of classes)
|
|
28721
|
+
for (const base of cls.nodes) {
|
|
28722
|
+
if (!base.name)
|
|
28723
|
+
continue;
|
|
28724
|
+
const qualified = `${base.getSourceFile().fileName.slice(ctx.rootDir.length + 1)}#${base.name.text}`;
|
|
28725
|
+
mergeAlias(qualified, [componentId("agent", cls.file.path, cls.name)]);
|
|
28726
|
+
let variants = classAliases.get(base.name.text);
|
|
28727
|
+
if (!variants)
|
|
28728
|
+
classAliases.set(base.name.text, variants = new Set);
|
|
28729
|
+
variants.add(qualified);
|
|
28730
|
+
}
|
|
28731
|
+
for (const [name, variants] of classAliases)
|
|
28732
|
+
if (variants.size === 1)
|
|
28733
|
+
mergeAlias(name, scopeOwners.get([...variants][0]) ?? []);
|
|
28734
|
+
const aliases = new Map;
|
|
28735
|
+
for (const scope of scopes.values())
|
|
28736
|
+
for (const alias of scope.aliases) {
|
|
28737
|
+
const matches = aliases.get(alias) ?? [];
|
|
28738
|
+
matches.push(scope);
|
|
28739
|
+
aliases.set(alias, matches);
|
|
28740
|
+
}
|
|
28741
|
+
for (const [alias, matches] of aliases) {
|
|
28742
|
+
const candidates = matches.map((scope) => [...owners.get(scope.key) ?? []].sort());
|
|
28743
|
+
if (candidates.every((ids) => JSON.stringify(ids) === JSON.stringify(candidates[0])))
|
|
28744
|
+
mergeAlias(alias, candidates[0]);
|
|
28745
|
+
}
|
|
28746
|
+
const output = {
|
|
28747
|
+
runtimeAgents: [...agents.values()].sort((a, b) => a.id.localeCompare(b.id)),
|
|
28748
|
+
agentForScope: (scope) => scope ? [...scopeOwners.get(scope) ?? []].sort() : [],
|
|
28749
|
+
scopes: [...scopes.values()]
|
|
28750
|
+
};
|
|
28751
|
+
enumerations.set(ctx, output);
|
|
28752
|
+
assignAgentIds(result, output.agentForScope);
|
|
28753
|
+
return output;
|
|
28754
|
+
}
|
|
28755
|
+
|
|
28756
|
+
// src/harness-extract/enumerators/workflows.ts
|
|
28757
|
+
var import_typescript15 = __toESM(require_typescript(), 1);
|
|
28758
|
+
function enumerateWorkflows(result, ctx) {
|
|
28759
|
+
const output = emptyResult();
|
|
28760
|
+
const repo = enumerationRepository(ctx);
|
|
28761
|
+
const nodeKey = (node) => `${node.getSourceFile().fileName}:${node.getStart()}`;
|
|
28762
|
+
const bindings = new Set;
|
|
28763
|
+
for (const worker of repo.workers) {
|
|
28764
|
+
const file = repo.files.find((entry) => entry.path === worker.main), checker = file && ctx.checkerFor(file.path);
|
|
28765
|
+
const module = file && checker?.getSymbolAtLocation(file.source);
|
|
28766
|
+
if (!module || !checker)
|
|
28767
|
+
continue;
|
|
28768
|
+
for (const name of worker.workflowClasses) {
|
|
28769
|
+
let exported = checker.getExportsOfModule(module).find((symbol) => symbol.name === name);
|
|
28770
|
+
if (exported && exported.flags & import_typescript15.default.SymbolFlags.Alias)
|
|
28771
|
+
exported = checker.getAliasedSymbol(exported);
|
|
28772
|
+
const declaration = exported && valueDeclaration(exported);
|
|
28773
|
+
if (declaration && import_typescript15.default.isClassDeclaration(declaration))
|
|
28774
|
+
bindings.add(nodeKey(declaration));
|
|
28775
|
+
}
|
|
28776
|
+
}
|
|
28777
|
+
const memo = createFrameworkMemo();
|
|
28778
|
+
for (const file of repo.files) {
|
|
28779
|
+
if (!/\.[cm]?[jt]sx?$/.test(file.path) || TEST_PATH4.test(file.path) || EVAL_PATH2.test(file.path))
|
|
28780
|
+
continue;
|
|
28781
|
+
visit(file.source, (node) => {
|
|
28782
|
+
if (!import_typescript15.default.isClassDeclaration(node) || !node.name || import_typescript15.default.getCombinedModifierFlags(node) & import_typescript15.default.ModifierFlags.Abstract)
|
|
28783
|
+
return;
|
|
28784
|
+
const checker = ctx.checkerFor(file.path);
|
|
28785
|
+
const base = checker ? frameworkBaseOf(node, checker, memo)?.base : undefined;
|
|
28786
|
+
const directBase = node.heritageClauses?.flatMap((h) => h.types).map((x) => x.expression.getText()) ?? [];
|
|
28787
|
+
if (!bindings.has(nodeKey(node)) && base !== "WorkflowEntrypoint" && base !== "AgentWorkflow" && !directBase.some((x) => /^(?:WorkflowEntrypoint|AgentWorkflow)$/.test(x)))
|
|
28788
|
+
return;
|
|
28789
|
+
const entry = artifact("entrypoint", node.name.text, file, node, "Concrete Workflow class or Wrangler workflow binding", repo.status(file.path));
|
|
28790
|
+
entry.id = componentId("workflow", file.path, node.name.text);
|
|
28791
|
+
if (bindings.has(nodeKey(node))) {
|
|
28792
|
+
Object.assign(entry, { componentKind: "workflow", trigger: "workflow_binding" });
|
|
28793
|
+
}
|
|
28794
|
+
output.artifacts.push(entry);
|
|
28795
|
+
});
|
|
28796
|
+
}
|
|
28797
|
+
for (const worker of repo.workers.filter((entry) => entry.cron)) {
|
|
28798
|
+
const scopes = enumerateAgents(result, ctx).scopes;
|
|
28799
|
+
const byKey = new Map(scopes.map((scope) => [scope.key, scope]));
|
|
28800
|
+
const implementations = new Map, workerClasses = new Set;
|
|
28801
|
+
const deployedCalls = (scope) => {
|
|
28802
|
+
const calls = scope.callsByWorker.get(worker.id) ?? scope.deployedCalls;
|
|
28803
|
+
const cls = enclosingClass(scope.node);
|
|
28804
|
+
if (!cls || !workerClasses.has(nodeKey(cls)))
|
|
28805
|
+
return calls;
|
|
28806
|
+
const member2 = scope.node.parent === cls ? scope.node : scope.node.parent;
|
|
28807
|
+
if (import_typescript15.default.getCombinedModifierFlags(member2) & import_typescript15.default.ModifierFlags.Static)
|
|
28808
|
+
return calls;
|
|
28809
|
+
return new Set([...calls].map((key) => {
|
|
28810
|
+
const method = scope.virtualCalls.get(key);
|
|
28811
|
+
return method && implementations.has(method) ? implementations.get(method) : key;
|
|
28812
|
+
}));
|
|
28813
|
+
};
|
|
28814
|
+
const modelScopes = new Set(scopes.filter((scope) => scope.modelWorkers.has(worker.id)).map((scope) => scope.key));
|
|
28815
|
+
const mainFile = repo.files.find((file) => file.path === worker.main), checker = mainFile && ctx.checkerFor(mainFile.path);
|
|
28816
|
+
const exportedNodes = new Set, visited = new Set;
|
|
28817
|
+
const resolveWorker = (node) => {
|
|
28818
|
+
if (!node || visited.has(node))
|
|
28819
|
+
return;
|
|
28820
|
+
visited.add(node);
|
|
28821
|
+
if (import_typescript15.default.isParenthesizedExpression(node) || import_typescript15.default.isAsExpression(node) || import_typescript15.default.isSatisfiesExpression(node) || import_typescript15.default.isNonNullExpression(node)) {
|
|
28822
|
+
resolveWorker(node.expression);
|
|
28823
|
+
return;
|
|
28824
|
+
}
|
|
28825
|
+
if (import_typescript15.default.isCallExpression(node) && /(?:^|\.)withSentry$/.test(node.expression.getText())) {
|
|
28826
|
+
for (const argument of node.arguments)
|
|
28827
|
+
resolveWorker(argument);
|
|
28828
|
+
return;
|
|
28829
|
+
}
|
|
28830
|
+
if (import_typescript15.default.isExportAssignment(node)) {
|
|
28831
|
+
resolveWorker(node.expression);
|
|
28832
|
+
return;
|
|
28833
|
+
}
|
|
28834
|
+
if (import_typescript15.default.isVariableDeclaration(node)) {
|
|
28835
|
+
resolveWorker(node.initializer);
|
|
28836
|
+
return;
|
|
28837
|
+
}
|
|
28838
|
+
if (import_typescript15.default.isIdentifier(node) && checker) {
|
|
28839
|
+
resolveWorker(valueDeclaration(resolvedSymbol(node, checker)));
|
|
28840
|
+
return;
|
|
28841
|
+
}
|
|
28842
|
+
exportedNodes.add(node);
|
|
28843
|
+
};
|
|
28844
|
+
const module = mainFile && checker?.getSymbolAtLocation(mainFile.source);
|
|
28845
|
+
const exported = module && checker?.getExportsOfModule(module).find((symbol2) => symbol2.name === "default");
|
|
28846
|
+
const symbol = exported && checker && (exported.flags & import_typescript15.default.SymbolFlags.Alias ? checker.getAliasedSymbol(exported) : exported);
|
|
28847
|
+
resolveWorker(symbol && valueDeclaration(symbol));
|
|
28848
|
+
const collectImplementations = (node) => {
|
|
28849
|
+
if (!import_typescript15.default.isClassLike(node) || workerClasses.has(nodeKey(node)))
|
|
28850
|
+
return;
|
|
28851
|
+
workerClasses.add(nodeKey(node));
|
|
28852
|
+
for (const member2 of node.members) {
|
|
28853
|
+
if (import_typescript15.default.getCombinedModifierFlags(member2) & import_typescript15.default.ModifierFlags.Static)
|
|
28854
|
+
continue;
|
|
28855
|
+
const name = nameOf(member2.name);
|
|
28856
|
+
const implementation = byKey.get(nodeKey(member2)) ?? (import_typescript15.default.isPropertyDeclaration(member2) && member2.initializer ? byKey.get(nodeKey(member2.initializer)) : undefined);
|
|
28857
|
+
if (name && !implementations.has(name))
|
|
28858
|
+
implementations.set(name, implementation?.key ?? "(non-callable override)");
|
|
28859
|
+
}
|
|
28860
|
+
if (checker)
|
|
28861
|
+
for (const base of node.heritageClauses?.flatMap((clause) => clause.types) ?? []) {
|
|
28862
|
+
const declaration = valueDeclaration(resolvedSymbol(base.expression, checker));
|
|
28863
|
+
if (declaration)
|
|
28864
|
+
collectImplementations(declaration);
|
|
28865
|
+
}
|
|
28866
|
+
};
|
|
28867
|
+
for (const node of exportedNodes)
|
|
28868
|
+
collectImplementations(node);
|
|
28869
|
+
let changed = true;
|
|
28870
|
+
while (changed) {
|
|
28871
|
+
changed = false;
|
|
28872
|
+
for (const scope of scopes)
|
|
28873
|
+
if (!modelScopes.has(scope.key) && [...deployedCalls(scope)].some((key) => modelScopes.has(key))) {
|
|
28874
|
+
modelScopes.add(scope.key);
|
|
28875
|
+
changed = true;
|
|
28876
|
+
}
|
|
28877
|
+
}
|
|
28878
|
+
const scheduledNodes = new Set;
|
|
28879
|
+
const resolveScheduled = (node, seen2 = new Set) => {
|
|
28880
|
+
if (!node || seen2.has(node))
|
|
28881
|
+
return;
|
|
28882
|
+
seen2.add(node);
|
|
28883
|
+
if (import_typescript15.default.isIdentifier(node) && checker) {
|
|
28884
|
+
resolveScheduled(valueDeclaration(resolvedSymbol(node, checker)), seen2);
|
|
28885
|
+
return;
|
|
28886
|
+
}
|
|
28887
|
+
if (import_typescript15.default.isVariableDeclaration(node) || import_typescript15.default.isPropertyAssignment(node) || import_typescript15.default.isPropertyDeclaration(node)) {
|
|
28888
|
+
resolveScheduled(node.initializer, seen2);
|
|
28889
|
+
return;
|
|
28890
|
+
}
|
|
28891
|
+
if (import_typescript15.default.isShorthandPropertyAssignment(node)) {
|
|
28892
|
+
resolveScheduled(checker && valueDeclaration(checker.getShorthandAssignmentValueSymbol(node)), seen2);
|
|
28893
|
+
return;
|
|
28894
|
+
}
|
|
28895
|
+
if (import_typescript15.default.isParenthesizedExpression(node) || import_typescript15.default.isAsExpression(node) || import_typescript15.default.isSatisfiesExpression(node) || import_typescript15.default.isNonNullExpression(node)) {
|
|
28896
|
+
resolveScheduled(node.expression, seen2);
|
|
28897
|
+
return;
|
|
28898
|
+
}
|
|
28899
|
+
scheduledNodes.add(node);
|
|
28900
|
+
};
|
|
28901
|
+
const collectScheduled = (node, seen2 = new Set) => {
|
|
28902
|
+
if (seen2.has(node))
|
|
28903
|
+
return;
|
|
28904
|
+
seen2.add(node);
|
|
28905
|
+
const members = import_typescript15.default.isObjectLiteralExpression(node) ? node.properties : import_typescript15.default.isClassLike(node) ? node.members : [];
|
|
28906
|
+
const handlers = members.filter((member2) => nameOf(member2.name) === "scheduled" && !(import_typescript15.default.getCombinedModifierFlags(member2) & import_typescript15.default.ModifierFlags.Static));
|
|
28907
|
+
for (const member2 of handlers)
|
|
28908
|
+
resolveScheduled(member2);
|
|
28909
|
+
if (!handlers.length && import_typescript15.default.isClassLike(node) && checker)
|
|
28910
|
+
for (const base of node.heritageClauses?.flatMap((clause) => clause.types) ?? []) {
|
|
28911
|
+
const declaration = valueDeclaration(resolvedSymbol(base.expression, checker));
|
|
28912
|
+
if (declaration)
|
|
28913
|
+
collectScheduled(declaration, seen2);
|
|
28914
|
+
}
|
|
28915
|
+
};
|
|
28916
|
+
for (const node of exportedNodes)
|
|
28917
|
+
collectScheduled(node);
|
|
28918
|
+
const roots = scopes.filter((scope) => [...scheduledNodes].some((node) => node.getSourceFile().fileName === scope.node.getSourceFile().fileName && node.getStart() === scope.node.getStart()));
|
|
28919
|
+
const seen = new Set;
|
|
28920
|
+
const queue = roots.flatMap((scope) => scope.modelWorkers.has(worker.id) ? [scope.key] : [...deployedCalls(scope)]);
|
|
28921
|
+
for (let i = 0;i < queue.length; i++) {
|
|
28922
|
+
const key = queue[i];
|
|
28923
|
+
if (seen.has(key))
|
|
28924
|
+
continue;
|
|
28925
|
+
seen.add(key);
|
|
28926
|
+
const scope = byKey.get(key);
|
|
28927
|
+
if (!scope)
|
|
28928
|
+
continue;
|
|
28929
|
+
if (!modelScopes.has(scope.key)) {
|
|
28930
|
+
if (/(?:Cron|Pipeline)$/.test(scope.label)) {
|
|
28931
|
+
const entry = artifact("entrypoint", scope.label, scope.file, scope.node, "Scheduled entrypoint has no deployed model step on its own pipeline", repo.status(scope.file.path));
|
|
28932
|
+
const proof = [...scope.calls].map((callee) => byKey.get(callee)).filter((callee) => callee?.reachesModel).map((callee) => `${callee.file.path}#${callee.label}`);
|
|
28933
|
+
Object.assign(entry, { exclusionReason: scope.reachesModel ? "Model reachable only through a queue consumer or statically excluded fallback" : "No model reachable from scheduled pipeline", proofPath: [`${scope.file.path}#${scope.label}`, ...proof] });
|
|
28934
|
+
output.artifacts.push(entry);
|
|
28935
|
+
} else
|
|
28936
|
+
queue.push(...deployedCalls(scope));
|
|
28937
|
+
continue;
|
|
28938
|
+
}
|
|
28939
|
+
if (/(?:Cron|Pipeline)$/.test(scope.label) || scope.modelWorkers.has(worker.id)) {
|
|
28940
|
+
const entry = artifact("entrypoint", scope.label, scope.file, scope.node, "Cron dispatch reaches this model-backed pipeline", repo.status(scope.file.path));
|
|
28941
|
+
entry.id = componentId("workflow", scope.file.path, scope.label);
|
|
28942
|
+
entry.componentKind = "workflow";
|
|
28943
|
+
entry.trigger = "cron";
|
|
28944
|
+
output.artifacts.push(entry);
|
|
28945
|
+
if (roots.includes(scope))
|
|
28946
|
+
queue.push(...deployedCalls(scope));
|
|
28947
|
+
} else
|
|
28948
|
+
queue.push(...deployedCalls(scope));
|
|
28949
|
+
}
|
|
28950
|
+
}
|
|
28951
|
+
return stableResult(output);
|
|
28952
|
+
}
|
|
28953
|
+
|
|
28954
|
+
// src/harness-extract/enumerators/catalog.ts
|
|
28955
|
+
var import_typescript16 = __toESM(require_typescript(), 1);
|
|
28956
|
+
var THINK_WORKSPACE_TOOLS = ["read", "write", "edit", "list", "find", "grep", "delete"];
|
|
28957
|
+
function strings(expression, checker, seen = new Set) {
|
|
28958
|
+
if (!expression)
|
|
28959
|
+
return [];
|
|
28960
|
+
const node = unwrap(expression);
|
|
28961
|
+
if (seen.has(node))
|
|
28962
|
+
return [];
|
|
28963
|
+
seen.add(node);
|
|
28964
|
+
if (import_typescript16.default.isIdentifier(node)) {
|
|
28965
|
+
const declaration = checker && valueDeclaration(resolvedSymbol(node, checker));
|
|
28966
|
+
if (!declaration || !import_typescript16.default.isVariableDeclaration(declaration))
|
|
28967
|
+
return [];
|
|
28968
|
+
return strings(declaration.initializer, checker, seen);
|
|
28969
|
+
}
|
|
28970
|
+
if (import_typescript16.default.isArrayLiteralExpression(node))
|
|
28971
|
+
return node.elements.flatMap((entry) => import_typescript16.default.isSpreadElement(entry) ? strings(entry.expression, checker, seen) : strings(entry, checker, seen));
|
|
28972
|
+
if (import_typescript16.default.isStringLiteralLike(node))
|
|
28973
|
+
return [{ name: node.text, node }];
|
|
28974
|
+
if (import_typescript16.default.isNewExpression(node) && node.expression.getText() === "Set")
|
|
28975
|
+
return strings(node.arguments?.[0], checker, seen);
|
|
28976
|
+
return [];
|
|
28977
|
+
}
|
|
28978
|
+
function property(object, name) {
|
|
28979
|
+
const entry = object.properties.find((item) => item.name && nameOf(item.name) === name);
|
|
28980
|
+
return entry && import_typescript16.default.isPropertyAssignment(entry) ? entry.initializer : entry && import_typescript16.default.isShorthandPropertyAssignment(entry) ? entry.name : undefined;
|
|
28981
|
+
}
|
|
28982
|
+
function inheritedTrue(node, propertyName2, checker, seen = new Set) {
|
|
28983
|
+
if (seen.has(node))
|
|
28984
|
+
return false;
|
|
28985
|
+
seen.add(node);
|
|
28986
|
+
const own = node.members.find((member2) => nameOf(member2.name) === propertyName2);
|
|
28987
|
+
if (own && import_typescript16.default.isPropertyDeclaration(own) && own.initializer)
|
|
28988
|
+
return unwrap(own.initializer).kind === import_typescript16.default.SyntaxKind.TrueKeyword;
|
|
28989
|
+
if (checker)
|
|
28990
|
+
for (const base of node.heritageClauses?.flatMap((h) => h.types) ?? []) {
|
|
28991
|
+
const declaration = valueDeclaration(resolvedSymbol(base.expression, checker));
|
|
28992
|
+
if (declaration && (import_typescript16.default.isClassDeclaration(declaration) || import_typescript16.default.isClassExpression(declaration)))
|
|
28993
|
+
return inheritedTrue(declaration, propertyName2, checker, seen);
|
|
28994
|
+
}
|
|
28995
|
+
return false;
|
|
28996
|
+
}
|
|
28997
|
+
function enumerateToolCatalog(result, ctx) {
|
|
28998
|
+
const output = emptyResult(), repo = enumerationRepository(ctx), agents = enumerateAgents(result, ctx);
|
|
28999
|
+
const agentByName = new Map(agents.runtimeAgents.map((entry) => [entry.name, entry.id]));
|
|
29000
|
+
const symbolOwners = new Map;
|
|
29001
|
+
for (const scope of agents.scopes) {
|
|
29002
|
+
const owners = agents.agentForScope(scope.key);
|
|
29003
|
+
const checker = ctx.checkerFor(scope.file.path);
|
|
29004
|
+
if (!owners.length || !checker)
|
|
29005
|
+
continue;
|
|
29006
|
+
visit(scope.node, (node) => {
|
|
29007
|
+
if (!import_typescript16.default.isIdentifier(node) || !/ALLOW|MCP_NAME/.test(node.text))
|
|
29008
|
+
return;
|
|
29009
|
+
const declaration = valueDeclaration(resolvedSymbol(node, checker));
|
|
29010
|
+
if (!declaration || !import_typescript16.default.isVariableDeclaration(declaration) || !import_typescript16.default.isIdentifier(declaration.name))
|
|
29011
|
+
return;
|
|
29012
|
+
const key = `${declaration.getSourceFile().fileName}:${declaration.name.text}`;
|
|
29013
|
+
let existing = symbolOwners.get(key);
|
|
29014
|
+
if (!existing)
|
|
29015
|
+
symbolOwners.set(key, existing = new Set);
|
|
29016
|
+
for (const id of owners)
|
|
29017
|
+
existing.add(id);
|
|
29018
|
+
});
|
|
29019
|
+
}
|
|
29020
|
+
const ownersAt = (node, path) => {
|
|
29021
|
+
const scope = agents.scopes.filter((s) => s.file.path === path && s.node.getStart() <= node.getStart() && s.node.getEnd() >= node.getEnd()).sort((a, b) => a.node.getWidth() - b.node.getWidth())[0];
|
|
29022
|
+
return scope ? agents.agentForScope(scope.key) : [];
|
|
29023
|
+
};
|
|
29024
|
+
const memo = createFrameworkMemo();
|
|
29025
|
+
const thinkIds = new Set;
|
|
29026
|
+
for (const runtime of agents.runtimeAgents) {
|
|
29027
|
+
const file = repo.files.find((entry) => entry.path === runtime.evidence[0]?.path), checker = file && ctx.checkerFor(file.path);
|
|
29028
|
+
if (!file || !checker)
|
|
29029
|
+
continue;
|
|
29030
|
+
visit(file.source, (node) => {
|
|
29031
|
+
if ((import_typescript16.default.isClassDeclaration(node) || import_typescript16.default.isClassExpression(node)) && node.name?.text === runtime.name && frameworkBaseOf(node, checker, memo)?.base === "Think")
|
|
29032
|
+
thinkIds.add(runtime.id);
|
|
29033
|
+
});
|
|
29034
|
+
}
|
|
29035
|
+
const add = (name, file, node, source, reason, owners = []) => {
|
|
29036
|
+
const evidenceFile = repo.files.find((entry2) => entry2.source.fileName === node.getSourceFile().fileName) ?? file;
|
|
29037
|
+
const entry = artifact("tool", name, evidenceFile, node, reason, repo.status(file.path));
|
|
29038
|
+
entry.catalogSource = source;
|
|
29039
|
+
entry.usedByAgentIds = owners;
|
|
29040
|
+
output.artifacts.push(entry);
|
|
29041
|
+
return entry;
|
|
29042
|
+
};
|
|
29043
|
+
for (const file of repo.files) {
|
|
29044
|
+
if (!/\.[cm]?[jt]sx?$/.test(file.path) || TEST_PATH4.test(file.path) || EVAL_PATH2.test(file.path) || /^scripts\//.test(file.path) && !repo.deployedFiles.has(file.path))
|
|
29045
|
+
continue;
|
|
29046
|
+
const checker = ctx.checkerFor(file.path), declarations = new Map;
|
|
29047
|
+
visit(file.source, (node) => {
|
|
29048
|
+
if (import_typescript16.default.isVariableDeclaration(node) && import_typescript16.default.isIdentifier(node.name) && node.initializer)
|
|
29049
|
+
declarations.set(node.name.text, node.initializer);
|
|
29050
|
+
});
|
|
29051
|
+
visit(file.source, (node) => {
|
|
29052
|
+
if ((import_typescript16.default.isClassDeclaration(node) || import_typescript16.default.isClassExpression(node)) && node.name && agentByName.has(node.name.text)) {
|
|
29053
|
+
const info = checker ? frameworkBaseOf(node, checker, memo) : undefined;
|
|
29054
|
+
const directThink = node.heritageClauses?.some((h) => h.types.some((t) => t.expression.getText() === "Think"));
|
|
29055
|
+
if (info?.base === "Think" || directThink) {
|
|
29056
|
+
const owners2 = agents.agentForScope(`${file.path}#${node.name.text}`);
|
|
29057
|
+
const names = [...THINK_WORKSPACE_TOOLS];
|
|
29058
|
+
if (inheritedTrue(node, "workspaceBash", checker))
|
|
29059
|
+
names.push("bash");
|
|
29060
|
+
for (const name of names)
|
|
29061
|
+
add(name, file, node, "think", "Think workspace tool inherited by this concrete agent", owners2);
|
|
29062
|
+
}
|
|
29063
|
+
}
|
|
29064
|
+
if (import_typescript16.default.isPropertyAccessExpression(node) && node.name.text === "activate_skill" || import_typescript16.default.isPropertyAssignment(node) && nameOf(node.name) === "activate_skill") {
|
|
29065
|
+
const owner = ownersAt(node, file.path);
|
|
29066
|
+
add("activate_skill", file, node, "think", "Source explicitly exposes Think skill activation", owner);
|
|
29067
|
+
}
|
|
29068
|
+
if (import_typescript16.default.isCallExpression(node) && import_typescript16.default.isPropertyAccessExpression(node.expression) && /^(?:registerTool|tool)$/.test(node.expression.name.text) && checker) {
|
|
29069
|
+
const receiver = valueDeclaration(resolvedSymbol(node.expression.expression, checker));
|
|
29070
|
+
const init = receiver && import_typescript16.default.isVariableDeclaration(receiver) && receiver.initializer ? unwrap(receiver.initializer) : undefined;
|
|
29071
|
+
const origin = init && import_typescript16.default.isNewExpression(init) && import_typescript16.default.isIdentifier(init.expression) ? importOrigin(init.expression, checker) : undefined;
|
|
29072
|
+
const name = node.arguments[0];
|
|
29073
|
+
if (origin?.module.startsWith("@modelcontextprotocol/sdk/") && origin.imported === "McpServer" && name && import_typescript16.default.isStringLiteralLike(name)) {
|
|
29074
|
+
add(name.text, file, name, "mcp_server", "MCP SDK server explicitly registers this model-facing tool name", ownersAt(node, file.path));
|
|
29075
|
+
}
|
|
29076
|
+
}
|
|
29077
|
+
if (import_typescript16.default.isCallExpression(node) && import_typescript16.default.isPropertyAccessExpression(node.expression) && node.expression.expression.getText() === "Output" && node.expression.name.text === "object") {
|
|
29078
|
+
const owners2 = ownersAt(node, file.path).filter((id) => thinkIds.has(id));
|
|
29079
|
+
if (owners2.length && checker && import_typescript16.default.isIdentifier(node.expression.expression) && ["ai", "@cloudflare/think"].includes(importOrigin(node.expression.expression, checker)?.module ?? ""))
|
|
29080
|
+
add("think_final_answer", file, node, "think", "Think structured output is offered as the framework final-answer tool", owners2);
|
|
29081
|
+
}
|
|
29082
|
+
if (import_typescript16.default.isCallExpression(node) && import_typescript16.default.isIdentifier(node.expression) && node.expression.text === "createExecuteRuntime") {
|
|
29083
|
+
const owners2 = ownersAt(node, file.path).filter((id) => thinkIds.has(id));
|
|
29084
|
+
if (owners2.length && checker && importOrigin(node.expression, checker)?.module.startsWith("@cloudflare/think"))
|
|
29085
|
+
add("execute", file, node, "think", "Think Code Mode runtime explicitly enabled for this agent", owners2);
|
|
29086
|
+
}
|
|
29087
|
+
if (!import_typescript16.default.isObjectLiteralExpression(node))
|
|
29088
|
+
return;
|
|
29089
|
+
const serverNames = strings(property(node, "name"), checker), allow = property(node, "allow");
|
|
29090
|
+
if (serverNames.length !== 1 || !allow || !property(node, "url"))
|
|
29091
|
+
return;
|
|
29092
|
+
const server = serverNames[0].name;
|
|
29093
|
+
const owners = ownersAt(node, file.path);
|
|
29094
|
+
if (import_typescript16.default.isStringLiteralLike(unwrap(allow)) && unwrap(allow).text === "all") {
|
|
29095
|
+
const entry = add(`${server} dynamic MCP tools`, file, allow, "mcp_dynamic_group", "Server schemas are admitted dynamically as one bounded catalog group", owners);
|
|
29096
|
+
entry.serverName = server;
|
|
29097
|
+
entry.dynamicGroup = true;
|
|
29098
|
+
entry.namePattern = `tool_<${server}ServerId>_*`;
|
|
29099
|
+
return;
|
|
29100
|
+
}
|
|
29101
|
+
for (const tool of strings(allow, checker)) {
|
|
29102
|
+
const name = `tool_<${server}ServerIdNoDashes>_${tool.name}`;
|
|
29103
|
+
const entry = add(name, file, tool.node, "mcp_allowlist", "Literal MCP allowlist converted by the Agents SDK tool_<serverId>_<name> contract", owners);
|
|
29104
|
+
entry.id = componentId("tool", file.path, `${server}:${name}`);
|
|
29105
|
+
entry.serverName = server;
|
|
29106
|
+
entry.namePattern = name;
|
|
29107
|
+
}
|
|
29108
|
+
});
|
|
29109
|
+
for (const [name, initializer] of declarations) {
|
|
29110
|
+
const match = name.match(/^(.*)_ALLOWED_TOOLS$/);
|
|
29111
|
+
if (!match)
|
|
29112
|
+
continue;
|
|
29113
|
+
const serverName = strings(declarations.get(`${match[1]}_MCP_NAME`), checker)[0]?.name;
|
|
29114
|
+
if (!serverName)
|
|
29115
|
+
continue;
|
|
29116
|
+
for (const tool of strings(initializer, checker)) {
|
|
29117
|
+
const entry = add(`${serverName}_${tool.name}`, file, tool.node, "mcp_allowlist", "Explicit named MCP allowlist with server-prefixed model name");
|
|
29118
|
+
entry.serverName = serverName;
|
|
29119
|
+
entry.usedByAgentIds = [...symbolOwners.get(`${file.source.fileName}:${name}`) ?? []].sort();
|
|
29120
|
+
}
|
|
29121
|
+
}
|
|
29122
|
+
}
|
|
29123
|
+
const merged = new Map;
|
|
29124
|
+
for (const item of output.artifacts) {
|
|
29125
|
+
const previous = merged.get(item.name);
|
|
29126
|
+
if (previous) {
|
|
29127
|
+
previous.usedByAgentIds = [...new Set([...previous.usedByAgentIds, ...item.usedByAgentIds])].sort();
|
|
29128
|
+
const seen = new Set(previous.evidence.map((e) => `${e.path}:${e.line}`));
|
|
29129
|
+
previous.evidence.push(...item.evidence.filter((e) => !seen.has(`${e.path}:${e.line}`)));
|
|
29130
|
+
if (item.status === "in_prod" || item.status === "reachable" && previous.status === "defined_only")
|
|
29131
|
+
previous.status = item.status;
|
|
29132
|
+
} else {
|
|
29133
|
+
item.id = `tool:catalog:${item.name}`;
|
|
29134
|
+
merged.set(item.name, item);
|
|
29135
|
+
}
|
|
29136
|
+
}
|
|
29137
|
+
output.artifacts = [...merged.values()];
|
|
29138
|
+
return stableResult(output);
|
|
29139
|
+
}
|
|
29140
|
+
|
|
29141
|
+
// src/harness-extract/enumerators/index.ts
|
|
29142
|
+
var ENUMERATORS = [
|
|
29143
|
+
{ id: "agents", enumerate: ({ result }, ctx) => ({ runtimeAgents: enumerateAgents(result, ctx).runtimeAgents }) },
|
|
29144
|
+
{ id: "workflows", enumerate: ({ result }, ctx) => enumerateWorkflows(result, ctx) },
|
|
29145
|
+
{ id: "framework-mcp-catalog", enumerate: ({ result }, ctx) => enumerateToolCatalog(result, ctx) },
|
|
29146
|
+
{ id: "tool-registry", enumerate: ({ result }, ctx) => ({ artifacts: toolArtifacts(emitToolsCached(result, ctx), enumerateAgents(result, ctx).runtimeAgents) }) }
|
|
29147
|
+
];
|
|
29148
|
+
function enumerateHarnessComponents(result, ctx) {
|
|
29149
|
+
const agents = enumerateAgents(result, ctx), workflows = enumerateWorkflows(result, ctx), catalog = enumerateToolCatalog(result, ctx);
|
|
29150
|
+
return stableResult({ runtimeAgents: agents.runtimeAgents, artifacts: [...workflows.artifacts, ...toolArtifacts(emitToolsCached(result, ctx), agents.runtimeAgents), ...catalog.artifacts], warnings: [...workflows.warnings, ...catalog.warnings] });
|
|
29151
|
+
}
|
|
29152
|
+
function mergeEnumeratedComponents(artifacts, components) {
|
|
29153
|
+
const merged = new Map;
|
|
29154
|
+
const definitions = new Map;
|
|
29155
|
+
const registry = components.artifacts.filter((item) => item.type === "tool" && item.id.startsWith("tool.extract."));
|
|
29156
|
+
for (const item of [...registry, ...artifacts, ...components.artifacts.filter((item2) => !registry.includes(item2))]) {
|
|
29157
|
+
const key = item.type === "tool" ? `tool:${item.name}` : item.id;
|
|
29158
|
+
if (item.type === "tool") {
|
|
29159
|
+
const own = item.definitionVariants ?? (item.body ? [{ sourcePath: item.sourcePath, body: item.body, evidence: item.evidence }] : []);
|
|
29160
|
+
let variants = definitions.get(key);
|
|
29161
|
+
for (const variant of own) {
|
|
29162
|
+
if (!variant.body)
|
|
29163
|
+
continue;
|
|
29164
|
+
if (!variants)
|
|
29165
|
+
definitions.set(key, variants = []);
|
|
29166
|
+
if (!variants.some((entry) => entry.id === item.id && entry.sourcePath === variant.sourcePath && entry.body === variant.body))
|
|
29167
|
+
variants.push({ id: item.id, sourcePath: variant.sourcePath, body: variant.body, evidence: [...variant.evidence ?? item.evidence], usedByAgentIds: [...item.usedByAgentIds] });
|
|
29168
|
+
}
|
|
29169
|
+
}
|
|
29170
|
+
const previous = merged.get(key);
|
|
29171
|
+
if (!previous) {
|
|
29172
|
+
merged.set(key, { ...item, evidence: [...item.evidence], usedByAgentIds: [...item.usedByAgentIds] });
|
|
29173
|
+
continue;
|
|
29174
|
+
}
|
|
29175
|
+
const rank = { defined_only: 0, reachable: 1, in_prod: 2 };
|
|
29176
|
+
if (item.status && (!previous.status || rank[item.status] > rank[previous.status]))
|
|
29177
|
+
previous.status = item.status;
|
|
29178
|
+
previous.usedByAgentIds = [...new Set([...previous.usedByAgentIds, ...item.usedByAgentIds])].sort();
|
|
29179
|
+
const evidence = new Set(previous.evidence.map((entry) => `${entry.path}:${entry.line}`));
|
|
29180
|
+
previous.evidence.push(...item.evidence.filter((entry) => !evidence.has(`${entry.path}:${entry.line}`)));
|
|
29181
|
+
if (!previous.body && item.body && !previous.definitionVariants)
|
|
29182
|
+
previous.body = item.body;
|
|
29183
|
+
}
|
|
29184
|
+
for (const [key, variants] of definitions) {
|
|
29185
|
+
const item = merged.get(key);
|
|
29186
|
+
if (variants.length > 1 || item.id !== variants[0].id)
|
|
29187
|
+
item.definitionVariants = variants;
|
|
29188
|
+
if (new Set(variants.map((entry) => entry.body)).size > 1)
|
|
29189
|
+
delete item.body;
|
|
29190
|
+
}
|
|
29191
|
+
const output = [...merged.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
29192
|
+
const runtimeAgents = components.runtimeAgents.map((entry) => ({
|
|
29193
|
+
...entry,
|
|
29194
|
+
artifactIds: [...new Set([...entry.artifactIds, ...output.filter((item) => item.usedByAgentIds.includes(entry.id)).map((item) => item.id)])].sort()
|
|
29195
|
+
}));
|
|
29196
|
+
return { runtimeAgents, artifacts: output };
|
|
29197
|
+
}
|
|
29198
|
+
|
|
29199
|
+
export { startRemoteAnalyze, pollRemoteAnalyzeUntilDone, isRecoverableRemoteRun, checkPromptMapping, safePromptEvidencePath, discoverHarnessPromptSources, valueChildren, selectKey, allValues, renderings, CALL_SINKS, registerCallSink, registerClassSink, isModelId, collectModels, modelArtifacts, modelUnknowns, collectModelLiterals, agentForScope, createAgentScopeResolver, enumerateAgents, toolNamesOf, EXTRACT_SCHEMA_VERSION, enumerateWorkflows, enumerateToolCatalog, ENUMERATORS, enumerateHarnessComponents, mergeEnumeratedComponents, clearExtractCache, extractHarness, extractHarnessWithContext, scopeOf, serializeExtractResult, runEnumerators, assignAgentIds, runHarnessCommand, writeHarnessAnalyzeResult, emitWrittenArtifacts, analyzeHarnessPrompts, isToolGroupPlaceholder, repairExternalAnalystReport, deriveRelationshipsFromGraph, deterministicArtifacts, deterministicGraphItems, mergeDeterministicArtifacts, analystRefutations, describeCodingAgentEvent, analystEventProgress, isRetryableExternalAnalystFailure, sleep, summarizeInvalidAnalystReport, isDeterministicLocalAnalyst, resolveHarnessAnalyzeConcern, resolveExternalHarnessAnalystInvocation, detectAvailableCodingAgents, codingAgentDisplayName, runCodingAgent, readOnlyClaudeExtraArgs, resolveAnalystModel, renderExternalHarnessAnalystPrompt, CONCERN_CANDIDATE_KINDS, SEED_CANDIDATE_KIND_LABELS, renderAnalystCandidateLines, renderAnalystSeedLines, truncateText, scanHarness, summarizeHarness, buildSourceSnapshot, writeHumanProgress, describeSyncIdentity, describeSyncTenantLabel, findFresherHarnessReports, writeHarnessArtifacts, assertHarnessGraphSyncQuality, graphForSync, inventoryFiles, numericEnv, numericEnvAllowZero, computeDeterministicFloor, stableId, titleFromId, promptNameFromPath };
|