agentlang 0.10.2 → 0.10.4

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 (154) 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 +171 -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 +66 -6
  19. package/out/language/generated/ast.d.ts.map +1 -1
  20. package/out/language/generated/ast.js +48 -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 +320 -190
  24. package/out/language/generated/grammar.js.map +1 -1
  25. package/out/language/main.cjs +870 -694
  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 +31 -98
  30. package/out/language/parser.js.map +1 -1
  31. package/out/language/syntax.d.ts +2 -0
  32. package/out/language/syntax.d.ts.map +1 -1
  33. package/out/language/syntax.js +6 -0
  34. package/out/language/syntax.js.map +1 -1
  35. package/out/runtime/api.d.ts.map +1 -1
  36. package/out/runtime/api.js +22 -0
  37. package/out/runtime/api.js.map +1 -1
  38. package/out/runtime/defs.d.ts +1 -0
  39. package/out/runtime/defs.d.ts.map +1 -1
  40. package/out/runtime/defs.js +2 -1
  41. package/out/runtime/defs.js.map +1 -1
  42. package/out/runtime/document-retriever.d.ts +24 -0
  43. package/out/runtime/document-retriever.d.ts.map +1 -0
  44. package/out/runtime/document-retriever.js +258 -0
  45. package/out/runtime/document-retriever.js.map +1 -0
  46. package/out/runtime/embeddings/chunker.d.ts +18 -0
  47. package/out/runtime/embeddings/chunker.d.ts.map +1 -1
  48. package/out/runtime/embeddings/chunker.js +47 -15
  49. package/out/runtime/embeddings/chunker.js.map +1 -1
  50. package/out/runtime/embeddings/openai.d.ts.map +1 -1
  51. package/out/runtime/embeddings/openai.js +22 -9
  52. package/out/runtime/embeddings/openai.js.map +1 -1
  53. package/out/runtime/embeddings/provider.d.ts +1 -0
  54. package/out/runtime/embeddings/provider.d.ts.map +1 -1
  55. package/out/runtime/embeddings/provider.js +20 -1
  56. package/out/runtime/embeddings/provider.js.map +1 -1
  57. package/out/runtime/integration-client.d.ts +21 -0
  58. package/out/runtime/integration-client.d.ts.map +1 -0
  59. package/out/runtime/integration-client.js +112 -0
  60. package/out/runtime/integration-client.js.map +1 -0
  61. package/out/runtime/integrations.d.ts.map +1 -1
  62. package/out/runtime/integrations.js +20 -9
  63. package/out/runtime/integrations.js.map +1 -1
  64. package/out/runtime/interpreter.d.ts +1 -0
  65. package/out/runtime/interpreter.d.ts.map +1 -1
  66. package/out/runtime/interpreter.js +172 -19
  67. package/out/runtime/interpreter.js.map +1 -1
  68. package/out/runtime/loader.d.ts.map +1 -1
  69. package/out/runtime/loader.js +70 -7
  70. package/out/runtime/loader.js.map +1 -1
  71. package/out/runtime/logger.d.ts.map +1 -1
  72. package/out/runtime/logger.js +8 -1
  73. package/out/runtime/logger.js.map +1 -1
  74. package/out/runtime/module.d.ts +10 -0
  75. package/out/runtime/module.d.ts.map +1 -1
  76. package/out/runtime/module.js +68 -3
  77. package/out/runtime/module.js.map +1 -1
  78. package/out/runtime/modules/ai.d.ts +9 -2
  79. package/out/runtime/modules/ai.d.ts.map +1 -1
  80. package/out/runtime/modules/ai.js +219 -67
  81. package/out/runtime/modules/ai.js.map +1 -1
  82. package/out/runtime/modules/core.d.ts.map +1 -1
  83. package/out/runtime/modules/core.js +3 -0
  84. package/out/runtime/modules/core.js.map +1 -1
  85. package/out/runtime/modules/messaging.d.ts +10 -0
  86. package/out/runtime/modules/messaging.d.ts.map +1 -0
  87. package/out/runtime/modules/messaging.js +210 -0
  88. package/out/runtime/modules/messaging.js.map +1 -0
  89. package/out/runtime/resolvers/interface.d.ts +4 -0
  90. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  91. package/out/runtime/resolvers/interface.js +14 -1
  92. package/out/runtime/resolvers/interface.js.map +1 -1
  93. package/out/runtime/resolvers/sqldb/database.d.ts +2 -0
  94. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  95. package/out/runtime/resolvers/sqldb/database.js +142 -126
  96. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  97. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  98. package/out/runtime/resolvers/sqldb/dbutil.js +8 -0
  99. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  100. package/out/runtime/resolvers/sqldb/impl.d.ts +1 -0
  101. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  102. package/out/runtime/resolvers/sqldb/impl.js +7 -0
  103. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  104. package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
  105. package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
  106. package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
  107. package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
  108. package/out/runtime/resolvers/vector/types.d.ts +32 -0
  109. package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
  110. package/out/runtime/resolvers/vector/types.js +2 -0
  111. package/out/runtime/resolvers/vector/types.js.map +1 -0
  112. package/out/runtime/services/documentFetcher.d.ts.map +1 -1
  113. package/out/runtime/services/documentFetcher.js +21 -6
  114. package/out/runtime/services/documentFetcher.js.map +1 -1
  115. package/out/runtime/state.d.ts +19 -1
  116. package/out/runtime/state.d.ts.map +1 -1
  117. package/out/runtime/state.js +36 -1
  118. package/out/runtime/state.js.map +1 -1
  119. package/out/syntaxes/agentlang.monarch.js +1 -1
  120. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  121. package/package.json +19 -19
  122. package/src/api/http.ts +197 -37
  123. package/src/cli/main.ts +3 -0
  124. package/src/language/agentlang-validator.ts +3 -0
  125. package/src/language/agentlang.langium +3 -1
  126. package/src/language/error-reporter.ts +1028 -0
  127. package/src/language/generated/ast.ts +77 -5
  128. package/src/language/generated/grammar.ts +320 -190
  129. package/src/language/parser.ts +31 -100
  130. package/src/language/syntax.ts +8 -0
  131. package/src/runtime/api.ts +31 -0
  132. package/src/runtime/defs.ts +2 -1
  133. package/src/runtime/document-retriever.ts +311 -0
  134. package/src/runtime/embeddings/chunker.ts +52 -14
  135. package/src/runtime/embeddings/openai.ts +27 -9
  136. package/src/runtime/embeddings/provider.ts +22 -1
  137. package/src/runtime/integration-client.ts +158 -0
  138. package/src/runtime/integrations.ts +20 -11
  139. package/src/runtime/interpreter.ts +164 -14
  140. package/src/runtime/loader.ts +83 -5
  141. package/src/runtime/logger.ts +12 -1
  142. package/src/runtime/module.ts +78 -3
  143. package/src/runtime/modules/ai.ts +263 -76
  144. package/src/runtime/modules/core.ts +4 -0
  145. package/src/runtime/modules/messaging.ts +228 -0
  146. package/src/runtime/resolvers/interface.ts +19 -1
  147. package/src/runtime/resolvers/sqldb/database.ts +158 -130
  148. package/src/runtime/resolvers/sqldb/dbutil.ts +8 -0
  149. package/src/runtime/resolvers/sqldb/impl.ts +8 -0
  150. package/src/runtime/resolvers/vector/lancedb-store.ts +187 -0
  151. package/src/runtime/resolvers/vector/types.ts +39 -0
  152. package/src/runtime/services/documentFetcher.ts +21 -6
  153. package/src/runtime/state.ts +40 -1
  154. package/src/syntaxes/agentlang.monarch.ts +1 -1
@@ -26,6 +26,7 @@ import {
26
26
  asJSONSchema,
27
27
  Decision,
28
28
  fetchModule,
29
+ getAllDocumentsForTopics,
29
30
  getDecision,
30
31
  getGlobalRetry,
31
32
  Instance,
@@ -37,7 +38,9 @@ import {
37
38
  newInstanceAttributes,
38
39
  objectToInstanceAttributes,
39
40
  Record,
41
+ registerTopic as registerTopicInRegistry,
40
42
  resolveDocumentAliases,
43
+ resolveTopicNames,
41
44
  Retry,
42
45
  } from '../module.js';
43
46
  import { provider } from '../agents/registry.js';
@@ -71,9 +74,10 @@ import {
71
74
  import { logger } from '../logger.js';
72
75
  import { FlowStep } from '../agents/flows.js';
73
76
  import { Statement } from '../../language/generated/ast.js';
74
- import { isMonitoringEnabled, TtlCache } from '../state.js';
77
+ import { getKnowledgeGraphConfig, isMonitoringEnabled, TtlCache } from '../state.js';
75
78
  import { isNodeEnv } from '../../utils/runtime.js';
76
79
  import { getFileSystem } from '../../utils/fs-utils.js';
80
+ import { getDocumentRetriever } from '../document-retriever.js';
77
81
 
78
82
  export const CoreAIModuleName = makeCoreModuleName('ai');
79
83
  export const AgentEntityName = 'Agent';
@@ -82,6 +86,50 @@ export const AgentLearnerType = 'learner';
82
86
 
83
87
  const AgentEvalType = 'eval';
84
88
 
89
+ // --- Agent cancellation infrastructure ---
90
+
91
+ export class AgentCancelledException extends Error {
92
+ constructor(chatId: string) {
93
+ super(`Agent cancelled for chatId: ${chatId}`);
94
+ this.name = 'AgentCancelledException';
95
+ }
96
+ }
97
+
98
+ export async function cancelAgent(chatId: string): Promise<string> {
99
+ const t = `${CoreAIModuleName}/${CancelledAgentEntity}`;
100
+ await parseAndEvaluateStatement(`{${t} {chatId "${chatId}"}, @upsert}`);
101
+ return `Cancellation requested for chatId: ${chatId}`;
102
+ }
103
+
104
+ export async function checkCancelled(chatId: string): Promise<void> {
105
+ if (!chatId) return;
106
+ try {
107
+ const results: Instance[] = await parseAndEvaluateStatement(
108
+ `{${CoreAIModuleName}/${CancelledAgentEntity} {chatId? "${chatId}"}}`
109
+ );
110
+ if (results && results.length > 0) {
111
+ await parseAndEvaluateStatement(
112
+ `purge {${CoreAIModuleName}/${CancelledAgentEntity} {chatId? "${chatId}"}}`
113
+ );
114
+ throw new AgentCancelledException(chatId);
115
+ }
116
+ } catch (err: any) {
117
+ if (err instanceof AgentCancelledException) throw err;
118
+ logger.debug(`checkCancelled DB error for ${chatId}: ${err}`);
119
+ }
120
+ }
121
+
122
+ export async function clearCancellation(chatId: string): Promise<void> {
123
+ if (!chatId) return;
124
+ try {
125
+ await parseAndEvaluateStatement(
126
+ `purge {${CoreAIModuleName}/${CancelledAgentEntity} {chatId? "${chatId}"}}`
127
+ );
128
+ } catch (err: any) {
129
+ logger.debug(`clearCancellation DB error for ${chatId}: ${err}`);
130
+ }
131
+ }
132
+
85
133
  export default `module ${CoreAIModuleName}
86
134
 
87
135
  import "./modules/ai.js" @as ai
@@ -100,7 +148,8 @@ entity ${AgentEntityName} {
100
148
  stateless Boolean @default(false),
101
149
  instruction String @optional,
102
150
  tools String @optional, // comma-separated list of tool names
103
- documents String @optional, // comma-separated list of document names
151
+ documents String @optional, // list of document names
152
+ topics String @optional, // list of topic names
104
153
  channels String @optional, // comma-separated list of channel names
105
154
  role String @optional,
106
155
  flows String @optional,
@@ -147,6 +196,15 @@ workflow processDoc {
147
196
  await ai.fetchAndCreateDocument(doc.title, doc.url, doc.retrievalConfig, doc.embeddingConfig)
148
197
  }
149
198
 
199
+ event topic {
200
+ name String,
201
+ documents String @optional // comma-separated or array of document titles
202
+ }
203
+
204
+ workflow processTopic {
205
+ await ai.registerTopic(topic.name, topic.documents)
206
+ }
207
+
150
208
  entity Directive {
151
209
  id UUID @id @default(uuid()),
152
210
  agentFqName String @indexed,
@@ -202,6 +260,10 @@ entity AgentFlowStep {
202
260
  suspensionId String
203
261
  }
204
262
 
263
+ entity CancelledAgent {
264
+ chatId String @id
265
+ }
266
+
205
267
  @public event restartFlow {
206
268
  chatId String,
207
269
  step String,
@@ -214,6 +276,14 @@ workflow restartFlow {
214
276
  {${DefaultModuleName}/restartSuspension {id fs.suspensionId, data restartFlow.userInput}}
215
277
  }
216
278
  }
279
+
280
+ @public event cancelAgent {
281
+ chatId String
282
+ }
283
+
284
+ workflow cancelAgent {
285
+ await ai.cancelAgent(cancelAgent.chatId)
286
+ }
217
287
  `;
218
288
 
219
289
  enum AgentCacheType {
@@ -317,6 +387,7 @@ export class AgentInstance {
317
387
  type: string = 'chat';
318
388
  tools: string | undefined;
319
389
  documents: string | undefined;
390
+ topics: string | undefined;
320
391
  channels: string | undefined;
321
392
  runWorkflows: boolean = true;
322
393
  role: string | undefined;
@@ -822,6 +893,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
822
893
  const p = await findProviderForLLM(this.llm, env);
823
894
  const agentName = this.name;
824
895
  const chatId = env.getAgentChatId() || agentName;
896
+ await checkCancelled(chatId);
825
897
  let isplnr = this.isPlanner();
826
898
  const isflow = !isplnr && this.isFlowExecutor();
827
899
  const iseval = !isplnr && !isflow && this.isEvaluator();
@@ -920,7 +992,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
920
992
  let response: AIResponse = await p.invoke(msgs, externalToolSpecs);
921
993
  const v = this.getValidationEvent();
922
994
  if (v) {
923
- response = await this.handleValidation(response, v, msgs, p);
995
+ response = await this.handleValidation(response, v, msgs, p, chatId);
924
996
  }
925
997
  if (!iseval)
926
998
  await AgentInstance.maybeEvaluateResponse(
@@ -1005,7 +1077,8 @@ Only return a pure JSON object with no extra text, annotations etc.`;
1005
1077
  response: AIResponse,
1006
1078
  validationEventName: string,
1007
1079
  msgs: BaseMessage[],
1008
- provider: AgentServiceProvider
1080
+ provider: AgentServiceProvider,
1081
+ chatId?: string
1009
1082
  ): Promise<AIResponse> {
1010
1083
  let r: Instance = await this.invokeValidator(response, validationEventName);
1011
1084
  const status = r.lookup('status');
@@ -1017,6 +1090,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
1017
1090
  let attempt = 0;
1018
1091
  let delay = this.retryObj.getNextDelayMs(attempt);
1019
1092
  while (delay) {
1093
+ if (chatId) await checkCancelled(chatId);
1020
1094
  msgs.push(assistantMessage(resp.content));
1021
1095
  const vs = JSON.stringify(r.asSerializableObject());
1022
1096
  msgs.push(
@@ -1068,93 +1142,139 @@ Only return a pure JSON object with no extra text, annotations etc.`;
1068
1142
  return result;
1069
1143
  }
1070
1144
 
1071
- private async maybeAddRelevantDocuments(message: string, env: Environment): Promise<string> {
1072
- if (this.documents && this.documents.length > 0) {
1073
- try {
1074
- const docNames = this.documents.split(',').map(d => d.trim());
1075
- const docTitles = resolveDocumentAliases(docNames);
1145
+ private async maybeAddRelevantDocuments(message: string, _env: Environment): Promise<string> {
1146
+ const hasDocuments = this.documents && this.documents.length > 0;
1147
+ const hasTopics = this.topics && this.topics.length > 0;
1076
1148
 
1077
- const searchQuery = message;
1149
+ if (!hasDocuments && !hasTopics) {
1150
+ return message;
1151
+ }
1078
1152
 
1079
- try {
1080
- const semanticResult: any[] = await parseHelper(
1081
- `{${CoreAIModuleName}/Document {content? "${searchQuery.replace(/"/g, '\\"')}"}}`,
1082
- env
1083
- );
1153
+ try {
1154
+ let containerTags: string[] = [];
1155
+ let topicDocumentTitles: string[] = [];
1084
1156
 
1085
- if (semanticResult && semanticResult.length > 0) {
1086
- const docs: Instance[] = [];
1087
- for (const doc of semanticResult) {
1088
- const docTitle = doc.lookup ? doc.lookup('title') : doc.title;
1089
- if (AgentInstance.docTitlesMatch(docTitle, docTitles)) {
1090
- docs.push(
1091
- doc instanceof Instance
1092
- ? doc
1093
- : Instance.newWithAttributes(doc, new Map(Object.entries(doc)))
1094
- );
1095
- }
1096
- }
1157
+ if (hasTopics) {
1158
+ const topicNames = resolveTopicNames(this.topics!);
1159
+ containerTags = topicNames;
1160
+ topicDocumentTitles = getAllDocumentsForTopics(topicNames);
1161
+ }
1097
1162
 
1098
- if (docs.length > 0) {
1099
- return message.concat('\n\nRelevant context from documents:\n').concat(
1100
- docs
1101
- .map((v: Instance) => {
1102
- return `Document: ${v.lookup('title')}\n${v.lookup('content') as string}`;
1103
- })
1104
- .join('\n\n---\n\n')
1105
- );
1106
- }
1107
- }
1108
- } catch (semanticErr) {
1109
- logger.debug(
1110
- `Semantic search is not available, falling back to title-based filtering: ${semanticErr}`
1111
- );
1112
- }
1163
+ if (containerTags.length === 0) {
1164
+ containerTags = [this.getFqName()];
1165
+ }
1113
1166
 
1114
- const result: any[] = await parseHelper(`{${CoreAIModuleName}/Document? {}}`, env);
1115
- if (result && result.length > 0) {
1116
- const docs: Instance[] = [];
1117
- for (let i = 0; i < result.length; ++i) {
1118
- const v: any = result[i];
1119
- const docTitle: string | undefined = AgentInstance.getDocumentTitle(v);
1120
-
1121
- if (docTitle && docTitles.includes(docTitle)) {
1122
- if (v instanceof Instance) {
1123
- docs.push(v);
1124
- }
1125
- }
1126
- }
1167
+ let documentTitles: string[] = [];
1168
+ let documentRefs: string[] = [];
1127
1169
 
1128
- if (docs.length > 0) {
1129
- return message.concat('\n\nRelevant context from documents:\n').concat(
1130
- docs
1131
- .map((v: Instance) => {
1132
- return v.lookup('content') as string;
1133
- })
1134
- .join('\n\n')
1135
- );
1170
+ if (hasDocuments) {
1171
+ const documentEntries = this.documents!.split(',')
1172
+ .map(d => d.trim())
1173
+ .filter(Boolean);
1174
+ documentTitles = resolveDocumentAliases(documentEntries);
1175
+ documentRefs = documentEntries.filter(d => d.startsWith('document-service://'));
1176
+ }
1177
+
1178
+ const allDocumentTitles = [...new Set([...topicDocumentTitles, ...documentTitles])];
1179
+
1180
+ const kgConfig = getKnowledgeGraphConfig();
1181
+ const serviceUrl = kgConfig?.serviceUrl?.trim() || process.env.KNOWLEDGE_SERVICE_URL || null;
1182
+
1183
+ if (serviceUrl) {
1184
+ return await this.queryRemoteKnowledgeService(
1185
+ message,
1186
+ serviceUrl,
1187
+ containerTags,
1188
+ allDocumentTitles,
1189
+ documentRefs
1190
+ );
1191
+ }
1192
+
1193
+ // Local mode: embed documents into pgvector/lancedb and query
1194
+ const retriever = getDocumentRetriever();
1195
+ const aiModule = isModule(DefaultModuleName + '.ai')
1196
+ ? fetchModule(DefaultModuleName + '.ai')
1197
+ : null;
1198
+ if (aiModule) {
1199
+ for (const title of allDocumentTitles) {
1200
+ const url = aiModule.getDocument(title);
1201
+ if (url) {
1202
+ await retriever.processDocument(title, url);
1136
1203
  }
1137
1204
  }
1138
- } catch (err) {
1139
- logger.debug(`Error retrieving documents: ${err}`);
1140
1205
  }
1206
+
1207
+ const contextString = await retriever.query(
1208
+ message,
1209
+ allDocumentTitles.length > 0 ? allDocumentTitles : undefined,
1210
+ 10
1211
+ );
1212
+
1213
+ if (contextString.trim().length > 0) {
1214
+ return message.concat('\n\nRelevant context from documents:\n').concat(contextString);
1215
+ }
1216
+ } catch (err) {
1217
+ logger.debug(`[KNOWLEDGE] Knowledge retrieval failed: ${err}`);
1141
1218
  }
1219
+
1142
1220
  return message;
1143
1221
  }
1144
1222
 
1145
- private static docTitlesMatch(title: string | undefined, docNames: string[]): boolean {
1146
- return title !== undefined && docNames.includes(title);
1147
- }
1223
+ private async queryRemoteKnowledgeService(
1224
+ message: string,
1225
+ serviceUrl: string,
1226
+ containerTags: string[],
1227
+ documentTitles: string[],
1228
+ documentRefs: string[]
1229
+ ): Promise<string> {
1230
+ const options = {
1231
+ chunkLimit: 10,
1232
+ entityLimit: 20,
1233
+ includeChunks: true,
1234
+ includeEntities: true,
1235
+ includeEdges: true,
1236
+ documentTitles: documentTitles.length > 0 ? documentTitles : undefined,
1237
+ documentRefs: documentRefs.length > 0 ? documentRefs : undefined,
1238
+ };
1148
1239
 
1149
- private static getDocumentTitle(doc: any): string | undefined {
1150
- if (typeof doc.lookup === 'function') {
1151
- return doc.lookup('title') as string | undefined;
1152
- } else if (doc.attributes) {
1153
- return doc.attributes.get('title') as string | undefined;
1154
- } else if (doc.title) {
1155
- return doc.title;
1240
+ let response = await fetch(`${serviceUrl}/api/knowledge/query`, {
1241
+ method: 'POST',
1242
+ headers: { 'Content-Type': 'application/json' },
1243
+ body: JSON.stringify({ query: message, containerTags, ...options }),
1244
+ });
1245
+
1246
+ if (!response.ok) {
1247
+ response = await fetch(`${serviceUrl}/knowledge.core/ApiKnowledgeQuery`, {
1248
+ method: 'POST',
1249
+ headers: { 'Content-Type': 'application/json' },
1250
+ body: JSON.stringify({
1251
+ queryText: message,
1252
+ containerTagsJson: JSON.stringify(containerTags),
1253
+ documentTitlesJson: JSON.stringify(options.documentTitles || []),
1254
+ documentRefsJson: JSON.stringify(options.documentRefs || []),
1255
+ optionsJson: JSON.stringify({
1256
+ includeChunks: options.includeChunks,
1257
+ includeEntities: options.includeEntities,
1258
+ includeEdges: options.includeEdges,
1259
+ chunkLimit: options.chunkLimit,
1260
+ entityLimit: options.entityLimit,
1261
+ }),
1262
+ }),
1263
+ });
1156
1264
  }
1157
- return undefined;
1265
+
1266
+ if (response.ok) {
1267
+ const rawPayload: any = await response.json();
1268
+ const first = Array.isArray(rawPayload) ? rawPayload[0] : rawPayload;
1269
+ const payload =
1270
+ first && typeof first === 'object' && first.KnowledgeQuery ? first.KnowledgeQuery : first;
1271
+ const contextString = payload?.contextString || '';
1272
+ if (contextString.trim().length > 0) {
1273
+ return message.concat('\n\nRelevant context from documents:\n').concat(contextString);
1274
+ }
1275
+ }
1276
+
1277
+ return message;
1158
1278
  }
1159
1279
 
1160
1280
  private static ToolsCache = new Map<string, string>();
@@ -1461,6 +1581,7 @@ export async function processAgentLearning(
1461
1581
  const LocalAgentgFlow = true;
1462
1582
  const LocalFlowStepsRootDirName = 'flows';
1463
1583
  const AgentFlowStep = 'AgentFlowStep';
1584
+ const CancelledAgentEntity = 'CancelledAgent';
1464
1585
 
1465
1586
  export async function saveFlowStepResultLocally(
1466
1587
  chatId: string,
@@ -1613,6 +1734,72 @@ export async function loadFlowStep(chatId: string, step: string): Promise<Instan
1613
1734
  }
1614
1735
  }
1615
1736
 
1737
+ export async function registerTopic(
1738
+ name: string,
1739
+ documents: string | undefined,
1740
+ _env: Environment
1741
+ ): Promise<any> {
1742
+ // Register in local registry
1743
+ registerTopicInRegistry(name, documents ?? undefined);
1744
+
1745
+ // Sync to knowledge service if configured
1746
+ const kgConfig = getKnowledgeGraphConfig();
1747
+ const serviceUrl = kgConfig?.serviceUrl?.trim() || process.env.KNOWLEDGE_SERVICE_URL || null;
1748
+
1749
+ if (serviceUrl) {
1750
+ try {
1751
+ // Parse document list
1752
+ const docList = documents
1753
+ ? documents
1754
+ .split(',')
1755
+ .map(d => d.trim())
1756
+ .filter(Boolean)
1757
+ : [];
1758
+
1759
+ // Create or update topic in knowledge service
1760
+ let response = await fetch(`${serviceUrl}/api/knowledge/topics`, {
1761
+ method: 'POST',
1762
+ headers: {
1763
+ 'Content-Type': 'application/json',
1764
+ },
1765
+ body: JSON.stringify({
1766
+ name,
1767
+ description: `Topic ${name} with ${docList.length} documents`,
1768
+ documentTitles: docList,
1769
+ }),
1770
+ });
1771
+
1772
+ if (!response.ok) {
1773
+ // Fallback to Agentlang entity path for deployed knowledge-service
1774
+ response = await fetch(`${serviceUrl}/knowledge.core/Topic`, {
1775
+ method: 'POST',
1776
+ headers: {
1777
+ 'Content-Type': 'application/json',
1778
+ },
1779
+ body: JSON.stringify({
1780
+ name,
1781
+ description: `Topic ${name} with ${docList.length} documents`,
1782
+ type: 'manual',
1783
+ documentCount: docList.length,
1784
+ }),
1785
+ });
1786
+ }
1787
+
1788
+ if (!response.ok) {
1789
+ const errorText = await response.text();
1790
+ logger.debug(`[KNOWLEDGE] Failed to sync topic to service: ${errorText}`);
1791
+ } else {
1792
+ logger.debug(`[KNOWLEDGE] Synced topic "${name}" to knowledge service`);
1793
+ }
1794
+ } catch (err) {
1795
+ // Don't fail topic registration if service sync fails
1796
+ logger.debug(`[KNOWLEDGE] Error syncing topic to service: ${err}`);
1797
+ }
1798
+ }
1799
+
1800
+ return { topic: { name, documents: documents ?? '' } };
1801
+ }
1802
+
1616
1803
  export async function fetchAndCreateDocument(
1617
1804
  title: string,
1618
1805
  url: string,
@@ -2,6 +2,7 @@ import { default as ai, normalizeGeneratedCode } from './ai.js';
2
2
  import { default as auth } from './auth.js';
3
3
  import { default as files } from './files.js';
4
4
  import { default as mcp } from './mcp.js';
5
+ import messaging, { initMessagingModule } from './messaging.js';
5
6
  import {
6
7
  DefaultModuleName,
7
8
  DefaultModules,
@@ -188,6 +189,7 @@ export function registerCoreModules() {
188
189
  { def: ai, name: makeCoreModuleName('ai') },
189
190
  { def: files, name: makeCoreModuleName('files') },
190
191
  { def: mcp, name: mcpn },
192
+ { def: messaging, name: makeCoreModuleName('messaging') },
191
193
  ];
192
194
 
193
195
  coreModuleInfo.forEach(({ def, name }) => {
@@ -639,6 +641,8 @@ async function internPersistentModules() {
639
641
  }
640
642
 
641
643
  export function initCoreModuleManager() {
644
+ initMessagingModule();
645
+
642
646
  const ModuleResolverName = 'agentlang/moduleResolver';
643
647
  const ModuleResolver = new GenericResolver(ModuleResolverName, {
644
648
  create: createModule,