agentlang
Version:
The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans
1,421 lines • 76.5 kB
JavaScript
import { isBinExpr, isGroup, isLiteral, isNegExpr, isNotExpr, isReturn, } from '../language/generated/ast.js';
import { maybeInstanceAsString, defineAgentEvent, getOneOfRef, getRelationship, getWorkflow, Instance, isAgentEventInstance, isBetweenRelationship, isContainsRelationship, isEmptyWorkflow, isEntityInstance, isEventInstance, isInstanceOfType, isTimer, makeInstance, newInstanceAttributes, PlaceholderRecordEntry, maybeSetMetaAttributes, } from './module.js';
import { Resolver } from './resolvers/interface.js';
import { ResolverAuthInfo } from './resolvers/authinfo.js';
import { SqlDbResolver } from './resolvers/sqldb/impl.js';
import { CrudType, DefaultModuleName, escapeFqName, escapeQueryName, fqNameFromPath, isFqName, isPath, isString, makeCoreModuleName, makeFqName, QuerySuffix, restoreSpecialChars, nameToPath, splitRefs, isCoreModule, preprocessRawConfig, } from './util.js';
import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
import { parseStatement, parseWorkflow } from '../language/parser.js';
import { AdminSession, AdminUserId } from './auth/defs.js';
import { AgentInstance, AgentEntityName, AgentFqName, findAgentByName, trimGeneratedCode, } from './modules/ai.js';
import { logger } from './logger.js';
import { FlowSuspensionTag, ParentAttributeName, PathAttributeName, PathAttributeNameQuery, } from './defs.js';
import { addCreateAudit, addDeleteAudit, addUpdateAudit, createSuspension, flushMonitoringData, maybeCancelTimer, setTimerRunning, } from './modules/core.js';
import { invokeModuleFn } from './jsmodules.js';
import { invokeOpenApiEvent, isOpenApiEventInstance } from './openapi.js';
import { fetchDoc } from './docs.js';
import { getAgentFlow } from './agents/flows.js';
import { isMonitoringEnabled } from './state.js';
import { Monitor, MonitorEntry } from './monitor.js';
const EmptyResult = null;
export function isEmptyResult(r) {
return r == EmptyResult;
}
function mkEnvName(name, parent) {
if (name)
return name;
else {
if (parent) {
return `${parent.name}+`;
}
else {
return 'env';
}
}
}
export class Environment extends Instance {
constructor(name, parent) {
super(PlaceholderRecordEntry, DefaultModuleName, mkEnvName(name, parent), newInstanceAttributes());
this.activeUser = AdminUserId;
this.activeUserSet = false;
this.trashedResult = undefined;
this.returnFlag = false;
this.inUpsertMode = false;
this.inDeleteMode = false;
this.inKernelMode = false;
this.eventExecutor = undefined;
this.statementsExecutor = undefined;
this.scratchPad = undefined;
this.agentMode = undefined;
this.agentChatId = undefined;
this.monitor = undefined;
this.activeUserData = undefined;
if (parent !== undefined) {
this.parent = parent;
this.activeModule = parent.activeModule;
this.activeUser = parent.activeUser;
this.activeUserSet = parent.activeUserSet;
this.setActiveEvent(parent.getActiveEventInstance());
this.lastResult = parent.lastResult;
this.activeTransactions = parent.activeTransactions;
this.activeResolvers = parent.activeResolvers;
this.inUpsertMode = parent.inUpsertMode;
this.inKernelMode = parent.inKernelMode;
this.activeCatchHandlers = parent.activeCatchHandlers;
this.suspensionId = parent.suspensionId;
this.eventExecutor = parent.eventExecutor;
this.agentChatId = parent.agentChatId;
this.monitor = parent.monitor;
}
else {
this.activeModule = DefaultModuleName;
this.activeResolvers = new Map();
this.activeTransactions = new Map();
this.activeCatchHandlers = new Array();
this.attributes.set('process', process);
}
this.preGeneratedSuspensionId = crypto.randomUUID();
}
static from(parent, name, isAsync = false) {
const env = new Environment(name, parent);
if (isAsync) {
env.activeResolvers = new Map();
env.activeTransactions = new Map();
env.activeCatchHandlers = new Array();
env.preGeneratedSuspensionId = parent.preGeneratedSuspensionId;
}
return env;
}
static fromInstance(inst) {
const env = new Environment();
env.attributes = inst.attributes;
return env;
}
asSerializableObject() {
const obj = super.asSerializableObject();
obj.activeModule = this.activeModule;
if (this.activeEventInstance) {
obj.activeEventInstance = this.activeEventInstance.asSerializableObject();
}
obj.activeUser = this.activeUser;
obj.activeUserSet = this.activeUserSet;
obj.inUpsertMode = this.inUpsertMode;
obj.inDeleteMode = this.inDeleteMode;
obj.inKernelMode = this.inKernelMode;
if (this.parent) {
obj.parent = this.parent.asSerializableObject();
}
return obj;
}
static FromSerializableObject(obj) {
const inst = Instance.FromSerializableObject(obj, PlaceholderRecordEntry);
const env = Environment.fromInstance(inst);
env.activeModule = obj.activeModule;
if (obj.activeEventInstance) {
env.activeEventInstance = Instance.FromSerializableObject(obj.activeEventInstance);
}
env.activeUser = obj.activeUser;
env.activeUserSet = obj.activeUserSet;
env.inUpsertMode = obj.inUpsertMode;
env.inDeleteMode = obj.inDeleteMode;
env.inKernelMode = obj.inKernelMode;
if (obj.parent) {
env.parent = Environment.FromSerializableObject(obj.parent);
}
return env;
}
lookup(k) {
const v = this.attributes.get(k);
if (v === undefined) {
if (this.parent !== undefined) {
return this.parent.lookup(k);
}
else if (this == GlobalEnvironment) {
return EmptyResult;
}
else {
return GlobalEnvironment.lookup(k);
}
}
else
return v;
}
bind(k, v) {
this.attributes.set(k, v);
return this;
}
bindInstance(inst) {
const n = inst.name;
this.attributes.set(n, inst);
return this;
}
setFlowContext(s) {
this.attributes.set(Environment.FlowContextTag, s);
return this;
}
resetFlowContext() {
this.attributes.set(Environment.FlowContextTag, undefined);
return this;
}
getFlowContext() {
return this.attributes.get(Environment.FlowContextTag);
}
addToScratchPad(k, data) {
if (this.scratchPad === undefined) {
this.scratchPad = {};
}
if (isFqName(k)) {
const parts = nameToPath(k);
this.scratchPad[parts.getEntryName()] = data;
}
this.scratchPad[k] = data;
return this.addAttributesToScratchPad(data);
}
addAttributesToScratchPad(data) {
if (data instanceof Map) {
data.forEach((v, k) => {
this.addToScratchPad(k, v);
});
}
else if (data instanceof Array) {
return this;
}
else if (data instanceof Object) {
Object.keys(data).forEach((k) => {
this.addToScratchPad(k, data[k]);
});
}
return this;
}
getScratchPad() {
return this.scratchPad;
}
resetScratchPad() {
this.scratchPad = undefined;
return this;
}
setScratchPad(obj) {
this.scratchPad = obj;
return this;
}
setTemplateMapping(k, v) {
if (this.templateMappings === undefined) {
this.templateMappings = new Map();
}
this.templateMappings.set(k, v);
return this;
}
rewriteTemplateMappings(s) {
if (this.templateMappings !== undefined) {
this.templateMappings.keys().forEach((k) => {
var _a;
const tk = `{{${k}}}`;
const v = (_a = this.templateMappings) === null || _a === void 0 ? void 0 : _a.get(k);
if (v) {
const tv = `{{${v}}}`;
s = s.replaceAll(tk, tv);
}
});
}
return s;
}
resetTemplateMappings() {
var _a;
(_a = this.templateMappings) === null || _a === void 0 ? void 0 : _a.clear();
return this;
}
bindSuspensionUserData(userData) {
this.bind(Environment.SuspensionUserData, userData);
return this;
}
lookupSuspensionUserData() {
return this.lookup(Environment.SuspensionUserData);
}
maybeLookupAgentInstance(entryName) {
const v = this.lookup(entryName);
if (v && isInstanceOfType(v, AgentFqName)) {
return v;
}
else {
return undefined;
}
}
setActiveEvent(eventInst) {
if (eventInst) {
if (!isEventInstance(eventInst))
throw new Error(`Not an event instance - ${eventInst.name}`);
this.bindInstance(eventInst);
this.activeModule = eventInst.moduleName;
this.activeEventInstance = eventInst;
if (!this.activeUserSet) {
this.activeUser = eventInst.getAuthContextUserId();
this.activeUserSet = true;
}
}
return this;
}
getActiveEventInstance() {
return this.activeEventInstance;
}
isSuspended() {
return this.suspensionId !== undefined;
}
suspend() {
if (this.suspensionId === undefined) {
const id = this.preGeneratedSuspensionId;
this.propagateSuspension(id);
return id;
}
else {
return this.suspensionId;
}
}
releaseSuspension() {
this.suspensionId = undefined;
this.preGeneratedSuspensionId = crypto.randomUUID();
return this;
}
fetchSuspensionId() {
return this.preGeneratedSuspensionId;
}
markForReturn() {
if (this.parent) {
this.parent.markForReturn();
}
this.returnFlag = true;
return this;
}
isMarkedForReturn() {
return this.returnFlag;
}
propagateLastResult() {
if (this.parent) {
this.parent.lastResult = this.lastResult;
this.parent.propagateLastResult();
}
return this;
}
resetReturnFlag() {
if (this.returnFlag) {
this.returnFlag = false;
if (this.parent) {
this.parent.resetReturnFlag();
}
}
return this;
}
propagateSuspension(suspId) {
this.suspensionId = suspId;
if (this.parent) {
this.parent.propagateSuspension(suspId);
}
}
getSuspensionId() {
if (this.suspensionId) {
return this.suspensionId;
}
else {
throw new Error('SuspensionId is not set');
}
}
getActiveAuthContext() {
if (this.activeEventInstance) {
return this.activeEventInstance.getAuthContext();
}
return undefined;
}
getActiveToken() {
if (this.activeEventInstance) {
const sess = this.activeEventInstance.getAuthContext();
if (sess) {
return sess.sessionId;
}
}
return undefined;
}
setActiveUser(userId) {
this.activeUser = userId;
this.activeUserSet = true;
return this;
}
getActiveUser() {
return this.activeUser;
}
setLastResult(result) {
this.trashedResult = this.lastResult;
this.lastResult = result;
return this;
}
revokeLastResult() {
if (this.trashedResult !== undefined) {
this.lastResult = this.trashedResult;
this.trashedResult = undefined;
}
return this;
}
getLastResult() {
return this.lastResult;
}
getActiveModuleName() {
return this.activeModule;
}
switchActiveModuleName(newModuleName) {
const oldModuleName = this.activeModule;
this.activeModule = newModuleName;
return oldModuleName;
}
setParentPath(path) {
this.parentPath = path;
return this;
}
getParentPath() {
return this.parentPath;
}
setNormalizedParentPath(path) {
this.normalizedParentPath = path;
return this;
}
getNormalizedParentPath() {
return this.normalizedParentPath;
}
setBetweenRelInfo(info) {
this.betweenRelInfo = info;
return this;
}
getBetweenRelInfo() {
return this.betweenRelInfo;
}
setActiveResolvers(resolvers) {
this.activeResolvers = resolvers;
return this;
}
getActiveResolvers() {
return this.activeResolvers;
}
getResolver(resolverName) {
const r = this.getActiveResolvers().get(resolverName);
if (r) {
return r.setEnvironment(this);
}
return undefined;
}
async addResolver(resolver) {
this.getActiveResolvers().set(resolver.getName(), resolver);
await this.ensureTransactionForResolver(resolver);
resolver.setEnvironment(this);
return this;
}
setActiveTransactions(txns) {
this.activeTransactions = txns;
return this;
}
getActiveTransactions() {
return this.activeTransactions;
}
async resetActiveTransactions(commit) {
await this.endAllTransactions(commit);
this.activeTransactions = new Map();
return this;
}
async getTransactionForResolver(resolver) {
const n = resolver.getName();
let txnId = this.activeTransactions.get(n);
if (txnId) {
return txnId;
}
else {
txnId = await resolver.startTransaction();
if (txnId) {
this.activeTransactions.set(n, txnId);
return txnId;
}
else {
throw new Error(`Failed to start transaction for ${n}`);
}
}
}
async ensureTransactionForResolver(resolver) {
await this.getTransactionForResolver(resolver);
return this;
}
async endAllTransactions(commit) {
const txns = this.activeTransactions;
for (const n of txns.keys()) {
const txnId = txns.get(n);
if (txnId) {
const res = this.getResolver(n);
if (res) {
if (commit)
await res.commitTransaction(txnId);
else
await res.rollbackTransaction(txnId);
}
}
}
}
async callInTransaction(f) {
let result;
let commit = true;
await f()
.then((r) => {
result = r;
})
.catch((r) => {
commit = false;
result = r;
});
await this.endAllTransactions(commit);
if (!commit) {
throw result;
}
return result;
}
async commitAllTransactions() {
await this.endAllTransactions(true);
}
async rollbackAllTransactions() {
await this.endAllTransactions(false);
}
setInUpsertMode(flag) {
this.inUpsertMode = flag;
return this;
}
isInUpsertMode() {
return this.inUpsertMode;
}
setInDeleteMode(flag) {
this.inDeleteMode = flag;
return this;
}
isInDeleteMode() {
return this.inDeleteMode;
}
setInKernelMode(flag) {
this.inKernelMode = flag;
return this;
}
isInKernelMode() {
return this.inKernelMode;
}
pushHandlers(handlers) {
if (handlers.has('error')) {
this.activeCatchHandlers.push(handlers);
return true;
}
return false;
}
hasHandlers() {
return this.activeCatchHandlers.length > 0;
}
popHandlers() {
const r = this.activeCatchHandlers.pop();
if (r === undefined) {
throw new Error(`No more handlers to pop`);
}
return r;
}
setEventExecutor(exec) {
this.eventExecutor = exec;
return this;
}
getEventExecutor() {
return this.eventExecutor;
}
unsetEventExecutor() {
this.eventExecutor = undefined;
return this;
}
setStatementsExecutor(f) {
this.statementsExecutor = f;
return this;
}
getStatementsExecutor() {
return this.statementsExecutor;
}
async callWithStatementsExecutor(exec, f) {
const oldExec = this.statementsExecutor;
this.statementsExecutor = exec;
try {
return await f();
}
finally {
this.statementsExecutor = oldExec;
}
}
setActiveUserData(data) {
this.activeUserData = data;
return this;
}
getActiveUserData() {
return this.activeUserData;
}
inChatAgentMode() {
this.agentMode = 'chat';
return this;
}
inPlannerAgentMode() {
this.agentMode = 'planner';
return this;
}
resetAgentMode() {
this.agentMode = undefined;
return this;
}
isInAgentChatMode() {
return this.agentMode === 'chat';
}
isAgentModeSet() {
return this.agentMode !== undefined;
}
setAgentChatId(chatId) {
this.agentChatId = chatId;
return this;
}
getAgentChatId() {
return this.agentChatId;
}
appendEntryToMonitor(stmt) {
if (this.monitor === undefined) {
if (this.activeEventInstance && isCoreModule(this.activeEventInstance.moduleName)) {
return this;
}
this.monitor = new Monitor(this.activeEventInstance, this.activeUser);
}
this.monitor.addEntry(new MonitorEntry(stmt));
return this;
}
setMonitorEntryError(reason) {
if (this.monitor !== undefined) {
this.monitor.setEntryError(reason);
}
return this;
}
setMonitorEntryResult(result) {
if (this.monitor !== undefined) {
this.monitor.setEntryResult(result);
}
return this;
}
flagMonitorEntryAsLlm() {
if (this.monitor !== undefined) {
this.monitor.flagEntryAsLlm();
}
return this;
}
flagMonitorEntryAsPlanner() {
if (this.monitor !== undefined) {
this.monitor.flagEntryAsPlanner();
}
return this;
}
flagMonitorEntryAsFlow() {
if (this.monitor !== undefined) {
this.monitor.flagEntryAsFlow();
}
return this;
}
flagMonitorEntryAsFlowStep() {
if (this.monitor !== undefined) {
this.monitor.flagEntryAsFlowStep();
}
return this;
}
flagMonitorEntryAsDecision() {
if (this.monitor !== undefined) {
this.monitor.flagEntryAsDecision();
}
return this;
}
setMonitorEntryLlmPrompt(s) {
if (this.monitor !== undefined) {
this.monitor.setEntryLlmPrompt(s);
}
return this;
}
setMonitorEntryLlmResponse(s) {
if (this.monitor !== undefined) {
this.monitor.setEntryLlmResponse(s);
}
return this;
}
incrementMonitor() {
if (this.monitor !== undefined) {
this.monitor = this.monitor.increment();
}
return this;
}
decrementMonitor() {
if (this.monitor !== undefined) {
this.monitor = this.monitor.decrement();
}
return this;
}
setMonitorFlowResult() {
if (this.monitor !== undefined) {
this.monitor.setFlowResult(this.lastResult);
}
return this;
}
}
Environment.FlowContextTag = 'flow-context';
Environment.SuspensionUserData = '^';
export const GlobalEnvironment = new Environment();
export let evaluate = async function (eventInstance, continuation, activeEnv, kernelCall) {
let env;
let txnRolledBack = false;
try {
if (isEventInstance(eventInstance)) {
const wf = getWorkflow(eventInstance);
if (!isEmptyWorkflow(wf)) {
env = new Environment(eventInstance.name + '.env', activeEnv);
env.setActiveEvent(eventInstance);
if (kernelCall) {
env.setInKernelMode(true);
}
await evaluateStatements(wf.statements, env, continuation);
return env.getLastResult();
}
else if (isAgentEventInstance(eventInstance)) {
env = new Environment(eventInstance.name + '.env', activeEnv);
await handleAgentInvocation(eventInstance, env);
if (continuation)
continuation(env.getLastResult());
}
else if (isOpenApiEventInstance(eventInstance)) {
env = new Environment(eventInstance.name + '.env', activeEnv);
await handleOpenApiEvent(eventInstance, env);
const r = env.getLastResult();
if (continuation)
continuation(r);
return r;
}
else {
if (continuation)
continuation(null);
return null;
}
}
else {
throw new Error('Not an event - ' + eventInstance.name);
}
}
catch (err) {
if (env && env.hasHandlers()) {
throw err;
}
else {
if (env !== undefined && activeEnv === undefined) {
await env.rollbackAllTransactions().then(() => {
txnRolledBack = true;
});
}
throw err;
}
}
finally {
if (!txnRolledBack && env !== undefined && activeEnv === undefined) {
await env.commitAllTransactions();
}
if (isMonitoringEnabled()) {
await flushMonitoringData(eventInstance.getId());
}
}
};
export function setEvaluateFn(f) {
const oldf = evaluate;
evaluate = f;
return oldf;
}
export async function evaluateAsEvent(moduleName, eventName, attrs, activeSession, env, kernelCall) {
const finalAttrs = attrs instanceof Array ? new Map(attrs) : new Map(Object.entries(attrs));
const eventInst = makeInstance(moduleName, eventName, finalAttrs).setAuthContext(activeSession || AdminSession);
let result;
await evaluate(eventInst, (r) => (result = r), env, kernelCall);
return result;
}
export function makeEventEvaluator(moduleName) {
return async (eventName, attrs, env, session, kernelCall = true) => {
if (!env) {
env = new Environment();
}
return await evaluateAsEvent(moduleName, eventName, attrs, session, env, kernelCall);
};
}
export async function evaluateStatements(stmts, env, continuation) {
for (let i = 0; i < stmts.length; ++i) {
const stmt = stmts[i];
await evaluateStatement(stmt, env);
if (env.isMarkedForReturn()) {
break;
}
}
if (continuation !== undefined) {
continuation(env.getLastResult());
}
}
async function evaluateAsyncPattern(pat, thenStmts, handlers, hints, env) {
try {
await evaluatePattern(pat, env);
maybeBindStatementResultToAlias(hints, env);
if (env.isSuspended()) {
await createSuspension(env.fetchSuspensionId(), thenStmts.map((s) => {
if (s.$cstNode) {
return s.$cstNode.text;
}
else {
throw new Error('failed to extract code for suspension statement');
}
}), env);
}
else {
await evaluateStatements(thenStmts, env);
}
}
catch (reason) {
await env.rollbackAllTransactions();
await maybeHandleError(handlers, reason, env);
}
finally {
await env.commitAllTransactions();
}
}
export async function evaluateStatement(stmt, env) {
const hints = stmt.hints;
const hasHints = hints && hints.length > 0;
const thenStmts = hasHints ? maybeFindThenStatements(hints) : undefined;
const handlers = hasHints ? maybeFindHandlers(hints) : undefined;
if (thenStmts) {
evaluateAsyncPattern(stmt.pattern, thenStmts, handlers, hints, Environment.from(env, env.name + 'async', true));
env.setLastResult(env.fetchSuspensionId());
if (isReturn(stmt.pattern)) {
env.markForReturn();
}
if (hasHints) {
maybeBindStatementResultToAlias(hints, env);
}
return;
}
let handlersPushed = false;
try {
if (handlers) {
handlersPushed = env.pushHandlers(handlers);
}
await evaluatePattern(stmt.pattern, env);
if (hasHints) {
maybeBindStatementResultToAlias(hints, env);
}
await maybeHandleNotFound(handlers, env);
}
catch (reason) {
await maybeHandleError(handlers, reason, env);
}
finally {
if (handlersPushed && env.hasHandlers()) {
env.popHandlers();
}
}
}
async function maybeHandleNotFound(handlers, env) {
const lastResult = env.getLastResult();
if (lastResult === null ||
lastResult === undefined ||
(lastResult instanceof Array && lastResult.length == 0)) {
const onNotFound = handlers ? handlers.get('not_found') : undefined;
if (onNotFound) {
const newEnv = new Environment('not-found-env', env).unsetEventExecutor();
await evaluateStatement(onNotFound, newEnv);
env.setLastResult(newEnv.getLastResult());
}
}
}
async function maybeHandleError(handlers, reason, env) {
const handler = handlers ? handlers.get('error') : undefined;
if (handler) {
const newEnv = new Environment('handler-env', env).unsetEventExecutor();
await evaluateStatement(handler, newEnv);
env.setLastResult(newEnv.getLastResult());
}
else {
throw reason;
}
}
export function maybeBindStatementResultToAlias(hints, env) {
for (let i = 0; i < hints.length; ++i) {
const rh = hints[i];
if (rh.aliasSpec) {
if (rh.aliasSpec.alias !== undefined || rh.aliasSpec.aliases.length > 0) {
const result = env.getLastResult();
const alias = rh.aliasSpec.alias;
if (alias !== undefined) {
env.bind(alias, result);
}
else {
const aliases = rh.aliasSpec.aliases;
if (result instanceof Array) {
const resArr = result;
for (let i = 0; i < aliases.length; ++i) {
const k = aliases[i];
if (k == '__') {
env.bind(aliases[i + 1], resArr.splice(i));
break;
}
else if (k != '_') {
env.bind(aliases[i], resArr[i]);
}
}
}
else {
env.bind(aliases[0], result);
}
}
}
break;
}
}
}
function maybeFindHandlers(hints) {
for (let i = 0; i < hints.length; ++i) {
const rh = hints[i];
if (rh.catchSpec) {
const result = new Map();
rh.catchSpec.handlers.forEach((h) => {
result.set(h.except, h.stmt);
});
return result;
}
}
return undefined;
}
function maybeFindThenStatements(hints) {
for (let i = 0; i < hints.length; ++i) {
const rh = hints[i];
if (rh.thenSpec) {
return rh.thenSpec.statements;
}
}
return undefined;
}
export let parseAndEvaluateStatement = async function (stmtString, activeUserId, actievEnv) {
const env = actievEnv ? actievEnv : new Environment();
if (activeUserId) {
env.setActiveUser(activeUserId);
}
let commit = true;
try {
const stmt = await parseStatement(stmtString);
if (stmt) {
await evaluateStatement(stmt, env);
return env.getLastResult();
}
else {
commit = false;
}
}
catch (err) {
commit = false;
throw err;
}
finally {
if (!actievEnv) {
if (commit) {
await env.commitAllTransactions();
}
else {
await env.rollbackAllTransactions();
}
}
}
};
export function setParseAndEvaluateStatementFn(f) {
const oldf = parseAndEvaluateStatement;
parseAndEvaluateStatement = f;
return oldf;
}
export async function lookupAllInstances(entityFqName) {
return await parseAndEvaluateStatement(`{${entityFqName}? {}}`);
}
export class PatternHandler {
async handleExpression(expr, env) {
await evaluateExpression(expr, env);
}
async handleCrudMap(crudMap, env) {
await evaluateCrudMap(crudMap, env);
}
async handleForEach(forEach, env) {
await evaluateForEach(forEach, env);
}
async handleIf(_if, env) {
await evaluateIf(_if, env);
}
async handleDelete(del, env) {
await evaluateDelete(del, env);
}
async handlePurge(purge, env) {
await evaluatePurge(purge, env);
}
async handleFullTextSearch(fullTextSearch, env) {
await evaluateFullTextSearch(fullTextSearch, env);
}
async handleReturn(ret, env) {
await evaluatePattern(ret.pattern, env);
}
}
const DefaultPatternHandler = new PatternHandler();
export async function evaluatePattern(pat, env, handler = DefaultPatternHandler) {
if (pat.expr) {
await handler.handleExpression(pat.expr, env);
}
else if (pat.crudMap) {
await handler.handleCrudMap(pat.crudMap, env);
}
else if (pat.forEach) {
await handler.handleForEach(pat.forEach, env);
}
else if (pat.if) {
await handler.handleIf(pat.if, env);
}
else if (pat.delete) {
await handler.handleDelete(pat.delete, env);
}
else if (pat.purge) {
await handler.handlePurge(pat.purge, env);
}
else if (pat.fullTextSearch) {
await handler.handleFullTextSearch(pat.fullTextSearch, env);
}
else if (pat.return) {
await handler.handleReturn(pat.return, env);
env.markForReturn();
}
}
async function evaluateFullTextSearch(fts, env) {
let n = escapeQueryName(fts.name);
if (!isFqName(n)) {
const inst = env.getActiveEventInstance();
if (inst) {
n = makeFqName(inst.moduleName, n);
}
else {
throw new Error(`Fully qualified name required for full-text-search in ${n}`);
}
}
const path = nameToPath(n);
const entryName = path.getEntryName();
const moduleName = path.getModuleName();
const resolver = await getResolverForPath(entryName, moduleName, env);
await evaluateLiteral(fts.query, env);
const q = env.getLastResult();
if (!isString(q)) {
throw new Error(`Full text search query must be a string - ${q}`);
}
let options;
if (fts.options) {
await realizeMap(fts.options, env);
options = env.getLastResult();
}
env.setLastResult(await resolver.fullTextSearch(entryName, moduleName, q, options));
}
async function evaluateLiteral(lit, env) {
if (lit.id !== undefined)
env.setLastResult(env.lookup(lit.id));
else if (lit.ref !== undefined)
env.setLastResult(await followReference(env, lit.ref));
else if (lit.fnCall !== undefined)
await applyFn(lit.fnCall, env, false);
else if (lit.asyncFnCall !== undefined)
await applyFn(lit.asyncFnCall.fnCall, env, true);
else if (lit.array !== undefined)
await realizeArray(lit.array, env);
else if (lit.map !== undefined)
await realizeMap(lit.map, env);
else if (lit.num !== undefined)
env.setLastResult(lit.num);
else if (lit.str !== undefined)
env.setLastResult(restoreSpecialChars(lit.str));
else if (lit.bool !== undefined)
env.setLastResult(lit.bool == 'true' ? true : false);
}
function getMapKey(k) {
if (k.str !== undefined)
return k.str;
else if (k.num !== undefined)
return k.num;
else if (k.bool !== undefined)
return k.bool == 'true' ? true : false;
}
const DefaultResolverName = '-';
async function getResolverForPath(entryName, moduleName, env, isReadForUpdate = false, isReadForDelete = false) {
const fqEntryName = isFqName(entryName) ? entryName : makeFqName(moduleName, entryName);
const resN = getResolverNameForPath(fqEntryName);
let res;
if (resN === undefined) {
res = env.getResolver(DefaultResolverName);
if (res === undefined) {
res = new SqlDbResolver(DefaultResolverName);
await env.addResolver(res);
}
}
else {
res = env.getResolver(resN);
if (res === undefined) {
res = getResolver(fqEntryName);
await env.addResolver(res);
}
}
const authInfo = new ResolverAuthInfo(env.getActiveUser(), isReadForUpdate, isReadForDelete);
return res.setAuthInfo(authInfo);
}
async function lookupOneOfVals(fqName, env) {
return await parseAndEvaluateStatement(`{${fqName}? {}}`, undefined, env);
}
async function patternToInstance(entryName, attributes, env) {
const attrs = newInstanceAttributes();
let qattrs;
let qattrVals;
const isQueryAll = entryName.endsWith(QuerySuffix);
if (isQueryAll) {
entryName = entryName.slice(0, entryName.length - 1);
}
if (attributes) {
for (let i = 0; i < attributes.length; ++i) {
const a = attributes[i];
await evaluateExpression(a.value, env);
const v = env.getLastResult();
let aname = a.name;
if (aname.endsWith(QuerySuffix)) {
if (isQueryAll) {
throw new Error(`Cannot specifiy query attribute ${aname} here`);
}
if (qattrs === undefined)
qattrs = newInstanceAttributes();
if (qattrVals === undefined)
qattrVals = newInstanceAttributes();
aname = aname.slice(0, aname.length - 1);
qattrs.set(aname, a.op === undefined ? '=' : a.op);
qattrVals.set(aname, v);
}
else {
attrs.set(aname, v);
}
}
}
let moduleName = env.getActiveModuleName();
if (isFqName(entryName)) {
const p = nameToPath(entryName);
if (p.hasModule())
moduleName = p.getModuleName();
if (p.hasEntry())
entryName = p.getEntryName();
}
return makeInstance(moduleName, entryName, attrs, qattrs, qattrVals, isQueryAll);
}
async function instanceFromSource(crud, env) {
if (crud.source) {
await evaluateLiteral(crud.source, env);
const attrsSrc = env.getLastResult();
if (attrsSrc && attrsSrc instanceof Object) {
const attrs = new Map(Object.entries(attrsSrc));
const nparts = nameToPath(crud.name);
const n = nparts.getEntryName();
const m = nparts.hasModule() ? nparts.getModuleName() : env.getActiveModuleName();
return makeInstance(m, n, attrs);
}
else {
throw new Error(`Failed to initialize instance of ${crud.name}, expected a map after @from.`);
}
}
else {
throw new Error(`Cannot create instance of ${crud.name}, CRUD pattern does not specify a source map.`);
}
}
async function maybeValidateOneOfRefs(inst, env) {
const attrs = inst.record.oneOfRefAttributes;
if (!attrs)
return;
for (let i = 0; i < attrs.length; ++i) {
const n = attrs[i];
const v = inst.lookup(n);
if (v === undefined)
continue;
const attrSpec = inst.record.schema.get(n);
if (!attrSpec)
continue;
const r = getOneOfRef(attrSpec);
if (!r)
throw new Error(`Failed to fetch one-of-ref for ${n}`);
if (r) {
const parts = r.split('.');
const insts = await lookupOneOfVals(parts[0], env);
if (!insts || insts.length == 0) {
logger.warn(`No enum values set for ${n}`);
continue;
}
if (!insts.some((i) => {
return i.lookup(parts[1]) == v;
})) {
throw new Error(`Invalid enum-value ${v} for ${n}`);
}
}
}
}
async function evaluateCrudMap(crud, env) {
var _a, _b, _c;
if (!env.isInUpsertMode() && crud.upsert.length > 0) {
return await evaluateUpsert(crud, env);
}
const inst = crud.source
? await instanceFromSource(crud, env)
: await patternToInstance(crud.name, (_a = crud.body) === null || _a === void 0 ? void 0 : _a.attributes, env);
const entryName = inst.name;
const moduleName = inst.moduleName;
const attrs = inst.attributes;
const qattrs = inst.queryAttributes;
const isQueryAll = crud.name.endsWith(QuerySuffix);
const distinct = crud.distinct.length > 0;
if (attrs.size > 0) {
await maybeValidateOneOfRefs(inst, env);
}
if (crud.into) {
if (attrs.size > 0) {
throw new Error(`Query pattern for ${entryName} with 'into' clause cannot be used to update attributes`);
}
if (qattrs === undefined && !isQueryAll) {
throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
}
if (crud.join) {
await evaluateJoinQuery(crud.join, crud.into, inst, distinct, env);
}
else {
await evaluateJoinQueryWithRelationships(crud.into, inst, crud.relationships, distinct, env);
}
return;
}
if (isEntityInstance(inst) || isBetweenRelationship(inst.name, inst.moduleName)) {
if (qattrs === undefined && !isQueryAll) {
const parentPath = env.getParentPath();
if (parentPath) {
inst.attributes.set(PathAttributeName, parentPath);
inst.attributes.set(ParentAttributeName, env.getNormalizedParentPath() || '');
}
const res = await getResolverForPath(entryName, moduleName, env);
let r;
await computeExprAttributes(inst, undefined, undefined, env);
maybeSetMetaAttributes(inst.attributes, env);
if (env.isInUpsertMode()) {
await runPreUpdateEvents(inst, env);
r = await res.upsertInstance(inst);
await runPostUpdateEvents(inst, undefined, env);
}
else {
await runPreCreateEvents(inst, env);
if (isTimer(inst))
triggerTimer(inst);
r = await res.createInstance(inst);
await runPostCreateEvents(inst, env);
}
if (r && entryName == AgentEntityName && inst.moduleName == CoreAIModuleName) {
defineAgentEvent(env.getActiveModuleName(), r.lookup('name'), r.lookup('instruction'));
}
env.setLastResult(r);
const betRelInfo = env.getBetweenRelInfo();
if (betRelInfo) {
await res.connectInstances(betRelInfo.connectedInstance, env.getLastResult(), betRelInfo.relationship, env.isInUpsertMode());
}
if (crud.relationships !== undefined) {
for (let i = 0; i < crud.relationships.length; ++i) {
const rel = crud.relationships[i];
const relEntry = getRelationship(rel.name, moduleName);
const newEnv = Environment.from(env);
if (isContainsRelationship(rel.name, moduleName)) {
const ppath = inst.attributes.get(PathAttributeName);
newEnv.setParentPath(`${ppath}/${escapeFqName(relEntry.getFqName())}`);
newEnv.setNormalizedParentPath(ppath);
await evaluatePattern(rel.pattern, newEnv);
const lastInst = env.getLastResult();
lastInst.attachRelatedInstances(rel.name, newEnv.getLastResult());
}
else if (isBetweenRelationship(rel.name, moduleName)) {
const lastInst = env.getLastResult();
await evaluatePattern(rel.pattern, newEnv);
const relResult = newEnv.getLastResult();
const res = await getResolverForPath(rel.name, moduleName, env);
await res.connectInstances(lastInst, relResult, relEntry, env.isInUpsertMode());
lastInst.attachRelatedInstances(rel.name, newEnv.getLastResult());
}
}
}
}
else {
const parentPath = env.getParentPath();
const betRelInfo = env.getBetweenRelInfo();
const isReadForUpdate = attrs.size > 0;
let res = Resolver.Default;
if (parentPath !== undefined) {
res = await getResolverForPath(inst.name, inst.moduleName, env);
const insts = await res.queryChildInstances(parentPath, inst);
env.setLastResult(insts);
}
else if (betRelInfo !== undefined) {
res = await getResolverForPath(betRelInfo.relationship.name, betRelInfo.relationship.moduleName, env);
const insts = await res.queryConnectedInstances(betRelInfo.relationship, betRelInfo.connectedInstance, inst);
env.setLastResult(insts);
}
else {
res = await getResolverForPath(inst.name, inst.moduleName, env, isReadForUpdate, env.isInDeleteMode());
const insts = await res.queryInstances(inst, isQueryAll, distinct);
env.setLastResult(insts);
}
if (crud.relationships !== undefined) {
const lastRes = env.getLastResult();
for (let i = 0; i < crud.relationships.length; ++i) {
const rel = crud.relationships[i];
const relEntry = getRelationship(rel.name, moduleName);
for (let j = 0; j < lastRes.length; ++j) {
const newEnv = Environment.from(env);
if (isContainsRelationship(rel.name, moduleName)) {
const currInst = lastRes[j];
let ppath = '';
if (relEntry.isParent(currInst)) {
ppath = currInst.lookup(PathAttributeName);
newEnv.setParentPath(ppath + '/' + escapeFqName(relEntry.getFqName()));
}
else {
ppath = currInst.lookup(ParentAttributeName);
newEnv.setParentPath(ppath);
}
newEnv.setNormalizedParentPath(ppath);
await evaluatePattern(rel.pattern, newEnv);
lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
}
else if (isBetweenRelationship(rel.name, moduleName)) {
newEnv.setBetweenRelInfo({ relationship: relEntry, connectedInstance: lastRes[j] });
await evaluatePattern(rel.pattern, newEnv);
lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
}
}
}
}
if (isReadForUpdate) {
const lastRes = env.getLastResult();
if (lastRes instanceof Array) {
if (lastRes.length > 0) {
const resolver = await getResolverForPath(lastRes[0].name, lastRes[0].moduleName, env);
const res = new Array();
for (let i = 0; i < lastRes.length; ++i) {
await computeExprAttributes(lastRes[i], (_b = crud.body) === null || _b === void 0 ? void 0 : _b.attributes, attrs, env);
env.attributes.set('__patch', attrs);
await runPreUpdateEvents(lastRes[i], env);
maybeSetMetaAttributes(attrs, env, true);
const finalInst = await resolver.updateInstance(lastRes[i], attrs);
await runPostUpdateEvents(finalInst, lastRes[i], env);
res.push(finalInst);
}
env.setLastResult(res);
}
else {
env.setLastResult(lastRes);
}
}
else {
const res = await getResolverForPath(lastRes.name, lastRes.moduleName, env);
await computeExprAttributes(lastRes, (_c = crud.body) === null || _c === void 0 ? void 0 : _c.attributes, attrs, env);
await runPreUpdateEvents(lastRes, env);
const finalInst = await res.updateInstance(lastRes, attrs);
await runPostUpdateEvents(finalInst, lastRes, env);
env.setLastResult(finalInst);
}
}
}
}
else if (isEventInstance(inst)) {
if (isAgentEventInstance(inst))
await handleAgentInvocation(inst, env);
else if (isOpenApiEventInstance(inst))
await handleOpenApiEvent(inst, env);
else if (isDocEventInstance(inst))
await handleDocEvent(inst, env);
else {
const eventExec = env.getEventExecutor();
const newEnv = new Environment(`${inst.name}.env`, env);
if (eventExec) {
await eventExec(inst, newEnv);
env.setLastResult(newEnv.getLastResult());
}
else {
await evaluate(inst, (result) => env.setLastResult(result), newEnv);
}
env.resetReturnFlag();
}
}
else {
env.setLastResult(inst);
}
}
const CoreAIModuleName = makeCoreModuleName('ai');
export const DocEventName = `${CoreAIModuleName}/doc`;
function isDocEventInstance(inst) {
return isInstanceOfType(inst, DocEventName);
}
async function handleDocEvent(inst, env) {
const s = await fetchDoc(inst.lookup('url'));
if (s) {
const title = inst.lookup('title');
await parseAndEvaluateStatement(`{${CoreAIModuleName}/Document {title "${title}", content "${s}"}}`, undefined, env);
}
}
function triggerTimer(timerInst) {
const dur = timerInst.lookup('duration');
const unit = timerInst.lookup('unit');
let millisecs = 0;
switch (unit) {
case 'millisecond': {
millisecs = dur;
break;
}
case 'second': {
millisecs = dur * 1000;
break;
}
case 'minute': {
millisecs = dur * 60 * 1000;
break;
}
case 'hour': {
millisecs = dur * 60 * 60 * 1000;
break;
}
}
const eventName = nameToPath(timerInst.lookup('trigger'));
const m = eventName.hasModule() ? eventName.getModuleName() : timerInst.moduleName;
const n = eventName.getEntryName();
const inst = makeInstance(m, n, newInstanceAttributes());
const name = timerInst.lookup('name');
const timer = setInterval(async () => {
const env = new Environment();
try {
await evaluate(inst, (result) => logger.debug(`Timer ${name} ran with result ${result}`), env);
await env.commitAllTransactions();
await maybeCancelTimer(name, timer, env);
}
catch (reason) {
logger.error(`Timer ${name} raised error: ${reason}`);
}
}, millisecs);
setTimerRunning(timerInst);
return timerInst;
}
async function computeExprAttributes(inst, origAttrs, updatedAttrs, env) {
const exprAttrs = inst.getExprAttributes();
if (exprAttrs || origAttrs) {
const newEnv = new Environment('expr-env', env);
inst.attributes.forEach((v, k) => {
if (v !== undefined)
newEnv.bind(k, v);
});
updatedAttrs === null || updatedAttrs === void 0 ? void 0 : updatedAttrs.forEach((v, k) => {
if (v !== undefined)
newEnv.bind(k, v);
});
if (exprAttrs) {
const ks = [...exprAttrs.keys()];
for (let i = 0; i < ks