@moda-ai/cli 1.55.88 → 1.55.89

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.
@@ -1,14 +1,6 @@
1
- import {
2
- $parseDocument
3
- } from "./cli-9kfxkxmk.js";
4
- import {
5
- SECRET_KEYS,
6
- redactString
7
- } from "./cli-p5grm12t.js";
8
1
  import {
9
2
  __commonJS,
10
- __require,
11
- __toESM
3
+ __require
12
4
  } from "./cli-0v6na3yp.js";
13
5
 
14
6
  // ../../node_modules/.bun/source-map@0.6.1/node_modules/source-map/lib/base64.js
@@ -170914,1511 +170906,4 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
170914
170906
  } });
170915
170907
  });
170916
170908
 
170917
- // src/prompt-sources.ts
170918
- var import_typescript2 = __toESM(require_typescript(), 1);
170919
- import { createHash } from "node:crypto";
170920
- import { lstatSync as lstatSync2, readFileSync as readFileSync3, realpathSync as realpathSync3 } from "node:fs";
170921
- import { extname as extname2, isAbsolute, relative, resolve as resolve3 } from "node:path";
170922
-
170923
- // src/prompt-frameworks.ts
170924
- var PYTHON_AGENT_MODULES = [
170925
- "agents",
170926
- "agno.agent",
170927
- "pydantic_ai",
170928
- "crewai",
170929
- "smolagents",
170930
- "google.adk.agents",
170931
- "autogen_agentchat.agents",
170932
- "llama_index.core.agent"
170933
- ];
170934
- var TYPESCRIPT_AGENT_MODULES = [
170935
- "@mastra/core/agent",
170936
- "@openai/agents",
170937
- "@openai/agents-core"
170938
- ];
170939
-
170940
- // src/prompt-source-code.ts
170941
- var import_typescript = __toESM(require_typescript(), 1);
170942
- import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
170943
- import { dirname, extname, join, resolve } from "node:path";
170944
- var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
170945
- var MAX_BYTES = 2 * 1024 * 1024;
170946
- var MAX_DEPTH = 32;
170947
- var MAX_FILES = 256;
170948
- var MAX_GRAPH_BYTES = 16 * 1024 * 1024;
170949
- function extractCodeSource(rootDir, sourcePath, symbol, forceDependencyGraph = false) {
170950
- const state = { root: resolve(rootDir), files: [], unresolved: [], visiting: new Set, importsVisited: new Set, parsed: new Map, assignments: new Map, bytesRead: 0 };
170951
- const file = safeFile(state.root, state.root, sourcePath);
170952
- const parsed = parseFile(file, state);
170953
- const target = symbol ? findSymbol(parsed, symbol) : findInstructionCandidate(parsed);
170954
- if (!target) {
170955
- if (symbol)
170956
- throw new Error(`${sourcePath}: symbol '${symbol}' was not found`);
170957
- collectLocalImports(parsed, state, 0);
170958
- return { sourceText: parsed.text, dynamic: true, unresolved: [`symbol '${symbol ?? "<instruction-like candidate>"}' was not found`], files: state.files };
170959
- }
170960
- const sourceText = target.getText(parsed.file);
170961
- const range = nodeRange(parsed.file, target);
170962
- const owner = symbol?.split(".")[0];
170963
- if (symbol?.includes(".") && owner && isBindingMutated(parsed.file, owner)) {
170964
- return { sourceText, range, dynamic: true, unresolved: [`binding '${owner}' is reassigned or mutated`], files: state.files };
170965
- }
170966
- if (import_typescript.default.isFunctionDeclaration(target) || import_typescript.default.isMethodDeclaration(target) || import_typescript.default.isArrowFunction(target) || import_typescript.default.isFunctionExpression(target)) {
170967
- collectLocalImports(parsed, state, 0);
170968
- return { sourceText, range, dynamic: true, unresolved: ["function/builder source requires runtime inputs", ...state.unresolved], files: state.files };
170969
- }
170970
- if (import_typescript.default.isVariableDeclaration(target) && import_typescript.default.isIdentifier(target.name) && isBindingMutated(parsed.file, target.name.text)) {
170971
- return { sourceText, range, dynamic: true, unresolved: [`binding '${target.name.text}' is reassigned or mutated`], files: state.files };
170972
- }
170973
- const value = evaluate(target, parsed, state, 0);
170974
- if (forceDependencyGraph || !value.ok)
170975
- collectLocalImports(parsed, state, 0);
170976
- return { value: value.ok ? value.value : undefined, sourceText, range, ...import_typescript.default.isClassDeclaration(target) ? { declarationKind: "class" } : {}, dynamic: !value.ok, unresolved: [...new Set([...state.unresolved, ...value.unresolved])], files: state.files };
170977
- }
170978
- function parseFile(filePath, state) {
170979
- const path = safeFile(state.root, state.root, filePath);
170980
- const cached = state.parsed.get(path);
170981
- if (cached)
170982
- return cached;
170983
- const text = readFileSync(path, "utf8");
170984
- state.bytesRead += Buffer.byteLength(text);
170985
- state.files.push(path);
170986
- const scriptKind = extname(path) === ".json" ? import_typescript.default.ScriptKind.JSON : /\.tsx?$/.test(extname(path)) ? import_typescript.default.ScriptKind.TSX : import_typescript.default.ScriptKind.JSX;
170987
- const file = import_typescript.default.createSourceFile(path, text, import_typescript.default.ScriptTarget.Latest, true, scriptKind);
170988
- const diagnostics = file.parseDiagnostics ?? [];
170989
- if (diagnostics.length)
170990
- throw new Error(`${path}: syntax error in source (${import_typescript.default.flattenDiagnosticMessageText(diagnostics[0].messageText, " ")})`);
170991
- const parsed = { file, text, path };
170992
- state.parsed.set(path, parsed);
170993
- return parsed;
170994
- }
170995
- function safeFile(root, base, requested) {
170996
- if (requested.includes("\x00"))
170997
- throw new Error(`Prompt source path contains NUL: ${requested}`);
170998
- const candidate = resolve(base, requested);
170999
- let cursor = candidate;
171000
- while (cursor !== root) {
171001
- let stat2;
171002
- try {
171003
- stat2 = lstatSync(cursor);
171004
- } catch {
171005
- break;
171006
- }
171007
- if (stat2.isSymbolicLink())
171008
- throw new Error(`Prompt source uses a symlink: ${requested}`);
171009
- const next = cursor.slice(0, cursor.lastIndexOf("/")) || "/";
171010
- if (next === cursor)
171011
- break;
171012
- cursor = next;
171013
- }
171014
- const rootReal = resolve(root);
171015
- let real;
171016
- try {
171017
- real = requireRealpath(candidate);
171018
- } catch {
171019
- throw new Error(`Prompt source file not found: ${requested}`);
171020
- }
171021
- if (!within(rootReal, real))
171022
- throw new Error(`Prompt source escapes repository root: ${requested}`);
171023
- const stat = lstatSync(real);
171024
- if (!stat.isFile() || stat.size > MAX_BYTES)
171025
- throw new Error(`Prompt source is not a bounded regular file: ${requested}`);
171026
- return real;
171027
- }
171028
- function requireRealpath(path) {
171029
- return realpathSync(path);
171030
- }
171031
- function within(root, child) {
171032
- return child === root || child.startsWith(`${root}/`);
171033
- }
171034
- function findSymbol(parsed, symbol) {
171035
- const names = symbol.split(".");
171036
- const matches = [];
171037
- const visit = (n) => {
171038
- if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name) && n.name.text === names[0])
171039
- matches.push(n);
171040
- else if (import_typescript.default.isFunctionDeclaration(n) && n.name?.text === names[0])
171041
- matches.push(n);
171042
- else if (import_typescript.default.isClassDeclaration(n) && n.name?.text === names[0])
171043
- matches.push(n);
171044
- import_typescript.default.forEachChild(n, visit);
171045
- };
171046
- visit(parsed.file);
171047
- if (matches.length > 1)
171048
- throw new Error(`${parsed.path}: symbol '${symbol}' is ambiguous (${matches.length} definitions)`);
171049
- const node = matches[0];
171050
- if (!node || names.length === 1)
171051
- return node;
171052
- let current = node;
171053
- for (const part of names.slice(1)) {
171054
- if (current && import_typescript.default.isClassDeclaration(current)) {
171055
- const members = current.members.filter((member) => propertyName(member.name) === part);
171056
- if (members.length > 1)
171057
- throw new Error(`${parsed.path}: symbol '${symbol}' is ambiguous`);
171058
- current = members[0];
171059
- if (!current)
171060
- return;
171061
- continue;
171062
- }
171063
- const initializer = current && import_typescript.default.isVariableDeclaration(current) ? current.initializer : undefined;
171064
- const object = initializer && import_typescript.default.isObjectLiteralExpression(initializer) ? initializer : initializer && import_typescript.default.isCallExpression(initializer) ? initializer.arguments.find(import_typescript.default.isObjectLiteralExpression) : initializer && import_typescript.default.isNewExpression(initializer) ? initializer.arguments?.find(import_typescript.default.isObjectLiteralExpression) : undefined;
171065
- const property = object?.properties.find((p) => import_typescript.default.isPropertyAssignment(p) && propertyName(p.name) === part);
171066
- if (!property)
171067
- return;
171068
- current = property;
171069
- }
171070
- return current;
171071
- }
171072
- function collectLocalImports(parsed, state, depth) {
171073
- if (depth > MAX_DEPTH) {
171074
- state.unresolved.push("local import graph exceeded depth bound");
171075
- return;
171076
- }
171077
- if (state.importsVisited.has(parsed.path))
171078
- return;
171079
- state.importsVisited.add(parsed.path);
171080
- parsed.file.forEachChild((node) => {
171081
- if (!import_typescript.default.isImportDeclaration(node) || !import_typescript.default.isStringLiteral(node.moduleSpecifier))
171082
- return;
171083
- const resolved = moduleSpecifier(parsed.path, node.moduleSpecifier.text);
171084
- if (!resolved) {
171085
- if (node.moduleSpecifier.text.startsWith("."))
171086
- state.unresolved.push(`unresolved local import '${node.moduleSpecifier.text}'`);
171087
- return;
171088
- }
171089
- if (state.files.length >= MAX_FILES || state.bytesRead >= MAX_GRAPH_BYTES) {
171090
- state.unresolved.push("local dependency graph exceeded file or byte bound");
171091
- return;
171092
- }
171093
- const imported = parseFile(resolved, state);
171094
- collectLocalImports(imported, state, depth + 1);
171095
- });
171096
- const scan = (node) => {
171097
- if (import_typescript.default.isCallExpression(node) && node.expression.kind === import_typescript.default.SyntaxKind.ImportKeyword) {
171098
- const argument = node.arguments[0];
171099
- if (argument && import_typescript.default.isStringLiteral(argument)) {
171100
- const resolved = moduleSpecifier(parsed.path, argument.text);
171101
- if (resolved) {
171102
- if (state.files.length >= MAX_FILES || state.bytesRead >= MAX_GRAPH_BYTES)
171103
- state.unresolved.push("local dependency graph exceeded file or byte bound");
171104
- else
171105
- collectLocalImports(parseFile(resolved, state), state, depth + 1);
171106
- } else if (argument.text.startsWith("."))
171107
- state.unresolved.push(`unresolved local dynamic import '${argument.text}'`);
171108
- } else
171109
- state.unresolved.push("dynamic import path is unresolved");
171110
- }
171111
- import_typescript.default.forEachChild(node, scan);
171112
- };
171113
- scan(parsed.file);
171114
- }
171115
- function findInstructionCandidate(parsed) {
171116
- const candidates = [];
171117
- const terms = /prompt|instruction|system|message|template/i;
171118
- const visit = (n) => {
171119
- if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name) && terms.test(n.name.text) && isCandidateInitializer(n.initializer))
171120
- candidates.push(n);
171121
- else if (import_typescript.default.isPropertyAssignment(n) && propertyName(n.name) && terms.test(propertyName(n.name)) && isCandidateInitializer(n.initializer))
171122
- candidates.push(n);
171123
- import_typescript.default.forEachChild(n, visit);
171124
- };
171125
- visit(parsed.file);
171126
- if (candidates.length > 1)
171127
- throw new Error(`${parsed.path}: instruction-like symbols are ambiguous; provide a selector`);
171128
- return candidates[0];
171129
- }
171130
- function isPromptName(name) {
171131
- return /prompt|instruction|system|message|template/i.test(name);
171132
- }
171133
- function isCandidateInitializer(initializer) {
171134
- if (!initializer)
171135
- return false;
171136
- return import_typescript.default.isStringLiteral(initializer) || import_typescript.default.isNoSubstitutionTemplateLiteral(initializer) || import_typescript.default.isTemplateExpression(initializer) || import_typescript.default.isBinaryExpression(initializer) && initializer.operatorToken.kind === import_typescript.default.SyntaxKind.PlusToken || import_typescript.default.isFunctionExpression(initializer) || import_typescript.default.isArrowFunction(initializer) || import_typescript.default.isIdentifier(initializer) && isPromptName(initializer.text) || import_typescript.default.isCallExpression(initializer) && import_typescript.default.isPropertyAccessExpression(initializer.expression) && initializer.expression.name.text === "join" || import_typescript.default.isArrayLiteralExpression(initializer) && initializer.elements.some((item) => import_typescript.default.isObjectLiteralExpression(item) || import_typescript.default.isStringLiteral(item));
171137
- }
171138
- function propertyName(name) {
171139
- if (!name)
171140
- return;
171141
- return import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name) ? name.text : undefined;
171142
- }
171143
- function evaluate(node, parsed, state, depth) {
171144
- if (depth > MAX_DEPTH)
171145
- return fail("static evaluation depth exceeded");
171146
- let expr;
171147
- if (import_typescript.default.isVariableDeclaration(node))
171148
- expr = node.initializer;
171149
- else if (import_typescript.default.isPropertyAssignment(node) || import_typescript.default.isPropertyDeclaration(node))
171150
- expr = node.initializer;
171151
- else if (import_typescript.default.isExpression(node))
171152
- expr = node;
171153
- if (!expr)
171154
- return fail("selected symbol has no static initializer");
171155
- return evalExpression(expr, parsed, state, depth);
171156
- }
171157
- function evalExpression(expr, parsed, state, depth) {
171158
- if (import_typescript.default.isStringLiteral(expr) || import_typescript.default.isNoSubstitutionTemplateLiteral(expr))
171159
- return ok(expr.text);
171160
- if (expr.kind === import_typescript.default.SyntaxKind.TrueKeyword)
171161
- return ok(true);
171162
- if (expr.kind === import_typescript.default.SyntaxKind.FalseKeyword)
171163
- return ok(false);
171164
- if (expr.kind === import_typescript.default.SyntaxKind.NullKeyword)
171165
- return ok(null);
171166
- if (import_typescript.default.isNumericLiteral(expr))
171167
- return ok(Number(expr.text));
171168
- if (import_typescript.default.isParenthesizedExpression(expr) || import_typescript.default.isAsExpression(expr) || import_typescript.default.isTypeAssertionExpression(expr))
171169
- return evalExpression(expr.expression, parsed, state, depth + 1);
171170
- if (import_typescript.default.isArrayLiteralExpression(expr)) {
171171
- const values = [];
171172
- for (const item of expr.elements) {
171173
- if (import_typescript.default.isSpreadElement(item))
171174
- return fail("spread elements are dynamic");
171175
- const result = evalExpression(item, parsed, state, depth + 1);
171176
- if (!result.ok)
171177
- return result;
171178
- values.push(result.value);
171179
- }
171180
- return ok(values);
171181
- }
171182
- if (import_typescript.default.isObjectLiteralExpression(expr)) {
171183
- const value = {};
171184
- for (const item of expr.properties) {
171185
- if (!import_typescript.default.isPropertyAssignment(item))
171186
- return fail("methods/getters in object literals are dynamic");
171187
- const name = propertyName(item.name);
171188
- if (!name)
171189
- return fail("computed object property is dynamic");
171190
- const result = evalExpression(item.initializer, parsed, state, depth + 1);
171191
- if (!result.ok)
171192
- return result;
171193
- value[name] = result.value;
171194
- }
171195
- return ok(value);
171196
- }
171197
- if (import_typescript.default.isBinaryExpression(expr) && expr.operatorToken.kind === import_typescript.default.SyntaxKind.PlusToken) {
171198
- const left = evalExpression(expr.left, parsed, state, depth + 1);
171199
- const right = evalExpression(expr.right, parsed, state, depth + 1);
171200
- if (!left.ok || !right.ok || typeof left.value !== "string" && typeof right.value !== "string")
171201
- return fail("concatenation contains a dynamic or non-string value");
171202
- return ok(String(left.value) + String(right.value));
171203
- }
171204
- if (import_typescript.default.isTemplateExpression(expr)) {
171205
- let value = expr.head.text;
171206
- for (const span of expr.templateSpans) {
171207
- const part = evalExpression(span.expression, parsed, state, depth + 1);
171208
- if (!part.ok || !["string", "number", "boolean"].includes(typeof part.value))
171209
- return fail("template interpolation contains runtime expressions");
171210
- value += String(part.value) + span.literal.text;
171211
- }
171212
- return ok(value);
171213
- }
171214
- if (import_typescript.default.isCallExpression(expr)) {
171215
- if (import_typescript.default.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "join") {
171216
- const receiver = evalExpression(expr.expression.expression, parsed, state, depth + 1);
171217
- const separator = expr.arguments.length ? evalExpression(expr.arguments[0], parsed, state, depth + 1) : ok(",");
171218
- if (receiver.ok && Array.isArray(receiver.value) && separator.ok && typeof separator.value === "string" && receiver.value.every((v) => typeof v === "string"))
171219
- return ok(receiver.value.join(separator.value));
171220
- }
171221
- return fail("function calls other than static Array.join are dynamic");
171222
- }
171223
- if (import_typescript.default.isIdentifier(expr))
171224
- return resolveIdentifier(expr.text, parsed, state, depth + 1);
171225
- return fail(`unsupported expression '${import_typescript.default.SyntaxKind[expr.kind] ?? "unknown"}'`);
171226
- }
171227
- function resolveIdentifier(name, parsed, state, depth) {
171228
- const key = `${parsed.path}:${name}`;
171229
- if (state.visiting.has(key))
171230
- return fail(`cyclic binding '${name}'`);
171231
- state.visiting.add(key);
171232
- try {
171233
- const declarations = [];
171234
- const imports = [];
171235
- parsed.file.forEachChild(function visit(n) {
171236
- if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name) && n.name.text === name)
171237
- declarations.push(n);
171238
- if (import_typescript.default.isImportDeclaration(n))
171239
- imports.push(n);
171240
- import_typescript.default.forEachChild(n, visit);
171241
- });
171242
- if (declarations.length !== 1) {
171243
- if (isBindingMutated(parsed.file, name))
171244
- return fail(`binding '${name}' is reassigned or mutated`);
171245
- const imported = imports.find((i) => {
171246
- const bindings = i.importClause?.namedBindings;
171247
- return Boolean(bindings && import_typescript.default.isNamedImports(bindings) && bindings.elements.some((e) => e.name.text === name || e.propertyName?.text === name));
171248
- });
171249
- const importedBindings = imported?.importClause?.namedBindings;
171250
- if (imported && importedBindings && import_typescript.default.isNamedImports(importedBindings)) {
171251
- const spec = importedBindings.elements.find((e) => e.name.text === name || e.propertyName?.text === name);
171252
- const moduleName = import_typescript.default.isStringLiteral(imported.moduleSpecifier) ? imported.moduleSpecifier.text : undefined;
171253
- const modulePath = moduleName ? moduleSpecifier(parsed.path, moduleName) : undefined;
171254
- if (!modulePath)
171255
- return fail(`unresolved local import for '${name}'`);
171256
- if (state.files.length >= MAX_FILES || state.bytesRead >= MAX_GRAPH_BYTES)
171257
- return fail("local dependency graph exceeded file or byte bound");
171258
- const importedParsed = parseFile(modulePath, state);
171259
- const importedNode = findSymbol(importedParsed, spec.propertyName?.text ?? spec.name.text);
171260
- if (!importedNode)
171261
- return fail(`imported symbol '${name}' was not found`);
171262
- if (import_typescript.default.isVariableDeclaration(importedNode) && import_typescript.default.isIdentifier(importedNode.name) && isBindingMutated(importedParsed.file, importedNode.name.text))
171263
- return fail(`binding '${name}' is reassigned or mutated`);
171264
- return evaluate(importedNode, importedParsed, state, depth + 1);
171265
- }
171266
- return fail(declarations.length ? `shadowed binding '${name}' is ambiguous` : `binding '${name}' was not found`);
171267
- }
171268
- if (isBindingMutated(parsed.file, name))
171269
- return fail(`binding '${name}' is reassigned or mutated`);
171270
- return evaluate(declarations[0], parsed, state, depth + 1);
171271
- } finally {
171272
- state.visiting.delete(key);
171273
- }
171274
- }
171275
- function isBindingMutated(file, name) {
171276
- const aliases = new Map;
171277
- const mutated = new Set;
171278
- const mutableBindings = new Set;
171279
- const escaped = new Set;
171280
- const mutatingMethods = new Set(["copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift"]);
171281
- const assignments = new Set([
171282
- import_typescript.default.SyntaxKind.EqualsToken,
171283
- import_typescript.default.SyntaxKind.PlusEqualsToken,
171284
- import_typescript.default.SyntaxKind.MinusEqualsToken,
171285
- import_typescript.default.SyntaxKind.AsteriskEqualsToken,
171286
- import_typescript.default.SyntaxKind.SlashEqualsToken,
171287
- import_typescript.default.SyntaxKind.PercentEqualsToken,
171288
- import_typescript.default.SyntaxKind.AmpersandEqualsToken,
171289
- import_typescript.default.SyntaxKind.BarEqualsToken,
171290
- import_typescript.default.SyntaxKind.CaretEqualsToken,
171291
- import_typescript.default.SyntaxKind.LessThanLessThanEqualsToken,
171292
- import_typescript.default.SyntaxKind.GreaterThanGreaterThanEqualsToken,
171293
- import_typescript.default.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
171294
- import_typescript.default.SyntaxKind.QuestionQuestionEqualsToken,
171295
- import_typescript.default.SyntaxKind.BarBarEqualsToken,
171296
- import_typescript.default.SyntaxKind.AmpersandAmpersandEqualsToken
171297
- ]);
171298
- const walk = (n) => {
171299
- if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name) && n.initializer && import_typescript.default.isIdentifier(n.initializer))
171300
- aliases.set(n.name.text, n.initializer.text);
171301
- if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name) && n.initializer && (import_typescript.default.isArrayLiteralExpression(n.initializer) || import_typescript.default.isObjectLiteralExpression(n.initializer)))
171302
- mutableBindings.add(n.name.text);
171303
- if (import_typescript.default.isBinaryExpression(n) && (assignments.has(n.operatorToken.kind) || n.operatorToken.getText().endsWith("=")) && import_typescript.default.isIdentifier(n.left))
171304
- mutated.add(n.left.text);
171305
- if (import_typescript.default.isBinaryExpression(n) && (assignments.has(n.operatorToken.kind) || n.operatorToken.getText().endsWith("=")) && (import_typescript.default.isPropertyAccessExpression(n.left) || import_typescript.default.isElementAccessExpression(n.left))) {
171306
- const receiver = import_typescript.default.isPropertyAccessExpression(n.left) ? n.left.expression : n.left.expression;
171307
- if (import_typescript.default.isIdentifier(receiver))
171308
- mutated.add(receiver.text);
171309
- }
171310
- if ((import_typescript.default.isPrefixUnaryExpression(n) || import_typescript.default.isPostfixUnaryExpression(n)) && import_typescript.default.isIdentifier(n.operand))
171311
- mutated.add(n.operand.text);
171312
- if (import_typescript.default.isCallExpression(n)) {
171313
- if (import_typescript.default.isPropertyAccessExpression(n.expression) && mutatingMethods.has(n.expression.name.text) && import_typescript.default.isIdentifier(n.expression.expression))
171314
- mutated.add(n.expression.expression.text);
171315
- const knownStaticJoin = import_typescript.default.isPropertyAccessExpression(n.expression) && n.expression.name.text === "join";
171316
- if (!knownStaticJoin) {
171317
- for (const argument of n.arguments)
171318
- if (import_typescript.default.isIdentifier(argument))
171319
- escaped.add(argument.text);
171320
- }
171321
- }
171322
- import_typescript.default.forEachChild(n, walk);
171323
- };
171324
- walk(file);
171325
- let changed = true;
171326
- while (changed) {
171327
- changed = false;
171328
- for (const [alias, original] of aliases)
171329
- if (mutableBindings.has(original) && !mutableBindings.has(alias)) {
171330
- mutableBindings.add(alias);
171331
- changed = true;
171332
- }
171333
- }
171334
- for (const binding of escaped)
171335
- if (mutableBindings.has(binding))
171336
- mutated.add(binding);
171337
- changed = true;
171338
- while (changed) {
171339
- changed = false;
171340
- for (const [alias, original] of aliases)
171341
- if (mutated.has(alias) && !mutated.has(original)) {
171342
- mutated.add(original);
171343
- changed = true;
171344
- }
171345
- }
171346
- return mutated.has(name);
171347
- }
171348
- function moduleSpecifier(from, specifier) {
171349
- if (!specifier.startsWith("."))
171350
- return;
171351
- const base = resolve(dirname(from), specifier);
171352
- const stripped = /\.(?:js|jsx|mjs|cjs)$/.test(base) ? base.slice(0, base.lastIndexOf(".")) : base;
171353
- for (const candidate of [base, stripped, ...EXTENSIONS.map((ext) => `${base}${ext}`), ...EXTENSIONS.map((ext) => `${stripped}${ext}`), ...EXTENSIONS.map((ext) => join(base, `index${ext}`)), ...EXTENSIONS.map((ext) => join(stripped, `index${ext}`))])
171354
- if (existsSync(candidate) && lstatSync(candidate).isFile())
171355
- return candidate;
171356
- return;
171357
- }
171358
- function ok(value) {
171359
- return { ok: true, value, unresolved: [] };
171360
- }
171361
- function fail(reason) {
171362
- return { ok: false, unresolved: [reason] };
171363
- }
171364
- function nodeRange(file, node) {
171365
- return {
171366
- line: file.getLineAndCharacterOfPosition(node.getStart(file)).line + 1,
171367
- endLine: file.getLineAndCharacterOfPosition(Math.max(node.getStart(file), node.getEnd() - 1)).line + 1
171368
- };
171369
- }
171370
- function inspectCodeSelectors(rootDir, sourcePath, symbol) {
171371
- const state = { root: resolve(rootDir), files: [], unresolved: [], visiting: new Set, importsVisited: new Set, parsed: new Map, assignments: new Map, bytesRead: 0 };
171372
- const parsed = parseFile(safeFile(state.root, state.root, sourcePath), state);
171373
- if (symbol) {
171374
- const node = findSymbol(parsed, symbol);
171375
- if (!node)
171376
- throw new Error(`Symbol '${symbol}' was not found`);
171377
- return [{ symbol, ...nodeRange(parsed.file, node), references: referenceNames(node), unresolved: [] }];
171378
- }
171379
- const entries = [];
171380
- const add = (name, node) => {
171381
- let unresolved = [];
171382
- try {
171383
- if (!findSymbol(parsed, name))
171384
- unresolved = ["Selector is not addressable by the source resolver"];
171385
- } catch (error) {
171386
- unresolved = [error instanceof Error ? error.message : String(error)];
171387
- }
171388
- entries.push({ symbol: name, ...nodeRange(parsed.file, node), references: referenceNames(node), unresolved });
171389
- };
171390
- const visit = (node) => {
171391
- if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name))
171392
- add(node.name.text, node);
171393
- if (import_typescript.default.isFunctionDeclaration(node) && node.name)
171394
- add(node.name.text, node);
171395
- if (import_typescript.default.isClassDeclaration(node) && node.name) {
171396
- add(node.name.text, node);
171397
- for (const member of node.members) {
171398
- const name = propertyName(member.name);
171399
- if (name)
171400
- add(`${node.name.text}.${name}`, member);
171401
- }
171402
- }
171403
- import_typescript.default.forEachChild(node, visit);
171404
- };
171405
- visit(parsed.file);
171406
- return entries;
171407
- }
171408
- function referenceNames(node) {
171409
- const names = new Set;
171410
- const visit = (child) => {
171411
- if (import_typescript.default.isIdentifier(child))
171412
- names.add(child.text);
171413
- import_typescript.default.forEachChild(child, visit);
171414
- };
171415
- visit(node);
171416
- return [...names].sort();
171417
- }
171418
-
171419
- // src/prompt-source-python.ts
171420
- import { spawnSync } from "node:child_process";
171421
- import { readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
171422
- import { resolve as resolve2 } from "node:path";
171423
- var PYTHON_AGENT_MODULES_LITERAL = JSON.stringify(PYTHON_AGENT_MODULES);
171424
- var SELECTOR_HELPER = String.raw`import ast,json,os,sys
171425
- request=json.loads(sys.stdin.read()); root=os.path.realpath(request['root']); path=os.path.abspath(request['path']); wanted=request.get('symbol')
171426
- def inside(p): return p==root or p.startswith(root+os.sep)
171427
- def fail(message): print(json.dumps({'candidates':[],'error':message})); raise SystemExit
171428
- if not inside(path): fail('source path escapes repository root')
171429
- cursor=path
171430
- while cursor != root:
171431
- if os.path.islink(cursor): fail('source path uses a symlink')
171432
- parent=os.path.dirname(cursor)
171433
- if parent==cursor: break
171434
- cursor=parent
171435
- if os.path.islink(path) or not os.path.isfile(path): fail('source path is unavailable or symlinked')
171436
- if os.path.getsize(path)>2*1024*1024: fail('source exceeds byte bound')
171437
- try:
171438
- source=open(path,encoding='utf-8').read(); tree=ast.parse(source,filename=path)
171439
- except SyntaxError as error: fail('syntax error: '+str(error))
171440
- except OSError as error: fail('source could not be read: '+str(error))
171441
- def static_string(node):
171442
- return isinstance(node,ast.Constant) and isinstance(node.value,str) or isinstance(node,ast.BinOp) and isinstance(node.op,ast.Add) and static_string(node.left) and static_string(node.right)
171443
- def segment(node): return ast.get_source_segment(source,node) or ''
171444
- def target_names(node):
171445
- if isinstance(node,ast.Name): return [node.id]
171446
- if isinstance(node,(ast.Tuple,ast.List)):
171447
- out=[]
171448
- for item in node.elts: out.extend(target_names(item))
171449
- return out
171450
- return []
171451
- found=[]
171452
- def add(symbol,node,definition):
171453
- dynamic=definition is None or not static_string(definition)
171454
- found.append({'symbol':symbol,'kind':'builder' if dynamic else 'text','coverage':'partial' if dynamic else 'complete','sourceText':segment(node),'references':sorted({item.id for item in ast.walk(node) if isinstance(item,ast.Name)}),'line':getattr(node,'lineno',1),'endLine':getattr(node,'end_lineno',getattr(node,'lineno',1)),'unresolved':[] if not dynamic else ['definition contains runtime expressions or callable behavior']})
171455
- def visit(statements,prefix):
171456
- for node in statements:
171457
- if isinstance(node,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)):
171458
- current=prefix+[node.name]; add('.'.join(current),node,None); visit(node.body,current); continue
171459
- if isinstance(node,ast.Assign):
171460
- for target in node.targets:
171461
- for name in target_names(target): add('.'.join(prefix+[name]),node,node.value)
171462
- elif isinstance(node,ast.AnnAssign):
171463
- for name in target_names(node.target): add('.'.join(prefix+[name]),node,node.value)
171464
- elif isinstance(node,ast.NamedExpr):
171465
- for name in target_names(node.target): add('.'.join(prefix+[name]),node,node.value)
171466
- for child in ast.iter_child_nodes(node):
171467
- if isinstance(child,ast.stmt): visit([child],prefix)
171468
- visit(tree.body,[])
171469
- if wanted:
171470
- exact=[item for item in found if item['symbol']==wanted] if '.' in wanted else [item for item in found if item['symbol'].split('.')[-1]==wanted]
171471
- if len(exact)>1: fail("ambiguous symbol '%s'; provide a qualified lexical selector" % wanted)
171472
- if not exact: fail("symbol '%s' was not found" % wanted)
171473
- print(json.dumps({'candidates':exact},ensure_ascii=False))
171474
- else:
171475
- counts={}
171476
- for item in found: counts[item['symbol']]=counts.get(item['symbol'],0)+1
171477
- for item in found:
171478
- if counts[item['symbol']]>1:
171479
- item['coverage']='partial'; item['unresolved']=list(item['unresolved'])+['qualified symbol is reassigned or duplicated']
171480
- print(json.dumps({'candidates':found},ensure_ascii=False))`;
171481
- function inspectPythonSelectors(rootDir, sourcePath, symbol) {
171482
- const root = realpathSync2(rootDir);
171483
- const absolute = resolve2(root, sourcePath);
171484
- const result = spawnSync("python3", ["-I", "-c", SELECTOR_HELPER], {
171485
- input: JSON.stringify({ root, path: absolute, ...symbol ? { symbol } : {} }),
171486
- encoding: "utf8",
171487
- maxBuffer: 4 * 1024 * 1024,
171488
- timeout: 1e4
171489
- });
171490
- if (result.error || result.status !== 0)
171491
- throw new Error(`python3 unavailable: ${result.error?.message ?? result.stderr ?? "helper failed"}`);
171492
- let parsed;
171493
- try {
171494
- parsed = JSON.parse(result.stdout.trim());
171495
- } catch {
171496
- throw new Error("Python AST selector helper returned invalid output");
171497
- }
171498
- if (parsed.error)
171499
- throw new Error(parsed.error);
171500
- return parsed.candidates ?? [];
171501
- }
171502
- var DISCOVERY_HELPER = String.raw`import ast,json,os,sys
171503
- req=json.loads(sys.stdin.read()); root=os.path.realpath(req['root']); path=os.path.abspath(req['path'])
171504
- def inside(p): return p==root or p.startswith(root+os.sep)
171505
- def fail(message): print(json.dumps({'candidates':[],'error':message})); raise SystemExit
171506
- try:
171507
- if not inside(path): fail('source path escapes repository root')
171508
- cursor=path
171509
- while cursor != root:
171510
- if os.path.islink(cursor): fail('source path uses a symlink')
171511
- parent=os.path.dirname(cursor)
171512
- if parent==cursor: break
171513
- cursor=parent
171514
- if os.path.islink(path) or not os.path.isfile(path): fail('source path is unavailable or symlinked')
171515
- if os.path.getsize(path)>2*1024*1024: fail('source exceeds byte bound')
171516
- text=open(path,encoding='utf-8').read(); tree=ast.parse(text,filename=path)
171517
- parents={child:node for node in ast.walk(tree) for child in ast.iter_child_nodes(node)}
171518
- bindings={}; binding_counts={}
171519
- # Bare Agent(...) is only trusted when Agent came from a known agent
171520
- # framework. Local classes and arbitrary SDKs must not become prompt sources.
171521
- allowed_modules=set(${PYTHON_AGENT_MODULES_LITERAL})
171522
- agent_names=set(); agent_modules=set(); shadowed=set(); shadowed_modules=set()
171523
- for node in tree.body:
171524
- if isinstance(node,ast.ImportFrom) and node.level==0:
171525
- for alias in node.names:
171526
- if alias.name=='Agent' and node.level==0 and node.module in allowed_modules: agent_names.add(alias.asname or alias.name)
171527
- if isinstance(node,ast.Import):
171528
- for alias in node.names:
171529
- module=alias.name
171530
- # A dotted import without an explicit alias binds its top-level
171531
- # package (for example, import agno.agent binds agno), so it must
171532
- # not silently authorize agno.Agent.
171533
- if module in allowed_modules and (alias.asname or '.' not in module): agent_modules.add(alias.asname or module)
171534
- if isinstance(node,(ast.ClassDef,ast.FunctionDef,ast.AsyncFunctionDef)) and node.name in agent_names: shadowed.add(node.name)
171535
- if isinstance(node,(ast.Assign,ast.AnnAssign)):
171536
- targets=node.targets if isinstance(node,ast.Assign) else [node.target]
171537
- for target in targets:
171538
- if isinstance(target,ast.Name):
171539
- binding_counts[target.id]=binding_counts.get(target.id,0)+1
171540
- bindings[target.id]=node.value
171541
- if target.id in agent_names: shadowed.add(target.id)
171542
- if target.id in agent_modules: shadowed_modules.add(target.id)
171543
- if isinstance(node,ast.AugAssign) and isinstance(node.target,ast.Name):
171544
- if node.target.id in agent_names: shadowed.add(node.target.id)
171545
- if node.target.id in agent_modules: shadowed_modules.add(node.target.id)
171546
- agent_names-=shadowed
171547
- agent_modules-=shadowed_modules
171548
- def literal(node, seen=None):
171549
- seen=seen or set()
171550
- if isinstance(node,ast.Constant) and isinstance(node.value,str): return (True,node.value,'')
171551
- if isinstance(node,ast.Name) and node.id in bindings and node.id not in seen:
171552
- return literal(bindings[node.id], seen|{node.id})
171553
- if isinstance(node,ast.BinOp) and isinstance(node.op,ast.Add):
171554
- left=literal(node.left,seen); right=literal(node.right,seen)
171555
- if left[0] and right[0]: return (True,left[1]+right[1],'')
171556
- return (False,None,'concatenation contains a dynamic value')
171557
- return (False,None,'unsupported Python expression '+type(node).__name__)
171558
- def context(node):
171559
- current=parents.get(node); cls=None; fn=None
171560
- while current is not None:
171561
- if isinstance(current,ast.ClassDef) and cls is None: cls=current.name
171562
- if isinstance(current,(ast.FunctionDef,ast.AsyncFunctionDef)) and fn is None: fn=current.name
171563
- current=parents.get(current)
171564
- return cls,fn
171565
- def assignment_target(node):
171566
- parent=parents.get(node)
171567
- if isinstance(parent,ast.Assign) and parent.value is node:
171568
- names=[target.id for target in parent.targets if isinstance(target,ast.Name)]
171569
- return names[0] if len(names)==1 else None
171570
- if isinstance(parent,ast.AnnAssign) and parent.value is node and isinstance(parent.target,ast.Name): return parent.target.id
171571
- return None
171572
- calls=[]
171573
- for node in ast.walk(tree):
171574
- if not isinstance(node,ast.Call): continue
171575
- fn=node.func.id if isinstance(node.func,ast.Name) else node.func.attr if isinstance(node.func,ast.Attribute) else None
171576
- trusted=(isinstance(node.func,ast.Name) and fn in agent_names) or (isinstance(node.func,ast.Attribute) and isinstance(node.func.value,ast.Name) and node.func.value.id in agent_modules and fn=='Agent')
171577
- if not trusted: continue
171578
- fields=[keyword for keyword in node.keywords if keyword.arg in ('instructions','system_prompt','systemPrompt')]
171579
- if len(fields)!=1: continue
171580
- field=fields[0]; cls,method=context(node); target=assignment_target(node)
171581
- if cls and method: symbol=cls+'.'+method
171582
- elif method: symbol=method
171583
- elif target: symbol=target+'.'+field.arg
171584
- else: continue
171585
- calls.append((symbol,field,node,cls,method))
171586
- by_symbol={}
171587
- for item in calls: by_symbol.setdefault(item[0],[]).append(item)
171588
- out=[]
171589
- for symbol,items in sorted(by_symbol.items()):
171590
- if len(items)!=1: continue
171591
- _,field,node,cls,method=items[0]
171592
- source=ast.get_source_segment(text,field.value) or ast.get_source_segment(text,node) or ''
171593
- ok,value,reason=literal(field.value)
171594
- out.append({'symbol':symbol,'field':field.arg,'kind':'text' if ok else 'builder','content':value if ok else None,'sourceText':source,'line':getattr(field,'lineno',getattr(node,'lineno',1)),'knownBuilder':bool(method),'unresolved':[] if ok else [reason]})
171595
- # A local system message can identify its owning builder without inventing
171596
- # a module-level selector for the local variable. Only directly addressable
171597
- # functions/class methods are eligible; nested functions stay unresolved.
171598
- seen_symbols={item['symbol'] for item in out}
171599
- for node in ast.walk(tree):
171600
- if not isinstance(node,ast.Call): continue
171601
- fn=node.func.id if isinstance(node.func,ast.Name) else node.func.attr if isinstance(node.func,ast.Attribute) else None
171602
- if fn!='SystemMessage': continue
171603
- current=parents.get(node)
171604
- while current is not None and not isinstance(current,(ast.FunctionDef,ast.AsyncFunctionDef)): current=parents.get(current)
171605
- if current is None: continue
171606
- parent=parents.get(current)
171607
- if parent is tree: symbol=current.name
171608
- elif isinstance(parent,ast.ClassDef) and parents.get(parent) is tree: symbol=parent.name+'.'+current.name
171609
- else: continue
171610
- if symbol in seen_symbols: continue
171611
- seen_symbols.add(symbol)
171612
- out.append({'symbol':symbol,'field':'instructions','kind':'builder','knownBuilder':True,'sourceText':ast.get_source_segment(text,current) or '', 'line':current.lineno,'unresolved':['system message is assembled inside a callable']})
171613
- for cls in tree.body:
171614
- if not isinstance(cls,ast.ClassDef): continue
171615
- for child in cls.body:
171616
- targets=child.targets if isinstance(child,ast.Assign) else [child.target] if isinstance(child,ast.AnnAssign) else []
171617
- for item in targets:
171618
- if not isinstance(item,ast.Name) or not any(term in item.id.lower() for term in ('prompt','instruction','system','template')): continue
171619
- if child.value is None: continue
171620
- ok,value,reason=literal(child.value)
171621
- out.append({'symbol':cls.name+'.'+item.id,'field':'instructions','kind':'text' if ok else 'builder','content':value if ok else None,'sourceText':ast.get_source_segment(text,child) or '', 'line':child.lineno,'unresolved':[] if ok else [reason]})
171622
- print(json.dumps({'candidates':out},ensure_ascii=False))
171623
- except SyntaxError as error: print(json.dumps({'candidates':[],'error':'syntax error: '+str(error)}))
171624
- except SystemExit: pass
171625
- except Exception as error: print(json.dumps({'candidates':[],'error':str(error)}))`;
171626
- function discoverPythonPromptSources(rootDir, sourcePath) {
171627
- const result = spawnSync("python3", ["-I", "-c", DISCOVERY_HELPER], {
171628
- input: JSON.stringify({ root: rootDir, path: resolve2(rootDir, sourcePath) }),
171629
- encoding: "utf8",
171630
- maxBuffer: 4 * 1024 * 1024,
171631
- timeout: 1e4
171632
- });
171633
- if (result.error || result.status !== 0)
171634
- throw new Error(`python3 unavailable: ${result.error?.message ?? result.stderr ?? "helper failed"}`);
171635
- let parsed;
171636
- try {
171637
- parsed = JSON.parse(result.stdout.trim());
171638
- } catch {
171639
- throw new Error("Python AST discovery returned invalid output");
171640
- }
171641
- if (parsed.error)
171642
- throw new Error(parsed.error);
171643
- return parsed.candidates ?? [];
171644
- }
171645
- var HELPER = String.raw`import ast,json,os,sys
171646
- req=json.loads(sys.stdin.read()); root=os.path.realpath(req['root']); path=os.path.abspath(req['path']); symbol=req.get('symbol'); force=req.get('force',False)
171647
- def inside(p): return p==root or p.startswith(root+os.sep)
171648
- files=[]; seen=set(); sum_bytes=0
171649
- def load(p):
171650
- global sum_bytes
171651
- p=os.path.abspath(p)
171652
- if not inside(p): raise ValueError('local import escapes repository root')
171653
- cursor=p
171654
- while cursor != root:
171655
- if os.path.islink(cursor): raise ValueError('local import uses a symlink')
171656
- parent=os.path.dirname(cursor)
171657
- if parent == cursor: break
171658
- cursor=parent
171659
- p=os.path.realpath(p)
171660
- if not inside(p): raise ValueError('local import escapes repository root')
171661
- if p in seen: return None,''
171662
- if not os.path.isfile(p): raise ValueError('local import not found')
171663
- size=os.path.getsize(p)
171664
- if size>2*1024*1024: raise ValueError('source exceeds byte bound')
171665
- if len(files)>=256 or sum_bytes+size>16*1024*1024: raise ValueError('local dependency graph exceeded file or byte bound')
171666
- seen.add(p); files.append(p); sum_bytes+=size; text=open(p,encoding='utf-8').read(); return ast.parse(text,filename=p),text
171667
- def fail(msg,src=''): return {'sourceText':src,'dynamic':True,'unresolved':[msg],'files':files, 'range':selected_range}
171668
- selected_range=None
171669
- try:
171670
- tree,text=load(path)
171671
- trusted_template_names=set()
171672
- allowed_agent_modules=set(${PYTHON_AGENT_MODULES_LITERAL})
171673
- agent_names=set(); agent_modules=set(); shadowed_agents=set(); shadowed_modules=set()
171674
- for item in tree.body:
171675
- if isinstance(item,ast.ImportFrom) and item.level==0:
171676
- for alias in item.names:
171677
- if alias.name=='Agent' and item.level==0 and item.module in allowed_agent_modules: agent_names.add(alias.asname or alias.name)
171678
- if item.module=='llama_index.core.prompts' and alias.name in ('RichPromptTemplate','PromptTemplate'):
171679
- trusted_template_names.add(alias.asname or alias.name)
171680
- if isinstance(item,ast.Import):
171681
- for alias in item.names:
171682
- if alias.name in allowed_agent_modules and (alias.asname or '.' not in alias.name): agent_modules.add(alias.asname or alias.name)
171683
- if isinstance(item,(ast.ClassDef,ast.FunctionDef,ast.AsyncFunctionDef)) and item.name in agent_names:
171684
- shadowed_agents.add(item.name)
171685
- if isinstance(item,(ast.Assign,ast.AnnAssign)):
171686
- targets=item.targets if isinstance(item,ast.Assign) else [item.target]
171687
- if any(isinstance(target,ast.Name) and target.id in agent_names for target in targets):
171688
- shadowed_agents.update(target.id for target in targets if isinstance(target,ast.Name) and target.id in agent_names)
171689
- if any(isinstance(target,ast.Name) and target.id in agent_modules for target in targets):
171690
- shadowed_modules.update(target.id for target in targets if isinstance(target,ast.Name) and target.id in agent_modules)
171691
- if isinstance(item,ast.AugAssign) and isinstance(item.target,ast.Name):
171692
- if item.target.id in agent_names: shadowed_agents.add(item.target.id)
171693
- if item.target.id in agent_modules: shadowed_modules.add(item.target.id)
171694
- agent_names-=shadowed_agents; agent_modules-=shadowed_modules
171695
- def imported_path(current, node):
171696
- if not isinstance(node,ast.ImportFrom) or node.level < 1:return None
171697
- base=os.path.dirname(current)
171698
- for _ in range(node.level-1): base=os.path.dirname(base)
171699
- stem=os.path.join(base, *(node.module or '').split('.'))
171700
- for candidate in (stem+'.py',os.path.join(stem,'__init__.py')):
171701
- if os.path.isfile(candidate):return candidate
171702
- return None
171703
- def target(t,n):
171704
- if n and '.' in n:
171705
- parts=n.split('.'); matches=[]
171706
- def walk(statements, wanted):
171707
- for x in statements:
171708
- name=x.name if isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)) else None
171709
- targets=x.targets if isinstance(x,ast.Assign) else [x.target] if isinstance(x,ast.AnnAssign) else []
171710
- names=[y.id for y in targets if isinstance(y,ast.Name)]
171711
- if wanted[0] in ([name] if name else []) or wanted[0] in names:
171712
- if len(wanted)==1: matches.append(x)
171713
- elif isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)):
171714
- walk(x.body,wanted[1:])
171715
- elif isinstance(x,(ast.Assign,ast.AnnAssign)) and isinstance(x.value,ast.Call):
171716
- constructor=x.value.func.id if isinstance(x.value.func,ast.Name) else x.value.func.attr if isinstance(x.value.func,ast.Attribute) else None
171717
- trusted_constructor=(isinstance(x.value.func,ast.Name) and constructor in agent_names) or (isinstance(x.value.func,ast.Attribute) and isinstance(x.value.func.value,ast.Name) and x.value.func.value.id in agent_modules and constructor=='Agent')
171718
- if trusted_constructor:
171719
- fields=[keyword.value for keyword in x.value.keywords if keyword.arg==wanted[1]]
171720
- if len(wanted)==2 and len(fields)==1: matches.append(fields[0])
171721
- for child in ast.iter_child_nodes(x):
171722
- if isinstance(child,ast.stmt) and not isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)): walk([child],wanted)
171723
- walk(t.body,parts)
171724
- if len(matches)>1: raise ValueError("ambiguous symbol '"+n+"'; provide a selector with a unique definition")
171725
- return matches[0] if matches else None
171726
- matches=[]
171727
- for x in t.body:
171728
- if isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)) and x.name==n:matches.append(x)
171729
- if isinstance(x,ast.Assign):
171730
- if any(isinstance(y,ast.Name) and y.id==n for y in x.targets):matches.append(x)
171731
- if isinstance(x,ast.AnnAssign) and isinstance(x.target,ast.Name) and x.target.id==n:matches.append(x)
171732
- if len(matches)>1: raise ValueError("ambiguous symbol '"+n+"'; provide a selector with a unique definition")
171733
- return matches[0] if matches else None
171734
- chosen=target(tree,symbol) if symbol else None
171735
- if chosen is None and symbol:
171736
- print(json.dumps(fail("symbol '"+symbol+"' was not found",text))); raise SystemExit
171737
- if chosen is None:
171738
- terms=('prompt','instruction','system','message','template')
171739
- candidates=[]
171740
- def candidate_value(v):
171741
- return isinstance(v,(ast.JoinedStr,ast.BinOp,ast.List,ast.Tuple,ast.Dict,ast.Call)) or isinstance(v,ast.Constant) and isinstance(v.value,str)
171742
- for x in tree.body:
171743
- names=[]
171744
- if isinstance(x,ast.Assign):names=[y.id for y in x.targets if isinstance(y,ast.Name)]
171745
- if isinstance(x,ast.AnnAssign) and isinstance(x.target,ast.Name):names=[x.target.id]
171746
- if any(any(t in n.lower() for t in terms) for n in names) and (isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)) or candidate_value(x.value if isinstance(x,(ast.Assign,ast.AnnAssign)) else None)):candidates.append(x)
171747
- if len(candidates)>1: raise ValueError('ambiguous instruction-like symbols; provide a selector')
171748
- if candidates: chosen=candidates[0]
171749
- needs_imports=force or isinstance(chosen,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)) or (chosen is not None and any(isinstance(n,ast.Name) for n in ast.walk(chosen)))
171750
- module_scopes={os.path.realpath(path):tree}; queue=[(path,tree)] if needs_imports else []
171751
- while queue:
171752
- current,current_tree=queue.pop()
171753
- for item in current_tree.body:
171754
- candidate=imported_path(current,item)
171755
- if candidate and os.path.realpath(candidate) not in seen:
171756
- loaded=load(candidate)
171757
- if loaded[0] is not None:
171758
- module_scopes[os.path.realpath(candidate)]=loaded[0]; queue.append((candidate,loaded[0]))
171759
- template_seen={'value':False}
171760
- def value(node, scope, depth=0):
171761
- if depth>32:return (False,None,'static evaluation depth exceeded')
171762
- if isinstance(node,ast.Constant) and isinstance(node.value,(str,int,float,bool,type(None))):return True,node.value,''
171763
- if isinstance(node,ast.JoinedStr):return False,None,'f-string interpolation contains runtime expressions'
171764
- if isinstance(node,ast.BinOp) and isinstance(node.op,ast.Add):
171765
- a=value(node.left,scope,depth+1); b=value(node.right,scope,depth+1)
171766
- if a[0] and b[0] and isinstance(a[1],str) and isinstance(b[1],str):return True,a[1]+b[1],''
171767
- return False,None,'concatenation contains a dynamic or non-string value'
171768
- if isinstance(node,(ast.List,ast.Tuple)):
171769
- out=[]
171770
- for e in node.elts:
171771
- v=value(e,scope,depth+1)
171772
- if not v[0]:return v
171773
- out.append(v[1])
171774
- return True,out,''
171775
- if isinstance(node,ast.Dict):
171776
- out={}
171777
- for k,vn in zip(node.keys,node.values):
171778
- if k is None:return False,None,'dictionary unpacking is dynamic'
171779
- kv=value(k,scope,depth+1); vv=value(vn,scope,depth+1)
171780
- if not kv[0] or not vv[0]:return (False,None,kv[2] or vv[2])
171781
- if not isinstance(kv[1],str):return False,None,'non-string dictionary key'
171782
- if kv[1] in out:return False,None,'duplicate dictionary key'
171783
- out[kv[1]]=vv[1]
171784
- return True,out,''
171785
- if isinstance(node,ast.Name):
171786
- if node.id in mutated:return False,None,"binding '"+node.id+"' is reassigned or mutated"
171787
- if node.id in scope:return value(scope[node.id],scope,depth+1)
171788
- return False,None,"binding '"+node.id+"' was not found"
171789
- if isinstance(node,ast.Call) and isinstance(node.func,ast.Name) and node.func.id in trusted_template_names:
171790
- if len(node.args)==1 and not node.keywords:
171791
- literal_value=value(node.args[0],scope,depth+1)
171792
- if literal_value[0] and isinstance(literal_value[1],str):
171793
- template_seen['value']=True
171794
- return literal_value
171795
- template_keyword=[keyword.value for keyword in node.keywords if keyword.arg=='template']
171796
- if len(template_keyword)==1 and len(node.args)==0 and len(node.keywords)==1:
171797
- literal_value=value(template_keyword[0],scope,depth+1)
171798
- if literal_value[0] and isinstance(literal_value[1],str):
171799
- template_seen['value']=True
171800
- return literal_value
171801
- return False,None,'template constructor has unsupported or dynamic arguments'
171802
- if isinstance(node,ast.Call) and isinstance(node.func,ast.Attribute) and node.func.attr=='join' and len(node.args)==1:
171803
- sep=value(node.func.value,scope,depth+1); items=value(node.args[0],scope,depth+1)
171804
- if sep[0] and items[0] and isinstance(sep[1],str) and isinstance(items[1],list) and all(isinstance(x,str) for x in items[1]):return True,sep[1].join(items[1]),''
171805
- return False,None,'join contains a dynamic or non-string value'
171806
- return False,None,'unsupported Python expression '+type(node).__name__
171807
- if chosen is None: print(json.dumps(fail("symbol '"+(symbol or '<instruction-like candidate>')+"' was not found",text))); raise SystemExit
171808
- selected_range={'line':chosen.lineno,'endLine':getattr(chosen,'end_lineno',chosen.lineno)}
171809
- source=ast.get_source_segment(text,chosen) or text
171810
- if isinstance(chosen,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)):
171811
- result=fail('function/builder source requires runtime inputs',source)
171812
- if isinstance(chosen,ast.ClassDef): result['declarationKind']='class'
171813
- print(json.dumps(result)); raise SystemExit
171814
- scope={}
171815
- for x in tree.body:
171816
- if isinstance(x,ast.Assign):
171817
- for y in x.targets:
171818
- if isinstance(y,ast.Name):
171819
- if y.id in scope: raise ValueError("binding '"+y.id+"' is reassigned or mutated")
171820
- scope[y.id]=x.value
171821
- if isinstance(x,ast.AnnAssign) and isinstance(x.target,ast.Name):
171822
- if x.target.id in scope: raise ValueError("binding '"+x.target.id+"' is reassigned or mutated")
171823
- scope[x.target.id]=x.value
171824
- for item in tree.body:
171825
- candidate=imported_path(path,item)
171826
- imported_tree=module_scopes.get(os.path.realpath(candidate)) if candidate else None
171827
- if imported_tree is not None and isinstance(item,ast.ImportFrom):
171828
- imported_values={}
171829
- for statement in imported_tree.body:
171830
- if isinstance(statement,ast.Assign):
171831
- for target_name in statement.targets:
171832
- if isinstance(target_name,ast.Name):imported_values[target_name.id]=statement.value
171833
- if isinstance(statement,ast.AnnAssign) and isinstance(statement.target,ast.Name):imported_values[statement.target.id]=statement.value
171834
- for alias in item.names:
171835
- if alias.name in imported_values:scope[alias.asname or alias.name]=imported_values[alias.name]
171836
- if isinstance(item,ast.AugAssign) and isinstance(item.target,ast.Name):
171837
- raise ValueError("binding '"+item.target.id+"' is reassigned or mutated")
171838
- aliases={}; mutated=set(); mutable=set(); escaped=set(); methods={'append','clear','extend','insert','pop','remove','reverse','sort'}
171839
- def mutation_scan(node):
171840
- if isinstance(node,ast.Assign):
171841
- for target_name in node.targets:
171842
- if isinstance(target_name,ast.Name) and isinstance(node.value,ast.Name):aliases[target_name.id]=node.value.id
171843
- if isinstance(target_name,ast.Name) and isinstance(node.value,(ast.List,ast.Dict)):mutable.add(target_name.id)
171844
- if isinstance(target_name,ast.Subscript) and isinstance(target_name.value,ast.Name):mutated.add(target_name.value.id)
171845
- if isinstance(target_name,ast.Attribute) and isinstance(target_name.value,ast.Name):mutated.add(target_name.value.id+'.'+target_name.attr)
171846
- if isinstance(node,ast.AugAssign) and isinstance(node.target,ast.Name):mutated.add(node.target.id)
171847
- if isinstance(node,ast.AugAssign) and isinstance(node.target,ast.Subscript) and isinstance(node.target.value,ast.Name):mutated.add(node.target.value.id)
171848
- if isinstance(node,ast.AugAssign) and isinstance(node.target,ast.Attribute) and isinstance(node.target.value,ast.Name):mutated.add(node.target.value.id+'.'+node.target.attr)
171849
- if isinstance(node,ast.Delete) and any(isinstance(target_name,ast.Subscript) and isinstance(target_name.value,ast.Name) for target_name in node.targets):
171850
- for target_name in node.targets:
171851
- if isinstance(target_name,ast.Subscript) and isinstance(target_name.value,ast.Name):mutated.add(target_name.value.id)
171852
- if isinstance(node,ast.Call):
171853
- if isinstance(node.func,ast.Attribute) and node.func.attr in methods and isinstance(node.func.value,ast.Name):mutated.add(node.func.value.id)
171854
- for argument in node.args:
171855
- if isinstance(argument,ast.Name):escaped.add(argument.id)
171856
- for child in ast.iter_child_nodes(node):mutation_scan(child)
171857
- mutation_scan(tree)
171858
- for imported_tree in module_scopes.values():
171859
- if imported_tree is not tree: mutation_scan(imported_tree)
171860
- changed=True
171861
- while changed:
171862
- changed=False
171863
- for alias,original in aliases.items():
171864
- if original in mutable and alias not in mutable:mutable.add(alias);changed=True
171865
- for binding in escaped:
171866
- if binding in mutable:mutated.add(binding)
171867
- changed=True
171868
- while changed:
171869
- changed=False
171870
- for alias,original in aliases.items():
171871
- if alias in mutated and original not in mutated:mutated.add(original);changed=True
171872
- selected_name = symbol if isinstance(chosen,ast.expr) and symbol and '.' in symbol else chosen.name if isinstance(chosen,(ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)) else (chosen.targets[0].id if isinstance(chosen,ast.Assign) and chosen.targets and isinstance(chosen.targets[0],ast.Name) else chosen.target.id if isinstance(chosen,ast.AnnAssign) and isinstance(chosen.target,ast.Name) else None)
171873
- if selected_name in mutated:
171874
- raise ValueError("binding '"+str(selected_name)+"' is reassigned or mutated")
171875
- node=chosen.value if isinstance(chosen,(ast.Assign,ast.AnnAssign)) else chosen if isinstance(chosen,ast.expr) else None
171876
- if node is None: print(json.dumps(fail('selected symbol has no static initializer',source))); raise SystemExit
171877
- good,val,reason=value(node,scope)
171878
- print(json.dumps({'value':val,'sourceText':source,'range':selected_range,'dynamic':not good,'template':template_seen['value'],'unresolved':[] if good else [reason],'files':files},ensure_ascii=False))
171879
- except SystemExit: pass
171880
- except SyntaxError as e: print(json.dumps({'sourceText':'','dynamic':True,'error':'syntax error: '+str(e),'unresolved':['Python source could not be parsed'],'files':files}))
171881
- except Exception as e:
171882
- message=str(e)
171883
- if message.startswith('ambiguous '): print(json.dumps({'sourceText':text if 'text' in locals() else '', 'dynamic':True, 'error':message, 'unresolved':[message], 'files':files}))
171884
- else: print(json.dumps(fail(message,source if selected_range is not None and 'source' in locals() else '')))`;
171885
- function extractPythonSource(rootDir, sourcePath, symbol, _forceDependencyGraph = false) {
171886
- let raw = "";
171887
- try {
171888
- raw = readFileSync2(sourcePath, "utf8");
171889
- } catch {}
171890
- const result = spawnSync("python3", ["-I", "-c", HELPER], {
171891
- input: JSON.stringify({ root: rootDir, path: sourcePath, symbol, force: _forceDependencyGraph }),
171892
- encoding: "utf8",
171893
- maxBuffer: 4 * 1024 * 1024,
171894
- timeout: 1e4
171895
- });
171896
- if (result.error || result.status !== 0)
171897
- throw new Error(`python3 unavailable: ${result.error?.message ?? result.stderr ?? "helper failed"}`);
171898
- let parsed;
171899
- try {
171900
- parsed = JSON.parse(result.stdout.trim());
171901
- } catch {
171902
- return { sourceText: raw, dynamic: true, unresolved: ["Python AST helper returned invalid output"], files: [sourcePath] };
171903
- }
171904
- if (parsed.error)
171905
- throw new Error(`${sourcePath}: ${parsed.error}`);
171906
- if (symbol && parsed.dynamic && parsed.unresolved.some((reason) => reason.includes("was not found")))
171907
- throw new Error(`${sourcePath}: symbol '${symbol}' was not found`);
171908
- return { ...parsed, sourceText: parsed.sourceText || raw, files: parsed.files?.length ? parsed.files : [sourcePath] };
171909
- }
171910
-
171911
- // src/prompt-sources.ts
171912
- var MAX_BYTES2 = 2 * 1024 * 1024;
171913
- var CODE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"]);
171914
- var TEMPLATE_EXTENSIONS = new Set([".jinja", ".j2", ".jinja2", ".mustache", ".hbs", ".handlebars"]);
171915
- function resolvePromptSource(rootDir, source) {
171916
- validatePromptSource(source);
171917
- const root = safeRoot(rootDir);
171918
- const sourceAbs = safePath(root, source.path);
171919
- const sourcePath = relative(root, sourceAbs).replaceAll("\\", "/");
171920
- if (selectorNamesAreSensitive(source.selector)) {
171921
- throw new Error(`${sourcePath}: selected prompt field name is sensitive credential material and was rejected`);
171922
- }
171923
- const ext = extname2(sourceAbs).toLowerCase();
171924
- let kind = source.kind ?? (TEMPLATE_EXTENSIONS.has(ext) ? "template" : "text");
171925
- let content = "";
171926
- let systemPrompt;
171927
- let messages;
171928
- let unresolved = [];
171929
- let complete = true;
171930
- const files = new Set([sourceAbs]);
171931
- const hasPointer = typeof source.selector?.pointer === "string";
171932
- if (CODE_EXTENSIONS.has(ext) && hasPointer) {
171933
- throw new Error(`${sourcePath}: pointer selectors require a JSON or YAML source`);
171934
- }
171935
- if (hasPointer && ![".json", ".yaml", ".yml"].includes(ext)) {
171936
- throw new Error(`${sourcePath}: pointer selectors require a JSON or YAML source`);
171937
- }
171938
- if (CODE_EXTENSIONS.has(ext) && !hasPointer) {
171939
- const extraction = ext === ".py" ? extractPythonSource(root, sourceAbs, source.selector?.symbol, source.kind === "builder") : extractCodeSource(root, sourceAbs, source.selector?.symbol, source.kind === "builder");
171940
- extraction.files.forEach((file) => files.add(safePath(root, file)));
171941
- unresolved = extraction.unresolved;
171942
- if (source.kind === "builder" || extraction.dynamic) {
171943
- kind = "builder";
171944
- content = extraction.sourceText;
171945
- complete = false;
171946
- } else if (typeof extraction.value === "string") {
171947
- content = extraction.value;
171948
- kind = source.kind === "template" || "template" in extraction && extraction.template === true || ext === ".py" && hasPythonFormatFields(extraction.value) ? "template" : "text";
171949
- } else if (Array.isArray(extraction.value)) {
171950
- if (isMessageArray(extraction.value)) {
171951
- messages = extraction.value;
171952
- content = JSON.stringify(messages);
171953
- kind = source.kind === "template" ? "template" : "messages";
171954
- } else {
171955
- kind = "builder";
171956
- content = extraction.sourceText;
171957
- complete = false;
171958
- unresolved = [...unresolved, "static array is not a role/content message array"];
171959
- }
171960
- } else if (extraction.value && typeof extraction.value === "object") {
171961
- const object = extraction.value;
171962
- if (!["content", "template", "systemPrompt", "system_prompt", "messages", "prompt"].some((field) => (field in object))) {
171963
- kind = "builder";
171964
- content = extraction.sourceText;
171965
- complete = false;
171966
- unresolved = [...unresolved, "static object is not a rendered prompt; retained selected source for semantic review"];
171967
- } else {
171968
- const structured = fromStructured(extraction.value, source.kind);
171969
- content = structured.content;
171970
- systemPrompt = structured.systemPrompt;
171971
- messages = structured.messages;
171972
- kind = structured.kind;
171973
- }
171974
- } else {
171975
- kind = "builder";
171976
- content = extraction.sourceText;
171977
- complete = false;
171978
- }
171979
- } else if (source.selector?.symbol) {
171980
- throw new Error(`${sourcePath}: symbol selectors require a JavaScript, TypeScript, or Python source file`);
171981
- } else if (ext === ".json" || ext === ".yaml" || ext === ".yml" || hasPointer || source.kind === "messages") {
171982
- const parsed = parseStructured(readFileSync3(sourceAbs, "utf8"), sourcePath, ext);
171983
- const selected = hasPointer ? jsonPointer(parsed, source.selector.pointer, sourcePath) : parsed;
171984
- const selectedField = source.selector?.pointer?.split("/").at(-1)?.replaceAll("~1", "/").replaceAll("~0", "~");
171985
- const inferredKind = !source.kind && selectedField && /^(?:template|system_template|instance_template)$/.test(selectedField) ? "template" : source.kind;
171986
- const structured = fromStructured(selected, inferredKind);
171987
- content = structured.content;
171988
- systemPrompt = structured.systemPrompt;
171989
- messages = structured.messages;
171990
- kind = structured.kind;
171991
- } else {
171992
- const raw = readFileSync3(sourceAbs, "utf8");
171993
- if (ext === ".md" && /\.prompt\.md$/i.test(sourcePath)) {
171994
- const legacy = parseLegacyMarkdown(raw);
171995
- content = legacy.content;
171996
- systemPrompt = legacy.systemPrompt;
171997
- } else
171998
- content = raw;
171999
- kind = source.kind === "builder" ? "builder" : source.kind === "template" ? "template" : kind;
172000
- if (kind === "builder")
172001
- complete = false;
172002
- }
172003
- if (kind === "text" && !source.kind && [".json", ".yaml", ".yml"].includes(ext) && /\{\{[\s\S]+?\}\}|\{%[\s\S]+?%\}/.test(content))
172004
- kind = "template";
172005
- if (kind === "builder")
172006
- complete = false;
172007
- if (kind === "builder" && !unresolved.some((reason) => reason.includes("dependency graph is not exhaustive"))) {
172008
- unresolved.push("dynamic dependency graph is not exhaustive; source hashes cover only resolved local files");
172009
- }
172010
- if (source.dependencies) {
172011
- for (const dependency of source.dependencies) {
172012
- const depAbs = safePath(root, dependency.path);
172013
- files.add(depAbs);
172014
- if (typeof dependency.selector?.pointer === "string") {
172015
- const parsed = parseStructured(readFileSync3(depAbs, "utf8"), dependency.path, extname2(depAbs).toLowerCase());
172016
- jsonPointer(parsed, dependency.selector.pointer, dependency.path);
172017
- } else if (dependency.selector?.symbol && CODE_EXTENSIONS.has(extname2(depAbs).toLowerCase())) {
172018
- const dep = extname2(depAbs).toLowerCase() === ".py" ? extractPythonSource(root, depAbs, dependency.selector.symbol) : extractCodeSource(root, depAbs, dependency.selector.symbol);
172019
- dep.files.forEach((file) => files.add(safePath(root, file)));
172020
- if (dep.dynamic) {
172021
- complete = false;
172022
- unresolved.push(...dep.unresolved);
172023
- }
172024
- }
172025
- }
172026
- }
172027
- if (!content && kind !== "builder")
172028
- throw new Error(`${sourcePath}: selected prompt source is empty`);
172029
- if (source.kind && source.kind !== kind) {
172030
- throw new Error(`${sourcePath}: declared source kind '${source.kind}' does not match resolved kind '${kind}'`);
172031
- }
172032
- const materialized = [content, systemPrompt ?? "", messages ? JSON.stringify(messages) : ""].join(`
172033
- `);
172034
- if (kind === "builder")
172035
- assertSafeMaterializedContent(content, sourcePath);
172036
- else
172037
- assertSafeMaterializedContent(materialized, sourcePath);
172038
- const dependencies = [...files].sort().map((path) => ({ path: relative(root, path).replaceAll("\\", "/"), contentHash: sha256(readFileSync3(path)) }));
172039
- const sourceHash = sha256(JSON.stringify({ kind, content, systemPrompt: systemPrompt ?? null, messages: messages ?? null }));
172040
- const sourceDefinition = {
172041
- schemaVersion: 1,
172042
- kind,
172043
- source,
172044
- dependencies,
172045
- coverage: complete && unresolved.length === 0 ? "complete" : "partial",
172046
- unresolved: [...new Set(unresolved)],
172047
- sourceHash
172048
- };
172049
- return { key: source.key, name: source.key, sourcePath, content, systemPrompt, messages, sourceDefinition };
172050
- }
172051
- function assertSafeMaterializedContent(value, sourcePath) {
172052
- if (redactString(value) !== value || /-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(value)) {
172053
- throw new Error(`${sourcePath}: selected prompt material looks like a credential and was rejected`);
172054
- }
172055
- }
172056
- function selectorNamesAreSensitive(selector) {
172057
- if (!selector)
172058
- return false;
172059
- const names = selector.symbol ? selector.symbol.split(".") : (selector.pointer ?? "").split("/").slice(1).map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"));
172060
- return names.some((name) => {
172061
- const normalized = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]/g, "_").toLowerCase();
172062
- return SECRET_KEYS.has(normalized) || normalized === "auth" || normalized === "credentials";
172063
- });
172064
- }
172065
- function validatePromptSource(value) {
172066
- if (!value || typeof value !== "object")
172067
- throw new Error("Prompt source must be an object");
172068
- const source = value;
172069
- if (Object.keys(source).some((key) => !["key", "path", "selector", "kind", "dependencies"].includes(key)))
172070
- throw new Error("Prompt source contains an unknown field");
172071
- if (typeof source.key !== "string" || !source.key.trim())
172072
- throw new Error("Prompt source key must be a non-empty string");
172073
- if (typeof source.path !== "string" || !source.path.trim() || source.path.includes("\x00") || isAbsolute(source.path) || source.path.split(/[\\/]/).includes(".."))
172074
- throw new Error(`${source.key}: source path must be a repository-relative safe string`);
172075
- if (source.kind !== undefined && !["text", "messages", "template", "builder"].includes(source.kind))
172076
- throw new Error(`${source.key}: unsupported source kind`);
172077
- if (source.selector && typeof source.selector === "object" && Object.keys(source.selector).some((key) => !["symbol", "pointer"].includes(key)))
172078
- throw new Error(`${source.key}: selector contains an unknown field`);
172079
- if (source.selector && (source.selector.symbol !== undefined && typeof source.selector.symbol !== "string" || source.selector.pointer !== undefined && typeof source.selector.pointer !== "string"))
172080
- throw new Error(`${source.key}: selector fields must be strings`);
172081
- if (source.selector) {
172082
- const hasSymbol = typeof source.selector.symbol === "string" && source.selector.symbol.trim().length > 0;
172083
- const hasPointer = typeof source.selector.pointer === "string";
172084
- if (hasSymbol === hasPointer)
172085
- throw new Error(`${source.key}: selector must contain exactly one of symbol or pointer`);
172086
- }
172087
- if (source.dependencies && (!Array.isArray(source.dependencies) || source.dependencies.some((dep) => {
172088
- if (!dep || typeof dep !== "object" || Object.keys(dep).some((key) => !["path", "selector"].includes(key)) || typeof dep.path !== "string" || !dep.path.trim() || isAbsolute(dep.path) || dep.path.split(/[\\/]/).includes(".."))
172089
- return true;
172090
- const selector = dep.selector;
172091
- if (!selector)
172092
- return false;
172093
- if (typeof selector !== "object" || Object.keys(selector).some((key) => !["symbol", "pointer"].includes(key)))
172094
- return true;
172095
- const hasSymbol = typeof selector.symbol === "string" && selector.symbol.trim().length > 0;
172096
- const hasPointer = typeof selector.pointer === "string";
172097
- return selector.symbol !== undefined && typeof selector.symbol !== "string" || selector.pointer !== undefined && typeof selector.pointer !== "string" || hasSymbol === hasPointer;
172098
- })))
172099
- throw new Error(`${source.key}: dependencies must contain safe paths and unambiguous selectors`);
172100
- return source;
172101
- }
172102
- function discoverStructuredPromptSources(rootDir, sourcePath) {
172103
- const root = safeRoot(rootDir);
172104
- const file = safePath(root, sourcePath);
172105
- const ext = extname2(file).toLowerCase();
172106
- if (![".json", ".yaml", ".yml"].includes(ext))
172107
- return [];
172108
- const value = parseStructured(readFileSync3(file, "utf8"), sourcePath, ext);
172109
- const sources = [];
172110
- const base = sourcePath.replace(/\.[^.]+$/, "").replace(/[^A-Za-z0-9_.-]+/g, ".");
172111
- const dedicatedPath = /(^|\/)prompts?(?:\/|\.[^/]+$)|\.prompt\.[^.]+$/i.test(sourcePath);
172112
- const modelKeys = /^(?:model|llm|model_name|modelName|model_id|modelId|model_config)$/i;
172113
- const hasModelContext = (objects) => objects.some((object) => Object.keys(object).some((key) => modelKeys.test(key)) || object.client_config && typeof object.client_config === "object" && Object.keys(object.client_config).some((key) => modelKeys.test(key)));
172114
- const hasToolAgentContext = (parts, objects) => parts.includes("agent") && objects.some((object) => {
172115
- const templates = object.templates;
172116
- return templates && typeof templates === "object" && !Array.isArray(templates) && typeof templates.system_template === "string" && object.tools && typeof object.tools === "object" && !Array.isArray(object.tools);
172117
- });
172118
- let visited = 0;
172119
- const walk = (node, parts, depth, ancestors) => {
172120
- if (++visited > 1e4 || depth > 32)
172121
- throw new Error("Structured prompt discovery exceeded node or depth bound");
172122
- if (!node || typeof node !== "object")
172123
- return;
172124
- const object = node;
172125
- const contextObjects = [...ancestors, object];
172126
- const nestedPromptContext = parts.some((part) => /^(?:agent|agents|prompt|prompts|template|templates|llm|model)$/i.test(part));
172127
- const contextual = dedicatedPath || hasModelContext([object]) || nestedPromptContext && hasModelContext(ancestors) || hasToolAgentContext(parts, contextObjects);
172128
- for (const [name, child] of Object.entries(node)) {
172129
- const path = [...parts, name];
172130
- const promptText = /^(?:system|system_prompt|systemPrompt|system_message|system_template|instance_template|instructions|prompt|backstory|template)$/.test(name) && typeof child === "string" && child.trim();
172131
- const promptMessages = name === "messages" && Array.isArray(child) && isMessageArray(child) && child.some((item) => ["system", "developer"].includes(String(item.role)));
172132
- if (promptText || promptMessages) {
172133
- const crewBackstory = name === "backstory" && "role" in object && "goal" in object;
172134
- if (!contextual && !crewBackstory)
172135
- continue;
172136
- if (name === "system" && !(("model" in object) || ("tools" in object)) && !contextual)
172137
- continue;
172138
- const pointer = "/" + path.map((part) => part.replaceAll("~", "~0").replaceAll("/", "~1")).join("/");
172139
- sources.push({ key: `${base}.${path.map((part) => part.replace(/[^A-Za-z0-9_-]/g, "_")).join(".")}`, path: sourcePath, selector: { pointer }, .../template$/.test(name) ? { kind: "template" } : {} });
172140
- } else if (child && typeof child === "object")
172141
- walk(child, path, depth + 1, contextObjects);
172142
- }
172143
- };
172144
- walk(value, [], 0, []);
172145
- return sources;
172146
- }
172147
- function backfillPromptSource(rootDir, artifact) {
172148
- if (artifact.promptSource)
172149
- return artifact.promptSource;
172150
- if (!artifact.sourcePath)
172151
- return;
172152
- if (/\.(?:[cm]?[jt]sx?|py)$/.test(artifact.sourcePath)) {
172153
- const candidates = discoverCodePromptSources(rootDir, artifact.sourcePath);
172154
- const name = (artifact.name ?? "").replace(/[^a-z0-9]/gi, "").toLowerCase();
172155
- const matching = candidates.filter((source) => source.selector?.symbol?.replace(/[^a-z0-9]/gi, "").toLowerCase() === name);
172156
- return matching.length === 1 ? matching[0] : undefined;
172157
- }
172158
- return { key: artifact.id, path: artifact.sourcePath };
172159
- }
172160
- function discoverCodePromptSources(rootDir, sourcePath) {
172161
- return discoverCodePromptCandidates(rootDir, sourcePath).map((candidate) => candidate.source);
172162
- }
172163
- function discoverCodePromptCandidates(rootDir, sourcePath) {
172164
- const root = safeRoot(rootDir);
172165
- const file = safePath(root, sourcePath);
172166
- const ext = extname2(file).toLowerCase();
172167
- if (!CODE_EXTENSIONS.has(ext))
172168
- return [];
172169
- const rel = relative(root, file).replaceAll("\\", "/");
172170
- const base = rel.replace(/\.[^.]+$/, "").replace(/[^A-Za-z0-9_.-]+/g, ".").replace(/[.-]+/g, ".");
172171
- const names = new Set;
172172
- const modelInputs = new Set;
172173
- const knownBuilders = new Set;
172174
- const addName = (name, knownBuilder = false) => {
172175
- names.add(name);
172176
- if (knownBuilder)
172177
- knownBuilders.add(name);
172178
- };
172179
- if (ext === ".py") {
172180
- for (const candidate of discoverPythonPromptSources(root, file)) {
172181
- addName(candidate.symbol, candidate.knownBuilder === true);
172182
- modelInputs.add(candidate.symbol);
172183
- }
172184
- for (const match of readFileSync3(file, "utf8").matchAll(/^(?:(?:async\s+)?def\s+([A-Za-z_]\w*)|([A-Za-z_]\w*)\s*=\s*([^\n#]+))/gm)) {
172185
- const name = match[1] ?? match[2];
172186
- const rhs = match[3] ?? "";
172187
- if (name && isPromptName2(name) && (match[1] || /['"`\[({+]/.test(rhs) || /^[A-Za-z_]\w*\s*$/.test(rhs)))
172188
- addName(name, Boolean(match[1]));
172189
- }
172190
- } else {
172191
- const sf = import_typescript2.default.createSourceFile(file, readFileSync3(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true, import_typescript2.default.ScriptKind.TSX);
172192
- const agentConstructors = new Set;
172193
- for (const statement of sf.statements) {
172194
- if (!import_typescript2.default.isImportDeclaration(statement) || !import_typescript2.default.isStringLiteral(statement.moduleSpecifier) || !TYPESCRIPT_AGENT_MODULES.includes(statement.moduleSpecifier.text))
172195
- continue;
172196
- const bindings = statement.importClause?.namedBindings;
172197
- if (bindings && import_typescript2.default.isNamedImports(bindings)) {
172198
- for (const binding of bindings.elements) {
172199
- if ((binding.propertyName ?? binding.name).text === "Agent")
172200
- agentConstructors.add(binding.name.text);
172201
- }
172202
- }
172203
- }
172204
- for (const statement of sf.statements) {
172205
- if (import_typescript2.default.isVariableStatement(statement)) {
172206
- for (const node of statement.declarationList.declarations) {
172207
- if (import_typescript2.default.isIdentifier(node.name) && isPromptName2(node.name.text) && isCandidateInitializer2(node.initializer)) {
172208
- addName(node.name.text, Boolean(node.initializer && (import_typescript2.default.isFunctionExpression(node.initializer) || import_typescript2.default.isArrowFunction(node.initializer))));
172209
- }
172210
- if (import_typescript2.default.isIdentifier(node.name) && node.initializer) {
172211
- const initializer = node.initializer;
172212
- const objects = import_typescript2.default.isObjectLiteralExpression(initializer) && isPromptName2(node.name.text) ? [initializer] : (import_typescript2.default.isCallExpression(initializer) || import_typescript2.default.isNewExpression(initializer)) && import_typescript2.default.isIdentifier(initializer.expression) && agentConstructors.has(initializer.expression.text) ? [...initializer.arguments ?? []].filter(import_typescript2.default.isObjectLiteralExpression) : [];
172213
- for (const object of objects) {
172214
- for (const property of object.properties) {
172215
- if (!import_typescript2.default.isPropertyAssignment(property) || !import_typescript2.default.isIdentifier(property.name))
172216
- continue;
172217
- if (!/^(?:instructions|systemPrompt|system_prompt|prompt|messages)$/.test(property.name.text))
172218
- continue;
172219
- if (isCandidateInitializer2(property.initializer) || import_typescript2.default.isIdentifier(property.initializer))
172220
- addName(`${node.name.text}.${property.name.text}`);
172221
- }
172222
- }
172223
- }
172224
- }
172225
- } else if (import_typescript2.default.isFunctionDeclaration(statement) && statement.name && isPromptName2(statement.name.text)) {
172226
- addName(statement.name.text, true);
172227
- } else if (import_typescript2.default.isClassDeclaration(statement) && statement.name) {
172228
- for (const member of statement.members) {
172229
- if (import_typescript2.default.isMethodDeclaration(member) && member.name && import_typescript2.default.isIdentifier(member.name) && isPromptName2(member.name.text)) {
172230
- addName(`${statement.name.text}.${member.name.text}`, true);
172231
- }
172232
- }
172233
- }
172234
- }
172235
- const visitModelCall = (node) => {
172236
- if (import_typescript2.default.isCallExpression(node)) {
172237
- const callee = node.expression.getText(sf).replace(/\s/g, "");
172238
- if (/(?:^|\.)(?:generateText|streamText)$|\.(?:messages|chat\.completions|responses)\.create$/.test(callee)) {
172239
- for (const argument of node.arguments) {
172240
- if (!import_typescript2.default.isObjectLiteralExpression(argument))
172241
- continue;
172242
- for (const property of argument.properties) {
172243
- if (!import_typescript2.default.isPropertyAssignment(property) || !import_typescript2.default.isIdentifier(property.name))
172244
- continue;
172245
- if (/^(?:system|instructions|systemPrompt|system_prompt)$/.test(property.name.text) && import_typescript2.default.isIdentifier(property.initializer)) {
172246
- addName(property.initializer.text, knownBuilders.has(property.initializer.text));
172247
- modelInputs.add(property.initializer.text);
172248
- }
172249
- }
172250
- }
172251
- }
172252
- }
172253
- import_typescript2.default.forEachChild(node, visitModelCall);
172254
- };
172255
- visitModelCall(sf);
172256
- }
172257
- return [...names].sort().map((name) => ({ source: { key: `${base}.${name}`, path: rel, selector: { symbol: name } }, modelInput: modelInputs.has(name), ...knownBuilders.has(name) ? { knownBuilder: true } : {} }));
172258
- }
172259
- function isPromptName2(name) {
172260
- return /prompt|instruction|system|message|template/i.test(name);
172261
- }
172262
- function isCandidateInitializer2(initializer) {
172263
- if (!initializer)
172264
- return false;
172265
- return import_typescript2.default.isStringLiteral(initializer) || import_typescript2.default.isNoSubstitutionTemplateLiteral(initializer) || import_typescript2.default.isTemplateExpression(initializer) || import_typescript2.default.isBinaryExpression(initializer) && initializer.operatorToken.kind === import_typescript2.default.SyntaxKind.PlusToken || import_typescript2.default.isFunctionExpression(initializer) || import_typescript2.default.isArrowFunction(initializer) || import_typescript2.default.isIdentifier(initializer) || import_typescript2.default.isCallExpression(initializer) && import_typescript2.default.isPropertyAccessExpression(initializer.expression) && initializer.expression.name.text === "join" || import_typescript2.default.isArrayLiteralExpression(initializer) && initializer.elements.some((item) => import_typescript2.default.isObjectLiteralExpression(item) || import_typescript2.default.isStringLiteral(item));
172266
- }
172267
- function fromStructured(value, requestedKind) {
172268
- if (typeof value === "string") {
172269
- if (requestedKind === "messages")
172270
- throw new Error("A messages source must resolve to a role/content array");
172271
- return { kind: requestedKind === "template" || requestedKind === "builder" ? requestedKind : "text", content: value };
172272
- }
172273
- if (Array.isArray(value)) {
172274
- if (requestedKind === "text")
172275
- throw new Error("A text source cannot resolve to a message array");
172276
- if (!isMessageArray(value))
172277
- throw new Error("Structured prompt array must contain role/content messages");
172278
- return { kind: requestedKind === "template" || requestedKind === "builder" ? requestedKind : "messages", content: JSON.stringify(value), messages: value };
172279
- }
172280
- if (!value || typeof value !== "object")
172281
- throw new Error("Selected structured prompt value must be text, object, or message array");
172282
- const object = value;
172283
- const hasMessages = object.messages !== undefined;
172284
- if (hasMessages && (!Array.isArray(object.messages) || !isMessageArray(object.messages))) {
172285
- throw new Error("Structured prompt messages must contain role/content messages");
172286
- }
172287
- const objectMessages = hasMessages ? object.messages : undefined;
172288
- if (requestedKind === "messages" && !objectMessages)
172289
- throw new Error("A messages source must resolve to a role/content array");
172290
- const standaloneSystemPrompt = typeof object.systemPrompt === "string" && object.systemPrompt.trim() ? object.systemPrompt : typeof object.system_prompt === "string" && object.system_prompt.trim() ? object.system_prompt : undefined;
172291
- const replayKind = requestedKind === "template" || requestedKind === "builder" ? requestedKind : undefined;
172292
- const contentKind = replayKind ?? (requestedKind === "text" ? "text" : objectMessages ? "messages" : "text");
172293
- if (typeof object.content === "string" && object.content.trim())
172294
- return { kind: contentKind, content: object.content, systemPrompt: standaloneSystemPrompt, messages: objectMessages };
172295
- if (standaloneSystemPrompt)
172296
- return { kind: contentKind, content: standaloneSystemPrompt, systemPrompt: standaloneSystemPrompt, messages: objectMessages };
172297
- if (objectMessages)
172298
- return { kind: replayKind ?? "messages", content: JSON.stringify(objectMessages), messages: objectMessages, systemPrompt: standaloneSystemPrompt };
172299
- if (typeof object.prompt === "string" && object.prompt.trim())
172300
- return { kind: replayKind ?? "text", content: object.prompt, systemPrompt: standaloneSystemPrompt };
172301
- if (typeof object.template === "string") {
172302
- if (requestedKind === "text" || requestedKind === "messages")
172303
- throw new Error(`A ${requestedKind} source cannot resolve from a template field`);
172304
- return { kind: requestedKind === "builder" ? "builder" : "template", content: object.template };
172305
- }
172306
- if (requestedKind === "messages")
172307
- throw new Error("A messages source must resolve to a role/content array");
172308
- throw new Error("Structured prompt object requires content, template, systemPrompt, messages, or prompt");
172309
- }
172310
- function isMessageArray(value) {
172311
- return value.length > 0 && value.every((item) => {
172312
- if (!item || typeof item !== "object")
172313
- return false;
172314
- const message = item;
172315
- return typeof message.role === "string" && ["system", "developer", "user", "assistant", "tool"].includes(message.role) && "content" in message;
172316
- });
172317
- }
172318
- function hasPythonFormatFields(value) {
172319
- return /(?<!\{)\{[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)?(?:![rsa])?(?::[^{}]*)?\}(?!\})/.test(value);
172320
- }
172321
- function parseLegacyMarkdown(raw) {
172322
- if (!raw.startsWith("---"))
172323
- return { content: raw.trim() };
172324
- const end = raw.indexOf(`
172325
- ---`, 3);
172326
- if (end < 0)
172327
- return { content: raw.trim() };
172328
- const frontmatter = {};
172329
- for (const line of raw.slice(3, end).trim().split(`
172330
- `)) {
172331
- const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
172332
- if (match)
172333
- frontmatter[match[1]] = match[2].replace(/^["']|["']$/g, "");
172334
- }
172335
- return { content: raw.slice(end + 4).trim(), systemPrompt: frontmatter.system_prompt || frontmatter.systemPrompt };
172336
- }
172337
- function parseStructured(raw, path, ext) {
172338
- try {
172339
- if (ext === ".json") {
172340
- JSON.parse(raw);
172341
- const document2 = $parseDocument(raw, { uniqueKeys: true, json: true });
172342
- if (document2.errors.length)
172343
- throw document2.errors[0];
172344
- return document2.toJS();
172345
- }
172346
- const document = $parseDocument(raw, { uniqueKeys: true, logLevel: "silent" });
172347
- if (document.errors.length || document.warnings.length)
172348
- throw document.errors[0] ?? document.warnings[0];
172349
- return document.toJS({ maxAliasCount: 100 });
172350
- } catch (error) {
172351
- throw new Error(`${path}: malformed structured source (${error instanceof Error ? error.message : String(error)})`);
172352
- }
172353
- }
172354
- function jsonPointer(value, pointer, path) {
172355
- if (pointer === "")
172356
- return value;
172357
- if (!pointer.startsWith("/"))
172358
- throw new Error(`${path}: JSON Pointer must start with '/'`);
172359
- let current = value;
172360
- for (const token of pointer.slice(1).split("/").map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"))) {
172361
- if (Array.isArray(current) && /^0$|^[1-9]\d*$/.test(token))
172362
- current = current[Number(token)];
172363
- else if (current && typeof current === "object" && token in current)
172364
- current = current[token];
172365
- else
172366
- throw new Error(`${path}: JSON Pointer '${pointer}' did not resolve`);
172367
- if (current === undefined)
172368
- throw new Error(`${path}: JSON Pointer '${pointer}' did not resolve`);
172369
- }
172370
- return current;
172371
- }
172372
- function safeRoot(dir) {
172373
- const root = realpathSync3(resolve3(dir));
172374
- if (!lstatSync2(root).isDirectory())
172375
- throw new Error(`Prompt source root is not a directory: ${dir}`);
172376
- return root;
172377
- }
172378
- function safePath(root, requested) {
172379
- const candidate = resolve3(root, requested);
172380
- rejectSymlinkPath(root, candidate, requested);
172381
- const real = realpathSync3(candidate);
172382
- if (!(real === root || real.startsWith(`${root}/`)))
172383
- throw new Error(`Prompt source escapes repository root: ${requested}`);
172384
- const rel = relative(root, real).replaceAll("\\", "/");
172385
- if (isSensitiveSourcePath(rel))
172386
- throw new Error(`Prompt source path is excluded as sensitive: ${requested}`);
172387
- const stat = lstatSync2(real);
172388
- if (!stat.isFile() || stat.size > MAX_BYTES2)
172389
- throw new Error(`Prompt source is not a bounded regular file: ${requested}`);
172390
- return real;
172391
- }
172392
- function isSensitiveSourcePath(path) {
172393
- const normalized = path.toLowerCase();
172394
- const name = normalized.split("/").at(-1) ?? normalized;
172395
- if (/^\.env\.(?:example|sample|template|defaults)$/.test(name))
172396
- return false;
172397
- if (name === ".env" || name.startsWith(".env."))
172398
- return true;
172399
- if (normalized.split("/").some((part) => part === "secrets" || part === "credentials"))
172400
- return true;
172401
- return /(?:secrets?|credentials?|private[-_]?keys?|passwords?|passwd|api[-_]?keys?|access[-_]?tokens?|auth[-_]?tokens?|tokens?)\b/.test(name) || /\.(?:pem|key|p12|pfx)$/i.test(name);
172402
- }
172403
- function rejectSymlinkPath(root, candidate, requested) {
172404
- let cursor = candidate;
172405
- while (cursor !== root) {
172406
- let stat;
172407
- try {
172408
- stat = lstatSync2(cursor);
172409
- } catch {
172410
- break;
172411
- }
172412
- if (stat.isSymbolicLink())
172413
- throw new Error(`Prompt source uses a symlink: ${requested}`);
172414
- const next = cursor.slice(0, cursor.lastIndexOf("/")) || "/";
172415
- if (next === cursor)
172416
- break;
172417
- cursor = next;
172418
- }
172419
- }
172420
- function sha256(input) {
172421
- return createHash("sha256").update(input).digest("hex");
172422
- }
172423
-
172424
- export { require_typescript, PYTHON_AGENT_MODULES, TYPESCRIPT_AGENT_MODULES, extractCodeSource, inspectCodeSelectors, inspectPythonSelectors, extractPythonSource, resolvePromptSource, validatePromptSource, discoverStructuredPromptSources, backfillPromptSource, discoverCodePromptSources, discoverCodePromptCandidates };
170909
+ export { require_typescript };