@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,368 +0,0 @@
|
|
|
1
|
-
import * as os from 'node:os';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { promises as fs } from 'node:fs';
|
|
4
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { WriteFileTool } from '../write-file';
|
|
6
|
-
import { appendContent, appendRawArgs, createWriteBufferSession } from '../../agent/write-buffer';
|
|
7
|
-
|
|
8
|
-
const tempDirs: string[] = [];
|
|
9
|
-
|
|
10
|
-
async function createTempDir(): Promise<string> {
|
|
11
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'renx-write-file-tool-'));
|
|
12
|
-
tempDirs.push(dir);
|
|
13
|
-
return dir;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
afterEach(async () => {
|
|
17
|
-
await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
function parseOutput<T>(output: string | undefined): T {
|
|
21
|
-
return JSON.parse(output || '{}') as T;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('WriteFileTool', () => {
|
|
25
|
-
it('writes small content directly with OK response', async () => {
|
|
26
|
-
const allowedDir = await createTempDir();
|
|
27
|
-
const bufferDir = await createTempDir();
|
|
28
|
-
const tool = new WriteFileTool({
|
|
29
|
-
allowedDirectories: [allowedDir],
|
|
30
|
-
bufferBaseDir: bufferDir,
|
|
31
|
-
maxChunkBytes: 64,
|
|
32
|
-
});
|
|
33
|
-
const target = path.join(allowedDir, 'a.txt');
|
|
34
|
-
|
|
35
|
-
const result = await tool.execute({
|
|
36
|
-
path: target,
|
|
37
|
-
content: 'hello',
|
|
38
|
-
mode: 'direct',
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(result.success).toBe(true);
|
|
42
|
-
const payload = parseOutput<{
|
|
43
|
-
ok: boolean;
|
|
44
|
-
code: string;
|
|
45
|
-
message: string;
|
|
46
|
-
nextAction: string;
|
|
47
|
-
}>(result.output);
|
|
48
|
-
expect(payload).toMatchObject({
|
|
49
|
-
ok: true,
|
|
50
|
-
code: 'OK',
|
|
51
|
-
nextAction: 'none',
|
|
52
|
-
});
|
|
53
|
-
expect(payload.message).toContain('File written successfully');
|
|
54
|
-
expect(await fs.readFile(target, 'utf8')).toBe('hello');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('allows write when target path goes through a symlinked directory inside allowed roots', async () => {
|
|
58
|
-
const allowedDir = await createTempDir();
|
|
59
|
-
const linkBaseDir = await createTempDir();
|
|
60
|
-
const bufferDir = await createTempDir();
|
|
61
|
-
const linkDir = path.join(linkBaseDir, 'allowed-link');
|
|
62
|
-
await fs.symlink(allowedDir, linkDir);
|
|
63
|
-
|
|
64
|
-
const tool = new WriteFileTool({
|
|
65
|
-
allowedDirectories: [allowedDir],
|
|
66
|
-
bufferBaseDir: bufferDir,
|
|
67
|
-
maxChunkBytes: 64,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const targetViaLink = path.join(linkDir, 'through-link.txt');
|
|
71
|
-
const result = await tool.execute({
|
|
72
|
-
path: targetViaLink,
|
|
73
|
-
content: 'hello-link',
|
|
74
|
-
mode: 'direct',
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
expect(result.success).toBe(true);
|
|
78
|
-
expect(await fs.readFile(path.join(allowedDir, 'through-link.txt'), 'utf8')).toBe('hello-link');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('buffers large direct write and requests finalize', async () => {
|
|
82
|
-
const allowedDir = await createTempDir();
|
|
83
|
-
const bufferDir = await createTempDir();
|
|
84
|
-
const tool = new WriteFileTool({
|
|
85
|
-
allowedDirectories: [allowedDir],
|
|
86
|
-
bufferBaseDir: bufferDir,
|
|
87
|
-
maxChunkBytes: 8,
|
|
88
|
-
});
|
|
89
|
-
const target = path.join(allowedDir, 'large.txt');
|
|
90
|
-
|
|
91
|
-
const result = await tool.execute(
|
|
92
|
-
{
|
|
93
|
-
path: target,
|
|
94
|
-
content: '0123456789012345',
|
|
95
|
-
mode: 'direct',
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
toolCallId: 'write_call_1',
|
|
99
|
-
loopIndex: 1,
|
|
100
|
-
agent: {},
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
expect(result.success).toBe(false);
|
|
105
|
-
const payload = parseOutput<{
|
|
106
|
-
ok: boolean;
|
|
107
|
-
code: string;
|
|
108
|
-
nextAction: string;
|
|
109
|
-
buffer: { bufferId: string; bufferedBytes: number; maxChunkBytes: number };
|
|
110
|
-
}>(result.output);
|
|
111
|
-
expect(payload.ok).toBe(false);
|
|
112
|
-
expect(payload.code).toBe('WRITE_FILE_PARTIAL_BUFFERED');
|
|
113
|
-
expect(payload.nextAction).toBe('finalize');
|
|
114
|
-
expect(payload.buffer.bufferId).toBe('write_call_1');
|
|
115
|
-
expect(payload.buffer.bufferedBytes).toBe(Buffer.byteLength('0123456789012345', 'utf8'));
|
|
116
|
-
expect(payload.buffer.maxChunkBytes).toBe(8);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('finalizes a fully buffered direct write without any intermediate chunk step', async () => {
|
|
120
|
-
const allowedDir = await createTempDir();
|
|
121
|
-
const bufferDir = await createTempDir();
|
|
122
|
-
const tool = new WriteFileTool({
|
|
123
|
-
allowedDirectories: [allowedDir],
|
|
124
|
-
bufferBaseDir: bufferDir,
|
|
125
|
-
maxChunkBytes: 8,
|
|
126
|
-
});
|
|
127
|
-
const target = path.join(allowedDir, 'finalize.txt');
|
|
128
|
-
const fullContent = 'abcdefghi';
|
|
129
|
-
|
|
130
|
-
const direct = await tool.execute(
|
|
131
|
-
{
|
|
132
|
-
path: target,
|
|
133
|
-
content: 'abcdefghi',
|
|
134
|
-
mode: 'direct',
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
toolCallId: 'write_call_2',
|
|
138
|
-
loopIndex: 1,
|
|
139
|
-
agent: {},
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
-
const directPayload = parseOutput<{ buffer: { bufferId: string } }>(direct.output);
|
|
143
|
-
|
|
144
|
-
const finalize = await tool.execute({
|
|
145
|
-
path: target,
|
|
146
|
-
mode: 'finalize',
|
|
147
|
-
bufferId: directPayload.buffer.bufferId,
|
|
148
|
-
});
|
|
149
|
-
const finalizePayload = parseOutput<{ ok: boolean; code: string; nextAction: string }>(
|
|
150
|
-
finalize.output
|
|
151
|
-
);
|
|
152
|
-
expect(finalizePayload).toMatchObject({
|
|
153
|
-
ok: true,
|
|
154
|
-
code: 'WRITE_FILE_FINALIZE_OK',
|
|
155
|
-
nextAction: 'none',
|
|
156
|
-
});
|
|
157
|
-
expect(await fs.readFile(target, 'utf8')).toBe(fullContent);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('finalizes buffered content to the original full direct payload', async () => {
|
|
161
|
-
const allowedDir = await createTempDir();
|
|
162
|
-
const bufferDir = await createTempDir();
|
|
163
|
-
const tool = new WriteFileTool({
|
|
164
|
-
allowedDirectories: [allowedDir],
|
|
165
|
-
bufferBaseDir: bufferDir,
|
|
166
|
-
maxChunkBytes: 8,
|
|
167
|
-
});
|
|
168
|
-
const target = path.join(allowedDir, 'finalize-no-checksum.txt');
|
|
169
|
-
const content = 'abcdefghijk';
|
|
170
|
-
|
|
171
|
-
const direct = await tool.execute(
|
|
172
|
-
{
|
|
173
|
-
path: target,
|
|
174
|
-
content,
|
|
175
|
-
mode: 'direct',
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
toolCallId: 'write_call_3',
|
|
179
|
-
loopIndex: 1,
|
|
180
|
-
agent: {},
|
|
181
|
-
}
|
|
182
|
-
);
|
|
183
|
-
const directPayload = parseOutput<{ buffer: { bufferId: string } }>(direct.output);
|
|
184
|
-
|
|
185
|
-
const finalize = await tool.execute({
|
|
186
|
-
path: target,
|
|
187
|
-
mode: 'finalize',
|
|
188
|
-
bufferId: directPayload.buffer.bufferId,
|
|
189
|
-
});
|
|
190
|
-
const finalizePayload = parseOutput<{ ok: boolean; code: string; nextAction: string }>(
|
|
191
|
-
finalize.output
|
|
192
|
-
);
|
|
193
|
-
expect(finalizePayload).toMatchObject({
|
|
194
|
-
ok: true,
|
|
195
|
-
code: 'WRITE_FILE_FINALIZE_OK',
|
|
196
|
-
nextAction: 'none',
|
|
197
|
-
});
|
|
198
|
-
expect(await fs.readFile(target, 'utf8')).toBe(content);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('finalizes a buffered direct write by bufferId without requiring path and keeps full content', async () => {
|
|
202
|
-
const allowedDir = await createTempDir();
|
|
203
|
-
const bufferDir = await createTempDir();
|
|
204
|
-
const tool = new WriteFileTool({
|
|
205
|
-
allowedDirectories: [allowedDir],
|
|
206
|
-
bufferBaseDir: bufferDir,
|
|
207
|
-
maxChunkBytes: 8,
|
|
208
|
-
});
|
|
209
|
-
const target = path.join(allowedDir, 'finalize-by-buffer-id.txt');
|
|
210
|
-
const content = 'abcdefghijklmno';
|
|
211
|
-
|
|
212
|
-
const direct = await tool.execute(
|
|
213
|
-
{
|
|
214
|
-
path: target,
|
|
215
|
-
content,
|
|
216
|
-
mode: 'direct',
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
toolCallId: 'write_call_finalize_by_id',
|
|
220
|
-
loopIndex: 1,
|
|
221
|
-
agent: {},
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
const directPayload = parseOutput<{ ok: boolean; buffer: { bufferId: string } }>(direct.output);
|
|
225
|
-
expect(directPayload.ok).toBe(false);
|
|
226
|
-
|
|
227
|
-
const finalize = await tool.execute({
|
|
228
|
-
mode: 'finalize',
|
|
229
|
-
bufferId: directPayload.buffer.bufferId,
|
|
230
|
-
} as never);
|
|
231
|
-
const finalizePayload = parseOutput<{ ok: boolean; code: string; nextAction: string }>(
|
|
232
|
-
finalize.output
|
|
233
|
-
);
|
|
234
|
-
expect(finalizePayload).toMatchObject({
|
|
235
|
-
ok: true,
|
|
236
|
-
code: 'WRITE_FILE_FINALIZE_OK',
|
|
237
|
-
nextAction: 'none',
|
|
238
|
-
});
|
|
239
|
-
expect(await fs.readFile(target, 'utf8')).toBe(content);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('finalizes an orphaned buffered session by scanning meta/rawArgs when pointer is missing', async () => {
|
|
243
|
-
const allowedDir = await createTempDir();
|
|
244
|
-
const bufferDir = await createTempDir();
|
|
245
|
-
const tool = new WriteFileTool({
|
|
246
|
-
allowedDirectories: [allowedDir],
|
|
247
|
-
bufferBaseDir: bufferDir,
|
|
248
|
-
maxChunkBytes: 8,
|
|
249
|
-
});
|
|
250
|
-
const target = path.join(allowedDir, 'finalize-orphaned-session.txt');
|
|
251
|
-
const content = 'orphaned buffered content';
|
|
252
|
-
const session = await createWriteBufferSession({
|
|
253
|
-
messageId: 'msg_orphaned',
|
|
254
|
-
toolCallId: 'write_call_orphaned',
|
|
255
|
-
baseDir: bufferDir,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
await appendRawArgs(
|
|
259
|
-
session,
|
|
260
|
-
JSON.stringify({
|
|
261
|
-
mode: 'direct',
|
|
262
|
-
path: target,
|
|
263
|
-
content,
|
|
264
|
-
})
|
|
265
|
-
);
|
|
266
|
-
await appendContent(session, content);
|
|
267
|
-
|
|
268
|
-
const finalize = await tool.execute({
|
|
269
|
-
mode: 'finalize',
|
|
270
|
-
bufferId: session.bufferId,
|
|
271
|
-
} as never);
|
|
272
|
-
const payload = parseOutput<{ ok: boolean; code: string; nextAction: string }>(finalize.output);
|
|
273
|
-
|
|
274
|
-
expect(payload).toMatchObject({
|
|
275
|
-
ok: true,
|
|
276
|
-
code: 'WRITE_FILE_FINALIZE_OK',
|
|
277
|
-
nextAction: 'none',
|
|
278
|
-
});
|
|
279
|
-
expect(await fs.readFile(target, 'utf8')).toBe(content);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('returns NEED_FINALIZE when finalize is missing bufferId', async () => {
|
|
283
|
-
const allowedDir = await createTempDir();
|
|
284
|
-
const bufferDir = await createTempDir();
|
|
285
|
-
const tool = new WriteFileTool({
|
|
286
|
-
allowedDirectories: [allowedDir],
|
|
287
|
-
bufferBaseDir: bufferDir,
|
|
288
|
-
maxChunkBytes: 8,
|
|
289
|
-
});
|
|
290
|
-
const payload = parseOutput<{
|
|
291
|
-
ok: boolean;
|
|
292
|
-
code: string;
|
|
293
|
-
nextAction: string;
|
|
294
|
-
}>(
|
|
295
|
-
(
|
|
296
|
-
await tool.execute({
|
|
297
|
-
mode: 'finalize',
|
|
298
|
-
} as never)
|
|
299
|
-
).output
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
expect(payload.ok).toBe(false);
|
|
303
|
-
expect(payload.code).toBe('WRITE_FILE_NEED_FINALIZE');
|
|
304
|
-
expect(payload.nextAction).toBe('finalize');
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it('prevents finalize from committing buffered content to a different target path', async () => {
|
|
308
|
-
const allowedDir = await createTempDir();
|
|
309
|
-
const bufferDir = await createTempDir();
|
|
310
|
-
const tool = new WriteFileTool({
|
|
311
|
-
allowedDirectories: [allowedDir],
|
|
312
|
-
bufferBaseDir: bufferDir,
|
|
313
|
-
maxChunkBytes: 8,
|
|
314
|
-
});
|
|
315
|
-
const targetA = path.join(allowedDir, 'target-a.txt');
|
|
316
|
-
const targetB = path.join(allowedDir, 'target-b.txt');
|
|
317
|
-
|
|
318
|
-
const direct = await tool.execute(
|
|
319
|
-
{
|
|
320
|
-
path: targetA,
|
|
321
|
-
content: 'abcdefghijk',
|
|
322
|
-
mode: 'direct',
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
toolCallId: 'write_call_path_mismatch',
|
|
326
|
-
loopIndex: 1,
|
|
327
|
-
agent: {},
|
|
328
|
-
}
|
|
329
|
-
);
|
|
330
|
-
const directPayload = parseOutput<{ buffer: { bufferId: string } }>(direct.output);
|
|
331
|
-
|
|
332
|
-
const finalize = await tool.execute({
|
|
333
|
-
path: targetB,
|
|
334
|
-
mode: 'finalize',
|
|
335
|
-
bufferId: directPayload.buffer.bufferId,
|
|
336
|
-
});
|
|
337
|
-
const payload = parseOutput<{ ok: boolean; code: string; message: string; nextAction: string }>(
|
|
338
|
-
finalize.output
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
expect(payload.ok).toBe(false);
|
|
342
|
-
expect(payload.code).toBe('WRITE_FILE_NEED_FINALIZE');
|
|
343
|
-
expect(payload.message).toContain('Target path does not match');
|
|
344
|
-
expect(payload.nextAction).toBe('finalize');
|
|
345
|
-
await expect(fs.readFile(targetB, 'utf8')).rejects.toBeDefined();
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('returns execution failure when path is outside allowed directories', async () => {
|
|
349
|
-
const allowedDir = await createTempDir();
|
|
350
|
-
const bufferDir = await createTempDir();
|
|
351
|
-
const tool = new WriteFileTool({
|
|
352
|
-
allowedDirectories: [allowedDir],
|
|
353
|
-
bufferBaseDir: bufferDir,
|
|
354
|
-
maxChunkBytes: 32,
|
|
355
|
-
});
|
|
356
|
-
const outside = path.resolve(allowedDir, '..', 'outside.txt');
|
|
357
|
-
|
|
358
|
-
const result = await tool.execute({
|
|
359
|
-
path: outside,
|
|
360
|
-
content: 'x',
|
|
361
|
-
mode: 'direct',
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
expect(result.success).toBe(false);
|
|
365
|
-
expect(result.error?.name).toBe('ToolExecutionError');
|
|
366
|
-
expect(result.output).toContain('Path is outside allowed directories');
|
|
367
|
-
});
|
|
368
|
-
});
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import type { LLMTool, ToolConcurrencyMode, ToolExecutionContext } from './types';
|
|
3
|
-
import { ToolExecutionError } from './error';
|
|
4
|
-
|
|
5
|
-
export type ToolParameterSchema = z.ZodType;
|
|
6
|
-
|
|
7
|
-
interface ZodDef {
|
|
8
|
-
typeName?: string;
|
|
9
|
-
shape?: unknown;
|
|
10
|
-
description?: string;
|
|
11
|
-
checks?: Array<{
|
|
12
|
-
kind?: string;
|
|
13
|
-
value?: number;
|
|
14
|
-
regex?: { source: string };
|
|
15
|
-
check?: string;
|
|
16
|
-
minimum?: number;
|
|
17
|
-
maximum?: number;
|
|
18
|
-
pattern?: string | RegExp;
|
|
19
|
-
format?: string;
|
|
20
|
-
inclusive?: boolean;
|
|
21
|
-
}>;
|
|
22
|
-
type?: z.ZodType | string;
|
|
23
|
-
values?: unknown[];
|
|
24
|
-
entries?: Record<string, unknown>;
|
|
25
|
-
value?: unknown;
|
|
26
|
-
innerType?: z.ZodType;
|
|
27
|
-
defaultValue?: unknown;
|
|
28
|
-
left?: z.ZodType;
|
|
29
|
-
right?: z.ZodType;
|
|
30
|
-
valueType?: z.ZodType;
|
|
31
|
-
items?: z.ZodType[];
|
|
32
|
-
options?: z.ZodType[];
|
|
33
|
-
element?: z.ZodType;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function normalizeTypeName(rawTypeName?: string): string | undefined {
|
|
37
|
-
if (!rawTypeName) return undefined;
|
|
38
|
-
const trimmed = rawTypeName.trim();
|
|
39
|
-
if (!trimmed) return undefined;
|
|
40
|
-
const withoutPrefix = trimmed.startsWith('Zod') ? trimmed.slice(3) : trimmed;
|
|
41
|
-
return withoutPrefix.toLowerCase();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getSchemaDef(schemaAny: unknown): ZodDef | undefined {
|
|
45
|
-
return ((schemaAny as { _zod_def?: ZodDef })._zod_def ||
|
|
46
|
-
(schemaAny as { _def?: ZodDef })._def) as ZodDef | undefined;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getSchemaKind(schemaAny: unknown): string | undefined {
|
|
50
|
-
const def = getSchemaDef(schemaAny);
|
|
51
|
-
const rawType = (def as { type?: unknown } | undefined)?.type;
|
|
52
|
-
const fromTypeField = typeof rawType === 'string' ? rawType : undefined;
|
|
53
|
-
const nestedTypeName = (rawType as { _def?: { typeName?: string } } | undefined)?._def?.typeName;
|
|
54
|
-
return normalizeTypeName(def?.typeName || fromTypeField || nestedTypeName);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function zodToJsonSchema(schema: z.ZodType): Record<string, unknown> {
|
|
58
|
-
const schemaAny = schema as unknown;
|
|
59
|
-
|
|
60
|
-
if (typeof (z as unknown as { toJSONSchema?: unknown }).toJSONSchema === 'function') {
|
|
61
|
-
try {
|
|
62
|
-
const result = (z as unknown as { toJSONSchema: (s: z.ZodType) => unknown }).toJSONSchema(
|
|
63
|
-
schema
|
|
64
|
-
);
|
|
65
|
-
if (result && typeof result === 'object') {
|
|
66
|
-
return result as Record<string, unknown>;
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
// fallback to local conversion
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (typeof (schemaAny as { toJSONSchema?: unknown }).toJSONSchema === 'function') {
|
|
74
|
-
try {
|
|
75
|
-
const result = (schemaAny as { toJSONSchema: () => Record<string, unknown> }).toJSONSchema();
|
|
76
|
-
if (result && typeof result === 'object') {
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
// fallback to local conversion
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const def = getSchemaDef(schemaAny);
|
|
85
|
-
const typeName = getSchemaKind(schemaAny);
|
|
86
|
-
|
|
87
|
-
if (typeName === 'object') {
|
|
88
|
-
const properties: Record<string, unknown> = {};
|
|
89
|
-
const required: string[] = [];
|
|
90
|
-
|
|
91
|
-
if (!def) return { type: 'object' };
|
|
92
|
-
|
|
93
|
-
const shape = typeof def.shape === 'function' ? (def.shape as () => unknown)() : def.shape;
|
|
94
|
-
|
|
95
|
-
for (const [key, value] of Object.entries(shape as Record<string, unknown>)) {
|
|
96
|
-
const valueAny = value as unknown;
|
|
97
|
-
properties[key] = zodToJsonSchema(valueAny as z.ZodType);
|
|
98
|
-
|
|
99
|
-
const propTypeName = getSchemaKind(valueAny);
|
|
100
|
-
if (propTypeName !== 'optional' && propTypeName !== 'default') {
|
|
101
|
-
required.push(key);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
type: 'object',
|
|
107
|
-
properties,
|
|
108
|
-
required: required.length > 0 ? required : undefined,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (typeName === 'string') {
|
|
113
|
-
const result: Record<string, unknown> = { type: 'string' };
|
|
114
|
-
if (def?.description) {
|
|
115
|
-
result.description = def.description;
|
|
116
|
-
}
|
|
117
|
-
if (def?.checks) {
|
|
118
|
-
for (const check of def.checks) {
|
|
119
|
-
const checkKind = check.kind || check.check;
|
|
120
|
-
if (checkKind === 'min') {
|
|
121
|
-
result.minLength = check.value;
|
|
122
|
-
} else if (checkKind === 'max') {
|
|
123
|
-
result.maxLength = check.value;
|
|
124
|
-
} else if (checkKind === 'regex') {
|
|
125
|
-
result.pattern = check.regex?.source;
|
|
126
|
-
} else if (checkKind === 'min_length') {
|
|
127
|
-
result.minLength = check.minimum;
|
|
128
|
-
} else if (checkKind === 'max_length') {
|
|
129
|
-
result.maxLength = check.maximum;
|
|
130
|
-
} else if (checkKind === 'string_format' && check.format === 'regex') {
|
|
131
|
-
result.pattern =
|
|
132
|
-
typeof check.pattern === 'string'
|
|
133
|
-
? check.pattern
|
|
134
|
-
: check.pattern instanceof RegExp
|
|
135
|
-
? check.pattern.source
|
|
136
|
-
: undefined;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (typeName === 'number') {
|
|
144
|
-
const result: Record<string, unknown> = { type: 'number' };
|
|
145
|
-
if (def?.description) {
|
|
146
|
-
result.description = def.description;
|
|
147
|
-
}
|
|
148
|
-
if (def?.checks) {
|
|
149
|
-
for (const check of def.checks) {
|
|
150
|
-
const checkKind = check.kind || check.check;
|
|
151
|
-
if (checkKind === 'min') {
|
|
152
|
-
result.minimum = check.value;
|
|
153
|
-
} else if (checkKind === 'max') {
|
|
154
|
-
result.maximum = check.value;
|
|
155
|
-
} else if (checkKind === 'int') {
|
|
156
|
-
result.type = 'integer';
|
|
157
|
-
} else if (checkKind === 'greater_than') {
|
|
158
|
-
if (check.inclusive) {
|
|
159
|
-
result.minimum = check.value;
|
|
160
|
-
} else {
|
|
161
|
-
result.exclusiveMinimum = check.value;
|
|
162
|
-
}
|
|
163
|
-
} else if (checkKind === 'less_than') {
|
|
164
|
-
if (check.inclusive) {
|
|
165
|
-
result.maximum = check.value;
|
|
166
|
-
} else {
|
|
167
|
-
result.exclusiveMaximum = check.value;
|
|
168
|
-
}
|
|
169
|
-
} else if (checkKind === 'number_format') {
|
|
170
|
-
result.type = 'integer';
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (typeName === 'boolean') {
|
|
178
|
-
return {
|
|
179
|
-
type: 'boolean',
|
|
180
|
-
...(def?.description && { description: def.description }),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (typeName === 'array') {
|
|
185
|
-
const itemSchema = def?.element || (def?.type as z.ZodType | undefined);
|
|
186
|
-
return {
|
|
187
|
-
type: 'array',
|
|
188
|
-
items: itemSchema ? zodToJsonSchema(itemSchema as z.ZodType) : {},
|
|
189
|
-
...(def?.description && { description: def.description }),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (typeName === 'enum') {
|
|
194
|
-
const enumValues = def?.values || (def?.entries ? Object.values(def.entries) : []);
|
|
195
|
-
return {
|
|
196
|
-
type: 'string',
|
|
197
|
-
enum: enumValues,
|
|
198
|
-
...(def?.description && { description: def.description }),
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (typeName === 'literal') {
|
|
203
|
-
const value = def?.value ?? def?.values?.[0];
|
|
204
|
-
if (typeof value === 'string') {
|
|
205
|
-
return { type: 'string', const: value };
|
|
206
|
-
}
|
|
207
|
-
if (typeof value === 'number') {
|
|
208
|
-
return { type: 'number', const: value };
|
|
209
|
-
}
|
|
210
|
-
if (typeof value === 'boolean') {
|
|
211
|
-
return { type: 'boolean', const: value };
|
|
212
|
-
}
|
|
213
|
-
return { const: value };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (typeName === 'optional') {
|
|
217
|
-
return def?.innerType ? zodToJsonSchema(def.innerType as unknown as z.ZodType) : {};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (typeName === 'default') {
|
|
221
|
-
const innerSchema = def?.innerType
|
|
222
|
-
? zodToJsonSchema(def.innerType as unknown as z.ZodType)
|
|
223
|
-
: {};
|
|
224
|
-
return {
|
|
225
|
-
...innerSchema,
|
|
226
|
-
default:
|
|
227
|
-
typeof def?.defaultValue === 'function'
|
|
228
|
-
? (def.defaultValue as () => unknown)()
|
|
229
|
-
: def?.defaultValue,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (typeName === 'nullable') {
|
|
234
|
-
const innerSchema = def?.innerType
|
|
235
|
-
? zodToJsonSchema(def.innerType as unknown as z.ZodType)
|
|
236
|
-
: {};
|
|
237
|
-
return {
|
|
238
|
-
...innerSchema,
|
|
239
|
-
nullable: true,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (typeName === 'union') {
|
|
244
|
-
return {
|
|
245
|
-
oneOf:
|
|
246
|
-
def?.options?.map((option: z.ZodType) => zodToJsonSchema(option as unknown as z.ZodType)) ||
|
|
247
|
-
[],
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (typeName === 'intersection') {
|
|
252
|
-
return {
|
|
253
|
-
allOf: [
|
|
254
|
-
def?.left ? zodToJsonSchema(def.left as unknown as z.ZodType) : {},
|
|
255
|
-
def?.right ? zodToJsonSchema(def.right as unknown as z.ZodType) : {},
|
|
256
|
-
],
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (typeName === 'record') {
|
|
261
|
-
return {
|
|
262
|
-
type: 'object',
|
|
263
|
-
additionalProperties: def?.valueType
|
|
264
|
-
? zodToJsonSchema(def.valueType as unknown as z.ZodType)
|
|
265
|
-
: {},
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (typeName === 'tuple') {
|
|
270
|
-
return {
|
|
271
|
-
type: 'array',
|
|
272
|
-
items:
|
|
273
|
-
def?.items?.map((item: z.ZodType) => zodToJsonSchema(item as unknown as z.ZodType)) || [],
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (typeName === 'null') {
|
|
278
|
-
return { type: 'null' };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (typeName === 'any' || typeName === 'unknown') {
|
|
282
|
-
return {};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return {};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export interface ToolResult {
|
|
289
|
-
success: boolean;
|
|
290
|
-
output?: string;
|
|
291
|
-
summary?: string;
|
|
292
|
-
payload?: unknown;
|
|
293
|
-
error?: ToolExecutionError;
|
|
294
|
-
metadata?: Record<string, unknown>;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export interface ToolConfirmDetails {
|
|
298
|
-
reason?: string;
|
|
299
|
-
metadata?: Record<string, unknown>;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export abstract class BaseTool<T extends ToolParameterSchema = ToolParameterSchema> {
|
|
303
|
-
abstract name: string;
|
|
304
|
-
abstract description: string;
|
|
305
|
-
abstract parameters: T;
|
|
306
|
-
|
|
307
|
-
shouldConfirm(_args: z.input<T>): boolean {
|
|
308
|
-
return false;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
getConfirmDetails(_args: z.input<T>): ToolConfirmDetails | null {
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
getConcurrencyMode(_args: z.input<T>): ToolConcurrencyMode {
|
|
316
|
-
return 'exclusive';
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
getConcurrencyLockKey(_args: z.input<T>): string | undefined {
|
|
320
|
-
return undefined;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
abstract execute(args: z.input<T>, context?: ToolExecutionContext): Promise<ToolResult>;
|
|
324
|
-
|
|
325
|
-
safeValidateArgs(
|
|
326
|
-
args: Record<string, unknown>
|
|
327
|
-
): { success: true; data: z.infer<T> } | { success: false; error: z.ZodError } {
|
|
328
|
-
const result = this.parameters.safeParse(args);
|
|
329
|
-
if (result.success) {
|
|
330
|
-
return { success: true, data: result.data };
|
|
331
|
-
}
|
|
332
|
-
return { success: false, error: result.error };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
toToolSchema(): LLMTool {
|
|
336
|
-
return {
|
|
337
|
-
type: 'function',
|
|
338
|
-
function: {
|
|
339
|
-
name: this.name,
|
|
340
|
-
description: this.description,
|
|
341
|
-
parameters: zodToJsonSchema(this.parameters),
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
}
|