@xalia/agent 0.6.8 → 0.6.10

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 (152) hide show
  1. package/.env.development +6 -0
  2. package/.env.test +7 -0
  3. package/README.md +11 -0
  4. package/context_system.md +498 -0
  5. package/dist/agent/src/agent/agent.js +169 -87
  6. package/dist/agent/src/agent/agentUtils.js +24 -18
  7. package/dist/agent/src/agent/compressingContextManager.js +10 -14
  8. package/dist/agent/src/agent/context.js +101 -127
  9. package/dist/agent/src/agent/contextWithWorkspace.js +133 -0
  10. package/dist/agent/src/agent/documentSummarizer.js +126 -0
  11. package/dist/agent/src/agent/dummyLLM.js +25 -22
  12. package/dist/agent/src/agent/imageGenLLM.js +22 -25
  13. package/dist/agent/src/agent/imageGenerator.js +2 -10
  14. package/dist/agent/src/agent/llm.js +1 -1
  15. package/dist/agent/src/agent/openAILLM.js +15 -12
  16. package/dist/agent/src/agent/openAILLMStreaming.js +73 -39
  17. package/dist/agent/src/agent/repeatLLM.js +16 -7
  18. package/dist/agent/src/agent/sudoMcpServerManager.js +21 -9
  19. package/dist/agent/src/agent/tokenCounter.js +390 -0
  20. package/dist/agent/src/agent/tokenCounter.test.js +206 -0
  21. package/dist/agent/src/agent/toolSettings.js +17 -0
  22. package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
  23. package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
  24. package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
  25. package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
  26. package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
  27. package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
  28. package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
  29. package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
  30. package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
  31. package/dist/agent/src/agent/tools/index.js +64 -0
  32. package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
  33. package/dist/agent/src/agent/tools/renderTool.js +89 -0
  34. package/dist/agent/src/agent/tools/utils.js +61 -0
  35. package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
  36. package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
  37. package/dist/agent/src/chat/client/chatClient.js +63 -2
  38. package/dist/agent/src/chat/client/connection.js +6 -1
  39. package/dist/agent/src/chat/client/index.js +4 -1
  40. package/dist/agent/src/chat/client/sessionClient.js +28 -9
  41. package/dist/agent/src/chat/constants.js +8 -0
  42. package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
  43. package/dist/agent/src/chat/data/dbSessionMessages.js +11 -0
  44. package/dist/agent/src/chat/protocol/messages.js +9 -0
  45. package/dist/agent/src/chat/server/chatContextManager.js +186 -156
  46. package/dist/agent/src/chat/server/conversation.js +3 -0
  47. package/dist/agent/src/chat/server/imageGeneratorTools.js +39 -16
  48. package/dist/agent/src/chat/server/openAIRouterLLM.js +111 -0
  49. package/dist/agent/src/chat/server/openSession.js +253 -91
  50. package/dist/agent/src/chat/server/promptRefiner.js +86 -0
  51. package/dist/agent/src/chat/server/server.js +10 -2
  52. package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
  53. package/dist/agent/src/chat/server/sessionRegistry.js +152 -6
  54. package/dist/agent/src/chat/server/sessionRegistry.test.js +1 -1
  55. package/dist/agent/src/chat/server/titleGenerator.js +112 -0
  56. package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
  57. package/dist/agent/src/chat/server/tools.js +64 -253
  58. package/dist/agent/src/chat/utils/approvalManager.js +6 -3
  59. package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
  60. package/dist/agent/src/test/agent.test.js +16 -17
  61. package/dist/agent/src/test/chatContextManager.test.js +44 -30
  62. package/dist/agent/src/test/clientServerConnection.test.js +1 -2
  63. package/dist/agent/src/test/compressingContextManager.test.js +22 -36
  64. package/dist/agent/src/test/context.test.js +55 -17
  65. package/dist/agent/src/test/contextTestTools.js +87 -0
  66. package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
  67. package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
  68. package/dist/agent/src/test/testTools.js +6 -1
  69. package/dist/agent/src/test/tools.test.js +27 -9
  70. package/dist/agent/src/tool/agentChat.js +5 -2
  71. package/dist/agent/src/tool/chatMain.js +56 -15
  72. package/dist/agent/src/tool/commandPrompt.js +2 -2
  73. package/dist/agent/src/tool/files.js +7 -8
  74. package/package.json +4 -1
  75. package/scripts/test_chat +195 -173
  76. package/src/agent/agent.ts +257 -137
  77. package/src/agent/agentUtils.ts +32 -20
  78. package/src/agent/compressingContextManager.ts +13 -44
  79. package/src/agent/context.ts +165 -159
  80. package/src/agent/contextWithWorkspace.ts +162 -0
  81. package/src/agent/documentSummarizer.ts +157 -0
  82. package/src/agent/dummyLLM.ts +27 -23
  83. package/src/agent/imageGenLLM.ts +28 -32
  84. package/src/agent/imageGenerator.ts +3 -18
  85. package/src/agent/llm.ts +2 -2
  86. package/src/agent/openAILLM.ts +17 -13
  87. package/src/agent/openAILLMStreaming.ts +99 -43
  88. package/src/agent/repeatLLM.ts +19 -7
  89. package/src/agent/sudoMcpServerManager.ts +41 -20
  90. package/src/agent/test_data/harrypotter.txt +6065 -0
  91. package/src/agent/tokenCounter.test.ts +243 -0
  92. package/src/agent/tokenCounter.ts +483 -0
  93. package/src/agent/toolSettings.ts +24 -0
  94. package/src/agent/tools/calculatorTool.ts +50 -0
  95. package/src/agent/tools/contentExtractors/pdfToText.ts +60 -0
  96. package/src/agent/tools/datetimeTool.ts +41 -0
  97. package/src/agent/tools/fileManager/fileManagerTool.ts +199 -0
  98. package/src/agent/tools/fileManager/index.ts +50 -0
  99. package/src/agent/tools/fileManager/memoryFileManager.ts +120 -0
  100. package/src/{chat/data → agent/tools/fileManager}/mimeTypes.ts +3 -1
  101. package/src/agent/tools/fileManager/prompt.ts +38 -0
  102. package/src/{chat/data/dbSessionFileModels.ts → agent/tools/fileManager/types.ts} +76 -0
  103. package/src/agent/tools/index.ts +49 -0
  104. package/src/agent/tools/openUrlTool.ts +62 -0
  105. package/src/agent/tools/renderTool.ts +92 -0
  106. package/src/agent/tools/utils.ts +74 -0
  107. package/src/{chat/utils/search.ts → agent/tools/webSearch.ts} +0 -1
  108. package/src/agent/tools/webSearchTool.ts +44 -0
  109. package/src/chat/client/chatClient.ts +92 -3
  110. package/src/chat/client/connection.ts +11 -1
  111. package/src/chat/client/index.ts +3 -0
  112. package/src/chat/client/sessionClient.ts +40 -11
  113. package/src/chat/client/sessionFiles.ts +1 -1
  114. package/src/chat/constants.ts +6 -0
  115. package/src/chat/data/dataModels.ts +12 -0
  116. package/src/chat/data/dbSessionFiles.ts +12 -4
  117. package/src/chat/data/dbSessionMessages.ts +34 -0
  118. package/src/chat/protocol/messages.ts +94 -14
  119. package/src/chat/server/chatContextManager.ts +255 -221
  120. package/src/chat/server/connectionManager.ts +1 -1
  121. package/src/chat/server/conversation.ts +3 -0
  122. package/src/chat/server/imageGeneratorTools.ts +62 -30
  123. package/src/chat/server/openAIRouterLLM.ts +168 -0
  124. package/src/chat/server/openSession.ts +381 -138
  125. package/src/chat/server/promptRefiner.ts +106 -0
  126. package/src/chat/server/server.ts +9 -2
  127. package/src/chat/server/sessionFileManager.ts +35 -306
  128. package/src/chat/server/sessionRegistry.test.ts +0 -1
  129. package/src/chat/server/sessionRegistry.ts +228 -4
  130. package/src/chat/server/titleGenerator.test.ts +103 -0
  131. package/src/chat/server/titleGenerator.ts +143 -0
  132. package/src/chat/server/tools.ts +92 -281
  133. package/src/chat/utils/approvalManager.ts +9 -3
  134. package/src/chat/utils/multiAsyncQueue.ts +4 -0
  135. package/src/test/agent.test.ts +25 -30
  136. package/src/test/chatContextManager.test.ts +68 -38
  137. package/src/test/clientServerConnection.test.ts +0 -2
  138. package/src/test/compressingContextManager.test.ts +29 -34
  139. package/src/test/context.test.ts +59 -15
  140. package/src/test/contextTestTools.ts +95 -0
  141. package/src/test/dbMcpServerConfigs.test.ts +4 -4
  142. package/src/test/dbSessionFiles.test.ts +16 -16
  143. package/src/test/testTools.ts +8 -3
  144. package/src/test/tools.test.ts +30 -5
  145. package/src/tool/agentChat.ts +12 -3
  146. package/src/tool/chatMain.ts +59 -18
  147. package/src/tool/commandPrompt.ts +2 -2
  148. package/src/tool/files.ts +1 -3
  149. package/dist/agent/src/agent/tools.js +0 -44
  150. package/src/agent/tools.ts +0 -57
  151. /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
  152. /package/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.ts +0 -0
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pdfToText = pdfToText;
4
+ /**
5
+ * Extract text string from a text content item if it has one.
6
+ */
7
+ function getTextStr(item) {
8
+ if (typeof item === "object" && item !== null && "str" in item) {
9
+ const str = item.str;
10
+ return typeof str === "string" ? str : undefined;
11
+ }
12
+ return undefined;
13
+ }
14
+ /**
15
+ * Extract text content from a PDF buffer.
16
+ *
17
+ * @param data - PDF file as ArrayBuffer
18
+ * @param maxLength - Optional maximum length of extracted text
19
+ * @returns Extracted text content
20
+ */
21
+ async function pdfToText(data, maxLength) {
22
+ // Dynamic import required for ESM module in CommonJS project
23
+ const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
24
+ // Disable worker for Node.js environment
25
+ const loadingTask = pdfjs.getDocument({
26
+ data,
27
+ useWorkerFetch: false,
28
+ isEvalSupported: false,
29
+ useSystemFonts: true,
30
+ });
31
+ const doc = await loadingTask.promise;
32
+ let fullText = "";
33
+ for (let i = 1; i <= doc.numPages; i++) {
34
+ const page = await doc.getPage(i);
35
+ const content = await page.getTextContent();
36
+ const strings = [];
37
+ for (const item of content.items) {
38
+ const str = getTextStr(item);
39
+ if (str !== undefined) {
40
+ strings.push(str);
41
+ }
42
+ }
43
+ fullText += strings.join(" ") + "\n";
44
+ page.cleanup();
45
+ // Early exit if we've exceeded maxLength
46
+ if (maxLength && fullText.length > maxLength) {
47
+ break;
48
+ }
49
+ }
50
+ // Apply truncation
51
+ if (maxLength && fullText.length > maxLength) {
52
+ return fullText.slice(0, maxLength) + "... [content truncated]";
53
+ }
54
+ return fullText.trim();
55
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isoWithTimezone = isoWithTimezone;
4
+ exports.datetimeTool = datetimeTool;
5
+ const DATETIME_DESC = {
6
+ type: "function",
7
+ function: {
8
+ name: "time_now",
9
+ description: "Current time",
10
+ },
11
+ };
12
+ function isoWithTimezone(timeZone) {
13
+ return (new Intl.DateTimeFormat("sv-SE", {
14
+ timeZone,
15
+ year: "numeric",
16
+ month: "2-digit",
17
+ day: "2-digit",
18
+ hour: "2-digit",
19
+ minute: "2-digit",
20
+ second: "2-digit",
21
+ hour12: false,
22
+ timeZoneName: "short",
23
+ })
24
+ .format(new Date())
25
+ .replace(" ", "T") + ` (${timeZone})`);
26
+ }
27
+ function datetimeTool(timezone) {
28
+ // eslint-disable-next-line @typescript-eslint/require-await
29
+ const toolFn = async () => {
30
+ return { response: isoWithTimezone(timezone) };
31
+ };
32
+ return {
33
+ // eslint-disable-next-line @typescript-eslint/require-await
34
+ setup: async (agent) => {
35
+ agent.addAgentTool(DATETIME_DESC, toolFn);
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fileManagerTool = fileManagerTool;
4
+ const utils_1 = require("../utils");
5
+ const types_1 = require("./types");
6
+ const GET_FILE_CONTENT_TOOL = {
7
+ type: "function",
8
+ function: {
9
+ name: "get_file_content",
10
+ description: "Get raw file content as data-url. Use this for images or when you " +
11
+ "need the original binary data. For PDFs and documents where you need " +
12
+ "the text content, use get_file_parsed_content instead.",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ name: {
17
+ type: "string",
18
+ description: "File name",
19
+ },
20
+ },
21
+ required: ["name"],
22
+ },
23
+ },
24
+ };
25
+ const PUT_FILE_CONTENT_TOOL = {
26
+ type: "function",
27
+ function: {
28
+ name: "put_file_content",
29
+ description: "Create or update file content",
30
+ parameters: {
31
+ type: "object",
32
+ properties: {
33
+ name: {
34
+ type: "string",
35
+ description: "File name",
36
+ },
37
+ summary: {
38
+ type: "string",
39
+ description: "Content summary",
40
+ },
41
+ data_url: {
42
+ type: "string",
43
+ description: "Content data-url: `data:<mime-type>[;<format>],<encoding>`",
44
+ },
45
+ },
46
+ required: ["name", "summary", "data_url"],
47
+ },
48
+ },
49
+ };
50
+ const DELETE_FILE_CONTENT_TOOL = {
51
+ type: "function",
52
+ function: {
53
+ name: "delete_file_content",
54
+ description: "Delete file",
55
+ parameters: {
56
+ type: "object",
57
+ properties: {
58
+ name: {
59
+ type: "string",
60
+ description: "File name",
61
+ },
62
+ },
63
+ required: ["name"],
64
+ },
65
+ },
66
+ };
67
+ const GET_FILE_PARSED_CONTENT_TOOL = {
68
+ type: "function",
69
+ function: {
70
+ name: "get_file_parsed_content",
71
+ description: "Get extracted text content from a file (e.g., PDF). " +
72
+ "Returns the parsed text if available, or an error if the file " +
73
+ "has no parsed content.",
74
+ parameters: {
75
+ type: "object",
76
+ properties: {
77
+ name: {
78
+ type: "string",
79
+ description: "File name",
80
+ },
81
+ },
82
+ required: ["name"],
83
+ },
84
+ },
85
+ };
86
+ function fileManagerTool(fileManager) {
87
+ // `get_file_content` tool
88
+ //
89
+ // LLM can read data from the file. To keep the context small, we overwrite
90
+ // the data with the session file url after the LLM has seen it.
91
+ const parseName = (0, utils_1.makeParseArgsFn)(["name"]);
92
+ const getFileContentFn = async (_agent, args) => {
93
+ const { name } = parseName(args);
94
+ const response = await fileManager.getFileContent(name);
95
+ const overwriteResponse = fileManager.getSessionFileRelativeUrl(name);
96
+ return { response, overwriteResponse };
97
+ };
98
+ // `set_file_content` tool
99
+ //
100
+ // Allows LLM to write data (as a data-url) to the session file manager. The
101
+ // data is replaced by a session file url after being saved.
102
+ const putArgs = ["name", "summary", "data_url"];
103
+ const parseNameSummaryDataUrl = (0, utils_1.makeParseArgsFn)(putArgs);
104
+ const putFileContentFn = async (_, args) => {
105
+ const parsed = parseNameSummaryDataUrl(args);
106
+ const { name, summary, data_url } = parsed;
107
+ const existingFile = fileManager.listFiles().find((f) => f.name === name);
108
+ const desc = await fileManager.putFileContent(name, summary, data_url);
109
+ const mimeType = (0, types_1.getSessionFileMimeTypeFromDataUrl)(data_url);
110
+ // Calculate content size for response message
111
+ const contentSize = data_url.split(",")[1]?.length || 0;
112
+ const sizeKB = (contentSize / 1024).toFixed(1);
113
+ parsed.data_url = fileManager.getSessionFileRelativeUrl(name);
114
+ const action = existingFile ? "updated" : "created";
115
+ const responseMsg = `Successfully ${action} file '${desc.name}' with ${sizeKB}KB of content`;
116
+ return {
117
+ response: responseMsg,
118
+ overwriteArgs: JSON.stringify(parsed),
119
+ _meta: {
120
+ "xalia/fileUri": parsed.data_url,
121
+ "xalia/fileMimeType": mimeType,
122
+ },
123
+ };
124
+ };
125
+ // `delete_file_content` tool
126
+ //
127
+ // Allows LLM to write data (as a data-url) to the session file manager. The
128
+ // data is replaced by a session file url after being saved.
129
+ const deleteFileFn = async (_, args) => {
130
+ const { name } = parseName(args);
131
+ await fileManager.deleteFile(name);
132
+ return { response: "" };
133
+ };
134
+ // `get_file_parsed_content` tool
135
+ //
136
+ // Returns extracted text content from files like PDFs.
137
+ const getFileParsedContentFn = async (_, args) => {
138
+ const { name } = parseName(args);
139
+ const parsedContent = await fileManager.getFileParsedContent(name);
140
+ if (!parsedContent) {
141
+ return { response: `File '${name}' has no parsed content available.` };
142
+ }
143
+ return { response: parsedContent.text };
144
+ };
145
+ return {
146
+ setup: (agent) => {
147
+ agent.addAgentTool(GET_FILE_CONTENT_TOOL, getFileContentFn);
148
+ agent.addAgentTool(PUT_FILE_CONTENT_TOOL, putFileContentFn);
149
+ agent.addAgentTool(DELETE_FILE_CONTENT_TOOL, deleteFileFn);
150
+ agent.addAgentTool(GET_FILE_PARSED_CONTENT_TOOL, getFileParsedContentFn);
151
+ return new Promise((r) => {
152
+ r();
153
+ });
154
+ },
155
+ };
156
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSessionFilesManagerPrompt = exports.fileManagerTool = exports.MemoryFileManager = exports.createSessionFileDataUrl = exports.getSessionFileMimeTypeFromDataUrl = exports.isParsedContent = exports.isFileMetaData = exports.EXTENSION_TO_SESSION_FILE_MIME_TYPE = exports.isSessionFileTextMimeType = exports.SESSION_FILE_TEXT_MIME_TYPES = exports.isSessionFileMimeType = exports.SESSION_FILE_MIME_TYPES = exports.createDataUrlFromText = exports.createDataUrlFromBuffer = exports.getMimeTypeFromDataUrl = exports.EXTENSION_TO_IMAGE_MIME_TYPE = exports.isImageMimeType = exports.IMAGE_MIME_TYPES = void 0;
4
+ // Mime types - values
5
+ var mimeTypes_1 = require("./mimeTypes");
6
+ Object.defineProperty(exports, "IMAGE_MIME_TYPES", { enumerable: true, get: function () { return mimeTypes_1.IMAGE_MIME_TYPES; } });
7
+ Object.defineProperty(exports, "isImageMimeType", { enumerable: true, get: function () { return mimeTypes_1.isImageMimeType; } });
8
+ Object.defineProperty(exports, "EXTENSION_TO_IMAGE_MIME_TYPE", { enumerable: true, get: function () { return mimeTypes_1.EXTENSION_TO_IMAGE_MIME_TYPE; } });
9
+ Object.defineProperty(exports, "getMimeTypeFromDataUrl", { enumerable: true, get: function () { return mimeTypes_1.getMimeTypeFromDataUrl; } });
10
+ Object.defineProperty(exports, "createDataUrlFromBuffer", { enumerable: true, get: function () { return mimeTypes_1.createDataUrlFromBuffer; } });
11
+ Object.defineProperty(exports, "createDataUrlFromText", { enumerable: true, get: function () { return mimeTypes_1.createDataUrlFromText; } });
12
+ // Session file types - values
13
+ var types_1 = require("./types");
14
+ Object.defineProperty(exports, "SESSION_FILE_MIME_TYPES", { enumerable: true, get: function () { return types_1.SESSION_FILE_MIME_TYPES; } });
15
+ Object.defineProperty(exports, "isSessionFileMimeType", { enumerable: true, get: function () { return types_1.isSessionFileMimeType; } });
16
+ Object.defineProperty(exports, "SESSION_FILE_TEXT_MIME_TYPES", { enumerable: true, get: function () { return types_1.SESSION_FILE_TEXT_MIME_TYPES; } });
17
+ Object.defineProperty(exports, "isSessionFileTextMimeType", { enumerable: true, get: function () { return types_1.isSessionFileTextMimeType; } });
18
+ Object.defineProperty(exports, "EXTENSION_TO_SESSION_FILE_MIME_TYPE", { enumerable: true, get: function () { return types_1.EXTENSION_TO_SESSION_FILE_MIME_TYPE; } });
19
+ Object.defineProperty(exports, "isFileMetaData", { enumerable: true, get: function () { return types_1.isFileMetaData; } });
20
+ Object.defineProperty(exports, "isParsedContent", { enumerable: true, get: function () { return types_1.isParsedContent; } });
21
+ Object.defineProperty(exports, "getSessionFileMimeTypeFromDataUrl", { enumerable: true, get: function () { return types_1.getSessionFileMimeTypeFromDataUrl; } });
22
+ Object.defineProperty(exports, "createSessionFileDataUrl", { enumerable: true, get: function () { return types_1.createSessionFileDataUrl; } });
23
+ // Implementations
24
+ var memoryFileManager_1 = require("./memoryFileManager");
25
+ Object.defineProperty(exports, "MemoryFileManager", { enumerable: true, get: function () { return memoryFileManager_1.MemoryFileManager; } });
26
+ // Tool - values
27
+ var fileManagerTool_1 = require("./fileManagerTool");
28
+ Object.defineProperty(exports, "fileManagerTool", { enumerable: true, get: function () { return fileManagerTool_1.fileManagerTool; } });
29
+ // Prompt
30
+ var prompt_1 = require("./prompt");
31
+ Object.defineProperty(exports, "createSessionFilesManagerPrompt", { enumerable: true, get: function () { return prompt_1.createSessionFilesManagerPrompt; } });
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryFileManager = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const types_1 = require("./types");
6
+ function isSingleLine(str) {
7
+ return !/[\r\n]/.test(str);
8
+ }
9
+ /**
10
+ * In-memory implementation of ISessionFileManager
11
+ */
12
+ class MemoryFileManager {
13
+ constructor() {
14
+ this.files = new Map();
15
+ this.eventHandlers = [];
16
+ }
17
+ // ISessionFileManager.listFiles
18
+ listFiles() {
19
+ return Array.from(this.files.values());
20
+ }
21
+ // ISessionFileManager.getFileContent
22
+ getFileContent(name) {
23
+ return new Promise((r, e) => {
24
+ const entry = this.files.get(name);
25
+ if (entry) {
26
+ r(entry.data_url);
27
+ }
28
+ else {
29
+ e(new Error(`no such file ${name}`));
30
+ }
31
+ });
32
+ }
33
+ // ISessionFileManager.getFileParsedContent
34
+ getFileParsedContent(name) {
35
+ return new Promise((r, e) => {
36
+ const entry = this.files.get(name);
37
+ if (entry) {
38
+ r(entry.parsed_content);
39
+ }
40
+ else {
41
+ e(new Error(`no such file ${name}`));
42
+ }
43
+ });
44
+ }
45
+ // ISessionFileManager.deleteFile
46
+ deleteFile(name) {
47
+ return new Promise((r, e) => {
48
+ if (this.files.has(name)) {
49
+ this.files.delete(name);
50
+ this.eventHandlers.forEach((eh) => {
51
+ eh.onFileDeleted(name);
52
+ });
53
+ r();
54
+ }
55
+ else {
56
+ e(new Error(`no such file ${name}`));
57
+ }
58
+ });
59
+ }
60
+ // ISessionFileManager.addFile
61
+ putFileContent(name, summary, data_url, parsed_content) {
62
+ return new Promise((r, e) => {
63
+ if (!name) {
64
+ name = (0, uuid_1.v4)();
65
+ }
66
+ if (!summary) {
67
+ summary = "";
68
+ }
69
+ if (!isSingleLine(summary)) {
70
+ e(new Error("summary must no contain new-lines"));
71
+ }
72
+ else {
73
+ const mime_type = (0, types_1.getSessionFileMimeTypeFromDataUrl)(data_url);
74
+ const is_new = !this.files.has(name);
75
+ const entry = {
76
+ name,
77
+ mime_type,
78
+ summary,
79
+ parsed_content,
80
+ data_url,
81
+ };
82
+ this.eventHandlers.forEach((eh) => {
83
+ eh.onFileChanged(entry, is_new);
84
+ });
85
+ this.files.set(name, entry);
86
+ r({ name, mime_type, summary, parsed_content });
87
+ }
88
+ });
89
+ }
90
+ // ISessionFileManager.setEventHandler
91
+ addEventHandler(eventHandler) {
92
+ this.eventHandlers.push(eventHandler);
93
+ }
94
+ getSessionFileRelativeUrl(name) {
95
+ return `file+session:/./${name}`;
96
+ }
97
+ getSessionFileAbsoluteUrl(name) {
98
+ // MemoryFileManager doesn't have a session ID, use relative URL
99
+ return this.getSessionFileRelativeUrl(name);
100
+ }
101
+ }
102
+ exports.MemoryFileManager = MemoryFileManager;
@@ -40,5 +40,7 @@ function createDataUrlFromBuffer(data, mime_type) {
40
40
  return `data:${mime_type};base64,${imgB64}`;
41
41
  }
42
42
  function createDataUrlFromText(data, mime_type) {
43
- return `data:${mime_type},${data}`;
43
+ // Properly URL-encode the content to handle special characters
44
+ const encodedData = encodeURIComponent(data);
45
+ return `data:${mime_type};charset=utf-8,${encodedData}`;
44
46
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSessionFilesManagerPrompt = createSessionFilesManagerPrompt;
4
+ /**
5
+ * Return the file list in a form easily parsable by the LLM.
6
+ */
7
+ function createSessionFilesManagerPrompt(fm) {
8
+ const files = fm.listFiles();
9
+ let prompt = "Files can be read/written as required. Create new files " +
10
+ "conservatively, usually when complex content is requested. " +
11
+ "\n\nDATA URL FORMAT: For text files (plain, markdown, html), use " +
12
+ "URL-encoded format: 'data:<mime-type>;charset=utf-8," +
13
+ "<URL-encoded-content>'. For binary files (images, PDF), use " +
14
+ "base64: 'data:<mime-type>;base64,<base64-data>'. URL encoding " +
15
+ "means using encodeURIComponent() or percent-encoding where " +
16
+ "special characters become %XX codes." +
17
+ "\n\n⚠️ CRITICAL: After you call put_file_content, the system " +
18
+ "AUTOMATICALLY replaces your data_url argument with " +
19
+ "'file+session://...' in the conversation history. This is NORMAL " +
20
+ "behavior to save context space. The replacement happens AFTER the " +
21
+ "file is successfully written. When you see 'file+session://...' in " +
22
+ "your previous tool call, this means SUCCESS - the file was created " +
23
+ "with your full content. NEVER retry or attempt to 'fix' this. The " +
24
+ "file+session:// reference is NOT an error - it confirms the " +
25
+ "operation succeeded.";
26
+ if (files.length > 0) {
27
+ prompt += "\n\nAvailable files:\nname,type,summary\n";
28
+ for (const f of files) {
29
+ prompt += `${f.name},${f.mime_type},${f.summary || ""}\n`;
30
+ }
31
+ }
32
+ return prompt;
33
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EXTENSION_TO_SESSION_FILE_MIME_TYPE = exports.SESSION_FILE_TEXT_MIME_TYPES = exports.SESSION_FILE_MIME_TYPES = void 0;
4
4
  exports.isSessionFileMimeType = isSessionFileMimeType;
5
5
  exports.isSessionFileTextMimeType = isSessionFileTextMimeType;
6
+ exports.isParsedContent = isParsedContent;
6
7
  exports.isFileMetaData = isFileMetaData;
7
8
  exports.getSessionFileMimeTypeFromDataUrl = getSessionFileMimeTypeFromDataUrl;
8
9
  exports.createSessionFileDataUrl = createSessionFileDataUrl;
@@ -34,6 +35,12 @@ exports.EXTENSION_TO_SESSION_FILE_MIME_TYPE = {
34
35
  txt: "text/plain",
35
36
  md: "text/markdown",
36
37
  };
38
+ function isParsedContent(obj) {
39
+ if (typeof obj !== "object" || obj === null)
40
+ return false;
41
+ const pc = obj;
42
+ return pc.version === 1 && typeof pc.text === "string";
43
+ }
37
44
  // Checks that the array of keys matches FileMetaData
38
45
  const fileMetaKeys = [
39
46
  "xalia/fileUri",
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * General-purpose agent tools.
4
+ *
5
+ * These tools are available to all agents, not just chat-based ones.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.htmlToText = exports.webSearch = exports.makeParseArgsFn = exports.renderTool = exports.openURL = exports.openURLTool = exports.webSearchTool = exports.calculatorEval = exports.calculatorTool = exports.isoWithTimezone = exports.datetimeTool = void 0;
23
+ exports.addDefaultTools = addDefaultTools;
24
+ // Tool implementations
25
+ var datetimeTool_1 = require("./datetimeTool");
26
+ Object.defineProperty(exports, "datetimeTool", { enumerable: true, get: function () { return datetimeTool_1.datetimeTool; } });
27
+ Object.defineProperty(exports, "isoWithTimezone", { enumerable: true, get: function () { return datetimeTool_1.isoWithTimezone; } });
28
+ var calculatorTool_1 = require("./calculatorTool");
29
+ Object.defineProperty(exports, "calculatorTool", { enumerable: true, get: function () { return calculatorTool_1.calculatorTool; } });
30
+ Object.defineProperty(exports, "calculatorEval", { enumerable: true, get: function () { return calculatorTool_1.calculatorEval; } });
31
+ var webSearchTool_1 = require("./webSearchTool");
32
+ Object.defineProperty(exports, "webSearchTool", { enumerable: true, get: function () { return webSearchTool_1.webSearchTool; } });
33
+ var openUrlTool_1 = require("./openUrlTool");
34
+ Object.defineProperty(exports, "openURLTool", { enumerable: true, get: function () { return openUrlTool_1.openURLTool; } });
35
+ Object.defineProperty(exports, "openURL", { enumerable: true, get: function () { return openUrlTool_1.openURL; } });
36
+ var renderTool_1 = require("./renderTool");
37
+ Object.defineProperty(exports, "renderTool", { enumerable: true, get: function () { return renderTool_1.renderTool; } });
38
+ // Utilities
39
+ var utils_1 = require("./utils");
40
+ Object.defineProperty(exports, "makeParseArgsFn", { enumerable: true, get: function () { return utils_1.makeParseArgsFn; } });
41
+ var webSearch_1 = require("./webSearch");
42
+ Object.defineProperty(exports, "webSearch", { enumerable: true, get: function () { return webSearch_1.webSearch; } });
43
+ var htmlToText_1 = require("./contentExtractors/htmlToText");
44
+ Object.defineProperty(exports, "htmlToText", { enumerable: true, get: function () { return htmlToText_1.htmlToText; } });
45
+ // File manager (re-export everything)
46
+ __exportStar(require("./fileManager"), exports);
47
+ // Import tools for addDefaultTools
48
+ const datetimeTool_2 = require("./datetimeTool");
49
+ const calculatorTool_2 = require("./calculatorTool");
50
+ const webSearchTool_2 = require("./webSearchTool");
51
+ const openUrlTool_2 = require("./openUrlTool");
52
+ const renderTool_2 = require("./renderTool");
53
+ const fileManager_1 = require("./fileManager");
54
+ /**
55
+ * Add the default set of general-purpose agent tools.
56
+ */
57
+ async function addDefaultTools(agent, timezone, _platform, fileManager) {
58
+ await agent.addAgentToolProvider((0, datetimeTool_2.datetimeTool)(timezone));
59
+ await agent.addAgentToolProvider(calculatorTool_2.calculatorTool);
60
+ await agent.addAgentToolProvider((0, renderTool_2.renderTool)(fileManager));
61
+ await agent.addAgentToolProvider((0, webSearchTool_2.webSearchTool)());
62
+ await agent.addAgentToolProvider((0, openUrlTool_2.openURLTool)());
63
+ await agent.addAgentToolProvider((0, fileManager_1.fileManagerTool)(fileManager));
64
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openURL = openURL;
4
+ exports.openURLTool = openURLTool;
5
+ const toolSettings_1 = require("../toolSettings");
6
+ const utils_1 = require("./utils");
7
+ const htmlToText_1 = require("./contentExtractors/htmlToText");
8
+ const pdfToText_1 = require("./contentExtractors/pdfToText");
9
+ const OPEN_URL_DESC = {
10
+ type: "function",
11
+ function: {
12
+ name: "open_url",
13
+ description: "Download and extract text content from a URL (supports HTML and PDF)",
14
+ parameters: {
15
+ type: "object",
16
+ properties: {
17
+ url: {
18
+ type: "string",
19
+ description: "URL to download (HTML pages or PDF files)",
20
+ },
21
+ },
22
+ required: ["url"],
23
+ },
24
+ },
25
+ };
26
+ async function openURL(url) {
27
+ const response = await fetch(url);
28
+ if (!response.ok) {
29
+ const status = String(response.status);
30
+ const code = response.statusText;
31
+ throw new Error(`Failed to fetch ${url}: ${status} ${code}`);
32
+ }
33
+ const contentType = response.headers.get("content-type") || "";
34
+ const isPdf = contentType.includes("application/pdf") ||
35
+ url.toLowerCase().endsWith(".pdf");
36
+ if (isPdf) {
37
+ const buffer = await response.arrayBuffer();
38
+ return (0, pdfToText_1.pdfToText)(buffer, toolSettings_1.OPEN_URL_MAX_LENGTH);
39
+ }
40
+ else {
41
+ const html = await response.text();
42
+ return (0, htmlToText_1.htmlToText)(html, toolSettings_1.OPEN_URL_MAX_LENGTH);
43
+ }
44
+ }
45
+ function openURLTool() {
46
+ const getURL = (0, utils_1.makeParseArgsFn)(["url"]);
47
+ const toolFn = async (_, args) => {
48
+ const { url } = getURL(args);
49
+ return { response: await openURL(url) };
50
+ };
51
+ return {
52
+ // eslint-disable-next-line @typescript-eslint/require-await
53
+ setup: async (agent) => {
54
+ agent.addAgentTool(OPEN_URL_DESC, toolFn);
55
+ },
56
+ };
57
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderTool = renderTool;
4
+ const utils_1 = require("./utils");
5
+ const RENDER_DESC = {
6
+ type: "function",
7
+ function: {
8
+ name: "render",
9
+ description: [
10
+ "Display HTML using only safe elements",
11
+ "Never include <script>, <iframe>, <object>, <embed>, <form>, <meta>,",
12
+ "<link>, <style>, event handlers (onclick, etc.), or javascript: URLs.",
13
+ "If the tool result is [HTML_SANITIZATION_WARNING], do not mention it.",
14
+ "Retry with only safe elements. If warning repeats, tell the user you",
15
+ "cannot render unsafe HTML.",
16
+ ].join(" "),
17
+ parameters: {
18
+ type: "object",
19
+ properties: {
20
+ name: { type: "string", description: "Filename for the HTML" },
21
+ summary: { type: "string", description: "One line summary" },
22
+ html: { type: "string", description: "HTML fragment to render" },
23
+ },
24
+ required: ["name", "summary", "html"],
25
+ },
26
+ },
27
+ };
28
+ function validateHtmlSafety(html) {
29
+ const issues = [];
30
+ if (/<script[\s>]/i.test(html))
31
+ issues.push("<script> tag");
32
+ if (/<iframe[\s>]/i.test(html))
33
+ issues.push("<iframe> tag");
34
+ if (/<object[\s>]/i.test(html))
35
+ issues.push("<object> tag");
36
+ if (/<embed[\s>]/i.test(html))
37
+ issues.push("<embed> tag");
38
+ if (/\bon\w+\s*=/.test(html))
39
+ issues.push("event handler (e.g., onclick)");
40
+ if (/javascript:/i.test(html))
41
+ issues.push("javascript: URL");
42
+ if (/<meta[\s>]/i.test(html))
43
+ issues.push("<meta> tag");
44
+ if (/<form[\s>]/i.test(html))
45
+ issues.push("<form> tag");
46
+ if (/<style[\s>]/i.test(html))
47
+ issues.push("<style> tag");
48
+ if (/<link[\s>]/i.test(html))
49
+ issues.push("<link> tag");
50
+ if (issues.length > 0) {
51
+ return `Unsafe HTML: ${issues.join(", ")}`;
52
+ }
53
+ return undefined;
54
+ }
55
+ function renderTool(fileManager) {
56
+ const getNameSummeryHtml = (0, utils_1.makeParseArgsFn)([
57
+ "name",
58
+ "summary",
59
+ "html",
60
+ ]);
61
+ const toolFn = async (_, args) => {
62
+ const { name, summary, html } = getNameSummeryHtml(args);
63
+ const safetyError = validateHtmlSafety(html);
64
+ if (safetyError) {
65
+ return {
66
+ response: "[HTML_SANITIZATION_WARNING]",
67
+ overwriteResponse: "",
68
+ structuredContent: {
69
+ kind: "htmlSanitizationWarning",
70
+ message: safetyError,
71
+ },
72
+ };
73
+ }
74
+ const mimeType = "text/html";
75
+ const dataURL = `data:${mimeType},${html}`;
76
+ await fileManager.putFileContent(name, summary, dataURL);
77
+ const uri = fileManager.getSessionFileRelativeUrl(name);
78
+ return {
79
+ response: "",
80
+ _meta: { "xalia/fileUri": uri, "xalia/fileMimeType": mimeType },
81
+ };
82
+ };
83
+ return {
84
+ // eslint-disable-next-line @typescript-eslint/require-await
85
+ setup: async (agent) => {
86
+ agent.addAgentTool(RENDER_DESC, toolFn);
87
+ },
88
+ };
89
+ }