clanka 0.0.26 → 0.0.27

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.
Files changed (44) hide show
  1. package/dist/Agent.d.ts +5 -4
  2. package/dist/Agent.d.ts.map +1 -1
  3. package/dist/Agent.js.map +1 -1
  4. package/dist/Agent.test.js +7 -8
  5. package/dist/Agent.test.js.map +1 -1
  6. package/dist/AgentTools.d.ts +274 -2
  7. package/dist/AgentTools.d.ts.map +1 -1
  8. package/dist/AgentTools.js +37 -2
  9. package/dist/AgentTools.js.map +1 -1
  10. package/dist/ApplyPatch.d.ts.map +1 -1
  11. package/dist/ApplyPatch.js +6 -2
  12. package/dist/ApplyPatch.js.map +1 -1
  13. package/dist/ApplyPatch.test.js +39 -0
  14. package/dist/ApplyPatch.test.js.map +1 -1
  15. package/dist/Codex.d.ts +1 -1
  16. package/dist/CodexAuth.d.ts +4 -4
  17. package/dist/Copilot.d.ts +1 -1
  18. package/dist/CopilotAuth.d.ts +3 -3
  19. package/dist/ExaSearch.d.ts +37 -0
  20. package/dist/ExaSearch.d.ts.map +1 -0
  21. package/dist/ExaSearch.js +56 -0
  22. package/dist/ExaSearch.js.map +1 -0
  23. package/dist/McpClient.d.ts +35 -0
  24. package/dist/McpClient.d.ts.map +1 -0
  25. package/dist/McpClient.js +51 -0
  26. package/dist/McpClient.js.map +1 -0
  27. package/dist/WebToMarkdown.d.ts +22 -0
  28. package/dist/WebToMarkdown.d.ts.map +1 -0
  29. package/dist/WebToMarkdown.js +66 -0
  30. package/dist/WebToMarkdown.js.map +1 -0
  31. package/package.json +13 -10
  32. package/src/Agent.test.ts +15 -9
  33. package/src/Agent.ts +11 -5
  34. package/src/AgentTools.ts +49 -1
  35. package/src/ApplyPatch.test.ts +44 -0
  36. package/src/ApplyPatch.ts +6 -2
  37. package/src/ExaSearch.ts +78 -0
  38. package/src/McpClient.ts +81 -0
  39. package/src/WebToMarkdown.ts +87 -0
  40. package/dist/AgentTools.test.d.ts +0 -2
  41. package/dist/AgentTools.test.d.ts.map +0 -1
  42. package/dist/AgentTools.test.js +0 -714
  43. package/dist/AgentTools.test.js.map +0 -1
  44. package/src/AgentTools.test.ts +0 -954
@@ -1,714 +0,0 @@
1
- import { tmpdir } from "node:os";
2
- import { join } from "node:path";
3
- import { NodeFileSystem, NodeServices } from "@effect/platform-node";
4
- import { Deferred, Effect, FileSystem, Stream } from "effect";
5
- import { describe, it } from "@effect/vitest";
6
- import { expect } from "vitest";
7
- import { AgentToolHandlers, AgentTools, CurrentDirectory, makeContextNoop, TaskCompleteDeferred, } from "./AgentTools.js";
8
- import { Executor } from "./Executor.js";
9
- import { ToolkitRenderer } from "./ToolkitRenderer.js";
10
- const makeTempRoot = (prefix) => Effect.gen(function* () {
11
- const fs = yield* FileSystem.FileSystem;
12
- return yield* fs.makeTempDirectoryScoped({
13
- directory: tmpdir(),
14
- prefix,
15
- });
16
- });
17
- describe("AgentTools", () => {
18
- it.effect("renders the tool signatures", () => Effect.gen(function* () {
19
- const renderer = yield* ToolkitRenderer;
20
- const output = renderer.render(AgentTools);
21
- expect(output).toContain("/** Read a file and optionally filter the lines to return. Returns null if the file doesn't exist. */");
22
- expect(output).toContain("declare function readFile(options: {");
23
- expect(output).toContain("readonly path: string;");
24
- expect(output).toContain("readonly startLine?: number | undefined;");
25
- expect(output).toContain("readonly endLine?: number | undefined;");
26
- expect(output).toContain("readonly noIgnore?: boolean | undefined;");
27
- expect(output).toContain("/** Apply a git diff / unified diff patch, or a wrapped apply_patch patch, across one or more files. */");
28
- expect(output).toContain("declare function applyPatch(patch: string): Promise<string>");
29
- expect(output).not.toContain("declare function python(");
30
- }).pipe(Effect.provide([
31
- AgentToolHandlers,
32
- Executor.layer,
33
- ToolkitRenderer.layer,
34
- ]), Effect.provide(NodeServices.layer), Effect.provideService(CurrentDirectory, process.cwd()), Effect.provideServiceEffect(TaskCompleteDeferred, Deferred.make())));
35
- it.effect("applies multi-file patches with add, move, and delete", () => Effect.gen(function* () {
36
- const fs = yield* FileSystem.FileSystem;
37
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-");
38
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
39
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
40
- yield* fs.writeFileString(join(tempRoot, "obsolete.txt"), "remove me\n");
41
- const executor = yield* Executor;
42
- const tools = yield* AgentTools;
43
- const output = yield* executor
44
- .execute({
45
- tools,
46
- script: [
47
- "const output = await applyPatch(`",
48
- "diff --git a/src/app.txt b/src/main.txt",
49
- "similarity index 100%",
50
- "rename from src/app.txt",
51
- "rename to src/main.txt",
52
- "--- a/src/app.txt",
53
- "+++ b/src/main.txt",
54
- "@@ -1 +1 @@",
55
- "-old",
56
- "+new",
57
- "diff --git a/obsolete.txt b/obsolete.txt",
58
- "deleted file mode 100644",
59
- "--- a/obsolete.txt",
60
- "+++ /dev/null",
61
- "diff --git a/dev/null b/notes/hello.txt",
62
- "new file mode 100644",
63
- "--- /dev/null",
64
- "+++ b/notes/hello.txt",
65
- "@@ -0,0 +1 @@",
66
- "+hello",
67
- "`)",
68
- "console.log(output)",
69
- ].join("\n"),
70
- })
71
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
72
- expect(output).toContain("A notes/hello.txt");
73
- expect(output).toContain("M src/main.txt");
74
- expect(output).toContain("D obsolete.txt");
75
- expect(yield* fs.readFileString(join(tempRoot, "notes", "hello.txt"))).toBe("hello\n");
76
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("new\n");
77
- yield* Effect.flip(fs.readFileString(join(tempRoot, "obsolete.txt")));
78
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
79
- }).pipe(Effect.provide([
80
- AgentToolHandlers,
81
- Executor.layer,
82
- ToolkitRenderer.layer,
83
- ]), Effect.provide(NodeServices.layer)));
84
- it.effect("plans later hunks against in-memory file state", () => Effect.gen(function* () {
85
- const fs = yield* FileSystem.FileSystem;
86
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-state-");
87
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
88
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
89
- const executor = yield* Executor;
90
- const tools = yield* AgentTools;
91
- const output = yield* executor
92
- .execute({
93
- tools,
94
- script: [
95
- "const output = await applyPatch(`",
96
- "diff --git a/dev/null b/notes/hello.txt",
97
- "new file mode 100644",
98
- "--- /dev/null",
99
- "+++ b/notes/hello.txt",
100
- "@@ -0,0 +1 @@",
101
- "+hello",
102
- "diff --git a/notes/hello.txt b/notes/hello.txt",
103
- "--- a/notes/hello.txt",
104
- "+++ b/notes/hello.txt",
105
- "@@ -1 +1 @@",
106
- "-hello",
107
- "+hello again",
108
- "diff --git a/src/app.txt b/src/main.txt",
109
- "similarity index 100%",
110
- "rename from src/app.txt",
111
- "rename to src/main.txt",
112
- "--- a/src/app.txt",
113
- "+++ b/src/main.txt",
114
- "@@ -1 +1 @@",
115
- "-old",
116
- "+new",
117
- "diff --git a/src/main.txt b/src/main.txt",
118
- "--- a/src/main.txt",
119
- "+++ b/src/main.txt",
120
- "@@ -1 +1 @@",
121
- "-new",
122
- "+newer",
123
- "`)",
124
- "console.log(output)",
125
- ].join("\n"),
126
- })
127
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
128
- expect(output).toContain("A notes/hello.txt");
129
- expect(output).toContain("M notes/hello.txt");
130
- expect(output).toContain("M src/main.txt");
131
- expect(yield* fs.readFileString(join(tempRoot, "notes", "hello.txt"))).toBe("hello again\n");
132
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("newer\n");
133
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
134
- }).pipe(Effect.provide([
135
- AgentToolHandlers,
136
- Executor.layer,
137
- ToolkitRenderer.layer,
138
- ]), Effect.provide(NodeServices.layer)));
139
- it.effect("applies wrapped apply_patch patches", () => Effect.gen(function* () {
140
- const fs = yield* FileSystem.FileSystem;
141
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-wrapped-");
142
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
143
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
144
- yield* fs.writeFileString(join(tempRoot, "obsolete.txt"), "remove me\n");
145
- const executor = yield* Executor;
146
- const tools = yield* AgentTools;
147
- const output = yield* executor
148
- .execute({
149
- tools,
150
- script: [
151
- "const output = await applyPatch(`",
152
- "*** Begin Patch",
153
- "*** Update File: src/app.txt",
154
- "*** Move to: src/main.txt",
155
- "@@",
156
- "-old",
157
- "+new",
158
- "*** Delete File: obsolete.txt",
159
- "*** Add File: notes/hello.txt",
160
- "+hello",
161
- "*** End Patch",
162
- "`)",
163
- "console.log(output)",
164
- ].join("\n"),
165
- })
166
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
167
- expect(output).toContain("A notes/hello.txt");
168
- expect(output).toContain("M src/main.txt");
169
- expect(output).toContain("D obsolete.txt");
170
- expect(yield* fs.readFileString(join(tempRoot, "notes", "hello.txt"))).toBe("hello\n");
171
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("new\n");
172
- yield* Effect.flip(fs.readFileString(join(tempRoot, "obsolete.txt")));
173
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
174
- }).pipe(Effect.provide([
175
- AgentToolHandlers,
176
- Executor.layer,
177
- ToolkitRenderer.layer,
178
- ]), Effect.provide(NodeServices.layer)));
179
- it.effect("applies larger wrapped apply_patch patches across multiple files", () => Effect.gen(function* () {
180
- const fs = yield* FileSystem.FileSystem;
181
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-wrapped-large-");
182
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
183
- yield* fs.makeDirectory(join(tempRoot, "docs"), { recursive: true });
184
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "alpha\nbeta\n");
185
- yield* fs.writeFileString(join(tempRoot, "src", "config.json"), '{"enabled":false}\n');
186
- yield* fs.writeFileString(join(tempRoot, "docs", "old.md"), "legacy\n");
187
- yield* fs.writeFileString(join(tempRoot, "README.md"), "# Title\nOld intro\n");
188
- const executor = yield* Executor;
189
- const tools = yield* AgentTools;
190
- const output = yield* executor
191
- .execute({
192
- tools,
193
- script: [
194
- "const output = await applyPatch(`",
195
- "*** Begin Patch",
196
- "",
197
- "*** Update File: src/app.txt",
198
- "*** Move to: src/main.txt",
199
- "@@",
200
- " alpha",
201
- "-beta",
202
- "+gamma",
203
- "",
204
- "*** Update File: src/config.json",
205
- "@@",
206
- '-{"enabled":false}',
207
- '+{"enabled":true}',
208
- "",
209
- "*** Update File: README.md",
210
- "@@",
211
- " # Title",
212
- "-Old intro",
213
- "+New intro",
214
- "+More details",
215
- "",
216
- "*** Delete File: docs/old.md",
217
- "",
218
- "*** Add File: docs/new.md",
219
- "+# Docs",
220
- "+",
221
- "+Updated",
222
- "",
223
- "*** Add File: notes/todo.txt",
224
- "+one",
225
- "+two",
226
- "*** End Patch",
227
- "`)",
228
- "console.log(output)",
229
- ].join("\n"),
230
- })
231
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
232
- expect(output).toContain("M src/main.txt");
233
- expect(output).toContain("M src/config.json");
234
- expect(output).toContain("M README.md");
235
- expect(output).toContain("D docs/old.md");
236
- expect(output).toContain("A docs/new.md");
237
- expect(output).toContain("A notes/todo.txt");
238
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("alpha\ngamma\n");
239
- expect(yield* fs.readFileString(join(tempRoot, "src", "config.json"))).toBe('{"enabled":true}\n');
240
- expect(yield* fs.readFileString(join(tempRoot, "README.md"))).toBe("# Title\nNew intro\nMore details\n");
241
- expect(yield* fs.readFileString(join(tempRoot, "docs", "new.md"))).toBe("# Docs\n\nUpdated\n");
242
- expect(yield* fs.readFileString(join(tempRoot, "notes", "todo.txt"))).toBe("one\ntwo\n");
243
- yield* Effect.flip(fs.readFileString(join(tempRoot, "docs", "old.md")));
244
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
245
- }).pipe(Effect.provide([
246
- AgentToolHandlers,
247
- Executor.layer,
248
- ToolkitRenderer.layer,
249
- ]), Effect.provide(NodeServices.layer)));
250
- it.effect("chains wrapped apply_patch updates through in-memory renamed state", () => Effect.gen(function* () {
251
- const fs = yield* FileSystem.FileSystem;
252
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-wrapped-state-");
253
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
254
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
255
- const executor = yield* Executor;
256
- const tools = yield* AgentTools;
257
- const output = yield* executor
258
- .execute({
259
- tools,
260
- script: [
261
- "const output = await applyPatch(`",
262
- "*** Begin Patch",
263
- "*** Update File: src/app.txt",
264
- "*** Move to: src/main.txt",
265
- "@@",
266
- "-old",
267
- "+new",
268
- "*** Update File: src/main.txt",
269
- "@@",
270
- "-new",
271
- "+newer",
272
- "*** Add File: notes/hello.txt",
273
- "+hello",
274
- "*** Update File: notes/hello.txt",
275
- "@@",
276
- "-hello",
277
- "+hello again",
278
- "*** End Patch",
279
- "`)",
280
- "console.log(output)",
281
- ].join("\n"),
282
- })
283
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
284
- expect(output).toContain("M src/main.txt");
285
- expect(output).toContain("A notes/hello.txt");
286
- expect(output).toContain("M notes/hello.txt");
287
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("newer\n");
288
- expect(yield* fs.readFileString(join(tempRoot, "notes", "hello.txt"))).toBe("hello again\n");
289
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
290
- }).pipe(Effect.provide([
291
- AgentToolHandlers,
292
- Executor.layer,
293
- ToolkitRenderer.layer,
294
- ]), Effect.provide(NodeServices.layer)));
295
- it.effect("applies wrapped apply_patch patches with multiple hunks", () => Effect.gen(function* () {
296
- const fs = yield* FileSystem.FileSystem;
297
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-wrapped-hunks-");
298
- yield* fs.writeFileString(join(tempRoot, "multi.txt"), "line1\nline2\nline3\nline4\n");
299
- const executor = yield* Executor;
300
- const tools = yield* AgentTools;
301
- const output = yield* executor
302
- .execute({
303
- tools,
304
- script: [
305
- "const output = await applyPatch(`",
306
- "*** Begin Patch",
307
- "*** Update File: multi.txt",
308
- "@@",
309
- "-line2",
310
- "+changed2",
311
- "@@",
312
- "-line4",
313
- "+changed4",
314
- "*** End Patch",
315
- "`)",
316
- "console.log(output)",
317
- ].join("\n"),
318
- })
319
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
320
- expect(output).toContain("M multi.txt");
321
- expect(yield* fs.readFileString(join(tempRoot, "multi.txt"))).toBe("line1\nchanged2\nline3\nchanged4\n");
322
- }).pipe(Effect.provide([
323
- AgentToolHandlers,
324
- Executor.layer,
325
- ToolkitRenderer.layer,
326
- ]), Effect.provide(NodeServices.layer)));
327
- it.effect("applies realistic multi-file git patches with repeated multi-hunk updates", () => Effect.gen(function* () {
328
- const fs = yield* FileSystem.FileSystem;
329
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-realistic-");
330
- yield* fs.makeDirectory(join(tempRoot, "dist", "internal"), {
331
- recursive: true,
332
- });
333
- const initial = [
334
- "if (reasoningStarted && !textStarted) {",
335
- " controller.enqueue({",
336
- ' type: "reasoning-end",',
337
- " id: reasoningId || generateId()",
338
- " });",
339
- "}",
340
- "",
341
- "separator",
342
- "",
343
- "if (reasoningStarted) {",
344
- " controller.enqueue({",
345
- ' type: "reasoning-end",',
346
- " id: reasoningId || generateId()",
347
- " });",
348
- "}",
349
- "",
350
- ].join("\n");
351
- for (const path of [
352
- join(tempRoot, "dist", "index.js"),
353
- join(tempRoot, "dist", "index.mjs"),
354
- join(tempRoot, "dist", "internal", "index.js"),
355
- join(tempRoot, "dist", "internal", "index.mjs"),
356
- ]) {
357
- yield* fs.writeFileString(path, initial);
358
- }
359
- const executor = yield* Executor;
360
- const tools = yield* AgentTools;
361
- const output = yield* executor
362
- .execute({
363
- tools,
364
- script: [
365
- "const output = await applyPatch(`",
366
- "diff --git a/dist/index.js b/dist/index.js",
367
- "index f33510a..e887a60 100644",
368
- "--- a/dist/index.js",
369
- "+++ b/dist/index.js",
370
- "@@ -1,7 +1,12 @@",
371
- " if (reasoningStarted && !textStarted) {",
372
- " controller.enqueue({",
373
- ' type: "reasoning-end",',
374
- "- id: reasoningId || generateId()",
375
- "+ id: reasoningId || generateId(),",
376
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
377
- "+ openrouter: {",
378
- "+ reasoning_details: accumulatedReasoningDetails",
379
- "+ }",
380
- "+ } : undefined",
381
- " });",
382
- " }",
383
- "@@ -10,7 +15,12 @@",
384
- " if (reasoningStarted) {",
385
- " controller.enqueue({",
386
- ' type: "reasoning-end",',
387
- "- id: reasoningId || generateId()",
388
- "+ id: reasoningId || generateId(),",
389
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
390
- "+ openrouter: {",
391
- "+ reasoning_details: accumulatedReasoningDetails",
392
- "+ }",
393
- "+ } : undefined",
394
- " });",
395
- " }",
396
- "diff --git a/dist/index.mjs b/dist/index.mjs",
397
- "index 8a68833..6310cb8 100644",
398
- "--- a/dist/index.mjs",
399
- "+++ b/dist/index.mjs",
400
- "@@ -1,7 +1,12 @@",
401
- " if (reasoningStarted && !textStarted) {",
402
- " controller.enqueue({",
403
- ' type: "reasoning-end",',
404
- "- id: reasoningId || generateId()",
405
- "+ id: reasoningId || generateId(),",
406
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
407
- "+ openrouter: {",
408
- "+ reasoning_details: accumulatedReasoningDetails",
409
- "+ }",
410
- "+ } : undefined",
411
- " });",
412
- " }",
413
- "@@ -10,7 +15,12 @@",
414
- " if (reasoningStarted) {",
415
- " controller.enqueue({",
416
- ' type: "reasoning-end",',
417
- "- id: reasoningId || generateId()",
418
- "+ id: reasoningId || generateId(),",
419
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
420
- "+ openrouter: {",
421
- "+ reasoning_details: accumulatedReasoningDetails",
422
- "+ }",
423
- "+ } : undefined",
424
- " });",
425
- " }",
426
- "diff --git a/dist/internal/index.js b/dist/internal/index.js",
427
- "index d40fa66..8dd86d1 100644",
428
- "--- a/dist/internal/index.js",
429
- "+++ b/dist/internal/index.js",
430
- "@@ -1,7 +1,12 @@",
431
- " if (reasoningStarted && !textStarted) {",
432
- " controller.enqueue({",
433
- ' type: "reasoning-end",',
434
- "- id: reasoningId || generateId()",
435
- "+ id: reasoningId || generateId(),",
436
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
437
- "+ openrouter: {",
438
- "+ reasoning_details: accumulatedReasoningDetails",
439
- "+ }",
440
- "+ } : undefined",
441
- " });",
442
- " }",
443
- "@@ -10,7 +15,12 @@",
444
- " if (reasoningStarted) {",
445
- " controller.enqueue({",
446
- ' type: "reasoning-end",',
447
- "- id: reasoningId || generateId()",
448
- "+ id: reasoningId || generateId(),",
449
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
450
- "+ openrouter: {",
451
- "+ reasoning_details: accumulatedReasoningDetails",
452
- "+ }",
453
- "+ } : undefined",
454
- " });",
455
- " }",
456
- "diff --git a/dist/internal/index.mjs b/dist/internal/index.mjs",
457
- "index b0ed9d1..5695930 100644",
458
- "--- a/dist/internal/index.mjs",
459
- "+++ b/dist/internal/index.mjs",
460
- "@@ -1,7 +1,12 @@",
461
- " if (reasoningStarted && !textStarted) {",
462
- " controller.enqueue({",
463
- ' type: "reasoning-end",',
464
- "- id: reasoningId || generateId()",
465
- "+ id: reasoningId || generateId(),",
466
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
467
- "+ openrouter: {",
468
- "+ reasoning_details: accumulatedReasoningDetails",
469
- "+ }",
470
- "+ } : undefined",
471
- " });",
472
- " }",
473
- "@@ -10,7 +15,12 @@",
474
- " if (reasoningStarted) {",
475
- " controller.enqueue({",
476
- ' type: "reasoning-end",',
477
- "- id: reasoningId || generateId()",
478
- "+ id: reasoningId || generateId(),",
479
- "+ providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
480
- "+ openrouter: {",
481
- "+ reasoning_details: accumulatedReasoningDetails",
482
- "+ }",
483
- "+ } : undefined",
484
- " });",
485
- " }",
486
- "`)",
487
- "console.log(output)",
488
- ].join("\n"),
489
- })
490
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
491
- expect(output).toContain("M dist/index.js");
492
- expect(output).toContain("M dist/index.mjs");
493
- expect(output).toContain("M dist/internal/index.js");
494
- expect(output).toContain("M dist/internal/index.mjs");
495
- const expected = [
496
- "if (reasoningStarted && !textStarted) {",
497
- " controller.enqueue({",
498
- ' type: "reasoning-end",',
499
- " id: reasoningId || generateId(),",
500
- " providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
501
- " openrouter: {",
502
- " reasoning_details: accumulatedReasoningDetails",
503
- " }",
504
- " } : undefined",
505
- " });",
506
- "}",
507
- "",
508
- "separator",
509
- "",
510
- "if (reasoningStarted) {",
511
- " controller.enqueue({",
512
- ' type: "reasoning-end",',
513
- " id: reasoningId || generateId(),",
514
- " providerMetadata: accumulatedReasoningDetails.length > 0 ? {",
515
- " openrouter: {",
516
- " reasoning_details: accumulatedReasoningDetails",
517
- " }",
518
- " } : undefined",
519
- " });",
520
- "}",
521
- "",
522
- ].join("\n");
523
- for (const path of [
524
- join(tempRoot, "dist", "index.js"),
525
- join(tempRoot, "dist", "index.mjs"),
526
- join(tempRoot, "dist", "internal", "index.js"),
527
- join(tempRoot, "dist", "internal", "index.mjs"),
528
- ]) {
529
- expect(yield* fs.readFileString(path)).toBe(expected);
530
- }
531
- }).pipe(Effect.provide([
532
- AgentToolHandlers,
533
- Executor.layer,
534
- ToolkitRenderer.layer,
535
- ]), Effect.provide(NodeServices.layer)));
536
- it.effect("fails multi-file git patches atomically", () => Effect.gen(function* () {
537
- const fs = yield* FileSystem.FileSystem;
538
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-git-fail-");
539
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
540
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
541
- yield* fs.writeFileString(join(tempRoot, "keep.txt"), "keep\n");
542
- const executor = yield* Executor;
543
- const tools = yield* AgentTools;
544
- const output = yield* executor
545
- .execute({
546
- tools,
547
- script: [
548
- "await applyPatch(`",
549
- "diff --git a/src/app.txt b/src/main.txt",
550
- "similarity index 100%",
551
- "rename from src/app.txt",
552
- "rename to src/main.txt",
553
- "--- a/src/app.txt",
554
- "+++ b/src/main.txt",
555
- "@@ -1 +1 @@",
556
- "-missing",
557
- "+new",
558
- "diff --git a/keep.txt b/keep.txt",
559
- "deleted file mode 100644",
560
- "--- a/keep.txt",
561
- "+++ /dev/null",
562
- "diff --git a/dev/null b/notes/hello.txt",
563
- "new file mode 100644",
564
- "--- /dev/null",
565
- "+++ b/notes/hello.txt",
566
- "@@ -0,0 +1 @@",
567
- "+hello",
568
- "`)",
569
- ].join("\n"),
570
- })
571
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
572
- expect(output).toContain("applyPatch verification failed");
573
- expect(output).toContain("Failed to find expected lines");
574
- expect(yield* fs.readFileString(join(tempRoot, "src", "app.txt"))).toBe("old\n");
575
- expect(yield* fs.readFileString(join(tempRoot, "keep.txt"))).toBe("keep\n");
576
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "main.txt")));
577
- yield* Effect.flip(fs.readFileString(join(tempRoot, "notes", "hello.txt")));
578
- }).pipe(Effect.provide([
579
- AgentToolHandlers,
580
- Executor.layer,
581
- ToolkitRenderer.layer,
582
- ]), Effect.provide(NodeServices.layer)));
583
- it.effect("fails wrapped apply_patch patches atomically", () => Effect.gen(function* () {
584
- const fs = yield* FileSystem.FileSystem;
585
- const tempRoot = yield* makeTempRoot("clanka-apply-patch-wrapped-fail-");
586
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
587
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "old\n");
588
- yield* fs.writeFileString(join(tempRoot, "keep.txt"), "keep\n");
589
- const executor = yield* Executor;
590
- const tools = yield* AgentTools;
591
- const output = yield* executor
592
- .execute({
593
- tools,
594
- script: [
595
- "await applyPatch(`",
596
- "*** Begin Patch",
597
- "*** Update File: src/app.txt",
598
- "@@",
599
- "-missing",
600
- "+new",
601
- "*** Delete File: keep.txt",
602
- "*** Add File: notes/hello.txt",
603
- "+hello",
604
- "*** End Patch",
605
- "`)",
606
- ].join("\n"),
607
- })
608
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
609
- expect(output).toContain("applyPatch verification failed");
610
- expect(output).toContain("Failed to find expected lines");
611
- expect(yield* fs.readFileString(join(tempRoot, "src", "app.txt"))).toBe("old\n");
612
- expect(yield* fs.readFileString(join(tempRoot, "keep.txt"))).toBe("keep\n");
613
- yield* Effect.flip(fs.readFileString(join(tempRoot, "notes", "hello.txt")));
614
- }).pipe(Effect.provide([
615
- AgentToolHandlers,
616
- Executor.layer,
617
- ToolkitRenderer.layer,
618
- ]), Effect.provide(NodeServices.layer)));
619
- it.effect("renames a file", () => Effect.gen(function* () {
620
- const fs = yield* FileSystem.FileSystem;
621
- const tempRoot = yield* makeTempRoot("clanka-rename-file-");
622
- yield* fs.makeDirectory(join(tempRoot, "src"), { recursive: true });
623
- yield* fs.writeFileString(join(tempRoot, "src", "app.txt"), "hello\n");
624
- const executor = yield* Executor;
625
- const tools = yield* AgentTools;
626
- yield* executor
627
- .execute({
628
- tools,
629
- script: [
630
- "await renameFile({",
631
- ' from: "src/app.txt",',
632
- ' to: "src/main.txt",',
633
- "})",
634
- 'console.log("renamed")',
635
- ].join("\n"),
636
- })
637
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
638
- expect(yield* fs.readFileString(join(tempRoot, "src", "main.txt"))).toBe("hello\n");
639
- yield* Effect.flip(fs.readFileString(join(tempRoot, "src", "app.txt")));
640
- }).pipe(Effect.provide([
641
- AgentToolHandlers,
642
- Executor.layer,
643
- ToolkitRenderer.layer,
644
- NodeFileSystem.layer,
645
- ]), Effect.provide(NodeServices.layer)));
646
- it.effect("rg respects ignore files by default and can disable them", () => Effect.gen(function* () {
647
- const fs = yield* FileSystem.FileSystem;
648
- const tempRoot = yield* makeTempRoot("clanka-rg-ignore-");
649
- yield* fs.writeFileString(join(tempRoot, ".ignore"), "ignored.txt\n");
650
- yield* fs.writeFileString(join(tempRoot, "visible.txt"), "match visible\n");
651
- yield* fs.writeFileString(join(tempRoot, "ignored.txt"), "match ignored\n");
652
- const executor = yield* Executor;
653
- const tools = yield* AgentTools;
654
- const defaultOutput = yield* executor
655
- .execute({
656
- tools,
657
- script: [
658
- 'const output = await rg({ pattern: "match" })',
659
- "console.log(output)",
660
- ].join("\n"),
661
- })
662
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
663
- expect(defaultOutput).toContain("visible.txt:1:match visible");
664
- expect(defaultOutput).not.toContain("ignored.txt:1:match ignored");
665
- const noIgnoreOutput = yield* executor
666
- .execute({
667
- tools,
668
- script: [
669
- 'const output = await rg({ pattern: "match", noIgnore: true })',
670
- "console.log(output)",
671
- ].join("\n"),
672
- })
673
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
674
- expect(noIgnoreOutput).toContain("visible.txt:1:match visible");
675
- expect(noIgnoreOutput).toContain("ignored.txt:1:match ignored");
676
- }).pipe(Effect.provide([
677
- AgentToolHandlers,
678
- Executor.layer,
679
- ToolkitRenderer.layer,
680
- ]), Effect.provide(NodeServices.layer)));
681
- it.effect("rg combines noIgnore with glob and maxLines", () => Effect.gen(function* () {
682
- const fs = yield* FileSystem.FileSystem;
683
- const tempRoot = yield* makeTempRoot("clanka-rg-no-ignore-glob-");
684
- yield* fs.writeFileString(join(tempRoot, ".ignore"), "ignored-*.txt\n");
685
- yield* fs.writeFileString(join(tempRoot, "ignored-a.txt"), "needle one\n");
686
- yield* fs.writeFileString(join(tempRoot, "ignored-b.txt"), "needle two\n");
687
- yield* fs.writeFileString(join(tempRoot, "visible.txt"), "needle visible\n");
688
- const executor = yield* Executor;
689
- const tools = yield* AgentTools;
690
- const output = yield* executor
691
- .execute({
692
- tools,
693
- script: [
694
- "const output = await rg({",
695
- ' pattern: "needle",',
696
- ' glob: "ignored-*.txt",',
697
- " noIgnore: true,",
698
- " maxLines: 1,",
699
- "})",
700
- "console.log(output)",
701
- ].join("\n"),
702
- })
703
- .pipe(Stream.mkString, Effect.provideServices(makeContextNoop(tempRoot)));
704
- const lines = output.trimEnd().split("\n");
705
- expect(lines).toHaveLength(1);
706
- expect(lines[0]).toMatch(/ignored-[ab]\.txt:1:needle (one|two)/);
707
- expect(output).not.toContain("visible.txt:1:needle visible");
708
- }).pipe(Effect.provide([
709
- AgentToolHandlers,
710
- Executor.layer,
711
- ToolkitRenderer.layer,
712
- ]), Effect.provide(NodeServices.layer)));
713
- });
714
- //# sourceMappingURL=AgentTools.test.js.map