agentlang 0.0.8 → 0.0.10

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 (51) hide show
  1. package/out/api/http.js +1 -1
  2. package/out/api/http.js.map +1 -1
  3. package/out/language/parser.d.ts +1 -0
  4. package/out/language/parser.d.ts.map +1 -1
  5. package/out/language/parser.js +4 -0
  6. package/out/language/parser.js.map +1 -1
  7. package/out/runtime/interpreter.d.ts +14 -1
  8. package/out/runtime/interpreter.d.ts.map +1 -1
  9. package/out/runtime/interpreter.js +134 -13
  10. package/out/runtime/interpreter.js.map +1 -1
  11. package/out/runtime/jsmodules.d.ts +2 -2
  12. package/out/runtime/jsmodules.d.ts.map +1 -1
  13. package/out/runtime/jsmodules.js +12 -4
  14. package/out/runtime/jsmodules.js.map +1 -1
  15. package/out/runtime/loader.d.ts +1 -1
  16. package/out/runtime/loader.d.ts.map +1 -1
  17. package/out/runtime/loader.js +5 -5
  18. package/out/runtime/loader.js.map +1 -1
  19. package/out/runtime/module.d.ts +4 -1
  20. package/out/runtime/module.d.ts.map +1 -1
  21. package/out/runtime/module.js +56 -11
  22. package/out/runtime/module.js.map +1 -1
  23. package/out/runtime/modules/ai.d.ts +1 -0
  24. package/out/runtime/modules/ai.d.ts.map +1 -1
  25. package/out/runtime/modules/ai.js +16 -4
  26. package/out/runtime/modules/ai.js.map +1 -1
  27. package/out/runtime/modules/core.d.ts +7 -0
  28. package/out/runtime/modules/core.d.ts.map +1 -1
  29. package/out/runtime/modules/core.js +80 -2
  30. package/out/runtime/modules/core.js.map +1 -1
  31. package/out/runtime/resolvers/interface.d.ts +4 -3
  32. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  33. package/out/runtime/resolvers/interface.js +4 -4
  34. package/out/runtime/resolvers/interface.js.map +1 -1
  35. package/out/runtime/resolvers/sqldb/impl.js +2 -2
  36. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  37. package/out/runtime/util.d.ts.map +1 -1
  38. package/out/runtime/util.js +3 -2
  39. package/out/runtime/util.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/api/http.ts +1 -1
  42. package/src/language/parser.ts +5 -0
  43. package/src/runtime/interpreter.ts +148 -13
  44. package/src/runtime/jsmodules.ts +12 -4
  45. package/src/runtime/loader.ts +8 -5
  46. package/src/runtime/module.ts +61 -11
  47. package/src/runtime/modules/ai.ts +15 -4
  48. package/src/runtime/modules/core.ts +109 -2
  49. package/src/runtime/resolvers/interface.ts +6 -6
  50. package/src/runtime/resolvers/sqldb/impl.ts +2 -2
  51. package/src/runtime/util.ts +4 -2
@@ -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
 
@@ -119,6 +122,8 @@ export class Environment extends Instance {
119
122
  private inUpsertMode: boolean = false;
120
123
  private inDeleteMode: boolean = false;
121
124
  private inKernelMode: boolean = false;
125
+ private suspensionId: string | undefined;
126
+ private activeCatchHandlers: Array<CatchHandlers>;
122
127
 
123
128
  constructor(name?: string, parent?: Environment) {
124
129
  super(
@@ -138,10 +143,13 @@ export class Environment extends Instance {
138
143
  this.activeResolvers = parent.activeResolvers;
139
144
  this.inUpsertMode = parent.inUpsertMode;
140
145
  this.inKernelMode = parent.inKernelMode;
146
+ this.activeCatchHandlers = parent.activeCatchHandlers;
147
+ this.suspensionId = parent.suspensionId;
141
148
  } else {
142
149
  this.activeModule = DefaultModuleName;
143
150
  this.activeResolvers = new Map<string, Resolver>();
144
151
  this.activeTransactions = new Map<string, string>();
152
+ this.activeCatchHandlers = new Array<CatchHandlers>();
145
153
  }
146
154
  }
147
155
 
@@ -149,6 +157,47 @@ export class Environment extends Instance {
149
157
  return new Environment(undefined, parent);
150
158
  }
151
159
 
160
+ static fromInstance(inst: Instance): Environment {
161
+ const env = new Environment();
162
+ env.attributes = inst.attributes;
163
+ return env;
164
+ }
165
+
166
+ override asSerializableObject(): object {
167
+ const obj: any = super.asSerializableObject();
168
+ obj.activeModule = this.activeModule;
169
+ if (this.activeEventInstance) {
170
+ obj.activeEventInstance = this.activeEventInstance.asSerializableObject();
171
+ }
172
+ obj.activeUser = this.activeUser;
173
+ obj.activeUserSet = this.activeUserSet;
174
+ obj.inUpsertMode = this.inUpsertMode;
175
+ obj.inDeleteMode = this.inDeleteMode;
176
+ obj.inKernelMode = this.inKernelMode;
177
+ if (this.parent) {
178
+ obj.parent = this.parent.asSerializableObject();
179
+ }
180
+ return obj;
181
+ }
182
+
183
+ static override FromSerializableObject(obj: any): Environment {
184
+ const inst = Instance.FromSerializableObject(obj, PlaceholderRecordEntry);
185
+ const env = Environment.fromInstance(inst);
186
+ env.activeModule = obj.activeModule;
187
+ if (obj.activeEventInstance) {
188
+ env.activeEventInstance = Instance.FromSerializableObject(obj.activeEventInstance);
189
+ }
190
+ env.activeUser = obj.activeUser;
191
+ env.activeUserSet = obj.activeUserSet;
192
+ env.inUpsertMode = obj.inUpsertMode;
193
+ env.inDeleteMode = obj.inDeleteMode;
194
+ env.inKernelMode = obj.inKernelMode;
195
+ if (obj.parent) {
196
+ env.parent = Environment.FromSerializableObject(obj.parent);
197
+ }
198
+ return env;
199
+ }
200
+
152
201
  override lookup(k: string): Result {
153
202
  const v = this.attributes.get(k);
154
203
  if (v == undefined) {
@@ -200,6 +249,35 @@ export class Environment extends Instance {
200
249
  return this.activeEventInstance;
201
250
  }
202
251
 
252
+ isSuspended(): boolean {
253
+ return this.suspensionId != undefined;
254
+ }
255
+
256
+ suspend(): string {
257
+ if (this.suspensionId == undefined) {
258
+ const id = crypto.randomUUID();
259
+ this.propagateSuspension(id);
260
+ return id;
261
+ } else {
262
+ return this.suspensionId;
263
+ }
264
+ }
265
+
266
+ protected propagateSuspension(suspId: string) {
267
+ this.suspensionId = suspId;
268
+ if (this.parent) {
269
+ this.parent.propagateSuspension(suspId);
270
+ }
271
+ }
272
+
273
+ getSuspensionId(): string {
274
+ if (this.suspensionId) {
275
+ return this.suspensionId;
276
+ } else {
277
+ throw new Error('SuspensionId is not set');
278
+ }
279
+ }
280
+
203
281
  getActiveAuthContext(): ActiveSessionInfo | undefined {
204
282
  if (this.activeEventInstance) {
205
283
  return this.activeEventInstance.getAuthContext();
@@ -285,7 +363,7 @@ export class Environment extends Instance {
285
363
  getResolver(resolverName: string): Resolver | undefined {
286
364
  const r: Resolver | undefined = this.getActiveResolvers().get(resolverName);
287
365
  if (r) {
288
- return r.setUserData(this);
366
+ return r.setEnvironment(this);
289
367
  }
290
368
  return undefined;
291
369
  }
@@ -293,7 +371,7 @@ export class Environment extends Instance {
293
371
  async addResolver(resolver: Resolver): Promise<Environment> {
294
372
  this.getActiveResolvers().set(resolver.getName(), resolver);
295
373
  await this.ensureTransactionForResolver(resolver);
296
- resolver.setUserData(this);
374
+ resolver.setEnvironment(this);
297
375
  return this;
298
376
  }
299
377
 
@@ -393,6 +471,26 @@ export class Environment extends Instance {
393
471
  isInKernelMode(): boolean {
394
472
  return this.inKernelMode;
395
473
  }
474
+
475
+ pushHandlers(handlers: CatchHandlers): boolean {
476
+ if (handlers.has('error')) {
477
+ this.activeCatchHandlers.push(handlers);
478
+ return true;
479
+ }
480
+ return false;
481
+ }
482
+
483
+ hasHandlers(): boolean {
484
+ return this.activeCatchHandlers.length > 0;
485
+ }
486
+
487
+ popHandlers(): CatchHandlers {
488
+ const r = this.activeCatchHandlers.pop();
489
+ if (r == undefined) {
490
+ throw new Error(`No more handlers to pop`);
491
+ }
492
+ return r;
493
+ }
396
494
  }
397
495
 
398
496
  export const GlobalEnvironment = new Environment();
@@ -402,7 +500,7 @@ export async function evaluate(
402
500
  continuation?: Function,
403
501
  activeEnv?: Environment,
404
502
  kernelCall?: boolean
405
- ): Promise<void> {
503
+ ): Promise<Result> {
406
504
  let env: Environment | undefined;
407
505
  let txnRolledBack: boolean = false;
408
506
  try {
@@ -415,17 +513,22 @@ export async function evaluate(
415
513
  env.setInKernelMode(true);
416
514
  }
417
515
  await evaluateStatements(wf.statements, env, continuation);
516
+ return env.getLastResult();
418
517
  }
419
518
  } else {
420
519
  throw new Error('Not an event - ' + eventInstance.name);
421
520
  }
422
521
  } catch (err) {
423
- if (env != undefined && activeEnv == undefined) {
424
- await env.rollbackAllTransactions().then(() => {
425
- txnRolledBack = true;
426
- });
522
+ if (env && env.hasHandlers()) {
523
+ throw err;
524
+ } else {
525
+ if (env != undefined && activeEnv == undefined) {
526
+ await env.rollbackAllTransactions().then(() => {
527
+ txnRolledBack = true;
528
+ });
529
+ }
530
+ throw err;
427
531
  }
428
- throw err;
429
532
  } finally {
430
533
  if (!txnRolledBack && env != undefined && activeEnv == undefined) {
431
534
  await env.commitAllTransactions();
@@ -466,6 +569,14 @@ export function makeEventEvaluator(moduleName: string): Function {
466
569
  };
467
570
  }
468
571
 
572
+ function statemtentString(stmt: Statement): string {
573
+ if (stmt.$cstNode) {
574
+ return stmt.$cstNode.text;
575
+ } else {
576
+ throw new Error(`Failed to fetch text for statement - ${stmt}`);
577
+ }
578
+ }
579
+
469
580
  export async function evaluateStatements(
470
581
  stmts: Statement[],
471
582
  env: Environment,
@@ -473,6 +584,20 @@ export async function evaluateStatements(
473
584
  ) {
474
585
  for (let i = 0; i < stmts.length; ++i) {
475
586
  await evaluateStatement(stmts[i], env);
587
+ if (env.isSuspended()) {
588
+ const cont = stmts.slice(i + 1, stmts.length);
589
+ if (cont.length > 0) {
590
+ const suspId = await createSuspension(
591
+ env.getSuspensionId(),
592
+ cont.map((stmt: Statement) => {
593
+ return statemtentString(stmt);
594
+ }),
595
+ env
596
+ );
597
+ env.setLastResult({ suspension: suspId || 'null' });
598
+ break;
599
+ }
600
+ }
476
601
  }
477
602
  if (continuation != undefined) {
478
603
  continuation(env.getLastResult());
@@ -482,10 +607,12 @@ export async function evaluateStatements(
482
607
  async function evaluateStatement(stmt: Statement, env: Environment): Promise<void> {
483
608
  const hints = stmt.hints;
484
609
  const hasHints = hints && hints.length > 0;
485
- const handlers: Map<string, Statement> | undefined = hasHints
486
- ? maybeFindHandlers(hints)
487
- : undefined;
610
+ const handlers: CatchHandlers | undefined = hasHints ? maybeFindHandlers(hints) : undefined;
611
+ let handlersPushed = false;
488
612
  try {
613
+ if (handlers) {
614
+ handlersPushed = env.pushHandlers(handlers);
615
+ }
489
616
  await evaluatePattern(stmt.pattern, env);
490
617
  if (hasHints) {
491
618
  maybeBindStatementResultToAlias(hints, env);
@@ -508,6 +635,10 @@ async function evaluateStatement(stmt: Statement, env: Environment): Promise<voi
508
635
  } else {
509
636
  throw reason;
510
637
  }
638
+ } finally {
639
+ if (handlersPushed && env.hasHandlers()) {
640
+ env.popHandlers();
641
+ }
511
642
  }
512
643
  }
513
644
 
@@ -652,7 +783,7 @@ function getMapKey(k: MapKey): Result {
652
783
  else if (k.bool != undefined) k.bool == 'true' ? true : false;
653
784
  }
654
785
 
655
- const DefaultResolverName: string = '--default-resolver--';
786
+ const DefaultResolverName: string = '-';
656
787
 
657
788
  async function getResolverForPath(
658
789
  entryName: string,
@@ -1384,7 +1515,11 @@ async function runPrePostEvents(
1384
1515
  logger.debug(`${prefix}: ${value}`);
1385
1516
  };
1386
1517
  const catchHandler = (reason: any) => {
1387
- logger.error(`${prefix}: ${reason}`);
1518
+ if (env.hasHandlers()) {
1519
+ throw reason;
1520
+ } else {
1521
+ logger.error(`${prefix}: ${reason}`);
1522
+ }
1388
1523
  };
1389
1524
  if (trigInfo.async) {
1390
1525
  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('.'))) {
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
  }
@@ -64,7 +64,7 @@ import { AgentEntityName, CoreAIModuleName, LlmEntityName } from './modules/ai.j
64
64
  import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
65
65
  import { registerResolver, setResolver, setSubscription } from './resolvers/registry.js';
66
66
  import { ConfigSchema } from './state.js';
67
- import { getModuleFn, importModule, valiadteImportName } from './jsmodules.js';
67
+ import { getModuleFn, importModule, validateImportName } from './jsmodules.js';
68
68
 
69
69
  export async function extractDocument(
70
70
  fileName: string,
@@ -258,7 +258,7 @@ async function loadModule(fileName: string, fsOptions?: any, callback?: Function
258
258
 
259
259
  // Extract the AST node
260
260
  const module = await extractAstNode<ModuleDefinition>(fileName, services);
261
- const result: Module = await internModule(module);
261
+ const result: Module = await internModule(module, fileName);
262
262
  console.log(chalk.green(`Module ${chalk.bold(result.name)} loaded`));
263
263
  logger.info(`Module ${result.name} loaded`);
264
264
  if (callback) {
@@ -544,12 +544,15 @@ export async function parseAndIntern(code: string, moduleName?: string) {
544
544
  await internModule(r.parseResult.value);
545
545
  }
546
546
 
547
- export async function internModule(module: ModuleDefinition): Promise<Module> {
547
+ export async function internModule(
548
+ module: ModuleDefinition,
549
+ moduleFileName?: string
550
+ ): Promise<Module> {
548
551
  const mn = module.name;
549
552
  const r = addModule(mn);
550
553
  module.imports.forEach(async (imp: Import) => {
551
- valiadteImportName(imp.name);
552
- await importModule(imp.path, imp.name);
554
+ validateImportName(imp.name);
555
+ await importModule(imp.path, imp.name, moduleFileName);
553
556
  });
554
557
  for (let i = 0; i < module.defs.length; ++i) {
555
558
  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
 
@@ -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)) {
@@ -1,11 +1,21 @@
1
1
  import { default as ai } from './ai.js';
2
2
  import { default as auth } from './auth.js';
3
3
  import { DefaultModuleName, DefaultModules } from '../util.js';
4
- import { Instance, isInstanceOfType } from '../module.js';
5
- import { Environment, parseAndEvaluateStatement } from '../interpreter.js';
4
+ import { Instance, isInstanceOfType, makeInstance, newInstanceAttributes } from '../module.js';
5
+ import {
6
+ Environment,
7
+ evaluate,
8
+ evaluateStatements,
9
+ parseAndEvaluateStatement,
10
+ } from '../interpreter.js';
6
11
  import { logger } from '../logger.js';
12
+ import { Statement } from '../../language/generated/ast.js';
13
+ import { parseStatements } from '../../language/parser.js';
7
14
 
8
15
  const CoreModuleDefinition = `module ${DefaultModuleName}
16
+
17
+ import "./modules/core.js" as Core
18
+
9
19
  entity timer {
10
20
  name String @id,
11
21
  duration Int,
@@ -23,6 +33,26 @@ entity auditlog {
23
33
  user String,
24
34
  token String @optional
25
35
  }
36
+
37
+ entity suspension {
38
+ id UUID @id,
39
+ continuation String[], // rest of the patterns to execute
40
+ env Any, // serialized environment-object
41
+ createdOn DateTime @default(now()),
42
+ createdBy String
43
+ }
44
+
45
+ workflow createSuspension {
46
+ {suspension
47
+ {id createSuspension.id
48
+ continuation createSuspension.continuation,
49
+ env createSuspension.env,
50
+ createdBy createSuspension.createdBy}}
51
+ }
52
+
53
+ workflow restartSuspension {
54
+ await Core.restartSuspension(restartSuspension.id)
55
+ }
26
56
  `;
27
57
  export const CoreModules: string[] = [];
28
58
 
@@ -95,3 +125,80 @@ export async function addUpdateAudit(
95
125
  ) {
96
126
  await addAudit(env, 'u', resource, previous_value);
97
127
  }
128
+
129
+ export async function createSuspension(
130
+ suspId: string,
131
+ continuation: string[],
132
+ env: Environment
133
+ ): Promise<string | undefined> {
134
+ const user = env.getActiveUser();
135
+ const newEnv = new Environment('susp', env).setInKernelMode(true);
136
+ const envObj = env.asSerializableObject();
137
+ const inst = makeInstance(
138
+ 'agentlang',
139
+ 'createSuspension',
140
+ newInstanceAttributes()
141
+ .set('id', suspId)
142
+ .set('continuation', continuation)
143
+ .set('env', envObj)
144
+ .set('createdBy', user)
145
+ );
146
+ const r: any = await evaluate(inst, undefined, newEnv);
147
+ if (!isInstanceOfType(r, 'agentlang/suspension')) {
148
+ logger.warn(`Failed to create suspension for user ${user}`);
149
+ return undefined;
150
+ }
151
+ return (r as Instance).lookup('id');
152
+ }
153
+
154
+ export type Suspension = {
155
+ continuation: Statement[];
156
+ env: Environment;
157
+ };
158
+
159
+ async function loadSuspension(suspId: string, env?: Environment): Promise<Suspension | undefined> {
160
+ const newEnv = new Environment('auditlog', env).setInKernelMode(true);
161
+ const r: any = await parseAndEvaluateStatement(
162
+ `{agentlang/suspension {id? "${suspId}"}}`,
163
+ undefined,
164
+ newEnv
165
+ );
166
+ if (r instanceof Array && r.length > 0) {
167
+ const inst: Instance = r[0];
168
+ const cont = inst.lookup('continuation');
169
+ const stmts: Statement[] = await parseStatements(cont);
170
+ const envStr = inst.lookup('env');
171
+ const suspEnv: Environment = Environment.FromSerializableObject(JSON.parse(envStr));
172
+ return {
173
+ continuation: stmts,
174
+ env: suspEnv,
175
+ };
176
+ }
177
+ return undefined;
178
+ }
179
+
180
+ async function deleteSuspension(suspId: string, env?: Environment): Promise<any> {
181
+ try {
182
+ await parseAndEvaluateStatement(
183
+ `purge {agentlang/suspension {id? "${suspId}"}}`,
184
+ undefined,
185
+ env
186
+ );
187
+ return suspId;
188
+ } catch (err: any) {
189
+ logger.warn(`Failed to delete suspension ${suspId} - ${err}`);
190
+ return undefined;
191
+ }
192
+ }
193
+
194
+ export async function restartSuspension(suspId: string, env?: Environment): Promise<any> {
195
+ const susp = await loadSuspension(suspId, env);
196
+ if (susp) {
197
+ await evaluateStatements(susp.continuation, susp.env);
198
+ await deleteSuspension(suspId, env);
199
+ return susp.env.getLastResult();
200
+ } else {
201
+ logger.warn(`Suspension ${suspId} not found`);
202
+ return undefined;
203
+ }
204
+ }
@@ -1,4 +1,4 @@
1
- import { evaluate } from '../interpreter.js';
1
+ import { Environment, evaluate } from '../interpreter.js';
2
2
  import { logger } from '../logger.js';
3
3
  import {
4
4
  Instance,
@@ -45,7 +45,7 @@ export function getSubscriptionEvent(resolverName: string): string | undefined {
45
45
 
46
46
  export class Resolver {
47
47
  protected authInfo: ResolverAuthInfo = DefaultAuthInfo;
48
- protected userData: any;
48
+ protected env: Environment | undefined;
49
49
  protected name: string = 'default';
50
50
 
51
51
  static Default = new Resolver();
@@ -59,13 +59,13 @@ export class Resolver {
59
59
  return this;
60
60
  }
61
61
 
62
- public setUserData(userData: any): Resolver {
63
- this.userData = userData;
62
+ public setEnvironment(env: Environment): Resolver {
63
+ this.env = env;
64
64
  return this;
65
65
  }
66
66
 
67
- public getUserData(): any {
68
- return this.userData;
67
+ public getEnvironment(): Environment | undefined {
68
+ return this.env;
69
69
  }
70
70
 
71
71
  public getName(): string {
@@ -65,7 +65,7 @@ export class SqlDbResolver extends Resolver {
65
65
  }
66
66
 
67
67
  private getDbContext(resourceFqName: string): DbContext {
68
- const activeEnv: Environment = this.getUserData() as Environment;
68
+ const activeEnv: Environment = this.getEnvironment() as Environment;
69
69
  if (!activeEnv) {
70
70
  throw new Error('Active environment context is required by SqlDbResolver');
71
71
  }
@@ -104,7 +104,7 @@ export class SqlDbResolver extends Resolver {
104
104
  attrs.set(PathAttributeName, p);
105
105
  }
106
106
  const n: string = asTableName(inst.moduleName, inst.name);
107
- const rowObj: object = inst.attributesAsObject();
107
+ const rowObj: object = inst.attributesWithStringifiedObjects();
108
108
  const ctx = this.getDbContext(inst.getFqName());
109
109
  await insertRow(n, rowObj, ctx, orUpdate);
110
110
  if (inst.record.getFullTextSearchAttributes()) {