agentlang 0.0.8 → 0.0.11

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 (77) hide show
  1. package/out/api/http.js +1 -1
  2. package/out/api/http.js.map +1 -1
  3. package/out/language/generated/ast.d.ts +11 -2
  4. package/out/language/generated/ast.d.ts.map +1 -1
  5. package/out/language/generated/ast.js +15 -2
  6. package/out/language/generated/ast.js.map +1 -1
  7. package/out/language/generated/grammar.d.ts.map +1 -1
  8. package/out/language/generated/grammar.js +162 -119
  9. package/out/language/generated/grammar.js.map +1 -1
  10. package/out/language/main.cjs +174 -121
  11. package/out/language/main.cjs.map +2 -2
  12. package/out/language/parser.d.ts +1 -0
  13. package/out/language/parser.d.ts.map +1 -1
  14. package/out/language/parser.js +4 -0
  15. package/out/language/parser.js.map +1 -1
  16. package/out/language/syntax.js +2 -2
  17. package/out/language/syntax.js.map +1 -1
  18. package/out/runtime/agents/common.d.ts +1 -1
  19. package/out/runtime/agents/common.d.ts.map +1 -1
  20. package/out/runtime/agents/common.js +10 -10
  21. package/out/runtime/interpreter.d.ts +20 -1
  22. package/out/runtime/interpreter.d.ts.map +1 -1
  23. package/out/runtime/interpreter.js +160 -13
  24. package/out/runtime/interpreter.js.map +1 -1
  25. package/out/runtime/jsmodules.d.ts +2 -2
  26. package/out/runtime/jsmodules.d.ts.map +1 -1
  27. package/out/runtime/jsmodules.js +12 -4
  28. package/out/runtime/jsmodules.js.map +1 -1
  29. package/out/runtime/loader.d.ts +1 -1
  30. package/out/runtime/loader.d.ts.map +1 -1
  31. package/out/runtime/loader.js +10 -7
  32. package/out/runtime/loader.js.map +1 -1
  33. package/out/runtime/module.d.ts +4 -1
  34. package/out/runtime/module.d.ts.map +1 -1
  35. package/out/runtime/module.js +56 -11
  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 +17 -5
  40. package/out/runtime/modules/ai.js.map +1 -1
  41. package/out/runtime/modules/auth.d.ts.map +1 -1
  42. package/out/runtime/modules/auth.js +21 -16
  43. package/out/runtime/modules/auth.js.map +1 -1
  44. package/out/runtime/modules/core.d.ts +9 -0
  45. package/out/runtime/modules/core.d.ts.map +1 -1
  46. package/out/runtime/modules/core.js +107 -2
  47. package/out/runtime/modules/core.js.map +1 -1
  48. package/out/runtime/resolvers/interface.d.ts +4 -3
  49. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  50. package/out/runtime/resolvers/interface.js +4 -4
  51. package/out/runtime/resolvers/interface.js.map +1 -1
  52. package/out/runtime/resolvers/sqldb/impl.js +2 -2
  53. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  54. package/out/runtime/util.d.ts.map +1 -1
  55. package/out/runtime/util.js +3 -2
  56. package/out/runtime/util.js.map +1 -1
  57. package/out/syntaxes/agentlang.monarch.js +1 -1
  58. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/api/http.ts +1 -1
  61. package/src/language/agentlang.langium +7 -5
  62. package/src/language/generated/ast.ts +29 -5
  63. package/src/language/generated/grammar.ts +162 -119
  64. package/src/language/parser.ts +5 -0
  65. package/src/language/syntax.ts +2 -2
  66. package/src/runtime/agents/common.ts +10 -10
  67. package/src/runtime/interpreter.ts +177 -13
  68. package/src/runtime/jsmodules.ts +12 -4
  69. package/src/runtime/loader.ts +12 -6
  70. package/src/runtime/module.ts +61 -11
  71. package/src/runtime/modules/ai.ts +16 -5
  72. package/src/runtime/modules/auth.ts +21 -17
  73. package/src/runtime/modules/core.ts +144 -2
  74. package/src/runtime/resolvers/interface.ts +6 -6
  75. package/src/runtime/resolvers/sqldb/impl.ts +2 -2
  76. package/src/runtime/util.ts +4 -2
  77. package/src/syntaxes/agentlang.monarch.ts +1 -1
@@ -89,6 +89,11 @@ export async function parseStatement(stmt: string): Promise<Statement> {
89
89
  }
90
90
  }
91
91
 
92
+ export async function parseStatements(stmts: string[]): Promise<Statement[]> {
93
+ const wf = await parseWorkflow(`workflow W {${stmts.join(';\n')}}`);
94
+ return wf.statements;
95
+ }
96
+
92
97
  export async function parseWorkflow(workflowDef: string): Promise<WorkflowDefinition> {
93
98
  const mod = await parseModule(`module Temp ${workflowDef}`);
94
99
  if (isWorkflowDefinition(mod.defs[0])) {
@@ -39,9 +39,9 @@ export class BasePattern {
39
39
 
40
40
  private aliasesAsString(): string | undefined {
41
41
  if (this.alias) {
42
- return ` as ${this.alias}`;
42
+ return ` @as ${this.alias}`;
43
43
  } else if (this.aliases) {
44
- return ` as [${this.aliases.join(',')}]`;
44
+ return ` @as [${this.aliases.join(',')}]`;
45
45
  } else {
46
46
  return undefined;
47
47
  }
@@ -92,24 +92,24 @@ Note the value passed to the 'salary' attribute - it's an arithmetic expression.
92
92
  A successful query pattern will return an array of instances. The 'for' pattern can be used to iterate over an array. An example follows:
93
93
 
94
94
  workflow NotifyEmployees {
95
- {Erp/Employee {salary?> 1000}} as employees;
95
+ {Erp/Employee {salary?> 1000}} @as employees;
96
96
  for emp in employees {
97
97
  {Erp/SendMail {email emp.email, body "You are selected for an increment!"}}
98
98
  }
99
99
  }
100
100
 
101
- Also note the use of the 'as' keyword - this binds the result of a pattern to an 'alias'. Here the result of the query is bound to the
101
+ Also note the use of the '@as' keyword - this binds the result of a pattern to an 'alias'. Here the result of the query is bound to the
102
102
  alias named 'employees'. Any pattern can have an alias, including 'if' and 'for'. An alias can be used to refer to the attributes of the instance,
103
103
  via the dot(.) notation. Aliases can also be used to destructure a query result - here's an example:
104
104
 
105
105
  workflow FindFirstTwoEmployees {
106
- {Erp/Employee {salary?> 1000}} as [emp1, emp2];
106
+ {Erp/Employee {salary?> 1000}} @as [emp1, emp2];
107
107
  [emp1, emp2]
108
108
  }
109
109
 
110
110
  This alias will bind the first two instances to 'a' and 'b' and the rest of the instances to an array named 'xs':
111
111
 
112
- {SomeEntity {id?> 1}} as [a, b, _, xs]
112
+ {SomeEntity {id?> 1}} @as [a, b, _, xs]
113
113
 
114
114
  Examples of binding aliases to 'if' and 'for':
115
115
 
@@ -117,11 +117,11 @@ if (IncrementSalary.percentage > 10) {
117
117
  {Erp/Employee {employeeId IncrementSalary.employeeId, salary salary + salary * IncrementSalary.percentage}}
118
118
  } else {
119
119
  {Erp/Employee {employeeId IncrementSalary.employeeId, salary salary + 1500}}
120
- } as emp
120
+ } @as emp
121
121
 
122
122
  for emp in employees {
123
123
  {Erp/SendMail {email emp.email, body "You are selected for an increment!"}}
124
- } as emails
124
+ } @as emails
125
125
 
126
126
  Make sure all references based on a preceding pattern is based either on an actual alias or the name of the workflow. For example, the following sequence of patterns
127
127
  are invalid, because the alias 'employee' is not defined:
@@ -131,7 +131,7 @@ are invalid, because the alias 'employee' is not defined:
131
131
 
132
132
  A fix for the reference-error is shown below:
133
133
 
134
- {Employee {id? 101}} as employee;
134
+ {Employee {id? 101}} @as employee;
135
135
  {SendEmail {to employee.email, body "hello"}}
136
136
 
137
137
  Entities in a module can be connected together in relationships. There are two types of relationships - 'contains' and 'between'.
@@ -198,7 +198,7 @@ fill-in values from the available context. For example, if your instruction is "
198
198
  'please call me as soon as possible'", the best workflow to return is:
199
199
 
200
200
  workflow sendEmail {
201
- {employee {id? 101}} as emp;
201
+ {employee {id? 101}} @as emp;
202
202
  {email {to emp.email body "please call me as soon as possible"}}
203
203
  }
204
204
 
@@ -206,7 +206,7 @@ because all the information needed is available in the context. If the instructi
206
206
  'please call me as soon as possible'", then you can return:
207
207
 
208
208
  workflow sendEmail {
209
- {employee {id? sendEmail.employeeId}} as emp;
209
+ {employee {id? sendEmail.employeeId}} @as emp;
210
210
  {email {to emp.email body "please call me as soon as possible"}}
211
211
  }
212
212
 
@@ -215,7 +215,7 @@ The point is use the immediate context to fill-in values in generated patterns,
215
215
  Also generate a workflow only if required explicitly by the user or the contextual information is incomplete. Otherwise, just return an array of patterns.
216
216
  As an example, if the user request is "send an email to employee 101 with this message - 'please call me as soon as possible'", you must return:
217
217
 
218
- [{employee {id? 101}} as emp;
218
+ [{employee {id? 101}} @as emp;
219
219
  {email {to emp.email, body "please call me as soon as possible"}}]
220
220
 
221
221
  Note that each pattern in the array is separated by a ; and not a comma(,).
@@ -74,6 +74,7 @@ import {
74
74
  addCreateAudit,
75
75
  addDeleteAudit,
76
76
  addUpdateAudit,
77
+ createSuspension,
77
78
  maybeCancelTimer,
78
79
  setTimerRunning,
79
80
  } from './modules/core.js';
@@ -103,6 +104,8 @@ function mkEnvName(name: string | undefined, parent: Environment | undefined): s
103
104
  }
104
105
  }
105
106
 
107
+ type CatchHandlers = Map<string, Statement>;
108
+
106
109
  export class Environment extends Instance {
107
110
  parent: Environment | undefined;
108
111
 
@@ -111,6 +114,7 @@ export class Environment extends Instance {
111
114
  private activeUser: string = AdminUserId;
112
115
  private activeUserSet: boolean = false;
113
116
  private lastResult: Result;
117
+ private returnFlag: boolean = false;
114
118
  private parentPath: string | undefined;
115
119
  private normalizedParentPath: string | undefined;
116
120
  private betweenRelInfo: BetweenRelInfo | undefined;
@@ -119,6 +123,8 @@ export class Environment extends Instance {
119
123
  private inUpsertMode: boolean = false;
120
124
  private inDeleteMode: boolean = false;
121
125
  private inKernelMode: boolean = false;
126
+ private suspensionId: string | undefined;
127
+ private activeCatchHandlers: Array<CatchHandlers>;
122
128
 
123
129
  constructor(name?: string, parent?: Environment) {
124
130
  super(
@@ -138,10 +144,13 @@ export class Environment extends Instance {
138
144
  this.activeResolvers = parent.activeResolvers;
139
145
  this.inUpsertMode = parent.inUpsertMode;
140
146
  this.inKernelMode = parent.inKernelMode;
147
+ this.activeCatchHandlers = parent.activeCatchHandlers;
148
+ this.suspensionId = parent.suspensionId;
141
149
  } else {
142
150
  this.activeModule = DefaultModuleName;
143
151
  this.activeResolvers = new Map<string, Resolver>();
144
152
  this.activeTransactions = new Map<string, string>();
153
+ this.activeCatchHandlers = new Array<CatchHandlers>();
145
154
  }
146
155
  }
147
156
 
@@ -149,6 +158,47 @@ export class Environment extends Instance {
149
158
  return new Environment(undefined, parent);
150
159
  }
151
160
 
161
+ static fromInstance(inst: Instance): Environment {
162
+ const env = new Environment();
163
+ env.attributes = inst.attributes;
164
+ return env;
165
+ }
166
+
167
+ override asSerializableObject(): object {
168
+ const obj: any = super.asSerializableObject();
169
+ obj.activeModule = this.activeModule;
170
+ if (this.activeEventInstance) {
171
+ obj.activeEventInstance = this.activeEventInstance.asSerializableObject();
172
+ }
173
+ obj.activeUser = this.activeUser;
174
+ obj.activeUserSet = this.activeUserSet;
175
+ obj.inUpsertMode = this.inUpsertMode;
176
+ obj.inDeleteMode = this.inDeleteMode;
177
+ obj.inKernelMode = this.inKernelMode;
178
+ if (this.parent) {
179
+ obj.parent = this.parent.asSerializableObject();
180
+ }
181
+ return obj;
182
+ }
183
+
184
+ static override FromSerializableObject(obj: any): Environment {
185
+ const inst = Instance.FromSerializableObject(obj, PlaceholderRecordEntry);
186
+ const env = Environment.fromInstance(inst);
187
+ env.activeModule = obj.activeModule;
188
+ if (obj.activeEventInstance) {
189
+ env.activeEventInstance = Instance.FromSerializableObject(obj.activeEventInstance);
190
+ }
191
+ env.activeUser = obj.activeUser;
192
+ env.activeUserSet = obj.activeUserSet;
193
+ env.inUpsertMode = obj.inUpsertMode;
194
+ env.inDeleteMode = obj.inDeleteMode;
195
+ env.inKernelMode = obj.inKernelMode;
196
+ if (obj.parent) {
197
+ env.parent = Environment.FromSerializableObject(obj.parent);
198
+ }
199
+ return env;
200
+ }
201
+
152
202
  override lookup(k: string): Result {
153
203
  const v = this.attributes.get(k);
154
204
  if (v == undefined) {
@@ -173,6 +223,17 @@ export class Environment extends Instance {
173
223
  return this;
174
224
  }
175
225
 
226
+ static SuspensionUserData = '^';
227
+
228
+ bindSuspensionUserData(userData: string): Environment {
229
+ this.bind(Environment.SuspensionUserData, userData);
230
+ return this;
231
+ }
232
+
233
+ lookupSuspensionUserData(): string | undefined {
234
+ return this.lookup(Environment.SuspensionUserData);
235
+ }
236
+
176
237
  maybeLookupAgentInstance(entryName: string): Instance | undefined {
177
238
  const v = this.lookup(entryName);
178
239
  if (v && isInstanceOfType(v, AgentFqName)) {
@@ -200,6 +261,47 @@ export class Environment extends Instance {
200
261
  return this.activeEventInstance;
201
262
  }
202
263
 
264
+ isSuspended(): boolean {
265
+ return this.suspensionId != undefined;
266
+ }
267
+
268
+ suspend(): string {
269
+ if (this.suspensionId == undefined) {
270
+ const id = crypto.randomUUID();
271
+ this.propagateSuspension(id);
272
+ return id;
273
+ } else {
274
+ return this.suspensionId;
275
+ }
276
+ }
277
+
278
+ markForReturn(): Environment {
279
+ if (this.parent) {
280
+ this.parent.markForReturn();
281
+ }
282
+ this.returnFlag = true;
283
+ return this;
284
+ }
285
+
286
+ isMarkedForReturn(): boolean {
287
+ return this.returnFlag;
288
+ }
289
+
290
+ protected propagateSuspension(suspId: string) {
291
+ this.suspensionId = suspId;
292
+ if (this.parent) {
293
+ this.parent.propagateSuspension(suspId);
294
+ }
295
+ }
296
+
297
+ getSuspensionId(): string {
298
+ if (this.suspensionId) {
299
+ return this.suspensionId;
300
+ } else {
301
+ throw new Error('SuspensionId is not set');
302
+ }
303
+ }
304
+
203
305
  getActiveAuthContext(): ActiveSessionInfo | undefined {
204
306
  if (this.activeEventInstance) {
205
307
  return this.activeEventInstance.getAuthContext();
@@ -285,7 +387,7 @@ export class Environment extends Instance {
285
387
  getResolver(resolverName: string): Resolver | undefined {
286
388
  const r: Resolver | undefined = this.getActiveResolvers().get(resolverName);
287
389
  if (r) {
288
- return r.setUserData(this);
390
+ return r.setEnvironment(this);
289
391
  }
290
392
  return undefined;
291
393
  }
@@ -293,7 +395,7 @@ export class Environment extends Instance {
293
395
  async addResolver(resolver: Resolver): Promise<Environment> {
294
396
  this.getActiveResolvers().set(resolver.getName(), resolver);
295
397
  await this.ensureTransactionForResolver(resolver);
296
- resolver.setUserData(this);
398
+ resolver.setEnvironment(this);
297
399
  return this;
298
400
  }
299
401
 
@@ -393,6 +495,26 @@ export class Environment extends Instance {
393
495
  isInKernelMode(): boolean {
394
496
  return this.inKernelMode;
395
497
  }
498
+
499
+ pushHandlers(handlers: CatchHandlers): boolean {
500
+ if (handlers.has('error')) {
501
+ this.activeCatchHandlers.push(handlers);
502
+ return true;
503
+ }
504
+ return false;
505
+ }
506
+
507
+ hasHandlers(): boolean {
508
+ return this.activeCatchHandlers.length > 0;
509
+ }
510
+
511
+ popHandlers(): CatchHandlers {
512
+ const r = this.activeCatchHandlers.pop();
513
+ if (r == undefined) {
514
+ throw new Error(`No more handlers to pop`);
515
+ }
516
+ return r;
517
+ }
396
518
  }
397
519
 
398
520
  export const GlobalEnvironment = new Environment();
@@ -402,7 +524,7 @@ export async function evaluate(
402
524
  continuation?: Function,
403
525
  activeEnv?: Environment,
404
526
  kernelCall?: boolean
405
- ): Promise<void> {
527
+ ): Promise<Result> {
406
528
  let env: Environment | undefined;
407
529
  let txnRolledBack: boolean = false;
408
530
  try {
@@ -415,17 +537,22 @@ export async function evaluate(
415
537
  env.setInKernelMode(true);
416
538
  }
417
539
  await evaluateStatements(wf.statements, env, continuation);
540
+ return env.getLastResult();
418
541
  }
419
542
  } else {
420
543
  throw new Error('Not an event - ' + eventInstance.name);
421
544
  }
422
545
  } catch (err) {
423
- if (env != undefined && activeEnv == undefined) {
424
- await env.rollbackAllTransactions().then(() => {
425
- txnRolledBack = true;
426
- });
546
+ if (env && env.hasHandlers()) {
547
+ throw err;
548
+ } else {
549
+ if (env != undefined && activeEnv == undefined) {
550
+ await env.rollbackAllTransactions().then(() => {
551
+ txnRolledBack = true;
552
+ });
553
+ }
554
+ throw err;
427
555
  }
428
- throw err;
429
556
  } finally {
430
557
  if (!txnRolledBack && env != undefined && activeEnv == undefined) {
431
558
  await env.commitAllTransactions();
@@ -466,6 +593,14 @@ export function makeEventEvaluator(moduleName: string): Function {
466
593
  };
467
594
  }
468
595
 
596
+ function statemtentString(stmt: Statement): string {
597
+ if (stmt.$cstNode) {
598
+ return stmt.$cstNode.text;
599
+ } else {
600
+ throw new Error(`Failed to fetch text for statement - ${stmt}`);
601
+ }
602
+ }
603
+
469
604
  export async function evaluateStatements(
470
605
  stmts: Statement[],
471
606
  env: Environment,
@@ -473,6 +608,22 @@ export async function evaluateStatements(
473
608
  ) {
474
609
  for (let i = 0; i < stmts.length; ++i) {
475
610
  await evaluateStatement(stmts[i], env);
611
+ if (env.isSuspended()) {
612
+ const cont = stmts.slice(i + 1, stmts.length);
613
+ if (cont.length > 0) {
614
+ const suspId = await createSuspension(
615
+ env.getSuspensionId(),
616
+ cont.map((stmt: Statement) => {
617
+ return statemtentString(stmt);
618
+ }),
619
+ env
620
+ );
621
+ env.setLastResult({ suspension: suspId || 'null' });
622
+ break;
623
+ }
624
+ } else if (env.isMarkedForReturn()) {
625
+ break;
626
+ }
476
627
  }
477
628
  if (continuation != undefined) {
478
629
  continuation(env.getLastResult());
@@ -482,10 +633,12 @@ export async function evaluateStatements(
482
633
  async function evaluateStatement(stmt: Statement, env: Environment): Promise<void> {
483
634
  const hints = stmt.hints;
484
635
  const hasHints = hints && hints.length > 0;
485
- const handlers: Map<string, Statement> | undefined = hasHints
486
- ? maybeFindHandlers(hints)
487
- : undefined;
636
+ const handlers: CatchHandlers | undefined = hasHints ? maybeFindHandlers(hints) : undefined;
637
+ let handlersPushed = false;
488
638
  try {
639
+ if (handlers) {
640
+ handlersPushed = env.pushHandlers(handlers);
641
+ }
489
642
  await evaluatePattern(stmt.pattern, env);
490
643
  if (hasHints) {
491
644
  maybeBindStatementResultToAlias(hints, env);
@@ -508,6 +661,10 @@ async function evaluateStatement(stmt: Statement, env: Environment): Promise<voi
508
661
  } else {
509
662
  throw reason;
510
663
  }
664
+ } finally {
665
+ if (handlersPushed && env.hasHandlers()) {
666
+ env.popHandlers();
667
+ }
511
668
  }
512
669
  }
513
670
 
@@ -604,6 +761,9 @@ async function evaluatePattern(pat: Pattern, env: Environment): Promise<void> {
604
761
  await evaluatePurge(pat.purge, env);
605
762
  } else if (pat.fullTextSearch) {
606
763
  await evaluateFullTextSearch(pat.fullTextSearch, env);
764
+ } else if (pat.return) {
765
+ await evaluatePattern(pat.return.pat, env);
766
+ env.markForReturn();
607
767
  }
608
768
  }
609
769
 
@@ -652,7 +812,7 @@ function getMapKey(k: MapKey): Result {
652
812
  else if (k.bool != undefined) k.bool == 'true' ? true : false;
653
813
  }
654
814
 
655
- const DefaultResolverName: string = '--default-resolver--';
815
+ const DefaultResolverName: string = '-';
656
816
 
657
817
  async function getResolverForPath(
658
818
  entryName: string,
@@ -1384,7 +1544,11 @@ async function runPrePostEvents(
1384
1544
  logger.debug(`${prefix}: ${value}`);
1385
1545
  };
1386
1546
  const catchHandler = (reason: any) => {
1387
- logger.error(`${prefix}: ${reason}`);
1547
+ if (env.hasHandlers()) {
1548
+ throw reason;
1549
+ } else {
1550
+ logger.error(`${prefix}: ${reason}`);
1551
+ }
1388
1552
  };
1389
1553
  if (trigInfo.async) {
1390
1554
  evaluate(eventInst, callback).catch(catchHandler);
@@ -1,3 +1,4 @@
1
+ import { dirname, sep } from 'path';
1
2
  import { logger } from './logger.js';
2
3
  import { setSubscription } from './resolvers/registry.js';
3
4
  import { now, splitRefs } from './util.js';
@@ -5,12 +6,19 @@ import { now, splitRefs } from './util.js';
5
6
  const importedModules = new Map<string, any>();
6
7
 
7
8
  // Usage: importModule("./mymodels/acme.js")
8
- export async function importModule(path: string, name: string) {
9
+ export async function importModule(path: string, name: string, moduleFileName?: string) {
9
10
  if (importedModules.has(name)) {
10
11
  logger.warn(`Alias '${name}' will overwrite a previously imported module`);
11
12
  }
12
- if (!(path.startsWith('/') || path.startsWith('.'))) {
13
- path = process.cwd() + '/' + path;
13
+ if (moduleFileName) {
14
+ let s: string = dirname(moduleFileName);
15
+ if (s.startsWith('./')) {
16
+ s = s.substring(2);
17
+ }
18
+ path = `${s}${sep}${path}`;
19
+ }
20
+ if ((path.startsWith(sep) || path.startsWith('.')) && moduleFileName) {
21
+ path = process.cwd() + sep + path;
14
22
  }
15
23
  const m = await import(/* @vite-ignore */ path);
16
24
  importedModules.set(name, m);
@@ -26,7 +34,7 @@ export function moduleImported(moduleName: string): boolean {
26
34
 
27
35
  const ReservedImports = new Set<string>(['resolvers']);
28
36
 
29
- export function valiadteImportName(n: string) {
37
+ export function validateImportName(n: string) {
30
38
  if (ReservedImports.has(n)) {
31
39
  throw new Error(`${n} is an import reserved by the runtime`);
32
40
  }
@@ -45,6 +45,7 @@ import {
45
45
  addAgent,
46
46
  } from './module.js';
47
47
  import {
48
+ escapeSpecialChars,
48
49
  findRbacSchema,
49
50
  isString,
50
51
  makeFqName,
@@ -64,7 +65,7 @@ import { AgentEntityName, CoreAIModuleName, LlmEntityName } from './modules/ai.j
64
65
  import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
65
66
  import { registerResolver, setResolver, setSubscription } from './resolvers/registry.js';
66
67
  import { ConfigSchema } from './state.js';
67
- import { getModuleFn, importModule, valiadteImportName } from './jsmodules.js';
68
+ import { getModuleFn, importModule, validateImportName } from './jsmodules.js';
68
69
 
69
70
  export async function extractDocument(
70
71
  fileName: string,
@@ -258,7 +259,7 @@ async function loadModule(fileName: string, fsOptions?: any, callback?: Function
258
259
 
259
260
  // Extract the AST node
260
261
  const module = await extractAstNode<ModuleDefinition>(fileName, services);
261
- const result: Module = await internModule(module);
262
+ const result: Module = await internModule(module, fileName);
262
263
  console.log(chalk.green(`Module ${chalk.bold(result.name)} loaded`));
263
264
  logger.info(`Module ${result.name} loaded`);
264
265
  if (callback) {
@@ -446,8 +447,10 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
446
447
  hasUserLlm = true;
447
448
  }
448
449
  const ov = v;
449
- if (apdef.value.str || apdef.value.id || apdef.value.array) {
450
+ if (apdef.value.id || apdef.value.array) {
450
451
  v = `"${v}"`;
452
+ } else if (apdef.value.str) {
453
+ v = `"${escapeSpecialChars(v)}"`;
451
454
  }
452
455
  attrsStrs.push(`${apdef.name} ${v}`);
453
456
  attrs.set(apdef.name, ov);
@@ -544,12 +547,15 @@ export async function parseAndIntern(code: string, moduleName?: string) {
544
547
  await internModule(r.parseResult.value);
545
548
  }
546
549
 
547
- export async function internModule(module: ModuleDefinition): Promise<Module> {
550
+ export async function internModule(
551
+ module: ModuleDefinition,
552
+ moduleFileName?: string
553
+ ): Promise<Module> {
548
554
  const mn = module.name;
549
555
  const r = addModule(mn);
550
556
  module.imports.forEach(async (imp: Import) => {
551
- valiadteImportName(imp.name);
552
- await importModule(imp.path, imp.name);
557
+ validateImportName(imp.name);
558
+ await importModule(imp.path, imp.name, moduleFileName);
553
559
  });
554
560
  for (let i = 0; i < module.defs.length; ++i) {
555
561
  const def = module.defs[i];
@@ -469,6 +469,11 @@ export class Record extends ModuleEntry {
469
469
  });
470
470
  scms = `${scms},\n @rbac [${rbs.join(',\n')}]`;
471
471
  }
472
+ if (this.meta && this.meta.size > 0) {
473
+ const metaObj = Object.fromEntries(this.meta);
474
+ const ms = `@meta ${JSON.stringify(metaObj)}`;
475
+ scms = `${scms},\n ${ms}`;
476
+ }
472
477
  return s.concat('\n{', scms, '\n}\n');
473
478
  }
474
479
 
@@ -2189,21 +2194,66 @@ export class Instance {
2189
2194
  return this.lookup(PathAttributeName);
2190
2195
  }
2191
2196
 
2197
+ asSerializableObject(): object {
2198
+ const obj = {
2199
+ AL_INSTANCE: true,
2200
+ name: this.name,
2201
+ moduleName: this.moduleName,
2202
+ attributes: this.attributesAsObject(true),
2203
+ queryAttributes: this.queryAttributesAsObject(),
2204
+ queryAttributeValues: this.queryAttributeValuesAsObject(),
2205
+ };
2206
+ return obj;
2207
+ }
2208
+
2209
+ static FromSerializableObject(obj: any, record?: Record): Instance {
2210
+ if (obj.AL_INSTANCE == true) {
2211
+ const m = fetchModule(obj.moduleName);
2212
+ return new Instance(
2213
+ record || (m.getEntry(obj.name) as Record),
2214
+ obj.moduleName,
2215
+ obj.name,
2216
+ new Map(Object.entries(obj.attributes)),
2217
+ new Map(Object.entries(obj.queryAttributes)),
2218
+ new Map(Object.entries(obj.queryAttributeValues))
2219
+ );
2220
+ } else {
2221
+ throw new Error(`Cannot deserialize ${JSON.stringify(obj)} to an Instance`);
2222
+ }
2223
+ }
2224
+
2192
2225
  asObject(): object {
2193
2226
  const result: Map<string, object> = new Map<string, object>();
2194
- result.set(this.name, Object.fromEntries(this.attributes));
2227
+ result.set(this.name, this.attributesAsObject());
2195
2228
  return Object.fromEntries(result);
2196
2229
  }
2197
2230
 
2198
- attributesAsObject(stringifyObjects: boolean = true): object {
2199
- if (stringifyObjects) {
2200
- this.attributes.forEach((v: any, k: string) => {
2201
- if (v instanceof Object) {
2202
- this.attributes.set(k, JSON.stringify(v instanceof Map ? Object.fromEntries(v) : v));
2203
- }
2204
- });
2205
- }
2206
- return Object.fromEntries(this.attributes);
2231
+ attributesAsObject(forSerialization: boolean = false): object {
2232
+ const attrs = newInstanceAttributes();
2233
+ this.attributes.forEach((v: any, k: string) => {
2234
+ if (v instanceof Instance) {
2235
+ const inst = v as Instance;
2236
+ attrs.set(k, forSerialization ? inst.asSerializableObject() : inst.asObject());
2237
+ } else if (v instanceof Object) {
2238
+ const obj = v instanceof Map ? Object.fromEntries(v) : v;
2239
+ attrs.set(k, obj);
2240
+ } else {
2241
+ attrs.set(k, v);
2242
+ }
2243
+ });
2244
+ return Object.fromEntries(attrs);
2245
+ }
2246
+
2247
+ attributesWithStringifiedObjects(): object {
2248
+ const attrs = newInstanceAttributes();
2249
+ this.attributes.forEach((v: any, k: string) => {
2250
+ if (v instanceof Object) {
2251
+ attrs.set(k, JSON.stringify(v instanceof Map ? Object.fromEntries(v) : v));
2252
+ } else {
2253
+ attrs.set(k, v);
2254
+ }
2255
+ });
2256
+ return Object.fromEntries(attrs);
2207
2257
  }
2208
2258
 
2209
2259
  queryAttributesAsObject(): object {
@@ -2414,7 +2464,7 @@ export function makeInstance(
2414
2464
  throw new Error(`Invalid attribute ${key} specified for ${moduleName}/${entryName}`);
2415
2465
  }
2416
2466
  const spec: AttributeSpec = getAttributeSpec(schema, key);
2417
- validateType(key, value, spec);
2467
+ if (value != null && value != undefined) validateType(key, value, spec);
2418
2468
  });
2419
2469
  }
2420
2470
  if (!queryAttributes && !queryAll) {
@@ -31,8 +31,9 @@ entity ${AgentEntityName} {
31
31
  type @enum("chat", "planner") @default("chat"),
32
32
  runWorkflows Boolean @default(true),
33
33
  instruction String @optional,
34
- tools String @optional, // comma-separated values
35
- documents String @optional, // comma-separated values
34
+ tools String @optional, // comma-separated list of tool names
35
+ documents String @optional, // comma-separated list of document names
36
+ channels String @optional, // comma-separated list of channel names
36
37
  llm String
37
38
  }
38
39
 
@@ -42,7 +43,7 @@ entity agentChatSession {
42
43
  }
43
44
 
44
45
  workflow findAgentChatSession {
45
- {agentChatSession {id? findAgentChatSession.id}} as [sess];
46
+ {agentChatSession {id? findAgentChatSession.id}} @as [sess];
46
47
  sess
47
48
  }
48
49
 
@@ -69,6 +70,7 @@ export class AgentInstance {
69
70
  type: string = 'chat';
70
71
  tools: string | undefined;
71
72
  documents: string | undefined;
73
+ channels: string | undefined;
72
74
  runWorkflows: boolean = true;
73
75
 
74
76
  private constructor() {}
@@ -150,10 +152,19 @@ export class AgentInstance {
150
152
  }
151
153
 
152
154
  private toolsAsString(): string {
153
- if (this.tools) {
155
+ let finalTools: string | undefined = undefined;
156
+ if (this.tools) finalTools = this.tools;
157
+ if (this.channels) {
158
+ if (finalTools) {
159
+ finalTools = `${finalTools},${this.channels}`;
160
+ } else {
161
+ finalTools = this.channels;
162
+ }
163
+ }
164
+ if (finalTools) {
154
165
  const tooldefs = new Array<string>();
155
166
  const slimModules = new Map<string, string[]>();
156
- this.tools.split(',').forEach((n: string) => {
167
+ finalTools.split(',').forEach((n: string) => {
157
168
  let moduleName: string | undefined;
158
169
  let entryName: string | undefined;
159
170
  if (isFqName(n)) {