zeitlich 0.2.46 → 0.2.47

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 (83) hide show
  1. package/README.md +64 -6
  2. package/dist/{activities-CyeiqK_f.d.cts → activities-CPwKoUlD.d.cts} +3 -3
  3. package/dist/{activities-Bm4TLTid.d.ts → activities-DlaBxNID.d.ts} +3 -3
  4. package/dist/adapters/thread/anthropic/index.cjs +105 -6
  5. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  6. package/dist/adapters/thread/anthropic/index.d.cts +48 -9
  7. package/dist/adapters/thread/anthropic/index.d.ts +48 -9
  8. package/dist/adapters/thread/anthropic/index.js +104 -7
  9. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  10. package/dist/adapters/thread/anthropic/workflow.cjs +38 -22
  11. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  12. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
  13. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
  14. package/dist/adapters/thread/anthropic/workflow.js +38 -22
  15. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  16. package/dist/adapters/thread/google-genai/index.d.cts +6 -5
  17. package/dist/adapters/thread/google-genai/index.d.ts +6 -5
  18. package/dist/adapters/thread/google-genai/workflow.cjs +38 -22
  19. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  20. package/dist/adapters/thread/google-genai/workflow.d.cts +7 -5
  21. package/dist/adapters/thread/google-genai/workflow.d.ts +7 -5
  22. package/dist/adapters/thread/google-genai/workflow.js +38 -22
  23. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  24. package/dist/adapters/thread/langchain/index.d.cts +6 -5
  25. package/dist/adapters/thread/langchain/index.d.ts +6 -5
  26. package/dist/adapters/thread/langchain/workflow.cjs +38 -22
  27. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  28. package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
  29. package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
  30. package/dist/adapters/thread/langchain/workflow.js +38 -22
  31. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  32. package/dist/{cold-store-CFHwemBJ.d.ts → cold-store-BDgJpwLI.d.ts} +8 -11
  33. package/dist/{cold-store-BC5L5Z8A.d.cts → cold-store-Z2wvK2cV.d.cts} +8 -11
  34. package/dist/index.cjs +264 -90
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +21 -9
  37. package/dist/index.d.ts +21 -9
  38. package/dist/index.js +265 -93
  39. package/dist/index.js.map +1 -1
  40. package/dist/proxy-CDh3Rsa7.d.cts +40 -0
  41. package/dist/proxy-Du8ggERu.d.ts +40 -0
  42. package/dist/{thread-manager-D33SUmZa.d.cts → thread-manager-BjoYYXgd.d.cts} +2 -2
  43. package/dist/{thread-manager-9tezUcLW.d.cts → thread-manager-D8zKNFZ9.d.cts} +2 -2
  44. package/dist/{thread-manager-B-zy3xrs.d.ts → thread-manager-DtHYws2F.d.ts} +2 -2
  45. package/dist/{thread-manager-DduoSkvJ.d.ts → thread-manager-Dw96FKH1.d.ts} +2 -2
  46. package/dist/{types-oxt8GN97.d.cts → types-BMJrsHo0.d.cts} +1 -1
  47. package/dist/{types-L5bvbF-n.d.ts → types-CtdOquo3.d.ts} +1 -1
  48. package/dist/{types-CnuN9T6t.d.cts → types-DNEl5uxQ.d.cts} +16 -0
  49. package/dist/{types-CwN6_tAL.d.ts → types-qQVZfhoT.d.ts} +16 -0
  50. package/dist/{workflow-DIaIV7L2.d.cts → workflow-BH9ImDGq.d.cts} +17 -2
  51. package/dist/{workflow-B1TOcHbt.d.ts → workflow-Cdw3-RNB.d.ts} +17 -2
  52. package/dist/workflow.cjs +33 -3
  53. package/dist/workflow.cjs.map +1 -1
  54. package/dist/workflow.d.cts +2 -2
  55. package/dist/workflow.d.ts +2 -2
  56. package/dist/workflow.js +33 -4
  57. package/dist/workflow.js.map +1 -1
  58. package/package.json +9 -3
  59. package/src/adapters/thread/anthropic/activities.ts +18 -11
  60. package/src/adapters/thread/anthropic/index.ts +8 -0
  61. package/src/adapters/thread/anthropic/model-invoker.test.ts +110 -0
  62. package/src/adapters/thread/anthropic/model-invoker.ts +26 -5
  63. package/src/adapters/thread/anthropic/prompt-cache.test.ts +134 -0
  64. package/src/adapters/thread/anthropic/prompt-cache.ts +163 -0
  65. package/src/adapters/thread/anthropic/proxy.ts +1 -0
  66. package/src/adapters/thread/google-genai/proxy.ts +1 -0
  67. package/src/adapters/thread/langchain/proxy.ts +1 -0
  68. package/src/index.ts +1 -1
  69. package/src/lib/subagent/define.ts +1 -0
  70. package/src/lib/subagent/handler.ts +11 -2
  71. package/src/lib/subagent/subagent.integration.test.ts +139 -0
  72. package/src/lib/subagent/types.ts +16 -0
  73. package/src/lib/thread/cold-store.test.ts +33 -5
  74. package/src/lib/thread/cold-store.ts +50 -31
  75. package/src/lib/thread/proxy.ts +79 -29
  76. package/src/tools/edit/handler.test.ts +177 -0
  77. package/src/tools/edit/handler.ts +249 -47
  78. package/src/tools/edit/tool.ts +40 -0
  79. package/src/tools/task-create/handler.ts +1 -1
  80. package/src/tools/task-update/handler.ts +1 -1
  81. package/src/workflow.ts +2 -2
  82. package/dist/proxy-BxFyd6cg.d.cts +0 -24
  83. package/dist/proxy-Cskmj4Yx.d.ts +0 -24
package/dist/index.cjs CHANGED
@@ -6,7 +6,9 @@ var crypto = require('crypto');
6
6
  var common = require('@temporalio/common');
7
7
  var path = require('path');
8
8
  var zlib = require('zlib');
9
+ var util = require('util');
9
10
  var clientS3 = require('@aws-sdk/client-s3');
11
+ var libStorage = require('@aws-sdk/lib-storage');
10
12
  var activity = require('@temporalio/activity');
11
13
  var fs = require('fs');
12
14
 
@@ -572,7 +574,8 @@ function createSubagentHandler(subagents) {
572
574
  }
573
575
  const threadMode = config.thread ?? "new";
574
576
  const allowsContinuation = threadMode !== "new";
575
- const continuationThreadId = args.threadId && allowsContinuation ? args.threadId : void 0;
577
+ const newThreadSource = config.newThreadSource ?? "new";
578
+ const continuationThreadId = !allowsContinuation ? void 0 : args.threadId ?? (newThreadSource === "from-parent" ? context.threadId : void 0);
576
579
  let thread;
577
580
  if (continuationThreadId) {
578
581
  thread = {
@@ -2220,6 +2223,13 @@ IMPORTANT:
2220
2223
  }),
2221
2224
  strict: true
2222
2225
  };
2226
+ var textEditSchema = z14.z.object({
2227
+ old_string: z14.z.string().describe("The exact text to replace"),
2228
+ new_string: z14.z.string().describe("The text to replace it with"),
2229
+ replace_all: z14.z.boolean().optional().describe(
2230
+ "If true, replace all occurrences of old_string for this edit (default: false)"
2231
+ )
2232
+ });
2223
2233
  var editTool = {
2224
2234
  name: "FileEdit",
2225
2235
  description: `Edit specific sections of a file by replacing text.
@@ -2248,6 +2258,27 @@ IMPORTANT:
2248
2258
  }),
2249
2259
  strict: true
2250
2260
  };
2261
+ var multiEditTool = {
2262
+ name: "FileMultiEdit",
2263
+ description: `Apply multiple exact text replacements to one file in order.
2264
+
2265
+ Usage:
2266
+ - Use this when a task needs several related edits in the same file
2267
+ - Each edit is applied to the file content produced by the prior edit
2268
+ - The operation is atomic: if any edit fails, the file is left unchanged
2269
+
2270
+ IMPORTANT:
2271
+ - You must read the file first (in this session) before editing it
2272
+ - Each old_string must match exactly (whitespace-sensitive)
2273
+ - Each old_string must be unique unless that edit uses replace_all: true
2274
+ - old_string and new_string must be different for every edit
2275
+ `,
2276
+ schema: z14.z.object({
2277
+ file_path: z14.z.string().describe("The absolute virtual path to the file to modify"),
2278
+ edits: z14.z.array(textEditSchema).min(1).describe("Exact replacements to apply sequentially to the file")
2279
+ }),
2280
+ strict: true
2281
+ };
2251
2282
  var taskCreateTool = {
2252
2283
  name: "TaskCreate",
2253
2284
  description: `Use this tool to create a structured task list. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
@@ -2318,7 +2349,7 @@ function createTaskCreateHandler(stateManager) {
2318
2349
  };
2319
2350
  stateManager.setTask(task);
2320
2351
  return {
2321
- toolResponse: JSON.stringify(task, null, 2),
2352
+ toolResponse: `Task ${task.id} created`,
2322
2353
  data: task
2323
2354
  };
2324
2355
  };
@@ -2417,7 +2448,7 @@ function createTaskUpdateHandler(stateManager) {
2417
2448
  }
2418
2449
  stateManager.setTask(task);
2419
2450
  return {
2420
- toolResponse: JSON.stringify(task, null, 2),
2451
+ toolResponse: `Task ${task.id} updated`,
2421
2452
  data: task
2422
2453
  };
2423
2454
  };
@@ -2749,6 +2780,44 @@ function createThreadManager(config) {
2749
2780
  }
2750
2781
  };
2751
2782
  }
2783
+ function getActivityContext() {
2784
+ try {
2785
+ const ctx = activity.Context.current();
2786
+ return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
2787
+ } catch {
2788
+ return {};
2789
+ }
2790
+ }
2791
+ async function queryParentWorkflowState(client) {
2792
+ const { workflowExecution } = activity.Context.current().info;
2793
+ if (!workflowExecution) {
2794
+ throw new Error("No workflow execution found");
2795
+ }
2796
+ const handle = client.getHandle(
2797
+ workflowExecution.workflowId,
2798
+ workflowExecution.runId
2799
+ );
2800
+ return handle.query("getAgentState");
2801
+ }
2802
+ function createRunAgentActivity(client, handler, scope) {
2803
+ const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2804
+ return {
2805
+ [name]: async (config) => {
2806
+ const state = await queryParentWorkflowState(client);
2807
+ return handler({ ...config, state });
2808
+ }
2809
+ };
2810
+ }
2811
+ function withParentWorkflowState(client, handler) {
2812
+ return async (args, context) => {
2813
+ const state = await queryParentWorkflowState(client);
2814
+ return handler(args, { ...context, state });
2815
+ };
2816
+ }
2817
+
2818
+ // src/lib/thread/cold-store.ts
2819
+ var gzipAsync = util.promisify(zlib.gzip);
2820
+ var gunzipAsync = util.promisify(zlib.gunzip);
2752
2821
  function joinKey(parts) {
2753
2822
  return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p.length > 0).join("/");
2754
2823
  }
@@ -2760,9 +2829,17 @@ function buildKey(prefix, threadKey, threadId, gzip) {
2760
2829
  `${threadId}.${ext}`
2761
2830
  ]);
2762
2831
  }
2763
- async function streamToBuffer(body) {
2832
+ async function streamToBuffer(body, onChunk) {
2764
2833
  if (body == null) return Buffer.alloc(0);
2765
2834
  if (body instanceof Uint8Array) return Buffer.from(body);
2835
+ if (typeof body[Symbol.asyncIterator] === "function") {
2836
+ const chunks = [];
2837
+ for await (const chunk of body) {
2838
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2839
+ onChunk?.();
2840
+ }
2841
+ return Buffer.concat(chunks);
2842
+ }
2766
2843
  if (typeof body.transformToByteArray === "function") {
2767
2844
  const bytes = await body.transformToByteArray();
2768
2845
  return Buffer.from(bytes);
@@ -2771,11 +2848,7 @@ async function streamToBuffer(body) {
2771
2848
  const ab = await body.arrayBuffer();
2772
2849
  return Buffer.from(ab);
2773
2850
  }
2774
- const chunks = [];
2775
- for await (const chunk of body) {
2776
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2777
- }
2778
- return Buffer.concat(chunks);
2851
+ return Buffer.alloc(0);
2779
2852
  }
2780
2853
  function createS3ColdStore(config) {
2781
2854
  const { s3, bucket, prefix, gzip = true } = config;
@@ -2787,8 +2860,9 @@ function createS3ColdStore(config) {
2787
2860
  const resp = await s3.send(
2788
2861
  new clientS3.GetObjectCommand({ Bucket: bucket, Key })
2789
2862
  );
2790
- const buf = await streamToBuffer(resp.Body);
2791
- const json = gzip ? zlib.gunzipSync(buf).toString("utf8") : buf.toString("utf8");
2863
+ const { heartbeat } = getActivityContext();
2864
+ const buf = await streamToBuffer(resp.Body, heartbeat);
2865
+ const json = gzip ? (await gunzipAsync(buf)).toString("utf8") : buf.toString("utf8");
2792
2866
  return JSON.parse(json);
2793
2867
  } catch (err) {
2794
2868
  if (isNotFound(err)) return null;
@@ -2798,15 +2872,14 @@ function createS3ColdStore(config) {
2798
2872
  async write(threadKey, threadId, snapshot) {
2799
2873
  const Key = buildKey(prefix, threadKey, threadId, gzip);
2800
2874
  const json = JSON.stringify(snapshot);
2801
- const body = gzip ? zlib.gzipSync(Buffer.from(json, "utf8")) : json;
2802
- await s3.send(
2803
- new clientS3.PutObjectCommand({
2804
- Bucket: bucket,
2805
- Key,
2806
- Body: body,
2807
- ContentType: contentType
2808
- })
2809
- );
2875
+ const body = gzip ? await gzipAsync(Buffer.from(json, "utf8")) : json;
2876
+ const upload = new libStorage.Upload({
2877
+ client: s3,
2878
+ params: { Bucket: bucket, Key, Body: body, ContentType: contentType }
2879
+ });
2880
+ const { heartbeat } = getActivityContext();
2881
+ if (heartbeat) upload.on("httpUploadProgress", heartbeat);
2882
+ await upload.done();
2810
2883
  },
2811
2884
  async delete(threadKey, threadId) {
2812
2885
  const Key = buildKey(prefix, threadKey, threadId, gzip);
@@ -2934,40 +3007,6 @@ function createTieredThreadManager(config) {
2934
3007
  }
2935
3008
  });
2936
3009
  }
2937
- function getActivityContext() {
2938
- try {
2939
- const ctx = activity.Context.current();
2940
- return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
2941
- } catch {
2942
- return {};
2943
- }
2944
- }
2945
- async function queryParentWorkflowState(client) {
2946
- const { workflowExecution } = activity.Context.current().info;
2947
- if (!workflowExecution) {
2948
- throw new Error("No workflow execution found");
2949
- }
2950
- const handle = client.getHandle(
2951
- workflowExecution.workflowId,
2952
- workflowExecution.runId
2953
- );
2954
- return handle.query("getAgentState");
2955
- }
2956
- function createRunAgentActivity(client, handler, scope) {
2957
- const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2958
- return {
2959
- [name]: async (config) => {
2960
- const state = await queryParentWorkflowState(client);
2961
- return handler({ ...config, state });
2962
- }
2963
- };
2964
- }
2965
- function withParentWorkflowState(client, handler) {
2966
- return async (args, context) => {
2967
- const state = await queryParentWorkflowState(client);
2968
- return handler(args, { ...context, state });
2969
- };
2970
- }
2971
3010
 
2972
3011
  // src/lib/sandbox/manager.ts
2973
3012
  var CAP_METHOD_TO_CAPABILITY = [
@@ -3639,57 +3678,190 @@ ${result.stderr}`,
3639
3678
  };
3640
3679
 
3641
3680
  // src/tools/edit/handler.ts
3642
- function escapeRegExp(str) {
3643
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3681
+ function splitLines(text) {
3682
+ if (text.length === 0) return [];
3683
+ return text.replace(/\r\n/g, "\n").split("\n");
3684
+ }
3685
+ function lineNumberAt(content, index) {
3686
+ let line = 1;
3687
+ for (let i = 0; i < index; i++) {
3688
+ if (content.charCodeAt(i) === 10) line++;
3689
+ }
3690
+ return line;
3691
+ }
3692
+ function lineEnd(startLine, lines) {
3693
+ return lines.length === 0 ? startLine : startLine + lines.length - 1;
3694
+ }
3695
+ function indicesOf(content, needle) {
3696
+ const indices = [];
3697
+ let cursor = 0;
3698
+ while (cursor <= content.length) {
3699
+ const index = content.indexOf(needle, cursor);
3700
+ if (index === -1) break;
3701
+ indices.push(index);
3702
+ cursor = index + needle.length;
3703
+ }
3704
+ return indices;
3705
+ }
3706
+ function makeHunk(editIndex, beforeContent, replacementIndex, oldString, newString) {
3707
+ const oldStartLine = lineNumberAt(beforeContent, replacementIndex);
3708
+ const oldLines = splitLines(oldString);
3709
+ const newLines = splitLines(newString);
3710
+ return {
3711
+ editIndex,
3712
+ oldStartLine,
3713
+ oldEndLine: lineEnd(oldStartLine, oldLines),
3714
+ newStartLine: oldStartLine,
3715
+ newEndLine: lineEnd(oldStartLine, newLines),
3716
+ oldLines,
3717
+ newLines
3718
+ };
3719
+ }
3720
+ function applyOneEdit(content, edit, editIndex) {
3721
+ const { old_string, new_string, replace_all = false } = edit;
3722
+ if (old_string.length === 0) {
3723
+ return {
3724
+ ok: false,
3725
+ editIndex,
3726
+ message: `Error: old_string for edit ${editIndex} must not be empty.`
3727
+ };
3728
+ }
3729
+ if (old_string === new_string) {
3730
+ return {
3731
+ ok: false,
3732
+ editIndex,
3733
+ message: `Error: old_string and new_string must be different for edit ${editIndex}.`
3734
+ };
3735
+ }
3736
+ const matches = indicesOf(content, old_string);
3737
+ if (matches.length === 0) {
3738
+ return {
3739
+ ok: false,
3740
+ editIndex,
3741
+ message: `Error: Could not find old_string for edit ${editIndex}. Make sure it matches exactly (whitespace-sensitive).`
3742
+ };
3743
+ }
3744
+ if (!replace_all && matches.length > 1) {
3745
+ return {
3746
+ ok: false,
3747
+ editIndex,
3748
+ message: `Error: old_string for edit ${editIndex} appears ${matches.length} times. Provide more context to make it unique, or use replace_all: true for that edit.`
3749
+ };
3750
+ }
3751
+ if (replace_all) {
3752
+ const hunks = matches.map(
3753
+ (index2) => makeHunk(editIndex, content, index2, old_string, new_string)
3754
+ );
3755
+ return {
3756
+ ok: true,
3757
+ content: content.split(old_string).join(new_string),
3758
+ replacements: matches.length,
3759
+ hunks
3760
+ };
3761
+ }
3762
+ const index = matches[0];
3763
+ if (index === void 0) {
3764
+ return {
3765
+ ok: false,
3766
+ editIndex,
3767
+ message: `Error: Could not find old_string for edit ${editIndex}.`
3768
+ };
3769
+ }
3770
+ return {
3771
+ ok: true,
3772
+ content: content.slice(0, index) + new_string + content.slice(index + old_string.length),
3773
+ replacements: 1,
3774
+ hunks: [makeHunk(editIndex, content, index, old_string, new_string)]
3775
+ };
3776
+ }
3777
+ function applyEditPlan(content, edits) {
3778
+ if (edits.length === 0) {
3779
+ return {
3780
+ ok: false,
3781
+ message: "Error: edits must contain at least one edit."
3782
+ };
3783
+ }
3784
+ let current = content;
3785
+ let replacements = 0;
3786
+ const hunks = [];
3787
+ for (const [index, edit] of edits.entries()) {
3788
+ const result = applyOneEdit(current, edit, index);
3789
+ if (!result.ok) return result;
3790
+ current = result.content;
3791
+ replacements += result.replacements;
3792
+ hunks.push(...result.hunks);
3793
+ }
3794
+ return { ok: true, content: current, replacements, hunks };
3795
+ }
3796
+ function editFailureResult(filePath, message) {
3797
+ return {
3798
+ toolResponse: message,
3799
+ data: { path: filePath, success: false, replacements: 0 }
3800
+ };
3644
3801
  }
3645
3802
  var editHandler = async (args, { sandbox }) => {
3646
3803
  const { fs } = sandbox;
3647
3804
  const { file_path, old_string, new_string, replace_all = false } = args;
3648
- if (old_string === new_string) {
3805
+ try {
3806
+ const exists = await fs.exists(file_path);
3807
+ if (!exists) {
3808
+ return editFailureResult(
3809
+ file_path,
3810
+ `Error: File "${file_path}" does not exist.`
3811
+ );
3812
+ }
3813
+ const content = await fs.readFile(file_path);
3814
+ const result = applyEditPlan(content, [
3815
+ { old_string, new_string, replace_all }
3816
+ ]);
3817
+ if (!result.ok) {
3818
+ return editFailureResult(file_path, result.message);
3819
+ }
3820
+ await fs.writeFile(file_path, result.content);
3821
+ const summary = replace_all ? `Replaced ${result.replacements} occurrence(s)` : `Replaced 1 occurrence`;
3822
+ return {
3823
+ toolResponse: `${summary} in ${file_path}`,
3824
+ data: {
3825
+ path: file_path,
3826
+ success: true,
3827
+ replacements: result.replacements,
3828
+ hunks: result.hunks
3829
+ }
3830
+ };
3831
+ } catch (error) {
3832
+ const message = error instanceof Error ? error.message : "Unknown error";
3649
3833
  return {
3650
- toolResponse: `Error: old_string and new_string must be different.`,
3834
+ toolResponse: `Error editing file "${file_path}": ${message}`,
3651
3835
  data: { path: file_path, success: false, replacements: 0 }
3652
3836
  };
3653
3837
  }
3838
+ };
3839
+ var multiEditHandler = async (args, { sandbox }) => {
3840
+ const { fs } = sandbox;
3841
+ const { file_path, edits } = args;
3654
3842
  try {
3655
3843
  const exists = await fs.exists(file_path);
3656
3844
  if (!exists) {
3657
- return {
3658
- toolResponse: `Error: File "${file_path}" does not exist.`,
3659
- data: { path: file_path, success: false, replacements: 0 }
3660
- };
3845
+ return editFailureResult(
3846
+ file_path,
3847
+ `Error: File "${file_path}" does not exist.`
3848
+ );
3661
3849
  }
3662
3850
  const content = await fs.readFile(file_path);
3663
- if (!content.includes(old_string)) {
3664
- return {
3665
- toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
3666
- data: { path: file_path, success: false, replacements: 0 }
3667
- };
3668
- }
3669
- const escapedOldString = escapeRegExp(old_string);
3670
- const globalRegex = new RegExp(escapedOldString, "g");
3671
- const occurrences = (content.match(globalRegex) || []).length;
3672
- if (!replace_all && occurrences > 1) {
3673
- return {
3674
- toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
3675
- data: { path: file_path, success: false, replacements: 0 }
3676
- };
3677
- }
3678
- let newContent;
3679
- let replacements;
3680
- if (replace_all) {
3681
- newContent = content.split(old_string).join(new_string);
3682
- replacements = occurrences;
3683
- } else {
3684
- const index = content.indexOf(old_string);
3685
- newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
3686
- replacements = 1;
3851
+ const result = applyEditPlan(content, edits);
3852
+ if (!result.ok) {
3853
+ const suffix = result.editIndex === void 0 ? "" : ` in ${file_path}`;
3854
+ return editFailureResult(file_path, `${result.message}${suffix}`);
3687
3855
  }
3688
- await fs.writeFile(file_path, newContent);
3689
- const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
3856
+ await fs.writeFile(file_path, result.content);
3690
3857
  return {
3691
- toolResponse: `${summary} in ${file_path}`,
3692
- data: { path: file_path, success: true, replacements }
3858
+ toolResponse: `Applied ${edits.length} edit(s), ${result.replacements} replacement(s) in ${file_path}`,
3859
+ data: {
3860
+ path: file_path,
3861
+ success: true,
3862
+ replacements: result.replacements,
3863
+ hunks: result.hunks
3864
+ }
3693
3865
  };
3694
3866
  } catch (error) {
3695
3867
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -3923,6 +4095,8 @@ exports.hasDirectory = hasDirectory;
3923
4095
  exports.hasFileWithMimeType = hasFileWithMimeType;
3924
4096
  exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
3925
4097
  exports.isTerminalStatus = isTerminalStatus;
4098
+ exports.multiEditHandler = multiEditHandler;
4099
+ exports.multiEditTool = multiEditTool;
3926
4100
  exports.parseSkillFile = parseSkillFile;
3927
4101
  exports.proxyRunAgent = proxyRunAgent;
3928
4102
  exports.proxyVirtualFsOps = proxyVirtualFsOps;