zeitlich 0.2.46 → 0.2.48

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 (89) hide show
  1. package/README.md +66 -6
  2. package/dist/{activities-CyeiqK_f.d.cts → activities-BlQR5gX4.d.cts} +3 -3
  3. package/dist/{activities-Bm4TLTid.d.ts → activities-DCaIPQBT.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-BC5L5Z8A.d.cts → cold-store-UL13Sstw.d.cts} +8 -11
  33. package/dist/{cold-store-CFHwemBJ.d.ts → cold-store-aD4TSKlU.d.ts} +8 -11
  34. package/dist/index.cjs +311 -99
  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 +312 -102
  39. package/dist/index.js.map +1 -1
  40. package/dist/proxy-BAty3CWM.d.cts +40 -0
  41. package/dist/proxy-mbnwBhHw.d.ts +40 -0
  42. package/dist/{thread-manager-DduoSkvJ.d.ts → thread-manager-CICj68PI.d.ts} +2 -2
  43. package/dist/{thread-manager-D33SUmZa.d.cts → thread-manager-DsXvJ5cJ.d.cts} +2 -2
  44. package/dist/{thread-manager-B-zy3xrs.d.ts → thread-manager-DtEtbUkp.d.ts} +2 -2
  45. package/dist/{thread-manager-9tezUcLW.d.cts → thread-manager-R6c3lnJy.d.cts} +2 -2
  46. package/dist/{types-oxt8GN97.d.cts → types-DDLPnxBh.d.cts} +1 -1
  47. package/dist/{types-L5bvbF-n.d.ts → types-DF4wzWQG.d.ts} +1 -1
  48. package/dist/{types-CnuN9T6t.d.cts → types-DWeyCTYK.d.cts} +47 -0
  49. package/dist/{types-CwN6_tAL.d.ts → types-DwBYd0ij.d.ts} +47 -0
  50. package/dist/{workflow-DIaIV7L2.d.cts → workflow-DVNPR7eX.d.cts} +17 -2
  51. package/dist/{workflow-B1TOcHbt.d.ts → workflow-DdaU7_j4.d.ts} +17 -2
  52. package/dist/workflow.cjs +80 -12
  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 +80 -13
  57. package/dist/workflow.js.map +1 -1
  58. package/package.json +14 -8
  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/lifecycle.ts +13 -1
  70. package/src/lib/session/session-edge-cases.integration.test.ts +44 -0
  71. package/src/lib/session/session.ts +15 -0
  72. package/src/lib/subagent/define.ts +1 -0
  73. package/src/lib/subagent/handler.ts +41 -6
  74. package/src/lib/subagent/subagent.integration.test.ts +178 -0
  75. package/src/lib/subagent/types.ts +16 -0
  76. package/src/lib/thread/cold-store.test.ts +33 -5
  77. package/src/lib/thread/cold-store.ts +50 -31
  78. package/src/lib/thread/proxy.ts +79 -29
  79. package/src/lib/tool-router/router-edge-cases.integration.test.ts +36 -0
  80. package/src/lib/tool-router/router.ts +21 -3
  81. package/src/lib/tool-router/types.ts +20 -0
  82. package/src/tools/edit/handler.test.ts +177 -0
  83. package/src/tools/edit/handler.ts +249 -47
  84. package/src/tools/edit/tool.ts +40 -0
  85. package/src/tools/task-create/handler.ts +1 -1
  86. package/src/tools/task-update/handler.ts +1 -1
  87. package/src/workflow.ts +2 -2
  88. package/dist/proxy-BxFyd6cg.d.cts +0 -24
  89. package/dist/proxy-Cskmj4Yx.d.ts +0 -24
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@ import z14, { z } from 'zod';
3
3
  import { randomUUID, randomFillSync } from 'crypto';
4
4
  import { ApplicationFailure as ApplicationFailure$1 } from '@temporalio/common';
5
5
  import { join, resolve, posix } from 'path';
6
- import { gzipSync, gunzipSync } from 'zlib';
7
- import { DeleteObjectCommand, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
6
+ import { gzip, gunzip } from 'zlib';
7
+ import { promisify } from 'util';
8
+ import { DeleteObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
9
+ import { Upload } from '@aws-sdk/lib-storage';
8
10
  import { Context } from '@temporalio/activity';
9
11
  import { promises } from 'fs';
10
12
 
@@ -123,7 +125,7 @@ function createToolRouter(options) {
123
125
  });
124
126
  }
125
127
  }
126
- async function processToolCall(toolCall, turn, sandboxId, onRewindRequested) {
128
+ async function processToolCall(toolCall, turn, sandboxId, onRewindRequested, assistantMessageId) {
127
129
  const startTime = Date.now();
128
130
  const tool = toolMap.get(toolCall.name);
129
131
  const preResult = await runPreHooks(toolCall, tool, turn);
@@ -158,7 +160,8 @@ function createToolRouter(options) {
158
160
  ...options.threadKey && { threadKey: options.threadKey },
159
161
  toolCallId: toolCall.id,
160
162
  toolName: toolCall.name,
161
- ...sandboxId !== void 0 && { sandboxId }
163
+ ...sandboxId !== void 0 && { sandboxId },
164
+ ...assistantMessageId !== void 0 && { assistantMessageId }
162
165
  };
163
166
  const response = await tool.handler(
164
167
  effectiveArgs,
@@ -285,6 +288,7 @@ function createToolRouter(options) {
285
288
  }
286
289
  const turn = context?.turn ?? 0;
287
290
  const sandboxId = context?.sandboxId;
291
+ const assistantMessageId = context?.assistantMessageId;
288
292
  let rewindSignal;
289
293
  if (options.parallel) {
290
294
  const scope = new CancellationScope({ cancellable: true });
@@ -297,7 +301,13 @@ function createToolRouter(options) {
297
301
  const outcomes = await scope.run(
298
302
  async () => Promise.allSettled(
299
303
  toolCalls.map(
300
- (tc) => processToolCall(tc, turn, sandboxId, onRewindRequested)
304
+ (tc) => processToolCall(
305
+ tc,
306
+ turn,
307
+ sandboxId,
308
+ onRewindRequested,
309
+ assistantMessageId
310
+ )
301
311
  )
302
312
  )
303
313
  );
@@ -317,7 +327,13 @@ function createToolRouter(options) {
317
327
  }
318
328
  const results = [];
319
329
  for (const toolCall of toolCalls) {
320
- const outcome = await processToolCall(toolCall, turn, sandboxId);
330
+ const outcome = await processToolCall(
331
+ toolCall,
332
+ turn,
333
+ sandboxId,
334
+ void 0,
335
+ assistantMessageId
336
+ );
321
337
  if (outcome.kind === "rewind") {
322
338
  rewindSignal = outcome.signal;
323
339
  break;
@@ -341,6 +357,9 @@ function createToolRouter(options) {
341
357
  toolName: toolCall.name,
342
358
  ...context?.sandboxId !== void 0 && {
343
359
  sandboxId: context.sandboxId
360
+ },
361
+ ...context?.assistantMessageId !== void 0 && {
362
+ assistantMessageId: context.assistantMessageId
344
363
  }
345
364
  };
346
365
  const response = await handler(
@@ -566,13 +585,27 @@ function createSubagentHandler(subagents) {
566
585
  }
567
586
  const threadMode = config.thread ?? "new";
568
587
  const allowsContinuation = threadMode !== "new";
569
- const continuationThreadId = args.threadId && allowsContinuation ? args.threadId : void 0;
588
+ const newThreadSource = config.newThreadSource ?? "new";
589
+ const usingParentFallback = allowsContinuation && !args.threadId && newThreadSource === "from-parent";
590
+ const continuationThreadId = !allowsContinuation ? void 0 : args.threadId ?? (usingParentFallback ? context.threadId : void 0);
570
591
  let thread;
571
592
  if (continuationThreadId) {
572
- thread = {
573
- mode: threadMode,
574
- threadId: continuationThreadId
575
- };
593
+ if (threadMode === "fork") {
594
+ thread = {
595
+ mode: "fork",
596
+ threadId: continuationThreadId,
597
+ ...usingParentFallback && context.assistantMessageId ? {
598
+ truncateAfterFork: {
599
+ fromMessageId: context.assistantMessageId
600
+ }
601
+ } : {}
602
+ };
603
+ } else {
604
+ thread = {
605
+ mode: "continue",
606
+ threadId: continuationThreadId
607
+ };
608
+ }
576
609
  }
577
610
  let sandbox;
578
611
  let sandboxShutdownOverride;
@@ -1033,6 +1066,7 @@ async function createSession(config) {
1033
1066
  appendSystemMessage,
1034
1067
  appendAgentMessage,
1035
1068
  forkThread,
1069
+ truncateThread,
1036
1070
  loadThreadState,
1037
1071
  saveThreadState,
1038
1072
  hydrateThread,
@@ -1182,6 +1216,10 @@ async function createSession(config) {
1182
1216
  if (threadMode === "fork" && sourceThreadId) {
1183
1217
  await hydrateThread(sourceThreadId, threadKey);
1184
1218
  await forkThread(sourceThreadId, threadId, threadKey);
1219
+ const truncate = threadInit.truncateAfterFork;
1220
+ if (truncate?.fromMessageId) {
1221
+ await truncateThread(threadId, truncate.fromMessageId, threadKey);
1222
+ }
1185
1223
  const forkedSlice = await loadThreadState(threadId, threadKey);
1186
1224
  if (forkedSlice) rehydrateFromSlice(forkedSlice);
1187
1225
  } else if (threadMode === "continue") {
@@ -1311,7 +1349,10 @@ async function createSession(config) {
1311
1349
  parsedToolCalls,
1312
1350
  {
1313
1351
  turn: currentTurn,
1314
- ...sandboxId !== void 0 && { sandboxId }
1352
+ ...sandboxId !== void 0 && { sandboxId },
1353
+ ...assistantId !== void 0 && {
1354
+ assistantMessageId: assistantId
1355
+ }
1315
1356
  }
1316
1357
  );
1317
1358
  for (const result of toolCallResults) {
@@ -2214,6 +2255,13 @@ IMPORTANT:
2214
2255
  }),
2215
2256
  strict: true
2216
2257
  };
2258
+ var textEditSchema = z.object({
2259
+ old_string: z.string().describe("The exact text to replace"),
2260
+ new_string: z.string().describe("The text to replace it with"),
2261
+ replace_all: z.boolean().optional().describe(
2262
+ "If true, replace all occurrences of old_string for this edit (default: false)"
2263
+ )
2264
+ });
2217
2265
  var editTool = {
2218
2266
  name: "FileEdit",
2219
2267
  description: `Edit specific sections of a file by replacing text.
@@ -2242,6 +2290,27 @@ IMPORTANT:
2242
2290
  }),
2243
2291
  strict: true
2244
2292
  };
2293
+ var multiEditTool = {
2294
+ name: "FileMultiEdit",
2295
+ description: `Apply multiple exact text replacements to one file in order.
2296
+
2297
+ Usage:
2298
+ - Use this when a task needs several related edits in the same file
2299
+ - Each edit is applied to the file content produced by the prior edit
2300
+ - The operation is atomic: if any edit fails, the file is left unchanged
2301
+
2302
+ IMPORTANT:
2303
+ - You must read the file first (in this session) before editing it
2304
+ - Each old_string must match exactly (whitespace-sensitive)
2305
+ - Each old_string must be unique unless that edit uses replace_all: true
2306
+ - old_string and new_string must be different for every edit
2307
+ `,
2308
+ schema: z.object({
2309
+ file_path: z.string().describe("The absolute virtual path to the file to modify"),
2310
+ edits: z.array(textEditSchema).min(1).describe("Exact replacements to apply sequentially to the file")
2311
+ }),
2312
+ strict: true
2313
+ };
2245
2314
  var taskCreateTool = {
2246
2315
  name: "TaskCreate",
2247
2316
  description: `Use this tool to create a structured task list. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
@@ -2312,7 +2381,7 @@ function createTaskCreateHandler(stateManager) {
2312
2381
  };
2313
2382
  stateManager.setTask(task);
2314
2383
  return {
2315
- toolResponse: JSON.stringify(task, null, 2),
2384
+ toolResponse: `Task ${task.id} created`,
2316
2385
  data: task
2317
2386
  };
2318
2387
  };
@@ -2411,7 +2480,7 @@ function createTaskUpdateHandler(stateManager) {
2411
2480
  }
2412
2481
  stateManager.setTask(task);
2413
2482
  return {
2414
- toolResponse: JSON.stringify(task, null, 2),
2483
+ toolResponse: `Task ${task.id} updated`,
2415
2484
  data: task
2416
2485
  };
2417
2486
  };
@@ -2743,6 +2812,44 @@ function createThreadManager(config) {
2743
2812
  }
2744
2813
  };
2745
2814
  }
2815
+ function getActivityContext() {
2816
+ try {
2817
+ const ctx = Context.current();
2818
+ return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
2819
+ } catch {
2820
+ return {};
2821
+ }
2822
+ }
2823
+ async function queryParentWorkflowState(client) {
2824
+ const { workflowExecution } = Context.current().info;
2825
+ if (!workflowExecution) {
2826
+ throw new Error("No workflow execution found");
2827
+ }
2828
+ const handle = client.getHandle(
2829
+ workflowExecution.workflowId,
2830
+ workflowExecution.runId
2831
+ );
2832
+ return handle.query("getAgentState");
2833
+ }
2834
+ function createRunAgentActivity(client, handler, scope) {
2835
+ const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2836
+ return {
2837
+ [name]: async (config) => {
2838
+ const state = await queryParentWorkflowState(client);
2839
+ return handler({ ...config, state });
2840
+ }
2841
+ };
2842
+ }
2843
+ function withParentWorkflowState(client, handler) {
2844
+ return async (args, context) => {
2845
+ const state = await queryParentWorkflowState(client);
2846
+ return handler(args, { ...context, state });
2847
+ };
2848
+ }
2849
+
2850
+ // src/lib/thread/cold-store.ts
2851
+ var gzipAsync = promisify(gzip);
2852
+ var gunzipAsync = promisify(gunzip);
2746
2853
  function joinKey(parts) {
2747
2854
  return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p.length > 0).join("/");
2748
2855
  }
@@ -2754,9 +2861,17 @@ function buildKey(prefix, threadKey, threadId, gzip) {
2754
2861
  `${threadId}.${ext}`
2755
2862
  ]);
2756
2863
  }
2757
- async function streamToBuffer(body) {
2864
+ async function streamToBuffer(body, onChunk) {
2758
2865
  if (body == null) return Buffer.alloc(0);
2759
2866
  if (body instanceof Uint8Array) return Buffer.from(body);
2867
+ if (typeof body[Symbol.asyncIterator] === "function") {
2868
+ const chunks = [];
2869
+ for await (const chunk of body) {
2870
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2871
+ onChunk?.();
2872
+ }
2873
+ return Buffer.concat(chunks);
2874
+ }
2760
2875
  if (typeof body.transformToByteArray === "function") {
2761
2876
  const bytes = await body.transformToByteArray();
2762
2877
  return Buffer.from(bytes);
@@ -2765,11 +2880,7 @@ async function streamToBuffer(body) {
2765
2880
  const ab = await body.arrayBuffer();
2766
2881
  return Buffer.from(ab);
2767
2882
  }
2768
- const chunks = [];
2769
- for await (const chunk of body) {
2770
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2771
- }
2772
- return Buffer.concat(chunks);
2883
+ return Buffer.alloc(0);
2773
2884
  }
2774
2885
  function createS3ColdStore(config) {
2775
2886
  const { s3, bucket, prefix, gzip = true } = config;
@@ -2781,8 +2892,9 @@ function createS3ColdStore(config) {
2781
2892
  const resp = await s3.send(
2782
2893
  new GetObjectCommand({ Bucket: bucket, Key })
2783
2894
  );
2784
- const buf = await streamToBuffer(resp.Body);
2785
- const json = gzip ? gunzipSync(buf).toString("utf8") : buf.toString("utf8");
2895
+ const { heartbeat } = getActivityContext();
2896
+ const buf = await streamToBuffer(resp.Body, heartbeat);
2897
+ const json = gzip ? (await gunzipAsync(buf)).toString("utf8") : buf.toString("utf8");
2786
2898
  return JSON.parse(json);
2787
2899
  } catch (err) {
2788
2900
  if (isNotFound(err)) return null;
@@ -2792,15 +2904,14 @@ function createS3ColdStore(config) {
2792
2904
  async write(threadKey, threadId, snapshot) {
2793
2905
  const Key = buildKey(prefix, threadKey, threadId, gzip);
2794
2906
  const json = JSON.stringify(snapshot);
2795
- const body = gzip ? gzipSync(Buffer.from(json, "utf8")) : json;
2796
- await s3.send(
2797
- new PutObjectCommand({
2798
- Bucket: bucket,
2799
- Key,
2800
- Body: body,
2801
- ContentType: contentType
2802
- })
2803
- );
2907
+ const body = gzip ? await gzipAsync(Buffer.from(json, "utf8")) : json;
2908
+ const upload = new Upload({
2909
+ client: s3,
2910
+ params: { Bucket: bucket, Key, Body: body, ContentType: contentType }
2911
+ });
2912
+ const { heartbeat } = getActivityContext();
2913
+ if (heartbeat) upload.on("httpUploadProgress", heartbeat);
2914
+ await upload.done();
2804
2915
  },
2805
2916
  async delete(threadKey, threadId) {
2806
2917
  const Key = buildKey(prefix, threadKey, threadId, gzip);
@@ -2928,40 +3039,6 @@ function createTieredThreadManager(config) {
2928
3039
  }
2929
3040
  });
2930
3041
  }
2931
- function getActivityContext() {
2932
- try {
2933
- const ctx = Context.current();
2934
- return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
2935
- } catch {
2936
- return {};
2937
- }
2938
- }
2939
- async function queryParentWorkflowState(client) {
2940
- const { workflowExecution } = Context.current().info;
2941
- if (!workflowExecution) {
2942
- throw new Error("No workflow execution found");
2943
- }
2944
- const handle = client.getHandle(
2945
- workflowExecution.workflowId,
2946
- workflowExecution.runId
2947
- );
2948
- return handle.query("getAgentState");
2949
- }
2950
- function createRunAgentActivity(client, handler, scope) {
2951
- const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
2952
- return {
2953
- [name]: async (config) => {
2954
- const state = await queryParentWorkflowState(client);
2955
- return handler({ ...config, state });
2956
- }
2957
- };
2958
- }
2959
- function withParentWorkflowState(client, handler) {
2960
- return async (args, context) => {
2961
- const state = await queryParentWorkflowState(client);
2962
- return handler(args, { ...context, state });
2963
- };
2964
- }
2965
3042
 
2966
3043
  // src/lib/sandbox/manager.ts
2967
3044
  var CAP_METHOD_TO_CAPABILITY = [
@@ -3633,57 +3710,190 @@ ${result.stderr}`,
3633
3710
  };
3634
3711
 
3635
3712
  // src/tools/edit/handler.ts
3636
- function escapeRegExp(str) {
3637
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3713
+ function splitLines(text) {
3714
+ if (text.length === 0) return [];
3715
+ return text.replace(/\r\n/g, "\n").split("\n");
3716
+ }
3717
+ function lineNumberAt(content, index) {
3718
+ let line = 1;
3719
+ for (let i = 0; i < index; i++) {
3720
+ if (content.charCodeAt(i) === 10) line++;
3721
+ }
3722
+ return line;
3723
+ }
3724
+ function lineEnd(startLine, lines) {
3725
+ return lines.length === 0 ? startLine : startLine + lines.length - 1;
3726
+ }
3727
+ function indicesOf(content, needle) {
3728
+ const indices = [];
3729
+ let cursor = 0;
3730
+ while (cursor <= content.length) {
3731
+ const index = content.indexOf(needle, cursor);
3732
+ if (index === -1) break;
3733
+ indices.push(index);
3734
+ cursor = index + needle.length;
3735
+ }
3736
+ return indices;
3737
+ }
3738
+ function makeHunk(editIndex, beforeContent, replacementIndex, oldString, newString) {
3739
+ const oldStartLine = lineNumberAt(beforeContent, replacementIndex);
3740
+ const oldLines = splitLines(oldString);
3741
+ const newLines = splitLines(newString);
3742
+ return {
3743
+ editIndex,
3744
+ oldStartLine,
3745
+ oldEndLine: lineEnd(oldStartLine, oldLines),
3746
+ newStartLine: oldStartLine,
3747
+ newEndLine: lineEnd(oldStartLine, newLines),
3748
+ oldLines,
3749
+ newLines
3750
+ };
3751
+ }
3752
+ function applyOneEdit(content, edit, editIndex) {
3753
+ const { old_string, new_string, replace_all = false } = edit;
3754
+ if (old_string.length === 0) {
3755
+ return {
3756
+ ok: false,
3757
+ editIndex,
3758
+ message: `Error: old_string for edit ${editIndex} must not be empty.`
3759
+ };
3760
+ }
3761
+ if (old_string === new_string) {
3762
+ return {
3763
+ ok: false,
3764
+ editIndex,
3765
+ message: `Error: old_string and new_string must be different for edit ${editIndex}.`
3766
+ };
3767
+ }
3768
+ const matches = indicesOf(content, old_string);
3769
+ if (matches.length === 0) {
3770
+ return {
3771
+ ok: false,
3772
+ editIndex,
3773
+ message: `Error: Could not find old_string for edit ${editIndex}. Make sure it matches exactly (whitespace-sensitive).`
3774
+ };
3775
+ }
3776
+ if (!replace_all && matches.length > 1) {
3777
+ return {
3778
+ ok: false,
3779
+ editIndex,
3780
+ 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.`
3781
+ };
3782
+ }
3783
+ if (replace_all) {
3784
+ const hunks = matches.map(
3785
+ (index2) => makeHunk(editIndex, content, index2, old_string, new_string)
3786
+ );
3787
+ return {
3788
+ ok: true,
3789
+ content: content.split(old_string).join(new_string),
3790
+ replacements: matches.length,
3791
+ hunks
3792
+ };
3793
+ }
3794
+ const index = matches[0];
3795
+ if (index === void 0) {
3796
+ return {
3797
+ ok: false,
3798
+ editIndex,
3799
+ message: `Error: Could not find old_string for edit ${editIndex}.`
3800
+ };
3801
+ }
3802
+ return {
3803
+ ok: true,
3804
+ content: content.slice(0, index) + new_string + content.slice(index + old_string.length),
3805
+ replacements: 1,
3806
+ hunks: [makeHunk(editIndex, content, index, old_string, new_string)]
3807
+ };
3808
+ }
3809
+ function applyEditPlan(content, edits) {
3810
+ if (edits.length === 0) {
3811
+ return {
3812
+ ok: false,
3813
+ message: "Error: edits must contain at least one edit."
3814
+ };
3815
+ }
3816
+ let current = content;
3817
+ let replacements = 0;
3818
+ const hunks = [];
3819
+ for (const [index, edit] of edits.entries()) {
3820
+ const result = applyOneEdit(current, edit, index);
3821
+ if (!result.ok) return result;
3822
+ current = result.content;
3823
+ replacements += result.replacements;
3824
+ hunks.push(...result.hunks);
3825
+ }
3826
+ return { ok: true, content: current, replacements, hunks };
3827
+ }
3828
+ function editFailureResult(filePath, message) {
3829
+ return {
3830
+ toolResponse: message,
3831
+ data: { path: filePath, success: false, replacements: 0 }
3832
+ };
3638
3833
  }
3639
3834
  var editHandler = async (args, { sandbox }) => {
3640
3835
  const { fs } = sandbox;
3641
3836
  const { file_path, old_string, new_string, replace_all = false } = args;
3642
- if (old_string === new_string) {
3837
+ try {
3838
+ const exists = await fs.exists(file_path);
3839
+ if (!exists) {
3840
+ return editFailureResult(
3841
+ file_path,
3842
+ `Error: File "${file_path}" does not exist.`
3843
+ );
3844
+ }
3845
+ const content = await fs.readFile(file_path);
3846
+ const result = applyEditPlan(content, [
3847
+ { old_string, new_string, replace_all }
3848
+ ]);
3849
+ if (!result.ok) {
3850
+ return editFailureResult(file_path, result.message);
3851
+ }
3852
+ await fs.writeFile(file_path, result.content);
3853
+ const summary = replace_all ? `Replaced ${result.replacements} occurrence(s)` : `Replaced 1 occurrence`;
3643
3854
  return {
3644
- toolResponse: `Error: old_string and new_string must be different.`,
3855
+ toolResponse: `${summary} in ${file_path}`,
3856
+ data: {
3857
+ path: file_path,
3858
+ success: true,
3859
+ replacements: result.replacements,
3860
+ hunks: result.hunks
3861
+ }
3862
+ };
3863
+ } catch (error) {
3864
+ const message = error instanceof Error ? error.message : "Unknown error";
3865
+ return {
3866
+ toolResponse: `Error editing file "${file_path}": ${message}`,
3645
3867
  data: { path: file_path, success: false, replacements: 0 }
3646
3868
  };
3647
3869
  }
3870
+ };
3871
+ var multiEditHandler = async (args, { sandbox }) => {
3872
+ const { fs } = sandbox;
3873
+ const { file_path, edits } = args;
3648
3874
  try {
3649
3875
  const exists = await fs.exists(file_path);
3650
3876
  if (!exists) {
3651
- return {
3652
- toolResponse: `Error: File "${file_path}" does not exist.`,
3653
- data: { path: file_path, success: false, replacements: 0 }
3654
- };
3877
+ return editFailureResult(
3878
+ file_path,
3879
+ `Error: File "${file_path}" does not exist.`
3880
+ );
3655
3881
  }
3656
3882
  const content = await fs.readFile(file_path);
3657
- if (!content.includes(old_string)) {
3658
- return {
3659
- toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
3660
- data: { path: file_path, success: false, replacements: 0 }
3661
- };
3662
- }
3663
- const escapedOldString = escapeRegExp(old_string);
3664
- const globalRegex = new RegExp(escapedOldString, "g");
3665
- const occurrences = (content.match(globalRegex) || []).length;
3666
- if (!replace_all && occurrences > 1) {
3667
- return {
3668
- toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
3669
- data: { path: file_path, success: false, replacements: 0 }
3670
- };
3671
- }
3672
- let newContent;
3673
- let replacements;
3674
- if (replace_all) {
3675
- newContent = content.split(old_string).join(new_string);
3676
- replacements = occurrences;
3677
- } else {
3678
- const index = content.indexOf(old_string);
3679
- newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
3680
- replacements = 1;
3883
+ const result = applyEditPlan(content, edits);
3884
+ if (!result.ok) {
3885
+ const suffix = result.editIndex === void 0 ? "" : ` in ${file_path}`;
3886
+ return editFailureResult(file_path, `${result.message}${suffix}`);
3681
3887
  }
3682
- await fs.writeFile(file_path, newContent);
3683
- const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
3888
+ await fs.writeFile(file_path, result.content);
3684
3889
  return {
3685
- toolResponse: `${summary} in ${file_path}`,
3686
- data: { path: file_path, success: true, replacements }
3890
+ toolResponse: `Applied ${edits.length} edit(s), ${result.replacements} replacement(s) in ${file_path}`,
3891
+ data: {
3892
+ path: file_path,
3893
+ success: true,
3894
+ replacements: result.replacements,
3895
+ hunks: result.hunks
3896
+ }
3687
3897
  };
3688
3898
  } catch (error) {
3689
3899
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -3863,6 +4073,6 @@ var toTree = async (fs, opts = {}) => {
3863
4073
  return base + subtree;
3864
4074
  };
3865
4075
 
3866
- export { DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT, FileSystemSkillProvider, NodeFsSandboxFileSystem, SandboxManager, SandboxNotFoundError, SandboxNotSupportedError, THREAD_TTL_SECONDS, VirtualFileSystem, applySnapshot, applyVirtualTreeMutations, askUserQuestionTool, bashHandler, bashTool, clearHotTier, composeHooks, createAgentStateManager, createAskUserQuestionHandler, createBashToolDescription, createObservabilityHooks, createReadSkillHandler, createReadSkillTool, createRunAgentActivity, createS3ColdStore, createSession, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createTieredThreadManager, createToolRouter, createVirtualFsActivities, defineSubagent, defineSubagentWorkflow, defineTool, defineWorkflow, editHandler, editTool, encodeSnapshot, filesWithMimeType, formatVirtualFileTree, getActivityContext, getShortId, getThreadDedupKey, getThreadListKey, getThreadMetaKey, getThreadStateKey, globHandler, globTool, grepTool, hasDirectory, hasFileWithMimeType, hasNoOtherToolCalls, isTerminalStatus, parseSkillFile, proxyRunAgent, proxyVirtualFsOps, queryParentWorkflowState, readFileHandler, readFileTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, withParentWorkflowState, withSandbox, withVirtualFs, writeFileHandler, writeFileTool };
4076
+ export { DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT, FileSystemSkillProvider, NodeFsSandboxFileSystem, SandboxManager, SandboxNotFoundError, SandboxNotSupportedError, THREAD_TTL_SECONDS, VirtualFileSystem, applySnapshot, applyVirtualTreeMutations, askUserQuestionTool, bashHandler, bashTool, clearHotTier, composeHooks, createAgentStateManager, createAskUserQuestionHandler, createBashToolDescription, createObservabilityHooks, createReadSkillHandler, createReadSkillTool, createRunAgentActivity, createS3ColdStore, createSession, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createTieredThreadManager, createToolRouter, createVirtualFsActivities, defineSubagent, defineSubagentWorkflow, defineTool, defineWorkflow, editHandler, editTool, encodeSnapshot, filesWithMimeType, formatVirtualFileTree, getActivityContext, getShortId, getThreadDedupKey, getThreadListKey, getThreadMetaKey, getThreadStateKey, globHandler, globTool, grepTool, hasDirectory, hasFileWithMimeType, hasNoOtherToolCalls, isTerminalStatus, multiEditHandler, multiEditTool, parseSkillFile, proxyRunAgent, proxyVirtualFsOps, queryParentWorkflowState, readFileHandler, readFileTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, withParentWorkflowState, withSandbox, withVirtualFs, writeFileHandler, writeFileTool };
3867
4077
  //# sourceMappingURL=index.js.map
3868
4078
  //# sourceMappingURL=index.js.map