@renxqoo/renx-code 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -40
- package/bin/renx.cjs +79 -42
- package/bin/renx.exe +0 -0
- package/package.json +10 -28
- package/src/App.tsx +0 -297
- package/src/agent/runtime/event-format.ts +0 -258
- package/src/agent/runtime/model-types.ts +0 -13
- package/src/agent/runtime/runtime.context-usage.test.ts +0 -192
- package/src/agent/runtime/runtime.error-handling.test.ts +0 -235
- package/src/agent/runtime/runtime.simple.test.ts +0 -16
- package/src/agent/runtime/runtime.test.ts +0 -296
- package/src/agent/runtime/runtime.ts +0 -875
- package/src/agent/runtime/runtime.usage-forwarding.test.ts +0 -228
- package/src/agent/runtime/source-modules.test.ts +0 -38
- package/src/agent/runtime/source-modules.ts +0 -370
- package/src/agent/runtime/tool-call-buffer.test.ts +0 -65
- package/src/agent/runtime/tool-call-buffer.ts +0 -60
- package/src/agent/runtime/tool-confirmation.test.ts +0 -56
- package/src/agent/runtime/tool-confirmation.ts +0 -15
- package/src/agent/runtime/types.ts +0 -99
- package/src/commands/slash-commands.test.ts +0 -216
- package/src/commands/slash-commands.ts +0 -64
- package/src/components/chat/assistant-reply.test.tsx +0 -47
- package/src/components/chat/assistant-reply.tsx +0 -136
- package/src/components/chat/assistant-segment.test.ts +0 -99
- package/src/components/chat/assistant-segment.tsx +0 -125
- package/src/components/chat/assistant-tool-group.tsx +0 -900
- package/src/components/chat/code-block.test.tsx +0 -206
- package/src/components/chat/code-block.tsx +0 -313
- package/src/components/chat/prompt-card.tsx +0 -81
- package/src/components/chat/segment-groups.test.ts +0 -52
- package/src/components/chat/segment-groups.ts +0 -106
- package/src/components/chat/turn-item.tsx +0 -39
- package/src/components/conversation-panel.tsx +0 -43
- package/src/components/file-mention-menu.tsx +0 -77
- package/src/components/file-picker-dialog.tsx +0 -206
- package/src/components/footer-hints.tsx +0 -75
- package/src/components/model-picker-dialog.tsx +0 -248
- package/src/components/prompt.tsx +0 -233
- package/src/components/slash-command-menu.tsx +0 -65
- package/src/components/tool-confirm-dialog-content.test.ts +0 -103
- package/src/components/tool-confirm-dialog-content.ts +0 -186
- package/src/components/tool-confirm-dialog.tsx +0 -187
- package/src/components/tool-display-config.ts +0 -119
- package/src/context-usage-regressions.test.ts +0 -26
- package/src/files/attachment-capabilities.test.ts +0 -30
- package/src/files/attachment-capabilities.ts +0 -50
- package/src/files/attachment-content.ts +0 -153
- package/src/files/file-mention-query.test.ts +0 -34
- package/src/files/file-mention-query.ts +0 -32
- package/src/files/prompt-display.ts +0 -13
- package/src/files/types.ts +0 -5
- package/src/files/workspace-files.ts +0 -61
- package/src/hooks/agent-event-handlers.test.ts +0 -207
- package/src/hooks/agent-event-handlers.ts +0 -196
- package/src/hooks/chat-local-replies.fixed.test.ts +0 -119
- package/src/hooks/chat-local-replies.test.ts +0 -153
- package/src/hooks/chat-local-replies.ts +0 -63
- package/src/hooks/turn-updater.test.ts +0 -70
- package/src/hooks/turn-updater.ts +0 -166
- package/src/hooks/use-agent-chat.context.test.ts +0 -10
- package/src/hooks/use-agent-chat.status.test.ts +0 -14
- package/src/hooks/use-agent-chat.test.ts +0 -80
- package/src/hooks/use-agent-chat.ts +0 -621
- package/src/hooks/use-file-mention-menu.ts +0 -196
- package/src/hooks/use-file-picker.ts +0 -185
- package/src/hooks/use-model-picker.ts +0 -196
- package/src/hooks/use-slash-command-menu.ts +0 -154
- package/src/index.tsx +0 -55
- package/src/runtime/clipboard.test.ts +0 -43
- package/src/runtime/clipboard.ts +0 -89
- package/src/runtime/exit.test.ts +0 -177
- package/src/runtime/exit.ts +0 -98
- package/src/runtime/runtime-support.test.ts +0 -31
- package/src/runtime/terminal-theme.test.ts +0 -55
- package/src/runtime/terminal-theme.ts +0 -196
- package/src/types/chat.ts +0 -32
- package/src/types/message-content.ts +0 -48
- package/src/ui/open-code-theme.ts +0 -176
- package/src/ui/opencode-markdown.ts +0 -211
- package/src/ui/theme.simple.test.ts +0 -52
- package/src/ui/theme.test.ts +0 -151
- package/src/ui/theme.ts +0 -152
- package/src/utils/time.test.ts +0 -144
- package/src/utils/time.ts +0 -7
- package/tsconfig.json +0 -30
- package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +0 -95
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +0 -1345
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +0 -1353
- package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +0 -60
- package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +0 -278
- package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +0 -72
- package/vendor/agent-root/src/agent/__test__/types.test.ts +0 -137
- package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +0 -83
- package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +0 -34
- package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +0 -323
- package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +0 -290
- package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +0 -377
- package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +0 -212
- package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +0 -295
- package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +0 -3607
- package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +0 -35
- package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +0 -517
- package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +0 -97
- package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +0 -479
- package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +0 -80
- package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +0 -76
- package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +0 -173
- package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +0 -109
- package/vendor/agent-root/src/agent/agent/abort-runtime.ts +0 -71
- package/vendor/agent-root/src/agent/agent/callback-safety.ts +0 -33
- package/vendor/agent-root/src/agent/agent/compaction.ts +0 -291
- package/vendor/agent-root/src/agent/agent/concurrency.ts +0 -103
- package/vendor/agent-root/src/agent/agent/error-normalizer.ts +0 -190
- package/vendor/agent-root/src/agent/agent/error.ts +0 -198
- package/vendor/agent-root/src/agent/agent/index.ts +0 -1772
- package/vendor/agent-root/src/agent/agent/logger.ts +0 -65
- package/vendor/agent-root/src/agent/agent/message-utils.ts +0 -101
- package/vendor/agent-root/src/agent/agent/stream-events.ts +0 -61
- package/vendor/agent-root/src/agent/agent/telemetry.ts +0 -123
- package/vendor/agent-root/src/agent/agent/timeout-budget.ts +0 -227
- package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +0 -111
- package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +0 -164
- package/vendor/agent-root/src/agent/agent/write-buffer.ts +0 -188
- package/vendor/agent-root/src/agent/agent/write-file-session.ts +0 -238
- package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +0 -1053
- package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +0 -158
- package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +0 -437
- package/vendor/agent-root/src/agent/app/agent-app-service.ts +0 -748
- package/vendor/agent-root/src/agent/app/contracts.ts +0 -109
- package/vendor/agent-root/src/agent/app/index.ts +0 -5
- package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +0 -151
- package/vendor/agent-root/src/agent/app/ports.ts +0 -72
- package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +0 -1182
- package/vendor/agent-root/src/agent/app/sqlite-client.ts +0 -177
- package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +0 -36
- package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +0 -33
- package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +0 -40
- package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +0 -91
- package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +0 -116
- package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +0 -52
- package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +0 -53
- package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +0 -52
- package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +0 -40
- package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +0 -19
- package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +0 -28
- package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +0 -26
- package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +0 -30
- package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +0 -567
- package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +0 -583
- package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +0 -972
- package/vendor/agent-root/src/agent/error-contract.ts +0 -154
- package/vendor/agent-root/src/agent/prompts/system.ts +0 -246
- package/vendor/agent-root/src/agent/prompts/system1.ts +0 -208
- package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +0 -98
- package/vendor/agent-root/src/agent/storage/file-history-store.ts +0 -313
- package/vendor/agent-root/src/agent/storage/file-storage-config.ts +0 -94
- package/vendor/agent-root/src/agent/storage/file-system.ts +0 -31
- package/vendor/agent-root/src/agent/storage/file-write-service.ts +0 -21
- package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +0 -413
- package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +0 -356
- package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +0 -375
- package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +0 -372
- package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +0 -108
- package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +0 -258
- package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +0 -121
- package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +0 -210
- package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +0 -139
- package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +0 -456
- package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +0 -192
- package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +0 -300
- package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +0 -214
- package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +0 -336
- package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +0 -494
- package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +0 -543
- package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +0 -172
- package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +0 -116
- package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +0 -267
- package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +0 -519
- package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +0 -225
- package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +0 -223
- package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +0 -184
- package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +0 -287
- package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +0 -190
- package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +0 -352
- package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +0 -395
- package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +0 -391
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +0 -176
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +0 -68
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +0 -630
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +0 -732
- package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +0 -494
- package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +0 -175
- package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +0 -505
- package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +0 -55
- package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +0 -244
- package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +0 -290
- package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +0 -368
- package/vendor/agent-root/src/agent/tool/base-tool.ts +0 -345
- package/vendor/agent-root/src/agent/tool/bash-policy.ts +0 -636
- package/vendor/agent-root/src/agent/tool/bash.ts +0 -688
- package/vendor/agent-root/src/agent/tool/error.ts +0 -131
- package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +0 -264
- package/vendor/agent-root/src/agent/tool/file-history-list.ts +0 -103
- package/vendor/agent-root/src/agent/tool/file-history-restore.ts +0 -149
- package/vendor/agent-root/src/agent/tool/file-read-tool.ts +0 -211
- package/vendor/agent-root/src/agent/tool/glob.ts +0 -171
- package/vendor/agent-root/src/agent/tool/grep.ts +0 -496
- package/vendor/agent-root/src/agent/tool/lsp.ts +0 -481
- package/vendor/agent-root/src/agent/tool/path-security.ts +0 -117
- package/vendor/agent-root/src/agent/tool/search/common.ts +0 -153
- package/vendor/agent-root/src/agent/tool/skill/index.ts +0 -13
- package/vendor/agent-root/src/agent/tool/skill/loader.ts +0 -229
- package/vendor/agent-root/src/agent/tool/skill/parser.ts +0 -124
- package/vendor/agent-root/src/agent/tool/skill/types.ts +0 -27
- package/vendor/agent-root/src/agent/tool/skill-tool.ts +0 -143
- package/vendor/agent-root/src/agent/tool/task-create.ts +0 -186
- package/vendor/agent-root/src/agent/tool/task-errors.ts +0 -42
- package/vendor/agent-root/src/agent/tool/task-get.ts +0 -116
- package/vendor/agent-root/src/agent/tool/task-graph.ts +0 -78
- package/vendor/agent-root/src/agent/tool/task-list.ts +0 -141
- package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +0 -232
- package/vendor/agent-root/src/agent/tool/task-output.ts +0 -223
- package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +0 -115
- package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +0 -336
- package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +0 -55
- package/vendor/agent-root/src/agent/tool/task-stop.ts +0 -187
- package/vendor/agent-root/src/agent/tool/task-store.ts +0 -217
- package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +0 -149
- package/vendor/agent-root/src/agent/tool/task-types.ts +0 -264
- package/vendor/agent-root/src/agent/tool/task-update.ts +0 -315
- package/vendor/agent-root/src/agent/tool/task.ts +0 -209
- package/vendor/agent-root/src/agent/tool/tool-manager.ts +0 -361
- package/vendor/agent-root/src/agent/tool/tool-prompts.ts +0 -242
- package/vendor/agent-root/src/agent/tool/types.ts +0 -116
- package/vendor/agent-root/src/agent/tool/web-fetch.ts +0 -227
- package/vendor/agent-root/src/agent/tool/web-search.ts +0 -208
- package/vendor/agent-root/src/agent/tool/write-file.ts +0 -497
- package/vendor/agent-root/src/agent/types.ts +0 -232
- package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +0 -18
- package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +0 -610
- package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +0 -223
- package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +0 -42
- package/vendor/agent-root/src/agent/utils/index.ts +0 -16
- package/vendor/agent-root/src/agent/utils/message.ts +0 -171
- package/vendor/agent-root/src/agent/utils/token.ts +0 -28
- package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +0 -238
- package/vendor/agent-root/src/config/__tests__/loader.test.ts +0 -361
- package/vendor/agent-root/src/config/__tests__/runtime.test.ts +0 -88
- package/vendor/agent-root/src/config/index.ts +0 -55
- package/vendor/agent-root/src/config/loader.ts +0 -494
- package/vendor/agent-root/src/config/paths.ts +0 -30
- package/vendor/agent-root/src/config/runtime.ts +0 -163
- package/vendor/agent-root/src/config/types.ts +0 -96
- package/vendor/agent-root/src/logger/index.ts +0 -57
- package/vendor/agent-root/src/logger/logger.ts +0 -819
- package/vendor/agent-root/src/logger/types.ts +0 -150
- package/vendor/agent-root/src/providers/__tests__/errors.test.ts +0 -441
- package/vendor/agent-root/src/providers/__tests__/index.test.ts +0 -16
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +0 -318
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +0 -600
- package/vendor/agent-root/src/providers/__tests__/registry.test.ts +0 -523
- package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +0 -298
- package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +0 -354
- package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +0 -58
- package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +0 -261
- package/vendor/agent-root/src/providers/adapters/anthropic.ts +0 -572
- package/vendor/agent-root/src/providers/adapters/base.ts +0 -131
- package/vendor/agent-root/src/providers/adapters/kimi.ts +0 -48
- package/vendor/agent-root/src/providers/adapters/responses.ts +0 -732
- package/vendor/agent-root/src/providers/adapters/standard.ts +0 -120
- package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +0 -313
- package/vendor/agent-root/src/providers/http/client.ts +0 -289
- package/vendor/agent-root/src/providers/http/stream-parser.ts +0 -109
- package/vendor/agent-root/src/providers/index.ts +0 -76
- package/vendor/agent-root/src/providers/kimi-headers.ts +0 -177
- package/vendor/agent-root/src/providers/openai-compatible.ts +0 -387
- package/vendor/agent-root/src/providers/registry/model-config.ts +0 -477
- package/vendor/agent-root/src/providers/registry/provider-factory.ts +0 -127
- package/vendor/agent-root/src/providers/registry.ts +0 -135
- package/vendor/agent-root/src/providers/types/api.ts +0 -284
- package/vendor/agent-root/src/providers/types/config.ts +0 -58
- package/vendor/agent-root/src/providers/types/errors.ts +0 -323
- package/vendor/agent-root/src/providers/types/index.ts +0 -72
- package/vendor/agent-root/src/providers/types/provider.ts +0 -45
- package/vendor/agent-root/src/providers/types/registry.ts +0 -68
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
|
-
import * as os from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { FileEditTool } from '../file-edit-tool';
|
|
6
|
-
import { FileReadTool } from '../file-read-tool';
|
|
7
|
-
import { FileHistoryStore } from '../../storage/file-history-store';
|
|
8
|
-
import type { FileStorageConfig } from '../../storage/file-storage-config';
|
|
9
|
-
|
|
10
|
-
function createHistoryStore(rootDir: string): FileHistoryStore {
|
|
11
|
-
const config: FileStorageConfig = {
|
|
12
|
-
rootDir,
|
|
13
|
-
writeBufferDir: path.join(rootDir, 'cache', 'write-buffer'),
|
|
14
|
-
historyDir: path.join(rootDir, 'history'),
|
|
15
|
-
historyEnabled: true,
|
|
16
|
-
historyMaxPerFile: 20,
|
|
17
|
-
historyMaxAgeDays: 14,
|
|
18
|
-
historyMaxTotalBytes: 1024 * 1024,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
return new FileHistoryStore({ config });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('FileEditTool', () => {
|
|
25
|
-
let rootDir: string;
|
|
26
|
-
let outsideDir: string;
|
|
27
|
-
let editTool: FileEditTool;
|
|
28
|
-
let readTool: FileReadTool;
|
|
29
|
-
|
|
30
|
-
beforeEach(async () => {
|
|
31
|
-
rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-file-edit-tool-'));
|
|
32
|
-
outsideDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-file-edit-outside-'));
|
|
33
|
-
|
|
34
|
-
editTool = new FileEditTool({ allowedDirectories: [rootDir] });
|
|
35
|
-
readTool = new FileReadTool({ allowedDirectories: [rootDir] });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
afterEach(async () => {
|
|
39
|
-
await fs.rm(rootDir, { recursive: true, force: true });
|
|
40
|
-
await fs.rm(outsideDir, { recursive: true, force: true });
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('previews and applies edits', async () => {
|
|
44
|
-
const targetPath = path.join(rootDir, 'edit.txt');
|
|
45
|
-
await fs.writeFile(targetPath, 'const value = 1;\nconsole.log(value);\n', 'utf8');
|
|
46
|
-
|
|
47
|
-
const preview = await editTool.execute({
|
|
48
|
-
path: targetPath,
|
|
49
|
-
edits: [{ oldText: 'value = 1', newText: 'value = 2' }],
|
|
50
|
-
dry_run: true,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
expect(preview.success).toBe(true);
|
|
54
|
-
expect(preview.output).toContain('value = 2');
|
|
55
|
-
expect((preview.metadata as { changed: boolean }).changed).toBe(true);
|
|
56
|
-
|
|
57
|
-
const apply = await editTool.execute({
|
|
58
|
-
path: targetPath,
|
|
59
|
-
edits: [{ oldText: 'value = 1', newText: 'value = 2' }],
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(apply.success).toBe(true);
|
|
63
|
-
expect((apply.metadata as { changed: boolean }).changed).toBe(true);
|
|
64
|
-
|
|
65
|
-
const readBack = await readTool.execute({ path: targetPath });
|
|
66
|
-
expect(readBack.success).toBe(true);
|
|
67
|
-
expect(readBack.output).toContain('value = 2');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('stores the previous file version before applying edits', async () => {
|
|
71
|
-
const historyStore = createHistoryStore(rootDir);
|
|
72
|
-
editTool = new FileEditTool({
|
|
73
|
-
allowedDirectories: [rootDir],
|
|
74
|
-
historyStore,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const targetPath = path.join(rootDir, 'history-edit.txt');
|
|
78
|
-
await fs.writeFile(targetPath, 'const value = 1;\n', 'utf8');
|
|
79
|
-
|
|
80
|
-
const result = await editTool.execute({
|
|
81
|
-
path: targetPath,
|
|
82
|
-
edits: [{ oldText: 'value = 1', newText: 'value = 2' }],
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(result.success).toBe(true);
|
|
86
|
-
const versions = await historyStore.listVersions(targetPath);
|
|
87
|
-
expect(versions).toHaveLength(1);
|
|
88
|
-
|
|
89
|
-
const restored = await historyStore.restoreVersion(targetPath, versions[0].versionId);
|
|
90
|
-
expect(restored).toBe(true);
|
|
91
|
-
expect(await fs.readFile(targetPath, 'utf8')).toBe('const value = 1;\n');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('returns no-change metadata when replacement is identical', async () => {
|
|
95
|
-
const targetPath = path.join(rootDir, 'same.txt');
|
|
96
|
-
await fs.writeFile(targetPath, 'const answer = 42;\n', 'utf8');
|
|
97
|
-
|
|
98
|
-
const result = await editTool.execute({
|
|
99
|
-
path: targetPath,
|
|
100
|
-
edits: [{ oldText: 'const answer = 42;', newText: 'const answer = 42;' }],
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(result.success).toBe(true);
|
|
104
|
-
expect((result.metadata as { changed: boolean }).changed).toBe(false);
|
|
105
|
-
expect(result.output).toContain('Index:');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('returns EDIT_CONFLICT when oldText does not match', async () => {
|
|
109
|
-
const targetPath = path.join(rootDir, 'conflict.txt');
|
|
110
|
-
await fs.writeFile(targetPath, 'const answer = 42;\n', 'utf8');
|
|
111
|
-
|
|
112
|
-
const result = await editTool.execute({
|
|
113
|
-
path: targetPath,
|
|
114
|
-
edits: [{ oldText: 'const answer = 100;', newText: 'const answer = 7;' }],
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
expect(result.success).toBe(false);
|
|
118
|
-
expect(result.output).toContain('EDIT_CONFLICT');
|
|
119
|
-
|
|
120
|
-
const metadata = result.metadata as {
|
|
121
|
-
error: string;
|
|
122
|
-
code: string;
|
|
123
|
-
recoverable: boolean;
|
|
124
|
-
next_actions: string[];
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
expect(metadata.error).toBe('EDIT_CONFLICT');
|
|
128
|
-
expect(metadata.code).toBe('EDIT_CONFLICT');
|
|
129
|
-
expect(metadata.recoverable).toBe(true);
|
|
130
|
-
expect(metadata.next_actions).toEqual(['file_read', 'file_edit']);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('rejects path outside allowed directories', async () => {
|
|
134
|
-
const outsidePath = path.join(outsideDir, 'outside.txt');
|
|
135
|
-
await fs.writeFile(outsidePath, 'x', 'utf8');
|
|
136
|
-
|
|
137
|
-
const result = await editTool.execute({
|
|
138
|
-
path: outsidePath,
|
|
139
|
-
edits: [{ oldText: 'x', newText: 'y' }],
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
expect(result.success).toBe(false);
|
|
143
|
-
expect(result.output).toContain('FILE_EDIT_FAILED');
|
|
144
|
-
expect(result.output).toContain('PATH_NOT_ALLOWED');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('always requires confirmation', () => {
|
|
148
|
-
expect(editTool.shouldConfirm()).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('returns failure when target path is not a file', async () => {
|
|
152
|
-
const result = await editTool.execute({
|
|
153
|
-
path: rootDir,
|
|
154
|
-
edits: [{ oldText: 'a', newText: 'b' }],
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(result.success).toBe(false);
|
|
158
|
-
expect(result.output).toContain('FILE_EDIT_FAILED');
|
|
159
|
-
expect(result.output).toContain('FILE_EDIT_NOT_FILE');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('supports whitespace-tolerant edit matching', async () => {
|
|
163
|
-
const targetPath = path.join(rootDir, 'whitespace.txt');
|
|
164
|
-
await fs.writeFile(targetPath, ' const answer = 42;\n', 'utf8');
|
|
165
|
-
|
|
166
|
-
const result = await editTool.execute({
|
|
167
|
-
path: targetPath,
|
|
168
|
-
edits: [{ oldText: 'const answer = 42;', newText: 'const answer = 7;' }],
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
expect(result.success).toBe(true);
|
|
172
|
-
|
|
173
|
-
const readBack = await readTool.execute({ path: targetPath });
|
|
174
|
-
expect(readBack.success).toBe(true);
|
|
175
|
-
expect(readBack.output).toContain('const answer = 7;');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('rebuilds relative indentation for multi-line tolerant matches', async () => {
|
|
179
|
-
const targetPath = path.join(rootDir, 'indent-relative.ts');
|
|
180
|
-
await fs.writeFile(
|
|
181
|
-
targetPath,
|
|
182
|
-
[' if (flag) {', ' console.log("old");', ' }', ''].join('\n'),
|
|
183
|
-
'utf8'
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
const result = await editTool.execute({
|
|
187
|
-
path: targetPath,
|
|
188
|
-
edits: [
|
|
189
|
-
{
|
|
190
|
-
oldText: ['if (flag) {', ' console.log("old");', '}'].join('\n'),
|
|
191
|
-
newText: ['if (flag) {', ' console.log("new");', '}'].join('\n'),
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
expect(result.success).toBe(true);
|
|
197
|
-
const readBack = await readTool.execute({ path: targetPath });
|
|
198
|
-
expect(readBack.success).toBe(true);
|
|
199
|
-
expect(readBack.output).toContain(' if (flag) {');
|
|
200
|
-
expect(readBack.output).toContain(' console.log("new");');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('keeps replacement line as-is when indent mapping cannot be derived', async () => {
|
|
204
|
-
const targetPath = path.join(rootDir, 'indent-fallback.ts');
|
|
205
|
-
await fs.writeFile(targetPath, [' if (ready) {', ' work();', ' }', ''].join('\n'), 'utf8');
|
|
206
|
-
|
|
207
|
-
const result = await editTool.execute({
|
|
208
|
-
path: targetPath,
|
|
209
|
-
edits: [
|
|
210
|
-
{
|
|
211
|
-
oldText: ['if (ready) {', ' work();', '}'].join('\n'),
|
|
212
|
-
newText: ['if (ready) {', 'workNow();', '}'].join('\n'),
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
expect(result.success).toBe(true);
|
|
218
|
-
const readBack = await readTool.execute({ path: targetPath });
|
|
219
|
-
expect(readBack.success).toBe(true);
|
|
220
|
-
expect(readBack.output).toContain('workNow();');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('maps pre-prefixed conflict messages to EDIT_CONFLICT envelope', () => {
|
|
224
|
-
const helper = editTool as unknown as {
|
|
225
|
-
mapFailure: (
|
|
226
|
-
requestPath: string,
|
|
227
|
-
error: unknown
|
|
228
|
-
) => {
|
|
229
|
-
success: boolean;
|
|
230
|
-
output?: string;
|
|
231
|
-
metadata?: Record<string, unknown>;
|
|
232
|
-
};
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const result = helper.mapFailure('demo.ts', new Error('EDIT_CONFLICT: anchored'));
|
|
236
|
-
expect(result.success).toBe(false);
|
|
237
|
-
expect(result.output).toContain('EDIT_CONFLICT');
|
|
238
|
-
expect(result.metadata?.error).toBe('EDIT_CONFLICT');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('maps non-error failures to FILE_EDIT_FAILED envelope', () => {
|
|
242
|
-
const helper = editTool as unknown as {
|
|
243
|
-
mapFailure: (
|
|
244
|
-
requestPath: string,
|
|
245
|
-
error: unknown
|
|
246
|
-
) => {
|
|
247
|
-
success: boolean;
|
|
248
|
-
output?: string;
|
|
249
|
-
metadata?: Record<string, unknown>;
|
|
250
|
-
};
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const result = helper.mapFailure('demo.ts', 1234);
|
|
254
|
-
expect(result.success).toBe(false);
|
|
255
|
-
expect(result.output).toBe('FILE_EDIT_FAILED: 1234');
|
|
256
|
-
expect(result.metadata?.error).toBe('FILE_EDIT_FAILED');
|
|
257
|
-
});
|
|
258
|
-
});
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
|
-
import * as syncFs from 'node:fs';
|
|
4
|
-
import * as os from 'node:os';
|
|
5
|
-
import * as path from 'node:path';
|
|
6
|
-
import { FileHistoryStore } from '../../storage/file-history-store';
|
|
7
|
-
import type { FileStorageConfig } from '../../storage/file-storage-config';
|
|
8
|
-
import { FileHistoryListTool } from '../file-history-list';
|
|
9
|
-
import { FileHistoryRestoreTool } from '../file-history-restore';
|
|
10
|
-
|
|
11
|
-
function createHistoryStore(rootDir: string): FileHistoryStore {
|
|
12
|
-
const config: FileStorageConfig = {
|
|
13
|
-
rootDir,
|
|
14
|
-
writeBufferDir: path.join(rootDir, 'cache', 'write-buffer'),
|
|
15
|
-
historyDir: path.join(rootDir, 'history'),
|
|
16
|
-
historyEnabled: true,
|
|
17
|
-
historyMaxPerFile: 20,
|
|
18
|
-
historyMaxAgeDays: 14,
|
|
19
|
-
historyMaxTotalBytes: 1024 * 1024,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return new FileHistoryStore({ config });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function parseOutput<T>(output: string | undefined): T {
|
|
26
|
-
return JSON.parse(output || '{}') as T;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
describe('file history tools', () => {
|
|
30
|
-
let rootDir: string;
|
|
31
|
-
let listTool: FileHistoryListTool;
|
|
32
|
-
let restoreTool: FileHistoryRestoreTool;
|
|
33
|
-
let historyStore: FileHistoryStore;
|
|
34
|
-
|
|
35
|
-
beforeEach(async () => {
|
|
36
|
-
rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-file-history-tool-'));
|
|
37
|
-
historyStore = createHistoryStore(rootDir);
|
|
38
|
-
listTool = new FileHistoryListTool({
|
|
39
|
-
allowedDirectories: [rootDir],
|
|
40
|
-
historyStore,
|
|
41
|
-
});
|
|
42
|
-
restoreTool = new FileHistoryRestoreTool({
|
|
43
|
-
allowedDirectories: [rootDir],
|
|
44
|
-
historyStore,
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
afterEach(async () => {
|
|
49
|
-
await fs.rm(rootDir, { recursive: true, force: true });
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('lists saved versions for a file', async () => {
|
|
53
|
-
const targetPath = path.join(rootDir, 'demo.ts');
|
|
54
|
-
await fs.writeFile(targetPath, 'const value = 1;\n', 'utf8');
|
|
55
|
-
await historyStore.snapshotBeforeWrite({
|
|
56
|
-
targetPath,
|
|
57
|
-
nextContent: 'const value = 2;\n',
|
|
58
|
-
source: 'write_file',
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const result = await listTool.execute({ path: targetPath });
|
|
62
|
-
const payload = parseOutput<{ path: string; versions: Array<{ versionId: string }> }>(
|
|
63
|
-
result.output
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
expect(result.success).toBe(true);
|
|
67
|
-
expect(payload.path).toBe(syncFs.realpathSync(targetPath));
|
|
68
|
-
expect(payload.versions).toHaveLength(1);
|
|
69
|
-
expect(payload.versions[0].versionId).toBeTruthy();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('restores the latest saved version when versionId is omitted', async () => {
|
|
73
|
-
const targetPath = path.join(rootDir, 'restore.ts');
|
|
74
|
-
await fs.writeFile(targetPath, 'const value = 1;\n', 'utf8');
|
|
75
|
-
await historyStore.snapshotBeforeWrite({
|
|
76
|
-
targetPath,
|
|
77
|
-
nextContent: 'const value = 2;\n',
|
|
78
|
-
source: 'write_file',
|
|
79
|
-
});
|
|
80
|
-
await fs.writeFile(targetPath, 'const value = 2;\n', 'utf8');
|
|
81
|
-
|
|
82
|
-
const result = await restoreTool.execute(
|
|
83
|
-
{ path: targetPath },
|
|
84
|
-
{
|
|
85
|
-
toolCallId: 'restore-history',
|
|
86
|
-
loopIndex: 1,
|
|
87
|
-
agent: {},
|
|
88
|
-
confirmationApproved: true,
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
const payload = parseOutput<{ restored: boolean; version: { versionId: string } }>(
|
|
92
|
-
result.output
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(result.success).toBe(true);
|
|
96
|
-
expect(payload.restored).toBe(true);
|
|
97
|
-
expect(payload.version.versionId).toBeTruthy();
|
|
98
|
-
expect(await fs.readFile(targetPath, 'utf8')).toBe('const value = 1;\n');
|
|
99
|
-
|
|
100
|
-
const versions = await historyStore.listVersions(targetPath);
|
|
101
|
-
expect(versions).toHaveLength(2);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('returns a typed error when no history exists for restore', async () => {
|
|
105
|
-
const targetPath = path.join(rootDir, 'missing.ts');
|
|
106
|
-
await fs.writeFile(targetPath, 'const value = 1;\n', 'utf8');
|
|
107
|
-
|
|
108
|
-
const result = await restoreTool.execute(
|
|
109
|
-
{ path: targetPath },
|
|
110
|
-
{
|
|
111
|
-
toolCallId: 'restore-history-empty',
|
|
112
|
-
loopIndex: 1,
|
|
113
|
-
agent: {},
|
|
114
|
-
confirmationApproved: true,
|
|
115
|
-
}
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
expect(result.success).toBe(false);
|
|
119
|
-
expect(result.output).toContain('FILE_HISTORY_EMPTY');
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { promises as fs } from 'node:fs';
|
|
3
|
-
import * as os from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { FileReadTool } from '../file-read-tool';
|
|
6
|
-
|
|
7
|
-
describe('FileReadTool', () => {
|
|
8
|
-
let rootDir: string;
|
|
9
|
-
let outsideDir: string;
|
|
10
|
-
let tool: FileReadTool;
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-file-read-tool-'));
|
|
14
|
-
outsideDir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-file-read-outside-'));
|
|
15
|
-
tool = new FileReadTool({
|
|
16
|
-
allowedDirectories: [rootDir],
|
|
17
|
-
maxOutputLength: 300,
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(async () => {
|
|
22
|
-
await fs.rm(rootDir, { recursive: true, force: true });
|
|
23
|
-
await fs.rm(outsideDir, { recursive: true, force: true });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('reads full content and returns metadata', async () => {
|
|
27
|
-
const targetPath = path.join(rootDir, 'note.txt');
|
|
28
|
-
await fs.writeFile(targetPath, 'hello\nworld\n', 'utf8');
|
|
29
|
-
|
|
30
|
-
const result = await tool.execute({ path: targetPath });
|
|
31
|
-
|
|
32
|
-
expect(result.success).toBe(true);
|
|
33
|
-
expect(result.output).toBe('hello\nworld');
|
|
34
|
-
|
|
35
|
-
const metadata = result.metadata as {
|
|
36
|
-
path: string;
|
|
37
|
-
etag: string;
|
|
38
|
-
truncated: boolean;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
expect(metadata.path).toBe(await fs.realpath(targetPath));
|
|
42
|
-
expect(metadata.etag).toBeDefined();
|
|
43
|
-
expect(metadata.truncated).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('reads content by line range', async () => {
|
|
47
|
-
const targetPath = path.join(rootDir, 'range.txt');
|
|
48
|
-
await fs.writeFile(targetPath, 'line-1\nline-2\nline-3\nline-4\n', 'utf8');
|
|
49
|
-
|
|
50
|
-
const result = await tool.execute({
|
|
51
|
-
path: targetPath,
|
|
52
|
-
startLine: 2,
|
|
53
|
-
limit: 3,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
expect(result.success).toBe(true);
|
|
57
|
-
expect(result.output).toBe('line-3\nline-4');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('supports open-ended line slicing', async () => {
|
|
61
|
-
const targetPath = path.join(rootDir, 'open-ended.txt');
|
|
62
|
-
await fs.writeFile(targetPath, 'line-1\nline-2\nline-3\nline-4\n', 'utf8');
|
|
63
|
-
|
|
64
|
-
const toEnd = await tool.execute({
|
|
65
|
-
path: targetPath,
|
|
66
|
-
startLine: 2,
|
|
67
|
-
});
|
|
68
|
-
expect(toEnd.success).toBe(true);
|
|
69
|
-
expect(toEnd.output).toBe('line-3\nline-4');
|
|
70
|
-
|
|
71
|
-
const fromStart = await tool.execute({
|
|
72
|
-
path: targetPath,
|
|
73
|
-
limit: 2,
|
|
74
|
-
});
|
|
75
|
-
expect(fromStart.success).toBe(true);
|
|
76
|
-
expect(fromStart.output).toBe('line-1\nline-2');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('rejects invalid limit', async () => {
|
|
80
|
-
const targetPath = path.join(rootDir, 'x.txt');
|
|
81
|
-
await fs.writeFile(targetPath, 'content', 'utf8');
|
|
82
|
-
|
|
83
|
-
const result = await tool.execute({
|
|
84
|
-
path: targetPath,
|
|
85
|
-
limit: 0,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
expect(result.success).toBe(false);
|
|
89
|
-
expect(result.output).toContain('FILE_READ_INVALID_LIMIT');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('truncates oversized content', async () => {
|
|
93
|
-
const targetPath = path.join(rootDir, 'large.txt');
|
|
94
|
-
await fs.writeFile(targetPath, 'x'.repeat(5000), 'utf8');
|
|
95
|
-
|
|
96
|
-
const result = await tool.execute({ path: targetPath });
|
|
97
|
-
|
|
98
|
-
expect(result.success).toBe(true);
|
|
99
|
-
expect((result.output || '').length).toBeLessThanOrEqual(300);
|
|
100
|
-
expect(result.output || '').toContain('[... Output Truncated ...]');
|
|
101
|
-
|
|
102
|
-
const metadata = result.metadata as {
|
|
103
|
-
truncated: boolean;
|
|
104
|
-
originalLength?: number;
|
|
105
|
-
};
|
|
106
|
-
expect(metadata.truncated).toBe(true);
|
|
107
|
-
expect(metadata.originalLength).toBe(5000);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('keeps a contiguous prefix when truncating content', async () => {
|
|
111
|
-
const targetPath = path.join(rootDir, 'prefix-only.txt');
|
|
112
|
-
const content =
|
|
113
|
-
Array.from({ length: 180 }, (_, index) => `line-${index + 1}`).join('\n') +
|
|
114
|
-
'\nTAIL_SENTINEL_END';
|
|
115
|
-
await fs.writeFile(targetPath, content, 'utf8');
|
|
116
|
-
|
|
117
|
-
const result = await tool.execute({ path: targetPath });
|
|
118
|
-
expect(result.success).toBe(true);
|
|
119
|
-
expect(result.output || '').toContain('[... Output Truncated ...]');
|
|
120
|
-
expect(result.output || '').toContain('line-1');
|
|
121
|
-
expect(result.output || '').not.toContain('TAIL_SENTINEL_END');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('rejects path outside allowed directories', async () => {
|
|
125
|
-
const outsidePath = path.join(outsideDir, 'outside.txt');
|
|
126
|
-
await fs.writeFile(outsidePath, 'blocked', 'utf8');
|
|
127
|
-
|
|
128
|
-
const result = await tool.execute({ path: outsidePath });
|
|
129
|
-
|
|
130
|
-
expect(result.success).toBe(false);
|
|
131
|
-
expect(result.output).toContain('PATH_NOT_ALLOWED');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('exposes parallel-safe concurrency policy and lock key', () => {
|
|
135
|
-
expect(tool.getConcurrencyMode()).toBe('parallel-safe');
|
|
136
|
-
expect(
|
|
137
|
-
tool.getConcurrencyLockKey({
|
|
138
|
-
path: 'a.txt',
|
|
139
|
-
})
|
|
140
|
-
).toBe('file_read:a.txt');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('returns not-found error code for missing files', async () => {
|
|
144
|
-
const result = await tool.execute({
|
|
145
|
-
path: path.join(rootDir, 'missing.txt'),
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
expect(result.success).toBe(false);
|
|
149
|
-
expect(result.output).toContain('FILE_READ_NOT_FOUND');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('returns failure for directory path', async () => {
|
|
153
|
-
const result = await tool.execute({
|
|
154
|
-
path: rootDir,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(result.success).toBe(false);
|
|
158
|
-
expect(result.output).toContain('FILE_READ_FAILED');
|
|
159
|
-
expect(result.output).toContain('FILE_READ_NOT_FILE');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('returns empty output when requested range is outside file bounds', async () => {
|
|
163
|
-
const targetPath = path.join(rootDir, 'bounds.txt');
|
|
164
|
-
await fs.writeFile(targetPath, 'one\ntwo\n', 'utf8');
|
|
165
|
-
|
|
166
|
-
const result = await tool.execute({
|
|
167
|
-
path: targetPath,
|
|
168
|
-
startLine: 99,
|
|
169
|
-
limit: 10,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
expect(result.success).toBe(true);
|
|
173
|
-
expect(result.output).toBe('');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('uses hard truncation branch when output budget is very small', async () => {
|
|
177
|
-
const tinyTool = new FileReadTool({
|
|
178
|
-
allowedDirectories: [rootDir],
|
|
179
|
-
maxOutputLength: 16,
|
|
180
|
-
});
|
|
181
|
-
const targetPath = path.join(rootDir, 'tiny-budget.txt');
|
|
182
|
-
await fs.writeFile(targetPath, 'a'.repeat(120), 'utf8');
|
|
183
|
-
|
|
184
|
-
const result = await tinyTool.execute({ path: targetPath });
|
|
185
|
-
expect(result.success).toBe(true);
|
|
186
|
-
expect((result.output || '').length).toBeLessThanOrEqual(16);
|
|
187
|
-
expect(result.output || '').not.toContain('[... Output Truncated ...]');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('maps permission and generic errors in toFailureMessage helper', () => {
|
|
191
|
-
const helper = tool as unknown as {
|
|
192
|
-
toFailureMessage: (requestedPath: string, error: unknown) => string;
|
|
193
|
-
};
|
|
194
|
-
const permissionMessage = helper.toFailureMessage('a.txt', {
|
|
195
|
-
code: 'EACCES',
|
|
196
|
-
});
|
|
197
|
-
const genericMessage = helper.toFailureMessage('a.txt', new Error('boom'));
|
|
198
|
-
|
|
199
|
-
expect(permissionMessage).toBe('FILE_READ_NO_PERMISSION: a.txt');
|
|
200
|
-
expect(genericMessage).toBe('FILE_READ_FAILED: boom');
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('maps non-error values in toFailureMessage helper', () => {
|
|
204
|
-
const helper = tool as unknown as {
|
|
205
|
-
toFailureMessage: (requestedPath: string, error: unknown) => string;
|
|
206
|
-
};
|
|
207
|
-
const genericMessage = helper.toFailureMessage('a.txt', 12345);
|
|
208
|
-
expect(genericMessage).toBe('FILE_READ_FAILED: 12345');
|
|
209
|
-
});
|
|
210
|
-
});
|