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.
- package/README.md +66 -6
- package/dist/{activities-CyeiqK_f.d.cts → activities-BlQR5gX4.d.cts} +3 -3
- package/dist/{activities-Bm4TLTid.d.ts → activities-DCaIPQBT.d.ts} +3 -3
- package/dist/adapters/thread/anthropic/index.cjs +105 -6
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +48 -9
- package/dist/adapters/thread/anthropic/index.d.ts +48 -9
- package/dist/adapters/thread/anthropic/index.js +104 -7
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +38 -22
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
- package/dist/adapters/thread/anthropic/workflow.js +38 -22
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +6 -5
- package/dist/adapters/thread/google-genai/index.d.ts +6 -5
- package/dist/adapters/thread/google-genai/workflow.cjs +38 -22
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +7 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +7 -5
- package/dist/adapters/thread/google-genai/workflow.js +38 -22
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +6 -5
- package/dist/adapters/thread/langchain/index.d.ts +6 -5
- package/dist/adapters/thread/langchain/workflow.cjs +38 -22
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
- package/dist/adapters/thread/langchain/workflow.js +38 -22
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/{cold-store-BC5L5Z8A.d.cts → cold-store-UL13Sstw.d.cts} +8 -11
- package/dist/{cold-store-CFHwemBJ.d.ts → cold-store-aD4TSKlU.d.ts} +8 -11
- package/dist/index.cjs +311 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -9
- package/dist/index.d.ts +21 -9
- package/dist/index.js +312 -102
- package/dist/index.js.map +1 -1
- package/dist/proxy-BAty3CWM.d.cts +40 -0
- package/dist/proxy-mbnwBhHw.d.ts +40 -0
- package/dist/{thread-manager-DduoSkvJ.d.ts → thread-manager-CICj68PI.d.ts} +2 -2
- package/dist/{thread-manager-D33SUmZa.d.cts → thread-manager-DsXvJ5cJ.d.cts} +2 -2
- package/dist/{thread-manager-B-zy3xrs.d.ts → thread-manager-DtEtbUkp.d.ts} +2 -2
- package/dist/{thread-manager-9tezUcLW.d.cts → thread-manager-R6c3lnJy.d.cts} +2 -2
- package/dist/{types-oxt8GN97.d.cts → types-DDLPnxBh.d.cts} +1 -1
- package/dist/{types-L5bvbF-n.d.ts → types-DF4wzWQG.d.ts} +1 -1
- package/dist/{types-CnuN9T6t.d.cts → types-DWeyCTYK.d.cts} +47 -0
- package/dist/{types-CwN6_tAL.d.ts → types-DwBYd0ij.d.ts} +47 -0
- package/dist/{workflow-DIaIV7L2.d.cts → workflow-DVNPR7eX.d.cts} +17 -2
- package/dist/{workflow-B1TOcHbt.d.ts → workflow-DdaU7_j4.d.ts} +17 -2
- package/dist/workflow.cjs +80 -12
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +2 -2
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +80 -13
- package/dist/workflow.js.map +1 -1
- package/package.json +14 -8
- package/src/adapters/thread/anthropic/activities.ts +18 -11
- package/src/adapters/thread/anthropic/index.ts +8 -0
- package/src/adapters/thread/anthropic/model-invoker.test.ts +110 -0
- package/src/adapters/thread/anthropic/model-invoker.ts +26 -5
- package/src/adapters/thread/anthropic/prompt-cache.test.ts +134 -0
- package/src/adapters/thread/anthropic/prompt-cache.ts +163 -0
- package/src/adapters/thread/anthropic/proxy.ts +1 -0
- package/src/adapters/thread/google-genai/proxy.ts +1 -0
- package/src/adapters/thread/langchain/proxy.ts +1 -0
- package/src/index.ts +1 -1
- package/src/lib/lifecycle.ts +13 -1
- package/src/lib/session/session-edge-cases.integration.test.ts +44 -0
- package/src/lib/session/session.ts +15 -0
- package/src/lib/subagent/define.ts +1 -0
- package/src/lib/subagent/handler.ts +41 -6
- package/src/lib/subagent/subagent.integration.test.ts +178 -0
- package/src/lib/subagent/types.ts +16 -0
- package/src/lib/thread/cold-store.test.ts +33 -5
- package/src/lib/thread/cold-store.ts +50 -31
- package/src/lib/thread/proxy.ts +79 -29
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +36 -0
- package/src/lib/tool-router/router.ts +21 -3
- package/src/lib/tool-router/types.ts +20 -0
- package/src/tools/edit/handler.test.ts +177 -0
- package/src/tools/edit/handler.ts +249 -47
- package/src/tools/edit/tool.ts +40 -0
- package/src/tools/task-create/handler.ts +1 -1
- package/src/tools/task-update/handler.ts +1 -1
- package/src/workflow.ts +2 -2
- package/dist/proxy-BxFyd6cg.d.cts +0 -24
- 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
|
|
|
@@ -129,7 +131,7 @@ function createToolRouter(options) {
|
|
|
129
131
|
});
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
|
-
async function processToolCall(toolCall, turn, sandboxId, onRewindRequested) {
|
|
134
|
+
async function processToolCall(toolCall, turn, sandboxId, onRewindRequested, assistantMessageId) {
|
|
133
135
|
const startTime = Date.now();
|
|
134
136
|
const tool = toolMap.get(toolCall.name);
|
|
135
137
|
const preResult = await runPreHooks(toolCall, tool, turn);
|
|
@@ -164,7 +166,8 @@ function createToolRouter(options) {
|
|
|
164
166
|
...options.threadKey && { threadKey: options.threadKey },
|
|
165
167
|
toolCallId: toolCall.id,
|
|
166
168
|
toolName: toolCall.name,
|
|
167
|
-
...sandboxId !== void 0 && { sandboxId }
|
|
169
|
+
...sandboxId !== void 0 && { sandboxId },
|
|
170
|
+
...assistantMessageId !== void 0 && { assistantMessageId }
|
|
168
171
|
};
|
|
169
172
|
const response = await tool.handler(
|
|
170
173
|
effectiveArgs,
|
|
@@ -291,6 +294,7 @@ function createToolRouter(options) {
|
|
|
291
294
|
}
|
|
292
295
|
const turn = context?.turn ?? 0;
|
|
293
296
|
const sandboxId = context?.sandboxId;
|
|
297
|
+
const assistantMessageId = context?.assistantMessageId;
|
|
294
298
|
let rewindSignal;
|
|
295
299
|
if (options.parallel) {
|
|
296
300
|
const scope = new workflow.CancellationScope({ cancellable: true });
|
|
@@ -303,7 +307,13 @@ function createToolRouter(options) {
|
|
|
303
307
|
const outcomes = await scope.run(
|
|
304
308
|
async () => Promise.allSettled(
|
|
305
309
|
toolCalls.map(
|
|
306
|
-
(tc) => processToolCall(
|
|
310
|
+
(tc) => processToolCall(
|
|
311
|
+
tc,
|
|
312
|
+
turn,
|
|
313
|
+
sandboxId,
|
|
314
|
+
onRewindRequested,
|
|
315
|
+
assistantMessageId
|
|
316
|
+
)
|
|
307
317
|
)
|
|
308
318
|
)
|
|
309
319
|
);
|
|
@@ -323,7 +333,13 @@ function createToolRouter(options) {
|
|
|
323
333
|
}
|
|
324
334
|
const results = [];
|
|
325
335
|
for (const toolCall of toolCalls) {
|
|
326
|
-
const outcome = await processToolCall(
|
|
336
|
+
const outcome = await processToolCall(
|
|
337
|
+
toolCall,
|
|
338
|
+
turn,
|
|
339
|
+
sandboxId,
|
|
340
|
+
void 0,
|
|
341
|
+
assistantMessageId
|
|
342
|
+
);
|
|
327
343
|
if (outcome.kind === "rewind") {
|
|
328
344
|
rewindSignal = outcome.signal;
|
|
329
345
|
break;
|
|
@@ -347,6 +363,9 @@ function createToolRouter(options) {
|
|
|
347
363
|
toolName: toolCall.name,
|
|
348
364
|
...context?.sandboxId !== void 0 && {
|
|
349
365
|
sandboxId: context.sandboxId
|
|
366
|
+
},
|
|
367
|
+
...context?.assistantMessageId !== void 0 && {
|
|
368
|
+
assistantMessageId: context.assistantMessageId
|
|
350
369
|
}
|
|
351
370
|
};
|
|
352
371
|
const response = await handler(
|
|
@@ -572,13 +591,27 @@ function createSubagentHandler(subagents) {
|
|
|
572
591
|
}
|
|
573
592
|
const threadMode = config.thread ?? "new";
|
|
574
593
|
const allowsContinuation = threadMode !== "new";
|
|
575
|
-
const
|
|
594
|
+
const newThreadSource = config.newThreadSource ?? "new";
|
|
595
|
+
const usingParentFallback = allowsContinuation && !args.threadId && newThreadSource === "from-parent";
|
|
596
|
+
const continuationThreadId = !allowsContinuation ? void 0 : args.threadId ?? (usingParentFallback ? context.threadId : void 0);
|
|
576
597
|
let thread;
|
|
577
598
|
if (continuationThreadId) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
599
|
+
if (threadMode === "fork") {
|
|
600
|
+
thread = {
|
|
601
|
+
mode: "fork",
|
|
602
|
+
threadId: continuationThreadId,
|
|
603
|
+
...usingParentFallback && context.assistantMessageId ? {
|
|
604
|
+
truncateAfterFork: {
|
|
605
|
+
fromMessageId: context.assistantMessageId
|
|
606
|
+
}
|
|
607
|
+
} : {}
|
|
608
|
+
};
|
|
609
|
+
} else {
|
|
610
|
+
thread = {
|
|
611
|
+
mode: "continue",
|
|
612
|
+
threadId: continuationThreadId
|
|
613
|
+
};
|
|
614
|
+
}
|
|
582
615
|
}
|
|
583
616
|
let sandbox;
|
|
584
617
|
let sandboxShutdownOverride;
|
|
@@ -1039,6 +1072,7 @@ async function createSession(config) {
|
|
|
1039
1072
|
appendSystemMessage,
|
|
1040
1073
|
appendAgentMessage,
|
|
1041
1074
|
forkThread,
|
|
1075
|
+
truncateThread,
|
|
1042
1076
|
loadThreadState,
|
|
1043
1077
|
saveThreadState,
|
|
1044
1078
|
hydrateThread,
|
|
@@ -1188,6 +1222,10 @@ async function createSession(config) {
|
|
|
1188
1222
|
if (threadMode === "fork" && sourceThreadId) {
|
|
1189
1223
|
await hydrateThread(sourceThreadId, threadKey);
|
|
1190
1224
|
await forkThread(sourceThreadId, threadId, threadKey);
|
|
1225
|
+
const truncate = threadInit.truncateAfterFork;
|
|
1226
|
+
if (truncate?.fromMessageId) {
|
|
1227
|
+
await truncateThread(threadId, truncate.fromMessageId, threadKey);
|
|
1228
|
+
}
|
|
1191
1229
|
const forkedSlice = await loadThreadState(threadId, threadKey);
|
|
1192
1230
|
if (forkedSlice) rehydrateFromSlice(forkedSlice);
|
|
1193
1231
|
} else if (threadMode === "continue") {
|
|
@@ -1317,7 +1355,10 @@ async function createSession(config) {
|
|
|
1317
1355
|
parsedToolCalls,
|
|
1318
1356
|
{
|
|
1319
1357
|
turn: currentTurn,
|
|
1320
|
-
...sandboxId !== void 0 && { sandboxId }
|
|
1358
|
+
...sandboxId !== void 0 && { sandboxId },
|
|
1359
|
+
...assistantId !== void 0 && {
|
|
1360
|
+
assistantMessageId: assistantId
|
|
1361
|
+
}
|
|
1321
1362
|
}
|
|
1322
1363
|
);
|
|
1323
1364
|
for (const result of toolCallResults) {
|
|
@@ -2220,6 +2261,13 @@ IMPORTANT:
|
|
|
2220
2261
|
}),
|
|
2221
2262
|
strict: true
|
|
2222
2263
|
};
|
|
2264
|
+
var textEditSchema = z14.z.object({
|
|
2265
|
+
old_string: z14.z.string().describe("The exact text to replace"),
|
|
2266
|
+
new_string: z14.z.string().describe("The text to replace it with"),
|
|
2267
|
+
replace_all: z14.z.boolean().optional().describe(
|
|
2268
|
+
"If true, replace all occurrences of old_string for this edit (default: false)"
|
|
2269
|
+
)
|
|
2270
|
+
});
|
|
2223
2271
|
var editTool = {
|
|
2224
2272
|
name: "FileEdit",
|
|
2225
2273
|
description: `Edit specific sections of a file by replacing text.
|
|
@@ -2248,6 +2296,27 @@ IMPORTANT:
|
|
|
2248
2296
|
}),
|
|
2249
2297
|
strict: true
|
|
2250
2298
|
};
|
|
2299
|
+
var multiEditTool = {
|
|
2300
|
+
name: "FileMultiEdit",
|
|
2301
|
+
description: `Apply multiple exact text replacements to one file in order.
|
|
2302
|
+
|
|
2303
|
+
Usage:
|
|
2304
|
+
- Use this when a task needs several related edits in the same file
|
|
2305
|
+
- Each edit is applied to the file content produced by the prior edit
|
|
2306
|
+
- The operation is atomic: if any edit fails, the file is left unchanged
|
|
2307
|
+
|
|
2308
|
+
IMPORTANT:
|
|
2309
|
+
- You must read the file first (in this session) before editing it
|
|
2310
|
+
- Each old_string must match exactly (whitespace-sensitive)
|
|
2311
|
+
- Each old_string must be unique unless that edit uses replace_all: true
|
|
2312
|
+
- old_string and new_string must be different for every edit
|
|
2313
|
+
`,
|
|
2314
|
+
schema: z14.z.object({
|
|
2315
|
+
file_path: z14.z.string().describe("The absolute virtual path to the file to modify"),
|
|
2316
|
+
edits: z14.z.array(textEditSchema).min(1).describe("Exact replacements to apply sequentially to the file")
|
|
2317
|
+
}),
|
|
2318
|
+
strict: true
|
|
2319
|
+
};
|
|
2251
2320
|
var taskCreateTool = {
|
|
2252
2321
|
name: "TaskCreate",
|
|
2253
2322
|
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 +2387,7 @@ function createTaskCreateHandler(stateManager) {
|
|
|
2318
2387
|
};
|
|
2319
2388
|
stateManager.setTask(task);
|
|
2320
2389
|
return {
|
|
2321
|
-
toolResponse:
|
|
2390
|
+
toolResponse: `Task ${task.id} created`,
|
|
2322
2391
|
data: task
|
|
2323
2392
|
};
|
|
2324
2393
|
};
|
|
@@ -2417,7 +2486,7 @@ function createTaskUpdateHandler(stateManager) {
|
|
|
2417
2486
|
}
|
|
2418
2487
|
stateManager.setTask(task);
|
|
2419
2488
|
return {
|
|
2420
|
-
toolResponse:
|
|
2489
|
+
toolResponse: `Task ${task.id} updated`,
|
|
2421
2490
|
data: task
|
|
2422
2491
|
};
|
|
2423
2492
|
};
|
|
@@ -2749,6 +2818,44 @@ function createThreadManager(config) {
|
|
|
2749
2818
|
}
|
|
2750
2819
|
};
|
|
2751
2820
|
}
|
|
2821
|
+
function getActivityContext() {
|
|
2822
|
+
try {
|
|
2823
|
+
const ctx = activity.Context.current();
|
|
2824
|
+
return { heartbeat: () => ctx.heartbeat(), signal: ctx.cancellationSignal };
|
|
2825
|
+
} catch {
|
|
2826
|
+
return {};
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
async function queryParentWorkflowState(client) {
|
|
2830
|
+
const { workflowExecution } = activity.Context.current().info;
|
|
2831
|
+
if (!workflowExecution) {
|
|
2832
|
+
throw new Error("No workflow execution found");
|
|
2833
|
+
}
|
|
2834
|
+
const handle = client.getHandle(
|
|
2835
|
+
workflowExecution.workflowId,
|
|
2836
|
+
workflowExecution.runId
|
|
2837
|
+
);
|
|
2838
|
+
return handle.query("getAgentState");
|
|
2839
|
+
}
|
|
2840
|
+
function createRunAgentActivity(client, handler, scope) {
|
|
2841
|
+
const name = `run${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
|
|
2842
|
+
return {
|
|
2843
|
+
[name]: async (config) => {
|
|
2844
|
+
const state = await queryParentWorkflowState(client);
|
|
2845
|
+
return handler({ ...config, state });
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
function withParentWorkflowState(client, handler) {
|
|
2850
|
+
return async (args, context) => {
|
|
2851
|
+
const state = await queryParentWorkflowState(client);
|
|
2852
|
+
return handler(args, { ...context, state });
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// src/lib/thread/cold-store.ts
|
|
2857
|
+
var gzipAsync = util.promisify(zlib.gzip);
|
|
2858
|
+
var gunzipAsync = util.promisify(zlib.gunzip);
|
|
2752
2859
|
function joinKey(parts) {
|
|
2753
2860
|
return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p.length > 0).join("/");
|
|
2754
2861
|
}
|
|
@@ -2760,9 +2867,17 @@ function buildKey(prefix, threadKey, threadId, gzip) {
|
|
|
2760
2867
|
`${threadId}.${ext}`
|
|
2761
2868
|
]);
|
|
2762
2869
|
}
|
|
2763
|
-
async function streamToBuffer(body) {
|
|
2870
|
+
async function streamToBuffer(body, onChunk) {
|
|
2764
2871
|
if (body == null) return Buffer.alloc(0);
|
|
2765
2872
|
if (body instanceof Uint8Array) return Buffer.from(body);
|
|
2873
|
+
if (typeof body[Symbol.asyncIterator] === "function") {
|
|
2874
|
+
const chunks = [];
|
|
2875
|
+
for await (const chunk of body) {
|
|
2876
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2877
|
+
onChunk?.();
|
|
2878
|
+
}
|
|
2879
|
+
return Buffer.concat(chunks);
|
|
2880
|
+
}
|
|
2766
2881
|
if (typeof body.transformToByteArray === "function") {
|
|
2767
2882
|
const bytes = await body.transformToByteArray();
|
|
2768
2883
|
return Buffer.from(bytes);
|
|
@@ -2771,11 +2886,7 @@ async function streamToBuffer(body) {
|
|
|
2771
2886
|
const ab = await body.arrayBuffer();
|
|
2772
2887
|
return Buffer.from(ab);
|
|
2773
2888
|
}
|
|
2774
|
-
|
|
2775
|
-
for await (const chunk of body) {
|
|
2776
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2777
|
-
}
|
|
2778
|
-
return Buffer.concat(chunks);
|
|
2889
|
+
return Buffer.alloc(0);
|
|
2779
2890
|
}
|
|
2780
2891
|
function createS3ColdStore(config) {
|
|
2781
2892
|
const { s3, bucket, prefix, gzip = true } = config;
|
|
@@ -2787,8 +2898,9 @@ function createS3ColdStore(config) {
|
|
|
2787
2898
|
const resp = await s3.send(
|
|
2788
2899
|
new clientS3.GetObjectCommand({ Bucket: bucket, Key })
|
|
2789
2900
|
);
|
|
2790
|
-
const
|
|
2791
|
-
const
|
|
2901
|
+
const { heartbeat } = getActivityContext();
|
|
2902
|
+
const buf = await streamToBuffer(resp.Body, heartbeat);
|
|
2903
|
+
const json = gzip ? (await gunzipAsync(buf)).toString("utf8") : buf.toString("utf8");
|
|
2792
2904
|
return JSON.parse(json);
|
|
2793
2905
|
} catch (err) {
|
|
2794
2906
|
if (isNotFound(err)) return null;
|
|
@@ -2798,15 +2910,14 @@ function createS3ColdStore(config) {
|
|
|
2798
2910
|
async write(threadKey, threadId, snapshot) {
|
|
2799
2911
|
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2800
2912
|
const json = JSON.stringify(snapshot);
|
|
2801
|
-
const body = gzip ?
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
);
|
|
2913
|
+
const body = gzip ? await gzipAsync(Buffer.from(json, "utf8")) : json;
|
|
2914
|
+
const upload = new libStorage.Upload({
|
|
2915
|
+
client: s3,
|
|
2916
|
+
params: { Bucket: bucket, Key, Body: body, ContentType: contentType }
|
|
2917
|
+
});
|
|
2918
|
+
const { heartbeat } = getActivityContext();
|
|
2919
|
+
if (heartbeat) upload.on("httpUploadProgress", heartbeat);
|
|
2920
|
+
await upload.done();
|
|
2810
2921
|
},
|
|
2811
2922
|
async delete(threadKey, threadId) {
|
|
2812
2923
|
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
@@ -2934,40 +3045,6 @@ function createTieredThreadManager(config) {
|
|
|
2934
3045
|
}
|
|
2935
3046
|
});
|
|
2936
3047
|
}
|
|
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
3048
|
|
|
2972
3049
|
// src/lib/sandbox/manager.ts
|
|
2973
3050
|
var CAP_METHOD_TO_CAPABILITY = [
|
|
@@ -3639,57 +3716,190 @@ ${result.stderr}`,
|
|
|
3639
3716
|
};
|
|
3640
3717
|
|
|
3641
3718
|
// src/tools/edit/handler.ts
|
|
3642
|
-
function
|
|
3643
|
-
|
|
3719
|
+
function splitLines(text) {
|
|
3720
|
+
if (text.length === 0) return [];
|
|
3721
|
+
return text.replace(/\r\n/g, "\n").split("\n");
|
|
3722
|
+
}
|
|
3723
|
+
function lineNumberAt(content, index) {
|
|
3724
|
+
let line = 1;
|
|
3725
|
+
for (let i = 0; i < index; i++) {
|
|
3726
|
+
if (content.charCodeAt(i) === 10) line++;
|
|
3727
|
+
}
|
|
3728
|
+
return line;
|
|
3729
|
+
}
|
|
3730
|
+
function lineEnd(startLine, lines) {
|
|
3731
|
+
return lines.length === 0 ? startLine : startLine + lines.length - 1;
|
|
3732
|
+
}
|
|
3733
|
+
function indicesOf(content, needle) {
|
|
3734
|
+
const indices = [];
|
|
3735
|
+
let cursor = 0;
|
|
3736
|
+
while (cursor <= content.length) {
|
|
3737
|
+
const index = content.indexOf(needle, cursor);
|
|
3738
|
+
if (index === -1) break;
|
|
3739
|
+
indices.push(index);
|
|
3740
|
+
cursor = index + needle.length;
|
|
3741
|
+
}
|
|
3742
|
+
return indices;
|
|
3743
|
+
}
|
|
3744
|
+
function makeHunk(editIndex, beforeContent, replacementIndex, oldString, newString) {
|
|
3745
|
+
const oldStartLine = lineNumberAt(beforeContent, replacementIndex);
|
|
3746
|
+
const oldLines = splitLines(oldString);
|
|
3747
|
+
const newLines = splitLines(newString);
|
|
3748
|
+
return {
|
|
3749
|
+
editIndex,
|
|
3750
|
+
oldStartLine,
|
|
3751
|
+
oldEndLine: lineEnd(oldStartLine, oldLines),
|
|
3752
|
+
newStartLine: oldStartLine,
|
|
3753
|
+
newEndLine: lineEnd(oldStartLine, newLines),
|
|
3754
|
+
oldLines,
|
|
3755
|
+
newLines
|
|
3756
|
+
};
|
|
3757
|
+
}
|
|
3758
|
+
function applyOneEdit(content, edit, editIndex) {
|
|
3759
|
+
const { old_string, new_string, replace_all = false } = edit;
|
|
3760
|
+
if (old_string.length === 0) {
|
|
3761
|
+
return {
|
|
3762
|
+
ok: false,
|
|
3763
|
+
editIndex,
|
|
3764
|
+
message: `Error: old_string for edit ${editIndex} must not be empty.`
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
if (old_string === new_string) {
|
|
3768
|
+
return {
|
|
3769
|
+
ok: false,
|
|
3770
|
+
editIndex,
|
|
3771
|
+
message: `Error: old_string and new_string must be different for edit ${editIndex}.`
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
const matches = indicesOf(content, old_string);
|
|
3775
|
+
if (matches.length === 0) {
|
|
3776
|
+
return {
|
|
3777
|
+
ok: false,
|
|
3778
|
+
editIndex,
|
|
3779
|
+
message: `Error: Could not find old_string for edit ${editIndex}. Make sure it matches exactly (whitespace-sensitive).`
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
if (!replace_all && matches.length > 1) {
|
|
3783
|
+
return {
|
|
3784
|
+
ok: false,
|
|
3785
|
+
editIndex,
|
|
3786
|
+
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.`
|
|
3787
|
+
};
|
|
3788
|
+
}
|
|
3789
|
+
if (replace_all) {
|
|
3790
|
+
const hunks = matches.map(
|
|
3791
|
+
(index2) => makeHunk(editIndex, content, index2, old_string, new_string)
|
|
3792
|
+
);
|
|
3793
|
+
return {
|
|
3794
|
+
ok: true,
|
|
3795
|
+
content: content.split(old_string).join(new_string),
|
|
3796
|
+
replacements: matches.length,
|
|
3797
|
+
hunks
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
const index = matches[0];
|
|
3801
|
+
if (index === void 0) {
|
|
3802
|
+
return {
|
|
3803
|
+
ok: false,
|
|
3804
|
+
editIndex,
|
|
3805
|
+
message: `Error: Could not find old_string for edit ${editIndex}.`
|
|
3806
|
+
};
|
|
3807
|
+
}
|
|
3808
|
+
return {
|
|
3809
|
+
ok: true,
|
|
3810
|
+
content: content.slice(0, index) + new_string + content.slice(index + old_string.length),
|
|
3811
|
+
replacements: 1,
|
|
3812
|
+
hunks: [makeHunk(editIndex, content, index, old_string, new_string)]
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function applyEditPlan(content, edits) {
|
|
3816
|
+
if (edits.length === 0) {
|
|
3817
|
+
return {
|
|
3818
|
+
ok: false,
|
|
3819
|
+
message: "Error: edits must contain at least one edit."
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
let current = content;
|
|
3823
|
+
let replacements = 0;
|
|
3824
|
+
const hunks = [];
|
|
3825
|
+
for (const [index, edit] of edits.entries()) {
|
|
3826
|
+
const result = applyOneEdit(current, edit, index);
|
|
3827
|
+
if (!result.ok) return result;
|
|
3828
|
+
current = result.content;
|
|
3829
|
+
replacements += result.replacements;
|
|
3830
|
+
hunks.push(...result.hunks);
|
|
3831
|
+
}
|
|
3832
|
+
return { ok: true, content: current, replacements, hunks };
|
|
3833
|
+
}
|
|
3834
|
+
function editFailureResult(filePath, message) {
|
|
3835
|
+
return {
|
|
3836
|
+
toolResponse: message,
|
|
3837
|
+
data: { path: filePath, success: false, replacements: 0 }
|
|
3838
|
+
};
|
|
3644
3839
|
}
|
|
3645
3840
|
var editHandler = async (args, { sandbox }) => {
|
|
3646
3841
|
const { fs } = sandbox;
|
|
3647
3842
|
const { file_path, old_string, new_string, replace_all = false } = args;
|
|
3648
|
-
|
|
3843
|
+
try {
|
|
3844
|
+
const exists = await fs.exists(file_path);
|
|
3845
|
+
if (!exists) {
|
|
3846
|
+
return editFailureResult(
|
|
3847
|
+
file_path,
|
|
3848
|
+
`Error: File "${file_path}" does not exist.`
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
const content = await fs.readFile(file_path);
|
|
3852
|
+
const result = applyEditPlan(content, [
|
|
3853
|
+
{ old_string, new_string, replace_all }
|
|
3854
|
+
]);
|
|
3855
|
+
if (!result.ok) {
|
|
3856
|
+
return editFailureResult(file_path, result.message);
|
|
3857
|
+
}
|
|
3858
|
+
await fs.writeFile(file_path, result.content);
|
|
3859
|
+
const summary = replace_all ? `Replaced ${result.replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
3860
|
+
return {
|
|
3861
|
+
toolResponse: `${summary} in ${file_path}`,
|
|
3862
|
+
data: {
|
|
3863
|
+
path: file_path,
|
|
3864
|
+
success: true,
|
|
3865
|
+
replacements: result.replacements,
|
|
3866
|
+
hunks: result.hunks
|
|
3867
|
+
}
|
|
3868
|
+
};
|
|
3869
|
+
} catch (error) {
|
|
3870
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3649
3871
|
return {
|
|
3650
|
-
toolResponse: `Error
|
|
3872
|
+
toolResponse: `Error editing file "${file_path}": ${message}`,
|
|
3651
3873
|
data: { path: file_path, success: false, replacements: 0 }
|
|
3652
3874
|
};
|
|
3653
3875
|
}
|
|
3876
|
+
};
|
|
3877
|
+
var multiEditHandler = async (args, { sandbox }) => {
|
|
3878
|
+
const { fs } = sandbox;
|
|
3879
|
+
const { file_path, edits } = args;
|
|
3654
3880
|
try {
|
|
3655
3881
|
const exists = await fs.exists(file_path);
|
|
3656
3882
|
if (!exists) {
|
|
3657
|
-
return
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3883
|
+
return editFailureResult(
|
|
3884
|
+
file_path,
|
|
3885
|
+
`Error: File "${file_path}" does not exist.`
|
|
3886
|
+
);
|
|
3661
3887
|
}
|
|
3662
3888
|
const content = await fs.readFile(file_path);
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
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;
|
|
3889
|
+
const result = applyEditPlan(content, edits);
|
|
3890
|
+
if (!result.ok) {
|
|
3891
|
+
const suffix = result.editIndex === void 0 ? "" : ` in ${file_path}`;
|
|
3892
|
+
return editFailureResult(file_path, `${result.message}${suffix}`);
|
|
3687
3893
|
}
|
|
3688
|
-
await fs.writeFile(file_path,
|
|
3689
|
-
const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
3894
|
+
await fs.writeFile(file_path, result.content);
|
|
3690
3895
|
return {
|
|
3691
|
-
toolResponse:
|
|
3692
|
-
data: {
|
|
3896
|
+
toolResponse: `Applied ${edits.length} edit(s), ${result.replacements} replacement(s) in ${file_path}`,
|
|
3897
|
+
data: {
|
|
3898
|
+
path: file_path,
|
|
3899
|
+
success: true,
|
|
3900
|
+
replacements: result.replacements,
|
|
3901
|
+
hunks: result.hunks
|
|
3902
|
+
}
|
|
3693
3903
|
};
|
|
3694
3904
|
} catch (error) {
|
|
3695
3905
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -3923,6 +4133,8 @@ exports.hasDirectory = hasDirectory;
|
|
|
3923
4133
|
exports.hasFileWithMimeType = hasFileWithMimeType;
|
|
3924
4134
|
exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
|
|
3925
4135
|
exports.isTerminalStatus = isTerminalStatus;
|
|
4136
|
+
exports.multiEditHandler = multiEditHandler;
|
|
4137
|
+
exports.multiEditTool = multiEditTool;
|
|
3926
4138
|
exports.parseSkillFile = parseSkillFile;
|
|
3927
4139
|
exports.proxyRunAgent = proxyRunAgent;
|
|
3928
4140
|
exports.proxyVirtualFsOps = proxyVirtualFsOps;
|