zeitlich 0.2.44 → 0.2.46
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 +78 -10
- package/dist/{activities-CPIB2v2C.d.ts → activities-Bm4TLTid.d.ts} +24 -4
- package/dist/{activities-DnmNOnq4.d.cts → activities-CyeiqK_f.d.cts} +24 -4
- package/dist/adapters/sandbox/daytona/index.d.cts +2 -2
- package/dist/adapters/sandbox/daytona/index.d.ts +2 -2
- package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +171 -65
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +19 -4
- package/dist/adapters/thread/anthropic/index.d.ts +19 -4
- package/dist/adapters/thread/anthropic/index.js +171 -65
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +3 -1
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +4 -4
- package/dist/adapters/thread/anthropic/workflow.d.ts +4 -4
- package/dist/adapters/thread/anthropic/workflow.js +3 -1
- 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 +5 -4
- package/dist/adapters/thread/google-genai/index.d.ts +5 -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 +3 -1
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -4
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -4
- package/dist/adapters/thread/google-genai/workflow.js +3 -1
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +181 -77
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +18 -4
- package/dist/adapters/thread/langchain/index.d.ts +18 -4
- package/dist/adapters/thread/langchain/index.js +182 -74
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +3 -1
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +4 -4
- package/dist/adapters/thread/langchain/workflow.d.ts +4 -4
- package/dist/adapters/thread/langchain/workflow.js +3 -1
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/cold-store-BC5L5Z8A.d.cts +117 -0
- package/dist/cold-store-CFHwemBJ.d.ts +117 -0
- package/dist/index.cjs +252 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -8
- package/dist/index.d.ts +138 -8
- package/dist/index.js +247 -54
- package/dist/index.js.map +1 -1
- package/dist/{proxy-DTnc5rqT.d.cts → proxy-BxFyd6cg.d.cts} +1 -1
- package/dist/{proxy-B7Xi1znZ.d.ts → proxy-Cskmj4Yx.d.ts} +1 -1
- package/dist/{thread-manager-BlX2TwRN.d.cts → thread-manager-9tezUcLW.d.cts} +9 -3
- package/dist/{thread-manager-BAv340mi.d.ts → thread-manager-B-zy3xrs.d.ts} +9 -3
- package/dist/{thread-manager-D2xorI-J.d.ts → thread-manager-D33SUmZa.d.cts} +10 -4
- package/dist/{thread-manager-BWv6ZXI3.d.cts → thread-manager-DduoSkvJ.d.ts} +10 -4
- package/dist/{types-C90VoEpt.d.cts → types-CjY93AWZ.d.cts} +1 -1
- package/dist/{types-4Wmk-wRq.d.cts → types-CnuN9T6t.d.cts} +23 -1
- package/dist/{types-DKsCdAtQ.d.ts → types-CwN6_tAL.d.ts} +23 -1
- package/dist/{types-Clhqautb.d.ts → types-L5bvbF-n.d.ts} +17 -1
- package/dist/{types-DpFD8ofR.d.ts → types-gVa5XCWD.d.ts} +1 -1
- package/dist/{types-DRJt1TMi.d.cts → types-oxt8GN97.d.cts} +17 -1
- package/dist/{workflow-D32TRMr-.d.ts → workflow-B1TOcHbt.d.ts} +33 -2
- package/dist/{workflow-XVt0ww8K.d.cts → workflow-DIaIV7L2.d.cts} +33 -2
- package/dist/workflow.cjs +29 -19
- 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 +29 -19
- package/dist/workflow.js.map +1 -1
- package/package.json +6 -1
- package/src/adapters/thread/anthropic/activities.ts +72 -36
- 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/thread-manager.ts +9 -1
- package/src/adapters/thread/langchain/activities.ts +63 -36
- package/src/adapters/thread/langchain/thread-manager.ts +9 -1
- package/src/index.ts +20 -1
- 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 +47 -22
- package/src/lib/session/types.ts +22 -0
- package/src/lib/thread/cold-store.test.ts +193 -0
- package/src/lib/thread/cold-store.ts +250 -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 +2 -0
- 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/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { defineSignal, CancellationScope, isCancellation, uuid4, setHandler, defineUpdate, ApplicationFailure, log, defineQuery, condition, proxySinks, workflowInfo, proxyActivities, getExternalWorkflowHandle, executeChild } from '@temporalio/workflow';
|
|
2
2
|
import z14, { z } from 'zod';
|
|
3
|
-
import
|
|
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
8
|
import { Context } from '@temporalio/activity';
|
|
7
9
|
import { promises } from 'fs';
|
|
8
10
|
|
|
@@ -1032,7 +1034,9 @@ async function createSession(config) {
|
|
|
1032
1034
|
appendAgentMessage,
|
|
1033
1035
|
forkThread,
|
|
1034
1036
|
loadThreadState,
|
|
1035
|
-
saveThreadState
|
|
1037
|
+
saveThreadState,
|
|
1038
|
+
hydrateThread,
|
|
1039
|
+
flushThread
|
|
1036
1040
|
} = threadOps;
|
|
1037
1041
|
const plugins = [];
|
|
1038
1042
|
let destroySubagentSandboxes;
|
|
@@ -1092,10 +1096,7 @@ async function createSession(config) {
|
|
|
1092
1096
|
stateManager.run();
|
|
1093
1097
|
}
|
|
1094
1098
|
);
|
|
1095
|
-
const lifecycle = resolveSessionLifecycle(
|
|
1096
|
-
sandboxInit,
|
|
1097
|
-
sandboxShutdown
|
|
1098
|
-
);
|
|
1099
|
+
const lifecycle = resolveSessionLifecycle(sandboxInit, sandboxShutdown);
|
|
1099
1100
|
const sandboxMode = lifecycle.mode;
|
|
1100
1101
|
const resolvedShutdown = lifecycle.shutdown;
|
|
1101
1102
|
let sandboxId;
|
|
@@ -1170,20 +1171,6 @@ async function createSession(config) {
|
|
|
1170
1171
|
...baseSnapshot && { baseSnapshot }
|
|
1171
1172
|
});
|
|
1172
1173
|
}
|
|
1173
|
-
if (hooks.onSessionStart) {
|
|
1174
|
-
await hooks.onSessionStart({
|
|
1175
|
-
threadId,
|
|
1176
|
-
agentName,
|
|
1177
|
-
metadata
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
log.info("session started", {
|
|
1181
|
-
agentName,
|
|
1182
|
-
threadId,
|
|
1183
|
-
threadMode,
|
|
1184
|
-
maxTurns,
|
|
1185
|
-
...sandboxId && { sandboxId }
|
|
1186
|
-
});
|
|
1187
1174
|
const sessionStartMs = Date.now();
|
|
1188
1175
|
const systemPrompt = stateManager.getSystemPrompt();
|
|
1189
1176
|
const rehydrateFromSlice = (slice) => {
|
|
@@ -1193,10 +1180,12 @@ async function createSession(config) {
|
|
|
1193
1180
|
});
|
|
1194
1181
|
};
|
|
1195
1182
|
if (threadMode === "fork" && sourceThreadId) {
|
|
1183
|
+
await hydrateThread(sourceThreadId, threadKey);
|
|
1196
1184
|
await forkThread(sourceThreadId, threadId, threadKey);
|
|
1197
1185
|
const forkedSlice = await loadThreadState(threadId, threadKey);
|
|
1198
1186
|
if (forkedSlice) rehydrateFromSlice(forkedSlice);
|
|
1199
1187
|
} else if (threadMode === "continue") {
|
|
1188
|
+
await hydrateThread(threadId, threadKey);
|
|
1200
1189
|
const continuedSlice = await loadThreadState(threadId, threadKey);
|
|
1201
1190
|
if (continuedSlice) rehydrateFromSlice(continuedSlice);
|
|
1202
1191
|
} else {
|
|
@@ -1256,6 +1245,20 @@ async function createSession(config) {
|
|
|
1256
1245
|
);
|
|
1257
1246
|
let exitReason = "completed";
|
|
1258
1247
|
let finalMessage = null;
|
|
1248
|
+
if (hooks.onSessionStart) {
|
|
1249
|
+
await hooks.onSessionStart({
|
|
1250
|
+
threadId,
|
|
1251
|
+
agentName,
|
|
1252
|
+
metadata
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
log.info("session started", {
|
|
1256
|
+
agentName,
|
|
1257
|
+
threadId,
|
|
1258
|
+
threadMode,
|
|
1259
|
+
maxTurns,
|
|
1260
|
+
...sandboxId && { sandboxId }
|
|
1261
|
+
});
|
|
1259
1262
|
try {
|
|
1260
1263
|
let assistantId;
|
|
1261
1264
|
while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
|
|
@@ -1364,6 +1367,15 @@ async function createSession(config) {
|
|
|
1364
1367
|
error: persistError instanceof Error ? persistError.message : String(persistError)
|
|
1365
1368
|
});
|
|
1366
1369
|
}
|
|
1370
|
+
try {
|
|
1371
|
+
await flushThread(threadId, threadKey);
|
|
1372
|
+
} catch (flushError) {
|
|
1373
|
+
log.warn("failed to flush thread to cold tier", {
|
|
1374
|
+
agentName,
|
|
1375
|
+
threadId,
|
|
1376
|
+
error: flushError instanceof Error ? flushError.message : String(flushError)
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1367
1379
|
await callSessionEnd(exitReason, stateManager.getTurns());
|
|
1368
1380
|
if (sandboxOwned && sandboxId && sandboxOps) {
|
|
1369
1381
|
switch (resolvedShutdown) {
|
|
@@ -1447,6 +1459,9 @@ function getThreadMetaKey(threadKey, threadId) {
|
|
|
1447
1459
|
function getThreadStateKey(threadKey, threadId) {
|
|
1448
1460
|
return `${threadKey}:state:thread:${threadId}`;
|
|
1449
1461
|
}
|
|
1462
|
+
function getThreadDedupKey(threadId, dedupId) {
|
|
1463
|
+
return `dedup:${dedupId}:thread:${threadId}`;
|
|
1464
|
+
}
|
|
1450
1465
|
|
|
1451
1466
|
// src/lib/types.ts
|
|
1452
1467
|
function isTerminalStatus(status) {
|
|
@@ -1653,7 +1668,7 @@ function createAgentStateManager({
|
|
|
1653
1668
|
};
|
|
1654
1669
|
}
|
|
1655
1670
|
|
|
1656
|
-
//
|
|
1671
|
+
// node_modules/uuid/dist/esm/stringify.js
|
|
1657
1672
|
var byteToHex = [];
|
|
1658
1673
|
for (let i = 0; i < 256; ++i) {
|
|
1659
1674
|
byteToHex.push((i + 256).toString(16).slice(1));
|
|
@@ -1665,26 +1680,30 @@ var rnds8Pool = new Uint8Array(256);
|
|
|
1665
1680
|
var poolPtr = rnds8Pool.length;
|
|
1666
1681
|
function rng() {
|
|
1667
1682
|
if (poolPtr > rnds8Pool.length - 16) {
|
|
1668
|
-
|
|
1683
|
+
randomFillSync(rnds8Pool);
|
|
1669
1684
|
poolPtr = 0;
|
|
1670
1685
|
}
|
|
1671
1686
|
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
1672
1687
|
}
|
|
1673
|
-
var native_default = {
|
|
1674
|
-
randomUUID: crypto.randomUUID
|
|
1675
|
-
};
|
|
1688
|
+
var native_default = { randomUUID };
|
|
1676
1689
|
|
|
1677
|
-
//
|
|
1690
|
+
// node_modules/uuid/dist/esm/v4.js
|
|
1678
1691
|
function v4(options, buf, offset) {
|
|
1679
1692
|
if (native_default.randomUUID && !buf && !options) {
|
|
1680
1693
|
return native_default.randomUUID();
|
|
1681
1694
|
}
|
|
1682
1695
|
options = options || {};
|
|
1683
|
-
const rnds = options.random
|
|
1696
|
+
const rnds = options.random ?? options.rng?.() ?? rng();
|
|
1697
|
+
if (rnds.length < 16) {
|
|
1698
|
+
throw new Error("Random bytes length must be >= 16");
|
|
1699
|
+
}
|
|
1684
1700
|
rnds[6] = rnds[6] & 15 | 64;
|
|
1685
1701
|
rnds[8] = rnds[8] & 63 | 128;
|
|
1686
1702
|
if (buf) {
|
|
1687
1703
|
offset = offset || 0;
|
|
1704
|
+
if (offset < 0 || offset + 16 > buf.length) {
|
|
1705
|
+
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
|
1706
|
+
}
|
|
1688
1707
|
for (let i = 0; i < 16; ++i) {
|
|
1689
1708
|
buf[offset + i] = rnds[i];
|
|
1690
1709
|
}
|
|
@@ -2587,9 +2606,6 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
|
|
|
2587
2606
|
redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
|
|
2588
2607
|
return 1
|
|
2589
2608
|
`;
|
|
2590
|
-
function getDedupKey(threadId, id) {
|
|
2591
|
-
return `dedup:${id}:thread:${threadId}`;
|
|
2592
|
-
}
|
|
2593
2609
|
function createThreadManager(config) {
|
|
2594
2610
|
const {
|
|
2595
2611
|
redis,
|
|
@@ -2597,11 +2613,13 @@ function createThreadManager(config) {
|
|
|
2597
2613
|
key = "messages",
|
|
2598
2614
|
serialize = (m) => JSON.stringify(m),
|
|
2599
2615
|
deserialize = (raw) => JSON.parse(raw),
|
|
2600
|
-
idOf
|
|
2616
|
+
idOf,
|
|
2617
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2601
2618
|
} = config;
|
|
2602
2619
|
const redisKey = getThreadListKey(key, threadId);
|
|
2603
2620
|
const metaKey = getThreadMetaKey(key, threadId);
|
|
2604
2621
|
const stateKey = getThreadStateKey(key, threadId);
|
|
2622
|
+
const dedupKey = (id) => getThreadDedupKey(threadId, id);
|
|
2605
2623
|
async function assertThreadExists() {
|
|
2606
2624
|
const exists = await redis.exists(metaKey);
|
|
2607
2625
|
if (!exists) {
|
|
@@ -2611,7 +2629,7 @@ function createThreadManager(config) {
|
|
|
2611
2629
|
return {
|
|
2612
2630
|
async initialize() {
|
|
2613
2631
|
await redis.del(redisKey);
|
|
2614
|
-
await redis.set(metaKey, "1", "EX",
|
|
2632
|
+
await redis.set(metaKey, "1", "EX", ttlSeconds);
|
|
2615
2633
|
},
|
|
2616
2634
|
async load() {
|
|
2617
2635
|
await assertThreadExists();
|
|
@@ -2623,18 +2641,17 @@ function createThreadManager(config) {
|
|
|
2623
2641
|
await assertThreadExists();
|
|
2624
2642
|
if (idOf) {
|
|
2625
2643
|
const dedupId = messages.map(idOf).join(":");
|
|
2626
|
-
const dedupKey = getDedupKey(threadId, dedupId);
|
|
2627
2644
|
await redis.eval(
|
|
2628
2645
|
APPEND_IDEMPOTENT_SCRIPT,
|
|
2629
2646
|
2,
|
|
2630
|
-
dedupKey,
|
|
2647
|
+
dedupKey(dedupId),
|
|
2631
2648
|
redisKey,
|
|
2632
|
-
String(
|
|
2649
|
+
String(ttlSeconds),
|
|
2633
2650
|
...messages.map(serialize)
|
|
2634
2651
|
);
|
|
2635
2652
|
} else {
|
|
2636
2653
|
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
2637
|
-
await redis.expire(redisKey,
|
|
2654
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2638
2655
|
}
|
|
2639
2656
|
},
|
|
2640
2657
|
async fork(newThreadId) {
|
|
@@ -2649,11 +2666,11 @@ function createThreadManager(config) {
|
|
|
2649
2666
|
if (data.length > 0) {
|
|
2650
2667
|
const newKey = getThreadListKey(key, newThreadId);
|
|
2651
2668
|
await redis.rpush(newKey, ...data);
|
|
2652
|
-
await redis.expire(newKey,
|
|
2669
|
+
await redis.expire(newKey, ttlSeconds);
|
|
2653
2670
|
}
|
|
2654
2671
|
if (stateRaw != null) {
|
|
2655
2672
|
const newStateKey = getThreadStateKey(key, newThreadId);
|
|
2656
|
-
await redis.set(newStateKey, stateRaw, "EX",
|
|
2673
|
+
await redis.set(newStateKey, stateRaw, "EX", ttlSeconds);
|
|
2657
2674
|
}
|
|
2658
2675
|
return forked;
|
|
2659
2676
|
},
|
|
@@ -2668,15 +2685,13 @@ function createThreadManager(config) {
|
|
|
2668
2685
|
const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
|
|
2669
2686
|
await redis.del(redisKey);
|
|
2670
2687
|
if (existingIds.length > 0) {
|
|
2671
|
-
await redis.del(
|
|
2672
|
-
...existingIds.map((id) => getDedupKey(threadId, id))
|
|
2673
|
-
);
|
|
2688
|
+
await redis.del(...existingIds.map(dedupKey));
|
|
2674
2689
|
}
|
|
2675
2690
|
if (messages.length > 0) {
|
|
2676
2691
|
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
2677
|
-
await redis.expire(redisKey,
|
|
2692
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2678
2693
|
}
|
|
2679
|
-
await redis.expire(metaKey,
|
|
2694
|
+
await redis.expire(metaKey, ttlSeconds);
|
|
2680
2695
|
},
|
|
2681
2696
|
async delete() {
|
|
2682
2697
|
await redis.del(redisKey, metaKey, stateKey);
|
|
@@ -2688,12 +2703,7 @@ function createThreadManager(config) {
|
|
|
2688
2703
|
},
|
|
2689
2704
|
async saveState(state) {
|
|
2690
2705
|
await assertThreadExists();
|
|
2691
|
-
await redis.set(
|
|
2692
|
-
stateKey,
|
|
2693
|
-
JSON.stringify(state),
|
|
2694
|
-
"EX",
|
|
2695
|
-
THREAD_TTL_SECONDS
|
|
2696
|
-
);
|
|
2706
|
+
await redis.set(stateKey, JSON.stringify(state), "EX", ttlSeconds);
|
|
2697
2707
|
},
|
|
2698
2708
|
async deleteState() {
|
|
2699
2709
|
await redis.del(stateKey);
|
|
@@ -2722,19 +2732,202 @@ function createThreadManager(config) {
|
|
|
2722
2732
|
if (idx === -1) return;
|
|
2723
2733
|
if (idx === 0) {
|
|
2724
2734
|
await redis.del(redisKey);
|
|
2725
|
-
await redis.expire(metaKey,
|
|
2735
|
+
await redis.expire(metaKey, ttlSeconds);
|
|
2726
2736
|
} else {
|
|
2727
2737
|
await redis.ltrim(redisKey, 0, idx - 1);
|
|
2728
|
-
await redis.expire(redisKey,
|
|
2738
|
+
await redis.expire(redisKey, ttlSeconds);
|
|
2729
2739
|
}
|
|
2730
2740
|
if (removedIds.length > 0) {
|
|
2731
|
-
await redis.del(
|
|
2732
|
-
|
|
2741
|
+
await redis.del(...removedIds.map(dedupKey));
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2746
|
+
function joinKey(parts) {
|
|
2747
|
+
return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter((p) => p.length > 0).join("/");
|
|
2748
|
+
}
|
|
2749
|
+
function buildKey(prefix, threadKey, threadId, gzip) {
|
|
2750
|
+
const ext = gzip ? "json.gz" : "json";
|
|
2751
|
+
return joinKey([
|
|
2752
|
+
prefix ?? "threads",
|
|
2753
|
+
threadKey,
|
|
2754
|
+
`${threadId}.${ext}`
|
|
2755
|
+
]);
|
|
2756
|
+
}
|
|
2757
|
+
async function streamToBuffer(body) {
|
|
2758
|
+
if (body == null) return Buffer.alloc(0);
|
|
2759
|
+
if (body instanceof Uint8Array) return Buffer.from(body);
|
|
2760
|
+
if (typeof body.transformToByteArray === "function") {
|
|
2761
|
+
const bytes = await body.transformToByteArray();
|
|
2762
|
+
return Buffer.from(bytes);
|
|
2763
|
+
}
|
|
2764
|
+
if (typeof body.arrayBuffer === "function") {
|
|
2765
|
+
const ab = await body.arrayBuffer();
|
|
2766
|
+
return Buffer.from(ab);
|
|
2767
|
+
}
|
|
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);
|
|
2773
|
+
}
|
|
2774
|
+
function createS3ColdStore(config) {
|
|
2775
|
+
const { s3, bucket, prefix, gzip = true } = config;
|
|
2776
|
+
const contentType = config.contentType ?? (gzip ? "application/gzip" : "application/json");
|
|
2777
|
+
return {
|
|
2778
|
+
async read(threadKey, threadId) {
|
|
2779
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2780
|
+
try {
|
|
2781
|
+
const resp = await s3.send(
|
|
2782
|
+
new GetObjectCommand({ Bucket: bucket, Key })
|
|
2733
2783
|
);
|
|
2784
|
+
const buf = await streamToBuffer(resp.Body);
|
|
2785
|
+
const json = gzip ? gunzipSync(buf).toString("utf8") : buf.toString("utf8");
|
|
2786
|
+
return JSON.parse(json);
|
|
2787
|
+
} catch (err) {
|
|
2788
|
+
if (isNotFound(err)) return null;
|
|
2789
|
+
throw err;
|
|
2734
2790
|
}
|
|
2791
|
+
},
|
|
2792
|
+
async write(threadKey, threadId, snapshot) {
|
|
2793
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2794
|
+
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
|
+
);
|
|
2804
|
+
},
|
|
2805
|
+
async delete(threadKey, threadId) {
|
|
2806
|
+
const Key = buildKey(prefix, threadKey, threadId, gzip);
|
|
2807
|
+
await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key }));
|
|
2735
2808
|
}
|
|
2736
2809
|
};
|
|
2737
2810
|
}
|
|
2811
|
+
function isNotFound(err) {
|
|
2812
|
+
if (typeof err !== "object" || err === null) return false;
|
|
2813
|
+
const e = err;
|
|
2814
|
+
return e.name === "NoSuchKey" || e.Code === "NoSuchKey" || e.code === "NoSuchKey" || e.name === "NotFound" || e.$metadata?.httpStatusCode === 404;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
// src/lib/thread/snapshot.ts
|
|
2818
|
+
async function encodeSnapshot(config) {
|
|
2819
|
+
const { redis, threadKey, threadId, idOf } = config;
|
|
2820
|
+
const metaKey = getThreadMetaKey(threadKey, threadId);
|
|
2821
|
+
if (await redis.exists(metaKey) === 0) {
|
|
2822
|
+
return null;
|
|
2823
|
+
}
|
|
2824
|
+
const listKey = getThreadListKey(threadKey, threadId);
|
|
2825
|
+
const stateKey = getThreadStateKey(threadKey, threadId);
|
|
2826
|
+
const messages = await redis.lrange(listKey, 0, -1);
|
|
2827
|
+
const stateRaw = await redis.get(stateKey);
|
|
2828
|
+
const state = stateRaw == null ? null : JSON.parse(stateRaw);
|
|
2829
|
+
const dedupIds = idOf ? messages.map(idOf) : [];
|
|
2830
|
+
return { v: 1, messages, state, dedupIds };
|
|
2831
|
+
}
|
|
2832
|
+
async function applySnapshot(config) {
|
|
2833
|
+
const {
|
|
2834
|
+
redis,
|
|
2835
|
+
threadKey,
|
|
2836
|
+
threadId,
|
|
2837
|
+
snapshot,
|
|
2838
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2839
|
+
} = config;
|
|
2840
|
+
const metaKey = getThreadMetaKey(threadKey, threadId);
|
|
2841
|
+
if (await redis.exists(metaKey) === 1) {
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
const listKey = getThreadListKey(threadKey, threadId);
|
|
2845
|
+
const stateKey = getThreadStateKey(threadKey, threadId);
|
|
2846
|
+
await redis.del(listKey, stateKey);
|
|
2847
|
+
const pipeline = redis.pipeline();
|
|
2848
|
+
if (snapshot.messages.length > 0) {
|
|
2849
|
+
pipeline.rpush(listKey, ...snapshot.messages);
|
|
2850
|
+
pipeline.expire(listKey, ttlSeconds);
|
|
2851
|
+
}
|
|
2852
|
+
if (snapshot.state != null) {
|
|
2853
|
+
pipeline.set(stateKey, JSON.stringify(snapshot.state), "EX", ttlSeconds);
|
|
2854
|
+
}
|
|
2855
|
+
for (const id of snapshot.dedupIds) {
|
|
2856
|
+
pipeline.set(getThreadDedupKey(threadId, id), "1", "EX", ttlSeconds);
|
|
2857
|
+
}
|
|
2858
|
+
const results = await pipeline.exec();
|
|
2859
|
+
if (results) {
|
|
2860
|
+
const firstErr = results.find(([err]) => err)?.[0] ?? null;
|
|
2861
|
+
if (firstErr) {
|
|
2862
|
+
await redis.del(
|
|
2863
|
+
listKey,
|
|
2864
|
+
stateKey,
|
|
2865
|
+
...snapshot.dedupIds.map((id) => getThreadDedupKey(threadId, id))
|
|
2866
|
+
).catch(() => void 0);
|
|
2867
|
+
throw firstErr;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
await redis.set(metaKey, "1", "EX", ttlSeconds);
|
|
2871
|
+
}
|
|
2872
|
+
async function clearHotTier(config) {
|
|
2873
|
+
const { redis, threadKey, threadId, dedupIds = [] } = config;
|
|
2874
|
+
const keys = [
|
|
2875
|
+
getThreadListKey(threadKey, threadId),
|
|
2876
|
+
getThreadMetaKey(threadKey, threadId),
|
|
2877
|
+
getThreadStateKey(threadKey, threadId),
|
|
2878
|
+
...dedupIds.map((id) => getThreadDedupKey(threadId, id))
|
|
2879
|
+
];
|
|
2880
|
+
await redis.del(...keys);
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
// src/lib/thread/tiered.ts
|
|
2884
|
+
function createTieredThreadManager(config) {
|
|
2885
|
+
const {
|
|
2886
|
+
redis,
|
|
2887
|
+
threadId,
|
|
2888
|
+
key = "messages",
|
|
2889
|
+
coldStore,
|
|
2890
|
+
idOf,
|
|
2891
|
+
deserialize = (raw) => JSON.parse(raw),
|
|
2892
|
+
ttlSeconds = THREAD_TTL_SECONDS
|
|
2893
|
+
} = config;
|
|
2894
|
+
const base = createThreadManager(config);
|
|
2895
|
+
const rawIdOf = idOf ? (raw) => idOf(deserialize(raw)) : void 0;
|
|
2896
|
+
return Object.assign(base, {
|
|
2897
|
+
async hydrate() {
|
|
2898
|
+
if (!coldStore) return;
|
|
2899
|
+
const snapshot = await coldStore.read(key, threadId);
|
|
2900
|
+
if (!snapshot) return;
|
|
2901
|
+
await applySnapshot({
|
|
2902
|
+
redis,
|
|
2903
|
+
threadKey: key,
|
|
2904
|
+
threadId,
|
|
2905
|
+
snapshot,
|
|
2906
|
+
ttlSeconds
|
|
2907
|
+
});
|
|
2908
|
+
},
|
|
2909
|
+
async flush(opts) {
|
|
2910
|
+
if (!coldStore) return;
|
|
2911
|
+
const snapshot = await encodeSnapshot({
|
|
2912
|
+
redis,
|
|
2913
|
+
threadKey: key,
|
|
2914
|
+
threadId,
|
|
2915
|
+
...rawIdOf ? { idOf: rawIdOf } : {}
|
|
2916
|
+
});
|
|
2917
|
+
if (!snapshot) return;
|
|
2918
|
+
await coldStore.write(key, threadId, snapshot);
|
|
2919
|
+
const deleteHot = opts?.deleteHot ?? true;
|
|
2920
|
+
if (deleteHot) {
|
|
2921
|
+
await clearHotTier({
|
|
2922
|
+
redis,
|
|
2923
|
+
threadKey: key,
|
|
2924
|
+
threadId,
|
|
2925
|
+
dedupIds: snapshot.dedupIds
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2738
2931
|
function getActivityContext() {
|
|
2739
2932
|
try {
|
|
2740
2933
|
const ctx = Context.current();
|
|
@@ -3670,6 +3863,6 @@ var toTree = async (fs, opts = {}) => {
|
|
|
3670
3863
|
return base + subtree;
|
|
3671
3864
|
};
|
|
3672
3865
|
|
|
3673
|
-
export { DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT, FileSystemSkillProvider, NodeFsSandboxFileSystem, SandboxManager, SandboxNotFoundError, SandboxNotSupportedError, THREAD_TTL_SECONDS, VirtualFileSystem, applyVirtualTreeMutations, askUserQuestionTool, bashHandler, bashTool, composeHooks, createAgentStateManager, createAskUserQuestionHandler, createBashToolDescription, createObservabilityHooks, createReadSkillHandler, createReadSkillTool, createRunAgentActivity, createSession, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createToolRouter, createVirtualFsActivities, defineSubagent, defineSubagentWorkflow, defineTool, defineWorkflow, editHandler, editTool, filesWithMimeType, formatVirtualFileTree, getActivityContext, getShortId, getThreadListKey, getThreadMetaKey, globHandler, globTool, grepTool, hasDirectory, hasFileWithMimeType, hasNoOtherToolCalls, isTerminalStatus, parseSkillFile, proxyRunAgent, proxyVirtualFsOps, queryParentWorkflowState, readFileHandler, readFileTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, withParentWorkflowState, withSandbox, withVirtualFs, writeFileHandler, writeFileTool };
|
|
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 };
|
|
3674
3867
|
//# sourceMappingURL=index.js.map
|
|
3675
3868
|
//# sourceMappingURL=index.js.map
|