@renxqoo/renx-code 0.0.4 → 0.0.5
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/bin/renx.cjs +16 -0
- package/package.json +2 -45
- package/src/agent/runtime/runtime.context-usage.test.ts +4 -5
- package/src/agent/runtime/runtime.error-handling.test.ts +4 -5
- package/src/agent/runtime/runtime.test.ts +7 -4
- package/src/agent/runtime/runtime.ts +3 -9
- package/src/agent/runtime/runtime.usage-forwarding.test.ts +4 -5
- package/src/agent/runtime/source-modules.test.ts +16 -35
- package/src/agent/runtime/source-modules.ts +17 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_ACCEPTANCE_CHECKLIST.md +95 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.html +1345 -0
- package/vendor/agent-root/src/agent/ENTERPRISE_REALTIME.md +1353 -0
- package/vendor/agent-root/src/agent/ERROR_CONTRACT.md +60 -0
- package/vendor/agent-root/src/agent/TEST_COVERAGE_ANALYSIS.md +278 -0
- package/vendor/agent-root/src/agent/__test__/error-contract.test.ts +72 -0
- package/vendor/agent-root/src/agent/__test__/types.test.ts +137 -0
- package/vendor/agent-root/src/agent/agent/__test__/abort-runtime.test.ts +83 -0
- package/vendor/agent-root/src/agent/agent/__test__/callback-safety.test.ts +34 -0
- package/vendor/agent-root/src/agent/agent/__test__/compaction.test.ts +323 -0
- package/vendor/agent-root/src/agent/agent/__test__/concurrency.test.ts +290 -0
- package/vendor/agent-root/src/agent/agent/__test__/error-normalizer.test.ts +377 -0
- package/vendor/agent-root/src/agent/agent/__test__/error.test.ts +212 -0
- package/vendor/agent-root/src/agent/agent/__test__/fault-injection.test.ts +295 -0
- package/vendor/agent-root/src/agent/agent/__test__/index.test.ts +3607 -0
- package/vendor/agent-root/src/agent/agent/__test__/logger.test.ts +35 -0
- package/vendor/agent-root/src/agent/agent/__test__/message-utils.test.ts +517 -0
- package/vendor/agent-root/src/agent/agent/__test__/telemetry.test.ts +97 -0
- package/vendor/agent-root/src/agent/agent/__test__/timeout-budget.test.ts +479 -0
- package/vendor/agent-root/src/agent/agent/__test__/tool-call-merge.test.ts +80 -0
- package/vendor/agent-root/src/agent/agent/__test__/tool-execution-ledger.test.ts +76 -0
- package/vendor/agent-root/src/agent/agent/__test__/write-buffer.test.ts +173 -0
- package/vendor/agent-root/src/agent/agent/__test__/write-file-session.test.ts +109 -0
- package/vendor/agent-root/src/agent/agent/abort-runtime.ts +71 -0
- package/vendor/agent-root/src/agent/agent/callback-safety.ts +33 -0
- package/vendor/agent-root/src/agent/agent/compaction.ts +291 -0
- package/vendor/agent-root/src/agent/agent/concurrency.ts +103 -0
- package/vendor/agent-root/src/agent/agent/error-normalizer.ts +190 -0
- package/vendor/agent-root/src/agent/agent/error.ts +198 -0
- package/vendor/agent-root/src/agent/agent/index.ts +1772 -0
- package/vendor/agent-root/src/agent/agent/logger.ts +65 -0
- package/vendor/agent-root/src/agent/agent/message-utils.ts +101 -0
- package/vendor/agent-root/src/agent/agent/stream-events.ts +61 -0
- package/vendor/agent-root/src/agent/agent/telemetry.ts +123 -0
- package/vendor/agent-root/src/agent/agent/timeout-budget.ts +227 -0
- package/vendor/agent-root/src/agent/agent/tool-call-merge.ts +111 -0
- package/vendor/agent-root/src/agent/agent/tool-execution-ledger.ts +164 -0
- package/vendor/agent-root/src/agent/agent/write-buffer.ts +188 -0
- package/vendor/agent-root/src/agent/agent/write-file-session.ts +238 -0
- package/vendor/agent-root/src/agent/app/__test__/agent-app-service.test.ts +1053 -0
- package/vendor/agent-root/src/agent/app/__test__/minimal-agent-application.test.ts +158 -0
- package/vendor/agent-root/src/agent/app/__test__/sqlite-agent-app-store.test.ts +437 -0
- package/vendor/agent-root/src/agent/app/agent-app-service.ts +748 -0
- package/vendor/agent-root/src/agent/app/contracts.ts +109 -0
- package/vendor/agent-root/src/agent/app/index.ts +5 -0
- package/vendor/agent-root/src/agent/app/minimal-agent-application.ts +151 -0
- package/vendor/agent-root/src/agent/app/ports.ts +72 -0
- package/vendor/agent-root/src/agent/app/sqlite-agent-app-store.ts +1182 -0
- package/vendor/agent-root/src/agent/app/sqlite-client.ts +177 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/00-README.md +36 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/01-scope-and-goals.md +33 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/02-architecture-overview.md +40 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/03-domain-model-and-contracts.md +91 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/04-ports-and-interfaces.md +116 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/05-run-orchestration-and-state-machine.md +52 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/06-cli-commands-and-ux.md +53 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/07-storage-design-local.md +52 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/08-error-and-observability.md +40 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/09-security-and-policy-boundary.md +19 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/10-test-plan-and-acceptance.md +28 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/11-implementation-phases.md +26 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/12-open-questions-and-risks.md +30 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/13-sqlite-schema-fields-and-rationale.md +567 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/14-project-flow-mermaid.md +583 -0
- package/vendor/agent-root/src/agent/docs/cli-app-layer/15-openclaw-style-project-blueprint.md +972 -0
- package/vendor/agent-root/src/agent/error-contract.ts +154 -0
- package/vendor/agent-root/src/agent/prompts/system.ts +246 -0
- package/vendor/agent-root/src/agent/prompts/system1.ts +208 -0
- package/vendor/agent-root/src/agent/storage/__test__/file-history-store.test.ts +98 -0
- package/vendor/agent-root/src/agent/storage/file-history-store.ts +313 -0
- package/vendor/agent-root/src/agent/storage/file-storage-config.ts +94 -0
- package/vendor/agent-root/src/agent/storage/file-system.ts +31 -0
- package/vendor/agent-root/src/agent/storage/file-write-service.ts +21 -0
- package/vendor/agent-root/src/agent/tool/__test__/base-tool.test.ts +413 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash-policy.test.ts +356 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash.mocked-coverage.test.ts +375 -0
- package/vendor/agent-root/src/agent/tool/__test__/bash.test.ts +372 -0
- package/vendor/agent-root/src/agent/tool/__test__/error.test.ts +108 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-edit-tool.test.ts +258 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-history-tools.test.ts +121 -0
- package/vendor/agent-root/src/agent/tool/__test__/file-read-tool.test.ts +210 -0
- package/vendor/agent-root/src/agent/tool/__test__/glob.test.ts +139 -0
- package/vendor/agent-root/src/agent/tool/__test__/grep.mocked-coverage.test.ts +456 -0
- package/vendor/agent-root/src/agent/tool/__test__/grep.test.ts +192 -0
- package/vendor/agent-root/src/agent/tool/__test__/lsp.test.ts +300 -0
- package/vendor/agent-root/src/agent/tool/__test__/outside-workspace-confirmation.test.ts +214 -0
- package/vendor/agent-root/src/agent/tool/__test__/path-security.test.ts +336 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-loader.test.ts +494 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-parser.test.ts +543 -0
- package/vendor/agent-root/src/agent/tool/__test__/skill-tool.test.ts +172 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-concurrency-and-version.test.ts +116 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-create-get-list-update.test.ts +267 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-create.test.ts +519 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-errors.test.ts +225 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-output-blocking.test.ts +223 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-output.test.ts +184 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-parent-abort.test.ts +287 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-real-runner-adapter.test.ts +190 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-run-lifecycle.test.ts +352 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-store-runner-branches.test.ts +395 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-store.test.ts +391 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config-integration.test.ts +176 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-subagent-config.test.ts +68 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-core-edges.test.ts +630 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-tools-runtime-edges.test.ts +732 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-types.test.ts +494 -0
- package/vendor/agent-root/src/agent/tool/__test__/task-utils-branches.test.ts +175 -0
- package/vendor/agent-root/src/agent/tool/__test__/tool-manager.test.ts +505 -0
- package/vendor/agent-root/src/agent/tool/__test__/types.test.ts +55 -0
- package/vendor/agent-root/src/agent/tool/__test__/web-fetch.test.ts +244 -0
- package/vendor/agent-root/src/agent/tool/__test__/web-search.test.ts +290 -0
- package/vendor/agent-root/src/agent/tool/__test__/write-file.test.ts +368 -0
- package/vendor/agent-root/src/agent/tool/base-tool.ts +345 -0
- package/vendor/agent-root/src/agent/tool/bash-policy.ts +636 -0
- package/vendor/agent-root/src/agent/tool/bash.ts +688 -0
- package/vendor/agent-root/src/agent/tool/error.ts +131 -0
- package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +264 -0
- package/vendor/agent-root/src/agent/tool/file-history-list.ts +103 -0
- package/vendor/agent-root/src/agent/tool/file-history-restore.ts +149 -0
- package/vendor/agent-root/src/agent/tool/file-read-tool.ts +211 -0
- package/vendor/agent-root/src/agent/tool/glob.ts +171 -0
- package/vendor/agent-root/src/agent/tool/grep.ts +496 -0
- package/vendor/agent-root/src/agent/tool/lsp.ts +481 -0
- package/vendor/agent-root/src/agent/tool/path-security.ts +117 -0
- package/vendor/agent-root/src/agent/tool/search/common.ts +153 -0
- package/vendor/agent-root/src/agent/tool/skill/index.ts +13 -0
- package/vendor/agent-root/src/agent/tool/skill/loader.ts +229 -0
- package/vendor/agent-root/src/agent/tool/skill/parser.ts +124 -0
- package/vendor/agent-root/src/agent/tool/skill/types.ts +27 -0
- package/vendor/agent-root/src/agent/tool/skill-tool.ts +143 -0
- package/vendor/agent-root/src/agent/tool/task-create.ts +186 -0
- package/vendor/agent-root/src/agent/tool/task-errors.ts +42 -0
- package/vendor/agent-root/src/agent/tool/task-get.ts +116 -0
- package/vendor/agent-root/src/agent/tool/task-graph.ts +78 -0
- package/vendor/agent-root/src/agent/tool/task-list.ts +141 -0
- package/vendor/agent-root/src/agent/tool/task-mock-runner-adapter.ts +232 -0
- package/vendor/agent-root/src/agent/tool/task-output.ts +223 -0
- package/vendor/agent-root/src/agent/tool/task-parent-abort.ts +115 -0
- package/vendor/agent-root/src/agent/tool/task-real-runner-adapter.ts +336 -0
- package/vendor/agent-root/src/agent/tool/task-runner-adapter.ts +55 -0
- package/vendor/agent-root/src/agent/tool/task-stop.ts +187 -0
- package/vendor/agent-root/src/agent/tool/task-store.ts +217 -0
- package/vendor/agent-root/src/agent/tool/task-subagent-config.ts +149 -0
- package/vendor/agent-root/src/agent/tool/task-types.ts +264 -0
- package/vendor/agent-root/src/agent/tool/task-update.ts +315 -0
- package/vendor/agent-root/src/agent/tool/task.ts +209 -0
- package/vendor/agent-root/src/agent/tool/tool-manager.ts +362 -0
- package/vendor/agent-root/src/agent/tool/tool-prompts.ts +242 -0
- package/vendor/agent-root/src/agent/tool/types.ts +116 -0
- package/vendor/agent-root/src/agent/tool/web-fetch.ts +227 -0
- package/vendor/agent-root/src/agent/tool/web-search.ts +208 -0
- package/vendor/agent-root/src/agent/tool/write-file.ts +497 -0
- package/vendor/agent-root/src/agent/types.ts +232 -0
- package/vendor/agent-root/src/agent/utils/__tests__/index.test.ts +18 -0
- package/vendor/agent-root/src/agent/utils/__tests__/message-utils.test.ts +610 -0
- package/vendor/agent-root/src/agent/utils/__tests__/message.test.ts +223 -0
- package/vendor/agent-root/src/agent/utils/__tests__/token.test.ts +42 -0
- package/vendor/agent-root/src/agent/utils/index.ts +16 -0
- package/vendor/agent-root/src/agent/utils/message.ts +171 -0
- package/vendor/agent-root/src/agent/utils/token.ts +28 -0
- package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +129 -0
- package/vendor/agent-root/src/config/__tests__/loader.test.ts +247 -0
- package/vendor/agent-root/src/config/__tests__/runtime.test.ts +88 -0
- package/vendor/agent-root/src/config/index.ts +54 -0
- package/vendor/agent-root/src/config/loader.ts +431 -0
- package/vendor/agent-root/src/config/paths.ts +30 -0
- package/vendor/agent-root/src/config/runtime.ts +163 -0
- package/vendor/agent-root/src/config/types.ts +70 -0
- package/vendor/agent-root/src/logger/index.ts +57 -0
- package/vendor/agent-root/src/logger/logger.ts +819 -0
- package/vendor/agent-root/src/logger/types.ts +150 -0
- package/vendor/agent-root/src/providers/__tests__/errors.test.ts +441 -0
- package/vendor/agent-root/src/providers/__tests__/index.test.ts +16 -0
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.options.test.ts +318 -0
- package/vendor/agent-root/src/providers/__tests__/openai-compatible.test.ts +600 -0
- package/vendor/agent-root/src/providers/__tests__/registry.test.ts +449 -0
- package/vendor/agent-root/src/providers/__tests__/responses-adapter.test.ts +298 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/anthropic.test.ts +354 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/kimi.test.ts +58 -0
- package/vendor/agent-root/src/providers/adapters/__tests__/standard.test.ts +261 -0
- package/vendor/agent-root/src/providers/adapters/anthropic.ts +572 -0
- package/vendor/agent-root/src/providers/adapters/base.ts +131 -0
- package/vendor/agent-root/src/providers/adapters/kimi.ts +48 -0
- package/vendor/agent-root/src/providers/adapters/responses.ts +732 -0
- package/vendor/agent-root/src/providers/adapters/standard.ts +120 -0
- package/vendor/agent-root/src/providers/http/__tests__/client.timeout.test.ts +313 -0
- package/vendor/agent-root/src/providers/http/client.ts +289 -0
- package/vendor/agent-root/src/providers/http/stream-parser.ts +109 -0
- package/vendor/agent-root/src/providers/index.ts +76 -0
- package/vendor/agent-root/src/providers/kimi-headers.ts +177 -0
- package/vendor/agent-root/src/providers/openai-compatible.ts +387 -0
- package/vendor/agent-root/src/providers/registry/model-config.ts +230 -0
- package/vendor/agent-root/src/providers/registry/provider-factory.ts +123 -0
- package/vendor/agent-root/src/providers/registry.ts +135 -0
- package/vendor/agent-root/src/providers/types/api.ts +284 -0
- package/vendor/agent-root/src/providers/types/config.ts +58 -0
- package/vendor/agent-root/src/providers/types/errors.ts +323 -0
- package/vendor/agent-root/src/providers/types/index.ts +72 -0
- package/vendor/agent-root/src/providers/types/provider.ts +45 -0
- package/vendor/agent-root/src/providers/types/registry.ts +88 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ContractError } from '../error-contract';
|
|
2
|
+
|
|
3
|
+
export class ToolExecutionError extends ContractError {
|
|
4
|
+
constructor(message: string, code = 2000) {
|
|
5
|
+
super(message, {
|
|
6
|
+
module: 'tool',
|
|
7
|
+
name: 'ToolExecutionError',
|
|
8
|
+
code,
|
|
9
|
+
errorCode: 'TOOL_EXECUTION_ERROR',
|
|
10
|
+
category: 'internal',
|
|
11
|
+
retryable: true,
|
|
12
|
+
httpStatus: 500,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class EmptyToolNameError extends ToolExecutionError {
|
|
18
|
+
constructor() {
|
|
19
|
+
super('Tool name is empty', 2001);
|
|
20
|
+
this.name = 'EmptyToolNameError';
|
|
21
|
+
this.errorCode = 'TOOL_NAME_EMPTY';
|
|
22
|
+
this.category = 'validation';
|
|
23
|
+
this.retryable = false;
|
|
24
|
+
this.httpStatus = 400;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class InvalidArgumentsError extends ToolExecutionError {
|
|
29
|
+
public toolName: string;
|
|
30
|
+
|
|
31
|
+
constructor(toolName: string, message: string) {
|
|
32
|
+
super(`Invalid arguments format for tool ${toolName}: ${message}`, 2002);
|
|
33
|
+
this.name = 'InvalidArgumentsError';
|
|
34
|
+
this.errorCode = 'TOOL_INVALID_ARGUMENTS';
|
|
35
|
+
this.category = 'validation';
|
|
36
|
+
this.retryable = false;
|
|
37
|
+
this.httpStatus = 400;
|
|
38
|
+
this.toolName = toolName;
|
|
39
|
+
this.details = {
|
|
40
|
+
toolName,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class ToolNotFoundError extends ToolExecutionError {
|
|
46
|
+
public toolName: string;
|
|
47
|
+
|
|
48
|
+
constructor(toolName: string) {
|
|
49
|
+
super(`Tool ${toolName} not found`, 2003);
|
|
50
|
+
this.name = 'ToolNotFoundError';
|
|
51
|
+
this.errorCode = 'TOOL_NOT_FOUND';
|
|
52
|
+
this.category = 'not_found';
|
|
53
|
+
this.retryable = false;
|
|
54
|
+
this.httpStatus = 404;
|
|
55
|
+
this.toolName = toolName;
|
|
56
|
+
this.details = {
|
|
57
|
+
toolName,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class ToolValidationError extends ToolExecutionError {
|
|
63
|
+
public toolName: string;
|
|
64
|
+
public issues: { message: string }[];
|
|
65
|
+
|
|
66
|
+
constructor(toolName: string, issues: { message: string }[]) {
|
|
67
|
+
super(issues.map((issue) => issue.message).join(', '), 2004);
|
|
68
|
+
this.name = 'ToolValidationError';
|
|
69
|
+
this.errorCode = 'TOOL_VALIDATION_FAILED';
|
|
70
|
+
this.category = 'validation';
|
|
71
|
+
this.retryable = false;
|
|
72
|
+
this.httpStatus = 400;
|
|
73
|
+
this.toolName = toolName;
|
|
74
|
+
this.issues = issues;
|
|
75
|
+
this.details = {
|
|
76
|
+
toolName,
|
|
77
|
+
issues,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class ToolDeniedError extends ToolExecutionError {
|
|
83
|
+
public toolName: string;
|
|
84
|
+
public reason?: string;
|
|
85
|
+
|
|
86
|
+
constructor(toolName: string, reason?: string) {
|
|
87
|
+
super(`Tool ${toolName} denied: ${reason || 'User rejected'}`, 2005);
|
|
88
|
+
this.name = 'ToolDeniedError';
|
|
89
|
+
this.errorCode = 'TOOL_DENIED';
|
|
90
|
+
this.category = 'permission';
|
|
91
|
+
this.retryable = false;
|
|
92
|
+
this.httpStatus = 403;
|
|
93
|
+
this.toolName = toolName;
|
|
94
|
+
this.reason = reason;
|
|
95
|
+
this.details = {
|
|
96
|
+
toolName,
|
|
97
|
+
reason,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class ToolPolicyDeniedError extends ToolExecutionError {
|
|
103
|
+
public toolName: string;
|
|
104
|
+
public reasonCode: string;
|
|
105
|
+
public reason?: string;
|
|
106
|
+
public audit?: Record<string, unknown>;
|
|
107
|
+
|
|
108
|
+
constructor(
|
|
109
|
+
toolName: string,
|
|
110
|
+
reasonCode = 'POLICY_DENIED',
|
|
111
|
+
reason?: string,
|
|
112
|
+
audit?: Record<string, unknown>
|
|
113
|
+
) {
|
|
114
|
+
super(`Tool ${toolName} blocked by policy [${reasonCode}]: ${reason || 'Policy denied'}`, 2006);
|
|
115
|
+
this.name = 'ToolPolicyDeniedError';
|
|
116
|
+
this.errorCode = 'TOOL_POLICY_DENIED';
|
|
117
|
+
this.category = 'permission';
|
|
118
|
+
this.retryable = false;
|
|
119
|
+
this.httpStatus = 403;
|
|
120
|
+
this.toolName = toolName;
|
|
121
|
+
this.reasonCode = reasonCode;
|
|
122
|
+
this.reason = reason;
|
|
123
|
+
this.audit = audit;
|
|
124
|
+
this.details = {
|
|
125
|
+
toolName,
|
|
126
|
+
reasonCode,
|
|
127
|
+
reason,
|
|
128
|
+
audit,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createTwoFilesPatch } from 'diff';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { BaseTool, ToolConfirmDetails, ToolResult } from './base-tool';
|
|
6
|
+
import { ToolExecutionError } from './error';
|
|
7
|
+
import {
|
|
8
|
+
assessPathAccess,
|
|
9
|
+
ensurePathWithinAllowed,
|
|
10
|
+
normalizeAllowedDirectories,
|
|
11
|
+
resolveRequestedPath,
|
|
12
|
+
} from './path-security';
|
|
13
|
+
import { FILE_EDIT_TOOL_DESCRIPTION } from './tool-prompts';
|
|
14
|
+
import type { ToolExecutionContext } from './types';
|
|
15
|
+
import { createConfiguredFileHistoryStore, FileHistoryStore } from '../storage/file-history-store';
|
|
16
|
+
import { writeTextFileWithHistory } from '../storage/file-write-service';
|
|
17
|
+
|
|
18
|
+
const fileEditSchema = z.object({
|
|
19
|
+
oldText: z.string().describe('The exact text segment to replace'),
|
|
20
|
+
newText: z.string().describe('The replacement text'),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const schema = z
|
|
24
|
+
.object({
|
|
25
|
+
path: z.string().min(1).describe('Path to the file to modify'),
|
|
26
|
+
edits: z.array(fileEditSchema).min(1).describe('Array of replacements to apply in order'),
|
|
27
|
+
dry_run: z.boolean().optional().describe('If true, only preview edit diff'),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
|
|
31
|
+
interface FileEdit {
|
|
32
|
+
oldText: string;
|
|
33
|
+
newText: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface FileEditToolOptions {
|
|
37
|
+
allowedDirectories?: string[];
|
|
38
|
+
historyStore?: FileHistoryStore;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface FileEditPayload {
|
|
42
|
+
path: string;
|
|
43
|
+
diff: string;
|
|
44
|
+
changed: boolean;
|
|
45
|
+
etag: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeLineEndings(input: string): string {
|
|
49
|
+
return input.replace(/\r\n/g, '\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createUnifiedDiff(originalContent: string, newContent: string, filePath: string): string {
|
|
53
|
+
const normalizedOriginal = normalizeLineEndings(originalContent);
|
|
54
|
+
const normalizedNew = normalizeLineEndings(newContent);
|
|
55
|
+
|
|
56
|
+
return createTwoFilesPatch(
|
|
57
|
+
filePath,
|
|
58
|
+
filePath,
|
|
59
|
+
normalizedOriginal,
|
|
60
|
+
normalizedNew,
|
|
61
|
+
'original',
|
|
62
|
+
'modified'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function applyEditsToContent(content: string, edits: FileEdit[]): string {
|
|
67
|
+
let modifiedContent = normalizeLineEndings(content);
|
|
68
|
+
|
|
69
|
+
for (const edit of edits) {
|
|
70
|
+
const normalizedOld = normalizeLineEndings(edit.oldText);
|
|
71
|
+
const normalizedNew = normalizeLineEndings(edit.newText);
|
|
72
|
+
|
|
73
|
+
if (modifiedContent.includes(normalizedOld)) {
|
|
74
|
+
modifiedContent = modifiedContent.replace(normalizedOld, normalizedNew);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const oldLines = normalizedOld.split('\n');
|
|
79
|
+
const contentLines = modifiedContent.split('\n');
|
|
80
|
+
let matched = false;
|
|
81
|
+
|
|
82
|
+
for (let index = 0; index <= contentLines.length - oldLines.length; index += 1) {
|
|
83
|
+
const window = contentLines.slice(index, index + oldLines.length);
|
|
84
|
+
const isMatch = oldLines.every(
|
|
85
|
+
(oldLine, lineIndex) => oldLine.trim() === window[lineIndex].trim()
|
|
86
|
+
);
|
|
87
|
+
if (!isMatch) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const originalIndent = /^\s*/.exec(contentLines[index])![0];
|
|
92
|
+
const replacementLines = normalizedNew.split('\n').map((line, lineIndex) => {
|
|
93
|
+
if (lineIndex === 0) {
|
|
94
|
+
return originalIndent + line.trimStart();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const oldIndent = oldLines[lineIndex]?.match(/^\s*/)?.[0] || '';
|
|
98
|
+
const newIndent = line.match(/^\s*/)?.[0] || '';
|
|
99
|
+
if (oldIndent && newIndent) {
|
|
100
|
+
const relativeIndent = newIndent.length - oldIndent.length;
|
|
101
|
+
return originalIndent + ' '.repeat(Math.max(0, relativeIndent)) + line.trimStart();
|
|
102
|
+
}
|
|
103
|
+
return line;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
contentLines.splice(index, oldLines.length, ...replacementLines);
|
|
107
|
+
modifiedContent = contentLines.join('\n');
|
|
108
|
+
matched = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!matched) {
|
|
113
|
+
throw new Error(`Could not find exact match for edit:\n${edit.oldText}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return modifiedContent;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class FileEditTool extends BaseTool<typeof schema> {
|
|
121
|
+
name = 'file_edit';
|
|
122
|
+
description = FILE_EDIT_TOOL_DESCRIPTION;
|
|
123
|
+
parameters = schema;
|
|
124
|
+
|
|
125
|
+
private readonly allowedDirectories: string[];
|
|
126
|
+
private readonly historyStore: FileHistoryStore;
|
|
127
|
+
|
|
128
|
+
constructor(options: FileEditToolOptions = {}) {
|
|
129
|
+
super();
|
|
130
|
+
this.allowedDirectories = normalizeAllowedDirectories(options.allowedDirectories);
|
|
131
|
+
this.historyStore = options.historyStore ?? createConfiguredFileHistoryStore();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override shouldConfirm(): boolean {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override getConfirmDetails(args: z.infer<typeof schema>): ToolConfirmDetails | null {
|
|
139
|
+
const absolute = resolveRequestedPath(args.path);
|
|
140
|
+
const assessment = assessPathAccess(absolute, this.allowedDirectories, 'PATH_NOT_ALLOWED');
|
|
141
|
+
if (assessment.allowed) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
reason: assessment.message,
|
|
146
|
+
metadata: {
|
|
147
|
+
requestedPath: absolute,
|
|
148
|
+
allowedDirectories: this.allowedDirectories,
|
|
149
|
+
errorCode: 'PATH_NOT_ALLOWED',
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async execute(args: z.infer<typeof schema>, context?: ToolExecutionContext): Promise<ToolResult> {
|
|
155
|
+
try {
|
|
156
|
+
const absolute = resolveRequestedPath(args.path);
|
|
157
|
+
const validatedPath = ensurePathWithinAllowed(
|
|
158
|
+
absolute,
|
|
159
|
+
this.allowedDirectories,
|
|
160
|
+
'PATH_NOT_ALLOWED',
|
|
161
|
+
context?.confirmationApproved === true
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const stats = await fs.stat(validatedPath);
|
|
165
|
+
if (!stats.isFile()) {
|
|
166
|
+
throw new Error(`FILE_EDIT_NOT_FILE: ${validatedPath}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const originalContent = await fs.readFile(validatedPath, 'utf8');
|
|
170
|
+
const edits: FileEdit[] = args.edits.map((edit) => ({
|
|
171
|
+
oldText: edit.oldText,
|
|
172
|
+
newText: edit.newText,
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
const updatedContent = applyEditsToContent(originalContent, edits);
|
|
176
|
+
const changed = updatedContent !== originalContent;
|
|
177
|
+
const diff = createUnifiedDiff(originalContent, updatedContent, validatedPath);
|
|
178
|
+
|
|
179
|
+
if (!changed || args.dry_run) {
|
|
180
|
+
const previewPayload: FileEditPayload = {
|
|
181
|
+
path: validatedPath,
|
|
182
|
+
diff,
|
|
183
|
+
changed,
|
|
184
|
+
etag: this.createEtag(originalContent),
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
output: diff,
|
|
189
|
+
metadata: previewPayload as unknown as Record<string, unknown>,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await this.writeAtomically(validatedPath, updatedContent);
|
|
194
|
+
const latestContent = await fs.readFile(validatedPath, 'utf8');
|
|
195
|
+
|
|
196
|
+
const payload: FileEditPayload = {
|
|
197
|
+
path: validatedPath,
|
|
198
|
+
diff,
|
|
199
|
+
changed: true,
|
|
200
|
+
etag: this.createEtag(latestContent),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
success: true,
|
|
205
|
+
output: diff,
|
|
206
|
+
metadata: payload as unknown as Record<string, unknown>,
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return this.mapFailure(args.path, error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private async writeAtomically(targetPath: string, content: string): Promise<void> {
|
|
214
|
+
await writeTextFileWithHistory(targetPath, content, {
|
|
215
|
+
source: 'file_edit',
|
|
216
|
+
historyStore: this.historyStore,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private createEtag(content: string): string {
|
|
221
|
+
return createHash('sha256').update(content).digest('hex');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private mapFailure(requestPath: string, error: unknown): ToolResult {
|
|
225
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
message.includes('Could not find exact match for edit') ||
|
|
229
|
+
message.startsWith('EDIT_CONFLICT:')
|
|
230
|
+
) {
|
|
231
|
+
const output = `EDIT_CONFLICT: ${message}`;
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
output,
|
|
235
|
+
error: new ToolExecutionError(output),
|
|
236
|
+
metadata: {
|
|
237
|
+
error: 'EDIT_CONFLICT',
|
|
238
|
+
code: 'EDIT_CONFLICT',
|
|
239
|
+
conflict: true,
|
|
240
|
+
recoverable: true,
|
|
241
|
+
message,
|
|
242
|
+
agent_hint:
|
|
243
|
+
'Edit oldText was not found in latest file content. Read latest content, update oldText anchor, then retry edit.',
|
|
244
|
+
next_actions: ['file_read', 'file_edit'],
|
|
245
|
+
path: requestPath,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const output = `FILE_EDIT_FAILED: ${message}`;
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
output,
|
|
254
|
+
error: new ToolExecutionError(output),
|
|
255
|
+
metadata: {
|
|
256
|
+
error: 'FILE_EDIT_FAILED',
|
|
257
|
+
message,
|
|
258
|
+
path: requestPath,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export default FileEditTool;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseTool, ToolConfirmDetails, ToolResult } from './base-tool';
|
|
3
|
+
import { ToolExecutionError } from './error';
|
|
4
|
+
import {
|
|
5
|
+
assessPathAccess,
|
|
6
|
+
ensurePathWithinAllowed,
|
|
7
|
+
normalizeAllowedDirectories,
|
|
8
|
+
resolveRequestedPath,
|
|
9
|
+
} from './path-security';
|
|
10
|
+
import type { ToolExecutionContext } from './types';
|
|
11
|
+
import {
|
|
12
|
+
createConfiguredFileHistoryStore,
|
|
13
|
+
FileHistoryStore,
|
|
14
|
+
type FileHistoryVersion,
|
|
15
|
+
} from '../storage/file-history-store';
|
|
16
|
+
import { FILE_HISTORY_LIST_TOOL_DESCRIPTION } from './tool-prompts';
|
|
17
|
+
|
|
18
|
+
const schema = z
|
|
19
|
+
.object({
|
|
20
|
+
path: z.string().min(1).describe('Path to the file whose saved history should be listed'),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
export interface FileHistoryListToolOptions {
|
|
25
|
+
allowedDirectories?: string[];
|
|
26
|
+
historyStore?: FileHistoryStore;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface FileHistoryListPayload {
|
|
30
|
+
path: string;
|
|
31
|
+
versions: FileHistoryVersion[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class FileHistoryListTool extends BaseTool<typeof schema> {
|
|
35
|
+
name = 'file_history_list';
|
|
36
|
+
description = FILE_HISTORY_LIST_TOOL_DESCRIPTION;
|
|
37
|
+
parameters = schema;
|
|
38
|
+
|
|
39
|
+
private readonly allowedDirectories: string[];
|
|
40
|
+
private readonly historyStore: FileHistoryStore;
|
|
41
|
+
|
|
42
|
+
constructor(options: FileHistoryListToolOptions = {}) {
|
|
43
|
+
super();
|
|
44
|
+
this.allowedDirectories = normalizeAllowedDirectories(options.allowedDirectories);
|
|
45
|
+
this.historyStore = options.historyStore ?? createConfiguredFileHistoryStore();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override getConcurrencyMode(): 'parallel-safe' {
|
|
49
|
+
return 'parallel-safe';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override getConcurrencyLockKey(args: z.infer<typeof schema>): string {
|
|
53
|
+
return `file_history_list:${args.path}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override getConfirmDetails(args: z.infer<typeof schema>): ToolConfirmDetails | null {
|
|
57
|
+
const absolute = resolveRequestedPath(args.path);
|
|
58
|
+
const assessment = assessPathAccess(absolute, this.allowedDirectories, 'PATH_NOT_ALLOWED');
|
|
59
|
+
if (assessment.allowed) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
reason: assessment.message,
|
|
64
|
+
metadata: {
|
|
65
|
+
requestedPath: absolute,
|
|
66
|
+
allowedDirectories: this.allowedDirectories,
|
|
67
|
+
errorCode: 'PATH_NOT_ALLOWED',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async execute(args: z.infer<typeof schema>, context?: ToolExecutionContext): Promise<ToolResult> {
|
|
73
|
+
try {
|
|
74
|
+
const absolute = resolveRequestedPath(args.path);
|
|
75
|
+
const validatedPath = ensurePathWithinAllowed(
|
|
76
|
+
absolute,
|
|
77
|
+
this.allowedDirectories,
|
|
78
|
+
'PATH_NOT_ALLOWED',
|
|
79
|
+
context?.confirmationApproved === true
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const payload: FileHistoryListPayload = {
|
|
83
|
+
path: validatedPath,
|
|
84
|
+
versions: await this.historyStore.listVersions(validatedPath),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
output: JSON.stringify(payload),
|
|
90
|
+
metadata: payload as unknown as Record<string, unknown>,
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
output: message,
|
|
97
|
+
error: new ToolExecutionError(message),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default FileHistoryListTool;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseTool, ToolConfirmDetails, ToolResult } from './base-tool';
|
|
3
|
+
import { ToolExecutionError } from './error';
|
|
4
|
+
import {
|
|
5
|
+
assessPathAccess,
|
|
6
|
+
ensurePathWithinAllowed,
|
|
7
|
+
normalizeAllowedDirectories,
|
|
8
|
+
resolveRequestedPath,
|
|
9
|
+
} from './path-security';
|
|
10
|
+
import type { ToolExecutionContext } from './types';
|
|
11
|
+
import {
|
|
12
|
+
createConfiguredFileHistoryStore,
|
|
13
|
+
FileHistoryStore,
|
|
14
|
+
type FileHistoryVersion,
|
|
15
|
+
} from '../storage/file-history-store';
|
|
16
|
+
import { FILE_HISTORY_RESTORE_TOOL_DESCRIPTION } from './tool-prompts';
|
|
17
|
+
|
|
18
|
+
const schema = z
|
|
19
|
+
.object({
|
|
20
|
+
path: z.string().min(1).describe('Path to the file that should be restored'),
|
|
21
|
+
versionId: z
|
|
22
|
+
.string()
|
|
23
|
+
.min(1)
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Specific saved version id to restore; defaults to the latest saved version'),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
|
|
29
|
+
export interface FileHistoryRestoreToolOptions {
|
|
30
|
+
allowedDirectories?: string[];
|
|
31
|
+
historyStore?: FileHistoryStore;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface FileHistoryRestorePayload {
|
|
35
|
+
path: string;
|
|
36
|
+
restored: boolean;
|
|
37
|
+
version?: FileHistoryVersion;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class FileHistoryRestoreTool extends BaseTool<typeof schema> {
|
|
41
|
+
name = 'file_history_restore';
|
|
42
|
+
description = FILE_HISTORY_RESTORE_TOOL_DESCRIPTION;
|
|
43
|
+
parameters = schema;
|
|
44
|
+
|
|
45
|
+
private readonly allowedDirectories: string[];
|
|
46
|
+
private readonly historyStore: FileHistoryStore;
|
|
47
|
+
|
|
48
|
+
constructor(options: FileHistoryRestoreToolOptions = {}) {
|
|
49
|
+
super();
|
|
50
|
+
this.allowedDirectories = normalizeAllowedDirectories(options.allowedDirectories);
|
|
51
|
+
this.historyStore = options.historyStore ?? createConfiguredFileHistoryStore();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override shouldConfirm(): boolean {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override getConfirmDetails(args: z.infer<typeof schema>): ToolConfirmDetails | null {
|
|
59
|
+
const absolute = resolveRequestedPath(args.path);
|
|
60
|
+
const assessment = assessPathAccess(absolute, this.allowedDirectories, 'PATH_NOT_ALLOWED');
|
|
61
|
+
if (assessment.allowed) {
|
|
62
|
+
return {
|
|
63
|
+
reason: `Restore historical content for ${assessment.normalizedCandidate}`,
|
|
64
|
+
metadata: {
|
|
65
|
+
requestedPath: assessment.normalizedCandidate,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
reason: assessment.message,
|
|
71
|
+
metadata: {
|
|
72
|
+
requestedPath: absolute,
|
|
73
|
+
allowedDirectories: this.allowedDirectories,
|
|
74
|
+
errorCode: 'PATH_NOT_ALLOWED',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async execute(args: z.infer<typeof schema>, context?: ToolExecutionContext): Promise<ToolResult> {
|
|
80
|
+
try {
|
|
81
|
+
const absolute = resolveRequestedPath(args.path);
|
|
82
|
+
const validatedPath = ensurePathWithinAllowed(
|
|
83
|
+
absolute,
|
|
84
|
+
this.allowedDirectories,
|
|
85
|
+
'PATH_NOT_ALLOWED',
|
|
86
|
+
context?.confirmationApproved === true
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const versions = await this.historyStore.listVersions(validatedPath);
|
|
90
|
+
const version = this.resolveVersion(versions, args.versionId);
|
|
91
|
+
if (!version) {
|
|
92
|
+
const message = args.versionId
|
|
93
|
+
? `FILE_HISTORY_VERSION_NOT_FOUND: ${args.versionId}`
|
|
94
|
+
: `FILE_HISTORY_EMPTY: ${validatedPath}`;
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
output: message,
|
|
98
|
+
error: new ToolExecutionError(message),
|
|
99
|
+
metadata: {
|
|
100
|
+
path: validatedPath,
|
|
101
|
+
versionId: args.versionId,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const restored = await this.historyStore.restoreVersion(validatedPath, version.versionId);
|
|
107
|
+
const payload: FileHistoryRestorePayload = {
|
|
108
|
+
path: validatedPath,
|
|
109
|
+
restored,
|
|
110
|
+
version: restored ? version : undefined,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (!restored) {
|
|
114
|
+
const message = `FILE_HISTORY_RESTORE_FAILED: ${version.versionId}`;
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
output: message,
|
|
118
|
+
error: new ToolExecutionError(message),
|
|
119
|
+
metadata: payload as unknown as Record<string, unknown>,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
output: JSON.stringify(payload),
|
|
126
|
+
metadata: payload as unknown as Record<string, unknown>,
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
output: message,
|
|
133
|
+
error: new ToolExecutionError(message),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private resolveVersion(
|
|
139
|
+
versions: FileHistoryVersion[],
|
|
140
|
+
versionId?: string
|
|
141
|
+
): FileHistoryVersion | undefined {
|
|
142
|
+
if (!versionId) {
|
|
143
|
+
return versions[0];
|
|
144
|
+
}
|
|
145
|
+
return versions.find((entry) => entry.versionId === versionId);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default FileHistoryRestoreTool;
|