@namzu/sdk 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/dist/compaction/__tests__/verifier-empty-reply.test.d.ts +12 -0
- package/dist/compaction/__tests__/verifier-empty-reply.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/verifier-empty-reply.test.js +73 -0
- package/dist/compaction/__tests__/verifier-empty-reply.test.js.map +1 -0
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +10 -1
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/__tests__/compaction-budget-schema.test.d.ts +18 -0
- package/dist/config/__tests__/compaction-budget-schema.test.d.ts.map +1 -0
- package/dist/config/__tests__/compaction-budget-schema.test.js +96 -0
- package/dist/config/__tests__/compaction-budget-schema.test.js.map +1 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +20 -11
- package/dist/config/runtime.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +2 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +3 -1
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/provider/__tests__/errors.test.d.ts +2 -0
- package/dist/provider/__tests__/errors.test.d.ts.map +1 -0
- package/dist/provider/__tests__/errors.test.js +61 -0
- package/dist/provider/__tests__/errors.test.js.map +1 -0
- package/dist/provider/errors.d.ts +108 -0
- package/dist/provider/errors.d.ts.map +1 -0
- package/dist/provider/errors.js +311 -0
- package/dist/provider/errors.js.map +1 -0
- package/dist/provider/index.d.ts +2 -0
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +1 -0
- package/dist/provider/index.js.map +1 -1
- package/dist/public-runtime.d.ts +11 -11
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +11 -11
- package/dist/public-runtime.js.map +1 -1
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +45 -12
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/execute.test.js +104 -7
- package/dist/registry/tool/execute.test.js.map +1 -1
- package/dist/registry/toolset/catalog.d.ts.map +1 -1
- package/dist/registry/toolset/catalog.js +17 -4
- package/dist/registry/toolset/catalog.js.map +1 -1
- package/dist/registry/toolset/catalog.test.js +101 -0
- package/dist/registry/toolset/catalog.test.js.map +1 -1
- package/dist/runtime/query/__tests__/capability-negotiation.test.js +79 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -1
- package/dist/runtime/query/__tests__/deferred-tools.test.js +100 -0
- package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
- package/dist/runtime/query/__tests__/long-document-flow.test.js +12 -12
- package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -1
- package/dist/runtime/query/__tests__/stream-recovery.test.js +55 -2
- package/dist/runtime/query/__tests__/stream-recovery.test.js.map +1 -1
- package/dist/runtime/query/executor.js +2 -2
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +10 -0
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.d.ts +32 -0
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.js +152 -0
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +52 -12
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +11 -5
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +11 -1
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/tools/builtins/__tests__/edit.test.js +216 -10
- package/dist/tools/builtins/__tests__/edit.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/payload-budget.test.js +30 -16
- package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/write-file.test.js +37 -13
- package/dist/tools/builtins/__tests__/write-file.test.js.map +1 -1
- package/dist/tools/builtins/atomic-write-file.d.ts +13 -0
- package/dist/tools/builtins/atomic-write-file.d.ts.map +1 -0
- package/dist/tools/builtins/atomic-write-file.js +46 -0
- package/dist/tools/builtins/atomic-write-file.js.map +1 -0
- package/dist/tools/builtins/bash.js +2 -2
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts +2 -5
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +105 -125
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/file-mutation-lock.d.ts +7 -0
- package/dist/tools/builtins/file-mutation-lock.d.ts.map +1 -0
- package/dist/tools/builtins/file-mutation-lock.js +25 -0
- package/dist/tools/builtins/file-mutation-lock.js.map +1 -0
- package/dist/tools/builtins/index.js +2 -2
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts +1 -2
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +66 -40
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/__tests__/task-list.test.js +14 -0
- package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts +6 -8
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +11 -32
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/defineTool.d.ts +3 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +3 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/types/provider/chat.d.ts +9 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/error.d.ts +22 -0
- package/dist/types/provider/error.d.ts.map +1 -0
- package/dist/types/provider/error.js +2 -0
- package/dist/types/provider/error.js.map +1 -0
- package/dist/types/provider/index.d.ts +1 -0
- package/dist/types/provider/index.d.ts.map +1 -1
- package/dist/types/run/entity.d.ts +2 -0
- package/dist/types/run/entity.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +2 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/sandbox/index.d.ts +4 -0
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/tool/index.d.ts +20 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/compaction/__tests__/verifier-empty-reply.test.ts +96 -0
- package/src/compaction/verifier.ts +10 -1
- package/src/config/__tests__/compaction-budget-schema.test.ts +114 -0
- package/src/config/runtime.ts +20 -11
- package/src/manager/run/persistence.ts +3 -1
- package/src/provider/__tests__/errors.test.ts +85 -0
- package/src/provider/errors.ts +344 -0
- package/src/provider/index.ts +15 -0
- package/src/public-runtime.ts +53 -10
- package/src/registry/tool/execute.test.ts +121 -7
- package/src/registry/tool/execute.ts +50 -12
- package/src/registry/toolset/catalog.test.ts +118 -0
- package/src/registry/toolset/catalog.ts +26 -4
- package/src/runtime/query/__tests__/capability-negotiation.test.ts +88 -0
- package/src/runtime/query/__tests__/deferred-tools.test.ts +111 -0
- package/src/runtime/query/__tests__/long-document-flow.test.ts +12 -12
- package/src/runtime/query/__tests__/stream-recovery.test.ts +65 -2
- package/src/runtime/query/executor.ts +2 -2
- package/src/runtime/query/iteration/index.ts +15 -0
- package/src/runtime/query/iteration/phases/compaction-safe-cut.test.ts +186 -0
- package/src/runtime/query/iteration/phases/compaction.ts +54 -12
- package/src/runtime/query/iteration/stream-turn.ts +11 -6
- package/src/runtime/query/result.ts +11 -1
- package/src/tools/builtins/__tests__/edit.test.ts +270 -10
- package/src/tools/builtins/__tests__/payload-budget.test.ts +40 -20
- package/src/tools/builtins/__tests__/write-file.test.ts +52 -13
- package/src/tools/builtins/atomic-write-file.ts +58 -0
- package/src/tools/builtins/bash.ts +2 -2
- package/src/tools/builtins/edit.ts +116 -162
- package/src/tools/builtins/file-mutation-lock.ts +27 -0
- package/src/tools/builtins/index.ts +2 -2
- package/src/tools/builtins/write-file.ts +68 -44
- package/src/tools/coordinator/__tests__/task-list.test.ts +16 -0
- package/src/tools/coordinator/agent.ts +6 -8
- package/src/tools/coordinator/index.ts +11 -34
- package/src/tools/defineTool.ts +6 -0
- package/src/types/provider/chat.ts +14 -1
- package/src/types/provider/error.ts +29 -0
- package/src/types/provider/index.ts +5 -0
- package/src/types/run/entity.ts +2 -0
- package/src/types/run/events.ts +7 -1
- package/src/types/sandbox/index.ts +4 -0
- package/src/types/tool/index.ts +20 -0
|
@@ -16,7 +16,7 @@ const inputSchema = z.object({
|
|
|
16
16
|
command: z
|
|
17
17
|
.string()
|
|
18
18
|
.min(1)
|
|
19
|
-
.describe('The bash command to execute. Required, non-empty. Single command per call (use `&&` / `;` chaining for compound commands). Avoid heredocs that span more than a few hundred bytes — large content
|
|
19
|
+
.describe('The bash command to execute. Required, non-empty. Single command per call (use `&&` / `;` chaining for compound commands). Avoid heredocs that span more than a few hundred bytes — create large content with bounded write plus exact marker-advancing edit calls instead.'),
|
|
20
20
|
timeout: z
|
|
21
21
|
.preprocess((v) => (typeof v === 'string' ? Number(v) : v), z.number().default(DEFAULT_BASH_TIMEOUT_MS))
|
|
22
22
|
.describe(`Command timeout in milliseconds. Default: ${DEFAULT_BASH_TIMEOUT_MS}`),
|
|
@@ -26,7 +26,7 @@ function isDangerousCommand(command) {
|
|
|
26
26
|
}
|
|
27
27
|
export const BashTool = defineTool({
|
|
28
28
|
name: 'bash',
|
|
29
|
-
description: 'Executes a bash command and returns stdout/stderr output. Command timeout is configurable. The `command` parameter is required — never call this tool with empty arguments. For very long content
|
|
29
|
+
description: 'Executes a bash command and returns stdout/stderr output. Command timeout is configurable. The `command` parameter is required — never call this tool with empty arguments. For very long content, prefer a bounded write with a deterministic marker followed by exact marker-advancing edit calls over a heredoc.',
|
|
30
30
|
inputSchema,
|
|
31
31
|
category: 'shell',
|
|
32
32
|
permissions: ['shell_execute'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AACjC,kEAAkE;AAClE,kEAAkE;AAClE,mEAAmE;AACnE,gEAAgE;AAChE,iEAAiE;AACjE,oCAAoC;AACpC,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,uBAAuB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;AAC3F,MAAM,6BAA6B,GAAG,kBAAkB,CACvD,6BAA6B,EAC7B,GAAG,GAAG,IAAI,GAAG,IAAI,CACjB,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export declare const EditTool: import("../../public-types.js").ToolDefinition<{
|
|
2
2
|
path: string;
|
|
3
|
+
old_string: string;
|
|
4
|
+
new_string: string;
|
|
3
5
|
replace_all: boolean;
|
|
4
|
-
newStr?: string | undefined;
|
|
5
|
-
old_string?: string | undefined;
|
|
6
|
-
oldStr?: string | undefined;
|
|
7
|
-
new_string?: string | undefined;
|
|
8
|
-
insertLine?: string | number | undefined;
|
|
9
6
|
}>;
|
|
10
7
|
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/edit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/edit.ts"],"names":[],"mappings":"AAiEA,eAAO,MAAM,QAAQ;;;;;EAwEnB,CAAA"}
|
|
@@ -1,167 +1,165 @@
|
|
|
1
|
-
import { readFile
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { defineTool } from '../defineTool.js';
|
|
5
|
+
import { atomicWriteFile } from './atomic-write-file.js';
|
|
6
|
+
import { withFileMutationLock } from './file-mutation-lock.js';
|
|
5
7
|
const inputSchema = z
|
|
6
8
|
.object({
|
|
7
|
-
path: z
|
|
8
|
-
old_string: z
|
|
9
|
+
path: z
|
|
9
10
|
.string()
|
|
10
|
-
.
|
|
11
|
-
.describe('
|
|
12
|
-
|
|
11
|
+
.refine((value) => value.trim().length > 0, 'Path must not be empty.')
|
|
12
|
+
.describe('Path to the file to edit. Must not be empty.'),
|
|
13
|
+
old_string: z
|
|
13
14
|
.string()
|
|
14
|
-
.
|
|
15
|
-
.describe('
|
|
15
|
+
.min(1)
|
|
16
|
+
.describe('The exact unique text to replace, without read-tool line-number prefixes. Must not be empty.'),
|
|
16
17
|
new_string: z
|
|
17
18
|
.string()
|
|
18
|
-
.
|
|
19
|
-
.describe('The replacement string. Self-budget this payload under 12000 characters before calling.'),
|
|
20
|
-
newStr: z
|
|
21
|
-
.string()
|
|
22
|
-
.optional()
|
|
23
|
-
.describe('Alias for new_string. Also used as inserted content when insertLine is provided. Self-budget this payload under 12000 characters before calling.'),
|
|
24
|
-
insertLine: z
|
|
25
|
-
.union([z.coerce.number().int().min(0), z.string().min(1)])
|
|
26
|
-
.optional()
|
|
27
|
-
.describe('Optional line insertion target. Inserts the replacement after this 1-indexed line; 0 inserts before the first line; "end" appends to the file.'),
|
|
19
|
+
.describe('The exact replacement text. May be empty to delete old_string. Keep this payload under 12000 characters.'),
|
|
28
20
|
replace_all: z
|
|
29
21
|
.boolean()
|
|
30
22
|
.default(false)
|
|
31
|
-
.describe('Replace
|
|
23
|
+
.describe('Replace every occurrence instead of requiring one unique match.'),
|
|
32
24
|
})
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
.strict();
|
|
26
|
+
const modelInputSchema = {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
path: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'Path to the file to edit. Must not be empty.',
|
|
32
|
+
},
|
|
33
|
+
old_string: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Exact unique text from the file, without read-tool line-number prefixes. Must not be empty.',
|
|
36
|
+
},
|
|
37
|
+
new_string: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Exact replacement text. May be empty to delete old_string. Keep under 12000 characters.',
|
|
40
|
+
},
|
|
41
|
+
replace_all: {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
description: 'Replace every occurrence instead of requiring one unique match.',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['path', 'old_string', 'new_string'],
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
};
|
|
39
49
|
export const EditTool = defineTool({
|
|
40
50
|
name: 'edit',
|
|
41
|
-
description:
|
|
51
|
+
description: "Performs one targeted exact-string replacement in an existing file. Pass path + old_string + new_string; old_string must match exactly and be unique unless replace_all is true. Read the file immediately before editing, preserve whitespace and indentation, and never include the read tool's line-number prefix in old_string. To append or insert, replace a unique existing tail/marker with itself plus the new text; advance a deterministic marker such as {{CHUNK_001}} to {{CHUNK_002}} so replaying a completed edit cannot duplicate content. Prefer this tool over rewriting an existing file. Self-budget new_string under 12000 characters.",
|
|
42
52
|
inputSchema,
|
|
53
|
+
modelInputSchema,
|
|
54
|
+
enforceModelInput: true,
|
|
55
|
+
validationErrorHint: 'Required shape: {"path":"file.md","old_string":"exact unique text","new_string":"replacement text"}. Optional: "replace_all": true.',
|
|
43
56
|
category: 'filesystem',
|
|
44
57
|
permissions: ['file_write'],
|
|
45
58
|
readOnly: false,
|
|
46
59
|
destructive: false,
|
|
47
60
|
concurrencySafe: false,
|
|
48
61
|
async execute(input, context) {
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
51
|
-
return {
|
|
62
|
+
const parsed = inputSchema.safeParse(input);
|
|
63
|
+
if (!parsed.success) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
output: '',
|
|
67
|
+
error: `Invalid edit input: ${parsed.error.issues.map((issue) => issue.message).join('; ')}`,
|
|
68
|
+
};
|
|
52
69
|
}
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
const canonicalInput = parsed.data;
|
|
71
|
+
const replacement = {
|
|
72
|
+
oldString: canonicalInput.old_string,
|
|
73
|
+
newString: canonicalInput.new_string,
|
|
74
|
+
replaceAll: canonicalInput.replace_all,
|
|
75
|
+
};
|
|
76
|
+
if (replacement.oldString === replacement.newString) {
|
|
55
77
|
return {
|
|
56
78
|
success: false,
|
|
57
79
|
output: '',
|
|
58
|
-
error: 'old_string
|
|
80
|
+
error: 'old_string and new_string are identical — no change needed.',
|
|
59
81
|
};
|
|
60
82
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
83
|
+
const filePath = resolve(context.workingDirectory, canonicalInput.path);
|
|
84
|
+
const lockKey = `${context.sandbox ? 'sandbox' : 'local'}:${filePath}`;
|
|
85
|
+
return withFileMutationLock(lockKey, async () => {
|
|
86
|
+
if (context.sandbox) {
|
|
87
|
+
const buffer = await context.sandbox.readFile(canonicalInput.path);
|
|
88
|
+
const result = applyEdit(buffer.toString('utf-8'), replacement);
|
|
89
|
+
if (!result.success)
|
|
90
|
+
return { success: false, output: '', error: result.error };
|
|
91
|
+
await context.sandbox.writeFile(canonicalInput.path, result.content);
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
output: `Edited ${canonicalInput.path}: ${result.replacements} replacement(s) [sandboxed]`,
|
|
95
|
+
data: {
|
|
96
|
+
path: canonicalInput.path,
|
|
97
|
+
replacements: result.replacements,
|
|
98
|
+
sandboxed: true,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
68
101
|
}
|
|
69
|
-
await
|
|
102
|
+
const content = await readFile(filePath, 'utf-8');
|
|
103
|
+
const result = applyEdit(content, replacement);
|
|
104
|
+
if (!result.success)
|
|
105
|
+
return { success: false, output: '', error: result.error };
|
|
106
|
+
await atomicWriteFile(filePath, result.content);
|
|
70
107
|
return {
|
|
71
108
|
success: true,
|
|
72
|
-
output: `Edited ${
|
|
73
|
-
data: { path:
|
|
109
|
+
output: `Edited ${filePath}: ${result.replacements} replacement(s)`,
|
|
110
|
+
data: { path: filePath, replacements: result.replacements },
|
|
74
111
|
};
|
|
75
|
-
}
|
|
76
|
-
const filePath = resolve(context.workingDirectory, input.path);
|
|
77
|
-
const content = await readFile(filePath, 'utf-8');
|
|
78
|
-
const result = applyEdit(content, normalized.operation);
|
|
79
|
-
if (!result.success) {
|
|
80
|
-
return { success: false, output: '', error: result.error };
|
|
81
|
-
}
|
|
82
|
-
await writeFile(filePath, result.content, 'utf-8');
|
|
83
|
-
return {
|
|
84
|
-
success: true,
|
|
85
|
-
output: `Edited ${filePath}: ${result.replacements} replacement(s)`,
|
|
86
|
-
data: { path: filePath, replacements: result.replacements },
|
|
87
|
-
};
|
|
112
|
+
});
|
|
88
113
|
},
|
|
89
114
|
});
|
|
90
|
-
function
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
if (input.insertLine !== undefined) {
|
|
96
|
-
const insertLine = normalizeInsertLine(input.insertLine);
|
|
97
|
-
if (!insertLine.success)
|
|
98
|
-
return insertLine;
|
|
115
|
+
function normalizeLineEndings(content, input) {
|
|
116
|
+
const withoutCrlf = content.replaceAll('\r\n', '');
|
|
117
|
+
const usesOnlyCrlf = content.includes('\r\n') && !withoutCrlf.includes('\n');
|
|
118
|
+
if (usesOnlyCrlf) {
|
|
99
119
|
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
replace_all: input.replace_all,
|
|
106
|
-
},
|
|
120
|
+
...input,
|
|
121
|
+
oldString: content.includes(input.oldString)
|
|
122
|
+
? input.oldString
|
|
123
|
+
: input.oldString.replaceAll('\r\n', '\n').replaceAll('\n', '\r\n'),
|
|
124
|
+
newString: input.newString.replaceAll('\r\n', '\n').replaceAll('\n', '\r\n'),
|
|
107
125
|
};
|
|
108
126
|
}
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
return { success: false, error: 'Either old_string/oldStr or insertLine is required.' };
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
success: true,
|
|
115
|
-
operation: {
|
|
116
|
-
operation: 'replace',
|
|
117
|
-
oldString,
|
|
118
|
-
newString,
|
|
119
|
-
replace_all: input.replace_all,
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function normalizeInsertLine(value) {
|
|
124
|
-
if (typeof value === 'string') {
|
|
125
|
-
if (value.trim().toLowerCase() === 'end')
|
|
126
|
-
return { success: true, value: 'end' };
|
|
127
|
-
const parsed = Number(value);
|
|
128
|
-
if (Number.isInteger(parsed) && parsed >= 0)
|
|
129
|
-
return { success: true, value: parsed };
|
|
127
|
+
const usesOnlyLf = content.includes('\n') && !content.includes('\r\n');
|
|
128
|
+
if (usesOnlyLf) {
|
|
130
129
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
...input,
|
|
131
|
+
oldString: content.includes(input.oldString)
|
|
132
|
+
? input.oldString
|
|
133
|
+
: input.oldString.replaceAll('\r\n', '\n'),
|
|
134
|
+
newString: input.newString.replaceAll('\r\n', '\n'),
|
|
133
135
|
};
|
|
134
136
|
}
|
|
135
|
-
return
|
|
137
|
+
return input;
|
|
136
138
|
}
|
|
137
|
-
function applyEdit(content,
|
|
138
|
-
|
|
139
|
-
return applyLineInsert(content, input);
|
|
140
|
-
}
|
|
139
|
+
function applyEdit(content, rawInput) {
|
|
140
|
+
const input = normalizeLineEndings(content, rawInput);
|
|
141
141
|
if (!content.includes(input.oldString)) {
|
|
142
142
|
return {
|
|
143
143
|
success: false,
|
|
144
|
-
error: 'old_string
|
|
144
|
+
error: 'old_string was not found. Read the file again and copy exact text without line-number prefixes.',
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
|
-
if (input.
|
|
147
|
+
if (input.replaceAll) {
|
|
148
148
|
const parts = content.split(input.oldString);
|
|
149
|
-
const replacements = parts.length - 1;
|
|
150
149
|
return {
|
|
151
150
|
success: true,
|
|
152
151
|
content: parts.join(input.newString),
|
|
153
|
-
replacements,
|
|
152
|
+
replacements: parts.length - 1,
|
|
154
153
|
};
|
|
155
154
|
}
|
|
156
|
-
// Uniqueness check: old_string/oldStr must appear exactly once
|
|
157
155
|
const firstIndex = content.indexOf(input.oldString);
|
|
158
|
-
const secondIndex = content.indexOf(input.oldString, firstIndex +
|
|
156
|
+
const secondIndex = content.indexOf(input.oldString, firstIndex + input.oldString.length);
|
|
159
157
|
if (secondIndex !== -1) {
|
|
160
|
-
const
|
|
158
|
+
const firstLine = content.slice(0, firstIndex).split('\n').length;
|
|
161
159
|
const secondLine = content.slice(0, secondIndex).split('\n').length;
|
|
162
160
|
return {
|
|
163
161
|
success: false,
|
|
164
|
-
error: `old_string
|
|
162
|
+
error: `old_string is not unique — found at lines ${firstLine} and ${secondLine}. Include more surrounding context, or use replace_all: true.`,
|
|
165
163
|
};
|
|
166
164
|
}
|
|
167
165
|
return {
|
|
@@ -172,22 +170,4 @@ function applyEdit(content, input) {
|
|
|
172
170
|
replacements: 1,
|
|
173
171
|
};
|
|
174
172
|
}
|
|
175
|
-
function applyLineInsert(content, input) {
|
|
176
|
-
const hasTrailingNewline = content.endsWith('\n');
|
|
177
|
-
const lines = content.split('\n');
|
|
178
|
-
if (hasTrailingNewline)
|
|
179
|
-
lines.pop();
|
|
180
|
-
const line = input.insertLine === 'end'
|
|
181
|
-
? lines.length
|
|
182
|
-
: Math.min(Math.max(input.insertLine, 0), lines.length);
|
|
183
|
-
const inserted = input.newString.endsWith('\n')
|
|
184
|
-
? input.newString.slice(0, -1).split('\n')
|
|
185
|
-
: input.newString.split('\n');
|
|
186
|
-
lines.splice(line, 0, ...inserted);
|
|
187
|
-
return {
|
|
188
|
-
success: true,
|
|
189
|
-
content: `${lines.join('\n')}${hasTrailingNewline ? '\n' : ''}`,
|
|
190
|
-
replacements: 1,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
173
|
//# sourceMappingURL=edit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/tools/builtins/edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/tools/builtins/edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAE9D,MAAM,WAAW,GAAG,CAAC;KACnB,MAAM,CAAC;IACP,IAAI,EAAE,CAAC;SACL,MAAM,EAAE;SACR,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,yBAAyB,CAAC;SACrE,QAAQ,CAAC,8CAA8C,CAAC;IAC1D,UAAU,EAAE,CAAC;SACX,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,8FAA8F,CAC9F;IACF,UAAU,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,CACR,0GAA0G,CAC1G;IACF,WAAW,EAAE,CAAC;SACZ,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,iEAAiE,CAAC;CAC7E,CAAC;KACD,MAAM,EAAE,CAAA;AAIV,MAAM,gBAAgB,GAA4B;IACjD,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACX,IAAI,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,8CAA8C;SAC3D;QACD,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,WAAW,EACV,6FAA6F;SAC9F;QACD,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,WAAW,EACV,yFAAyF;SAC1F;QACD,WAAW,EAAE;YACZ,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,iEAAiE;SAC9E;KACD;IACD,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC;IAC9C,oBAAoB,EAAE,KAAK;CAC3B,CAAA;AAQD,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAAC;IAClC,IAAI,EAAE,MAAM;IACZ,WAAW,EACV,8nBAA8nB;IAC/nB,WAAW;IACX,gBAAgB;IAChB,iBAAiB,EAAE,IAAI;IACvB,mBAAmB,EAClB,qIAAqI;IACtI,QAAQ,EAAE,YAAY;IACtB,WAAW,EAAE,CAAC,YAAY,CAAC;IAC3B,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,KAAK;IAEtB,KAAK,CAAC,OAAO,CAAC,KAAgB,EAAE,OAAO;QACtC,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,uBAAuB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aAC5F,CAAA;QACF,CAAC;QACD,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAA;QAElC,MAAM,WAAW,GAAqB;YACrC,SAAS,EAAE,cAAc,CAAC,UAAU;YACpC,SAAS,EAAE,cAAc,CAAC,UAAU;YACpC,UAAU,EAAE,cAAc,CAAC,WAAW;SACtC,CAAA;QACD,IAAI,WAAW,CAAC,SAAS,KAAK,WAAW,CAAC,SAAS,EAAE,CAAC;YACrD,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,6DAA6D;aACpE,CAAA;QACF,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,cAAc,CAAC,IAAI,CAAC,CAAA;QACvE,MAAM,OAAO,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAA;QAEtE,OAAO,oBAAoB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YAC/C,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;gBAClE,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,CAAA;gBAC/D,IAAI,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,EAAE,OAAO,EAAE,KAAc,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAA;gBAExF,MAAM,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;gBACpE,OAAO;oBACN,OAAO,EAAE,IAAa;oBACtB,MAAM,EAAE,UAAU,cAAc,CAAC,IAAI,KAAK,MAAM,CAAC,YAAY,6BAA6B;oBAC1F,IAAI,EAAE;wBACL,IAAI,EAAE,cAAc,CAAC,IAAI;wBACzB,YAAY,EAAE,MAAM,CAAC,YAAY;wBACjC,SAAS,EAAE,IAAI;qBACf;iBACD,CAAA;YACF,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YACjD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;YAC9C,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,OAAO,EAAE,KAAc,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAA;YAExF,MAAM,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;YAC/C,OAAO;gBACN,OAAO,EAAE,IAAa;gBACtB,MAAM,EAAE,UAAU,QAAQ,KAAK,MAAM,CAAC,YAAY,iBAAiB;gBACnE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE;aAC3D,CAAA;QACF,CAAC,CAAC,CAAA;IACH,CAAC;CACD,CAAC,CAAA;AAEF,SAAS,oBAAoB,CAAC,OAAe,EAAE,KAAuB;IACrE,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAClD,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC5E,IAAI,YAAY,EAAE,CAAC;QAClB,OAAO;YACN,GAAG,KAAK;YACR,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC3C,CAAC,CAAC,KAAK,CAAC,SAAS;gBACjB,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;YACpE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;SAC5E,CAAA;IACF,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IACtE,IAAI,UAAU,EAAE,CAAC;QAChB,OAAO;YACN,GAAG,KAAK;YACR,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;gBAC3C,CAAC,CAAC,KAAK,CAAC,SAAS;gBACjB,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC;YAC3C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC;SACnD,CAAA;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED,SAAS,SAAS,CACjB,OAAe,EACf,QAA0B;IAE1B,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACrD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,OAAO;YACN,OAAO,EAAE,KAAK;YACd,KAAK,EACJ,iGAAiG;SAClG,CAAA;IACF,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAC5C,OAAO;YACN,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;YACpC,YAAY,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC;SAC9B,CAAA;IACF,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACnD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACzF,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAA;QACjE,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAA;QACnE,OAAO;YACN,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,6CAA6C,SAAS,QAAQ,UAAU,+DAA+D;SAC9I,CAAA;IACF,CAAC;IAED,OAAO;QACN,OAAO,EAAE,IAAI;QACb,OAAO,EACN,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;YAC5B,KAAK,CAAC,SAAS;YACf,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC;QACnD,YAAY,EAAE,CAAC;KACf,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize read-modify-write filesystem operations by resolved path within
|
|
3
|
+
* one SDK process. Distributed hosts must still assign one writer per file or
|
|
4
|
+
* provide a storage-level compare-and-swap primitive.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withFileMutationLock<T>(key: string, operation: () => Promise<T>): Promise<T>;
|
|
7
|
+
//# sourceMappingURL=file-mutation-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-mutation-lock.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/file-mutation-lock.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC3C,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const mutationTails = new Map();
|
|
2
|
+
/**
|
|
3
|
+
* Serialize read-modify-write filesystem operations by resolved path within
|
|
4
|
+
* one SDK process. Distributed hosts must still assign one writer per file or
|
|
5
|
+
* provide a storage-level compare-and-swap primitive.
|
|
6
|
+
*/
|
|
7
|
+
export async function withFileMutationLock(key, operation) {
|
|
8
|
+
const previous = mutationTails.get(key) ?? Promise.resolve();
|
|
9
|
+
let release = () => { };
|
|
10
|
+
const current = new Promise((resolve) => {
|
|
11
|
+
release = resolve;
|
|
12
|
+
});
|
|
13
|
+
const tail = previous.then(() => current);
|
|
14
|
+
mutationTails.set(key, tail);
|
|
15
|
+
await previous;
|
|
16
|
+
try {
|
|
17
|
+
return await operation();
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
release();
|
|
21
|
+
if (mutationTails.get(key) === tail)
|
|
22
|
+
mutationTails.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=file-mutation-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-mutation-lock.js","sourceRoot":"","sources":["../../../src/tools/builtins/file-mutation-lock.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,GAAG,EAAyB,CAAA;AAEtD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,GAAW,EACX,SAA2B;IAE3B,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;IAC5D,IAAI,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;IACtB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC7C,OAAO,GAAG,OAAO,CAAA;IAClB,CAAC,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAA;IACzC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE5B,MAAM,QAAQ,CAAA;IACd,IAAI,CAAC;QACJ,OAAO,MAAM,SAAS,EAAE,CAAA;IACzB,CAAC;YAAS,CAAC;QACV,OAAO,EAAE,CAAA;QACT,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAC/D,CAAC;AACF,CAAC"}
|
|
@@ -24,8 +24,8 @@ import { WriteFileTool } from './write-file.js';
|
|
|
24
24
|
// `code.claude.com/docs/en/tools-reference`) does NOT include `LS` —
|
|
25
25
|
// directory listing is canonical `Bash` + `Glob`. `search_tools` has no
|
|
26
26
|
// Claude analogue at all. Including these in the defaults gives the model
|
|
27
|
-
// tools that look right but degrade alignment. File extension
|
|
28
|
-
//
|
|
27
|
+
// tools that look right but degrade alignment. File extension uses canonical
|
|
28
|
+
// exact replacement against a unique tail or deterministic marker.
|
|
29
29
|
// Hosts that genuinely want LS/search can still register them explicitly.
|
|
30
30
|
export function getBuiltinTools() {
|
|
31
31
|
return [BashTool, EditTool, GlobTool, GrepTool, ReadFileTool, VerifyOutputsTool, WriteFileTool];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/builtins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAA;AAC/F,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,wEAAwE;AACxE,sEAAsE;AACtE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,qEAAqE;AACrE,wEAAwE;AACxE,0EAA0E;AAC1E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/builtins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAA;AAC/F,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,wEAAwE;AACxE,sEAAsE;AACtE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,qEAAqE;AACrE,wEAAwE;AACxE,0EAA0E;AAC1E,6EAA6E;AAC7E,mEAAmE;AACnE,0EAA0E;AAE1E,MAAM,UAAU,eAAe;IAC9B,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,aAAa,CAAC,CAAA;AAChG,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/write-file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"write-file.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/write-file.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,aAAa;;;EA8DxB,CAAA"}
|
|
@@ -1,67 +1,93 @@
|
|
|
1
|
-
import { access
|
|
2
|
-
import {
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { defineTool } from '../defineTool.js';
|
|
5
|
+
import { atomicWriteFile } from './atomic-write-file.js';
|
|
6
|
+
import { withFileMutationLock } from './file-mutation-lock.js';
|
|
5
7
|
const inputSchema = z
|
|
6
8
|
.object({
|
|
7
9
|
path: z
|
|
8
10
|
.string()
|
|
9
|
-
.
|
|
10
|
-
.describe('Relative path to the file to write (e.g. "outputs/report.md").
|
|
11
|
+
.refine((value) => value.trim().length > 0, 'Path must not be empty.')
|
|
12
|
+
.describe('Relative path to the file to write (e.g. "outputs/report.md"). Must not be empty.'),
|
|
11
13
|
content: z
|
|
12
14
|
.string()
|
|
13
|
-
.
|
|
14
|
-
.describe('Full file body to write. Required (use "" only for an intentionally empty file). The file is fully overwritten — pass the COMPLETE intended content for this bounded chunk, not a diff. Self-budget content under 12000 characters before calling; if the intended body is longer, write a smaller opening section here, then use `edit` with insertLine: "end" to extend the file section by section. Do NOT try to chain multiple `write` calls, since each one overwrites the previous.'),
|
|
15
|
-
newStr: z
|
|
16
|
-
.string()
|
|
17
|
-
.optional()
|
|
18
|
-
.describe('Alias for content. Useful for hosts that expose create/write operations as newStr. Self-budget this payload under 12000 characters before calling.'),
|
|
15
|
+
.describe('Complete bounded file body. Use "" only for an intentionally empty file. Keep under 12000 characters. For longer documents, include a deterministic marker such as {{CHUNK_001}} and advance it with exact edit calls.'),
|
|
19
16
|
})
|
|
20
|
-
.
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
.strict();
|
|
18
|
+
const modelInputSchema = {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
path: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Non-empty path to the file to write.',
|
|
24
|
+
},
|
|
25
|
+
content: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Complete bounded file body. May be empty only for an intentionally empty file.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['path', 'content'],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
};
|
|
23
33
|
export const WriteFileTool = defineTool({
|
|
24
34
|
name: 'write',
|
|
25
|
-
description: 'Writes a
|
|
35
|
+
description: 'Writes a complete bounded file body. Pass exactly path + content. If the file exists, read it first; prefer edit for targeted changes. Self-budget content under 12000 characters. For longer documents, write an opening with a deterministic marker such as {{CHUNK_001}}, then use exact edit calls that advance the marker one chunk at a time. Do not chain write calls because each overwrites the file.',
|
|
26
36
|
inputSchema,
|
|
37
|
+
modelInputSchema,
|
|
38
|
+
enforceModelInput: true,
|
|
39
|
+
validationErrorHint: 'Required shape: {"path":"file.md","content":"complete bounded file body"}.',
|
|
27
40
|
category: 'filesystem',
|
|
28
41
|
permissions: ['file_write'],
|
|
29
42
|
readOnly: false,
|
|
30
43
|
destructive: true,
|
|
31
44
|
concurrencySafe: false,
|
|
32
45
|
async execute(input, context) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
const parsed = inputSchema.safeParse(input);
|
|
47
|
+
if (!parsed.success) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
output: '',
|
|
51
|
+
error: `Invalid write input: ${parsed.error.issues.map((issue) => issue.message).join('; ')}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const canonicalInput = parsed.data;
|
|
55
|
+
const filePath = resolve(context.workingDirectory, canonicalInput.path);
|
|
56
|
+
const lockKey = `${context.sandbox ? 'sandbox' : 'local'}:${filePath}`;
|
|
57
|
+
return withFileMutationLock(lockKey, async () => {
|
|
58
|
+
if (context.sandbox) {
|
|
59
|
+
const sandboxExists = await sandboxFileExists(context, canonicalInput.path);
|
|
60
|
+
if (sandboxExists) {
|
|
61
|
+
const guard = enforceReadBeforeOverwrite(context, canonicalInput.path);
|
|
62
|
+
if (guard)
|
|
63
|
+
return guard;
|
|
64
|
+
}
|
|
65
|
+
await context.sandbox.writeFile(canonicalInput.path, canonicalInput.content);
|
|
66
|
+
context.fileReadTracker?.recordRead(canonicalInput.path);
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
output: `File written successfully: ${canonicalInput.path} (${canonicalInput.content.length} chars) [sandboxed]`,
|
|
70
|
+
data: {
|
|
71
|
+
path: canonicalInput.path,
|
|
72
|
+
size: canonicalInput.content.length,
|
|
73
|
+
sandboxed: true,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const localExists = await pathExists(filePath);
|
|
78
|
+
if (localExists) {
|
|
79
|
+
const guard = enforceReadBeforeOverwrite(context, filePath);
|
|
39
80
|
if (guard)
|
|
40
81
|
return guard;
|
|
41
82
|
}
|
|
42
|
-
await
|
|
43
|
-
context.fileReadTracker?.recordRead(
|
|
83
|
+
await atomicWriteFile(filePath, canonicalInput.content);
|
|
84
|
+
context.fileReadTracker?.recordRead(filePath);
|
|
44
85
|
return {
|
|
45
86
|
success: true,
|
|
46
|
-
output: `File written successfully: ${
|
|
47
|
-
data: { path:
|
|
87
|
+
output: `File written successfully: ${filePath} (${canonicalInput.content.length} chars)`,
|
|
88
|
+
data: { path: filePath, size: canonicalInput.content.length },
|
|
48
89
|
};
|
|
49
|
-
}
|
|
50
|
-
const filePath = resolve(context.workingDirectory, input.path);
|
|
51
|
-
const localExists = await pathExists(filePath);
|
|
52
|
-
if (localExists) {
|
|
53
|
-
const guard = enforceReadBeforeOverwrite(context, filePath);
|
|
54
|
-
if (guard)
|
|
55
|
-
return guard;
|
|
56
|
-
}
|
|
57
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
58
|
-
await writeFile(filePath, content, 'utf-8');
|
|
59
|
-
context.fileReadTracker?.recordRead(filePath);
|
|
60
|
-
return {
|
|
61
|
-
success: true,
|
|
62
|
-
output: `File written successfully: ${filePath} (${content.length} chars)`,
|
|
63
|
-
data: { path: filePath, size: content.length },
|
|
64
|
-
};
|
|
90
|
+
});
|
|
65
91
|
},
|
|
66
92
|
});
|
|
67
93
|
function enforceReadBeforeOverwrite(context, key) {
|