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.
- package/README.md +7 -14
- package/out/api/http.d.ts +4 -0
- package/out/api/http.d.ts.map +1 -1
- package/out/api/http.js +307 -26
- package/out/api/http.js.map +1 -1
- package/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +3 -0
- package/out/cli/main.js.map +1 -1
- package/out/extension/main.cjs +250 -250
- package/out/extension/main.cjs.map +2 -2
- package/out/language/agentlang-validator.d.ts.map +1 -1
- package/out/language/agentlang-validator.js +4 -0
- package/out/language/agentlang-validator.js.map +1 -1
- package/out/language/error-reporter.d.ts +53 -0
- package/out/language/error-reporter.d.ts.map +1 -0
- package/out/language/error-reporter.js +879 -0
- package/out/language/error-reporter.js.map +1 -0
- package/out/language/generated/ast.d.ts +77 -2
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +60 -0
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +342 -206
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +901 -710
- package/out/language/main.cjs.map +3 -3
- package/out/language/parser.d.ts +4 -2
- package/out/language/parser.d.ts.map +1 -1
- package/out/language/parser.js +58 -99
- package/out/language/parser.js.map +1 -1
- package/out/language/syntax.d.ts +16 -0
- package/out/language/syntax.d.ts.map +1 -1
- package/out/language/syntax.js +66 -27
- package/out/language/syntax.js.map +1 -1
- package/out/runtime/api.d.ts +2 -0
- package/out/runtime/api.d.ts.map +1 -1
- package/out/runtime/api.js +25 -0
- package/out/runtime/api.js.map +1 -1
- package/out/runtime/datefns.d.ts +34 -0
- package/out/runtime/datefns.d.ts.map +1 -0
- package/out/runtime/datefns.js +82 -0
- package/out/runtime/datefns.js.map +1 -0
- package/out/runtime/defs.d.ts +1 -0
- package/out/runtime/defs.d.ts.map +1 -1
- package/out/runtime/defs.js +2 -1
- package/out/runtime/defs.js.map +1 -1
- package/out/runtime/document-retriever.d.ts +24 -0
- package/out/runtime/document-retriever.d.ts.map +1 -0
- package/out/runtime/document-retriever.js +258 -0
- package/out/runtime/document-retriever.js.map +1 -0
- package/out/runtime/embeddings/chunker.d.ts +18 -0
- package/out/runtime/embeddings/chunker.d.ts.map +1 -1
- package/out/runtime/embeddings/chunker.js +47 -15
- package/out/runtime/embeddings/chunker.js.map +1 -1
- package/out/runtime/embeddings/openai.d.ts.map +1 -1
- package/out/runtime/embeddings/openai.js +22 -9
- package/out/runtime/embeddings/openai.js.map +1 -1
- package/out/runtime/embeddings/provider.d.ts +1 -0
- package/out/runtime/embeddings/provider.d.ts.map +1 -1
- package/out/runtime/embeddings/provider.js +20 -1
- package/out/runtime/embeddings/provider.js.map +1 -1
- package/out/runtime/exec-graph.d.ts.map +1 -1
- package/out/runtime/exec-graph.js +22 -3
- package/out/runtime/exec-graph.js.map +1 -1
- package/out/runtime/integration-client.d.ts +21 -0
- package/out/runtime/integration-client.d.ts.map +1 -0
- package/out/runtime/integration-client.js +112 -0
- package/out/runtime/integration-client.js.map +1 -0
- package/out/runtime/integrations.d.ts.map +1 -1
- package/out/runtime/integrations.js +20 -9
- package/out/runtime/integrations.js.map +1 -1
- package/out/runtime/interpreter.d.ts +10 -0
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +221 -22
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +70 -7
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/logger.d.ts.map +1 -1
- package/out/runtime/logger.js +8 -1
- package/out/runtime/logger.js.map +1 -1
- package/out/runtime/module.d.ts +18 -0
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +91 -3
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +16 -5
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +286 -88
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/core.d.ts.map +1 -1
- package/out/runtime/modules/core.js +5 -1
- package/out/runtime/modules/core.js.map +1 -1
- package/out/runtime/monitor.d.ts +6 -0
- package/out/runtime/monitor.d.ts.map +1 -1
- package/out/runtime/monitor.js +21 -1
- package/out/runtime/monitor.js.map +1 -1
- package/out/runtime/relgraph.d.ts.map +1 -1
- package/out/runtime/relgraph.js +7 -3
- package/out/runtime/relgraph.js.map +1 -1
- package/out/runtime/resolvers/interface.d.ts +7 -2
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js +17 -3
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +2 -0
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +142 -126
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.js +25 -4
- package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +2 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +24 -7
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
- package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
- package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
- package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
- package/out/runtime/resolvers/vector/types.d.ts +32 -0
- package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
- package/out/runtime/resolvers/vector/types.js +2 -0
- package/out/runtime/resolvers/vector/types.js.map +1 -0
- package/out/runtime/services/documentFetcher.d.ts.map +1 -1
- package/out/runtime/services/documentFetcher.js +21 -6
- package/out/runtime/services/documentFetcher.js.map +1 -1
- package/out/runtime/state.d.ts +19 -1
- package/out/runtime/state.d.ts.map +1 -1
- package/out/runtime/state.js +36 -1
- package/out/runtime/state.js.map +1 -1
- package/out/runtime/util.d.ts +3 -2
- package/out/runtime/util.d.ts.map +1 -1
- package/out/runtime/util.js +13 -2
- package/out/runtime/util.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/out/test-harness.d.ts +36 -0
- package/out/test-harness.d.ts.map +1 -0
- package/out/test-harness.js +341 -0
- package/out/test-harness.js.map +1 -0
- package/package.json +22 -19
- package/src/api/http.ts +336 -38
- package/src/cli/main.ts +3 -0
- package/src/language/agentlang-validator.ts +3 -0
- package/src/language/agentlang.langium +6 -2
- package/src/language/error-reporter.ts +1028 -0
- package/src/language/generated/ast.ts +94 -1
- package/src/language/generated/grammar.ts +342 -206
- package/src/language/parser.ts +64 -101
- package/src/language/syntax.ts +79 -24
- package/src/runtime/api.ts +36 -0
- package/src/runtime/datefns.ts +112 -0
- package/src/runtime/defs.ts +2 -1
- package/src/runtime/document-retriever.ts +311 -0
- package/src/runtime/embeddings/chunker.ts +52 -14
- package/src/runtime/embeddings/openai.ts +27 -9
- package/src/runtime/embeddings/provider.ts +22 -1
- package/src/runtime/exec-graph.ts +23 -2
- package/src/runtime/integration-client.ts +158 -0
- package/src/runtime/integrations.ts +20 -11
- package/src/runtime/interpreter.ts +221 -15
- package/src/runtime/loader.ts +83 -5
- package/src/runtime/logger.ts +12 -1
- package/src/runtime/module.ts +104 -3
- package/src/runtime/modules/ai.ts +341 -107
- package/src/runtime/modules/core.ts +5 -1
- package/src/runtime/monitor.ts +27 -1
- package/src/runtime/relgraph.ts +7 -3
- package/src/runtime/resolvers/interface.ts +23 -3
- package/src/runtime/resolvers/sqldb/database.ts +158 -130
- package/src/runtime/resolvers/sqldb/dbutil.ts +28 -6
- package/src/runtime/resolvers/sqldb/impl.ts +25 -7
- package/src/runtime/resolvers/vector/lancedb-store.ts +187 -0
- package/src/runtime/resolvers/vector/types.ts +39 -0
- package/src/runtime/services/documentFetcher.ts +21 -6
- package/src/runtime/state.ts +40 -1
- package/src/runtime/util.ts +19 -2
- package/src/syntaxes/agentlang.monarch.ts +1 -1
- 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,
|
|
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
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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)
|
|
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,
|
|
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
|
-
|
|
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
|
|
1853
|
-
if (
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
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
|
-
|
|
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);
|