zeitlich 0.2.45 → 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.
- package/README.md +137 -11
- package/dist/{activities-Coafq5zr.d.cts → activities-CPwKoUlD.d.cts} +22 -2
- package/dist/{activities-CrN-ghLo.d.ts → activities-DlaBxNID.d.ts} +22 -2
- package/dist/adapters/thread/anthropic/index.cjs +276 -71
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +62 -8
- package/dist/adapters/thread/anthropic/index.d.ts +62 -8
- package/dist/adapters/thread/anthropic/index.js +275 -72
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +38 -20
- 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 -20
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +171 -69
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +6 -4
- package/dist/adapters/thread/google-genai/index.d.ts +6 -4
- package/dist/adapters/thread/google-genai/index.js +171 -69
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +38 -20
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +7 -4
- package/dist/adapters/thread/google-genai/workflow.d.ts +7 -4
- package/dist/adapters/thread/google-genai/workflow.js +38 -20
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +170 -66
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +19 -4
- package/dist/adapters/thread/langchain/index.d.ts +19 -4
- package/dist/adapters/thread/langchain/index.js +170 -66
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +38 -20
- 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 -20
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/cold-store-BDgJpwLI.d.ts +114 -0
- package/dist/cold-store-Z2wvK2cV.d.cts +114 -0
- package/dist/index.cjs +440 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -8
- package/dist/index.d.ts +150 -8
- package/dist/index.js +432 -68
- package/dist/index.js.map +1 -1
- package/dist/proxy-CDh3Rsa7.d.cts +40 -0
- package/dist/proxy-Du8ggERu.d.ts +40 -0
- package/dist/{thread-manager-wRVVBFgj.d.cts → thread-manager-BjoYYXgd.d.cts} +8 -2
- package/dist/{thread-manager-BsLO3Fgc.d.cts → thread-manager-D8zKNFZ9.d.cts} +8 -2
- package/dist/{thread-manager-Bi1XlbpJ.d.ts → thread-manager-DtHYws2F.d.ts} +8 -2
- package/dist/{thread-manager-BhkOyQ1I.d.ts → thread-manager-Dw96FKH1.d.ts} +8 -2
- package/dist/{types-C66-BVBr.d.cts → types-BMJrsHo0.d.cts} +17 -1
- package/dist/{types-BkX4HLzi.d.ts → types-CtdOquo3.d.ts} +17 -1
- package/dist/{types-CdALEF3z.d.cts → types-DNEl5uxQ.d.cts} +38 -0
- package/dist/{types-ChAy_jSP.d.ts → types-qQVZfhoT.d.ts} +38 -0
- package/dist/{workflow-DMmiaw6w.d.cts → workflow-BH9ImDGq.d.cts} +48 -2
- package/dist/{workflow-BwT5EybR.d.ts → workflow-Cdw3-RNB.d.ts} +48 -2
- package/dist/workflow.cjs +47 -4
- 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 +47 -5
- package/dist/workflow.js.map +1 -1
- package/package.json +14 -3
- package/src/adapters/thread/anthropic/activities.ts +82 -39
- 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/anthropic/thread-manager.ts +9 -1
- package/src/adapters/thread/google-genai/activities.ts +64 -40
- package/src/adapters/thread/google-genai/proxy.ts +1 -0
- package/src/adapters/thread/google-genai/thread-manager.ts +9 -1
- package/src/adapters/thread/langchain/activities.ts +63 -36
- package/src/adapters/thread/langchain/proxy.ts +1 -0
- package/src/adapters/thread/langchain/thread-manager.ts +9 -1
- package/src/index.ts +21 -2
- package/src/lib/session/session-edge-cases.integration.test.ts +12 -0
- package/src/lib/session/session.integration.test.ts +138 -0
- package/src/lib/session/session.ts +29 -0
- package/src/lib/session/types.ts +22 -0
- package/src/lib/subagent/define.ts +1 -0
- package/src/lib/subagent/handler.ts +11 -2
- package/src/lib/subagent/subagent.integration.test.ts +139 -0
- package/src/lib/subagent/types.ts +16 -0
- package/src/lib/thread/cold-store.test.ts +221 -0
- package/src/lib/thread/cold-store.ts +269 -0
- package/src/lib/thread/index.ts +32 -0
- package/src/lib/thread/keys.ts +20 -0
- package/src/lib/thread/manager.ts +16 -27
- package/src/lib/thread/proxy.ts +79 -27
- package/src/lib/thread/snapshot.test.ts +443 -0
- package/src/lib/thread/snapshot.ts +163 -0
- package/src/lib/thread/test-utils.ts +228 -0
- package/src/lib/thread/tiered.test.ts +281 -0
- package/src/lib/thread/tiered.ts +135 -0
- package/src/lib/thread/types.ts +16 -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-Bf7uI-Hw.d.cts +0 -24
- package/dist/proxy-COqA95FW.d.ts +0 -24
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,10 @@ var z14 = require('zod');
|
|
|
5
5
|
var crypto = require('crypto');
|
|
6
6
|
var common = require('@temporalio/common');
|
|
7
7
|
var path = require('path');
|
|
8
|
+
var zlib = require('zlib');
|
|
9
|
+
var util = require('util');
|
|
10
|
+
var clientS3 = require('@aws-sdk/client-s3');
|
|
11
|
+
var libStorage = require('@aws-sdk/lib-storage');
|
|
8
12
|
var activity = require('@temporalio/activity');
|
|
9
13
|
var fs = require('fs');
|
|
10
14
|
|
|
@@ -570,7 +574,8 @@ function createSubagentHandler(subagents) {
|
|
|
570
574
|
}
|
|
571
575
|
const threadMode = config.thread ?? "new";
|
|
572
576
|
const allowsContinuation = threadMode !== "new";
|
|
573
|
-
const
|
|
577
|
+
const newThreadSource = config.newThreadSource ?? "new";
|
|
578
|
+
const continuationThreadId = !allowsContinuation ? void 0 : args.threadId ?? (newThreadSource === "from-parent" ? context.threadId : void 0);
|
|
574
579
|
let thread;
|
|
575
580
|
if (continuationThreadId) {
|
|
576
581
|
thread = {
|
|
@@ -1038,7 +1043,9 @@ async function createSession(config) {
|
|
|
1038
1043
|
appendAgentMessage,
|
|
1039
1044
|
forkThread,
|
|
1040
1045
|
loadThreadState,
|
|
1041
|
-
saveThreadState
|
|
1046
|
+
saveThreadState,
|
|
1047
|
+
hydrateThread,
|
|
1048
|
+
flushThread
|
|
1042
1049
|
} = threadOps;
|
|
1043
1050
|
const plugins = [];
|
|
1044
1051
|
let destroySubagentSandboxes;
|
|
@@ -1182,10 +1189,12 @@ async function createSession(config) {
|
|
|
1182
1189
|
});
|
|
1183
1190
|
};
|
|
1184
1191
|
if (threadMode === "fork" && sourceThreadId) {
|
|
1192
|
+
await hydrateThread(sourceThreadId, threadKey);
|
|
1185
1193
|
await forkThread(sourceThreadId, threadId, threadKey);
|
|
1186
1194
|
const forkedSlice = await loadThreadState(threadId, threadKey);
|
|
1187
1195
|
if (forkedSlice) rehydrateFromSlice(forkedSlice);
|
|
1188
1196
|
} else if (threadMode === "continue") {
|
|
1197
|
+
await hydrateThread(threadId, threadKey);
|
|
1189
1198
|
const continuedSlice = await loadThreadState(threadId, threadKey);
|
|
1190
1199
|
if (continuedSlice) rehydrateFromSlice(continuedSlice);
|
|
1191
1200
|
} else {
|
|
@@ -1367,6 +1376,15 @@ async function createSession(config) {
|
|
|
1367
1376
|
error: persistError instanceof Error ? persistError.message : String(persistError)
|
|
1368
1377
|
});
|
|
1369
1378
|
}
|
|
1379
|
+
try {
|
|
1380
|
+
await flushThread(threadId, threadKey);
|
|
1381
|
+
} catch (flushError) {
|
|
1382
|
+
workflow.log.warn("failed to flush thread to cold tier", {
|
|
1383
|
+
agentName,
|
|
1384
|
+
threadId,
|
|
1385
|
+
error: flushError instanceof Error ? flushError.message : String(flushError)
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1370
1388
|
await callSessionEnd(exitReason, stateManager.getTurns());
|
|
1371
1389
|
if (sandboxOwned && sandboxId && sandboxOps) {
|
|
1372
1390
|
switch (resolvedShutdown) {
|
|
@@ -1450,6 +1468,9 @@ function getThreadMetaKey(threadKey, threadId) {
|
|
|
1450
1468
|
function getThreadStateKey(threadKey, threadId) {
|
|
1451
1469
|
return `${threadKey}:state:thread:${threadId}`;
|
|
1452
1470
|
}
|
|
1471
|
+
function getThreadDedupKey(threadId, dedupId) {
|
|
1472
|
+
return `dedup:${dedupId}:thread:${threadId}`;
|
|
1473
|
+
}
|
|
1453
1474
|
|
|
1454
1475
|
// src/lib/types.ts
|
|
1455
1476
|
function isTerminalStatus(status) {
|
|
@@ -2202,6 +2223,13 @@ IMPORTANT:
|
|
|
2202
2223
|
}),
|
|
2203
2224
|
strict: true
|
|
2204
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
|
+
});
|
|
2205
2233
|
var editTool = {
|
|
2206
2234
|
name: "FileEdit",
|
|
2207
2235
|
description: `Edit specific sections of a file by replacing text.
|
|
@@ -2230,6 +2258,27 @@ IMPORTANT:
|
|
|
2230
2258
|
}),
|
|
2231
2259
|
strict: true
|
|
2232
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
|
+
};
|
|
2233
2282
|
var taskCreateTool = {
|
|
2234
2283
|
name: "TaskCreate",
|
|
2235
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.
|
|
@@ -2300,7 +2349,7 @@ function createTaskCreateHandler(stateManager) {
|
|
|
2300
2349
|
};
|
|
2301
2350
|
stateManager.setTask(task);
|
|
2302
2351
|
return {
|
|
2303
|
-
toolResponse:
|
|
2352
|
+
toolResponse: `Task ${task.id} created`,
|
|
2304
2353
|
data: task
|
|
2305
2354
|
};
|
|
2306
2355
|
};
|
|
@@ -2399,7 +2448,7 @@ function createTaskUpdateHandler(stateManager) {
|
|
|
2399
2448
|
}
|
|
2400
2449
|
stateManager.setTask(task);
|
|
2401
2450
|
return {
|
|
2402
|
-
toolResponse:
|
|
2451
|
+
toolResponse: `Task ${task.id} updated`,
|
|
2403
2452
|
data: task
|
|
2404
2453
|
};
|
|
2405
2454
|
};
|
|
@@ -2594,9 +2643,6 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
|
|
|
2594
2643
|
redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
|
|
2595
2644
|
return 1
|
|
2596
2645
|
`;
|
|
2597
|
-
function getDedupKey(threadId, id) {
|
|
2598
|
-
return `dedup:${id}:thread:${threadId}`;
|
|
2599
|
-
}
|
|
2600
2646
|
function createThreadManager(config) {
|
|
2601
2647
|
const {
|
|
2602
2648
|
redis,
|
|
@@ -2604,11 +2650,13 @@ function createThreadManager(config) {
|
|
|
2604
2650
|
key = "messages",
|
|
2605
2651
|
serialize = (m) => JSON.stringify(m),
|
|
2606
2652
|
deserialize = (raw) => JSON.parse(raw),
|
|
2607
|
-
idOf
|
|
2653
|
+
idOf,
|
|
2654
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2608
2655
|
} = config;
|
|
2609
2656
|
const redisKey = getThreadListKey(key, threadId);
|
|
2610
2657
|
const metaKey = getThreadMetaKey(key, threadId);
|
|
2611
2658
|
const stateKey = getThreadStateKey(key, threadId);
|
|
2659
|
+
const dedupKey = (id) => getThreadDedupKey(threadId, id);
|
|
2612
2660
|
async function assertThreadExists() {
|
|
2613
2661
|
const exists = await redis.exists(metaKey);
|
|
2614
2662
|
if (!exists) {
|
|
@@ -2618,7 +2666,7 @@ function createThreadManager(config) {
|
|
|
2618
2666
|
return {
|
|
2619
2667
|
async initialize() {
|
|
2620
2668
|
await redis.del(redisKey);
|
|
2621
|
-
await redis.set(metaKey, "1", "EX",
|
|
2669
|
+
await redis.set(metaKey, "1", "EX", ttlSeconds);
|
|
2622
2670
|
},
|
|
2623
2671
|
async load() {
|
|
2624
2672
|
await assertThreadExists();
|
|
@@ -2630,18 +2678,17 @@ function createThreadManager(config) {
|
|
|
2630
2678
|
await assertThreadExists();
|
|
2631
2679
|
if (idOf) {
|
|
2632
2680
|
const dedupId = messages.map(idOf).join(":");
|
|
2633
|
-
const dedupKey = getDedupKey(threadId, dedupId);
|
|
2634
2681
|
await redis.eval(
|
|
2635
2682
|
APPEND_IDEMPOTENT_SCRIPT,
|
|
2636
2683
|
2,
|
|
2637
|
-
dedupKey,
|
|
2684
|
+
dedupKey(dedupId),
|
|
2638
2685
|
redisKey,
|
|
2639
|
-
String(
|
|
2686
|
+
String(ttlSeconds),
|
|
2640
2687
|
...messages.map(serialize)
|
|
2641
2688
|
);
|
|
2642
2689
|
} else {
|
|
2643
2690
|
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
2644
|
-
await redis.expire(redisKey,
|
|
2691
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2645
2692
|
}
|
|
2646
2693
|
},
|
|
2647
2694
|
async fork(newThreadId) {
|
|
@@ -2656,11 +2703,11 @@ function createThreadManager(config) {
|
|
|
2656
2703
|
if (data.length > 0) {
|
|
2657
2704
|
const newKey = getThreadListKey(key, newThreadId);
|
|
2658
2705
|
await redis.rpush(newKey, ...data);
|
|
2659
|
-
await redis.expire(newKey,
|
|
2706
|
+
await redis.expire(newKey, ttlSeconds);
|
|
2660
2707
|
}
|
|
2661
2708
|
if (stateRaw != null) {
|
|
2662
2709
|
const newStateKey = getThreadStateKey(key, newThreadId);
|
|
2663
|
-
await redis.set(newStateKey, stateRaw, "EX",
|
|
2710
|
+
await redis.set(newStateKey, stateRaw, "EX", ttlSeconds);
|
|
2664
2711
|
}
|
|
2665
2712
|
return forked;
|
|
2666
2713
|
},
|
|
@@ -2675,15 +2722,13 @@ function createThreadManager(config) {
|
|
|
2675
2722
|
const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
|
|
2676
2723
|
await redis.del(redisKey);
|
|
2677
2724
|
if (existingIds.length > 0) {
|
|
2678
|
-
await redis.del(
|
|
2679
|
-
...existingIds.map((id) => getDedupKey(threadId, id))
|
|
2680
|
-
);
|
|
2725
|
+
await redis.del(...existingIds.map(dedupKey));
|
|
2681
2726
|
}
|
|
2682
2727
|
if (messages.length > 0) {
|
|
2683
2728
|
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
2684
|
-
await redis.expire(redisKey,
|
|
2729
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2685
2730
|
}
|
|
2686
|
-
await redis.expire(metaKey,
|
|
2731
|
+
await redis.expire(metaKey, ttlSeconds);
|
|
2687
2732
|
},
|
|
2688
2733
|
async delete() {
|
|
2689
2734
|
await redis.del(redisKey, metaKey, stateKey);
|
|
@@ -2695,12 +2740,7 @@ function createThreadManager(config) {
|
|
|
2695
2740
|
},
|
|
2696
2741
|
async saveState(state) {
|
|
2697
2742
|
await assertThreadExists();
|
|
2698
|
-
await redis.set(
|
|
2699
|
-
stateKey,
|
|
2700
|
-
JSON.stringify(state),
|
|
2701
|
-
"EX",
|
|
2702
|
-
THREAD_TTL_SECONDS
|
|
2703
|
-
);
|
|
2743
|
+
await redis.set(stateKey, JSON.stringify(state), "EX", ttlSeconds);
|
|
2704
2744
|
},
|
|
2705
2745
|
async deleteState() {
|
|
2706
2746
|
await redis.del(stateKey);
|
|
@@ -2729,15 +2769,13 @@ function createThreadManager(config) {
|
|
|
2729
2769
|
if (idx === -1) return;
|
|
2730
2770
|
if (idx === 0) {
|
|
2731
2771
|
await redis.del(redisKey);
|
|
2732
|
-
await redis.expire(metaKey,
|
|
2772
|
+
await redis.expire(metaKey, ttlSeconds);
|
|
2733
2773
|
} else {
|
|
2734
2774
|
await redis.ltrim(redisKey, 0, idx - 1);
|
|
2735
|
-
await redis.expire(redisKey,
|
|
2775
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2736
2776
|
}
|
|
2737
2777
|
if (removedIds.length > 0) {
|
|
2738
|
-
await redis.del(
|
|
2739
|
-
...removedIds.map((id) => getDedupKey(threadId, id))
|
|
2740
|
-
);
|
|
2778
|
+
await redis.del(...removedIds.map(dedupKey));
|
|
2741
2779
|
}
|
|
2742
2780
|
}
|
|
2743
2781
|
};
|
|
@@ -2777,6 +2815,199 @@ function withParentWorkflowState(client, handler) {
|
|
|
2777
2815
|
};
|
|
2778
2816
|
}
|
|
2779
2817
|
|
|
2818
|
+
// src/lib/thread/cold-store.ts
|
|
2819
|
+
var gzipAsync = util.promisify(zlib.gzip);
|
|
2820
|
+
var gunzipAsync = util.promisify(zlib.gunzip);
|
|
2821
|
+
function joinKey(parts) {
|
|
2822
|
+
return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p.length > 0).join("/");
|
|
2823
|
+
}
|
|
2824
|
+
function buildKey(prefix, threadKey, threadId, gzip) {
|
|
2825
|
+
const ext = gzip ? "json.gz" : "json";
|
|
2826
|
+
return joinKey([
|
|
2827
|
+
prefix ?? "threads",
|
|
2828
|
+
threadKey,
|
|
2829
|
+
`${threadId}.${ext}`
|
|
2830
|
+
]);
|
|
2831
|
+
}
|
|
2832
|
+
async function streamToBuffer(body, onChunk) {
|
|
2833
|
+
if (body == null) return Buffer.alloc(0);
|
|
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
|
+
}
|
|
2843
|
+
if (typeof body.transformToByteArray === "function") {
|
|
2844
|
+
const bytes = await body.transformToByteArray();
|
|
2845
|
+
return Buffer.from(bytes);
|
|
2846
|
+
}
|
|
2847
|
+
if (typeof body.arrayBuffer === "function") {
|
|
2848
|
+
const ab = await body.arrayBuffer();
|
|
2849
|
+
return Buffer.from(ab);
|
|
2850
|
+
}
|
|
2851
|
+
return Buffer.alloc(0);
|
|
2852
|
+
}
|
|
2853
|
+
function createS3ColdStore(config) {
|
|
2854
|
+
const { s3, bucket, prefix, gzip = true } = config;
|
|
2855
|
+
const contentType = config.contentType ?? (gzip ? "application/gzip" : "application/json");
|
|
2856
|
+
return {
|
|
2857
|
+
async read(threadKey, threadId) {
|
|
2858
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2859
|
+
try {
|
|
2860
|
+
const resp = await s3.send(
|
|
2861
|
+
new clientS3.GetObjectCommand({ Bucket: bucket, Key })
|
|
2862
|
+
);
|
|
2863
|
+
const { heartbeat } = getActivityContext();
|
|
2864
|
+
const buf = await streamToBuffer(resp.Body, heartbeat);
|
|
2865
|
+
const json = gzip ? (await gunzipAsync(buf)).toString("utf8") : buf.toString("utf8");
|
|
2866
|
+
return JSON.parse(json);
|
|
2867
|
+
} catch (err) {
|
|
2868
|
+
if (isNotFound(err)) return null;
|
|
2869
|
+
throw err;
|
|
2870
|
+
}
|
|
2871
|
+
},
|
|
2872
|
+
async write(threadKey, threadId, snapshot) {
|
|
2873
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2874
|
+
const json = JSON.stringify(snapshot);
|
|
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();
|
|
2883
|
+
},
|
|
2884
|
+
async delete(threadKey, threadId) {
|
|
2885
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2886
|
+
await s3.send(new clientS3.DeleteObjectCommand({ Bucket: bucket, Key }));
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
function isNotFound(err) {
|
|
2891
|
+
if (typeof err !== "object" || err === null) return false;
|
|
2892
|
+
const e = err;
|
|
2893
|
+
return e.name === "NoSuchKey" || e.Code === "NoSuchKey" || e.code === "NoSuchKey" || e.name === "NotFound" || e.$metadata?.httpStatusCode === 404;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// src/lib/thread/snapshot.ts
|
|
2897
|
+
async function encodeSnapshot(config) {
|
|
2898
|
+
const { redis, threadKey, threadId, idOf } = config;
|
|
2899
|
+
const metaKey = getThreadMetaKey(threadKey, threadId);
|
|
2900
|
+
if (await redis.exists(metaKey) === 0) {
|
|
2901
|
+
return null;
|
|
2902
|
+
}
|
|
2903
|
+
const listKey = getThreadListKey(threadKey, threadId);
|
|
2904
|
+
const stateKey = getThreadStateKey(threadKey, threadId);
|
|
2905
|
+
const messages = await redis.lrange(listKey, 0, -1);
|
|
2906
|
+
const stateRaw = await redis.get(stateKey);
|
|
2907
|
+
const state = stateRaw == null ? null : JSON.parse(stateRaw);
|
|
2908
|
+
const dedupIds = idOf ? messages.map(idOf) : [];
|
|
2909
|
+
return { v: 1, messages, state, dedupIds };
|
|
2910
|
+
}
|
|
2911
|
+
async function applySnapshot(config) {
|
|
2912
|
+
const {
|
|
2913
|
+
redis,
|
|
2914
|
+
threadKey,
|
|
2915
|
+
threadId,
|
|
2916
|
+
snapshot,
|
|
2917
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2918
|
+
} = config;
|
|
2919
|
+
const metaKey = getThreadMetaKey(threadKey, threadId);
|
|
2920
|
+
if (await redis.exists(metaKey) === 1) {
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
const listKey = getThreadListKey(threadKey, threadId);
|
|
2924
|
+
const stateKey = getThreadStateKey(threadKey, threadId);
|
|
2925
|
+
await redis.del(listKey, stateKey);
|
|
2926
|
+
const pipeline = redis.pipeline();
|
|
2927
|
+
if (snapshot.messages.length > 0) {
|
|
2928
|
+
pipeline.rpush(listKey, ...snapshot.messages);
|
|
2929
|
+
pipeline.expire(listKey, ttlSeconds);
|
|
2930
|
+
}
|
|
2931
|
+
if (snapshot.state != null) {
|
|
2932
|
+
pipeline.set(stateKey, JSON.stringify(snapshot.state), "EX", ttlSeconds);
|
|
2933
|
+
}
|
|
2934
|
+
for (const id of snapshot.dedupIds) {
|
|
2935
|
+
pipeline.set(getThreadDedupKey(threadId, id), "1", "EX", ttlSeconds);
|
|
2936
|
+
}
|
|
2937
|
+
const results = await pipeline.exec();
|
|
2938
|
+
if (results) {
|
|
2939
|
+
const firstErr = results.find(([err]) => err)?.[0] ?? null;
|
|
2940
|
+
if (firstErr) {
|
|
2941
|
+
await redis.del(
|
|
2942
|
+
listKey,
|
|
2943
|
+
stateKey,
|
|
2944
|
+
...snapshot.dedupIds.map((id) => getThreadDedupKey(threadId, id))
|
|
2945
|
+
).catch(() => void 0);
|
|
2946
|
+
throw firstErr;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
await redis.set(metaKey, "1", "EX", ttlSeconds);
|
|
2950
|
+
}
|
|
2951
|
+
async function clearHotTier(config) {
|
|
2952
|
+
const { redis, threadKey, threadId, dedupIds = [] } = config;
|
|
2953
|
+
const keys = [
|
|
2954
|
+
getThreadListKey(threadKey, threadId),
|
|
2955
|
+
getThreadMetaKey(threadKey, threadId),
|
|
2956
|
+
getThreadStateKey(threadKey, threadId),
|
|
2957
|
+
...dedupIds.map((id) => getThreadDedupKey(threadId, id))
|
|
2958
|
+
];
|
|
2959
|
+
await redis.del(...keys);
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
// src/lib/thread/tiered.ts
|
|
2963
|
+
function createTieredThreadManager(config) {
|
|
2964
|
+
const {
|
|
2965
|
+
redis,
|
|
2966
|
+
threadId,
|
|
2967
|
+
key = "messages",
|
|
2968
|
+
coldStore,
|
|
2969
|
+
idOf,
|
|
2970
|
+
deserialize = (raw) => JSON.parse(raw),
|
|
2971
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2972
|
+
} = config;
|
|
2973
|
+
const base = createThreadManager(config);
|
|
2974
|
+
const rawIdOf = idOf ? (raw) => idOf(deserialize(raw)) : void 0;
|
|
2975
|
+
return Object.assign(base, {
|
|
2976
|
+
async hydrate() {
|
|
2977
|
+
if (!coldStore) return;
|
|
2978
|
+
const snapshot = await coldStore.read(key, threadId);
|
|
2979
|
+
if (!snapshot) return;
|
|
2980
|
+
await applySnapshot({
|
|
2981
|
+
redis,
|
|
2982
|
+
threadKey: key,
|
|
2983
|
+
threadId,
|
|
2984
|
+
snapshot,
|
|
2985
|
+
ttlSeconds
|
|
2986
|
+
});
|
|
2987
|
+
},
|
|
2988
|
+
async flush(opts) {
|
|
2989
|
+
if (!coldStore) return;
|
|
2990
|
+
const snapshot = await encodeSnapshot({
|
|
2991
|
+
redis,
|
|
2992
|
+
threadKey: key,
|
|
2993
|
+
threadId,
|
|
2994
|
+
...rawIdOf ? { idOf: rawIdOf } : {}
|
|
2995
|
+
});
|
|
2996
|
+
if (!snapshot) return;
|
|
2997
|
+
await coldStore.write(key, threadId, snapshot);
|
|
2998
|
+
const deleteHot = opts?.deleteHot ?? true;
|
|
2999
|
+
if (deleteHot) {
|
|
3000
|
+
await clearHotTier({
|
|
3001
|
+
redis,
|
|
3002
|
+
threadKey: key,
|
|
3003
|
+
threadId,
|
|
3004
|
+
dedupIds: snapshot.dedupIds
|
|
3005
|
+
});
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
|
|
2780
3011
|
// src/lib/sandbox/manager.ts
|
|
2781
3012
|
var CAP_METHOD_TO_CAPABILITY = [
|
|
2782
3013
|
{ method: "pause", capability: "pause" },
|
|
@@ -3447,57 +3678,190 @@ ${result.stderr}`,
|
|
|
3447
3678
|
};
|
|
3448
3679
|
|
|
3449
3680
|
// src/tools/edit/handler.ts
|
|
3450
|
-
function
|
|
3451
|
-
|
|
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
|
+
};
|
|
3452
3801
|
}
|
|
3453
3802
|
var editHandler = async (args, { sandbox }) => {
|
|
3454
3803
|
const { fs } = sandbox;
|
|
3455
3804
|
const { file_path, old_string, new_string, replace_all = false } = args;
|
|
3456
|
-
|
|
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`;
|
|
3457
3822
|
return {
|
|
3458
|
-
toolResponse:
|
|
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";
|
|
3833
|
+
return {
|
|
3834
|
+
toolResponse: `Error editing file "${file_path}": ${message}`,
|
|
3459
3835
|
data: { path: file_path, success: false, replacements: 0 }
|
|
3460
3836
|
};
|
|
3461
3837
|
}
|
|
3838
|
+
};
|
|
3839
|
+
var multiEditHandler = async (args, { sandbox }) => {
|
|
3840
|
+
const { fs } = sandbox;
|
|
3841
|
+
const { file_path, edits } = args;
|
|
3462
3842
|
try {
|
|
3463
3843
|
const exists = await fs.exists(file_path);
|
|
3464
3844
|
if (!exists) {
|
|
3465
|
-
return
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3845
|
+
return editFailureResult(
|
|
3846
|
+
file_path,
|
|
3847
|
+
`Error: File "${file_path}" does not exist.`
|
|
3848
|
+
);
|
|
3469
3849
|
}
|
|
3470
3850
|
const content = await fs.readFile(file_path);
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
};
|
|
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}`);
|
|
3476
3855
|
}
|
|
3477
|
-
|
|
3478
|
-
const globalRegex = new RegExp(escapedOldString, "g");
|
|
3479
|
-
const occurrences = (content.match(globalRegex) || []).length;
|
|
3480
|
-
if (!replace_all && occurrences > 1) {
|
|
3481
|
-
return {
|
|
3482
|
-
toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
|
|
3483
|
-
data: { path: file_path, success: false, replacements: 0 }
|
|
3484
|
-
};
|
|
3485
|
-
}
|
|
3486
|
-
let newContent;
|
|
3487
|
-
let replacements;
|
|
3488
|
-
if (replace_all) {
|
|
3489
|
-
newContent = content.split(old_string).join(new_string);
|
|
3490
|
-
replacements = occurrences;
|
|
3491
|
-
} else {
|
|
3492
|
-
const index = content.indexOf(old_string);
|
|
3493
|
-
newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
|
|
3494
|
-
replacements = 1;
|
|
3495
|
-
}
|
|
3496
|
-
await fs.writeFile(file_path, newContent);
|
|
3497
|
-
const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
3856
|
+
await fs.writeFile(file_path, result.content);
|
|
3498
3857
|
return {
|
|
3499
|
-
toolResponse:
|
|
3500
|
-
data: {
|
|
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
|
+
}
|
|
3501
3865
|
};
|
|
3502
3866
|
} catch (error) {
|
|
3503
3867
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -3685,10 +4049,12 @@ exports.SandboxNotFoundError = SandboxNotFoundError;
|
|
|
3685
4049
|
exports.SandboxNotSupportedError = SandboxNotSupportedError;
|
|
3686
4050
|
exports.THREAD_TTL_SECONDS = THREAD_TTL_SECONDS;
|
|
3687
4051
|
exports.VirtualFileSystem = VirtualFileSystem;
|
|
4052
|
+
exports.applySnapshot = applySnapshot;
|
|
3688
4053
|
exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
|
|
3689
4054
|
exports.askUserQuestionTool = askUserQuestionTool;
|
|
3690
4055
|
exports.bashHandler = bashHandler;
|
|
3691
4056
|
exports.bashTool = bashTool;
|
|
4057
|
+
exports.clearHotTier = clearHotTier;
|
|
3692
4058
|
exports.composeHooks = composeHooks;
|
|
3693
4059
|
exports.createAgentStateManager = createAgentStateManager;
|
|
3694
4060
|
exports.createAskUserQuestionHandler = createAskUserQuestionHandler;
|
|
@@ -3697,12 +4063,14 @@ exports.createObservabilityHooks = createObservabilityHooks;
|
|
|
3697
4063
|
exports.createReadSkillHandler = createReadSkillHandler;
|
|
3698
4064
|
exports.createReadSkillTool = createReadSkillTool;
|
|
3699
4065
|
exports.createRunAgentActivity = createRunAgentActivity;
|
|
4066
|
+
exports.createS3ColdStore = createS3ColdStore;
|
|
3700
4067
|
exports.createSession = createSession;
|
|
3701
4068
|
exports.createTaskCreateHandler = createTaskCreateHandler;
|
|
3702
4069
|
exports.createTaskGetHandler = createTaskGetHandler;
|
|
3703
4070
|
exports.createTaskListHandler = createTaskListHandler;
|
|
3704
4071
|
exports.createTaskUpdateHandler = createTaskUpdateHandler;
|
|
3705
4072
|
exports.createThreadManager = createThreadManager;
|
|
4073
|
+
exports.createTieredThreadManager = createTieredThreadManager;
|
|
3706
4074
|
exports.createToolRouter = createToolRouter;
|
|
3707
4075
|
exports.createVirtualFsActivities = createVirtualFsActivities;
|
|
3708
4076
|
exports.defineSubagent = defineSubagent;
|
|
@@ -3711,12 +4079,15 @@ exports.defineTool = defineTool;
|
|
|
3711
4079
|
exports.defineWorkflow = defineWorkflow;
|
|
3712
4080
|
exports.editHandler = editHandler;
|
|
3713
4081
|
exports.editTool = editTool;
|
|
4082
|
+
exports.encodeSnapshot = encodeSnapshot;
|
|
3714
4083
|
exports.filesWithMimeType = filesWithMimeType;
|
|
3715
4084
|
exports.formatVirtualFileTree = formatVirtualFileTree;
|
|
3716
4085
|
exports.getActivityContext = getActivityContext;
|
|
3717
4086
|
exports.getShortId = getShortId;
|
|
4087
|
+
exports.getThreadDedupKey = getThreadDedupKey;
|
|
3718
4088
|
exports.getThreadListKey = getThreadListKey;
|
|
3719
4089
|
exports.getThreadMetaKey = getThreadMetaKey;
|
|
4090
|
+
exports.getThreadStateKey = getThreadStateKey;
|
|
3720
4091
|
exports.globHandler = globHandler;
|
|
3721
4092
|
exports.globTool = globTool;
|
|
3722
4093
|
exports.grepTool = grepTool;
|
|
@@ -3724,6 +4095,8 @@ exports.hasDirectory = hasDirectory;
|
|
|
3724
4095
|
exports.hasFileWithMimeType = hasFileWithMimeType;
|
|
3725
4096
|
exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
|
|
3726
4097
|
exports.isTerminalStatus = isTerminalStatus;
|
|
4098
|
+
exports.multiEditHandler = multiEditHandler;
|
|
4099
|
+
exports.multiEditTool = multiEditTool;
|
|
3727
4100
|
exports.parseSkillFile = parseSkillFile;
|
|
3728
4101
|
exports.proxyRunAgent = proxyRunAgent;
|
|
3729
4102
|
exports.proxyVirtualFsOps = proxyVirtualFsOps;
|