agentlang 0.0.19 → 0.0.20

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.
Files changed (82) hide show
  1. package/out/cli/main.d.ts.map +1 -1
  2. package/out/cli/main.js +18 -0
  3. package/out/cli/main.js.map +1 -1
  4. package/out/extension/main.cjs +250 -250
  5. package/out/extension/main.cjs.map +2 -2
  6. package/out/language/generated/ast.d.ts +23 -4
  7. package/out/language/generated/ast.d.ts.map +1 -1
  8. package/out/language/generated/ast.js +29 -1
  9. package/out/language/generated/ast.js.map +1 -1
  10. package/out/language/generated/grammar.d.ts.map +1 -1
  11. package/out/language/generated/grammar.js +251 -171
  12. package/out/language/generated/grammar.js.map +1 -1
  13. package/out/language/main.cjs +773 -674
  14. package/out/language/main.cjs.map +3 -3
  15. package/out/runtime/agents/common.d.ts +1 -1
  16. package/out/runtime/agents/common.d.ts.map +1 -1
  17. package/out/runtime/agents/common.js +4 -1
  18. package/out/runtime/agents/common.js.map +1 -1
  19. package/out/runtime/defs.d.ts +4 -0
  20. package/out/runtime/defs.d.ts.map +1 -1
  21. package/out/runtime/defs.js +8 -0
  22. package/out/runtime/defs.js.map +1 -1
  23. package/out/runtime/interpreter.d.ts.map +1 -1
  24. package/out/runtime/interpreter.js +48 -2
  25. package/out/runtime/interpreter.js.map +1 -1
  26. package/out/runtime/jsmodules.d.ts +0 -1
  27. package/out/runtime/jsmodules.d.ts.map +1 -1
  28. package/out/runtime/jsmodules.js +26 -34
  29. package/out/runtime/jsmodules.js.map +1 -1
  30. package/out/runtime/loader.d.ts.map +1 -1
  31. package/out/runtime/loader.js +42 -21
  32. package/out/runtime/loader.js.map +1 -1
  33. package/out/runtime/module.d.ts +2 -2
  34. package/out/runtime/module.d.ts.map +1 -1
  35. package/out/runtime/module.js +19 -3
  36. package/out/runtime/module.js.map +1 -1
  37. package/out/runtime/modules/ai.d.ts +1 -0
  38. package/out/runtime/modules/ai.d.ts.map +1 -1
  39. package/out/runtime/modules/ai.js +1 -0
  40. package/out/runtime/modules/ai.js.map +1 -1
  41. package/out/runtime/modules/core.js +1 -1
  42. package/out/runtime/modules/core.js.map +1 -1
  43. package/out/runtime/openapi.d.ts +16 -0
  44. package/out/runtime/openapi.d.ts.map +1 -0
  45. package/out/runtime/openapi.js +51 -0
  46. package/out/runtime/openapi.js.map +1 -0
  47. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  48. package/out/runtime/resolvers/interface.js +11 -5
  49. package/out/runtime/resolvers/interface.js.map +1 -1
  50. package/out/runtime/resolvers/registry.d.ts +1 -2
  51. package/out/runtime/resolvers/registry.d.ts.map +1 -1
  52. package/out/runtime/resolvers/registry.js +2 -1
  53. package/out/runtime/resolvers/registry.js.map +1 -1
  54. package/out/runtime/state.d.ts +23 -0
  55. package/out/runtime/state.d.ts.map +1 -1
  56. package/out/runtime/state.js +7 -0
  57. package/out/runtime/state.js.map +1 -1
  58. package/out/runtime/util.d.ts +1 -0
  59. package/out/runtime/util.d.ts.map +1 -1
  60. package/out/runtime/util.js +20 -0
  61. package/out/runtime/util.js.map +1 -1
  62. package/out/syntaxes/agentlang.monarch.js +1 -1
  63. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  64. package/package.json +2 -5
  65. package/src/cli/main.ts +19 -0
  66. package/src/language/agentlang.langium +9 -3
  67. package/src/language/generated/ast.ts +55 -4
  68. package/src/language/generated/grammar.ts +251 -171
  69. package/src/runtime/agents/common.ts +4 -1
  70. package/src/runtime/defs.ts +12 -0
  71. package/src/runtime/interpreter.ts +48 -1
  72. package/src/runtime/jsmodules.ts +26 -37
  73. package/src/runtime/loader.ts +46 -21
  74. package/src/runtime/module.ts +27 -3
  75. package/src/runtime/modules/ai.ts +2 -0
  76. package/src/runtime/modules/core.ts +1 -1
  77. package/src/runtime/openapi.ts +74 -0
  78. package/src/runtime/resolvers/interface.ts +14 -9
  79. package/src/runtime/resolvers/registry.ts +3 -2
  80. package/src/runtime/state.ts +9 -0
  81. package/src/runtime/util.ts +22 -0
  82. package/src/syntaxes/agentlang.monarch.ts +1 -1
@@ -145,9 +145,12 @@ are invalid, because the alias 'employee' is not defined:
145
145
 
146
146
  A fix for the reference-error is shown below:
147
147
 
148
- {Employee {id? 101}} @as employee;
148
+ {Employee {id? 101}} @as [employee];
149
149
  {SendEmail {to employee.email, body "hello"}}
150
150
 
151
+ Note that the alias for the query is '[employee]' so that the resultset is destructured to select exactly one instance of Employee
152
+ selected into the reference. You must follow this pattern if your goal is to select exactly a single instance.
153
+
151
154
  Keep in mind that the only valid syntax for the 'if' condition is:
152
155
 
153
156
  if (<expr>) {
@@ -76,3 +76,15 @@ export class CodeMismatchError extends Error {
76
76
  super(message || 'The verification code is incorrect. Please try again.', options);
77
77
  }
78
78
  }
79
+
80
+ export let FetchModuleFn: any = undefined;
81
+
82
+ export function setModuleFnFetcher(f: Function) {
83
+ FetchModuleFn = f;
84
+ }
85
+
86
+ export let SetSubscription: any = undefined;
87
+
88
+ export function setSubscriptionFn(f: Function) {
89
+ SetSubscription = f;
90
+ }
@@ -54,6 +54,7 @@ import {
54
54
  DefaultModuleName,
55
55
  escapeFqName,
56
56
  escapeQueryName,
57
+ escapeSpecialChars,
57
58
  fqNameFromPath,
58
59
  isFqName,
59
60
  isPath,
@@ -80,6 +81,7 @@ import {
80
81
  setTimerRunning,
81
82
  } from './modules/core.js';
82
83
  import { invokeModuleFn } from './jsmodules.js';
84
+ import { invokeOpenApiEvent, isOpenApiEventInstance } from './openapi.js';
83
85
 
84
86
  export type Result = any;
85
87
 
@@ -545,6 +547,15 @@ export async function evaluate(
545
547
  }
546
548
  await evaluateStatements(wf.statements, env, continuation);
547
549
  return env.getLastResult();
550
+ } else if (isOpenApiEventInstance(eventInstance)) {
551
+ env = new Environment(eventInstance.name + '.env', activeEnv);
552
+ await handleOpenApiEvent(eventInstance, env);
553
+ const r = env.getLastResult();
554
+ if (continuation) continuation(r);
555
+ return r;
556
+ } else {
557
+ if (continuation) continuation(null);
558
+ return null;
548
559
  }
549
560
  } else {
550
561
  throw new Error('Not an event - ' + eventInstance.name);
@@ -1180,6 +1191,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1180
1191
  }
1181
1192
  } else if (isEventInstance(inst)) {
1182
1193
  if (isAgentEventInstance(inst)) await handleAgentInvocation(inst, env);
1194
+ else if (isOpenApiEventInstance(inst)) await handleOpenApiEvent(inst, env);
1183
1195
  else await evaluate(inst, (result: Result) => env.setLastResult(result), env);
1184
1196
  } else {
1185
1197
  env.setLastResult(inst);
@@ -1350,11 +1362,37 @@ async function handleAgentInvocation(agentEventInst: Instance, env: Environment)
1350
1362
  }
1351
1363
  }
1352
1364
  }
1365
+ if (agent.output) {
1366
+ await pushToAgent(agent.output, env.getLastResult(), env);
1367
+ }
1353
1368
  } else {
1354
1369
  logger.warn(`Agent ${agent.name} failed to generate a response`);
1355
1370
  }
1356
1371
  }
1357
1372
 
1373
+ function agentInputAsString(result: any): string {
1374
+ if (!isString(result)) {
1375
+ if (result instanceof Instance) {
1376
+ return JSON.stringify((result as Instance).asObject());
1377
+ } else if (result instanceof Array) {
1378
+ return `[${(result as Array<any>)
1379
+ .map((r: any) => {
1380
+ return agentInputAsString(r);
1381
+ })
1382
+ .join(',')}]`;
1383
+ } else {
1384
+ return JSON.stringify(result);
1385
+ }
1386
+ }
1387
+ return result;
1388
+ }
1389
+
1390
+ async function pushToAgent(agentName: string, result: any, env: Environment) {
1391
+ const r = escapeSpecialChars(agentInputAsString(result));
1392
+ const pat = `{${agentName} {message "\n${r}"}}`;
1393
+ env.setLastResult(await parseAndEvaluateStatement(pat, undefined, env));
1394
+ }
1395
+
1358
1396
  function cleanupAgentResponse(response: string | undefined): string | undefined {
1359
1397
  if (response) {
1360
1398
  const resp = response.trim();
@@ -1387,6 +1425,15 @@ function cleanupAgentResponse(response: string | undefined): string | undefined
1387
1425
  }
1388
1426
  }
1389
1427
 
1428
+ async function handleOpenApiEvent(eventInst: Instance, env: Environment): Promise<void> {
1429
+ const r = await invokeOpenApiEvent(
1430
+ eventInst.moduleName,
1431
+ eventInst.name,
1432
+ eventInst.attributesAsObject()
1433
+ );
1434
+ env.setLastResult(r);
1435
+ }
1436
+
1390
1437
  async function evaluateUpsert(crud: CrudMap, env: Environment): Promise<void> {
1391
1438
  env.setInUpsertMode(true);
1392
1439
  try {
@@ -1650,7 +1697,7 @@ async function runPrePostEvents(
1650
1697
  if (env.hasHandlers()) {
1651
1698
  throw reason;
1652
1699
  } else {
1653
- logger.error(`${prefix}: ${reason}`);
1700
+ throw new Error(`${prefix}: ${reason}`);
1654
1701
  }
1655
1702
  };
1656
1703
  if (trigInfo.async) {
@@ -1,7 +1,7 @@
1
1
  import { logger } from './logger.js';
2
- import { setSubscription } from './resolvers/registry.js';
3
2
  import { now, splitRefs } from './util.js';
4
3
  import { isNodeEnv } from '../utils/runtime.js';
4
+ import { setModuleFnFetcher } from './defs.js';
5
5
 
6
6
  let dirname: any = undefined;
7
7
  let sep: any = undefined;
@@ -26,39 +26,37 @@ const importedModules = new Map<string, any>();
26
26
 
27
27
  // Usage: importModule("./mymodels/acme.js")
28
28
  export async function importModule(path: string, name: string, moduleFileName?: string) {
29
- if (importedModules.has(name)) {
30
- logger.warn(`Alias '${name}' will overwrite a previously imported module`);
31
- }
32
- if (moduleFileName) {
33
- let s: string = dirname(moduleFileName);
34
- if (s.startsWith('./')) {
35
- s = s.substring(2);
29
+ if (isNodeEnv) {
30
+ if (importedModules.has(name)) {
31
+ logger.warn(`Alias '${name}' will overwrite a previously imported module`);
36
32
  }
37
- path = `${s}${sep}${path}`;
38
- }
39
- if (!(path.startsWith(sep) || path.startsWith('.'))) {
40
- path = process.cwd() + sep + path;
33
+ if (moduleFileName) {
34
+ let s: string = dirname(moduleFileName);
35
+ if (s.startsWith('./')) {
36
+ s = s.substring(2);
37
+ } else if (s == '.') {
38
+ s = process.cwd();
39
+ }
40
+ path = `${s}${sep}${path}`;
41
+ }
42
+ if (!(path.startsWith(sep) || path.startsWith('.'))) {
43
+ path = process.cwd() + sep + path;
44
+ }
45
+ const m = await import(/* @vite-ignore */ path);
46
+ importedModules.set(name, m);
47
+ // e.g of dynamic fn-call:
48
+ //// let f = eval("(a, b) => m.add(a, b)");
49
+ //// console.log(f(10, 20))
50
+ return m;
51
+ } else {
52
+ return {};
41
53
  }
42
- const m = await import(/* @vite-ignore */ path);
43
- importedModules.set(name, m);
44
- // e.g of dynamic fn-call:
45
- //// let f = eval("(a, b) => m.add(a, b)");
46
- //// console.log(f(10, 20))
47
- return m;
48
54
  }
49
55
 
50
56
  export function moduleImported(moduleName: string): boolean {
51
57
  return importedModules.has(moduleName);
52
58
  }
53
59
 
54
- const ReservedImports = new Set<string>(['resolvers']);
55
-
56
- export function validateImportName(n: string) {
57
- if (ReservedImports.has(n)) {
58
- throw new Error(`${n} is an import reserved by the runtime`);
59
- }
60
- }
61
-
62
60
  function maybeEvalFunction(fnName: string): Function | undefined {
63
61
  try {
64
62
  return eval(fnName);
@@ -68,14 +66,6 @@ function maybeEvalFunction(fnName: string): Function | undefined {
68
66
  }
69
67
  }
70
68
 
71
- function invokeReservedFn(moduleName: string, fnName: string, args: Array<any> | null) {
72
- if (moduleName == 'resolvers' && fnName == 'setSubscription' && args != null) {
73
- return setSubscription(args[0], args[1]);
74
- } else {
75
- throw new Error(`Failed to call ${moduleName}.${fnName} with the given arguments`);
76
- }
77
- }
78
-
79
69
  async function invokeBuiltInFn(fnName: string, args: Array<any> | null, isAsync: boolean = false) {
80
70
  if (fnName == 'now') {
81
71
  return now();
@@ -112,9 +102,6 @@ export async function invokeModuleFn(
112
102
  }
113
103
  }
114
104
  const mname = refs[0];
115
- if (ReservedImports.has(mname)) {
116
- return invokeReservedFn(mname, refs[1], args);
117
- }
118
105
  const m = importedModules.get(mname);
119
106
  if (m != undefined) {
120
107
  const f = m[refs[1]];
@@ -151,3 +138,5 @@ export function getModuleFn(fqFnName: string): Function | undefined {
151
138
  return m[refs[1]];
152
139
  } else return undefined;
153
140
  }
141
+
142
+ setModuleFnFetcher(getModuleFn);
@@ -63,9 +63,11 @@ import { Environment, evaluateStatements, GlobalEnvironment } from './interprete
63
63
  import { createPermission, createRole } from './modules/auth.js';
64
64
  import { AgentEntityName, CoreAIModuleName, LlmEntityName } from './modules/ai.js';
65
65
  import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
66
- import { registerResolver, setResolver, setSubscription } from './resolvers/registry.js';
66
+ import { registerResolver, setResolver } from './resolvers/registry.js';
67
67
  import { ConfigSchema } from './state.js';
68
- import { getModuleFn, importModule, validateImportName } from './jsmodules.js';
68
+ import { getModuleFn, importModule } from './jsmodules.js';
69
+ import { SetSubscription } from './defs.js';
70
+ import { ExtendedFileSystem } from '../utils/fs/interfaces.js';
69
71
 
70
72
  export async function extractDocument(
71
73
  fileName: string,
@@ -135,6 +137,31 @@ export const DefaultAppSpec: ApplicationSpec = {
135
137
  version: '0.0.1',
136
138
  };
137
139
 
140
+ async function getAllModules(
141
+ dir: string,
142
+ fs: ExtendedFileSystem,
143
+ drill: boolean = true
144
+ ): Promise<string[]> {
145
+ let alFiles = new Array<string>();
146
+ if (!(await fs.exists(dir))) {
147
+ return alFiles;
148
+ }
149
+ const directoryContents = await fs.readdir(dir);
150
+ for (let i = 0; i < directoryContents.length; ++i) {
151
+ const file = directoryContents[i];
152
+ if (path.extname(file).toLowerCase() == '.al') {
153
+ alFiles.push(dir + path.sep + file);
154
+ } else if (drill) {
155
+ const fullPath = dir + path.sep + file;
156
+ const stat = await fs.stat(fullPath);
157
+ if (stat.isDirectory()) {
158
+ alFiles = alFiles.concat(await getAllModules(fullPath, fs));
159
+ }
160
+ }
161
+ }
162
+ return alFiles;
163
+ }
164
+
138
165
  async function loadApp(appDir: string, fsOptions?: any, callback?: Function): Promise<string> {
139
166
  // Initialize filesystem if not already done
140
167
  const fs = await getFileSystem(fsOptions);
@@ -142,19 +169,11 @@ async function loadApp(appDir: string, fsOptions?: any, callback?: Function): Pr
142
169
  const appJsonFile = `${appDir}${path.sep}package.json`;
143
170
  const s: string = await fs.readFile(appJsonFile);
144
171
  const appSpec: ApplicationSpec = JSON.parse(s);
145
- const alFiles: Array<string> = new Array<string>();
146
- const directoryContents = await fs.readdir(appDir);
147
172
  let lastModuleLoaded: string = '';
148
173
  async function cont2() {
149
- if (!directoryContents) {
150
- console.error(chalk.red(`Directory ${appDir} does not exist or is empty.`));
151
- return;
152
- }
153
- directoryContents.forEach(file => {
154
- if (path.extname(file).toLowerCase() == '.al') {
155
- alFiles.push(appDir + path.sep + file);
156
- }
157
- });
174
+ const fls01 = await getAllModules(appDir, fs, false);
175
+ const fls02 = await getAllModules(appDir + path.sep + 'src', fs);
176
+ const alFiles = fls01.concat(fls02);
158
177
  for (let i = 0; i < alFiles.length; ++i) {
159
178
  lastModuleLoaded = (await loadModule(alFiles[i], fsOptions)).name;
160
179
  }
@@ -164,7 +183,10 @@ async function loadApp(appDir: string, fsOptions?: any, callback?: Function): Pr
164
183
  for (const [depName, _] of Object.entries(appSpec.dependencies)) {
165
184
  try {
166
185
  const depDirName = `./node_modules/${depName}`;
167
- const files = await fs.readdir(depDirName);
186
+ const fls01 = await fs.readdir(depDirName);
187
+ const srcDir = depDirName + path.sep + 'src';
188
+ const hasSrc = await fs.exists(srcDir);
189
+ const files = hasSrc ? fls01.concat(await fs.readdir(srcDir)) : fls01;
168
190
  if (
169
191
  files.find(file => {
170
192
  return path.extname(file).toLowerCase() == '.al';
@@ -356,7 +378,7 @@ export function addRelationshipFromDef(
356
378
  }
357
379
 
358
380
  export function addWorkflowFromDef(def: WorkflowDefinition, moduleName: string): Workflow {
359
- return addWorkflow(def.name, moduleName, def.statements);
381
+ return addWorkflow(def.name, moduleName, def.statements, def.hints);
360
382
  }
361
383
 
362
384
  const StandaloneStatements = new Map<string, Statement[]>();
@@ -489,7 +511,7 @@ function addResolverDefinition(def: ResolverDefinition, moduleName: string) {
489
511
  resolver.subs = {
490
512
  subscribe: subsFn,
491
513
  };
492
- if (subsEvent) setSubscription(subsEvent, resolverName);
514
+ if (subsEvent) SetSubscription(subsEvent, resolverName);
493
515
  resolver.subscribe();
494
516
  }
495
517
  });
@@ -537,7 +559,6 @@ export async function internModule(
537
559
  const mn = module.name;
538
560
  const r = addModule(mn);
539
561
  module.imports.forEach(async (imp: Import) => {
540
- validateImportName(imp.name);
541
562
  await importModule(imp.path, imp.name, moduleFileName);
542
563
  });
543
564
  for (let i = 0; i < module.defs.length; ++i) {
@@ -569,11 +590,15 @@ export async function loadRawConfig(
569
590
  fsOptions?: any
570
591
  ): Promise<any> {
571
592
  const fs = await getFileSystem(fsOptions);
572
- let rawConfig = preprocessRawConfig(JSON.parse(await fs.readFile(configFileName)));
573
- if (validate) {
574
- rawConfig = ConfigSchema.parse(rawConfig);
593
+ if (await fs.exists(configFileName)) {
594
+ let rawConfig = preprocessRawConfig(JSON.parse(await fs.readFile(configFileName)));
595
+ if (validate) {
596
+ rawConfig = ConfigSchema.parse(rawConfig);
597
+ }
598
+ return rawConfig;
599
+ } else {
600
+ return { service: { port: 8080 } };
575
601
  }
576
- return rawConfig;
577
602
  }
578
603
 
579
604
  export function generateRawConfig(configObj: any): string {
@@ -19,6 +19,7 @@ import {
19
19
  RbacSpecEntry,
20
20
  RbacSpecEntries,
21
21
  RbacOpr,
22
+ WorkflowHint,
22
23
  } from '../language/generated/ast.js';
23
24
  import {
24
25
  Path,
@@ -43,7 +44,12 @@ import {
43
44
  } from './util.js';
44
45
  import { parseStatement } from '../language/parser.js';
45
46
  import { ActiveSessionInfo, AdminSession } from './auth/defs.js';
46
- import { DefaultIdAttributeName, PathAttributeName } from './defs.js';
47
+ import {
48
+ DefaultIdAttributeName,
49
+ FetchModuleFn,
50
+ PathAttributeName,
51
+ SetSubscription,
52
+ } from './defs.js';
47
53
  import { logger } from './logger.js';
48
54
 
49
55
  export class ModuleEntry {
@@ -1594,6 +1600,7 @@ export const propertyNames = new Set([
1594
1600
  '@indexed',
1595
1601
  '@default',
1596
1602
  '@optional',
1603
+ '@check',
1597
1604
  '@unique',
1598
1605
  '@autoincrement',
1599
1606
  '@array',
@@ -1856,7 +1863,8 @@ function normalizeWorkflowName(n: string): string {
1856
1863
  export function addWorkflow(
1857
1864
  name: string,
1858
1865
  moduleName = activeModule,
1859
- statements?: Statement[]
1866
+ statements?: Statement[],
1867
+ hints?: WorkflowHint[]
1860
1868
  ): Workflow {
1861
1869
  const module: Module = fetchModule(moduleName);
1862
1870
  if (module.hasEntry(name)) {
@@ -1869,6 +1877,13 @@ export function addWorkflow(
1869
1877
  event.addMeta(SystemDefinedEvent, 'true');
1870
1878
  }
1871
1879
  if (!statements) statements = new Array<Statement>();
1880
+ if (hints && hints.length > 0) {
1881
+ hints.forEach((hint: WorkflowHint) => {
1882
+ if (hint.subs) {
1883
+ SetSubscription(makeFqName(moduleName, name), hint.subs.resolverName);
1884
+ }
1885
+ });
1886
+ }
1872
1887
  return module.addEntry(new Workflow(asWorkflowName(name), statements, moduleName)) as Workflow;
1873
1888
  }
1874
1889
 
@@ -2103,13 +2118,22 @@ function checkOneOfValue(attrSpec: AttributeSpec, attrName: string, attrValue: a
2103
2118
  return false;
2104
2119
  }
2105
2120
 
2121
+ function getCheckPredicate(attrSpec: AttributeSpec): any {
2122
+ const p = getAnyProperty('check', attrSpec);
2123
+ if (isString(p)) {
2124
+ return FetchModuleFn(p);
2125
+ }
2126
+ return undefined;
2127
+ }
2128
+
2106
2129
  function validateType(attrName: string, attrValue: any, attrSpec: AttributeSpec) {
2107
2130
  if (attrSpec.type == 'Path') {
2108
2131
  if (!isPath(attrValue, getRefSpec(attrSpec))) {
2109
2132
  throw new Error(`Failed to validate Path ${attrValue} passed to ${attrName}`);
2110
2133
  }
2111
2134
  }
2112
- const predic = builtInChecks.get(attrSpec.type);
2135
+ let predic = getCheckPredicate(attrSpec);
2136
+ predic = predic ? predic : builtInChecks.get(attrSpec.type);
2113
2137
  if (predic != undefined) {
2114
2138
  if (isArrayAttribute(attrSpec)) {
2115
2139
  if (!(attrValue instanceof Array)) {
@@ -34,6 +34,7 @@ entity ${AgentEntityName} {
34
34
  tools String @optional, // comma-separated list of tool names
35
35
  documents String @optional, // comma-separated list of document names
36
36
  channels String @optional, // comma-separated list of channel names
37
+ output String @optional, // fq-name of another agent to which the result will be pushed
37
38
  llm String
38
39
  }
39
40
 
@@ -72,6 +73,7 @@ export class AgentInstance {
72
73
  documents: string | undefined;
73
74
  channels: string | undefined;
74
75
  runWorkflows: boolean = true;
76
+ output: string | undefined;
75
77
 
76
78
  private constructor() {}
77
79
 
@@ -48,7 +48,7 @@ entity activeSuspension {
48
48
  id UUID @id
49
49
  }
50
50
 
51
- resolver servicenow ["${DefaultModuleName}/activeSuspension"] {
51
+ resolver suspensionResolver ["${DefaultModuleName}/activeSuspension"] {
52
52
  query Core.lookupActiveSuspension
53
53
  }
54
54
 
@@ -0,0 +1,74 @@
1
+ import { parseAndIntern } from './loader.js';
2
+ import { logger } from './logger.js';
3
+ import { Instance } from './module.js';
4
+ import { isReservedName } from './util.js';
5
+ import { OpenAPIClient, OpenAPIClientAxios } from 'openapi-client-axios';
6
+
7
+ export type OpenApiHandle = {
8
+ api: OpenAPIClientAxios;
9
+ client: OpenAPIClient;
10
+ };
11
+
12
+ let OpenApiModules: Map<string, OpenApiHandle> | undefined = undefined;
13
+
14
+ export async function registerOpenApiModule(
15
+ moduleName: string,
16
+ handle: OpenApiHandle
17
+ ): Promise<string> {
18
+ if (OpenApiModules == undefined) {
19
+ OpenApiModules = new Map();
20
+ }
21
+ const m = new Map(Object.entries(handle.client));
22
+ const events = new Array<string>();
23
+ m.forEach((v: any, k: string) => {
24
+ if (v instanceof Function) {
25
+ if (isReservedName(k)) {
26
+ k = `_${k}`;
27
+ }
28
+ logger.debug(`OpenAPI event: ${moduleName}/${k}`);
29
+ events.push(
30
+ `event ${k} {parameters Any @optional, data Any @optional, config Any @optional}`
31
+ );
32
+ }
33
+ });
34
+ await parseAndIntern(`module ${moduleName}\n${events.join('\n')}`);
35
+ OpenApiModules.set(moduleName, handle);
36
+ return moduleName;
37
+ }
38
+
39
+ export function isOpenApiModule(moduleName: string): boolean {
40
+ return OpenApiModules != undefined && OpenApiModules.has(moduleName);
41
+ }
42
+
43
+ export type OpenApiArgs = {
44
+ parameters?: any;
45
+ data?: any;
46
+ config?: any;
47
+ };
48
+
49
+ export async function invokeOpenApiEvent(
50
+ moduleName: string,
51
+ eventName: string,
52
+ params: OpenApiArgs
53
+ ): Promise<any> {
54
+ if (OpenApiModules) {
55
+ const handle = OpenApiModules.get(moduleName);
56
+ if (handle) {
57
+ const f = handle.client[eventName];
58
+ if (!f) {
59
+ throw new Error(`No event ${eventName} found in ${moduleName}`);
60
+ } else {
61
+ const r: any = await f(params.parameters, params.data, params.config);
62
+ return r.data;
63
+ }
64
+ } else {
65
+ throw new Error(`No OpenAPI module found - ${moduleName}`);
66
+ }
67
+ } else {
68
+ throw new Error(`OpenAPI module ${moduleName} not initialized`);
69
+ }
70
+ }
71
+
72
+ export function isOpenApiEventInstance(eventInst: Instance): boolean {
73
+ return isOpenApiModule(eventInst.moduleName);
74
+ }
@@ -193,15 +193,20 @@ export class Resolver {
193
193
 
194
194
  public async onSubscription(result: any): Promise<any> {
195
195
  if (result != undefined) {
196
- const eventName = getSubscriptionEvent(this.name);
197
- if (eventName) {
198
- const path = splitFqName(eventName);
199
- const inst = makeInstance(
200
- path.getModuleName(),
201
- path.getEntryName(),
202
- newInstanceAttributes().set('data', result)
203
- );
204
- return await evaluate(inst);
196
+ try {
197
+ const eventName = getSubscriptionEvent(this.name);
198
+ if (eventName) {
199
+ const path = splitFqName(eventName);
200
+ const inst = makeInstance(
201
+ path.getModuleName(),
202
+ path.getEntryName(),
203
+ newInstanceAttributes().set('data', result)
204
+ );
205
+ return await evaluate(inst);
206
+ }
207
+ } catch (err: any) {
208
+ logger.error(`Resolver ${this.name} raised error in onSubscription handler: ${err}`);
209
+ return undefined;
205
210
  }
206
211
  }
207
212
  }
@@ -1,3 +1,4 @@
1
+ import { setSubscriptionFn } from '../defs.js';
1
2
  import { Resolver, setSubscriptionEvent } from './interface.js';
2
3
 
3
4
  type MakeResolver = () => Resolver;
@@ -17,8 +18,6 @@ export function setResolver(fqEntryName: string, resolverName: string) {
17
18
  }
18
19
  }
19
20
 
20
- export const setSubscription = setSubscriptionEvent;
21
-
22
21
  export function getResolverNameForPath(fqEntryName: string): string | undefined {
23
22
  return resolverPathMappings.get(fqEntryName);
24
23
  }
@@ -31,3 +30,5 @@ export function getResolver(fqEntryName: string): Resolver {
31
30
  }
32
31
  throw new Error(`No resolver registered for ${fqEntryName}`);
33
32
  }
33
+
34
+ setSubscriptionFn(setSubscriptionEvent);
@@ -93,6 +93,15 @@ export const ConfigSchema = z.object({
93
93
  }),
94
94
  ])
95
95
  .optional(),
96
+ openapi: z
97
+ .array(
98
+ z.object({
99
+ specUrl: z.string(),
100
+ baseUrl: z.string().optional(),
101
+ name: z.string(),
102
+ })
103
+ )
104
+ .optional(),
96
105
  });
97
106
 
98
107
  export type Config = z.infer<typeof ConfigSchema>;
@@ -472,3 +472,25 @@ export function areSetsEqual<T>(set1: Set<T>, set2: Set<T>): boolean {
472
472
  }
473
473
  return true;
474
474
  }
475
+
476
+ const ReservedNames = new Set([
477
+ 'if',
478
+ 'else',
479
+ 'for',
480
+ 'or',
481
+ 'and',
482
+ 'entity',
483
+ 'record',
484
+ 'event',
485
+ 'workflow',
486
+ 'create',
487
+ 'delete',
488
+ 'update',
489
+ 'upsert',
490
+ 'agent',
491
+ 'resolver',
492
+ ]);
493
+
494
+ export function isReservedName(s: string): boolean {
495
+ return ReservedNames.has(s);
496
+ }
@@ -1,7 +1,7 @@
1
1
  // Monarch syntax highlighting for the agentlang language.
2
2
  export default {
3
3
  keywords: [
4
- '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
4
+ '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@subs','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
5
5
  ],
6
6
  operators: [
7
7
  '!=','*','+',',','-','.','/',':',';','<','<=','<>','=','>','>=','?','@'