@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,173 @@
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
+
8
+ import type * as child_process from 'node:child_process';
9
+ import { CwdTracker } from './shared/cwd-tracker.js';
10
+ import { EditHistory } from './shared/edit-history.js';
11
+ import { FileMutationLock } from './shared/file-mutation-lock.js';
12
+ import { ReadRegistry } from './shared/read-registry.js';
13
+ import { WebFetchCache } from './web-fetch/cache.js';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Background task state
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export interface BackgroundTask {
20
+ id: string;
21
+ /** The command that was executed (for status display). */
22
+ command: string;
23
+ process: child_process.ChildProcess;
24
+ stdout: string;
25
+ stderr: string;
26
+ exitCode: number | null;
27
+ completed: boolean;
28
+ startTime: number;
29
+ }
30
+
31
+ export class BackgroundTaskStore {
32
+ private readonly tasks = new Map<string, BackgroundTask>();
33
+ private taskIdCounter = 0;
34
+
35
+ nextTaskId(): string {
36
+ this.taskIdCounter += 1;
37
+ return `task_${this.taskIdCounter}`;
38
+ }
39
+
40
+ set(task: BackgroundTask): void {
41
+ this.tasks.set(task.id, task);
42
+ }
43
+
44
+ get(taskId: string): BackgroundTask | undefined {
45
+ return this.tasks.get(taskId);
46
+ }
47
+
48
+ getAll(): Map<string, BackgroundTask> {
49
+ return this.tasks;
50
+ }
51
+
52
+ cleanupCompletedTasks(maxAgeMs = 30 * 60 * 1000): void {
53
+ const now = Date.now();
54
+ for (const [id, task] of this.tasks) {
55
+ if (task.completed && now - task.startTime > maxAgeMs) {
56
+ this.tasks.delete(id);
57
+ }
58
+ }
59
+ }
60
+
61
+ clear(): void {
62
+ this.tasks.clear();
63
+ }
64
+ }
65
+
66
+ export const globalBackgroundTaskStore = new BackgroundTaskStore();
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // WebFetch runtime state
70
+ // ---------------------------------------------------------------------------
71
+
72
+ export class WebFetchRuntimeState {
73
+ private readonly cache = new WebFetchCache();
74
+ private fetchesThisLoop = 0;
75
+
76
+ getCache(): WebFetchCache {
77
+ return this.cache;
78
+ }
79
+
80
+ get fetchCount(): number {
81
+ return this.fetchesThisLoop;
82
+ }
83
+
84
+ incrementFetchCount(): void {
85
+ this.fetchesThisLoop += 1;
86
+ }
87
+
88
+ resetLoop(): void {
89
+ this.fetchesThisLoop = 0;
90
+ }
91
+
92
+ destroy(): void {
93
+ this.cache.destroy();
94
+ }
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Per-agent runtime container
99
+ // ---------------------------------------------------------------------------
100
+
101
+ export class CortexToolRuntime {
102
+ readonly cwdTracker: CwdTracker;
103
+ readonly readRegistry: ReadRegistry;
104
+ readonly fileMutationLock: FileMutationLock;
105
+ readonly editHistory: EditHistory;
106
+ readonly backgroundTasks: BackgroundTaskStore;
107
+ readonly webFetch: WebFetchRuntimeState;
108
+
109
+ constructor(workingDirectory: string) {
110
+ this.cwdTracker = new CwdTracker(workingDirectory);
111
+ this.readRegistry = new ReadRegistry();
112
+ this.fileMutationLock = new FileMutationLock();
113
+ this.editHistory = new EditHistory();
114
+ this.backgroundTasks = new BackgroundTaskStore();
115
+ this.webFetch = new WebFetchRuntimeState();
116
+ }
117
+
118
+ resetForLoop(): void {
119
+ this.cwdTracker.reset();
120
+ this.readRegistry.clear();
121
+ this.fileMutationLock.clear();
122
+ this.editHistory.clear();
123
+ this.webFetch.resetLoop();
124
+ }
125
+
126
+ destroy(): void {
127
+ this.readRegistry.clear();
128
+ this.fileMutationLock.clear();
129
+ this.editHistory.clear();
130
+ this.backgroundTasks.clear();
131
+ this.webFetch.destroy();
132
+ }
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Runtime-aware tool metadata
137
+ // ---------------------------------------------------------------------------
138
+
139
+ export interface RuntimeAwareToolMetadata<TTool> {
140
+ readonly toolKind: string;
141
+ cloneForRuntime: (runtime: CortexToolRuntime) => TTool;
142
+ }
143
+
144
+ const RUNTIME_AWARE_TOOL = Symbol.for('cortex.runtimeAwareTool');
145
+
146
+ export function attachRuntimeAwareTool<TTool extends object>(
147
+ tool: TTool,
148
+ metadata: RuntimeAwareToolMetadata<TTool>,
149
+ ): TTool {
150
+ Object.defineProperty(tool, RUNTIME_AWARE_TOOL, {
151
+ value: metadata,
152
+ enumerable: false,
153
+ configurable: false,
154
+ writable: false,
155
+ });
156
+ return tool;
157
+ }
158
+
159
+ export function getRuntimeAwareToolMetadata<TTool>(
160
+ tool: TTool,
161
+ ): RuntimeAwareToolMetadata<TTool> | undefined {
162
+ if (!tool || typeof tool !== 'object') return undefined;
163
+ const record = tool as Record<string | symbol, unknown>;
164
+ return record[RUNTIME_AWARE_TOOL] as RuntimeAwareToolMetadata<TTool> | undefined;
165
+ }
166
+
167
+ export function cloneRuntimeAwareTool<TTool>(
168
+ tool: TTool,
169
+ runtime: CortexToolRuntime,
170
+ ): TTool | null {
171
+ const metadata = getRuntimeAwareToolMetadata(tool);
172
+ return metadata ? metadata.cloneForRuntime(runtime) : null;
173
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Working directory tracking across Bash calls.
3
+ *
4
+ * Each Bash tool call spawns a new shell process. Shell state
5
+ * (env vars, aliases) does NOT persist between calls. Only the
6
+ * working directory persists via this tracker.
7
+ *
8
+ * The tracker is reset to the default directory at the start
9
+ * of each agentic loop.
10
+ */
11
+
12
+ import * as path from 'node:path';
13
+
14
+ export class CwdTracker {
15
+ private readonly defaultDir: string;
16
+ private currentDir: string;
17
+
18
+ constructor(defaultDir: string) {
19
+ this.defaultDir = path.resolve(defaultDir);
20
+ this.currentDir = this.defaultDir;
21
+ }
22
+
23
+ /**
24
+ * Get the current working directory.
25
+ */
26
+ getCwd(): string {
27
+ return this.currentDir;
28
+ }
29
+
30
+ /**
31
+ * Update the working directory. The path is resolved to absolute.
32
+ */
33
+ updateCwd(newDir: string): void {
34
+ this.currentDir = path.resolve(newDir);
35
+ }
36
+
37
+ /**
38
+ * Reset to the default directory. Called at the start of each agentic loop.
39
+ */
40
+ reset(): void {
41
+ this.currentDir = this.defaultDir;
42
+ }
43
+
44
+ /**
45
+ * Get the default (initial) directory.
46
+ */
47
+ getDefaultDir(): string {
48
+ return this.defaultDir;
49
+ }
50
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Per-file pre-mutation snapshot stack.
3
+ *
4
+ * Both Edit and Write push a snapshot immediately AFTER each successful
5
+ * mutation. The UndoEdit tool pops the most recent snapshot to revert
6
+ * a file to its prior state, as long as the on-disk state still matches
7
+ * what we recorded when the mutation completed (so we never undo on top
8
+ * of unrelated external changes).
9
+ *
10
+ * Scoped per-agent via `CortexToolRuntime`, cleared on each loop reset
11
+ * — history does not persist across agentic loops or across agents.
12
+ *
13
+ * The stack is intentionally bounded (`MAX_STACK_DEPTH`). An unbounded
14
+ * stack would accumulate full file contents during long refactors and
15
+ * bloat memory. A deep stack is also rarely useful for the agentic
16
+ * loop: if the model wants to roll back more than a few edits, it's
17
+ * more reliable to Read the file and write the intended content
18
+ * directly.
19
+ */
20
+
21
+ import * as path from 'node:path';
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Types
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export interface EditHistoryEntry {
28
+ /**
29
+ * File contents BEFORE the mutation, or `null` when the file did not
30
+ * exist before the mutation (created by Write). Undoing a `null`
31
+ * entry deletes the file.
32
+ */
33
+ originalContent: string | null;
34
+ /** mtime (ms since epoch) of the file immediately AFTER the mutation. */
35
+ postMutationMtimeMs: number;
36
+ /** SHA-256 hex digest of the file bytes immediately AFTER the mutation. */
37
+ postMutationContentHash: string;
38
+ /** Which tool created the entry (for diagnostics / undo messaging). */
39
+ source: 'Edit' | 'Write';
40
+ }
41
+
42
+ /** Upper bound on snapshots retained per file. */
43
+ export const MAX_STACK_DEPTH = 5;
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Registry
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export class EditHistory {
50
+ private readonly stacks = new Map<string, EditHistoryEntry[]>();
51
+
52
+ /**
53
+ * Push a snapshot for `filePath`. When the per-file stack is at its
54
+ * depth cap, the oldest entry is dropped so newer edits remain
55
+ * undoable.
56
+ */
57
+ record(filePath: string, entry: EditHistoryEntry): void {
58
+ const key = this.normalize(filePath);
59
+ const stack = this.stacks.get(key);
60
+ if (stack === undefined) {
61
+ this.stacks.set(key, [entry]);
62
+ return;
63
+ }
64
+ stack.push(entry);
65
+ while (stack.length > MAX_STACK_DEPTH) stack.shift();
66
+ }
67
+
68
+ /**
69
+ * Pop and return the most recent entry for `filePath`, or `undefined`
70
+ * when the file has no recorded history.
71
+ */
72
+ pop(filePath: string): EditHistoryEntry | undefined {
73
+ const key = this.normalize(filePath);
74
+ const stack = this.stacks.get(key);
75
+ if (!stack || stack.length === 0) return undefined;
76
+ const entry = stack.pop()!;
77
+ if (stack.length === 0) this.stacks.delete(key);
78
+ return entry;
79
+ }
80
+
81
+ /** Current stack depth for `filePath` (0 when absent). Diagnostic use. */
82
+ depth(filePath: string): number {
83
+ return this.stacks.get(this.normalize(filePath))?.length ?? 0;
84
+ }
85
+
86
+ /** Drop all history. Called from `CortexToolRuntime.resetForLoop`. */
87
+ clear(): void {
88
+ this.stacks.clear();
89
+ }
90
+
91
+ private normalize(filePath: string): string {
92
+ // Matches the normalization used by FileMutationLock and
93
+ // ReadRegistry so keys stay consistent across the three.
94
+ return path.resolve(filePath);
95
+ }
96
+ }