@animus-labs/cortex 0.2.0

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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/budget-guard.d.ts +75 -0
  4. package/dist/budget-guard.d.ts.map +1 -0
  5. package/dist/budget-guard.js +142 -0
  6. package/dist/budget-guard.js.map +1 -0
  7. package/dist/compaction/compaction.d.ts +99 -0
  8. package/dist/compaction/compaction.d.ts.map +1 -0
  9. package/dist/compaction/compaction.js +302 -0
  10. package/dist/compaction/compaction.js.map +1 -0
  11. package/dist/compaction/failsafe.d.ts +57 -0
  12. package/dist/compaction/failsafe.d.ts.map +1 -0
  13. package/dist/compaction/failsafe.js +135 -0
  14. package/dist/compaction/failsafe.js.map +1 -0
  15. package/dist/compaction/index.d.ts +381 -0
  16. package/dist/compaction/index.d.ts.map +1 -0
  17. package/dist/compaction/index.js +979 -0
  18. package/dist/compaction/index.js.map +1 -0
  19. package/dist/compaction/microcompaction.d.ts +219 -0
  20. package/dist/compaction/microcompaction.d.ts.map +1 -0
  21. package/dist/compaction/microcompaction.js +536 -0
  22. package/dist/compaction/microcompaction.js.map +1 -0
  23. package/dist/compaction/observational/buffering.d.ts +225 -0
  24. package/dist/compaction/observational/buffering.d.ts.map +1 -0
  25. package/dist/compaction/observational/buffering.js +354 -0
  26. package/dist/compaction/observational/buffering.js.map +1 -0
  27. package/dist/compaction/observational/constants.d.ts +70 -0
  28. package/dist/compaction/observational/constants.d.ts.map +1 -0
  29. package/dist/compaction/observational/constants.js +507 -0
  30. package/dist/compaction/observational/constants.js.map +1 -0
  31. package/dist/compaction/observational/index.d.ts +219 -0
  32. package/dist/compaction/observational/index.d.ts.map +1 -0
  33. package/dist/compaction/observational/index.js +641 -0
  34. package/dist/compaction/observational/index.js.map +1 -0
  35. package/dist/compaction/observational/observer.d.ts +97 -0
  36. package/dist/compaction/observational/observer.d.ts.map +1 -0
  37. package/dist/compaction/observational/observer.js +424 -0
  38. package/dist/compaction/observational/observer.js.map +1 -0
  39. package/dist/compaction/observational/recall-tool.d.ts +27 -0
  40. package/dist/compaction/observational/recall-tool.d.ts.map +1 -0
  41. package/dist/compaction/observational/recall-tool.js +93 -0
  42. package/dist/compaction/observational/recall-tool.js.map +1 -0
  43. package/dist/compaction/observational/reflector.d.ts +94 -0
  44. package/dist/compaction/observational/reflector.d.ts.map +1 -0
  45. package/dist/compaction/observational/reflector.js +167 -0
  46. package/dist/compaction/observational/reflector.js.map +1 -0
  47. package/dist/compaction/observational/types.d.ts +271 -0
  48. package/dist/compaction/observational/types.d.ts.map +1 -0
  49. package/dist/compaction/observational/types.js +15 -0
  50. package/dist/compaction/observational/types.js.map +1 -0
  51. package/dist/context-manager.d.ts +134 -0
  52. package/dist/context-manager.d.ts.map +1 -0
  53. package/dist/context-manager.js +170 -0
  54. package/dist/context-manager.js.map +1 -0
  55. package/dist/cortex-agent.d.ts +1020 -0
  56. package/dist/cortex-agent.d.ts.map +1 -0
  57. package/dist/cortex-agent.js +3589 -0
  58. package/dist/cortex-agent.js.map +1 -0
  59. package/dist/error-classifier.d.ts +48 -0
  60. package/dist/error-classifier.d.ts.map +1 -0
  61. package/dist/error-classifier.js +152 -0
  62. package/dist/error-classifier.js.map +1 -0
  63. package/dist/event-bridge.d.ts +166 -0
  64. package/dist/event-bridge.d.ts.map +1 -0
  65. package/dist/event-bridge.js +381 -0
  66. package/dist/event-bridge.js.map +1 -0
  67. package/dist/index.d.ts +55 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +57 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/mcp-client.d.ts +119 -0
  72. package/dist/mcp-client.d.ts.map +1 -0
  73. package/dist/mcp-client.js +474 -0
  74. package/dist/mcp-client.js.map +1 -0
  75. package/dist/model-wrapper.d.ts +58 -0
  76. package/dist/model-wrapper.d.ts.map +1 -0
  77. package/dist/model-wrapper.js +86 -0
  78. package/dist/model-wrapper.js.map +1 -0
  79. package/dist/noop-logger.d.ts +4 -0
  80. package/dist/noop-logger.d.ts.map +1 -0
  81. package/dist/noop-logger.js +8 -0
  82. package/dist/noop-logger.js.map +1 -0
  83. package/dist/prompt-diagnostics.d.ts +47 -0
  84. package/dist/prompt-diagnostics.d.ts.map +1 -0
  85. package/dist/prompt-diagnostics.js +230 -0
  86. package/dist/prompt-diagnostics.js.map +1 -0
  87. package/dist/provider-manager.d.ts +224 -0
  88. package/dist/provider-manager.d.ts.map +1 -0
  89. package/dist/provider-manager.js +563 -0
  90. package/dist/provider-manager.js.map +1 -0
  91. package/dist/provider-registry.d.ts +115 -0
  92. package/dist/provider-registry.d.ts.map +1 -0
  93. package/dist/provider-registry.js +305 -0
  94. package/dist/provider-registry.js.map +1 -0
  95. package/dist/schema-converter.d.ts +20 -0
  96. package/dist/schema-converter.d.ts.map +1 -0
  97. package/dist/schema-converter.js +48 -0
  98. package/dist/schema-converter.js.map +1 -0
  99. package/dist/skill-preprocessor.d.ts +46 -0
  100. package/dist/skill-preprocessor.d.ts.map +1 -0
  101. package/dist/skill-preprocessor.js +237 -0
  102. package/dist/skill-preprocessor.js.map +1 -0
  103. package/dist/skill-registry.d.ts +107 -0
  104. package/dist/skill-registry.d.ts.map +1 -0
  105. package/dist/skill-registry.js +330 -0
  106. package/dist/skill-registry.js.map +1 -0
  107. package/dist/skill-tool.d.ts +54 -0
  108. package/dist/skill-tool.d.ts.map +1 -0
  109. package/dist/skill-tool.js +88 -0
  110. package/dist/skill-tool.js.map +1 -0
  111. package/dist/sub-agent-manager.d.ts +90 -0
  112. package/dist/sub-agent-manager.d.ts.map +1 -0
  113. package/dist/sub-agent-manager.js +192 -0
  114. package/dist/sub-agent-manager.js.map +1 -0
  115. package/dist/token-estimator.d.ts +23 -0
  116. package/dist/token-estimator.d.ts.map +1 -0
  117. package/dist/token-estimator.js +27 -0
  118. package/dist/token-estimator.js.map +1 -0
  119. package/dist/tool-contract.d.ts +68 -0
  120. package/dist/tool-contract.d.ts.map +1 -0
  121. package/dist/tool-contract.js +35 -0
  122. package/dist/tool-contract.js.map +1 -0
  123. package/dist/tool-result-persistence.d.ts +89 -0
  124. package/dist/tool-result-persistence.d.ts.map +1 -0
  125. package/dist/tool-result-persistence.js +152 -0
  126. package/dist/tool-result-persistence.js.map +1 -0
  127. package/dist/tools/bash/index.d.ts +71 -0
  128. package/dist/tools/bash/index.d.ts.map +1 -0
  129. package/dist/tools/bash/index.js +485 -0
  130. package/dist/tools/bash/index.js.map +1 -0
  131. package/dist/tools/bash/interactive.d.ts +47 -0
  132. package/dist/tools/bash/interactive.d.ts.map +1 -0
  133. package/dist/tools/bash/interactive.js +262 -0
  134. package/dist/tools/bash/interactive.js.map +1 -0
  135. package/dist/tools/bash/safety.d.ts +149 -0
  136. package/dist/tools/bash/safety.d.ts.map +1 -0
  137. package/dist/tools/bash/safety.js +1116 -0
  138. package/dist/tools/bash/safety.js.map +1 -0
  139. package/dist/tools/edit.d.ts +57 -0
  140. package/dist/tools/edit.d.ts.map +1 -0
  141. package/dist/tools/edit.js +310 -0
  142. package/dist/tools/edit.js.map +1 -0
  143. package/dist/tools/glob.d.ts +34 -0
  144. package/dist/tools/glob.d.ts.map +1 -0
  145. package/dist/tools/glob.js +268 -0
  146. package/dist/tools/glob.js.map +1 -0
  147. package/dist/tools/grep.d.ts +53 -0
  148. package/dist/tools/grep.d.ts.map +1 -0
  149. package/dist/tools/grep.js +673 -0
  150. package/dist/tools/grep.js.map +1 -0
  151. package/dist/tools/index.d.ts +62 -0
  152. package/dist/tools/index.d.ts.map +1 -0
  153. package/dist/tools/index.js +52 -0
  154. package/dist/tools/index.js.map +1 -0
  155. package/dist/tools/read.d.ts +43 -0
  156. package/dist/tools/read.d.ts.map +1 -0
  157. package/dist/tools/read.js +459 -0
  158. package/dist/tools/read.js.map +1 -0
  159. package/dist/tools/runtime.d.ts +62 -0
  160. package/dist/tools/runtime.d.ts.map +1 -0
  161. package/dist/tools/runtime.js +116 -0
  162. package/dist/tools/runtime.js.map +1 -0
  163. package/dist/tools/shared/cwd-tracker.d.ts +32 -0
  164. package/dist/tools/shared/cwd-tracker.d.ts.map +1 -0
  165. package/dist/tools/shared/cwd-tracker.js +44 -0
  166. package/dist/tools/shared/cwd-tracker.js.map +1 -0
  167. package/dist/tools/shared/edit-history.d.ts +55 -0
  168. package/dist/tools/shared/edit-history.d.ts.map +1 -0
  169. package/dist/tools/shared/edit-history.js +72 -0
  170. package/dist/tools/shared/edit-history.js.map +1 -0
  171. package/dist/tools/shared/edit-matcher.d.ts +83 -0
  172. package/dist/tools/shared/edit-matcher.d.ts.map +1 -0
  173. package/dist/tools/shared/edit-matcher.js +359 -0
  174. package/dist/tools/shared/edit-matcher.js.map +1 -0
  175. package/dist/tools/shared/file-mutation-lock.d.ts +22 -0
  176. package/dist/tools/shared/file-mutation-lock.d.ts.map +1 -0
  177. package/dist/tools/shared/file-mutation-lock.js +35 -0
  178. package/dist/tools/shared/file-mutation-lock.js.map +1 -0
  179. package/dist/tools/shared/gitignore.d.ts +17 -0
  180. package/dist/tools/shared/gitignore.d.ts.map +1 -0
  181. package/dist/tools/shared/gitignore.js +59 -0
  182. package/dist/tools/shared/gitignore.js.map +1 -0
  183. package/dist/tools/shared/pdf-extractor.d.ts +96 -0
  184. package/dist/tools/shared/pdf-extractor.d.ts.map +1 -0
  185. package/dist/tools/shared/pdf-extractor.js +196 -0
  186. package/dist/tools/shared/pdf-extractor.js.map +1 -0
  187. package/dist/tools/shared/read-registry.d.ts +66 -0
  188. package/dist/tools/shared/read-registry.d.ts.map +1 -0
  189. package/dist/tools/shared/read-registry.js +65 -0
  190. package/dist/tools/shared/read-registry.js.map +1 -0
  191. package/dist/tools/shared/safe-env.d.ts +18 -0
  192. package/dist/tools/shared/safe-env.d.ts.map +1 -0
  193. package/dist/tools/shared/safe-env.js +70 -0
  194. package/dist/tools/shared/safe-env.js.map +1 -0
  195. package/dist/tools/sub-agent.d.ts +91 -0
  196. package/dist/tools/sub-agent.d.ts.map +1 -0
  197. package/dist/tools/sub-agent.js +89 -0
  198. package/dist/tools/sub-agent.js.map +1 -0
  199. package/dist/tools/task-output.d.ts +38 -0
  200. package/dist/tools/task-output.d.ts.map +1 -0
  201. package/dist/tools/task-output.js +186 -0
  202. package/dist/tools/task-output.js.map +1 -0
  203. package/dist/tools/tool-search/index.d.ts +40 -0
  204. package/dist/tools/tool-search/index.d.ts.map +1 -0
  205. package/dist/tools/tool-search/index.js +110 -0
  206. package/dist/tools/tool-search/index.js.map +1 -0
  207. package/dist/tools/tool-search/registry.d.ts +82 -0
  208. package/dist/tools/tool-search/registry.d.ts.map +1 -0
  209. package/dist/tools/tool-search/registry.js +238 -0
  210. package/dist/tools/tool-search/registry.js.map +1 -0
  211. package/dist/tools/undo-edit.d.ts +51 -0
  212. package/dist/tools/undo-edit.d.ts.map +1 -0
  213. package/dist/tools/undo-edit.js +231 -0
  214. package/dist/tools/undo-edit.js.map +1 -0
  215. package/dist/tools/web-fetch/cache.d.ts +49 -0
  216. package/dist/tools/web-fetch/cache.d.ts.map +1 -0
  217. package/dist/tools/web-fetch/cache.js +89 -0
  218. package/dist/tools/web-fetch/cache.js.map +1 -0
  219. package/dist/tools/web-fetch/index.d.ts +53 -0
  220. package/dist/tools/web-fetch/index.d.ts.map +1 -0
  221. package/dist/tools/web-fetch/index.js +513 -0
  222. package/dist/tools/web-fetch/index.js.map +1 -0
  223. package/dist/tools/write.d.ts +59 -0
  224. package/dist/tools/write.d.ts.map +1 -0
  225. package/dist/tools/write.js +316 -0
  226. package/dist/tools/write.js.map +1 -0
  227. package/dist/types.d.ts +881 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +16 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/working-tags.d.ts +44 -0
  232. package/dist/working-tags.d.ts.map +1 -0
  233. package/dist/working-tags.js +103 -0
  234. package/dist/working-tags.js.map +1 -0
  235. package/package.json +87 -0
  236. package/src/budget-guard.ts +170 -0
  237. package/src/compaction/compaction.ts +386 -0
  238. package/src/compaction/failsafe.ts +185 -0
  239. package/src/compaction/index.ts +1199 -0
  240. package/src/compaction/microcompaction.ts +709 -0
  241. package/src/compaction/observational/buffering.ts +430 -0
  242. package/src/compaction/observational/constants.ts +532 -0
  243. package/src/compaction/observational/index.ts +837 -0
  244. package/src/compaction/observational/observer.ts +510 -0
  245. package/src/compaction/observational/recall-tool.ts +130 -0
  246. package/src/compaction/observational/reflector.ts +221 -0
  247. package/src/compaction/observational/types.ts +343 -0
  248. package/src/context-manager.ts +237 -0
  249. package/src/cortex-agent.ts +4297 -0
  250. package/src/error-classifier.ts +199 -0
  251. package/src/event-bridge.ts +508 -0
  252. package/src/index.ts +292 -0
  253. package/src/mcp-client.ts +582 -0
  254. package/src/model-wrapper.ts +128 -0
  255. package/src/noop-logger.ts +9 -0
  256. package/src/prompt-diagnostics.ts +296 -0
  257. package/src/provider-manager.ts +823 -0
  258. package/src/provider-registry.ts +386 -0
  259. package/src/schema-converter.ts +51 -0
  260. package/src/skill-preprocessor.ts +314 -0
  261. package/src/skill-registry.ts +378 -0
  262. package/src/skill-tool.ts +130 -0
  263. package/src/sub-agent-manager.ts +236 -0
  264. package/src/token-estimator.ts +26 -0
  265. package/src/tool-contract.ts +113 -0
  266. package/src/tool-result-persistence.ts +197 -0
  267. package/src/tools/bash/index.ts +633 -0
  268. package/src/tools/bash/interactive.ts +302 -0
  269. package/src/tools/bash/safety.ts +1297 -0
  270. package/src/tools/edit.ts +422 -0
  271. package/src/tools/glob.ts +330 -0
  272. package/src/tools/grep.ts +819 -0
  273. package/src/tools/index.ts +110 -0
  274. package/src/tools/read.ts +580 -0
  275. package/src/tools/runtime.ts +173 -0
  276. package/src/tools/shared/cwd-tracker.ts +50 -0
  277. package/src/tools/shared/edit-history.ts +96 -0
  278. package/src/tools/shared/edit-matcher.ts +457 -0
  279. package/src/tools/shared/file-mutation-lock.ts +40 -0
  280. package/src/tools/shared/gitignore.ts +61 -0
  281. package/src/tools/shared/pdf-extractor.ts +290 -0
  282. package/src/tools/shared/read-registry.ts +93 -0
  283. package/src/tools/shared/safe-env.ts +82 -0
  284. package/src/tools/sub-agent.ts +171 -0
  285. package/src/tools/task-output.ts +236 -0
  286. package/src/tools/tool-search/index.ts +167 -0
  287. package/src/tools/tool-search/registry.ts +278 -0
  288. package/src/tools/undo-edit.ts +314 -0
  289. package/src/tools/web-fetch/cache.ts +112 -0
  290. package/src/tools/web-fetch/index.ts +604 -0
  291. package/src/tools/write.ts +385 -0
  292. package/src/types.ts +1057 -0
  293. package/src/working-tags.ts +118 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Read tool: read file contents from the local filesystem.
3
+ *
4
+ * Returns file content with line numbers in `cat -n` format.
5
+ * Handles text files, images (base64 ImageContent), and
6
+ * detects binary files.
7
+ *
8
+ * Reference: docs/cortex/tools/read.md
9
+ */
10
+ import * as crypto from 'node:crypto';
11
+ import * as fs from 'node:fs';
12
+ import * as path from 'node:path';
13
+ import { Type } from 'typebox';
14
+ import { attachRuntimeAwareTool } from './runtime.js';
15
+ import { estimateTokens } from '../token-estimator.js';
16
+ import { extractPdfText } from './shared/pdf-extractor.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Schema
19
+ // ---------------------------------------------------------------------------
20
+ export const ReadParams = Type.Object({
21
+ file_path: Type.String({ description: 'Absolute path to the file to read' }),
22
+ offset: Type.Optional(Type.Number({ description: 'Line number to start reading from (1-based). Only provide if the file is too large to read at once.' })),
23
+ limit: Type.Optional(Type.Number({ description: 'Maximum number of lines to read. Only provide if the file is too large to read at once.' })),
24
+ pages: Type.Optional(Type.String({ description: 'Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files. Max 20 pages per request.' })),
25
+ });
26
+ // ---------------------------------------------------------------------------
27
+ // Constants
28
+ // ---------------------------------------------------------------------------
29
+ const DEFAULT_LIMIT = 2000;
30
+ const MAX_LINE_LENGTH = 2000;
31
+ /** Pre-read gate for full reads (no offset/limit provided). */
32
+ const MAX_FULL_READ_BYTES = 256 * 1024; // 256 KB
33
+ /** Hard ceiling even with offset/limit. Beyond this, use Bash. */
34
+ const MAX_READABLE_BYTES = 10 * 1024 * 1024; // 10 MB
35
+ /** Post-read token ceiling on formatted output. */
36
+ const MAX_OUTPUT_TOKENS = 25_000;
37
+ const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp']);
38
+ const IMAGE_MIME_TYPES = {
39
+ '.png': 'image/png',
40
+ '.jpg': 'image/jpeg',
41
+ '.jpeg': 'image/jpeg',
42
+ '.gif': 'image/gif',
43
+ '.webp': 'image/webp',
44
+ };
45
+ /**
46
+ * Device files that would hang the process: infinite output or blocking input.
47
+ * Checked by path only (no I/O).
48
+ */
49
+ const BLOCKED_DEVICE_PATHS = new Set([
50
+ // Infinite output
51
+ '/dev/zero',
52
+ '/dev/random',
53
+ '/dev/urandom',
54
+ '/dev/full',
55
+ // Blocks waiting for input
56
+ '/dev/stdin',
57
+ '/dev/tty',
58
+ '/dev/console',
59
+ // Nonsensical to read
60
+ '/dev/stdout',
61
+ '/dev/stderr',
62
+ // fd aliases for stdin/stdout/stderr
63
+ '/dev/fd/0',
64
+ '/dev/fd/1',
65
+ '/dev/fd/2',
66
+ ]);
67
+ function isBlockedDevicePath(filePath) {
68
+ if (BLOCKED_DEVICE_PATHS.has(filePath))
69
+ return true;
70
+ // /proc/self/fd/0-2 and /proc/<pid>/fd/0-2 are Linux aliases for stdio
71
+ if (filePath.startsWith('/proc/') &&
72
+ (filePath.endsWith('/fd/0') ||
73
+ filePath.endsWith('/fd/1') ||
74
+ filePath.endsWith('/fd/2')))
75
+ return true;
76
+ return false;
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Helpers
80
+ // ---------------------------------------------------------------------------
81
+ /**
82
+ * Detect if a buffer contains binary content.
83
+ * A file is considered binary if it contains null bytes in the first 8KB.
84
+ */
85
+ function isBinaryBuffer(buffer) {
86
+ const checkLength = Math.min(buffer.length, 8192);
87
+ for (let i = 0; i < checkLength; i++) {
88
+ if (buffer[i] === 0) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+ /**
95
+ * Try to detect and decode file content with common encodings.
96
+ * Handles UTF-8, UTF-16 LE/BE (via BOM), and falls back to Latin-1.
97
+ */
98
+ function decodeFileContent(buffer) {
99
+ // Check for UTF-16 BOM
100
+ if (buffer.length >= 2) {
101
+ if (buffer[0] === 0xff && buffer[1] === 0xfe) {
102
+ return buffer.toString('utf16le');
103
+ }
104
+ if (buffer[0] === 0xfe && buffer[1] === 0xff) {
105
+ // UTF-16 BE: swap bytes and decode as UTF-16 LE
106
+ const swapped = Buffer.alloc(buffer.length);
107
+ for (let i = 0; i < buffer.length - 1; i += 2) {
108
+ swapped[i] = buffer[i + 1];
109
+ swapped[i + 1] = buffer[i];
110
+ }
111
+ return swapped.toString('utf16le');
112
+ }
113
+ }
114
+ // Check for UTF-8 BOM
115
+ if (buffer.length >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
116
+ return buffer.toString('utf8').slice(1); // Skip the BOM character
117
+ }
118
+ // Try UTF-8 first (most common)
119
+ const utf8 = buffer.toString('utf8');
120
+ // Check for replacement characters that suggest bad UTF-8 decoding
121
+ // Only fall back to Latin-1 if there are many replacement chars
122
+ const replacementCount = (utf8.match(/\ufffd/g) ?? []).length;
123
+ if (replacementCount > 0 && replacementCount > buffer.length * 0.01) {
124
+ return buffer.toString('latin1');
125
+ }
126
+ return utf8;
127
+ }
128
+ /**
129
+ * Format lines with `cat -n` style line numbers.
130
+ * Format: spaces + line_number + tab + content
131
+ */
132
+ function formatWithLineNumbers(lines, startLine) {
133
+ const maxLineNum = startLine + lines.length - 1;
134
+ const width = String(maxLineNum).length;
135
+ return lines
136
+ .map((line, i) => {
137
+ const lineNum = startLine + i;
138
+ const paddedNum = String(lineNum).padStart(width + 2);
139
+ // Truncate long lines
140
+ const truncatedLine = line.length > MAX_LINE_LENGTH
141
+ ? line.slice(0, MAX_LINE_LENGTH) + '... [truncated]'
142
+ : line;
143
+ return `${paddedNum}\t${truncatedLine}`;
144
+ })
145
+ .join('\n');
146
+ }
147
+ /**
148
+ * Format byte count as a human-readable string (KB or MB).
149
+ */
150
+ function formatBytes(bytes) {
151
+ if (bytes >= 1024 * 1024) {
152
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
153
+ }
154
+ return `${Math.round(bytes / 1024)} KB`;
155
+ }
156
+ /**
157
+ * Build a rejection result for size/token gate failures.
158
+ * Returns an error message as tool content with `rejected: true` in details.
159
+ */
160
+ function makeRejection(filePath, byteSize, message) {
161
+ return {
162
+ content: [{ type: 'text', text: message }],
163
+ details: {
164
+ filePath,
165
+ totalLines: 0,
166
+ byteSize,
167
+ truncated: false,
168
+ truncatedLines: false,
169
+ truncatedChars: false,
170
+ startLine: 1,
171
+ rejected: true,
172
+ },
173
+ };
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Tool factory
177
+ // ---------------------------------------------------------------------------
178
+ export function createReadTool(config) {
179
+ const readRegistry = config.runtime?.readRegistry ?? config.readRegistry;
180
+ if (!readRegistry) {
181
+ throw new Error('createReadTool requires either runtime or readRegistry');
182
+ }
183
+ const tool = {
184
+ name: 'Read',
185
+ description: [
186
+ 'Read file contents from the local filesystem.',
187
+ 'Returns content with line numbers in cat -n format.',
188
+ '',
189
+ 'Size limits:',
190
+ `- Files up to ${formatBytes(MAX_FULL_READ_BYTES)}: read in full (no offset/limit needed)`,
191
+ `- Files ${formatBytes(MAX_FULL_READ_BYTES)} to ${formatBytes(MAX_READABLE_BYTES)}: must provide offset and limit`,
192
+ `- Files over ${formatBytes(MAX_READABLE_BYTES)}: use Bash (head, tail, sed) instead`,
193
+ `- Output capped at ~${MAX_OUTPUT_TOKENS.toLocaleString()} tokens; reduce limit if exceeded`,
194
+ '',
195
+ 'For searching file contents, use Grep instead of reading the whole file.',
196
+ ].join('\n'),
197
+ parameters: ReadParams,
198
+ async execute(params) {
199
+ const filePath = path.resolve(params.file_path);
200
+ const offset = params.offset ?? 1;
201
+ const limit = params.limit ?? DEFAULT_LIMIT;
202
+ // Block device paths that would hang (infinite output or blocking input)
203
+ if (isBlockedDevicePath(filePath)) {
204
+ return {
205
+ content: [{ type: 'text', text: `Cannot read '${params.file_path}': this device file would block or produce infinite output.` }],
206
+ details: {
207
+ filePath,
208
+ totalLines: 0,
209
+ byteSize: 0,
210
+ truncated: false,
211
+ truncatedLines: false,
212
+ truncatedChars: false,
213
+ startLine: 1,
214
+ },
215
+ };
216
+ }
217
+ // Check if path exists
218
+ let stat;
219
+ try {
220
+ stat = await fs.promises.stat(filePath);
221
+ }
222
+ catch (err) {
223
+ const code = err.code;
224
+ if (code === 'ENOENT') {
225
+ return {
226
+ content: [{ type: 'text', text: `File does not exist: ${filePath}` }],
227
+ details: {
228
+ filePath,
229
+ totalLines: 0,
230
+ byteSize: 0,
231
+ truncated: false,
232
+ truncatedLines: false,
233
+ truncatedChars: false,
234
+ startLine: 1,
235
+ },
236
+ };
237
+ }
238
+ if (code === 'EACCES') {
239
+ return {
240
+ content: [{ type: 'text', text: `Permission denied: ${filePath}` }],
241
+ details: {
242
+ filePath,
243
+ totalLines: 0,
244
+ byteSize: 0,
245
+ truncated: false,
246
+ truncatedLines: false,
247
+ truncatedChars: false,
248
+ startLine: 1,
249
+ },
250
+ };
251
+ }
252
+ throw err;
253
+ }
254
+ // Cannot read directories
255
+ if (stat.isDirectory()) {
256
+ return {
257
+ content: [{ type: 'text', text: 'Cannot read a directory. Use `ls` via Bash.' }],
258
+ details: {
259
+ filePath,
260
+ totalLines: 0,
261
+ byteSize: 0,
262
+ truncated: false,
263
+ truncatedLines: false,
264
+ truncatedChars: false,
265
+ startLine: 1,
266
+ },
267
+ };
268
+ }
269
+ // Gate 1: Absolute size ceiling - reject files > 10 MB entirely
270
+ if (stat.size > MAX_READABLE_BYTES) {
271
+ return makeRejection(filePath, stat.size, `File is too large to read (${formatBytes(stat.size)}, limit ${formatBytes(MAX_READABLE_BYTES)}). Use Bash with head, tail, or sed to extract specific sections.`);
272
+ }
273
+ const ext = path.extname(filePath).toLowerCase();
274
+ // Handle image files
275
+ if (IMAGE_EXTENSIONS.has(ext)) {
276
+ const buffer = await fs.promises.readFile(filePath);
277
+ const mimeType = IMAGE_MIME_TYPES[ext] ?? 'application/octet-stream';
278
+ const base64 = buffer.toString('base64');
279
+ readRegistry.markRead(filePath, { timestamp: stat.mtimeMs });
280
+ return {
281
+ content: [{ type: 'image', data: base64, mimeType }],
282
+ details: {
283
+ filePath,
284
+ totalLines: 0,
285
+ byteSize: stat.size,
286
+ truncated: false,
287
+ truncatedLines: false,
288
+ truncatedChars: false,
289
+ startLine: 1,
290
+ },
291
+ };
292
+ }
293
+ // Handle PDF files. The extractor (shared/pdf-extractor.ts) wraps
294
+ // unpdf and returns a structured result: the Read tool's only
295
+ // responsibility is to decide how to surface each outcome.
296
+ if (ext === '.pdf') {
297
+ const pdfBuffer = await fs.promises.readFile(filePath);
298
+ const extraction = await extractPdfText({
299
+ data: pdfBuffer,
300
+ pagesSpec: params.pages,
301
+ });
302
+ if (extraction.kind === 'error' ||
303
+ extraction.kind === 'invalid-range' ||
304
+ extraction.kind === 'empty') {
305
+ // All three are read failures from the caller's perspective:
306
+ // there is no usable content to hand to the model. Flag as
307
+ // rejected so consumers can surface them uniformly and the
308
+ // model can retry (with a different pages spec, OCR, etc.).
309
+ return makeRejection(filePath, stat.size, extraction.message);
310
+ }
311
+ // Line-number the rendered output to match the cat -n style
312
+ // used for text files. Line numbers reset to 1 per call; PDFs
313
+ // don't map cleanly to the file-wide offset/limit model.
314
+ const renderedLines = extraction.rendered.split('\n');
315
+ const formatted = formatWithLineNumbers(renderedLines, 1);
316
+ // Gate 3: token ceiling on the formatted output.
317
+ const pdfTokenCount = estimateTokens(formatted);
318
+ if (pdfTokenCount > MAX_OUTPUT_TOKENS) {
319
+ const requestedPages = extraction.lastPage - extraction.firstPage + 1;
320
+ const suggestedPages = Math.max(1, Math.floor(requestedPages * MAX_OUTPUT_TOKENS / pdfTokenCount));
321
+ return makeRejection(filePath, stat.size, `PDF extraction too large (estimated ~${pdfTokenCount.toLocaleString()} tokens, limit ${MAX_OUTPUT_TOKENS.toLocaleString()}). ` +
322
+ `Narrow the \`pages\` range (try ~${suggestedPages} page${suggestedPages === 1 ? '' : 's'} per call).`);
323
+ }
324
+ readRegistry.markRead(filePath, { timestamp: stat.mtimeMs });
325
+ return {
326
+ content: [{ type: 'text', text: formatted }],
327
+ details: {
328
+ filePath,
329
+ totalLines: renderedLines.length,
330
+ byteSize: stat.size,
331
+ truncated: false,
332
+ truncatedLines: false,
333
+ truncatedChars: false,
334
+ startLine: 1,
335
+ },
336
+ };
337
+ }
338
+ // Gate 2: Full-read size gate - reject full reads of files > 256 KB
339
+ const hasExplicitRange = params.offset !== undefined || params.limit !== undefined;
340
+ if (!hasExplicitRange && stat.size > MAX_FULL_READ_BYTES) {
341
+ return makeRejection(filePath, stat.size, `File is too large to read in full (${formatBytes(stat.size)}, limit ${formatBytes(MAX_FULL_READ_BYTES)}). Provide offset and limit to read a specific range, or use Grep to search for specific content.`);
342
+ }
343
+ // File-unchanged dedup: if we already read this exact range and the
344
+ // file hasn't changed on disk, return a stub. The earlier Read result
345
+ // is still in context, so re-sending wastes tokens.
346
+ const existingState = readRegistry.getState(filePath);
347
+ if (existingState && existingState.offset !== undefined) {
348
+ const rangeMatch = existingState.offset === offset && existingState.limit === limit;
349
+ if (rangeMatch && stat.mtimeMs === existingState.timestamp) {
350
+ return {
351
+ content: [{ type: 'text', text: `[File unchanged since last read: ${filePath}]` }],
352
+ details: {
353
+ filePath,
354
+ totalLines: 0,
355
+ byteSize: stat.size,
356
+ truncated: false,
357
+ truncatedLines: false,
358
+ truncatedChars: false,
359
+ startLine: 1,
360
+ },
361
+ };
362
+ }
363
+ }
364
+ // Read the raw buffer
365
+ const buffer = await fs.promises.readFile(filePath);
366
+ // Binary detection (not image, not PDF)
367
+ if (isBinaryBuffer(buffer)) {
368
+ return {
369
+ content: [{ type: 'text', text: 'Binary file detected. Cannot display as text.' }],
370
+ details: {
371
+ filePath,
372
+ totalLines: 0,
373
+ byteSize: stat.size,
374
+ truncated: false,
375
+ truncatedLines: false,
376
+ truncatedChars: false,
377
+ startLine: 1,
378
+ },
379
+ };
380
+ }
381
+ // Decode and split into lines
382
+ const content = decodeFileContent(buffer);
383
+ const allLines = content.split('\n');
384
+ const totalLines = allLines.length;
385
+ // Handle empty file
386
+ if (totalLines === 0 || (totalLines === 1 && allLines[0] === '')) {
387
+ readRegistry.markRead(filePath, { timestamp: stat.mtimeMs, offset, limit });
388
+ return {
389
+ content: [{ type: 'text', text: `[File is empty: ${filePath}]` }],
390
+ details: {
391
+ filePath,
392
+ totalLines: 0,
393
+ byteSize: stat.size,
394
+ truncated: false,
395
+ truncatedLines: false,
396
+ truncatedChars: false,
397
+ startLine: 1,
398
+ },
399
+ };
400
+ }
401
+ // Apply offset and limit
402
+ const startIdx = Math.max(0, offset - 1); // Convert 1-based to 0-based
403
+ const endIdx = Math.min(totalLines, startIdx + limit);
404
+ const selectedLines = allLines.slice(startIdx, endIdx);
405
+ const truncatedLines = endIdx < totalLines;
406
+ const truncatedChars = selectedLines.some((line) => line.length > MAX_LINE_LENGTH);
407
+ // Format with line numbers
408
+ const formatted = formatWithLineNumbers(selectedLines, startIdx + 1);
409
+ // Gate 3: Post-read token estimation
410
+ const estimatedTokenCount = estimateTokens(formatted);
411
+ if (estimatedTokenCount > MAX_OUTPUT_TOKENS) {
412
+ const suggestedLimit = Math.floor(limit * MAX_OUTPUT_TOKENS / estimatedTokenCount);
413
+ return makeRejection(filePath, stat.size, `Read result too large (estimated ~${estimatedTokenCount.toLocaleString()} tokens, limit ${MAX_OUTPUT_TOKENS.toLocaleString()}). ` +
414
+ `The file has ${totalLines} lines. Use a smaller limit (try limit: ${Math.max(1, suggestedLimit)}) ` +
415
+ `or use Grep to find the specific content you need.`);
416
+ }
417
+ // Only mark as read after passing all gates, so rejected reads
418
+ // can be retried without hitting the dedup stub.
419
+ // For full, non-truncated reads, record a content hash so the
420
+ // Edit/Write tools can distinguish real file modifications from
421
+ // mtime-only changes (formatters, cloud sync, antivirus, etc.).
422
+ const isFullRead = !hasExplicitRange && !truncatedLines;
423
+ const contentHash = isFullRead
424
+ ? crypto.createHash('sha256').update(buffer).digest('hex')
425
+ : undefined;
426
+ readRegistry.markRead(filePath, {
427
+ timestamp: stat.mtimeMs,
428
+ offset,
429
+ limit,
430
+ ...(contentHash !== undefined ? { contentHash } : {}),
431
+ });
432
+ let text = formatted;
433
+ if (truncatedLines) {
434
+ text += `\n\n[Showing lines ${startIdx + 1}-${endIdx} of ${totalLines} total. Use offset/limit to read more.]`;
435
+ }
436
+ return {
437
+ content: [{ type: 'text', text }],
438
+ details: {
439
+ filePath,
440
+ totalLines,
441
+ byteSize: stat.size,
442
+ truncated: truncatedLines || truncatedChars,
443
+ truncatedLines,
444
+ truncatedChars,
445
+ startLine: offset,
446
+ },
447
+ };
448
+ },
449
+ };
450
+ return attachRuntimeAwareTool(tool, {
451
+ toolKind: 'Read',
452
+ cloneForRuntime: (runtime) => createReadTool({
453
+ ...config,
454
+ runtime,
455
+ readRegistry: runtime.readRegistry,
456
+ }),
457
+ });
458
+ }
459
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Per-agent runtime state for mutable built-in tool behavior.
3
+ *
4
+ * Cortex clones runtime-aware built-in tools into a fresh runtime for each
5
+ * agent so parent and child agents do not share mutable closures.
6
+ */
7
+ import type * as child_process from 'node:child_process';
8
+ import { CwdTracker } from './shared/cwd-tracker.js';
9
+ import { EditHistory } from './shared/edit-history.js';
10
+ import { FileMutationLock } from './shared/file-mutation-lock.js';
11
+ import { ReadRegistry } from './shared/read-registry.js';
12
+ import { WebFetchCache } from './web-fetch/cache.js';
13
+ export interface BackgroundTask {
14
+ id: string;
15
+ /** The command that was executed (for status display). */
16
+ command: string;
17
+ process: child_process.ChildProcess;
18
+ stdout: string;
19
+ stderr: string;
20
+ exitCode: number | null;
21
+ completed: boolean;
22
+ startTime: number;
23
+ }
24
+ export declare class BackgroundTaskStore {
25
+ private readonly tasks;
26
+ private taskIdCounter;
27
+ nextTaskId(): string;
28
+ set(task: BackgroundTask): void;
29
+ get(taskId: string): BackgroundTask | undefined;
30
+ getAll(): Map<string, BackgroundTask>;
31
+ cleanupCompletedTasks(maxAgeMs?: number): void;
32
+ clear(): void;
33
+ }
34
+ export declare const globalBackgroundTaskStore: BackgroundTaskStore;
35
+ export declare class WebFetchRuntimeState {
36
+ private readonly cache;
37
+ private fetchesThisLoop;
38
+ getCache(): WebFetchCache;
39
+ get fetchCount(): number;
40
+ incrementFetchCount(): void;
41
+ resetLoop(): void;
42
+ destroy(): void;
43
+ }
44
+ export declare class CortexToolRuntime {
45
+ readonly cwdTracker: CwdTracker;
46
+ readonly readRegistry: ReadRegistry;
47
+ readonly fileMutationLock: FileMutationLock;
48
+ readonly editHistory: EditHistory;
49
+ readonly backgroundTasks: BackgroundTaskStore;
50
+ readonly webFetch: WebFetchRuntimeState;
51
+ constructor(workingDirectory: string);
52
+ resetForLoop(): void;
53
+ destroy(): void;
54
+ }
55
+ export interface RuntimeAwareToolMetadata<TTool> {
56
+ readonly toolKind: string;
57
+ cloneForRuntime: (runtime: CortexToolRuntime) => TTool;
58
+ }
59
+ export declare function attachRuntimeAwareTool<TTool extends object>(tool: TTool, metadata: RuntimeAwareToolMetadata<TTool>): TTool;
60
+ export declare function getRuntimeAwareToolMetadata<TTool>(tool: TTool): RuntimeAwareToolMetadata<TTool> | undefined;
61
+ export declare function cloneRuntimeAwareTool<TTool>(tool: TTool, runtime: CortexToolRuntime): TTool | null;
62
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/tools/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,KAAK,aAAa,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAMrD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,aAAa,CAAK;IAE1B,UAAU,IAAI,MAAM;IAKpB,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAI/B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI/C,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAIrC,qBAAqB,CAAC,QAAQ,SAAiB,GAAG,IAAI;IAStD,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,yBAAyB,qBAA4B,CAAC;AAMnE,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,eAAe,CAAK;IAE5B,QAAQ,IAAI,aAAa;IAIzB,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,mBAAmB,IAAI,IAAI;IAI3B,SAAS,IAAI,IAAI;IAIjB,OAAO,IAAI,IAAI;CAGhB;AAMD,qBAAa,iBAAiB;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,mBAAmB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;gBAE5B,gBAAgB,EAAE,MAAM;IASpC,YAAY,IAAI,IAAI;IAQpB,OAAO,IAAI,IAAI;CAOhB;AAMD,MAAM,WAAW,wBAAwB,CAAC,KAAK;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,KAAK,CAAC;CACxD;AAID,wBAAgB,sBAAsB,CAAC,KAAK,SAAS,MAAM,EACzD,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,wBAAwB,CAAC,KAAK,CAAC,GACxC,KAAK,CAQP;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAC/C,IAAI,EAAE,KAAK,GACV,wBAAwB,CAAC,KAAK,CAAC,GAAG,SAAS,CAI7C;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EACzC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,iBAAiB,GACzB,KAAK,GAAG,IAAI,CAGd"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Per-agent runtime state for mutable built-in tool behavior.
3
+ *
4
+ * Cortex clones runtime-aware built-in tools into a fresh runtime for each
5
+ * agent so parent and child agents do not share mutable closures.
6
+ */
7
+ import { CwdTracker } from './shared/cwd-tracker.js';
8
+ import { EditHistory } from './shared/edit-history.js';
9
+ import { FileMutationLock } from './shared/file-mutation-lock.js';
10
+ import { ReadRegistry } from './shared/read-registry.js';
11
+ import { WebFetchCache } from './web-fetch/cache.js';
12
+ export class BackgroundTaskStore {
13
+ tasks = new Map();
14
+ taskIdCounter = 0;
15
+ nextTaskId() {
16
+ this.taskIdCounter += 1;
17
+ return `task_${this.taskIdCounter}`;
18
+ }
19
+ set(task) {
20
+ this.tasks.set(task.id, task);
21
+ }
22
+ get(taskId) {
23
+ return this.tasks.get(taskId);
24
+ }
25
+ getAll() {
26
+ return this.tasks;
27
+ }
28
+ cleanupCompletedTasks(maxAgeMs = 30 * 60 * 1000) {
29
+ const now = Date.now();
30
+ for (const [id, task] of this.tasks) {
31
+ if (task.completed && now - task.startTime > maxAgeMs) {
32
+ this.tasks.delete(id);
33
+ }
34
+ }
35
+ }
36
+ clear() {
37
+ this.tasks.clear();
38
+ }
39
+ }
40
+ export const globalBackgroundTaskStore = new BackgroundTaskStore();
41
+ // ---------------------------------------------------------------------------
42
+ // WebFetch runtime state
43
+ // ---------------------------------------------------------------------------
44
+ export class WebFetchRuntimeState {
45
+ cache = new WebFetchCache();
46
+ fetchesThisLoop = 0;
47
+ getCache() {
48
+ return this.cache;
49
+ }
50
+ get fetchCount() {
51
+ return this.fetchesThisLoop;
52
+ }
53
+ incrementFetchCount() {
54
+ this.fetchesThisLoop += 1;
55
+ }
56
+ resetLoop() {
57
+ this.fetchesThisLoop = 0;
58
+ }
59
+ destroy() {
60
+ this.cache.destroy();
61
+ }
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Per-agent runtime container
65
+ // ---------------------------------------------------------------------------
66
+ export class CortexToolRuntime {
67
+ cwdTracker;
68
+ readRegistry;
69
+ fileMutationLock;
70
+ editHistory;
71
+ backgroundTasks;
72
+ webFetch;
73
+ constructor(workingDirectory) {
74
+ this.cwdTracker = new CwdTracker(workingDirectory);
75
+ this.readRegistry = new ReadRegistry();
76
+ this.fileMutationLock = new FileMutationLock();
77
+ this.editHistory = new EditHistory();
78
+ this.backgroundTasks = new BackgroundTaskStore();
79
+ this.webFetch = new WebFetchRuntimeState();
80
+ }
81
+ resetForLoop() {
82
+ this.cwdTracker.reset();
83
+ this.readRegistry.clear();
84
+ this.fileMutationLock.clear();
85
+ this.editHistory.clear();
86
+ this.webFetch.resetLoop();
87
+ }
88
+ destroy() {
89
+ this.readRegistry.clear();
90
+ this.fileMutationLock.clear();
91
+ this.editHistory.clear();
92
+ this.backgroundTasks.clear();
93
+ this.webFetch.destroy();
94
+ }
95
+ }
96
+ const RUNTIME_AWARE_TOOL = Symbol.for('cortex.runtimeAwareTool');
97
+ export function attachRuntimeAwareTool(tool, metadata) {
98
+ Object.defineProperty(tool, RUNTIME_AWARE_TOOL, {
99
+ value: metadata,
100
+ enumerable: false,
101
+ configurable: false,
102
+ writable: false,
103
+ });
104
+ return tool;
105
+ }
106
+ export function getRuntimeAwareToolMetadata(tool) {
107
+ if (!tool || typeof tool !== 'object')
108
+ return undefined;
109
+ const record = tool;
110
+ return record[RUNTIME_AWARE_TOOL];
111
+ }
112
+ export function cloneRuntimeAwareTool(tool, runtime) {
113
+ const metadata = getRuntimeAwareToolMetadata(tool);
114
+ return metadata ? metadata.cloneForRuntime(runtime) : null;
115
+ }
116
+ //# sourceMappingURL=runtime.js.map