agentlang 0.9.6 → 0.9.8

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 (78) hide show
  1. package/out/cli/main.d.ts.map +1 -1
  2. package/out/cli/main.js +8 -3
  3. package/out/cli/main.js.map +1 -1
  4. package/out/extension/main.cjs +250 -250
  5. package/out/extension/main.cjs.map +2 -2
  6. package/out/language/generated/ast.js +1 -0
  7. package/out/language/generated/ast.js.map +1 -1
  8. package/out/language/main.cjs +2420 -776
  9. package/out/language/main.cjs.map +4 -4
  10. package/out/runtime/docs.d.ts.map +1 -1
  11. package/out/runtime/docs.js +109 -7
  12. package/out/runtime/docs.js.map +1 -1
  13. package/out/runtime/embeddings/chunker.d.ts +9 -0
  14. package/out/runtime/embeddings/chunker.d.ts.map +1 -0
  15. package/out/runtime/embeddings/chunker.js +41 -0
  16. package/out/runtime/embeddings/chunker.js.map +1 -0
  17. package/out/runtime/embeddings/index.d.ts +6 -0
  18. package/out/runtime/embeddings/index.d.ts.map +1 -0
  19. package/out/runtime/embeddings/index.js +6 -0
  20. package/out/runtime/embeddings/index.js.map +1 -0
  21. package/out/runtime/embeddings/openai.d.ts +15 -0
  22. package/out/runtime/embeddings/openai.d.ts.map +1 -0
  23. package/out/runtime/embeddings/openai.js +34 -0
  24. package/out/runtime/embeddings/openai.js.map +1 -0
  25. package/out/runtime/embeddings/provider.d.ts +20 -0
  26. package/out/runtime/embeddings/provider.d.ts.map +1 -0
  27. package/out/runtime/embeddings/provider.js +17 -0
  28. package/out/runtime/embeddings/provider.js.map +1 -0
  29. package/out/runtime/embeddings/registry.d.ts +3 -0
  30. package/out/runtime/embeddings/registry.d.ts.map +1 -0
  31. package/out/runtime/embeddings/registry.js +16 -0
  32. package/out/runtime/embeddings/registry.js.map +1 -0
  33. package/out/runtime/interpreter.d.ts.map +1 -1
  34. package/out/runtime/interpreter.js +8 -4
  35. package/out/runtime/interpreter.js.map +1 -1
  36. package/out/runtime/module.d.ts +24 -2
  37. package/out/runtime/module.d.ts.map +1 -1
  38. package/out/runtime/module.js +94 -7
  39. package/out/runtime/module.js.map +1 -1
  40. package/out/runtime/modules/ai.d.ts +2 -0
  41. package/out/runtime/modules/ai.d.ts.map +1 -1
  42. package/out/runtime/modules/ai.js +81 -18
  43. package/out/runtime/modules/ai.js.map +1 -1
  44. package/out/runtime/resolvers/sqldb/database.d.ts +1 -1
  45. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  46. package/out/runtime/resolvers/sqldb/database.js +177 -51
  47. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  48. package/out/runtime/resolvers/sqldb/impl.d.ts +21 -1
  49. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  50. package/out/runtime/resolvers/sqldb/impl.js +176 -45
  51. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  52. package/package.json +188 -185
  53. package/public/pdf.worker.mjs +65152 -0
  54. package/src/cli/main.ts +7 -2
  55. package/src/language/generated/ast.ts +1 -1
  56. package/src/runtime/docs.ts +120 -9
  57. package/src/runtime/embeddings/chunker.ts +50 -0
  58. package/src/runtime/embeddings/index.ts +5 -0
  59. package/src/runtime/embeddings/openai.ts +49 -0
  60. package/src/runtime/embeddings/provider.ts +37 -0
  61. package/src/runtime/embeddings/registry.ts +17 -0
  62. package/src/runtime/interpreter.ts +16 -12
  63. package/src/runtime/module.ts +148 -40
  64. package/src/runtime/modules/ai.ts +100 -20
  65. package/src/runtime/resolvers/sqldb/database.ts +190 -59
  66. package/src/runtime/resolvers/sqldb/impl.ts +235 -58
  67. package/out/setupClassic.d.ts +0 -98
  68. package/out/setupClassic.d.ts.map +0 -1
  69. package/out/setupClassic.js +0 -38
  70. package/out/setupClassic.js.map +0 -1
  71. package/out/setupCommon.d.ts +0 -2
  72. package/out/setupCommon.d.ts.map +0 -1
  73. package/out/setupCommon.js +0 -33
  74. package/out/setupCommon.js.map +0 -1
  75. package/out/setupExtended.d.ts +0 -40
  76. package/out/setupExtended.d.ts.map +0 -1
  77. package/out/setupExtended.js +0 -67
  78. package/out/setupExtended.js.map +0 -1
package/src/cli/main.ts CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  setRuntimeMode_init_schema,
39
39
  setRuntimeMode_migration,
40
40
  setRuntimeMode_prod,
41
+ setRuntimeMode_test,
41
42
  setRuntimeMode_undo_migration,
42
43
  updateEndpoints,
43
44
  } from '../runtime/defs.js';
@@ -216,8 +217,12 @@ async function internDynamicModule(name: string, definition: string): Promise<st
216
217
  setInternDynamicModuleFn(internDynamicModule);
217
218
 
218
219
  export const runModule = async (fileName: string, releaseDb: boolean = false): Promise<void> => {
219
- if (isRuntimeMode_dev() && process.env.NODE_ENV === 'production') {
220
- setRuntimeMode_prod();
220
+ if (isRuntimeMode_dev()) {
221
+ if (process.env.NODE_ENV === 'production') {
222
+ setRuntimeMode_prod();
223
+ } else if (process.env.NODE_ENV === 'test') {
224
+ setRuntimeMode_test();
225
+ }
221
226
  }
222
227
  const r: boolean = await runPreInitTasks();
223
228
  if (!r) {
@@ -3,7 +3,7 @@
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
 
6
-
6
+ /* eslint-disable */
7
7
  import * as langium from 'langium';
8
8
 
9
9
  export const AgentlangTerminals = {
@@ -1,5 +1,6 @@
1
- import { getFileSystem } from '../utils/fs-utils.js';
2
1
  import { logger } from './logger.js';
2
+ import { isNodeEnv } from '../utils/runtime.js';
3
+ import { getFileSystem } from '../utils/fs-utils.js';
3
4
 
4
5
  const DocFetchers = new Map<string, Function>();
5
6
 
@@ -25,6 +26,39 @@ export async function fetchDoc(url: string): Promise<string | undefined> {
25
26
  else return undefined;
26
27
  }
27
28
 
29
+ let PDFParse: any = null;
30
+ let pdfWorkerSet = false;
31
+
32
+ async function getPDFParse() {
33
+ if (!PDFParse) {
34
+ const pdfModule = await import('pdf-parse');
35
+ PDFParse = pdfModule.PDFParse;
36
+
37
+ // Set up web worker for browser
38
+ if (!isNodeEnv && !pdfWorkerSet && PDFParse.setWorker) {
39
+ // Worker is served from public/ directory
40
+ PDFParse.setWorker('/pdf.worker.mjs');
41
+ pdfWorkerSet = true;
42
+ }
43
+ }
44
+ return PDFParse;
45
+ }
46
+
47
+ async function parsePdfBuffer(buffer: Uint8Array): Promise<string> {
48
+ try {
49
+ const PDFParseClass = await getPDFParse();
50
+ const parser = new PDFParseClass({
51
+ data: buffer,
52
+ verbosity: 0,
53
+ });
54
+ const data = await parser.getText();
55
+ return data.text;
56
+ } catch (error: any) {
57
+ logger.error(`Failed to parse PDF: ${error.message}`);
58
+ throw new Error(`PDF parsing failed: ${error.message}`);
59
+ }
60
+ }
61
+
28
62
  async function httpFetcher(url: string): Promise<string | undefined> {
29
63
  try {
30
64
  const response = await fetch(url, {
@@ -32,12 +66,24 @@ async function httpFetcher(url: string): Promise<string | undefined> {
32
66
  });
33
67
 
34
68
  if (!response.ok) {
35
- logger.error(
36
- `Failed to fetch document ${url}, HTTP error! status: ${response.status} ${response.text} ${response.statusText}`
37
- );
69
+ logger.error(`Failed to fetch document ${url}, HTTP error! status: ${response.status}`);
38
70
  return undefined;
39
71
  }
40
- return await response.text();
72
+
73
+ const contentType = response.headers.get('content-type') || '';
74
+ const content = await response.arrayBuffer();
75
+
76
+ const lowerUrl = url.toLowerCase();
77
+
78
+ // Process based on content type or file extension
79
+ if (contentType.includes('application/pdf') || lowerUrl.endsWith('.pdf')) {
80
+ return await parsePdfBuffer(new Uint8Array(content));
81
+ } else if (contentType.includes('text/markdown') || lowerUrl.endsWith('.md')) {
82
+ return new TextDecoder().decode(content);
83
+ } else {
84
+ // Default to text
85
+ return new TextDecoder().decode(content);
86
+ }
41
87
  } catch (reason: any) {
42
88
  logger.error(`Failed to fetch document ${url}: ${reason}`);
43
89
  }
@@ -45,11 +91,76 @@ async function httpFetcher(url: string): Promise<string | undefined> {
45
91
  }
46
92
 
47
93
  async function fetchFile(path: string): Promise<string> {
48
- const fs = await getFileSystem();
49
- if (path.startsWith('.')) {
50
- path = `${process.cwd()}${path.substring(1)}`;
94
+ const lowerPath = path.toLowerCase();
95
+
96
+ if (lowerPath.endsWith('.pdf')) {
97
+ return await fetchPdfFile(path);
98
+ } else if (
99
+ lowerPath.endsWith('.md') ||
100
+ lowerPath.endsWith('.markdown') ||
101
+ lowerPath.endsWith('.mdown')
102
+ ) {
103
+ return await fetchMarkdownFile(path);
104
+ } else {
105
+ // Default: plain text
106
+ return await fetchTextFile(path);
107
+ }
108
+ }
109
+
110
+ async function fetchPdfFile(path: string): Promise<string> {
111
+ try {
112
+ const fs = await getFileSystem();
113
+
114
+ if (isNodeEnv && path.startsWith('.')) {
115
+ path = `${process.cwd()}${path.substring(1)}`;
116
+ }
117
+
118
+ const content = await fs.readFile(path);
119
+
120
+ let buffer: Uint8Array;
121
+ if (typeof content === 'string') {
122
+ buffer = new TextEncoder().encode(content);
123
+ } else if (Buffer.isBuffer(content)) {
124
+ buffer = new Uint8Array(content);
125
+ } else {
126
+ buffer = new Uint8Array(Buffer.from(content));
127
+ }
128
+
129
+ return await parsePdfBuffer(buffer);
130
+ } catch (error: any) {
131
+ logger.error(`Failed to read PDF file ${path}: ${error.message}`);
132
+ throw error;
133
+ }
134
+ }
135
+
136
+ async function fetchMarkdownFile(path: string): Promise<string> {
137
+ try {
138
+ const fs = await getFileSystem();
139
+
140
+ if (isNodeEnv && path.startsWith('.')) {
141
+ path = `${process.cwd()}${path.substring(1)}`;
142
+ }
143
+
144
+ return await fs.readFile(path);
145
+ } catch (error: any) {
146
+ logger.error(`Failed to read Markdown file ${path}: ${error.message}`);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ async function fetchTextFile(path: string): Promise<string> {
152
+ try {
153
+ const fs = await getFileSystem();
154
+
155
+ if (isNodeEnv && path.startsWith('.')) {
156
+ path = `${process.cwd()}${path.substring(1)}`;
157
+ }
158
+
159
+ return await fs.readFile(path);
160
+ } catch (error: any) {
161
+ logger.error(`Failed to read text file ${path}: ${error.message}`);
162
+ throw error;
51
163
  }
52
- return fs.readFile(path);
53
164
  }
54
165
 
55
166
  registerDocFetcher('http', httpFetcher);
@@ -0,0 +1,50 @@
1
+ export class TextChunker {
2
+ private chunkSize: number;
3
+ private chunkOverlap: number;
4
+ private separators: string[] = ['\n\n', '\n', '. ', ' ', ''];
5
+
6
+ constructor(chunkSize: number = 1000, chunkOverlap: number = 200) {
7
+ this.chunkSize = chunkSize;
8
+ this.chunkOverlap = chunkOverlap;
9
+ }
10
+
11
+ splitText(text: string): string[] {
12
+ if (text.length <= this.chunkSize) {
13
+ return [text];
14
+ }
15
+
16
+ const chunks: string[] = [];
17
+ let start = 0;
18
+
19
+ while (start < text.length) {
20
+ let end = Math.min(start + this.chunkSize, text.length);
21
+
22
+ if (end < text.length) {
23
+ end = this.findBestSplitPoint(text, start, end);
24
+ }
25
+
26
+ chunks.push(text.substring(start, end));
27
+ start = end - this.chunkOverlap;
28
+
29
+ if (start < 0) start = 0;
30
+ if (start >= text.length - this.chunkOverlap) {
31
+ if (start < text.length) {
32
+ chunks.push(text.substring(start));
33
+ }
34
+ break;
35
+ }
36
+ }
37
+
38
+ return chunks;
39
+ }
40
+
41
+ private findBestSplitPoint(text: string, start: number, end: number): number {
42
+ for (const sep of this.separators) {
43
+ const lastSep = text.lastIndexOf(sep, end);
44
+ if (lastSep > start) {
45
+ return Math.min(lastSep + sep.length, end);
46
+ }
47
+ }
48
+ return end;
49
+ }
50
+ }
@@ -0,0 +1,5 @@
1
+ export { EmbeddingProvider, EmbeddingProviderConfig } from './provider.js';
2
+ export { EmbeddingService } from '../resolvers/sqldb/impl.js';
3
+ export { TextChunker } from './chunker.js';
4
+ export { embeddingProvider, getDefaultEmbeddingProvider } from './registry.js';
5
+ export { OpenAIEmbeddingProvider, OpenAIEmbeddingConfig } from './openai.js';
@@ -0,0 +1,49 @@
1
+ import { OpenAIEmbeddings } from '@langchain/openai';
2
+ import { EmbeddingProvider, EmbeddingProviderConfig } from './provider.js';
3
+ import { getLocalEnv } from '../auth/defs.js';
4
+
5
+ export interface OpenAIEmbeddingConfig extends EmbeddingProviderConfig {
6
+ model?: string;
7
+ dimensions?: number;
8
+ maxRetries?: number;
9
+ }
10
+
11
+ export class OpenAIEmbeddingProvider extends EmbeddingProvider {
12
+ private openaiConfig: OpenAIEmbeddingConfig;
13
+
14
+ constructor(config?: EmbeddingProviderConfig) {
15
+ super(config || {});
16
+ this.openaiConfig = (this.config as OpenAIEmbeddingConfig) || {};
17
+ }
18
+
19
+ protected createEmbeddings(): OpenAIEmbeddings {
20
+ const config: any = {
21
+ apiKey: this.resolveApiKey(),
22
+ };
23
+
24
+ if (this.openaiConfig.model) {
25
+ config.model = this.openaiConfig.model;
26
+ }
27
+
28
+ if (this.openaiConfig.dimensions) {
29
+ config.dimensions = this.openaiConfig.dimensions;
30
+ }
31
+
32
+ if (this.openaiConfig.maxRetries !== undefined) {
33
+ config.maxRetries = this.openaiConfig.maxRetries;
34
+ }
35
+
36
+ return new OpenAIEmbeddings(config);
37
+ }
38
+
39
+ protected resolveApiKey(): string {
40
+ if (this.openaiConfig.apiKey) {
41
+ return this.openaiConfig.apiKey;
42
+ }
43
+ return process.env.AGENTLANG_OPENAI_KEY || getLocalEnv('AGENTLANG_OPENAI_KEY') || '';
44
+ }
45
+
46
+ getProviderName(): string {
47
+ return 'openai';
48
+ }
49
+ }
@@ -0,0 +1,37 @@
1
+ import { Embeddings } from '@langchain/core/embeddings';
2
+
3
+ export interface EmbeddingProviderConfig {
4
+ chunkSize?: number;
5
+ chunkOverlap?: number;
6
+ apiKey?: string;
7
+ model?: string;
8
+ [key: string]: any;
9
+ }
10
+
11
+ export abstract class EmbeddingProvider {
12
+ protected config: EmbeddingProviderConfig;
13
+ protected embeddings: Embeddings;
14
+
15
+ constructor(config: EmbeddingProviderConfig = {}) {
16
+ this.config = config;
17
+ this.embeddings = this.createEmbeddings();
18
+ }
19
+
20
+ protected abstract createEmbeddings(): Embeddings;
21
+ protected abstract resolveApiKey(): string;
22
+
23
+ abstract getProviderName(): string;
24
+
25
+ async embedText(text: string): Promise<number[]> {
26
+ return await this.embeddings.embedQuery(text);
27
+ }
28
+
29
+ getConfig(): EmbeddingProviderConfig {
30
+ return { ...this.config };
31
+ }
32
+
33
+ updateConfig(newConfig: Partial<EmbeddingProviderConfig>): void {
34
+ this.config = { ...this.config, ...newConfig };
35
+ this.embeddings = this.createEmbeddings();
36
+ }
37
+ }
@@ -0,0 +1,17 @@
1
+ import { OpenAIEmbeddingProvider } from './openai.js';
2
+
3
+ const EmbeddingProviders = new Map().set('openai', OpenAIEmbeddingProvider);
4
+
5
+ export function embeddingProvider(service: string): any {
6
+ const requestedService = service.toLowerCase();
7
+ const provider = EmbeddingProviders.get(requestedService);
8
+ if (provider) {
9
+ return provider;
10
+ } else {
11
+ throw new Error(`No embedding provider found for ${service}`);
12
+ }
13
+ }
14
+
15
+ export function getDefaultEmbeddingProvider(): string {
16
+ return 'openai';
17
+ }
@@ -32,8 +32,8 @@ import {
32
32
  WhereSpec,
33
33
  } from '../language/generated/ast.js';
34
34
  import {
35
- maybeInstanceAsString,
36
35
  defineAgentEvent,
36
+ Event,
37
37
  getOneOfRef,
38
38
  getRelationship,
39
39
  getWorkflow,
@@ -46,15 +46,15 @@ import {
46
46
  isEntityInstance,
47
47
  isEventInstance,
48
48
  isInstanceOfType,
49
+ isOneToOneBetweenRelationship,
49
50
  isTimer,
50
51
  makeInstance,
52
+ maybeInstanceAsString,
51
53
  newInstanceAttributes,
52
54
  PlaceholderRecordEntry,
53
55
  Relationship,
54
- Workflow,
55
56
  setMetaAttributes,
56
- Event,
57
- isOneToOneBetweenRelationship,
57
+ Workflow,
58
58
  } from './module.js';
59
59
  import { JoinInfo, Resolver, WhereClause } from './resolvers/interface.js';
60
60
  import { ResolverAuthInfo } from './resolvers/authinfo.js';
@@ -65,26 +65,26 @@ import {
65
65
  escapeFqName,
66
66
  escapeQueryName,
67
67
  fqNameFromPath,
68
+ isCoreModule,
68
69
  isFqName,
69
70
  isPath,
70
71
  isString,
71
72
  makeCoreModuleName,
72
73
  makeFqName,
74
+ nameToPath,
73
75
  Path,
76
+ preprocessRawConfig,
74
77
  QuerySuffix,
75
78
  restoreSpecialChars,
76
- nameToPath,
77
79
  splitRefs,
78
- isCoreModule,
79
- preprocessRawConfig,
80
80
  } from './util.js';
81
81
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
82
82
  import { parseStatement, parseWorkflow } from '../language/parser.js';
83
83
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
84
84
  import {
85
- AgentInstance,
86
85
  AgentEntityName,
87
86
  AgentFqName,
87
+ AgentInstance,
88
88
  findAgentByName,
89
89
  normalizeGeneratedCode,
90
90
  } from './modules/ai.js';
@@ -1755,11 +1755,15 @@ async function handleDocEvent(inst: Instance, env: Environment): Promise<void> {
1755
1755
  const s = await fetchDoc(inst.lookup('url'));
1756
1756
  if (s) {
1757
1757
  const title = inst.lookup('title');
1758
- await parseAndEvaluateStatement(
1759
- `{${CoreAIModuleName}/Document {title "${title}", content "${s}"}}`,
1760
- undefined,
1761
- env
1758
+ const doc = makeInstance(
1759
+ CoreAIModuleName,
1760
+ 'Document',
1761
+ newInstanceAttributes().set('title', title).set('content', s)
1762
1762
  );
1763
+ await computeExprAttributes(doc, undefined, undefined, env);
1764
+ await setMetaAttributes(doc.attributes, env);
1765
+ const res: Resolver = await getResolverForPath('Document', CoreAIModuleName, env);
1766
+ await res.createInstance(doc);
1763
1767
  }
1764
1768
  }
1765
1769
 
@@ -1,57 +1,58 @@
1
1
  import chalk from 'chalk';
2
2
  import {
3
- Statement,
4
- KvPair,
5
- Literal,
3
+ AttributeDefinition,
4
+ Expr,
5
+ FlowDefinition,
6
+ FlowEntry,
6
7
  FnCall,
7
- RelNodes,
8
+ isLiteral,
8
9
  isRelNodes,
9
- AttributeDefinition,
10
- PropertyDefinition,
11
- NodeDefinition,
12
- RecordSchemaDefinition,
10
+ KvPair,
11
+ Literal,
13
12
  MapEntry,
14
- isLiteral,
15
13
  MetaDefinition,
14
+ NodeDefinition,
16
15
  PrePostTriggerDefinition,
17
- TriggerEntry,
18
- Expr,
19
- RbacSpecEntry,
20
- RbacSpecEntries,
16
+ PropertyDefinition,
21
17
  RbacOpr,
22
- WorkflowHeader,
23
- FlowDefinition,
24
- FlowEntry,
18
+ RbacSpecEntries,
19
+ RbacSpecEntry,
20
+ RecordSchemaDefinition,
21
+ RelNodes,
22
+ Statement,
23
+ TriggerEntry,
25
24
  WorkflowDirectives,
25
+ WorkflowHeader,
26
26
  } from '../language/generated/ast.js';
27
27
  import {
28
- Path,
29
- nameToPath,
30
- isString,
31
- isNumber,
32
- isBoolean,
33
- isFqName,
34
- makeFqName,
28
+ asCrudType,
29
+ CrudType,
35
30
  DefaultModuleName,
36
31
  DefaultModules,
37
- joinStatements,
38
- isMinusZero,
39
- now,
40
- findMetaSchema,
32
+ encryptPassword,
33
+ escapeFqName,
34
+ escapeSpecialChars,
41
35
  findAllPrePostTriggerSchema,
42
- CrudType,
43
- asCrudType,
44
- isPath,
36
+ findMetaSchema,
45
37
  findUqCompositeAttributes,
46
- escapeFqName,
47
- encryptPassword,
48
- splitFqName,
49
- splitRefs,
50
38
  forceAsFqName,
51
- validateIdFormat,
39
+ isBoolean,
40
+ isFqName,
41
+ isMinusZero,
42
+ isNumber,
43
+ isPath,
44
+ isString,
45
+ joinStatements,
46
+ makeFqName,
52
47
  nameContainsSepEscape,
48
+ nameToPath,
49
+ now,
50
+ Path,
53
51
  registerInitFunction,
54
52
  ScratchModuleName,
53
+ splitFqName,
54
+ splitRefs,
55
+ validateIdFormat,
55
56
  } from './util.js';
56
57
  import { parseStatement } from '../language/parser.js';
57
58
  import { ActiveSessionInfo, AdminSession } from './auth/defs.js';
@@ -513,7 +514,10 @@ export class Record extends ModuleEntry {
513
514
  }
514
515
 
515
516
  getFullTextSearchAttributes(): string[] | undefined {
516
- const fts: string[] | string | undefined = this.getMeta('fullTextSearch');
517
+ let fts: string[] | string | undefined = this.getMeta('fullTextSearchAttributes');
518
+ if (!fts) {
519
+ fts = this.getMeta('fullTextSearch');
520
+ }
517
521
  if (fts) {
518
522
  if (fts instanceof Array) {
519
523
  return fts as string[];
@@ -527,6 +531,14 @@ export class Record extends ModuleEntry {
527
531
  }
528
532
  }
529
533
 
534
+ getEmbeddingConfig(): { [key: string]: any } | undefined {
535
+ const config = this.getMeta('embeddingConfig');
536
+ if (config && typeof config === 'object') {
537
+ return config as { [key: string]: any };
538
+ }
539
+ return undefined;
540
+ }
541
+
530
542
  private resetUserAttrs() {
531
543
  this.userAttrNames = undefined;
532
544
  this.userAttrsSchema = undefined;
@@ -1150,7 +1162,16 @@ export class Agent extends Record {
1150
1162
  if (!skip && value !== null && value !== undefined) {
1151
1163
  let v = value;
1152
1164
  const isf = key == 'flows';
1153
- if (isf || key == 'tools') {
1165
+ const isDocs = key == 'documents';
1166
+ if (isDocs) {
1167
+ const raw = isString(v) ? v : `${v}`;
1168
+ const items = raw
1169
+ .split(',')
1170
+ .map((entry: string) => entry.trim())
1171
+ .filter((entry: string) => entry.length > 0)
1172
+ .map((entry: string) => entry.replace(/\\/g, '\\\\').replace(/"/g, '\\"'));
1173
+ v = `[${items.map((entry: string) => `"${entry}"`).join(', ')}]`;
1174
+ } else if (isf || key == 'tools') {
1154
1175
  if (isf || v.indexOf(',') > 0 || v.indexOf('/') > 0) v = `[${v}]`;
1155
1176
  else v = `"${v}"`;
1156
1177
  } else if (isString(v)) {
@@ -1874,6 +1895,42 @@ export class GlossaryEntry extends ModuleEntry {
1874
1895
  }
1875
1896
  }
1876
1897
 
1898
+ export type ModuleDocument = {
1899
+ title: string;
1900
+ url: string;
1901
+ };
1902
+
1903
+ export class DocumentEntry extends ModuleEntry {
1904
+ private doc: ModuleDocument;
1905
+
1906
+ constructor(title: string, moduleName: string, url: string) {
1907
+ super(title, moduleName);
1908
+ this.doc = { title, url };
1909
+ }
1910
+
1911
+ getUrl(): string {
1912
+ return this.doc.url;
1913
+ }
1914
+
1915
+ getTitle(): string {
1916
+ return this.doc.title;
1917
+ }
1918
+
1919
+ override toString(): string {
1920
+ const escapedTitle = escapeSpecialChars(this.doc.title);
1921
+ const escapedUrl = escapeSpecialChars(this.doc.url);
1922
+ return `{${DefaultModuleName}.ai/doc {\n title "${escapedTitle}",\n url "${escapedUrl}"\n}}`;
1923
+ }
1924
+
1925
+ get url(): string {
1926
+ return this.doc.url;
1927
+ }
1928
+
1929
+ set url(value: string) {
1930
+ this.doc.url = value;
1931
+ }
1932
+ }
1933
+
1877
1934
  function statementLabel(stmt: Statement | undefined): string {
1878
1935
  if (stmt === undefined) return '';
1879
1936
  let lbl: string | undefined = undefined;
@@ -2461,6 +2518,48 @@ export class Module {
2461
2518
  return this;
2462
2519
  }
2463
2520
 
2521
+ addDocument(title: string, url: string): Module {
2522
+ if (this.hasEntry(title)) {
2523
+ const existing = this.getEntry(title);
2524
+ if (existing instanceof DocumentEntry) {
2525
+ const doc = existing as DocumentEntry;
2526
+ doc.url = url;
2527
+ return this;
2528
+ }
2529
+ }
2530
+
2531
+ const docEntry = new DocumentEntry(title, this.name, url);
2532
+ this.addEntry(docEntry);
2533
+ return this;
2534
+ }
2535
+
2536
+ getDocument(title: string): string | undefined {
2537
+ if (this.hasEntry(title)) {
2538
+ const entry = this.getEntry(title);
2539
+ if (entry instanceof DocumentEntry) {
2540
+ return (entry as DocumentEntry).url;
2541
+ }
2542
+ }
2543
+ return undefined;
2544
+ }
2545
+
2546
+ getAllDocuments(): DocumentEntry[] {
2547
+ return this.entries.filter((e: ModuleEntry) => {
2548
+ return e instanceof DocumentEntry;
2549
+ }) as DocumentEntry[];
2550
+ }
2551
+
2552
+ removeDocument(title: string): Module {
2553
+ for (let i = 0; i < this.entries.length; ++i) {
2554
+ const entry = this.entries[i];
2555
+ if (entry.name === title && entry instanceof DocumentEntry) {
2556
+ this.entries.splice(i, 1);
2557
+ break;
2558
+ }
2559
+ }
2560
+ return this;
2561
+ }
2562
+
2464
2563
  addRetry(retry: Retry): Module {
2465
2564
  this.addEntry(retry);
2466
2565
  return this;
@@ -2496,7 +2595,7 @@ export class Module {
2496
2595
 
2497
2596
  private getEntryIndex(entryName: string): number {
2498
2597
  return this.entries.findIndex((v: ModuleEntry) => {
2499
- return v.name == entryName;
2598
+ return v.name === entryName;
2500
2599
  });
2501
2600
  }
2502
2601
 
@@ -2723,6 +2822,17 @@ export class Module {
2723
2822
 
2724
2823
  let GlobalRetries: Array<Retry> | undefined = undefined;
2725
2824
 
2825
+ const DocumentAliasMap: Map<string, string> = new Map();
2826
+
2827
+ export function registerDocumentAlias(name: string, title: string): void {
2828
+ if (!name || !title) return;
2829
+ DocumentAliasMap.set(name, title);
2830
+ }
2831
+
2832
+ export function resolveDocumentAliases(names: string[]): string[] {
2833
+ return names.map((name: string) => DocumentAliasMap.get(name) ?? name);
2834
+ }
2835
+
2726
2836
  export function addGlobalRetry(r: Retry): Retry {
2727
2837
  if (GlobalRetries === undefined) {
2728
2838
  GlobalRetries = new Array<Retry>();
@@ -2782,8 +2892,6 @@ export function removeModule(name: string): boolean {
2782
2892
  return false;
2783
2893
  }
2784
2894
 
2785
- addModule(DefaultModuleName);
2786
- addRecord('env', DefaultModuleName);
2787
2895
  addModule(ScratchModuleName);
2788
2896
 
2789
2897
  export function getModuleNames(): string[] {