agentlang 0.10.1 → 0.10.3

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 (178) hide show
  1. package/README.md +7 -14
  2. package/out/api/http.d.ts +4 -0
  3. package/out/api/http.d.ts.map +1 -1
  4. package/out/api/http.js +307 -26
  5. package/out/api/http.js.map +1 -1
  6. package/out/cli/main.d.ts.map +1 -1
  7. package/out/cli/main.js +3 -0
  8. package/out/cli/main.js.map +1 -1
  9. package/out/extension/main.cjs +250 -250
  10. package/out/extension/main.cjs.map +2 -2
  11. package/out/language/agentlang-validator.d.ts.map +1 -1
  12. package/out/language/agentlang-validator.js +4 -0
  13. package/out/language/agentlang-validator.js.map +1 -1
  14. package/out/language/error-reporter.d.ts +53 -0
  15. package/out/language/error-reporter.d.ts.map +1 -0
  16. package/out/language/error-reporter.js +879 -0
  17. package/out/language/error-reporter.js.map +1 -0
  18. package/out/language/generated/ast.d.ts +77 -2
  19. package/out/language/generated/ast.d.ts.map +1 -1
  20. package/out/language/generated/ast.js +60 -0
  21. package/out/language/generated/ast.js.map +1 -1
  22. package/out/language/generated/grammar.d.ts.map +1 -1
  23. package/out/language/generated/grammar.js +342 -206
  24. package/out/language/generated/grammar.js.map +1 -1
  25. package/out/language/main.cjs +901 -710
  26. package/out/language/main.cjs.map +3 -3
  27. package/out/language/parser.d.ts +4 -2
  28. package/out/language/parser.d.ts.map +1 -1
  29. package/out/language/parser.js +58 -99
  30. package/out/language/parser.js.map +1 -1
  31. package/out/language/syntax.d.ts +16 -0
  32. package/out/language/syntax.d.ts.map +1 -1
  33. package/out/language/syntax.js +66 -27
  34. package/out/language/syntax.js.map +1 -1
  35. package/out/runtime/api.d.ts +2 -0
  36. package/out/runtime/api.d.ts.map +1 -1
  37. package/out/runtime/api.js +25 -0
  38. package/out/runtime/api.js.map +1 -1
  39. package/out/runtime/datefns.d.ts +34 -0
  40. package/out/runtime/datefns.d.ts.map +1 -0
  41. package/out/runtime/datefns.js +82 -0
  42. package/out/runtime/datefns.js.map +1 -0
  43. package/out/runtime/defs.d.ts +1 -0
  44. package/out/runtime/defs.d.ts.map +1 -1
  45. package/out/runtime/defs.js +2 -1
  46. package/out/runtime/defs.js.map +1 -1
  47. package/out/runtime/document-retriever.d.ts +24 -0
  48. package/out/runtime/document-retriever.d.ts.map +1 -0
  49. package/out/runtime/document-retriever.js +258 -0
  50. package/out/runtime/document-retriever.js.map +1 -0
  51. package/out/runtime/embeddings/chunker.d.ts +18 -0
  52. package/out/runtime/embeddings/chunker.d.ts.map +1 -1
  53. package/out/runtime/embeddings/chunker.js +47 -15
  54. package/out/runtime/embeddings/chunker.js.map +1 -1
  55. package/out/runtime/embeddings/openai.d.ts.map +1 -1
  56. package/out/runtime/embeddings/openai.js +22 -9
  57. package/out/runtime/embeddings/openai.js.map +1 -1
  58. package/out/runtime/embeddings/provider.d.ts +1 -0
  59. package/out/runtime/embeddings/provider.d.ts.map +1 -1
  60. package/out/runtime/embeddings/provider.js +20 -1
  61. package/out/runtime/embeddings/provider.js.map +1 -1
  62. package/out/runtime/exec-graph.d.ts.map +1 -1
  63. package/out/runtime/exec-graph.js +22 -3
  64. package/out/runtime/exec-graph.js.map +1 -1
  65. package/out/runtime/integration-client.d.ts +21 -0
  66. package/out/runtime/integration-client.d.ts.map +1 -0
  67. package/out/runtime/integration-client.js +112 -0
  68. package/out/runtime/integration-client.js.map +1 -0
  69. package/out/runtime/integrations.d.ts.map +1 -1
  70. package/out/runtime/integrations.js +20 -9
  71. package/out/runtime/integrations.js.map +1 -1
  72. package/out/runtime/interpreter.d.ts +10 -0
  73. package/out/runtime/interpreter.d.ts.map +1 -1
  74. package/out/runtime/interpreter.js +221 -22
  75. package/out/runtime/interpreter.js.map +1 -1
  76. package/out/runtime/loader.d.ts.map +1 -1
  77. package/out/runtime/loader.js +70 -7
  78. package/out/runtime/loader.js.map +1 -1
  79. package/out/runtime/logger.d.ts.map +1 -1
  80. package/out/runtime/logger.js +8 -1
  81. package/out/runtime/logger.js.map +1 -1
  82. package/out/runtime/module.d.ts +18 -0
  83. package/out/runtime/module.d.ts.map +1 -1
  84. package/out/runtime/module.js +91 -3
  85. package/out/runtime/module.js.map +1 -1
  86. package/out/runtime/modules/ai.d.ts +16 -5
  87. package/out/runtime/modules/ai.d.ts.map +1 -1
  88. package/out/runtime/modules/ai.js +286 -88
  89. package/out/runtime/modules/ai.js.map +1 -1
  90. package/out/runtime/modules/core.d.ts.map +1 -1
  91. package/out/runtime/modules/core.js +5 -1
  92. package/out/runtime/modules/core.js.map +1 -1
  93. package/out/runtime/monitor.d.ts +6 -0
  94. package/out/runtime/monitor.d.ts.map +1 -1
  95. package/out/runtime/monitor.js +21 -1
  96. package/out/runtime/monitor.js.map +1 -1
  97. package/out/runtime/relgraph.d.ts.map +1 -1
  98. package/out/runtime/relgraph.js +7 -3
  99. package/out/runtime/relgraph.js.map +1 -1
  100. package/out/runtime/resolvers/interface.d.ts +7 -2
  101. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  102. package/out/runtime/resolvers/interface.js +17 -3
  103. package/out/runtime/resolvers/interface.js.map +1 -1
  104. package/out/runtime/resolvers/sqldb/database.d.ts +2 -0
  105. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  106. package/out/runtime/resolvers/sqldb/database.js +142 -126
  107. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  108. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  109. package/out/runtime/resolvers/sqldb/dbutil.js +25 -4
  110. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  111. package/out/runtime/resolvers/sqldb/impl.d.ts +2 -1
  112. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  113. package/out/runtime/resolvers/sqldb/impl.js +24 -7
  114. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  115. package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
  116. package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
  117. package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
  118. package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
  119. package/out/runtime/resolvers/vector/types.d.ts +32 -0
  120. package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
  121. package/out/runtime/resolvers/vector/types.js +2 -0
  122. package/out/runtime/resolvers/vector/types.js.map +1 -0
  123. package/out/runtime/services/documentFetcher.d.ts.map +1 -1
  124. package/out/runtime/services/documentFetcher.js +21 -6
  125. package/out/runtime/services/documentFetcher.js.map +1 -1
  126. package/out/runtime/state.d.ts +19 -1
  127. package/out/runtime/state.d.ts.map +1 -1
  128. package/out/runtime/state.js +36 -1
  129. package/out/runtime/state.js.map +1 -1
  130. package/out/runtime/util.d.ts +3 -2
  131. package/out/runtime/util.d.ts.map +1 -1
  132. package/out/runtime/util.js +13 -2
  133. package/out/runtime/util.js.map +1 -1
  134. package/out/syntaxes/agentlang.monarch.js +1 -1
  135. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  136. package/out/test-harness.d.ts +36 -0
  137. package/out/test-harness.d.ts.map +1 -0
  138. package/out/test-harness.js +341 -0
  139. package/out/test-harness.js.map +1 -0
  140. package/package.json +22 -19
  141. package/src/api/http.ts +336 -38
  142. package/src/cli/main.ts +3 -0
  143. package/src/language/agentlang-validator.ts +3 -0
  144. package/src/language/agentlang.langium +6 -2
  145. package/src/language/error-reporter.ts +1028 -0
  146. package/src/language/generated/ast.ts +94 -1
  147. package/src/language/generated/grammar.ts +342 -206
  148. package/src/language/parser.ts +64 -101
  149. package/src/language/syntax.ts +79 -24
  150. package/src/runtime/api.ts +36 -0
  151. package/src/runtime/datefns.ts +112 -0
  152. package/src/runtime/defs.ts +2 -1
  153. package/src/runtime/document-retriever.ts +311 -0
  154. package/src/runtime/embeddings/chunker.ts +52 -14
  155. package/src/runtime/embeddings/openai.ts +27 -9
  156. package/src/runtime/embeddings/provider.ts +22 -1
  157. package/src/runtime/exec-graph.ts +23 -2
  158. package/src/runtime/integration-client.ts +158 -0
  159. package/src/runtime/integrations.ts +20 -11
  160. package/src/runtime/interpreter.ts +221 -15
  161. package/src/runtime/loader.ts +83 -5
  162. package/src/runtime/logger.ts +12 -1
  163. package/src/runtime/module.ts +104 -3
  164. package/src/runtime/modules/ai.ts +341 -107
  165. package/src/runtime/modules/core.ts +5 -1
  166. package/src/runtime/monitor.ts +27 -1
  167. package/src/runtime/relgraph.ts +7 -3
  168. package/src/runtime/resolvers/interface.ts +23 -3
  169. package/src/runtime/resolvers/sqldb/database.ts +158 -130
  170. package/src/runtime/resolvers/sqldb/dbutil.ts +28 -6
  171. package/src/runtime/resolvers/sqldb/impl.ts +25 -7
  172. package/src/runtime/resolvers/vector/lancedb-store.ts +187 -0
  173. package/src/runtime/resolvers/vector/types.ts +39 -0
  174. package/src/runtime/services/documentFetcher.ts +21 -6
  175. package/src/runtime/state.ts +40 -1
  176. package/src/runtime/util.ts +19 -2
  177. package/src/syntaxes/agentlang.monarch.ts +1 -1
  178. package/src/test-harness.ts +423 -0
@@ -0,0 +1,158 @@
1
+ let host: string | undefined;
2
+ let headers: Record<string, string> | undefined;
3
+
4
+ export function configureIntegrationClient(h: string, hdrs?: Record<string, string>): void {
5
+ host = h;
6
+ headers = hdrs;
7
+ }
8
+
9
+ export function isIntegrationClientConfigured(): boolean {
10
+ return host !== undefined;
11
+ }
12
+
13
+ export async function getIntegrationAuthHeaders(
14
+ integrationName: string
15
+ ): Promise<Record<string, string>> {
16
+ if (!host) {
17
+ throw new Error('Integration client not configured — call configureIntegrationClient() first');
18
+ }
19
+
20
+ const url = `${host}/integmanager.auth/authHeaders/${encodeURIComponent(integrationName)}`;
21
+ const response = await fetch(url, {
22
+ method: 'GET',
23
+ headers: { 'Content-Type': 'application/json', ...headers },
24
+ });
25
+
26
+ if (!response.ok) {
27
+ throw new Error(
28
+ `Failed to get auth headers for integration "${integrationName}": ${response.status} ${response.statusText}`
29
+ );
30
+ }
31
+
32
+ const data = await response.json();
33
+ // The response is either the entity directly or wrapped in an array
34
+ const result = Array.isArray(data) ? data[0] : data;
35
+ return result?.headers ?? {};
36
+ }
37
+
38
+ export async function refreshIntegrationAuth(integrationName: string): Promise<void> {
39
+ if (!host) {
40
+ throw new Error('Integration client not configured — call configureIntegrationClient() first');
41
+ }
42
+
43
+ const url = `${host}/integmanager.auth/authRefresh`;
44
+ const response = await fetch(url, {
45
+ method: 'POST',
46
+ headers: { 'Content-Type': 'application/json', ...headers },
47
+ body: JSON.stringify({ integrationName }),
48
+ });
49
+
50
+ if (!response.ok) {
51
+ throw new Error(
52
+ `Failed to refresh auth for integration "${integrationName}": ${response.status} ${response.statusText}`
53
+ );
54
+ }
55
+ }
56
+
57
+ export async function integrationAuthFetch(
58
+ integrationName: string,
59
+ url: string | URL,
60
+ options: RequestInit = {}
61
+ ): Promise<Response> {
62
+ const authHeaders = await getIntegrationAuthHeaders(integrationName);
63
+ const mergedHeaders = { ...authHeaders, ...((options.headers as Record<string, string>) || {}) };
64
+ return fetch(url, { ...options, headers: mergedHeaders });
65
+ }
66
+
67
+ // --- OAuth consent flow helpers ---
68
+
69
+ export async function getOAuthAuthorizeUrl(
70
+ integrationName: string,
71
+ redirectUri: string
72
+ ): Promise<{ authorizationUrl: string; state: string }> {
73
+ if (!host) {
74
+ throw new Error('Integration client not configured — call configureIntegrationClient() first');
75
+ }
76
+
77
+ const params = new URLSearchParams({
78
+ action: 'authorize',
79
+ integrationName,
80
+ redirectUri,
81
+ });
82
+ const url = `${host}/integmanager.auth/oauthFlow?${params.toString()}`;
83
+ const response = await fetch(url, {
84
+ method: 'GET',
85
+ headers: { 'Content-Type': 'application/json', ...headers },
86
+ });
87
+
88
+ if (!response.ok) {
89
+ throw new Error(
90
+ `Failed to get OAuth authorize URL for "${integrationName}": ${response.status} ${response.statusText}`
91
+ );
92
+ }
93
+
94
+ const data = await response.json();
95
+ const result = Array.isArray(data) ? data[0] : data;
96
+ return { authorizationUrl: result.authorizationUrl, state: result.state };
97
+ }
98
+
99
+ export async function exchangeOAuthCode(
100
+ integrationName: string,
101
+ code: string,
102
+ state: string
103
+ ): Promise<{ accessToken: string; refreshToken: string; expiresIn: number; tokenType: string }> {
104
+ if (!host) {
105
+ throw new Error('Integration client not configured — call configureIntegrationClient() first');
106
+ }
107
+
108
+ const url = `${host}/integmanager.auth/oauthFlow`;
109
+ const response = await fetch(url, {
110
+ method: 'POST',
111
+ headers: { 'Content-Type': 'application/json', ...headers },
112
+ body: JSON.stringify({ action: 'exchange', integrationName, code, state }),
113
+ });
114
+
115
+ if (!response.ok) {
116
+ throw new Error(
117
+ `Failed to exchange OAuth code for "${integrationName}": ${response.status} ${response.statusText}`
118
+ );
119
+ }
120
+
121
+ const data = await response.json();
122
+ const result = Array.isArray(data) ? data[0] : data;
123
+ return {
124
+ accessToken: result.accessToken,
125
+ refreshToken: result.refreshToken,
126
+ expiresIn: result.expiresIn,
127
+ tokenType: result.tokenType,
128
+ };
129
+ }
130
+
131
+ export async function getIntegrationAccessToken(
132
+ integrationName: string
133
+ ): Promise<{ accessToken: string; expiresIn: number; tokenType: string }> {
134
+ if (!host) {
135
+ throw new Error('Integration client not configured — call configureIntegrationClient() first');
136
+ }
137
+
138
+ const params = new URLSearchParams({ integrationName });
139
+ const url = `${host}/integmanager.auth/oauthToken?${params.toString()}`;
140
+ const response = await fetch(url, {
141
+ method: 'GET',
142
+ headers: { 'Content-Type': 'application/json', ...headers },
143
+ });
144
+
145
+ if (!response.ok) {
146
+ throw new Error(
147
+ `Failed to get access token for "${integrationName}": ${response.status} ${response.statusText}`
148
+ );
149
+ }
150
+
151
+ const data = await response.json();
152
+ const result = Array.isArray(data) ? data[0] : data;
153
+ return {
154
+ accessToken: result.accessToken,
155
+ expiresIn: result.expiresIn,
156
+ tokenType: result.tokenType,
157
+ };
158
+ }
@@ -1,7 +1,6 @@
1
- import { Instance } from './module.js';
2
1
  import { isString } from './util.js';
3
2
 
4
- const Integrations = new Map<string, Instance>();
3
+ const Integrations = new Map<string, any>();
5
4
 
6
5
  const IntegManagerModel = 'integmanager.core';
7
6
 
@@ -16,7 +15,8 @@ export async function prepareIntegrations(
16
15
  const keys = [...integConfig.keys()];
17
16
  for (let i = 0; i < keys.length; ++i) {
18
17
  const configName = keys[i];
19
- const configPath = integConfig.get(configName);
18
+ const entry = integConfig.get(configName);
19
+ const configPath = typeof entry === 'string' ? entry : entry?.config;
20
20
  if (configPath) {
21
21
  const apiUrl = mkApiUrl(integManagerHost, configPath);
22
22
  try {
@@ -35,9 +35,6 @@ export async function prepareIntegrations(
35
35
  const data = await response.json();
36
36
  if (data.length > 0) {
37
37
  const inst: any = data[0].config;
38
- if (inst.type == 'custom' && isString(inst.parameter)) {
39
- inst.parameter = new Map(Object.entries(JSON.parse(inst.parameter)));
40
- }
41
38
  Integrations.set(configName, inst);
42
39
  } else {
43
40
  console.error(`Integration not found for ${configPath}`);
@@ -84,14 +81,26 @@ function mkApiUrl(integManagerHost: string, configPath: string): string {
84
81
  const parts = configPath.split('/');
85
82
  const integId = parts[0];
86
83
  const configId = parts[1];
87
- return `${integManagerHost}/${IntegManagerModel}/integration/${integId}/integrationConfig/config/${configId}`;
84
+ return `${integManagerHost}/${IntegManagerModel}/integration/${integId}/integrationConfig/config/${configId}?tree=true`;
88
85
  }
89
86
 
90
87
  export function getIntegrationConfig(name: string, configName: string): any {
91
88
  const config: any = Integrations.get(name);
92
- if (config) {
93
- return config.parameter.get(configName);
94
- } else {
95
- return undefined;
89
+ if (!config) return undefined;
90
+ if (config.parameter == null) return undefined;
91
+
92
+ if (config.parameter instanceof Map) {
93
+ return Object.fromEntries(config.parameter).get(configName);
94
+ }
95
+ if (isString(config.parameter)) {
96
+ try {
97
+ return JSON.parse(config.parameter)[configName];
98
+ } catch {
99
+ return undefined;
100
+ }
101
+ }
102
+ if (typeof config.parameter === 'object') {
103
+ return config.parameter[configName];
96
104
  }
105
+ return undefined;
97
106
  }
@@ -47,6 +47,7 @@ import {
47
47
  isEntityInstance,
48
48
  isEventInstance,
49
49
  isInstanceOfType,
50
+ getAllBetweenRelationshipsForEntity,
50
51
  isOneToOneBetweenRelationship,
51
52
  isTimer,
52
53
  makeInstance,
@@ -65,6 +66,7 @@ import {
65
66
  DefaultModuleName,
66
67
  escapeFqName,
67
68
  escapeQueryName,
69
+ firstAliasSpec,
68
70
  fqNameFromPath,
69
71
  isCoreModule,
70
72
  isFqName,
@@ -77,6 +79,7 @@ import {
77
79
  preprocessRawConfig,
78
80
  QuerySuffix,
79
81
  restoreSpecialChars,
82
+ splitFqName,
80
83
  splitRefs,
81
84
  } from './util.js';
82
85
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
@@ -88,9 +91,12 @@ import {
88
91
  } from '../language/parser.js';
89
92
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
90
93
  import {
94
+ AgentCancelledException,
91
95
  AgentEntityName,
92
96
  AgentFqName,
93
97
  AgentInstance,
98
+ checkCancelled,
99
+ clearCancellation,
94
100
  findAgentByName,
95
101
  normalizeGeneratedCode,
96
102
  saveFlowStepResult,
@@ -119,6 +125,7 @@ import { Monitor, MonitorEntry } from './monitor.js';
119
125
  import { detailedDiff } from 'deep-object-diff';
120
126
  import { callMcpTool, mcpClientNameFromToolEvent } from './mcpclient.js';
121
127
  import { isNodeEnv } from '../utils/runtime.js';
128
+ import Handlebars from 'handlebars';
122
129
 
123
130
  export type Result = any;
124
131
 
@@ -131,6 +138,7 @@ export function isEmptyResult(r: Result): boolean {
131
138
  type BetweenRelInfo = {
132
139
  relationship: Relationship;
133
140
  connectedInstance: Instance;
141
+ connectedAlias?: string;
134
142
  };
135
143
 
136
144
  function mkEnvName(name: string | undefined, parent: Environment | undefined): string {
@@ -155,6 +163,7 @@ export class Environment extends Instance {
155
163
  private activeUserSet: boolean = false;
156
164
  private lastResult: Result;
157
165
  private trashedResult: Result = undefined;
166
+ private lastPattern: string | undefined;
158
167
  private returnFlag: boolean = false;
159
168
  private parentPath: string | undefined;
160
169
  private normalizedParentPath: string | undefined;
@@ -174,6 +183,7 @@ export class Environment extends Instance {
174
183
  private agentChatId: string | undefined = undefined;
175
184
  private monitor: Monitor | undefined = undefined;
176
185
  private escalatedRole: string | undefined;
186
+ private activeChatId: string | undefined;
177
187
 
178
188
  private activeUserData: any = undefined;
179
189
 
@@ -201,6 +211,7 @@ export class Environment extends Instance {
201
211
  this.agentChatId = parent.agentChatId;
202
212
  this.monitor = parent.monitor;
203
213
  this.escalatedRole = parent.escalatedRole;
214
+ this.activeChatId = parent.activeChatId;
204
215
  } else {
205
216
  this.activeModule = DefaultModuleName;
206
217
  this.activeResolvers = new Map<string, Resolver>();
@@ -327,6 +338,15 @@ export class Environment extends Instance {
327
338
  return this.attributes.get(Environment.FlowContextTag);
328
339
  }
329
340
 
341
+ setActiveChatId(chatId: string): Environment {
342
+ this.activeChatId = chatId;
343
+ return this;
344
+ }
345
+
346
+ getActiveChatId(): string | undefined {
347
+ return this.activeChatId;
348
+ }
349
+
330
350
  addToScratchPad(k: string, data: any): Environment {
331
351
  if (this.scratchPad === undefined) {
332
352
  this.scratchPad = {};
@@ -397,6 +417,11 @@ export class Environment extends Instance {
397
417
  return this;
398
418
  }
399
419
 
420
+ maybeRewriteTemplatePatterns(instruction: string, scratchPad?: any): string {
421
+ const templ = Handlebars.compile(this.rewriteTemplateMappings(instruction));
422
+ return templ(scratchPad);
423
+ }
424
+
400
425
  static SuspensionUserData = '^';
401
426
 
402
427
  bindSuspensionUserData(userData: string): Environment {
@@ -554,6 +579,15 @@ export class Environment extends Instance {
554
579
  return this.lastResult;
555
580
  }
556
581
 
582
+ setLastPattern(pattern: string | undefined): Environment {
583
+ this.lastPattern = pattern;
584
+ return this;
585
+ }
586
+
587
+ getLastPattern(): string | undefined {
588
+ return this.lastPattern;
589
+ }
590
+
557
591
  getActiveModuleName(): string {
558
592
  return this.activeModule;
559
593
  }
@@ -891,6 +925,13 @@ export class Environment extends Instance {
891
925
  return this;
892
926
  }
893
927
 
928
+ setMonitorEntryLlmTokenUsage(input: number, output: number, total: number): Environment {
929
+ if (this.monitor !== undefined) {
930
+ this.monitor.setEntryLlmTokenUsage(input, output, total);
931
+ }
932
+ return this;
933
+ }
934
+
894
935
  incrementMonitor(): Environment {
895
936
  if (this.monitor !== undefined) {
896
937
  this.monitor = this.monitor.increment();
@@ -939,7 +980,13 @@ export let evaluate = async function (
939
980
  } else if (isAgentEventInstance(eventInstance)) {
940
981
  env = new Environment(eventInstance.name + '.env', activeEnv);
941
982
  await handleAgentInvocation(eventInstance, env);
942
- if (continuation) continuation(env.getLastResult());
983
+ if (continuation) {
984
+ if (env.getLastPattern()) {
985
+ continuation({ result: env.getLastResult(), pattern: env.getLastPattern() });
986
+ } else {
987
+ continuation(env.getLastResult());
988
+ }
989
+ }
943
990
  } else if (isOpenApiEventInstance(eventInstance)) {
944
991
  env = new Environment(eventInstance.name + '.env', activeEnv);
945
992
  await handleOpenApiEvent(eventInstance, env);
@@ -1092,7 +1139,11 @@ export async function evaluateStatement(stmt: Statement, env: Environment): Prom
1092
1139
  handlersPushed = env.pushHandlers(handlers);
1093
1140
  }
1094
1141
  await evaluatePattern(stmt.pattern, env);
1142
+ let skipOuterAlias = false;
1095
1143
  if (hasHints) {
1144
+ skipOuterAlias = await maybeHandleEmpty(hints, env);
1145
+ }
1146
+ if (hasHints && !skipOuterAlias) {
1096
1147
  maybeBindStatementResultToAlias(hints, env);
1097
1148
  }
1098
1149
  await maybeHandleNotFound(handlers, env);
@@ -1121,6 +1172,31 @@ async function maybeHandleNotFound(handlers: CatchHandlers | undefined, env: Env
1121
1172
  }
1122
1173
  }
1123
1174
 
1175
+ async function maybeHandleEmpty(hints: RuntimeHint[], env: Environment): Promise<boolean> {
1176
+ const lastResult: Result = env.getLastResult();
1177
+ if (
1178
+ lastResult === null ||
1179
+ lastResult === undefined ||
1180
+ (lastResult instanceof Array && lastResult.length == 0)
1181
+ ) {
1182
+ for (const rh of hints) {
1183
+ if (rh.emptySpec) {
1184
+ const newEnv = new Environment('empty-env', env).unsetEventExecutor();
1185
+ await evaluateStatement(rh.emptySpec.stmt, newEnv);
1186
+ env.setLastResult(newEnv.getLastResult());
1187
+ // If inner statement has its own @as, propagate binding to parent env
1188
+ const innerAlias = firstAliasSpec(rh.emptySpec.stmt);
1189
+ if (innerAlias) {
1190
+ maybeBindStatementResultToAlias(rh.emptySpec.stmt.hints, env);
1191
+ return true; // signal: skip outer @as
1192
+ }
1193
+ break;
1194
+ }
1195
+ }
1196
+ }
1197
+ return false;
1198
+ }
1199
+
1124
1200
  async function maybeHandleError(
1125
1201
  handlers: CatchHandlers | undefined,
1126
1202
  reason: any,
@@ -1509,6 +1585,12 @@ function maybeSetQueryClauses(inst: Instance, qopts: ExtractedQueryOptions) {
1509
1585
  if (qopts.orderByClause) {
1510
1586
  inst.setOrderBy(qopts.orderByClause.colNames, qopts.orderByClause.order === '@desc');
1511
1587
  }
1588
+ if (qopts.limitClause) {
1589
+ inst.setLimit(qopts.limitClause.value);
1590
+ }
1591
+ if (qopts.offsetClause) {
1592
+ inst.setOffset(qopts.offsetClause.value);
1593
+ }
1512
1594
  }
1513
1595
 
1514
1596
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
@@ -1562,7 +1644,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1562
1644
  }
1563
1645
  const res: Resolver = await getResolverForPath(entryName, moduleName, env);
1564
1646
  let r: Instance | undefined;
1565
- await computeExprAttributes(inst, undefined, undefined, env);
1647
+ await computeExprAttributes(inst, crud.body?.attributes, inst.attributes, env);
1566
1648
  await setMetaAttributes(inst.attributes, env);
1567
1649
  if (env.isInUpsertMode()) {
1568
1650
  await runPreUpdateEvents(inst, env);
@@ -1646,7 +1728,8 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1646
1728
  const insts: Instance[] = await res.queryConnectedInstances(
1647
1729
  betRelInfo.relationship,
1648
1730
  betRelInfo.connectedInstance,
1649
- inst
1731
+ inst,
1732
+ betRelInfo.connectedAlias
1650
1733
  );
1651
1734
  env.setLastResult(insts);
1652
1735
  } else {
@@ -1671,7 +1754,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1671
1754
  false,
1672
1755
  true
1673
1756
  );
1674
- env.setLastResult(inst);
1757
+ env.setLastResult([inst]);
1675
1758
  } else {
1676
1759
  const insts: Instance[] = await res.queryInstances(inst, isQueryAll, distinct);
1677
1760
  env.setLastResult(insts);
@@ -1698,7 +1781,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1698
1781
  await evaluatePattern(rel.pattern, newEnv);
1699
1782
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1700
1783
  } else if (isBetweenRelationship(rel.name, moduleName)) {
1701
- newEnv.setBetweenRelInfo({ relationship: relEntry, connectedInstance: lastRes[j] });
1784
+ const connAlias = relEntry.isSelfReferencing() ? relEntry.node1.alias : undefined;
1785
+ newEnv.setBetweenRelInfo({
1786
+ relationship: relEntry,
1787
+ connectedInstance: lastRes[j],
1788
+ connectedAlias: connAlias,
1789
+ });
1702
1790
  await evaluatePattern(rel.pattern, newEnv);
1703
1791
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1704
1792
  }
@@ -1734,7 +1822,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1734
1822
  await runPreUpdateEvents(lastRes, env);
1735
1823
  const finalInst: Instance = await res.updateInstance(lastRes, attrs);
1736
1824
  await runPostUpdateEvents(finalInst, lastRes, env);
1737
- env.setLastResult(finalInst);
1825
+ env.setLastResult([finalInst]);
1738
1826
  }
1739
1827
  }
1740
1828
  }
@@ -1845,24 +1933,98 @@ async function computeExprAttributes(
1845
1933
  updatedAttrs?.forEach((v: any, k: string) => {
1846
1934
  if (v !== undefined) newEnv.bind(k, v);
1847
1935
  });
1936
+ // Bind related instances from between-relationships so that @expr references
1937
+ // like DeptEmployee.Department.BudgetMultiplier can be resolved via followReference.
1938
+ const entityFqName = inst.getFqName();
1939
+ const fqParts = splitFqName(entityFqName);
1940
+ const rels = getAllBetweenRelationshipsForEntity(fqParts[0], fqParts[1]);
1941
+ for (const rel of rels) {
1942
+ const isFirst = rel.isFirstNodeName(entityFqName);
1943
+ // Only bind if this entity is on a valid side for single-entity resolution
1944
+ if (rel.isManyToMany()) continue;
1945
+ if (rel.isOneToMany() && isFirst) continue;
1946
+ // Determine the target entity on the other side
1947
+ const targetNode = isFirst ? rel.node2 : rel.node1;
1948
+ const targetAlias = targetNode.alias;
1949
+ let connectedInst: Instance | undefined;
1950
+ // Fast path: check if the environment carries between-rel info for this relationship
1951
+ const betRelInfo = env.getBetweenRelInfo();
1952
+ if (betRelInfo && betRelInfo.relationship.name === rel.name) {
1953
+ connectedInst = betRelInfo.connectedInstance;
1954
+ } else if (inst.lookup(PathAttributeName)) {
1955
+ // Slow path: query the connected instance through the resolver.
1956
+ // Only possible if the current instance has been persisted (has a __path__).
1957
+ try {
1958
+ const targetFqName = targetNode.path.asFqName();
1959
+ const targetParts = splitFqName(targetFqName);
1960
+ const res: Resolver = await getResolverForPath(targetParts[1], targetParts[0], env);
1961
+ const queryInst = Instance.EmptyInstance(targetParts[1], targetParts[0]);
1962
+ const connected: Instance[] = await res.queryConnectedInstances(rel, inst, queryInst);
1963
+ if (connected && connected.length > 0) {
1964
+ connectedInst = connected[0];
1965
+ }
1966
+ } catch (reason: any) {
1967
+ logger.debug(
1968
+ `Relationship query failed (e.g. not yet linked) - skip binding - ${reason}`
1969
+ );
1970
+ }
1971
+ }
1972
+ if (connectedInst) {
1973
+ const relMap = new Map<string, Instance>();
1974
+ relMap.set(targetAlias, connectedInst);
1975
+ newEnv.bind(rel.name, relMap);
1976
+ }
1977
+ }
1978
+ // Build a map of user-provided values for @expr attributes so that
1979
+ // overrides are applied inline during expression evaluation, allowing
1980
+ // dependent expressions to see the user's value immediately.
1981
+ let userExprOverrides: Map<string, Expr> | undefined;
1982
+ if (exprAttrs && origAttrs) {
1983
+ for (let i = 0; i < origAttrs.length; ++i) {
1984
+ const a: SetAttribute = origAttrs[i];
1985
+ const n = a.name;
1986
+ if (exprAttrs.has(n) && !n.endsWith(QuerySuffix) && a.value !== undefined) {
1987
+ if (userExprOverrides === undefined) {
1988
+ userExprOverrides = new Map();
1989
+ }
1990
+ userExprOverrides.set(n, a.value);
1991
+ }
1992
+ }
1993
+ }
1848
1994
  if (exprAttrs) {
1849
1995
  const ks = [...exprAttrs.keys()];
1850
1996
  for (let i = 0; i < ks.length; ++i) {
1851
1997
  const n = ks[i];
1852
- const expr: Expr | undefined = exprAttrs.get(n);
1853
- if (expr) {
1854
- await evaluateExpression(expr, newEnv);
1855
- const v: Result = newEnv.getLastResult();
1856
- newEnv.bind(n, v);
1857
- inst.attributes.set(n, v);
1858
- updatedAttrs?.set(n, v);
1998
+ const userValue = userExprOverrides?.get(n);
1999
+ if (userValue !== undefined) {
2000
+ // User explicitly provided a value for this @expr attribute - use it
2001
+ await evaluateExpression(userValue, newEnv);
2002
+ } else {
2003
+ // No user override - evaluate the @expr expression
2004
+ const expr: Expr | undefined = exprAttrs.get(n);
2005
+ if (expr) {
2006
+ await evaluateExpression(expr, newEnv);
2007
+ } else {
2008
+ continue;
2009
+ }
1859
2010
  }
2011
+ const v: Result = newEnv.getLastResult();
2012
+ newEnv.bind(n, v);
2013
+ inst.attributes.set(n, v);
2014
+ updatedAttrs?.set(n, v);
1860
2015
  }
1861
2016
  }
1862
- if (origAttrs && updatedAttrs) {
2017
+ // Re-evaluate non-@expr attribute expressions from origAttrs in the context
2018
+ // of the queried instance. This handles workflow update expressions like
2019
+ // `balance balance + (balance * interestRate) + makeDeposit.amount` where
2020
+ // the local attribute references must resolve from the existing instance.
2021
+ // Only runs on the update path (where updatedAttrs is a separate map from
2022
+ // inst.attributes) to avoid double-evaluating expressions on create.
2023
+ if (origAttrs && updatedAttrs && updatedAttrs !== inst.attributes) {
1863
2024
  for (let i = 0; i < origAttrs.length; ++i) {
1864
2025
  const a: SetAttribute = origAttrs[i];
1865
2026
  const n = a.name;
2027
+ if (exprAttrs?.has(n)) continue;
1866
2028
  if (!n.endsWith(QuerySuffix) && updatedAttrs.has(n) && a.value !== undefined) {
1867
2029
  await evaluateExpression(a.value, newEnv);
1868
2030
  const v: Result = newEnv.getLastResult();
@@ -2017,6 +2179,8 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2017
2179
  console.debug(invokeDebugMsg);
2018
2180
  //
2019
2181
 
2182
+ const agentChatId = env.getAgentChatId() || env.getActiveChatId() || '';
2183
+ await clearCancellation(agentChatId);
2020
2184
  const monitoringEnabled = isMonitoringEnabled();
2021
2185
 
2022
2186
  await agent.invoke(msg, env);
@@ -2033,8 +2197,14 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2033
2197
  }
2034
2198
  let retries = 0;
2035
2199
  while (true) {
2200
+ await checkCancelled(agentChatId);
2036
2201
  try {
2037
2202
  let rs: string = result ? normalizeGeneratedCode(result) : '';
2203
+ if (agent.tools) {
2204
+ env.setLastPattern(rs);
2205
+ } else {
2206
+ env.setLastPattern(undefined);
2207
+ }
2038
2208
  let isWf = rs.startsWith('workflow');
2039
2209
  if (isWf && !agent.runWorkflows) {
2040
2210
  await parseWorkflow(rs);
@@ -2089,6 +2259,7 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2089
2259
  } else {
2090
2260
  let retries = 0;
2091
2261
  while (true) {
2262
+ await checkCancelled(agentChatId);
2092
2263
  try {
2093
2264
  result = normalizeGeneratedCode(result);
2094
2265
  const obj = agent.maybeValidateJsonResponse(result);
@@ -2128,6 +2299,10 @@ export async function handleAgentInvocation(
2128
2299
  env: Environment
2129
2300
  ): Promise<void> {
2130
2301
  const agent: AgentInstance = await findAgentByName(agentEventInst.name, env);
2302
+ const chatId = agentEventInst.lookup('chatId');
2303
+ if (chatId) {
2304
+ env.setActiveChatId(chatId);
2305
+ }
2131
2306
  const origMsg: any =
2132
2307
  agentEventInst.lookup('message') || JSON.stringify(agentEventInst.asObject());
2133
2308
  const msg: string = isString(origMsg) ? origMsg : maybeInstanceAsString(origMsg);
@@ -2137,7 +2312,6 @@ export async function handleAgentInvocation(
2137
2312
  } else {
2138
2313
  const mode = agentEventInst.lookup('mode');
2139
2314
  let activeEnv = env;
2140
- const chatId = agentEventInst.lookup('chatId');
2141
2315
  if (chatId !== undefined) {
2142
2316
  activeEnv.setAgentChatId(chatId);
2143
2317
  }
@@ -2211,9 +2385,11 @@ async function iterateOnFlow(
2211
2385
  rootAgent.disableSession();
2212
2386
  const chatId = env.getActiveEventInstance()?.lookup('chatId');
2213
2387
  const iterId = chatId || crypto.randomUUID();
2388
+ await clearCancellation(iterId);
2214
2389
  let step = '';
2215
2390
  let fullFlowRetries = 0;
2216
2391
  while (true) {
2392
+ await checkCancelled(iterId);
2217
2393
  try {
2218
2394
  const initContext = msg;
2219
2395
  const s = `Now consider the following flowchart and return the next step:\n${flow}\n
@@ -2236,6 +2412,7 @@ async function iterateOnFlow(
2236
2412
  env.flagMonitorEntryAsFlow().incrementMonitor();
2237
2413
  }
2238
2414
  while (step != 'DONE' && !executedSteps.has(step)) {
2415
+ await checkCancelled(iterId);
2239
2416
  if (stepc > MaxFlowSteps) {
2240
2417
  throw new Error(`Flow execution exceeded maximum steps limit`);
2241
2418
  }
@@ -2290,6 +2467,9 @@ async function iterateOnFlow(
2290
2467
  needAgentProcessing = preprocResult.needAgentProcessing;
2291
2468
  }
2292
2469
  } catch (reason: any) {
2470
+ if (reason instanceof AgentCancelledException) {
2471
+ throw reason;
2472
+ }
2293
2473
  if (fullFlowRetries < MaxFlowRetries) {
2294
2474
  msg = `The previous attempt failed at step ${step} with the error ${reason}. Restart the flow the appropriate step
2295
2475
  (maybe even from the first step) and try to fix the issue.`;
@@ -2522,6 +2702,32 @@ export async function evaluateExpression(expr: Expr, env: Environment): Promise<
2522
2702
  env.setLastResult(result);
2523
2703
  }
2524
2704
 
2705
+ export function extractRefsFromExpr(expr: Expr): string[] {
2706
+ const refs: string[] = [];
2707
+ function walk(e: Expr) {
2708
+ if (isBinExpr(e)) {
2709
+ walk(e.e1);
2710
+ walk(e.e2);
2711
+ } else if (isLiteral(e)) {
2712
+ if (e.ref) refs.push(e.ref);
2713
+ if (e.fnCall) {
2714
+ for (const arg of e.fnCall.args) walk(arg);
2715
+ }
2716
+ if (e.asyncFnCall) {
2717
+ for (const arg of e.asyncFnCall.fnCall.args) walk(arg);
2718
+ }
2719
+ } else if (isGroup(e)) {
2720
+ walk(e.ge);
2721
+ } else if (isNegExpr(e)) {
2722
+ walk(e.ne);
2723
+ } else if (isNotExpr(e)) {
2724
+ walk(e.ne);
2725
+ }
2726
+ }
2727
+ walk(expr);
2728
+ return refs;
2729
+ }
2730
+
2525
2731
  async function getRef(r: string, src: any, env: Environment): Promise<Result> {
2526
2732
  if (Instance.IsInstance(src)) return src.lookup(r);
2527
2733
  else if (src instanceof Map) return src.get(r);