agentlang 0.10.1 → 0.10.2

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 (95) hide show
  1. package/out/api/http.d.ts.map +1 -1
  2. package/out/api/http.js +136 -0
  3. package/out/api/http.js.map +1 -1
  4. package/out/language/generated/ast.d.ts +27 -2
  5. package/out/language/generated/ast.d.ts.map +1 -1
  6. package/out/language/generated/ast.js +20 -0
  7. package/out/language/generated/ast.js.map +1 -1
  8. package/out/language/generated/grammar.d.ts.map +1 -1
  9. package/out/language/generated/grammar.js +246 -206
  10. package/out/language/generated/grammar.js.map +1 -1
  11. package/out/language/main.cjs +263 -206
  12. package/out/language/main.cjs.map +2 -2
  13. package/out/language/parser.d.ts.map +1 -1
  14. package/out/language/parser.js +28 -2
  15. package/out/language/parser.js.map +1 -1
  16. package/out/language/syntax.d.ts +14 -0
  17. package/out/language/syntax.d.ts.map +1 -1
  18. package/out/language/syntax.js +60 -27
  19. package/out/language/syntax.js.map +1 -1
  20. package/out/runtime/api.d.ts +2 -0
  21. package/out/runtime/api.d.ts.map +1 -1
  22. package/out/runtime/api.js +3 -0
  23. package/out/runtime/api.js.map +1 -1
  24. package/out/runtime/datefns.d.ts +34 -0
  25. package/out/runtime/datefns.d.ts.map +1 -0
  26. package/out/runtime/datefns.js +82 -0
  27. package/out/runtime/datefns.js.map +1 -0
  28. package/out/runtime/exec-graph.d.ts.map +1 -1
  29. package/out/runtime/exec-graph.js +22 -3
  30. package/out/runtime/exec-graph.js.map +1 -1
  31. package/out/runtime/interpreter.d.ts +9 -0
  32. package/out/runtime/interpreter.d.ts.map +1 -1
  33. package/out/runtime/interpreter.js +71 -7
  34. package/out/runtime/interpreter.js.map +1 -1
  35. package/out/runtime/module.d.ts +8 -0
  36. package/out/runtime/module.d.ts.map +1 -1
  37. package/out/runtime/module.js +23 -0
  38. package/out/runtime/module.js.map +1 -1
  39. package/out/runtime/modules/ai.d.ts +7 -3
  40. package/out/runtime/modules/ai.d.ts.map +1 -1
  41. package/out/runtime/modules/ai.js +67 -21
  42. package/out/runtime/modules/ai.js.map +1 -1
  43. package/out/runtime/modules/core.d.ts.map +1 -1
  44. package/out/runtime/modules/core.js +5 -1
  45. package/out/runtime/modules/core.js.map +1 -1
  46. package/out/runtime/monitor.d.ts +6 -0
  47. package/out/runtime/monitor.d.ts.map +1 -1
  48. package/out/runtime/monitor.js +21 -1
  49. package/out/runtime/monitor.js.map +1 -1
  50. package/out/runtime/relgraph.d.ts.map +1 -1
  51. package/out/runtime/relgraph.js +7 -3
  52. package/out/runtime/relgraph.js.map +1 -1
  53. package/out/runtime/resolvers/interface.d.ts +3 -2
  54. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  55. package/out/runtime/resolvers/interface.js +3 -2
  56. package/out/runtime/resolvers/interface.js.map +1 -1
  57. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  58. package/out/runtime/resolvers/sqldb/dbutil.js +17 -4
  59. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  60. package/out/runtime/resolvers/sqldb/impl.d.ts +1 -1
  61. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  62. package/out/runtime/resolvers/sqldb/impl.js +17 -7
  63. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  64. package/out/runtime/util.d.ts +3 -2
  65. package/out/runtime/util.d.ts.map +1 -1
  66. package/out/runtime/util.js +13 -2
  67. package/out/runtime/util.js.map +1 -1
  68. package/out/syntaxes/agentlang.monarch.js +1 -1
  69. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  70. package/out/test-harness.d.ts +36 -0
  71. package/out/test-harness.d.ts.map +1 -0
  72. package/out/test-harness.js +341 -0
  73. package/out/test-harness.js.map +1 -0
  74. package/package.json +4 -1
  75. package/src/api/http.ts +138 -0
  76. package/src/language/agentlang.langium +3 -1
  77. package/src/language/generated/ast.ts +32 -1
  78. package/src/language/generated/grammar.ts +246 -206
  79. package/src/language/parser.ts +33 -1
  80. package/src/language/syntax.ts +71 -24
  81. package/src/runtime/api.ts +5 -0
  82. package/src/runtime/datefns.ts +112 -0
  83. package/src/runtime/exec-graph.ts +23 -2
  84. package/src/runtime/interpreter.ts +82 -6
  85. package/src/runtime/module.ts +26 -0
  86. package/src/runtime/modules/ai.ts +78 -31
  87. package/src/runtime/modules/core.ts +5 -1
  88. package/src/runtime/monitor.ts +27 -1
  89. package/src/runtime/relgraph.ts +7 -3
  90. package/src/runtime/resolvers/interface.ts +4 -2
  91. package/src/runtime/resolvers/sqldb/dbutil.ts +20 -6
  92. package/src/runtime/resolvers/sqldb/impl.ts +17 -7
  93. package/src/runtime/util.ts +19 -2
  94. package/src/syntaxes/agentlang.monarch.ts +1 -1
  95. package/src/test-harness.ts +423 -0
@@ -36,9 +36,16 @@ import {
36
36
  SetAttribute,
37
37
  Statement,
38
38
  WhereSpec,
39
+ WhereSpecClause,
39
40
  WorkflowDefinition,
40
41
  } from './generated/ast.js';
41
- import { firstAliasSpec, firstCatchSpec, isString, QuerySuffix } from '../runtime/util.js';
42
+ import {
43
+ firstAliasSpec,
44
+ firstCatchSpec,
45
+ firstEmptySpec,
46
+ isString,
47
+ QuerySuffix,
48
+ } from '../runtime/util.js';
42
49
  import {
43
50
  BasePattern,
44
51
  CrudPattern,
@@ -54,6 +61,7 @@ import {
54
61
  NegExpressionPattern,
55
62
  NotExpressionPattern,
56
63
  ReturnPattern,
64
+ WhereSpecClausePattern,
57
65
  } from './syntax.js';
58
66
 
59
67
  let nextDocumentId = 1;
@@ -262,6 +270,10 @@ function introspectStatement(stmt: Statement): BasePattern {
262
270
  r.addHandler(h.except, introspectStatement(h.stmt));
263
271
  });
264
272
  }
273
+ const emptySpec = firstEmptySpec(stmt);
274
+ if (emptySpec) {
275
+ r.setEmptyHandler(introspectStatement(emptySpec.stmt));
276
+ }
265
277
  return r;
266
278
  }
267
279
 
@@ -299,6 +311,14 @@ function introspectPattern(pat: Pattern): BasePattern {
299
311
  function introspectInto(intoSpec: SelectIntoSpec, p: CrudPattern): CrudPattern {
300
312
  intoSpec.entries.forEach((se: SelectIntoEntry) => {
301
313
  if (se.attribute) p.addInto(se.alias, se.attribute);
314
+ else if (se.aggregate) {
315
+ const args = se.aggregate.args
316
+ .map((s: string) => {
317
+ return s;
318
+ })
319
+ .join(', ');
320
+ p.addInto(se.alias, `@${se.aggregate?.name}(${args})`);
321
+ }
302
322
  });
303
323
  return p;
304
324
  }
@@ -379,6 +399,18 @@ function introspectQueryPattern(crudMap: CrudMap): CrudPattern {
379
399
  };
380
400
  cp.joins.push(jp);
381
401
  });
402
+ if (opts.where?.clauses) {
403
+ cp.where = new Array<WhereSpecClausePattern>();
404
+ opts.where.clauses.forEach((wc: WhereSpecClause) => {
405
+ cp.where?.push({ lhs: wc.lhs, op: wc.op || '=', rhs: wc.rhs.$cstNode?.text || '' });
406
+ });
407
+ }
408
+ if (opts.groupByClause) {
409
+ cp.groupBy = opts.groupByClause.colNames;
410
+ }
411
+ if (opts.orderByClause) {
412
+ cp.orderBy = opts.orderByClause.colNames;
413
+ }
382
414
  cp.isCreate = false;
383
415
  cp.isQueryUpdate = false;
384
416
  cp.isQuery = true;
@@ -15,6 +15,7 @@ export class BasePattern {
15
15
  alias: string | undefined;
16
16
  aliases: string[] | undefined;
17
17
  handlers: Map<string, BasePattern> | undefined;
18
+ emptyHandler: BasePattern | undefined;
18
19
 
19
20
  setAlias(alias: string) {
20
21
  this.alias = alias;
@@ -44,6 +45,11 @@ export class BasePattern {
44
45
  this.handlers.set(k, handler);
45
46
  }
46
47
 
48
+ setEmptyHandler(handler: BasePattern) {
49
+ this.emptyHandler = handler;
50
+ return this;
51
+ }
52
+
47
53
  private aliasesAsString(): string | undefined {
48
54
  if (this.alias) {
49
55
  return ` @as ${this.alias}`;
@@ -54,24 +60,30 @@ export class BasePattern {
54
60
  }
55
61
  }
56
62
 
63
+ private emptyHandlerAsString(): string | undefined {
64
+ if (this.emptyHandler) {
65
+ return ` @empty ${this.emptyHandler.toString()}`;
66
+ }
67
+ return undefined;
68
+ }
69
+
57
70
  private handlersAsString(): string | undefined {
58
- if (this.handlers) {
59
- let s = '{';
71
+ if (this.handlers && this.handlers.size > 0) {
72
+ let s = ' @catch {';
60
73
  this.handlers.forEach((handler: BasePattern, k: string) => {
61
- s = `${s} ${k} ${handler.toString()}\n`;
74
+ s = `${s}${k} ${handler.toString()} `;
62
75
  });
63
- return s + '}';
64
- } else {
65
- return undefined;
76
+ return s.trimEnd() + '}';
66
77
  }
78
+ return undefined;
67
79
  }
80
+
68
81
  hintsAsString(): string {
69
- const a = this.aliasesAsString();
70
- const h = this.handlersAsString();
71
- if (!a && !h) return '';
72
- if (a && !h) return a;
73
- if (!a && h) return h;
74
- return `${a}\n${h}`;
82
+ // Order matters: @as, then @catch, then @empty (must be last because it's greedy in the grammar)
83
+ const a = this.aliasesAsString() ?? '';
84
+ const h = this.handlersAsString() ?? '';
85
+ const e = this.emptyHandlerAsString() ?? '';
86
+ return `${a}${h}${e}`;
75
87
  }
76
88
 
77
89
  toString(): string {
@@ -386,12 +398,21 @@ function joinPatternToString(jp: JoinPattern): string {
386
398
  return `${jp.type} ${jp.targetEntity} {${jp.conditionLhs}${opr} ${jp.conditionRhs}}`;
387
399
  }
388
400
 
401
+ export type WhereSpecClausePattern = {
402
+ lhs: string;
403
+ op: string;
404
+ rhs: string;
405
+ };
406
+
389
407
  export class CrudPattern extends BasePattern {
390
408
  recordName: string;
391
409
  attributes: Array<AttributePattern>;
392
410
  relationships: Map<string, CrudPattern[] | CrudPattern> | undefined;
393
411
  joins: JoinPattern[];
394
412
  into: Map<string, string> | undefined;
413
+ where: WhereSpecClausePattern[] | undefined;
414
+ groupBy: string[] | undefined;
415
+ orderBy: string[] | undefined;
395
416
  isQuery: boolean = false;
396
417
  isQueryUpdate: boolean = false;
397
418
  isCreate: boolean = false;
@@ -521,15 +542,29 @@ export class CrudPattern extends BasePattern {
521
542
  return escapeQueryName(this.recordName);
522
543
  }
523
544
 
524
- private intoAsString(): string | undefined {
525
- if (this.into) {
526
- const ss = new Array<string>();
527
- this.into.forEach((attr: string, alias: string) => {
528
- ss.push(`${alias} ${attr}`);
529
- });
530
- return `@into { ${ss.join(',\n')} }`;
531
- }
532
- return undefined;
545
+ private intoAsString(): string {
546
+ const ss = new Array<string>();
547
+ this.into?.forEach((attr: string, alias: string) => {
548
+ ss.push(`${alias} ${attr}`);
549
+ });
550
+ return `@into { ${ss.join(',\n')} }`;
551
+ }
552
+
553
+ private whereAsString(): string {
554
+ const ss = new Array<string>();
555
+ this.where?.forEach((wc: WhereSpecClausePattern) => {
556
+ if (wc.op == '=') ss.push(`${wc.lhs} ${wc.rhs}`);
557
+ else ss.push(`${wc.lhs} ${wc.op} ${wc.rhs}`);
558
+ });
559
+ return `@where {${ss.join(`,\n`)}}`;
560
+ }
561
+
562
+ private groupByAsString(): string {
563
+ return `@groupBy(${this.groupBy?.join(', ')})`;
564
+ }
565
+
566
+ private orderByAsString(): string {
567
+ return `@orderBy(${this.orderBy?.join(', ')})`;
533
568
  }
534
569
 
535
570
  override toString(): string {
@@ -542,10 +577,22 @@ export class CrudPattern extends BasePattern {
542
577
  const js = this.joins.map(joinPatternToString);
543
578
  s = s.concat(`,\n${js.join(',\n')}`);
544
579
  }
545
- const ins = this.intoAsString();
546
- if (ins) {
547
- s = s.concat(`,\n${ins}`);
580
+ if (this.into) {
581
+ const ins = this.intoAsString();
582
+ if (ins) {
583
+ s = s.concat(`,\n${ins}`);
584
+ }
585
+ }
586
+ if (this.where) {
587
+ s = s.concat(`,\n${this.whereAsString()}`);
548
588
  }
589
+ if (this.groupBy) {
590
+ s = s.concat(`,\n${this.groupByAsString()}`);
591
+ }
592
+ if (this.orderBy) {
593
+ s = s.concat(`,\n${this.orderByAsString()}`);
594
+ }
595
+
549
596
  return s.concat('}', this.hintsAsString());
550
597
  }
551
598
  }
@@ -5,9 +5,11 @@ import {
5
5
  } from './module.js';
6
6
  import { getLocalEnv as al_getLocalEnv, setLocalEnv as al_setLocalEnv } from './auth/defs.js';
7
7
  import { now } from './util.js';
8
+ import { initDateFns } from './datefns.js';
8
9
 
9
10
  declare global {
10
11
  var agentlang: any | undefined;
12
+ var dateFns: ReturnType<typeof initDateFns> | undefined;
11
13
  function getLocalEnv(k: string, defaultValue?: string): string | undefined;
12
14
  function setLocalEnv(k: string, v: string): string;
13
15
  function uuid(): string;
@@ -32,6 +34,9 @@ export function initGlobalApi() {
32
34
  globalThis.getLocalEnv = al_getLocalEnv;
33
35
  globalThis.setLocalEnv = al_setLocalEnv;
34
36
 
37
+ // Expose date-fns functions globally as dateFns.*
38
+ globalThis.dateFns = initDateFns();
39
+
35
40
  ApiInited = true;
36
41
  }
37
42
  }
@@ -0,0 +1,112 @@
1
+ import {
2
+ addDays as _addDays,
3
+ addWeeks as _addWeeks,
4
+ addMonths as _addMonths,
5
+ startOfWeek as _startOfWeek,
6
+ endOfWeek as _endOfWeek,
7
+ startOfMonth as _startOfMonth,
8
+ endOfMonth as _endOfMonth,
9
+ getISOWeek,
10
+ format,
11
+ parse,
12
+ differenceInDays,
13
+ differenceInWeeks,
14
+ } from 'date-fns';
15
+ import { formatInTimeZone } from 'date-fns-tz';
16
+
17
+ /**
18
+ * Parse a date string as local midnight to avoid timezone day-shift issues.
19
+ * date-fns operates in local time, so we must parse date-only strings
20
+ * (YYYY-MM-DD) as local midnight rather than UTC midnight.
21
+ */
22
+ function toDate(dateStr: string): Date {
23
+ if (!dateStr.includes('T')) {
24
+ const [y, m, d] = dateStr.split('-').map(Number);
25
+ return new Date(y, m - 1, d);
26
+ }
27
+ return new Date(dateStr);
28
+ }
29
+
30
+ function toDateStr(d: Date): string {
31
+ return format(d, 'yyyy-MM-dd');
32
+ }
33
+
34
+ function addDays(dateStr: string, days: number): string {
35
+ return toDateStr(_addDays(toDate(dateStr), days));
36
+ }
37
+
38
+ function addWeeks(dateStr: string, weeks: number): string {
39
+ return toDateStr(_addWeeks(toDate(dateStr), weeks));
40
+ }
41
+
42
+ function addMonths(dateStr: string, months: number): string {
43
+ return toDateStr(_addMonths(toDate(dateStr), months));
44
+ }
45
+
46
+ function startOfWeek(dateStr: string): string {
47
+ return toDateStr(_startOfWeek(toDate(dateStr), { weekStartsOn: 1 }));
48
+ }
49
+
50
+ function endOfWeek(dateStr: string): string {
51
+ return toDateStr(_endOfWeek(toDate(dateStr), { weekStartsOn: 1 }));
52
+ }
53
+
54
+ function startOfMonth(dateStr: string): string {
55
+ return toDateStr(_startOfMonth(toDate(dateStr)));
56
+ }
57
+
58
+ function endOfMonth(dateStr: string): string {
59
+ return toDateStr(_endOfMonth(toDate(dateStr)));
60
+ }
61
+
62
+ function getWeek(dateStr: string): number {
63
+ return getISOWeek(toDate(dateStr));
64
+ }
65
+
66
+ function dayName(dateStr: string): string {
67
+ return format(toDate(dateStr), 'EEEE');
68
+ }
69
+
70
+ function formatDate(dateStr: string, fmt: string): string {
71
+ return format(toDate(dateStr), fmt);
72
+ }
73
+
74
+ function parseDate(str: string, fmt: string): string {
75
+ return toDateStr(parse(str, fmt, new Date()));
76
+ }
77
+
78
+ function diffInDays(dateStr1: string, dateStr2: string): number {
79
+ return differenceInDays(toDate(dateStr1), toDate(dateStr2));
80
+ }
81
+
82
+ function diffInWeeks(dateStr1: string, dateStr2: string): number {
83
+ return differenceInWeeks(toDate(dateStr1), toDate(dateStr2));
84
+ }
85
+
86
+ function today(): string {
87
+ return toDateStr(new Date());
88
+ }
89
+
90
+ function toTimezone(dateStr: string, tz: string): string {
91
+ return formatInTimeZone(new Date(dateStr), tz, 'yyyy-MM-dd HH:mm:ssXXX');
92
+ }
93
+
94
+ export function initDateFns() {
95
+ return {
96
+ addDays,
97
+ addWeeks,
98
+ addMonths,
99
+ startOfWeek,
100
+ endOfWeek,
101
+ startOfMonth,
102
+ endOfMonth,
103
+ getWeek,
104
+ dayName,
105
+ formatDate,
106
+ parseDate,
107
+ diffInDays,
108
+ diffInWeeks,
109
+ today,
110
+ toTimezone,
111
+ };
112
+ }
@@ -304,8 +304,8 @@ export async function executeGraph(execGraph: ExecGraph, env: Environment): Prom
304
304
  throw reason;
305
305
  } finally {
306
306
  if (monitoringEnabled) {
307
- if (monitorIncr) env.decrementMonitor();
308
307
  env.setMonitorEntryResult(env.getLastResult());
308
+ if (monitorIncr) env.decrementMonitor();
309
309
  }
310
310
  }
311
311
  }
@@ -425,10 +425,28 @@ export async function executeEvent(
425
425
  env.setActiveEvent(eventInstance);
426
426
  await executeEventHelper(eventInstance, env);
427
427
  } else if (isAgentEventInstance(eventInstance)) {
428
+ env.setActiveEvent(eventInstance);
429
+ if (isMonitoringEnabled()) {
430
+ env.appendEntryToMonitor(
431
+ `{${eventInstance.getFqName()} {message "${eventInstance.lookup('message')}"}}`
432
+ );
433
+ }
428
434
  await handleAgentInvocation(eventInstance, env);
435
+ if (isMonitoringEnabled()) {
436
+ env.setMonitorEntryResult(env.getLastResult());
437
+ }
429
438
  }
430
439
  const r = env.getLastResult();
431
- if (continuation) continuation(r);
440
+ if (continuation) {
441
+ if (env.getLastPattern() && isAgentEventInstance(eventInstance)) {
442
+ continuation({
443
+ result: r.map((res: any) => (res.attributes ? Object.fromEntries(res.attributes) : res)),
444
+ pattern: env.getLastPattern(),
445
+ });
446
+ } else {
447
+ continuation(r);
448
+ }
449
+ }
432
450
  return r;
433
451
  } catch (err) {
434
452
  if (env && env.hasHandlers()) {
@@ -490,6 +508,9 @@ export async function executeEventHelper(eventInstance: Instance, env?: Environm
490
508
  );
491
509
  }
492
510
  await handleAgentInvocation(eventInstance, env);
511
+ if (isMonitoringEnabled()) {
512
+ env.setMonitorEntryResult(env.getLastResult());
513
+ }
493
514
  }
494
515
  if (isLocalEnv) {
495
516
  await env.commitAllTransactions();
@@ -119,6 +119,7 @@ import { Monitor, MonitorEntry } from './monitor.js';
119
119
  import { detailedDiff } from 'deep-object-diff';
120
120
  import { callMcpTool, mcpClientNameFromToolEvent } from './mcpclient.js';
121
121
  import { isNodeEnv } from '../utils/runtime.js';
122
+ import Handlebars from 'handlebars';
122
123
 
123
124
  export type Result = any;
124
125
 
@@ -131,6 +132,7 @@ export function isEmptyResult(r: Result): boolean {
131
132
  type BetweenRelInfo = {
132
133
  relationship: Relationship;
133
134
  connectedInstance: Instance;
135
+ connectedAlias?: string;
134
136
  };
135
137
 
136
138
  function mkEnvName(name: string | undefined, parent: Environment | undefined): string {
@@ -155,6 +157,7 @@ export class Environment extends Instance {
155
157
  private activeUserSet: boolean = false;
156
158
  private lastResult: Result;
157
159
  private trashedResult: Result = undefined;
160
+ private lastPattern: string | undefined;
158
161
  private returnFlag: boolean = false;
159
162
  private parentPath: string | undefined;
160
163
  private normalizedParentPath: string | undefined;
@@ -174,6 +177,7 @@ export class Environment extends Instance {
174
177
  private agentChatId: string | undefined = undefined;
175
178
  private monitor: Monitor | undefined = undefined;
176
179
  private escalatedRole: string | undefined;
180
+ private activeChatId: string | undefined;
177
181
 
178
182
  private activeUserData: any = undefined;
179
183
 
@@ -201,6 +205,7 @@ export class Environment extends Instance {
201
205
  this.agentChatId = parent.agentChatId;
202
206
  this.monitor = parent.monitor;
203
207
  this.escalatedRole = parent.escalatedRole;
208
+ this.activeChatId = parent.activeChatId;
204
209
  } else {
205
210
  this.activeModule = DefaultModuleName;
206
211
  this.activeResolvers = new Map<string, Resolver>();
@@ -327,6 +332,15 @@ export class Environment extends Instance {
327
332
  return this.attributes.get(Environment.FlowContextTag);
328
333
  }
329
334
 
335
+ setActiveChatId(chatId: string): Environment {
336
+ this.activeChatId = chatId;
337
+ return this;
338
+ }
339
+
340
+ getActiveChatId(): string | undefined {
341
+ return this.activeChatId;
342
+ }
343
+
330
344
  addToScratchPad(k: string, data: any): Environment {
331
345
  if (this.scratchPad === undefined) {
332
346
  this.scratchPad = {};
@@ -397,6 +411,11 @@ export class Environment extends Instance {
397
411
  return this;
398
412
  }
399
413
 
414
+ maybeRewriteTemplatePatterns(instruction: string, scratchPad?: any): string {
415
+ const templ = Handlebars.compile(this.rewriteTemplateMappings(instruction));
416
+ return templ(scratchPad);
417
+ }
418
+
400
419
  static SuspensionUserData = '^';
401
420
 
402
421
  bindSuspensionUserData(userData: string): Environment {
@@ -554,6 +573,15 @@ export class Environment extends Instance {
554
573
  return this.lastResult;
555
574
  }
556
575
 
576
+ setLastPattern(pattern: string | undefined): Environment {
577
+ this.lastPattern = pattern;
578
+ return this;
579
+ }
580
+
581
+ getLastPattern(): string | undefined {
582
+ return this.lastPattern;
583
+ }
584
+
557
585
  getActiveModuleName(): string {
558
586
  return this.activeModule;
559
587
  }
@@ -891,6 +919,13 @@ export class Environment extends Instance {
891
919
  return this;
892
920
  }
893
921
 
922
+ setMonitorEntryLlmTokenUsage(input: number, output: number, total: number): Environment {
923
+ if (this.monitor !== undefined) {
924
+ this.monitor.setEntryLlmTokenUsage(input, output, total);
925
+ }
926
+ return this;
927
+ }
928
+
894
929
  incrementMonitor(): Environment {
895
930
  if (this.monitor !== undefined) {
896
931
  this.monitor = this.monitor.increment();
@@ -939,7 +974,13 @@ export let evaluate = async function (
939
974
  } else if (isAgentEventInstance(eventInstance)) {
940
975
  env = new Environment(eventInstance.name + '.env', activeEnv);
941
976
  await handleAgentInvocation(eventInstance, env);
942
- if (continuation) continuation(env.getLastResult());
977
+ if (continuation) {
978
+ if (env.getLastPattern()) {
979
+ continuation({ result: env.getLastResult(), pattern: env.getLastPattern() });
980
+ } else {
981
+ continuation(env.getLastResult());
982
+ }
983
+ }
943
984
  } else if (isOpenApiEventInstance(eventInstance)) {
944
985
  env = new Environment(eventInstance.name + '.env', activeEnv);
945
986
  await handleOpenApiEvent(eventInstance, env);
@@ -1092,6 +1133,9 @@ export async function evaluateStatement(stmt: Statement, env: Environment): Prom
1092
1133
  handlersPushed = env.pushHandlers(handlers);
1093
1134
  }
1094
1135
  await evaluatePattern(stmt.pattern, env);
1136
+ if (hasHints) {
1137
+ await maybeHandleEmpty(hints, env);
1138
+ }
1095
1139
  if (hasHints) {
1096
1140
  maybeBindStatementResultToAlias(hints, env);
1097
1141
  }
@@ -1121,6 +1165,24 @@ async function maybeHandleNotFound(handlers: CatchHandlers | undefined, env: Env
1121
1165
  }
1122
1166
  }
1123
1167
 
1168
+ async function maybeHandleEmpty(hints: RuntimeHint[], env: Environment) {
1169
+ const lastResult: Result = env.getLastResult();
1170
+ if (
1171
+ lastResult === null ||
1172
+ lastResult === undefined ||
1173
+ (lastResult instanceof Array && lastResult.length == 0)
1174
+ ) {
1175
+ for (const rh of hints) {
1176
+ if (rh.emptySpec) {
1177
+ const newEnv = new Environment('empty-env', env).unsetEventExecutor();
1178
+ await evaluateStatement(rh.emptySpec.stmt, newEnv);
1179
+ env.setLastResult(newEnv.getLastResult());
1180
+ break;
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+
1124
1186
  async function maybeHandleError(
1125
1187
  handlers: CatchHandlers | undefined,
1126
1188
  reason: any,
@@ -1646,7 +1708,8 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1646
1708
  const insts: Instance[] = await res.queryConnectedInstances(
1647
1709
  betRelInfo.relationship,
1648
1710
  betRelInfo.connectedInstance,
1649
- inst
1711
+ inst,
1712
+ betRelInfo.connectedAlias
1650
1713
  );
1651
1714
  env.setLastResult(insts);
1652
1715
  } else {
@@ -1671,7 +1734,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1671
1734
  false,
1672
1735
  true
1673
1736
  );
1674
- env.setLastResult(inst);
1737
+ env.setLastResult([inst]);
1675
1738
  } else {
1676
1739
  const insts: Instance[] = await res.queryInstances(inst, isQueryAll, distinct);
1677
1740
  env.setLastResult(insts);
@@ -1698,7 +1761,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1698
1761
  await evaluatePattern(rel.pattern, newEnv);
1699
1762
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1700
1763
  } else if (isBetweenRelationship(rel.name, moduleName)) {
1701
- newEnv.setBetweenRelInfo({ relationship: relEntry, connectedInstance: lastRes[j] });
1764
+ const connAlias = relEntry.isSelfReferencing() ? relEntry.node1.alias : undefined;
1765
+ newEnv.setBetweenRelInfo({
1766
+ relationship: relEntry,
1767
+ connectedInstance: lastRes[j],
1768
+ connectedAlias: connAlias,
1769
+ });
1702
1770
  await evaluatePattern(rel.pattern, newEnv);
1703
1771
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1704
1772
  }
@@ -1734,7 +1802,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1734
1802
  await runPreUpdateEvents(lastRes, env);
1735
1803
  const finalInst: Instance = await res.updateInstance(lastRes, attrs);
1736
1804
  await runPostUpdateEvents(finalInst, lastRes, env);
1737
- env.setLastResult(finalInst);
1805
+ env.setLastResult([finalInst]);
1738
1806
  }
1739
1807
  }
1740
1808
  }
@@ -2035,6 +2103,11 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2035
2103
  while (true) {
2036
2104
  try {
2037
2105
  let rs: string = result ? normalizeGeneratedCode(result) : '';
2106
+ if (agent.tools) {
2107
+ env.setLastPattern(rs);
2108
+ } else {
2109
+ env.setLastPattern(undefined);
2110
+ }
2038
2111
  let isWf = rs.startsWith('workflow');
2039
2112
  if (isWf && !agent.runWorkflows) {
2040
2113
  await parseWorkflow(rs);
@@ -2128,6 +2201,10 @@ export async function handleAgentInvocation(
2128
2201
  env: Environment
2129
2202
  ): Promise<void> {
2130
2203
  const agent: AgentInstance = await findAgentByName(agentEventInst.name, env);
2204
+ const chatId = agentEventInst.lookup('chatId');
2205
+ if (chatId) {
2206
+ env.setActiveChatId(chatId);
2207
+ }
2131
2208
  const origMsg: any =
2132
2209
  agentEventInst.lookup('message') || JSON.stringify(agentEventInst.asObject());
2133
2210
  const msg: string = isString(origMsg) ? origMsg : maybeInstanceAsString(origMsg);
@@ -2137,7 +2214,6 @@ export async function handleAgentInvocation(
2137
2214
  } else {
2138
2215
  const mode = agentEventInst.lookup('mode');
2139
2216
  let activeEnv = env;
2140
- const chatId = agentEventInst.lookup('chatId');
2141
2217
  if (chatId !== undefined) {
2142
2218
  activeEnv.setAgentChatId(chatId);
2143
2219
  }
@@ -1409,6 +1409,10 @@ export class Relationship extends Record {
1409
1409
  return this.relType == RelType.BETWEEN;
1410
1410
  }
1411
1411
 
1412
+ isSelfReferencing(): boolean {
1413
+ return this.node1.path.asFqName() === this.node2.path.asFqName();
1414
+ }
1415
+
1412
1416
  parentNode(): RelationshipNode {
1413
1417
  return this.node1;
1414
1418
  }
@@ -1502,6 +1506,28 @@ export class Relationship extends Record {
1502
1506
  }
1503
1507
  }
1504
1508
 
1509
+ /**
1510
+ * For self-referencing relationships, resolve the alias for the connected instance
1511
+ * based on an explicit connectedAlias. Falls back to fqName-based lookup for
1512
+ * non-self-referencing relationships.
1513
+ */
1514
+ getAliasForConnected(inst: Instance, connectedAlias?: string): string {
1515
+ if (connectedAlias && this.isSelfReferencing()) {
1516
+ return connectedAlias;
1517
+ }
1518
+ return this.getAliasFor(inst);
1519
+ }
1520
+
1521
+ getInverseAliasForConnected(inst: Instance, connectedAlias?: string): string {
1522
+ if (connectedAlias && this.isSelfReferencing()) {
1523
+ if (connectedAlias === this.node1.alias) {
1524
+ return this.node2.alias;
1525
+ }
1526
+ return this.node1.alias;
1527
+ }
1528
+ return this.getInverseAliasFor(inst);
1529
+ }
1530
+
1505
1531
  isParent(inst: Instance): boolean {
1506
1532
  return inst.getFqName() == this.node1.path.asFqName();
1507
1533
  }