@botpress/adk-cli
Version:
Command-line interface for the Botpress Agent Development Kit (ADK)
1,800 lines • 58.2 kB
JavaScript
// @bun
import {
createCliLogger
} from "./chunk-gzwt1qdr.js";
import {
Uk
} from "./chunk-3xrpxgq4.js";
// src/server/handlers/agent-map/deployed-snapshot.ts
import { createHash } from "crypto";
// src/server/handlers/agent-map/ast/locations.ts
import ts2 from "typescript";
// src/server/handlers/agent-map/ast/source-cache.ts
import ts from "typescript";
import { readFileSync } from "fs";
import { resolve } from "path";
var cache = new Map;
function getCachedSourceFile(agentRoot, relativePath) {
const absPath = resolve(agentRoot, relativePath);
const cached = cache.get(absPath);
if (cached !== undefined)
return cached;
let source;
try {
source = readFileSync(absPath, "utf-8");
} catch {
cache.set(absPath, null);
return null;
}
const sourceFile = ts.createSourceFile(absPath, source, ts.ScriptTarget.Latest, true);
cache.set(absPath, sourceFile);
return sourceFile;
}
function clearSourceFileCache() {
cache.clear();
}
// src/server/handlers/agent-map/ast/locations.ts
function getExportLocation(opts) {
const sourceFile = getCachedSourceFile(opts.agentRoot, opts.relativePath);
if (!sourceFile)
return null;
const found = findExportNode(sourceFile, opts.exportName);
if (!found)
return null;
const start = sourceFile.getLineAndCharacterOfPosition(found.getStart());
const end = sourceFile.getLineAndCharacterOfPosition(found.getEnd());
return {
path: opts.relativePath,
startLine: start.line + 1,
endLine: end.line + 1
};
}
function findExportNode(sourceFile, exportName) {
let found;
ts2.forEachChild(sourceFile, (node) => {
if (found)
return;
if (exportName === "default" && ts2.isExportAssignment(node)) {
found = node;
return;
}
if (!hasExportModifier(node))
return;
if (ts2.isVariableStatement(node)) {
for (const decl of node.declarationList.declarations) {
if (ts2.isIdentifier(decl.name) && decl.name.text === exportName) {
found = node;
return;
}
}
return;
}
if (ts2.isFunctionDeclaration(node) && node.name?.text === exportName) {
found = node;
return;
}
if (ts2.isClassDeclaration(node) && node.name?.text === exportName) {
found = node;
return;
}
if (exportName === "default" && hasDefaultModifier(node) && (ts2.isFunctionDeclaration(node) || ts2.isClassDeclaration(node))) {
found = node;
return;
}
});
return found;
}
function hasExportModifier(node) {
if (!ts2.canHaveModifiers(node))
return false;
const modifiers = ts2.getModifiers(node);
return !!modifiers?.some((m) => m.kind === ts2.SyntaxKind.ExportKeyword);
}
function hasDefaultModifier(node) {
if (!ts2.canHaveModifiers(node))
return false;
const modifiers = ts2.getModifiers(node);
return !!modifiers?.some((m) => m.kind === ts2.SyntaxKind.DefaultKeyword);
}
function fallbackLocation(relativePath) {
return { path: relativePath, startLine: 1, endLine: 1 };
}
// src/server/handlers/agent-map/ast/compiled-bot.ts
import ts3 from "typescript";
import { readFileSync as readFileSync2 } from "fs";
import { join } from "path";
function parseCompiledBotIntegrations(agentRoot) {
const result = new Map;
const botDefPath = join(agentRoot, ".adk", "bot", "bot.definition.ts");
let source;
try {
source = readFileSync2(botDefPath, "utf-8");
} catch {
return result;
}
const sourceFile = ts3.createSourceFile(botDefPath, source, ts3.ScriptTarget.Latest, true);
for (const call of findAddIntegrationCalls(sourceFile)) {
const configArg = call.arguments[1];
if (!configArg || !ts3.isObjectLiteralExpression(configArg))
continue;
const alias = getStringProperty(configArg, "alias");
if (!alias)
continue;
result.set(alias, {
alias,
enabled: getBooleanProperty(configArg, "enabled") ?? true,
hasConfiguration: hasProperty(configArg, "configuration")
});
}
return result;
}
function findAddIntegrationCalls(sourceFile) {
const results = [];
function visit(node) {
if (ts3.isCallExpression(node) && ts3.isPropertyAccessExpression(node.expression) && node.expression.name.text === "addIntegration") {
results.push(node);
}
ts3.forEachChild(node, visit);
}
visit(sourceFile);
return results;
}
function getPropertyName(prop) {
if (ts3.isIdentifier(prop.name))
return prop.name.text;
if (ts3.isStringLiteral(prop.name))
return prop.name.text;
return;
}
function findProperty(obj, name) {
for (const prop of obj.properties) {
if (ts3.isPropertyAssignment(prop) && getPropertyName(prop) === name) {
return prop;
}
}
return;
}
function getStringProperty(obj, name) {
const prop = findProperty(obj, name);
if (prop && ts3.isStringLiteral(prop.initializer)) {
return prop.initializer.text;
}
return;
}
function getBooleanProperty(obj, name) {
const prop = findProperty(obj, name);
if (!prop)
return;
if (prop.initializer.kind === ts3.SyntaxKind.TrueKeyword)
return true;
if (prop.initializer.kind === ts3.SyntaxKind.FalseKeyword)
return false;
return;
}
function hasProperty(obj, name) {
return findProperty(obj, name) !== undefined;
}
// src/server/handlers/agent-map/ast/workflow-steps.ts
import ts4 from "typescript";
function parseWorkflowSteps(opts) {
const sourceFile = getCachedSourceFile(opts.agentRoot, opts.relativePath);
if (!sourceFile)
return [];
const handlerBody = findWorkflowHandlerBody(sourceFile, opts.exportName);
if (!handlerBody)
return [];
const ctx = {
sourceFile,
relativePath: opts.relativePath,
workflowId: opts.workflowId,
counter: 0
};
const result = [];
walk(handlerBody, result, ctx);
return result;
}
function walk(node, parent, ctx) {
ts4.forEachChild(node, (child) => {
if (ts4.isCallExpression(child)) {
const classification = classifyStepCall(child);
if (classification) {
const step = {
id: `${ctx.workflowId}/step:${ctx.counter++}`,
name: classification.name,
kind: classification.kind,
definedAt: nodeLocation(child, ctx)
};
if (classification.kind === "iterate") {
step.children = [];
const cb = child.arguments[2];
if (cb && (ts4.isArrowFunction(cb) || ts4.isFunctionExpression(cb))) {
walk(cb.body, step.children, ctx);
}
}
parent.push(step);
return;
}
}
walk(child, parent, ctx);
});
}
function nodeLocation(node, ctx) {
const start = ctx.sourceFile.getLineAndCharacterOfPosition(node.getStart(ctx.sourceFile));
const end = ctx.sourceFile.getLineAndCharacterOfPosition(node.getEnd());
return {
path: ctx.relativePath,
startLine: start.line + 1,
endLine: end.line + 1
};
}
var METHOD_KINDS = {
listen: "listen",
progress: "progress",
request: "request",
notify: "notify",
sleep: "sleep",
sleepUntil: "sleep",
forEach: "iterate",
map: "iterate",
batch: "iterate",
waitForWorkflow: "waitForWorkflow",
executeWorkflow: "waitForWorkflow"
};
function classifyStepCall(call) {
const expr = call.expression;
if (ts4.isIdentifier(expr) && expr.text === "step") {
const name = stringArg(call.arguments[0]);
if (name === undefined)
return null;
return { kind: "generic", name };
}
if (ts4.isPropertyAccessExpression(expr) && ts4.isIdentifier(expr.expression) && expr.expression.text === "step") {
const method = expr.name.text;
if (method === "fail" || method === "abort")
return null;
const kind = METHOD_KINDS[method];
if (!kind) {
const name2 = stringArg(call.arguments[0]) ?? `step.${method}`;
return { kind: "unknown", name: name2 };
}
let name;
if (kind === "request" || kind === "notify") {
name = stringArg(call.arguments[2]) ?? stringArg(call.arguments[0]);
} else {
name = stringArg(call.arguments[0]);
}
if (name === undefined)
return null;
return { kind, name };
}
return null;
}
function stringArg(node) {
if (!node)
return;
if (ts4.isStringLiteral(node) || ts4.isNoSubstitutionTemplateLiteral(node)) {
return node.text;
}
return;
}
function findWorkflowHandlerBody(sourceFile, exportName) {
let exactMatch;
let firstMatch;
function visit(node) {
if (exactMatch)
return;
if (ts4.isNewExpression(node) && ts4.isIdentifier(node.expression) && node.expression.text === "Workflow") {
const arg = node.arguments?.[0];
if (arg && ts4.isObjectLiteralExpression(arg)) {
const handlerBody = extractHandlerBody(arg);
if (handlerBody) {
if (firstMatch === undefined)
firstMatch = handlerBody;
if (isExportedAs(node, exportName)) {
exactMatch = handlerBody;
return;
}
}
}
}
ts4.forEachChild(node, visit);
}
visit(sourceFile);
return exactMatch ?? firstMatch;
}
function extractHandlerBody(obj) {
for (const prop of obj.properties) {
if (ts4.isPropertyAssignment(prop) && propName(prop.name) === "handler") {
const init = prop.initializer;
if (ts4.isArrowFunction(init) || ts4.isFunctionExpression(init)) {
return init.body;
}
}
if (ts4.isMethodDeclaration(prop) && propName(prop.name) === "handler") {
if (prop.body)
return prop.body;
}
}
return;
}
function isExportedAs(newExpr, exportName) {
let cursor = newExpr.parent;
while (cursor) {
if (ts4.isVariableDeclaration(cursor)) {
const decl = cursor;
const matchesName = ts4.isIdentifier(decl.name) && decl.name.text === exportName;
if (!matchesName)
return false;
const stmt = decl.parent.parent;
if (stmt && ts4.isVariableStatement(stmt) && ts4.canHaveModifiers(stmt) && ts4.getModifiers(stmt)?.some((m) => m.kind === ts4.SyntaxKind.ExportKeyword)) {
return true;
}
return false;
}
cursor = cursor.parent;
}
return false;
}
function propName(name) {
if (ts4.isIdentifier(name))
return name.text;
if (ts4.isStringLiteral(name))
return name.text;
return;
}
// src/server/handlers/agent-map/ast/facts-to-graph.ts
function factsToGraph(facts) {
const edges = [];
const seenEdges = new Set;
const integrationActions = new Map;
const aiAgents = [];
function addEdge(edge) {
if (seenEdges.has(edge.id))
return;
seenEdges.add(edge.id);
edges.push(edge);
}
for (const fact of facts) {
switch (fact.kind) {
case "primitiveCall": {
addEdge(makeEdge({
sourceId: fact.sourceId,
sourceType: fact.sourceType,
target: fact.target,
type: fact.edgeType,
via: "handler"
}));
break;
}
case "integrationActionCall": {
if (!integrationActions.has(fact.actionId)) {
integrationActions.set(fact.actionId, { id: fact.actionId, name: fact.name, alias: fact.alias });
}
addEdge(makeEdge({
sourceId: fact.sourceId,
sourceType: fact.sourceType,
target: { id: fact.actionId, type: "integrationAction" },
type: "invokes",
via: "handler"
}));
break;
}
case "executeBlock": {
const aiAgent = makeAIAgent(fact);
aiAgents.push(aiAgent);
addEdge(makeEdge({
sourceId: fact.parentId,
sourceType: fact.parentType,
target: { id: aiAgent.id, type: "aiAgent" },
type: "invokes",
via: "handler"
}));
for (const ref of fact.toolRefRecords) {
addEdge(makeEdge({
sourceId: aiAgent.id,
sourceType: "aiAgent",
target: ref,
type: "invokes",
via: "agent-loop"
}));
}
for (const ref of fact.knowledgeRefRecords) {
addEdge(makeEdge({
sourceId: aiAgent.id,
sourceType: "aiAgent",
target: ref,
type: "queries",
via: "agent-loop"
}));
}
break;
}
}
}
return {
edges,
integrationActions: [...integrationActions.values()],
aiAgents
};
}
function makeAIAgent(fact) {
const definedAt = {
path: fact.callsite.path,
startLine: fact.callsite.startLine,
endLine: fact.callsite.endLine
};
return {
id: makeAIAgentId(fact),
parentId: fact.parentId,
parentType: fact.parentType,
definedAt,
model: fact.model ?? "default",
toolRefs: fact.toolRefs,
knowledgeRefs: fact.knowledgeRefs,
...fact.iterations !== undefined && { iterations: fact.iterations },
...fact.mode !== undefined && { mode: fact.mode },
...fact.temperature !== undefined && { temperature: fact.temperature },
...fact.reasoningEffort !== undefined && { reasoningEffort: fact.reasoningEffort },
...fact.instructions !== undefined && { instructions: fact.instructions },
...fact.exits !== undefined && { exits: fact.exits },
...fact.hooks !== undefined && { hooks: fact.hooks },
...fact.objects !== undefined && { objects: fact.objects },
parseStatus: { ok: true }
};
}
function makeAIAgentId(fact) {
return `aiAgent:${fact.parentId}:${fact.callsite.startLine}:${fact.callsite.startColumn}`;
}
function makeEdge(opts) {
return {
id: `edge:${opts.sourceId}->${opts.type}->${opts.target.id}`,
source: opts.sourceId,
sourceType: opts.sourceType,
target: opts.target.id,
targetType: opts.target.type,
type: opts.type,
via: opts.via
};
}
// src/server/handlers/agent-map/ast/project-index.ts
var logger = createCliLogger({ tag: "agent-map" });
function buildProjectIndex(project) {
const exportLookup = new Map;
const actionByName = new Map;
const knownIntegrationAliases = new Set;
function register(exportName, ref) {
const existing = exportLookup.get(exportName);
if (existing && existing.id !== ref.id) {
logger.warn(`export-name collision on "${exportName}": "${existing.id}" overwritten by "${ref.id}". ` + `Edges from handlers that imported "${existing.id}" will resolve to "${ref.id}". ` + `Rename one of the exports to disambiguate.`);
}
exportLookup.set(exportName, ref);
}
for (const ref of project.actions) {
if (isBuiltInPrimitive(ref))
continue;
const primitiveRef = { id: `action:${ref.definition.name}`, type: "action" };
register(ref.export, primitiveRef);
actionByName.set(ref.definition.name, primitiveRef);
}
for (const ref of project.tools) {
if (isBuiltInPrimitive(ref))
continue;
register(ref.export, { id: `tool:${ref.definition.name}`, type: "tool" });
}
for (const ref of project.workflows) {
if (isBuiltInPrimitive(ref))
continue;
register(ref.export, { id: `workflow:${ref.definition.name}`, type: "workflow" });
}
for (const ref of project.tables) {
register(ref.export, { id: `table:${ref.definition.name}`, type: "table" });
}
for (const ref of project.knowledge) {
register(ref.export, { id: `knowledge:${ref.definition.name}`, type: "knowledge" });
}
for (const ref of project.integrations) {
knownIntegrationAliases.add(ref.alias);
}
return { exportLookup, actionByName, knownIntegrationAliases };
}
function isBuiltInPrimitive(ref) {
if (ref.path === "<adk:builtin>")
return true;
if (ref.definition?.name?.startsWith("data_source_sync_"))
return true;
return false;
}
// src/server/handlers/agent-map/ast/source-walk.ts
import ts10 from "typescript";
// src/server/handlers/agent-map/ast/import-resolution.ts
import ts5 from "typescript";
function buildImportResolution(sourceFile, globalLookup) {
const resolution = new Map;
for (const stmt of sourceFile.statements) {
if (!ts5.isImportDeclaration(stmt) || !stmt.importClause)
continue;
if (stmt.importClause.name) {
const localName = stmt.importClause.name.text;
const ref = globalLookup.get(localName);
if (ref !== undefined)
resolution.set(localName, ref);
}
const bindings = stmt.importClause.namedBindings;
if (bindings && ts5.isNamedImports(bindings)) {
for (const elem of bindings.elements) {
const localName = elem.name.text;
const lookupName = elem.propertyName?.text ?? localName;
const ref = globalLookup.get(lookupName);
if (ref !== undefined)
resolution.set(localName, ref);
}
}
}
return resolution;
}
// src/server/handlers/agent-map/ast/matchers/actions-proxy.ts
import ts6 from "typescript";
var matchActionsProxyCall = (node, ctx) => {
if (!ts6.isCallExpression(node) || !ts6.isPropertyAccessExpression(node.expression))
return null;
const inner = node.expression.expression;
const lastName = node.expression.name.text;
if (ts6.isIdentifier(inner) && inner.text === "actions") {
const ref = ctx.projectIndex.actionByName.get(lastName);
if (!ref)
return null;
return {
facts: [
{
kind: "primitiveCall",
sourceId: ctx.callerId,
sourceType: ctx.callerType,
target: ref,
edgeType: "invokes"
}
],
traversal: "continue"
};
}
if (ts6.isPropertyAccessExpression(inner) && ts6.isIdentifier(inner.expression) && inner.expression.text === "actions") {
const alias = inner.name.text;
if (!ctx.projectIndex.knownIntegrationAliases.has(alias))
return null;
const actionId = `integrationAction:${alias}:${lastName}`;
return {
facts: [
{
kind: "integrationActionCall",
sourceId: ctx.callerId,
sourceType: ctx.callerType,
alias,
name: lastName,
actionId
}
],
traversal: "continue"
};
}
return null;
};
// src/server/handlers/agent-map/ast/matchers/execute.ts
import ts8 from "typescript";
// src/server/handlers/agent-map/ast/execute-blocks.ts
import ts7 from "typescript";
function extractExecuteBlockFact(opts) {
const { call, sourceFile, resolution, parentId, parentType, relativePath } = opts;
const start = sourceFile.getLineAndCharacterOfPosition(call.getStart(sourceFile));
const startLine = start.line + 1;
const startColumn = start.character + 1;
const endLine = sourceFile.getLineAndCharacterOfPosition(call.getEnd()).line + 1;
const props = extractExecuteProps(call, resolution);
return {
kind: "executeBlock",
parentId,
parentType,
callsite: {
path: relativePath,
startLine,
startColumn,
endLine
},
...props.model !== undefined && { model: props.model },
toolRefs: props.toolRefs,
knowledgeRefs: props.knowledgeRefs,
toolRefRecords: props.toolRefRecords,
knowledgeRefRecords: props.knowledgeRefRecords,
...props.iterations !== undefined && { iterations: props.iterations },
...props.mode !== undefined && { mode: props.mode },
...props.temperature !== undefined && { temperature: props.temperature },
...props.reasoningEffort !== undefined && { reasoningEffort: props.reasoningEffort },
...props.instructions !== undefined && { instructions: props.instructions },
...props.exits !== undefined && { exits: props.exits },
...props.hooks !== undefined && { hooks: props.hooks },
...props.objects !== undefined && { objects: props.objects }
};
}
function isExecuteCall(call) {
const expr = call.expression;
if (ts7.isIdentifier(expr) && expr.text === "execute")
return true;
if (ts7.isPropertyAccessExpression(expr) && expr.name.text === "execute" && ts7.isIdentifier(expr.expression) && expr.expression.text === "Autonomous") {
return true;
}
return false;
}
var TOOL_TYPES = new Set(["action", "tool", "workflow"]);
function extractExecuteProps(call, resolution) {
const props = {
toolRefs: [],
knowledgeRefs: [],
toolRefRecords: [],
knowledgeRefRecords: []
};
const arg = call.arguments[0];
if (!arg || !ts7.isObjectLiteralExpression(arg))
return props;
for (const prop of arg.properties) {
if (!ts7.isPropertyAssignment(prop))
continue;
const key = ts7.isIdentifier(prop.name) ? prop.name.text : null;
if (!key)
continue;
const init = prop.initializer;
switch (key) {
case "model":
case "mode":
case "reasoningEffort": {
const s = stringLiteral(init);
if (s !== null)
props[key] = s;
break;
}
case "instructions": {
const instructions = instructionText(init);
if (instructions !== null)
props.instructions = instructions;
break;
}
case "iterations":
case "temperature": {
const n = numberLiteral(init);
if (n !== null)
props[key] = n;
break;
}
case "exits":
case "objects": {
const names = identifierArray(init);
if (names.length > 0)
props[key] = names;
break;
}
case "hooks": {
const names = objectKeyNames(init);
if (names.length > 0)
props[key] = names;
break;
}
case "tools":
case "knowledge": {
if (!ts7.isArrayLiteralExpression(init))
break;
for (const elem of init.elements) {
const ref = resolveToolElement(elem, resolution);
if (!ref)
continue;
if (key === "tools" && !TOOL_TYPES.has(ref.type))
continue;
if (key === "knowledge" && ref.type !== "knowledge")
continue;
const records = key === "tools" ? props.toolRefRecords : props.knowledgeRefRecords;
const ids = key === "tools" ? props.toolRefs : props.knowledgeRefs;
if (!ids.includes(ref.id)) {
ids.push(ref.id);
records.push(ref);
}
}
break;
}
default:
break;
}
}
return props;
}
function resolveToolElement(elem, resolution) {
if (ts7.isIdentifier(elem)) {
return resolution.get(elem.text) ?? null;
}
if (ts7.isCallExpression(elem) && ts7.isPropertyAccessExpression(elem.expression) && elem.expression.name.text === "asTool" && ts7.isIdentifier(elem.expression.expression)) {
return resolution.get(elem.expression.expression.text) ?? null;
}
return null;
}
function stringLiteral(node) {
if (ts7.isStringLiteral(node) || ts7.isNoSubstitutionTemplateLiteral(node))
return node.text;
return null;
}
function instructionText(node) {
const unwrapped = unwrapStringExpression(node);
const literal = stringLiteral(unwrapped);
if (literal !== null)
return literal.trim();
if (ts7.isTemplateExpression(unwrapped)) {
const parts = [unwrapped.head.text];
for (const span of unwrapped.templateSpans) {
parts.push(`\${${span.expression.getText()}}`);
parts.push(span.literal.text);
}
return parts.join("").trim();
}
return null;
}
function unwrapStringExpression(node) {
if (ts7.isParenthesizedExpression(node))
return unwrapStringExpression(node.expression);
if (ts7.isCallExpression(node) && ts7.isPropertyAccessExpression(node.expression) && node.expression.name.text === "trim") {
return unwrapStringExpression(node.expression.expression);
}
return node;
}
function numberLiteral(node) {
if (ts7.isNumericLiteral(node))
return Number(node.text);
return null;
}
function identifierArray(node) {
if (!ts7.isArrayLiteralExpression(node))
return [];
const names = [];
for (const elem of node.elements) {
if (ts7.isIdentifier(elem))
names.push(elem.text);
}
return names;
}
function objectKeyNames(node) {
if (!ts7.isObjectLiteralExpression(node))
return [];
const names = [];
for (const prop of node.properties) {
const name = ts7.isPropertyAssignment(prop) || ts7.isShorthandPropertyAssignment(prop) || ts7.isMethodDeclaration(prop) ? ts7.isIdentifier(prop.name) ? prop.name.text : ts7.isStringLiteral(prop.name) ? prop.name.text : null : null;
if (name)
names.push(name);
}
return names;
}
// src/server/handlers/agent-map/ast/matchers/execute.ts
var matchExecuteCall = (node, ctx) => {
if (!ts8.isCallExpression(node) || !isExecuteCall(node))
return null;
return {
facts: [
extractExecuteBlockFact({
call: node,
sourceFile: ctx.sourceFile,
resolution: ctx.fileResolution,
parentId: ctx.callerId,
parentType: ctx.callerType,
relativePath: ctx.relativePath
})
],
traversal: "skip-subtree"
};
};
// src/server/handlers/agent-map/ast/matchers/primitive-calls.ts
import ts9 from "typescript";
var TABLE_READ_METHODS = new Set(["findRows", "getRow", "getRows", "list", "select", "find"]);
var TABLE_WRITE_METHODS = new Set([
"createRows",
"insertRows",
"updateRows",
"upsertRows",
"deleteRows",
"deleteRowIds",
"deleteRow"
]);
var KB_SEARCH_METHODS = new Set(["search", "find", "query"]);
var WORKFLOW_INVOKE_METHODS = new Set(["start", "startAndWait", "getOrCreate"]);
var matchPrimitiveCall = (node, ctx) => {
if (!ts9.isCallExpression(node))
return null;
if (ts9.isPropertyAccessExpression(node.expression)) {
const target = node.expression.expression;
const method = node.expression.name.text;
if (ts9.isIdentifier(target)) {
const ref = ctx.fileResolution.get(target.text);
if (ref) {
const edgeType = classifyMethodOnPrimitive(ref.type, method);
if (edgeType) {
return {
facts: [
{
kind: "primitiveCall",
sourceId: ctx.callerId,
sourceType: ctx.callerType,
target: ref,
edgeType
}
],
traversal: "continue"
};
}
}
}
}
if (ts9.isIdentifier(node.expression)) {
const ref = ctx.fileResolution.get(node.expression.text);
if (ref && (ref.type === "action" || ref.type === "workflow")) {
return {
facts: [
{
kind: "primitiveCall",
sourceId: ctx.callerId,
sourceType: ctx.callerType,
target: ref,
edgeType: "invokes"
}
],
traversal: "continue"
};
}
}
return null;
};
function classifyMethodOnPrimitive(type, method) {
if (type === "table") {
if (TABLE_READ_METHODS.has(method))
return "reads";
if (TABLE_WRITE_METHODS.has(method))
return "writes";
return null;
}
if (type === "workflow") {
if (WORKFLOW_INVOKE_METHODS.has(method))
return "invokes";
return null;
}
if (type === "knowledge") {
if (KB_SEARCH_METHODS.has(method))
return "queries";
return null;
}
return null;
}
// src/server/handlers/agent-map/ast/source-walk.ts
var MATCHERS = [matchExecuteCall, matchPrimitiveCall, matchActionsProxyCall];
function extractFactsFromFile(opts) {
const sourceFile = getCachedSourceFile(opts.agentRoot, opts.relativePath);
if (!sourceFile)
return [];
const ctx = {
sourceFile,
relativePath: opts.relativePath,
callerId: opts.callerId,
callerType: opts.callerType,
fileResolution: buildImportResolution(sourceFile, opts.projectIndex.exportLookup),
projectIndex: opts.projectIndex
};
const facts = [];
function visit(node) {
for (const matcher of MATCHERS) {
const result = matcher(node, ctx);
if (!result)
continue;
facts.push(...result.facts);
if (result.traversal === "skip-subtree")
return;
}
ts10.forEachChild(node, visit);
}
visit(sourceFile);
return facts;
}
// src/server/handlers/agent-map/ast/handler-edges.ts
function extractAstEdges(opts) {
const projectIndex = buildProjectIndex(opts.project);
const facts = [];
for (const caller of opts.callers) {
facts.push(...extractFactsFromFile({
agentRoot: opts.project.path,
relativePath: caller.relativePath,
callerId: caller.primitiveId,
callerType: caller.primitiveType,
projectIndex
}));
}
return factsToGraph(facts);
}
// src/server/handlers/agent-map/ast/component-refs.ts
import ts11 from "typescript";
function extractComponentRefs(opts) {
const sourceFile = getCachedSourceFile(opts.agentRoot, opts.relativePath);
if (!sourceFile)
return [];
const fileResolution = buildImportResolution(sourceFile, opts.resolution);
const found = new Set;
function visit(node) {
if (ts11.isNewExpression(node) && ts11.isIdentifier(node.expression) && node.expression.text === "Conversation") {
const arg = node.arguments?.[0];
if (arg && ts11.isObjectLiteralExpression(arg)) {
const componentsProp = arg.properties.find((p) => ts11.isPropertyAssignment(p) && ts11.isIdentifier(p.name) && p.name.text === "components");
if (componentsProp && ts11.isPropertyAssignment(componentsProp) && ts11.isArrayLiteralExpression(componentsProp.initializer)) {
for (const elem of componentsProp.initializer.elements) {
if (ts11.isIdentifier(elem)) {
const id = fileResolution.get(elem.text);
if (id)
found.add(id);
}
}
}
}
}
if (ts11.isCallExpression(node) && ts11.isPropertyAccessExpression(node.expression) && node.expression.name.text === "send") {
const arg = node.arguments[0];
if (arg && ts11.isObjectLiteralExpression(arg)) {
const typeProp = arg.properties.find((p) => ts11.isPropertyAssignment(p) && ts11.isIdentifier(p.name) && p.name.text === "type");
const isCustomComponent = typeProp && ts11.isPropertyAssignment(typeProp) && ts11.isStringLiteral(typeProp.initializer) && typeProp.initializer.text === "customComponent";
if (isCustomComponent) {
const payloadProp = arg.properties.find((p) => ts11.isPropertyAssignment(p) && ts11.isIdentifier(p.name) && p.name.text === "payload");
if (payloadProp && ts11.isPropertyAssignment(payloadProp) && ts11.isObjectLiteralExpression(payloadProp.initializer)) {
const componentProp = payloadProp.initializer.properties.find((p) => ts11.isPropertyAssignment(p) && ts11.isIdentifier(p.name) && p.name.text === "component");
if (componentProp && ts11.isPropertyAssignment(componentProp) && ts11.isIdentifier(componentProp.initializer)) {
const id = fileResolution.get(componentProp.initializer.text);
if (id)
found.add(id);
}
}
}
}
}
ts11.forEachChild(node, visit);
}
visit(sourceFile);
return [...found];
}
// src/server/handlers/agent-map/ast/conversation-lifecycle.ts
import ts12 from "typescript";
function extractConversationAstProps(opts) {
const sourceFile = getCachedSourceFile(opts.agentRoot, opts.relativePath);
if (!sourceFile)
return {};
let result;
function visit(node) {
if (result)
return;
if (ts12.isNewExpression(node) && ts12.isIdentifier(node.expression) && node.expression.text === "Conversation") {
const arg = node.arguments?.[0];
if (arg && ts12.isObjectLiteralExpression(arg)) {
result = {
lifecycle: lifecycleFromConversationProps(arg),
stateSchema: stateSchemaFromConversationProps(arg)
};
if (result.lifecycle || result.stateSchema)
return;
}
}
ts12.forEachChild(node, visit);
}
visit(sourceFile);
return result ?? {};
}
function lifecycleFromConversationProps(props) {
const lifecycleProp = findObjectProperty(props, "lifecycle");
if (!lifecycleProp || !ts12.isObjectLiteralExpression(lifecycleProp.initializer))
return;
const lifecycle = {};
const nudgeProp = findObjectProperty(lifecycleProp.initializer, "nudge");
if (nudgeProp && ts12.isObjectLiteralExpression(nudgeProp.initializer)) {
const nudge = {
after: stringProp(nudgeProp.initializer, "after"),
interval: stringProp(nudgeProp.initializer, "interval"),
max: numberProp(nudgeProp.initializer, "max")
};
if (nudge.after !== undefined || nudge.interval !== undefined || nudge.max !== undefined) {
lifecycle.nudge = nudge;
}
}
const expireProp = findObjectProperty(lifecycleProp.initializer, "expire");
if (expireProp && ts12.isObjectLiteralExpression(expireProp.initializer)) {
const expire = { after: stringProp(expireProp.initializer, "after") };
if (expire.after !== undefined) {
lifecycle.expire = expire;
}
}
return lifecycle.nudge || lifecycle.expire ? lifecycle : undefined;
}
function stateSchemaFromConversationProps(props) {
const stateProp = findObjectProperty(props, "state");
if (!stateProp)
return;
return parseZuiSchema(stateProp.initializer)?.schema;
}
function parseZuiSchema(node) {
if (ts12.isParenthesizedExpression(node))
return parseZuiSchema(node.expression);
if (!ts12.isCallExpression(node))
return;
const callee = node.expression;
if (!ts12.isPropertyAccessExpression(callee))
return;
const method = callee.name.text;
const receiver = callee.expression;
if (ts12.isIdentifier(receiver) && receiver.text === "z") {
return parseBaseZuiCall(method, node);
}
const base = parseZuiSchema(receiver);
if (!base)
return;
switch (method) {
case "optional":
case "default":
return { ...base, optional: true };
case "describe": {
const description = stringLiteral2(node.arguments[0]);
return description ? { ...base, schema: { ...base.schema, description } } : base;
}
case "nullable":
return { ...base, schema: { ...base.schema, nullable: true } };
default:
return base;
}
}
function parseBaseZuiCall(method, call) {
switch (method) {
case "object":
return parseZuiObject(call);
case "string":
return { schema: { type: "string" }, optional: false };
case "number":
return { schema: { type: "number" }, optional: false };
case "boolean":
return { schema: { type: "boolean" }, optional: false };
case "array": {
const item = call.arguments[0] ? parseZuiSchema(call.arguments[0]) : undefined;
return { schema: { type: "array", ...item && { items: item.schema } }, optional: false };
}
case "record": {
const valueSchema = call.arguments[1] ? parseZuiSchema(call.arguments[1]) : undefined;
return {
schema: { type: "object", ...valueSchema && { additionalProperties: valueSchema.schema } },
optional: false
};
}
case "enum": {
const values = stringArray(call.arguments[0]);
return { schema: values.length > 0 ? { type: "string", enum: values } : { type: "string" }, optional: false };
}
case "literal": {
const value = literalValue(call.arguments[0]);
return { schema: value === undefined ? {} : { enum: [value] }, optional: false };
}
case "any":
case "unknown":
return { schema: {}, optional: false };
default:
return;
}
}
function parseZuiObject(call) {
const shape = call.arguments[0];
if (!shape || !ts12.isObjectLiteralExpression(shape))
return { schema: { type: "object" }, optional: false };
const properties = {};
const required = [];
for (const prop of shape.properties) {
if (!ts12.isPropertyAssignment(prop))
continue;
const name = propertyName(prop.name);
if (!name)
continue;
const parsed = parseZuiSchema(prop.initializer);
if (!parsed)
continue;
properties[name] = parsed.schema;
if (!parsed.optional)
required.push(name);
}
return {
schema: {
type: "object",
properties,
...required.length > 0 && { required }
},
optional: false
};
}
function findObjectProperty(object, name) {
return object.properties.find((prop) => ts12.isPropertyAssignment(prop) && propertyName(prop.name) === name);
}
function propertyName(name) {
if (ts12.isIdentifier(name) || ts12.isStringLiteral(name) || ts12.isNumericLiteral(name))
return name.text;
return;
}
function stringProp(object, name) {
const prop = findObjectProperty(object, name);
const value = prop?.initializer;
if (!value)
return;
if (ts12.isStringLiteral(value) || ts12.isNoSubstitutionTemplateLiteral(value))
return value.text;
return;
}
function stringLiteral2(value) {
if (!value)
return;
if (ts12.isStringLiteral(value) || ts12.isNoSubstitutionTemplateLiteral(value))
return value.text;
return;
}
function numberProp(object, name) {
const prop = findObjectProperty(object, name);
const value = prop?.initializer;
if (!value)
return;
if (ts12.isNumericLiteral(value))
return Number(value.text);
return;
}
function stringArray(node) {
if (!node || !ts12.isArrayLiteralExpression(node))
return [];
return node.elements.map((elem) => ts12.isStringLiteral(elem) || ts12.isNoSubstitutionTemplateLiteral(elem) ? elem.text : undefined).filter((value) => value !== undefined);
}
function literalValue(node) {
if (!node)
return;
if (ts12.isStringLiteral(node) || ts12.isNoSubstitutionTemplateLiteral(node))
return node.text;
if (ts12.isNumericLiteral(node))
return Number(node.text);
if (node.kind === ts12.SyntaxKind.TrueKeyword)
return true;
if (node.kind === ts12.SyntaxKind.FalseKeyword)
return false;
return;
}
// src/server/handlers/agent-map/snapshot.ts
function buildAgentSnapshot(project, options = {}) {
clearSourceFileCache();
const integrations = buildIntegrationMeta(project);
const customComponents = parseCustomComponents(project);
const componentResolution = new Map(project.customComponents.map((ref) => [ref.export, `customComponent:${ref.definition.name}`]));
const conversations = parseConversations(project, integrations, componentResolution);
const triggers = parseTriggers(project, integrations);
const {
edges: astEdges,
integrationActions,
aiAgents
} = extractAstEdges({
project,
callers: collectCallerSources(project)
});
const declarativeEdges = buildDeclarativeUsesComponentEdges(conversations);
const edges = mergeEdges(declarativeEdges, astEdges);
return {
origin: options.origin ?? { kind: "local" },
agent: buildAgentConfigSummary(project),
actions: parseActions(project),
tools: parseTools(project),
workflows: parseWorkflows(project),
conversations,
triggers,
tables: parseTables(project),
knowledge: parseKnowledge(project),
customComponents,
integrationActions,
aiAgents,
edges,
integrations
};
}
function buildAgentConfigSummary(project) {
const config = project.config;
const state = compactObject({
user: schemaToJsonSchema(asRecord(config?.user)?.state),
bot: schemaToJsonSchema(asRecord(config?.bot)?.state)
});
const configurationSchema = schemaToJsonSchema(asRecord(config?.configuration)?.schema);
return {
...typeof config?.name === "string" && config.name.length > 0 && { name: config.name },
...typeof config?.description === "string" && config.description.length > 0 && { description: config.description },
defaultModels: normalizeDefaultModels(config?.defaultModels),
...Object.keys(state).length > 0 && { state },
...normalizeTags(config),
...configurationSchema && { configuration: { schema: configurationSchema } },
secrets: normalizeSecrets(config?.secrets),
...normalizeEvals(config?.evals)
};
}
function normalizeDefaultModels(value) {
const models = asRecord(value);
return {
autonomous: normalizeModelConfig(models.autonomous) ?? "auto",
zai: normalizeModelConfig(models.zai) ?? "auto"
};
}
function normalizeModelConfig(value) {
if (typeof value === "string" && value.length > 0)
return value;
if (Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0)) {
return value;
}
return;
}
function normalizeTags(config) {
if (!config)
return {};
const tags = {};
for (const key of ["user", "bot", "conversation", "message", "workflow"]) {
const normalized = normalizeTagDefinitions(asRecord(config[key])?.tags);
if (Object.keys(normalized).length > 0) {
tags[key] = normalized;
}
}
return Object.keys(tags).length > 0 ? { tags } : {};
}
function normalizeTagDefinitions(value) {
const out = {};
for (const [name, rawDefinition] of Object.entries(asRecord(value))) {
const definition = asRecord(rawDefinition);
out[name] = {
title: typeof definition.title === "string" && definition.title.length > 0 ? definition.title : name,
...typeof definition.description === "string" && definition.description.length > 0 && { description: definition.description }
};
}
return out;
}
function normalizeSecrets(value) {
return Object.entries(asRecord(value)).map(([name, rawDeclaration]) => {
const declaration = asRecord(rawDeclaration);
return {
name,
optional: declaration.optional === true,
...typeof declaration.description === "string" && declaration.description.length > 0 && { description: declaration.description }
};
});
}
function normalizeEvals(value) {
const evals = asRecord(value);
const normalized = {
...typeof evals.judgeModel === "string" && evals.judgeModel.length > 0 && { judgeModel: evals.judgeModel },
...typeof evals.judgePassThreshold === "number" && { judgePassThreshold: evals.judgePassThreshold },
...typeof evals.idleTimeout === "number" && { idleTimeout: evals.idleTimeout }
};
return Object.keys(normalized).length > 0 ? { evals: normalized } : {};
}
function schemaToJsonSchema(schema) {
if (!isRecord(schema))
return;
if (typeof schema.toJSONSchema === "function") {
try {
return schema.toJSONSchema();
} catch {
return;
}
}
return schema;
}
function compactObject(value) {
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
}
function asRecord(value) {
return isRecord(value) ? value : {};
}
function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function collectCallerSources(project) {
const callers = [];
for (const ref of project.actions) {
if (isBuiltInPrimitive2(ref))
continue;
callers.push({
primitiveId: `action:${ref.definition.name}`,
primitiveType: "action",
relativePath: ref.path
});
}
for (const ref of project.tools) {
if (isBuiltInPrimitive2(ref))
continue;
callers.push({
primitiveId: `tool:${ref.definition.name}`,
primitiveType: "tool",
relativePath: ref.path
});
}
for (const ref of project.workflows) {
if (isBuiltInPrimitive2(ref))
continue;
callers.push({
primitiveId: `workflow:${ref.definition.name}`,
primitiveType: "workflow",
relativePath: ref.path
});
}
for (const ref of project.conversations) {
callers.push({
primitiveId: `conversation:${pathToId(ref.path)}`,
primitiveType: "conversation",
relativePath: ref.path
});
}
for (const ref of project.triggers) {
if (isBuiltInPrimitive2(ref))
continue;
callers.push({
primitiveId: `trigger:${ref.definition.name}`,
primitiveType: "trigger",
relativePath: ref.path
});
}
return callers;
}
function isBuiltInPrimitive2(ref) {
if (ref.path === "<adk:builtin>")
return true;
if (ref.definition?.name?.startsWith("data_source_sync_"))
return true;
return false;
}
function mergeEdges(a, b) {
const seen = new Set;
const result = [];
for (const e of [...a, ...b]) {
if (seen.has(e.id))
continue;
seen.add(e.id);
result.push(e);
}
return result;
}
function parseActions(project) {
return project.actions.filter((ref) => !isBuiltInPrimitive2(ref)).map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `action:${def.name}`,
name: def.name,
...def.title !== undefined && { title: def.title },
...def.description !== undefined && { description: def.description },
...def.attributes !== undefined && { attributes: def.attributes },
definedAt,
...def.input !== undefined && { inputSchema: def.input },
...def.output !== undefined && { outputSchema: def.output },
...def.cached !== undefined && { cached: def.cached },
parseStatus: { ok: true }
};
});
}
function parseTools(project) {
return project.tools.filter((ref) => !isBuiltInPrimitive2(ref)).map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `tool:${def.name}`,
name: def.name,
...def.description !== undefined && { description: def.description },
definedAt,
parseStatus: { ok: true }
};
});
}
function parseTriggers(project, integrations) {
return project.triggers.filter((ref) => !isBuiltInPrimitive2(ref)).map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `trigger:${def.name}`,
name: def.name,
...def.description !== undefined && { description: def.description },
definedAt,
events: def.events.map((raw) => parseTriggerEvent(raw, integrations)),
...def.state !== undefined && { stateSchema: def.state },
parseStatus: { ok: true }
};
});
}
function parseTriggerEvent(raw, integrations) {
const colonIdx = raw.indexOf(":");
if (colonIdx === -1)
return { name: raw };
const alias = raw.slice(0, colonIdx);
const eventName = raw.slice(colonIdx + 1);
if (integrations[alias])
return { name: eventName, integrationAlias: alias };
return { name: raw };
}
function parseTables(project) {
return project.tables.map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `table:${def.name}`,
name: def.name,
...def.description !== undefined && { description: def.description },
definedAt,
schema: def.schema,
...def.keyColumn !== undefined && { keyColumn: def.keyColumn },
factor: def.factor,
...def.tags !== undefined && { tags: def.tags },
parseStatus: { ok: true }
};
});
}
function parseKnowledge(project) {
return project.knowledge.map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `knowledge:${def.name}`,
name: def.name,
...def.description !== undefined && { description: def.description },
definedAt,
sources: parseKnowledgeSources(def.sources),
parseStatus: { ok: true }
};
});
}
function parseKnowledgeSources(sources) {
return sources.map((source) => {
const ds = source;
let config = {};
try {
if (typeof ds.getConfig === "function") {
config = ds.getConfig();
}
} catch {}
return {
id: ds.id,
type: ds.type,
config
};
});
}
function parseWorkflows(project) {
return project.workflows.filter((ref) => !isBuiltInPrimitive2(ref)).map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `workflow:${def.name}`,
name: def.name,
...def.description !== undefined && { description: def.description },
definedAt,
...def.input !== undefined && { inputSchema: def.input },
...def.output !== undefined && { outputSchema: def.output },
...def.state !== undefined && { stateSchema: def.state },
...def.schedule !== undefined && { schedule: def.schedule },
timeout: def.timeout,
steps: parseWorkflowSteps({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export,
workflowId: `workflow:${def.name}`
}),
parseStatus: { ok: true }
};
});
}
function parseConversations(project, integrations, componentResolution) {
return project.conversations.map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
const componentRefs = extractComponentRefs({
agentRoot: project.path,
relativePath: ref.path,
resolution: componentResolution
});
const astProps = extractConversationAstProps({
agentRoot: project.path,
relativePath: ref.path
});
const channelStrings = Array.isArray(def.channel) ? def.channel : [def.channel];
const channels = channelStrings.map((s) => parseChannelBinding(s, integrations));
return {
id: `conversation:${pathToId(ref.path)}`,
channels,
...def.description !== undefined && { description: def.description },
definedAt,
...astProps.stateSchema !== undefined && { stateSchema: astProps.stateSchema },
...def.events !== undefined && { events: def.events },
hasLifecycle: def.hasLifecycle ?? false,
...astProps.lifecycle !== undefined && { lifecycle: astProps.lifecycle },
...componentRefs.length > 0 && { componentRefs },
parseStatus: { ok: true }
};
});
}
function parseChannelBinding(raw, integrations) {
if (raw === "*")
return { name: "*" };
const dotIdx = raw.indexOf(".");
if (dotIdx === -1)
return { name: raw };
const alias = raw.slice(0, dotIdx);
const name = raw.slice(dotIdx + 1);
if (integrations[alias])
return { name, integrationAlias: alias };
return { name: raw };
}
function pathToId(relativePath) {
return relativePath.replace(/\.(ts|tsx|js|jsx)$/, "").replace(/_/g, "__").replace(/[/\\]/g, "_");
}
function buildIntegrationMeta(project) {
const compiled = parseCompiledBotIntegrations(project.path);
const out = {};
for (const parsed of project.integrations) {
const compiledEntry = compiled.get(parsed.alias);
const enabled = compiledEntry?.enabled ?? parsed.enabled ?? true;
const hasConfiguration = compiledEntry?.hasConfiguration ?? false;
const def = parsed.definition;
const iconUrl = def?.iconUrl ?? def?.icon;
out[parsed.alias] = {
alias: parsed.alias,
ref: `${parsed.ref.fullName}@${parsed.ref.version}`,
...typeof iconUrl === "string" && iconUrl.length > 0 && { iconUrl },
enabled,
hasConfiguration
};
}
return out;
}
function buildDeclarativeUsesComponentEdges(conversations) {
const edges = [];
for (const conv of conversations) {
for (const componentId of conv.componentRefs ?? []) {
edges.push({
id: `edge:${conv.id}->uses_component->${componentId}`,
source: conv.id,
sourceType: "conversation",
target: componentId,
targetType: "customComponent",
type: "uses_component",
via: "handler"
});
}
}
return edges;
}
function parseCustomComponents(project) {
return project.customComponents.map((ref) => {
const def = ref.definition;
const definedAt = getExportLocation({
agentRoot: project.path,
relativePath: ref.path,
exportName: ref.export
}) ?? fallbackLocation(ref.path);
return {
id: `customComponent:${def.name}`,
name: def.name,
definedAt,
hasLlmMetadata: false,
parseStatus: { ok: true }
};
});
}
// src/server/handlers/agent-map/deployed-snapshot.ts
var DEPLOYED_AGENT_MAP_SNAPSHOT_SCHEMA_VERSION = 1;
var DEPLOYED_AGENT_MAP_SNAPSHOT_FILE_KEY = ".adk/agent-map-snapshot.json";
var DEPLOYED_AGENT_MAP_SNAPSHOT_TAGS = {
type: "adk-agent-map-snapshot",
schemaVersion: String(DEPLOYED_AGENT_MAP_SNAPSHOT_SCHEMA_VERSION)
};
var AGENT_MAP_SNAPSHOT_NOT_PUBLISHED_MESSAGE = "Agent Map metadata has not been published yet. Redeploy this ADK bot to publish Agent Map metadata.";
var SNAPSHOT_CACHE_TTL_MS = 15000;
class AgentMapSnapshotNotPublishedError extends Error {
constructor(message = AGENT_MAP_SNAPSHOT_NOT_PUBLISHED_MESSAGE) {
super(message);
this.name = "AgentMapSnapshotNotPublishedError";
}
}
function createDeployedAgentMapSnapshot(project, options = {}) {
const generatedAt = options.generatedAt ?? new Date().toISOString();
return {
schemaVersion: DEPLOYED_AGENT_MAP_SNAPSHOT_SCHEMA_VERSION,
generatedAt,
snapshot: buildAgentSnapshot(project, {
origin: { kind: "deployed-snapshot", generatedAt }
})
};
}
function serializeDeployedAgentMapSnapshot(artifact) {
return `${JSON.stringify(artifact, null, 2)}
`;
}
function parseDeployedAgentMapSnapshot(content) {
let value;
try {
value = JSON.parse(content);
} catch {
throw new Error("Deployed Agent Map metadata is not valid JSON.");
}
if (!isRecord2(value)) {
throw new Error("Invalid deployed Agent Map metadata: expected an object.");
}
if (value.schemaVersion !== DEPLOYED_AGENT_MAP_SNAPSHOT_SCHEMA_VERSION) {
throw new Error(`Invalid deployed Agent Map metadata: unsupported schema version.`);
}
if (typeof value.generatedAt !== "string" || value.generatedAt.length === 0) {
throw new Error("Invalid deployed Agent Map metadata: missing generatedAt.");
}
if (!isAgentSnapshot(value.snapshot)) {
throw new Error("Invalid deployed Agent Map metadata: invalid snapshot shape.");
}
const snapshot = value.snapshot;
return {
schemaVersion: DEPLOYED_AGENT_MAP_SNAPSHOT_SCHEMA_VERSION,
generatedAt: value.generatedAt,
snapshot: {
...snapshot,
origin: { kind: "deployed-snapshot", generatedAt: value.generatedAt }
}
};
}
async function uploadDeployedAgentMapSnapshot(client, artifact) {
await client.uploadFile({
key: DEPLOYED_AGENT_MAP_SNAPSHOT_FILE_KEY,
content: serializeDeployedAgentMapSnapshot(artifact),
contentType: "application/json",
tags: DEPLOYED_AGENT_MAP_SNAPSHOT_TAGS,
index: false
});
return { key: DEPLOYED_AGENT_MAP_SNAPSHOT_FILE_KEY };
}
class ProdAgentMapSnapshotFileService {
cache = new Map;
async getArtifact(target) {
const cacheKey = this.getCacheKey(target);
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.fetchedAt < SNAPSHOT_CACHE_TTL_MS) {
return cached.artifact;
}
const client = this.makeClient(target);
const file = await this.findSnapshotFile(client);
if (!file.url) {
throw new Error("Deployed Agent Map metadata file does not have a download URL.");
}
const response = await fetch(file.url);
if (!response.ok) {
throw new Error(`Failed to download deployed Agent Map metadata: ${response.status} ${response.statusText}`);
}
const artifact = parseDeployedAgentMapSnapshot(await response.text());
this.cache.set(cacheKey, { fetchedAt: Date.now(), artifact });
return artifact;
}
clear(target) {
if (!target) {
this.cache.clear();
return;
}
this.cache.delete(this.getCacheKey(target));
}
async findSnapshotFile(client) {
try {
const { file: file2 } = await client.getFile({ id: DEPLOYED_AGENT_MAP_SNAPSHOT_FILE_KEY });
return file2;
} catch (err) {
if (!isFileLookupFallbackError(err)) {
throw err;
}
}
const { files } = await client.listFiles({ tags: { ...DEPLOYED_AGENT_MAP_SNAPSHOT_TAGS } });
const file = files.find((candidate) => candidate.key === DEPLOYED_AGENT_MAP_SNAPSHOT_FILE_KEY) ?? files[0];
if (!file) {
throw new AgentMapSnapshotNotPublishedError;
}
return file;
}
makeClient(target) {
return new Uk({
token: target.token,
apiUrl: target.apiUrl,
workspaceId: target.workspaceId,
botId: target.botId,
headers: { "x-multiple-integrations": "true" }
});
}
getCacheKey(target) {
const tokenHash = createHash("sha256").update(target.token).digest("hex");
return `${target.apiUrl}:${target.workspaceId}:${target.botId}:${tokenHash}`;
}
}
class ProdAgentMapSnapshotService {
fileService;
constructor(fileService = new ProdAgentMapSnapshotFileService) {
this.fileService = fileService;
}
async getSnapshot(target) {
const artifact = await this.fileService.getArtifact(target);
return artifact.snapshot;
}
}
var prodAgentMapSnapshotService = new ProdAgentMapSnapshotService;
function isAgentSnapshot(value) {
if (!isRecord2(value) || !isRecord2(value.agent) || !isRecord2(value.integrations))
return false;
return [
value.actions,
value.tools,
value.workflows,
value.conversations,
value.triggers,
value.tables,
value.knowledge,
value.customComponents,
value.aiAgents,
value.integrationActions,
value.edges
].every(Array.isArray);
}
function isRecord2(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isFileLookupFallbackError(err) {
if (!isRecord2(err))
return false;
const type = err.type;
if (type === "ResourceNotFound" || type === "InvalidIdentifier" || type === "InvalidPayload") {
return true;
}
const status = err.status ?? err.statusCode ?? err.code ?? (isRecord2(err.response) ? err.response.status : undefined);
return status === 404 || status === 400;
}
export { buildAgentSnapshot, AgentMapSnapshotNotPublishedError, createDeployedAgentMapSnapshot, uploadDeployedAgentMapSnapshot, prodAgentMapSnapshotService };