@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.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/budget-guard.d.ts +75 -0
- package/dist/budget-guard.d.ts.map +1 -0
- package/dist/budget-guard.js +142 -0
- package/dist/budget-guard.js.map +1 -0
- package/dist/compaction/compaction.d.ts +99 -0
- package/dist/compaction/compaction.d.ts.map +1 -0
- package/dist/compaction/compaction.js +302 -0
- package/dist/compaction/compaction.js.map +1 -0
- package/dist/compaction/failsafe.d.ts +57 -0
- package/dist/compaction/failsafe.d.ts.map +1 -0
- package/dist/compaction/failsafe.js +135 -0
- package/dist/compaction/failsafe.js.map +1 -0
- package/dist/compaction/index.d.ts +381 -0
- package/dist/compaction/index.d.ts.map +1 -0
- package/dist/compaction/index.js +979 -0
- package/dist/compaction/index.js.map +1 -0
- package/dist/compaction/microcompaction.d.ts +219 -0
- package/dist/compaction/microcompaction.d.ts.map +1 -0
- package/dist/compaction/microcompaction.js +536 -0
- package/dist/compaction/microcompaction.js.map +1 -0
- package/dist/compaction/observational/buffering.d.ts +225 -0
- package/dist/compaction/observational/buffering.d.ts.map +1 -0
- package/dist/compaction/observational/buffering.js +354 -0
- package/dist/compaction/observational/buffering.js.map +1 -0
- package/dist/compaction/observational/constants.d.ts +70 -0
- package/dist/compaction/observational/constants.d.ts.map +1 -0
- package/dist/compaction/observational/constants.js +507 -0
- package/dist/compaction/observational/constants.js.map +1 -0
- package/dist/compaction/observational/index.d.ts +219 -0
- package/dist/compaction/observational/index.d.ts.map +1 -0
- package/dist/compaction/observational/index.js +641 -0
- package/dist/compaction/observational/index.js.map +1 -0
- package/dist/compaction/observational/observer.d.ts +97 -0
- package/dist/compaction/observational/observer.d.ts.map +1 -0
- package/dist/compaction/observational/observer.js +424 -0
- package/dist/compaction/observational/observer.js.map +1 -0
- package/dist/compaction/observational/recall-tool.d.ts +27 -0
- package/dist/compaction/observational/recall-tool.d.ts.map +1 -0
- package/dist/compaction/observational/recall-tool.js +93 -0
- package/dist/compaction/observational/recall-tool.js.map +1 -0
- package/dist/compaction/observational/reflector.d.ts +94 -0
- package/dist/compaction/observational/reflector.d.ts.map +1 -0
- package/dist/compaction/observational/reflector.js +167 -0
- package/dist/compaction/observational/reflector.js.map +1 -0
- package/dist/compaction/observational/types.d.ts +271 -0
- package/dist/compaction/observational/types.d.ts.map +1 -0
- package/dist/compaction/observational/types.js +15 -0
- package/dist/compaction/observational/types.js.map +1 -0
- package/dist/context-manager.d.ts +134 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +170 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/cortex-agent.d.ts +1020 -0
- package/dist/cortex-agent.d.ts.map +1 -0
- package/dist/cortex-agent.js +3589 -0
- package/dist/cortex-agent.js.map +1 -0
- package/dist/error-classifier.d.ts +48 -0
- package/dist/error-classifier.d.ts.map +1 -0
- package/dist/error-classifier.js +152 -0
- package/dist/error-classifier.js.map +1 -0
- package/dist/event-bridge.d.ts +166 -0
- package/dist/event-bridge.d.ts.map +1 -0
- package/dist/event-bridge.js +381 -0
- package/dist/event-bridge.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-client.d.ts +119 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +474 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/model-wrapper.d.ts +58 -0
- package/dist/model-wrapper.d.ts.map +1 -0
- package/dist/model-wrapper.js +86 -0
- package/dist/model-wrapper.js.map +1 -0
- package/dist/noop-logger.d.ts +4 -0
- package/dist/noop-logger.d.ts.map +1 -0
- package/dist/noop-logger.js +8 -0
- package/dist/noop-logger.js.map +1 -0
- package/dist/prompt-diagnostics.d.ts +47 -0
- package/dist/prompt-diagnostics.d.ts.map +1 -0
- package/dist/prompt-diagnostics.js +230 -0
- package/dist/prompt-diagnostics.js.map +1 -0
- package/dist/provider-manager.d.ts +224 -0
- package/dist/provider-manager.d.ts.map +1 -0
- package/dist/provider-manager.js +563 -0
- package/dist/provider-manager.js.map +1 -0
- package/dist/provider-registry.d.ts +115 -0
- package/dist/provider-registry.d.ts.map +1 -0
- package/dist/provider-registry.js +305 -0
- package/dist/provider-registry.js.map +1 -0
- package/dist/schema-converter.d.ts +20 -0
- package/dist/schema-converter.d.ts.map +1 -0
- package/dist/schema-converter.js +48 -0
- package/dist/schema-converter.js.map +1 -0
- package/dist/skill-preprocessor.d.ts +46 -0
- package/dist/skill-preprocessor.d.ts.map +1 -0
- package/dist/skill-preprocessor.js +237 -0
- package/dist/skill-preprocessor.js.map +1 -0
- package/dist/skill-registry.d.ts +107 -0
- package/dist/skill-registry.d.ts.map +1 -0
- package/dist/skill-registry.js +330 -0
- package/dist/skill-registry.js.map +1 -0
- package/dist/skill-tool.d.ts +54 -0
- package/dist/skill-tool.d.ts.map +1 -0
- package/dist/skill-tool.js +88 -0
- package/dist/skill-tool.js.map +1 -0
- package/dist/sub-agent-manager.d.ts +90 -0
- package/dist/sub-agent-manager.d.ts.map +1 -0
- package/dist/sub-agent-manager.js +192 -0
- package/dist/sub-agent-manager.js.map +1 -0
- package/dist/token-estimator.d.ts +23 -0
- package/dist/token-estimator.d.ts.map +1 -0
- package/dist/token-estimator.js +27 -0
- package/dist/token-estimator.js.map +1 -0
- package/dist/tool-contract.d.ts +68 -0
- package/dist/tool-contract.d.ts.map +1 -0
- package/dist/tool-contract.js +35 -0
- package/dist/tool-contract.js.map +1 -0
- package/dist/tool-result-persistence.d.ts +89 -0
- package/dist/tool-result-persistence.d.ts.map +1 -0
- package/dist/tool-result-persistence.js +152 -0
- package/dist/tool-result-persistence.js.map +1 -0
- package/dist/tools/bash/index.d.ts +71 -0
- package/dist/tools/bash/index.d.ts.map +1 -0
- package/dist/tools/bash/index.js +485 -0
- package/dist/tools/bash/index.js.map +1 -0
- package/dist/tools/bash/interactive.d.ts +47 -0
- package/dist/tools/bash/interactive.d.ts.map +1 -0
- package/dist/tools/bash/interactive.js +262 -0
- package/dist/tools/bash/interactive.js.map +1 -0
- package/dist/tools/bash/safety.d.ts +149 -0
- package/dist/tools/bash/safety.d.ts.map +1 -0
- package/dist/tools/bash/safety.js +1116 -0
- package/dist/tools/bash/safety.js.map +1 -0
- package/dist/tools/edit.d.ts +57 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +310 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +34 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +268 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +53 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +673 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +62 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +43 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +459 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/runtime.d.ts +62 -0
- package/dist/tools/runtime.d.ts.map +1 -0
- package/dist/tools/runtime.js +116 -0
- package/dist/tools/runtime.js.map +1 -0
- package/dist/tools/shared/cwd-tracker.d.ts +32 -0
- package/dist/tools/shared/cwd-tracker.d.ts.map +1 -0
- package/dist/tools/shared/cwd-tracker.js +44 -0
- package/dist/tools/shared/cwd-tracker.js.map +1 -0
- package/dist/tools/shared/edit-history.d.ts +55 -0
- package/dist/tools/shared/edit-history.d.ts.map +1 -0
- package/dist/tools/shared/edit-history.js +72 -0
- package/dist/tools/shared/edit-history.js.map +1 -0
- package/dist/tools/shared/edit-matcher.d.ts +83 -0
- package/dist/tools/shared/edit-matcher.d.ts.map +1 -0
- package/dist/tools/shared/edit-matcher.js +359 -0
- package/dist/tools/shared/edit-matcher.js.map +1 -0
- package/dist/tools/shared/file-mutation-lock.d.ts +22 -0
- package/dist/tools/shared/file-mutation-lock.d.ts.map +1 -0
- package/dist/tools/shared/file-mutation-lock.js +35 -0
- package/dist/tools/shared/file-mutation-lock.js.map +1 -0
- package/dist/tools/shared/gitignore.d.ts +17 -0
- package/dist/tools/shared/gitignore.d.ts.map +1 -0
- package/dist/tools/shared/gitignore.js +59 -0
- package/dist/tools/shared/gitignore.js.map +1 -0
- package/dist/tools/shared/pdf-extractor.d.ts +96 -0
- package/dist/tools/shared/pdf-extractor.d.ts.map +1 -0
- package/dist/tools/shared/pdf-extractor.js +196 -0
- package/dist/tools/shared/pdf-extractor.js.map +1 -0
- package/dist/tools/shared/read-registry.d.ts +66 -0
- package/dist/tools/shared/read-registry.d.ts.map +1 -0
- package/dist/tools/shared/read-registry.js +65 -0
- package/dist/tools/shared/read-registry.js.map +1 -0
- package/dist/tools/shared/safe-env.d.ts +18 -0
- package/dist/tools/shared/safe-env.d.ts.map +1 -0
- package/dist/tools/shared/safe-env.js +70 -0
- package/dist/tools/shared/safe-env.js.map +1 -0
- package/dist/tools/sub-agent.d.ts +91 -0
- package/dist/tools/sub-agent.d.ts.map +1 -0
- package/dist/tools/sub-agent.js +89 -0
- package/dist/tools/sub-agent.js.map +1 -0
- package/dist/tools/task-output.d.ts +38 -0
- package/dist/tools/task-output.d.ts.map +1 -0
- package/dist/tools/task-output.js +186 -0
- package/dist/tools/task-output.js.map +1 -0
- package/dist/tools/tool-search/index.d.ts +40 -0
- package/dist/tools/tool-search/index.d.ts.map +1 -0
- package/dist/tools/tool-search/index.js +110 -0
- package/dist/tools/tool-search/index.js.map +1 -0
- package/dist/tools/tool-search/registry.d.ts +82 -0
- package/dist/tools/tool-search/registry.d.ts.map +1 -0
- package/dist/tools/tool-search/registry.js +238 -0
- package/dist/tools/tool-search/registry.js.map +1 -0
- package/dist/tools/undo-edit.d.ts +51 -0
- package/dist/tools/undo-edit.d.ts.map +1 -0
- package/dist/tools/undo-edit.js +231 -0
- package/dist/tools/undo-edit.js.map +1 -0
- package/dist/tools/web-fetch/cache.d.ts +49 -0
- package/dist/tools/web-fetch/cache.d.ts.map +1 -0
- package/dist/tools/web-fetch/cache.js +89 -0
- package/dist/tools/web-fetch/cache.js.map +1 -0
- package/dist/tools/web-fetch/index.d.ts +53 -0
- package/dist/tools/web-fetch/index.d.ts.map +1 -0
- package/dist/tools/web-fetch/index.js +513 -0
- package/dist/tools/web-fetch/index.js.map +1 -0
- package/dist/tools/write.d.ts +59 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +316 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +881 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/working-tags.d.ts +44 -0
- package/dist/working-tags.d.ts.map +1 -0
- package/dist/working-tags.js +103 -0
- package/dist/working-tags.js.map +1 -0
- package/package.json +87 -0
- package/src/budget-guard.ts +170 -0
- package/src/compaction/compaction.ts +386 -0
- package/src/compaction/failsafe.ts +185 -0
- package/src/compaction/index.ts +1199 -0
- package/src/compaction/microcompaction.ts +709 -0
- package/src/compaction/observational/buffering.ts +430 -0
- package/src/compaction/observational/constants.ts +532 -0
- package/src/compaction/observational/index.ts +837 -0
- package/src/compaction/observational/observer.ts +510 -0
- package/src/compaction/observational/recall-tool.ts +130 -0
- package/src/compaction/observational/reflector.ts +221 -0
- package/src/compaction/observational/types.ts +343 -0
- package/src/context-manager.ts +237 -0
- package/src/cortex-agent.ts +4297 -0
- package/src/error-classifier.ts +199 -0
- package/src/event-bridge.ts +508 -0
- package/src/index.ts +292 -0
- package/src/mcp-client.ts +582 -0
- package/src/model-wrapper.ts +128 -0
- package/src/noop-logger.ts +9 -0
- package/src/prompt-diagnostics.ts +296 -0
- package/src/provider-manager.ts +823 -0
- package/src/provider-registry.ts +386 -0
- package/src/schema-converter.ts +51 -0
- package/src/skill-preprocessor.ts +314 -0
- package/src/skill-registry.ts +378 -0
- package/src/skill-tool.ts +130 -0
- package/src/sub-agent-manager.ts +236 -0
- package/src/token-estimator.ts +26 -0
- package/src/tool-contract.ts +113 -0
- package/src/tool-result-persistence.ts +197 -0
- package/src/tools/bash/index.ts +633 -0
- package/src/tools/bash/interactive.ts +302 -0
- package/src/tools/bash/safety.ts +1297 -0
- package/src/tools/edit.ts +422 -0
- package/src/tools/glob.ts +330 -0
- package/src/tools/grep.ts +819 -0
- package/src/tools/index.ts +110 -0
- package/src/tools/read.ts +580 -0
- package/src/tools/runtime.ts +173 -0
- package/src/tools/shared/cwd-tracker.ts +50 -0
- package/src/tools/shared/edit-history.ts +96 -0
- package/src/tools/shared/edit-matcher.ts +457 -0
- package/src/tools/shared/file-mutation-lock.ts +40 -0
- package/src/tools/shared/gitignore.ts +61 -0
- package/src/tools/shared/pdf-extractor.ts +290 -0
- package/src/tools/shared/read-registry.ts +93 -0
- package/src/tools/shared/safe-env.ts +82 -0
- package/src/tools/sub-agent.ts +171 -0
- package/src/tools/task-output.ts +236 -0
- package/src/tools/tool-search/index.ts +167 -0
- package/src/tools/tool-search/registry.ts +278 -0
- package/src/tools/undo-edit.ts +314 -0
- package/src/tools/web-fetch/cache.ts +112 -0
- package/src/tools/web-fetch/index.ts +604 -0
- package/src/tools/write.ts +385 -0
- package/src/types.ts +1057 -0
- package/src/working-tags.ts +118 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UndoEdit tool: revert the most recent Edit or Write on a single file.
|
|
3
|
+
*
|
|
4
|
+
* Uses the per-runtime `EditHistory` stack: every successful Edit /
|
|
5
|
+
* Write push a pre-mutation snapshot, and `UndoEdit` pops the top
|
|
6
|
+
* snapshot to restore the prior state.
|
|
7
|
+
*
|
|
8
|
+
* The undo is guarded: we verify the current on-disk state still
|
|
9
|
+
* matches what we recorded at the moment the mutation completed (via
|
|
10
|
+
* mtime + SHA-256 hash). If anything has diverged since — another
|
|
11
|
+
* Edit/Write that wasn't history-tracked, a formatter, cloud sync —
|
|
12
|
+
* we refuse rather than overwrite unrelated changes.
|
|
13
|
+
*
|
|
14
|
+
* Reference: docs/cortex/tools/undo-edit.md
|
|
15
|
+
*/
|
|
16
|
+
import * as crypto from 'node:crypto';
|
|
17
|
+
import * as fs from 'node:fs';
|
|
18
|
+
import * as path from 'node:path';
|
|
19
|
+
import { Type } from 'typebox';
|
|
20
|
+
import { attachRuntimeAwareTool } from './runtime.js';
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Schema
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
export const UndoEditParams = Type.Object({
|
|
25
|
+
file_path: Type.String({
|
|
26
|
+
description: 'Absolute path to the file whose most recent Edit or Write should be reverted.',
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Helpers
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
function hashBytes(data) {
|
|
33
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
function rejection(filePath, text, remainingDepth) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text }],
|
|
38
|
+
details: { filePath, rejected: true, remainingDepth },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Atomically write the restored content to disk. Mirrors the pattern
|
|
43
|
+
* used by Edit and Write so concurrent consumers observe a coherent
|
|
44
|
+
* file at all times.
|
|
45
|
+
*/
|
|
46
|
+
async function atomicWrite(filePath, content) {
|
|
47
|
+
const tempPath = path.join(path.dirname(filePath), `.undo-${crypto.randomUUID()}.tmp`);
|
|
48
|
+
try {
|
|
49
|
+
await fs.promises.writeFile(tempPath, content, 'utf8');
|
|
50
|
+
try {
|
|
51
|
+
await fs.promises.rename(tempPath, filePath);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Rename may fail on Windows if the target is open. Fall back to a
|
|
55
|
+
// direct write and clean up the temp file.
|
|
56
|
+
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
57
|
+
try {
|
|
58
|
+
await fs.promises.unlink(tempPath);
|
|
59
|
+
}
|
|
60
|
+
catch { /* ignore */ }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
try {
|
|
65
|
+
await fs.promises.unlink(tempPath);
|
|
66
|
+
}
|
|
67
|
+
catch { /* ignore */ }
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Tool factory
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
export function createUndoEditTool(config) {
|
|
75
|
+
const editHistory = config.runtime?.editHistory ?? config.editHistory;
|
|
76
|
+
if (!editHistory) {
|
|
77
|
+
throw new Error('createUndoEditTool requires either runtime or editHistory');
|
|
78
|
+
}
|
|
79
|
+
const readRegistry = config.runtime?.readRegistry ?? config.readRegistry;
|
|
80
|
+
const fileMutationLock = config.runtime?.fileMutationLock ?? config.fileMutationLock;
|
|
81
|
+
const tool = {
|
|
82
|
+
name: 'UndoEdit',
|
|
83
|
+
description: 'Revert the most recent Edit or Write on a single file, returning it to the state it had before the last mutation. ' +
|
|
84
|
+
'Only reverts the most recent mutation; call repeatedly to roll back multiple steps (bounded history). ' +
|
|
85
|
+
'Refuses if the file has been modified externally since the mutation was recorded.',
|
|
86
|
+
parameters: UndoEditParams,
|
|
87
|
+
async execute(params) {
|
|
88
|
+
const filePath = path.resolve(params.file_path);
|
|
89
|
+
const release = fileMutationLock ? await fileMutationLock.acquire(filePath) : undefined;
|
|
90
|
+
try {
|
|
91
|
+
// Peek-then-pop pattern isn't expressible directly since `pop`
|
|
92
|
+
// returns the entry. We pop immediately and, on abort, push it
|
|
93
|
+
// back so the stack isn't silently truncated by a rejected
|
|
94
|
+
// undo.
|
|
95
|
+
const entry = editHistory.pop(filePath);
|
|
96
|
+
if (!entry) {
|
|
97
|
+
return rejection(filePath, `No recorded Edit or Write to undo for ${filePath}.`, 0);
|
|
98
|
+
}
|
|
99
|
+
const restoreEntry = () => editHistory.record(filePath, entry);
|
|
100
|
+
const remainingAfter = editHistory.depth(filePath);
|
|
101
|
+
// Verify current on-disk state matches what we recorded when
|
|
102
|
+
// the mutation completed. Any mismatch means the file drifted
|
|
103
|
+
// and undoing would stomp unrelated changes.
|
|
104
|
+
const verification = await verifyPostMutationState(filePath, entry);
|
|
105
|
+
if (verification.kind === 'drift') {
|
|
106
|
+
restoreEntry();
|
|
107
|
+
return rejection(filePath, `Cannot undo: ${verification.reason} Read the file to resync, then issue an explicit Edit or Write.`, remainingAfter + 1);
|
|
108
|
+
}
|
|
109
|
+
// Apply the revert.
|
|
110
|
+
if (entry.originalContent === null) {
|
|
111
|
+
// Write created the file; undo = delete.
|
|
112
|
+
try {
|
|
113
|
+
await fs.promises.unlink(filePath);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
restoreEntry();
|
|
117
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
118
|
+
return rejection(filePath, `Cannot undo: failed to delete ${filePath}: ${msg}`, remainingAfter + 1);
|
|
119
|
+
}
|
|
120
|
+
readRegistry?.invalidate(filePath);
|
|
121
|
+
return {
|
|
122
|
+
content: [{
|
|
123
|
+
type: 'text',
|
|
124
|
+
text: `Undid ${entry.source} of ${filePath} (file deleted; was created by the prior ${entry.source}).`,
|
|
125
|
+
}],
|
|
126
|
+
details: {
|
|
127
|
+
filePath,
|
|
128
|
+
revertedSource: entry.source,
|
|
129
|
+
deleted: true,
|
|
130
|
+
rejected: false,
|
|
131
|
+
remainingDepth: remainingAfter,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Existing file — restore prior content.
|
|
136
|
+
try {
|
|
137
|
+
await atomicWrite(filePath, entry.originalContent);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
restoreEntry();
|
|
141
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
142
|
+
return rejection(filePath, `Cannot undo: failed to restore ${filePath}: ${msg}`, remainingAfter + 1);
|
|
143
|
+
}
|
|
144
|
+
// Refresh read state: after a successful undo, the agent's
|
|
145
|
+
// knowledge of the file is the restored content.
|
|
146
|
+
try {
|
|
147
|
+
const postStat = await fs.promises.stat(filePath);
|
|
148
|
+
const postHash = hashBytes(entry.originalContent);
|
|
149
|
+
readRegistry?.markRead(filePath, {
|
|
150
|
+
timestamp: postStat.mtimeMs,
|
|
151
|
+
contentHash: postHash,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
readRegistry?.invalidate(filePath);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
content: [{
|
|
159
|
+
type: 'text',
|
|
160
|
+
text: `Undid ${entry.source} of ${filePath} (restored ${entry.originalContent.length} character${entry.originalContent.length === 1 ? '' : 's'} of prior content).`,
|
|
161
|
+
}],
|
|
162
|
+
details: {
|
|
163
|
+
filePath,
|
|
164
|
+
revertedSource: entry.source,
|
|
165
|
+
restored: true,
|
|
166
|
+
rejected: false,
|
|
167
|
+
remainingDepth: remainingAfter,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
release?.();
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
return attachRuntimeAwareTool(tool, {
|
|
177
|
+
toolKind: 'UndoEdit',
|
|
178
|
+
cloneForRuntime: (runtime) => createUndoEditTool({
|
|
179
|
+
...config,
|
|
180
|
+
runtime,
|
|
181
|
+
editHistory: runtime.editHistory,
|
|
182
|
+
readRegistry: runtime.readRegistry,
|
|
183
|
+
fileMutationLock: runtime.fileMutationLock,
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Confirm that the file on disk still carries the exact post-mutation
|
|
189
|
+
* fingerprint we recorded. Two axes:
|
|
190
|
+
* - existence: the file must be present iff we expected it to be
|
|
191
|
+
* - identity: bytes must hash to the recorded value
|
|
192
|
+
* mtime is checked first as a cheap short-circuit; on mtime mismatch
|
|
193
|
+
* the hash check is authoritative (formatters / cloud sync can touch
|
|
194
|
+
* mtime without changing bytes).
|
|
195
|
+
*/
|
|
196
|
+
async function verifyPostMutationState(filePath, entry) {
|
|
197
|
+
let stat;
|
|
198
|
+
try {
|
|
199
|
+
stat = await fs.promises.stat(filePath);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
const code = err.code;
|
|
203
|
+
if (code === 'ENOENT') {
|
|
204
|
+
return {
|
|
205
|
+
kind: 'drift',
|
|
206
|
+
reason: 'the file has been deleted since the recorded mutation.',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
kind: 'drift',
|
|
211
|
+
reason: `the file is inaccessible (${code ?? 'unknown error'}).`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Fast path: mtime matches exactly. (We use equality here because
|
|
215
|
+
// we're comparing against our OWN previously-recorded mtime, not a
|
|
216
|
+
// user-provided read state — quirks around mtime going backwards
|
|
217
|
+
// aren't relevant when both values came from our writes.)
|
|
218
|
+
if (stat.mtimeMs === entry.postMutationMtimeMs)
|
|
219
|
+
return { kind: 'ok' };
|
|
220
|
+
// mtime drifted — confirm via hash before rejecting, since formatters
|
|
221
|
+
// and cloud-sync can touch mtime without changing bytes.
|
|
222
|
+
const bytes = await fs.promises.readFile(filePath);
|
|
223
|
+
const currentHash = hashBytes(bytes);
|
|
224
|
+
if (currentHash === entry.postMutationContentHash)
|
|
225
|
+
return { kind: 'ok' };
|
|
226
|
+
return {
|
|
227
|
+
kind: 'drift',
|
|
228
|
+
reason: 'the file has been modified since the recorded mutation.',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=undo-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo-edit.js","sourceRoot":"","sources":["../../src/tools/undo-edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory URL cache with 15-minute TTL for WebFetch tool.
|
|
3
|
+
*
|
|
4
|
+
* Keyed by URL. Cached entries include the converted markdown content
|
|
5
|
+
* so repeated fetches skip both the HTTP request and HTML conversion.
|
|
6
|
+
*
|
|
7
|
+
* Self-cleaning: expired entries are removed on access and periodically.
|
|
8
|
+
*/
|
|
9
|
+
export interface CacheEntry {
|
|
10
|
+
content: string;
|
|
11
|
+
fetchedAt: number;
|
|
12
|
+
statusCode: number;
|
|
13
|
+
finalUrl: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class WebFetchCache {
|
|
16
|
+
private readonly cache;
|
|
17
|
+
private readonly ttlMs;
|
|
18
|
+
private cleanupTimer;
|
|
19
|
+
constructor(ttlMs?: number);
|
|
20
|
+
/**
|
|
21
|
+
* Get a cached entry for a URL, or undefined if not cached or expired.
|
|
22
|
+
*/
|
|
23
|
+
get(url: string): CacheEntry | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Store a cache entry for a URL.
|
|
26
|
+
*/
|
|
27
|
+
set(url: string, entry: CacheEntry): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a URL has a valid (non-expired) cache entry.
|
|
30
|
+
*/
|
|
31
|
+
has(url: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Remove expired entries.
|
|
34
|
+
*/
|
|
35
|
+
cleanup(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all cached entries.
|
|
38
|
+
*/
|
|
39
|
+
clear(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Stop the cleanup timer. Call on shutdown.
|
|
42
|
+
*/
|
|
43
|
+
destroy(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get the number of cached entries (for diagnostics).
|
|
46
|
+
*/
|
|
47
|
+
get size(): number;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/tools/web-fetch/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAaD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,YAAY,CAA+C;gBAEvD,KAAK,GAAE,MAAuB;IAU1C;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAYxC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAIzC;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB;;OAEG;IACH,OAAO,IAAI,IAAI;IASf;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,IAAI,IAAI;IAQf;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory URL cache with 15-minute TTL for WebFetch tool.
|
|
3
|
+
*
|
|
4
|
+
* Keyed by URL. Cached entries include the converted markdown content
|
|
5
|
+
* so repeated fetches skip both the HTTP request and HTML conversion.
|
|
6
|
+
*
|
|
7
|
+
* Self-cleaning: expired entries are removed on access and periodically.
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Constants
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const DEFAULT_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
|
13
|
+
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Cache
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
export class WebFetchCache {
|
|
18
|
+
cache = new Map();
|
|
19
|
+
ttlMs;
|
|
20
|
+
cleanupTimer = null;
|
|
21
|
+
constructor(ttlMs = DEFAULT_TTL_MS) {
|
|
22
|
+
this.ttlMs = ttlMs;
|
|
23
|
+
// Start periodic cleanup
|
|
24
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
|
|
25
|
+
// Unref the timer so it doesn't prevent process exit
|
|
26
|
+
if (this.cleanupTimer && typeof this.cleanupTimer === 'object' && 'unref' in this.cleanupTimer) {
|
|
27
|
+
this.cleanupTimer.unref();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get a cached entry for a URL, or undefined if not cached or expired.
|
|
32
|
+
*/
|
|
33
|
+
get(url) {
|
|
34
|
+
const entry = this.cache.get(url);
|
|
35
|
+
if (!entry)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (Date.now() - entry.fetchedAt > this.ttlMs) {
|
|
38
|
+
this.cache.delete(url);
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return entry;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Store a cache entry for a URL.
|
|
45
|
+
*/
|
|
46
|
+
set(url, entry) {
|
|
47
|
+
this.cache.set(url, entry);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if a URL has a valid (non-expired) cache entry.
|
|
51
|
+
*/
|
|
52
|
+
has(url) {
|
|
53
|
+
return this.get(url) !== undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Remove expired entries.
|
|
57
|
+
*/
|
|
58
|
+
cleanup() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
for (const [url, entry] of this.cache) {
|
|
61
|
+
if (now - entry.fetchedAt > this.ttlMs) {
|
|
62
|
+
this.cache.delete(url);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Clear all cached entries.
|
|
68
|
+
*/
|
|
69
|
+
clear() {
|
|
70
|
+
this.cache.clear();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stop the cleanup timer. Call on shutdown.
|
|
74
|
+
*/
|
|
75
|
+
destroy() {
|
|
76
|
+
if (this.cleanupTimer) {
|
|
77
|
+
clearInterval(this.cleanupTimer);
|
|
78
|
+
this.cleanupTimer = null;
|
|
79
|
+
}
|
|
80
|
+
this.cache.clear();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the number of cached entries (for diagnostics).
|
|
84
|
+
*/
|
|
85
|
+
get size() {
|
|
86
|
+
return this.cache.size;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/tools/web-fetch/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AACpD,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAEvD,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,MAAM,OAAO,aAAa;IACP,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,KAAK,CAAS;IACvB,YAAY,GAA0C,IAAI,CAAC;IAEnE,YAAY,QAAgB,cAAc;QACxC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,yBAAyB;QACzB,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAC3E,qDAAqD;QACrD,IAAI,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC/F,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW,EAAE,KAAiB;QAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebFetch tool: fetch a web page and return its content as processed text.
|
|
3
|
+
*
|
|
4
|
+
* Two-model architecture:
|
|
5
|
+
* 1. Fetch: HTTP request via Node built-in fetch
|
|
6
|
+
* 2. Convert: HTML to markdown via Turndown
|
|
7
|
+
* 3. Summarize: utility model answers the prompt using the page content
|
|
8
|
+
*
|
|
9
|
+
* The main agent never sees raw page content.
|
|
10
|
+
*
|
|
11
|
+
* Reference: docs/cortex/tools/web-fetch.md
|
|
12
|
+
*/
|
|
13
|
+
import { Type, type Static } from 'typebox';
|
|
14
|
+
import type { ToolContentDetails } from '../../types.js';
|
|
15
|
+
import { WebFetchCache } from './cache.js';
|
|
16
|
+
import type { CortexToolRuntime } from '../runtime.js';
|
|
17
|
+
export declare const WebFetchParams: Type.TObject<{
|
|
18
|
+
url: Type.TString;
|
|
19
|
+
prompt: Type.TString;
|
|
20
|
+
}>;
|
|
21
|
+
export type WebFetchParamsType = Static<typeof WebFetchParams>;
|
|
22
|
+
export interface WebFetchDetails {
|
|
23
|
+
finalUrl: string;
|
|
24
|
+
statusCode: number;
|
|
25
|
+
cacheHit: boolean;
|
|
26
|
+
rawSize: number;
|
|
27
|
+
markdownSize: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check whether an IP address (v4 or v6) belongs to a private, loopback,
|
|
31
|
+
* link-local, or otherwise non-routable range. Handles IPv4-mapped IPv6
|
|
32
|
+
* addresses (::ffff:x.x.x.x) and parses octets numerically to catch
|
|
33
|
+
* alternate encodings (decimal IPs, zero-padded, etc.).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isPrivateIp(ip: string): boolean;
|
|
36
|
+
export interface WebFetchToolConfig {
|
|
37
|
+
runtime?: CortexToolRuntime | undefined;
|
|
38
|
+
/** Utility model completion function for summarization. */
|
|
39
|
+
utilityComplete?: ((context: unknown) => Promise<unknown>) | undefined;
|
|
40
|
+
/** Max fetches per agentic loop. */
|
|
41
|
+
maxPerLoop?: number | undefined;
|
|
42
|
+
}
|
|
43
|
+
export declare function createWebFetchTool(config: WebFetchToolConfig): {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
parameters: typeof WebFetchParams;
|
|
47
|
+
execute: (params: WebFetchParamsType) => Promise<ToolContentDetails<WebFetchDetails>>;
|
|
48
|
+
/** Reset the per-loop rate counter. Called at the start of each loop. */
|
|
49
|
+
resetRateLimit: () => void;
|
|
50
|
+
/** Get the underlying cache (for testing/diagnostics). */
|
|
51
|
+
getCache: () => WebFetchCache;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/web-fetch/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAOvD,eAAO,MAAM,cAAc;;;EAGzB,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAM/D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAsCD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CA8C/C;AAyBD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,2DAA2D;IAC3D,eAAe,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACvE,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAwFD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,cAAc,CAAC;IAClC,OAAO,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC,CAAC;IACtF,yEAAyE;IACzE,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,aAAa,CAAC;CAC/B,CA8RA"}
|