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.
Files changed (99) hide show
  1. package/README.md +78 -10
  2. package/dist/{activities-CPIB2v2C.d.ts → activities-Bm4TLTid.d.ts} +24 -4
  3. package/dist/{activities-DnmNOnq4.d.cts → activities-CyeiqK_f.d.cts} +24 -4
  4. package/dist/adapters/sandbox/daytona/index.d.cts +2 -2
  5. package/dist/adapters/sandbox/daytona/index.d.ts +2 -2
  6. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  7. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  8. package/dist/adapters/sandbox/e2b/index.d.cts +1 -1
  9. package/dist/adapters/sandbox/e2b/index.d.ts +1 -1
  10. package/dist/adapters/thread/anthropic/index.cjs +171 -65
  11. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  12. package/dist/adapters/thread/anthropic/index.d.cts +19 -4
  13. package/dist/adapters/thread/anthropic/index.d.ts +19 -4
  14. package/dist/adapters/thread/anthropic/index.js +171 -65
  15. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  16. package/dist/adapters/thread/anthropic/workflow.cjs +3 -1
  17. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  18. package/dist/adapters/thread/anthropic/workflow.d.cts +4 -4
  19. package/dist/adapters/thread/anthropic/workflow.d.ts +4 -4
  20. package/dist/adapters/thread/anthropic/workflow.js +3 -1
  21. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  22. package/dist/adapters/thread/google-genai/index.cjs +171 -69
  23. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  24. package/dist/adapters/thread/google-genai/index.d.cts +5 -4
  25. package/dist/adapters/thread/google-genai/index.d.ts +5 -4
  26. package/dist/adapters/thread/google-genai/index.js +171 -69
  27. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  28. package/dist/adapters/thread/google-genai/workflow.cjs +3 -1
  29. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  30. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -4
  31. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -4
  32. package/dist/adapters/thread/google-genai/workflow.js +3 -1
  33. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  34. package/dist/adapters/thread/langchain/index.cjs +181 -77
  35. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  36. package/dist/adapters/thread/langchain/index.d.cts +18 -4
  37. package/dist/adapters/thread/langchain/index.d.ts +18 -4
  38. package/dist/adapters/thread/langchain/index.js +182 -74
  39. package/dist/adapters/thread/langchain/index.js.map +1 -1
  40. package/dist/adapters/thread/langchain/workflow.cjs +3 -1
  41. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  42. package/dist/adapters/thread/langchain/workflow.d.cts +4 -4
  43. package/dist/adapters/thread/langchain/workflow.d.ts +4 -4
  44. package/dist/adapters/thread/langchain/workflow.js +3 -1
  45. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  46. package/dist/cold-store-BC5L5Z8A.d.cts +117 -0
  47. package/dist/cold-store-CFHwemBJ.d.ts +117 -0
  48. package/dist/index.cjs +252 -53
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +138 -8
  51. package/dist/index.d.ts +138 -8
  52. package/dist/index.js +247 -54
  53. package/dist/index.js.map +1 -1
  54. package/dist/{proxy-DTnc5rqT.d.cts → proxy-BxFyd6cg.d.cts} +1 -1
  55. package/dist/{proxy-B7Xi1znZ.d.ts → proxy-Cskmj4Yx.d.ts} +1 -1
  56. package/dist/{thread-manager-BlX2TwRN.d.cts → thread-manager-9tezUcLW.d.cts} +9 -3
  57. package/dist/{thread-manager-BAv340mi.d.ts → thread-manager-B-zy3xrs.d.ts} +9 -3
  58. package/dist/{thread-manager-D2xorI-J.d.ts → thread-manager-D33SUmZa.d.cts} +10 -4
  59. package/dist/{thread-manager-BWv6ZXI3.d.cts → thread-manager-DduoSkvJ.d.ts} +10 -4
  60. package/dist/{types-C90VoEpt.d.cts → types-CjY93AWZ.d.cts} +1 -1
  61. package/dist/{types-4Wmk-wRq.d.cts → types-CnuN9T6t.d.cts} +23 -1
  62. package/dist/{types-DKsCdAtQ.d.ts → types-CwN6_tAL.d.ts} +23 -1
  63. package/dist/{types-Clhqautb.d.ts → types-L5bvbF-n.d.ts} +17 -1
  64. package/dist/{types-DpFD8ofR.d.ts → types-gVa5XCWD.d.ts} +1 -1
  65. package/dist/{types-DRJt1TMi.d.cts → types-oxt8GN97.d.cts} +17 -1
  66. package/dist/{workflow-D32TRMr-.d.ts → workflow-B1TOcHbt.d.ts} +33 -2
  67. package/dist/{workflow-XVt0ww8K.d.cts → workflow-DIaIV7L2.d.cts} +33 -2
  68. package/dist/workflow.cjs +29 -19
  69. package/dist/workflow.cjs.map +1 -1
  70. package/dist/workflow.d.cts +2 -2
  71. package/dist/workflow.d.ts +2 -2
  72. package/dist/workflow.js +29 -19
  73. package/dist/workflow.js.map +1 -1
  74. package/package.json +6 -1
  75. package/src/adapters/thread/anthropic/activities.ts +72 -36
  76. package/src/adapters/thread/anthropic/thread-manager.ts +9 -1
  77. package/src/adapters/thread/google-genai/activities.ts +64 -40
  78. package/src/adapters/thread/google-genai/thread-manager.ts +9 -1
  79. package/src/adapters/thread/langchain/activities.ts +63 -36
  80. package/src/adapters/thread/langchain/thread-manager.ts +9 -1
  81. package/src/index.ts +20 -1
  82. package/src/lib/session/session-edge-cases.integration.test.ts +12 -0
  83. package/src/lib/session/session.integration.test.ts +138 -0
  84. package/src/lib/session/session.ts +47 -22
  85. package/src/lib/session/types.ts +22 -0
  86. package/src/lib/thread/cold-store.test.ts +193 -0
  87. package/src/lib/thread/cold-store.ts +250 -0
  88. package/src/lib/thread/index.ts +32 -0
  89. package/src/lib/thread/keys.ts +20 -0
  90. package/src/lib/thread/manager.ts +16 -27
  91. package/src/lib/thread/proxy.ts +2 -0
  92. package/src/lib/thread/snapshot.test.ts +443 -0
  93. package/src/lib/thread/snapshot.ts +163 -0
  94. package/src/lib/thread/test-utils.ts +228 -0
  95. package/src/lib/thread/tiered.test.ts +281 -0
  96. package/src/lib/thread/tiered.ts +135 -0
  97. package/src/lib/thread/types.ts +16 -0
  98. package/src/lib/.env +0 -1
  99. package/src/tools/bash/.env +0 -1
@@ -14,6 +14,9 @@ function getThreadMetaKey(threadKey, threadId) {
14
14
  function getThreadStateKey(threadKey, threadId) {
15
15
  return `${threadKey}:state:thread:${threadId}`;
16
16
  }
17
+ function getThreadDedupKey(threadId, dedupId) {
18
+ return `dedup:${dedupId}:thread:${threadId}`;
19
+ }
17
20
 
18
21
  // src/lib/thread/manager.ts
19
22
  var APPEND_IDEMPOTENT_SCRIPT = `
@@ -27,9 +30,6 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
27
30
  redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
28
31
  return 1
29
32
  `;
30
- function getDedupKey(threadId, id) {
31
- return `dedup:${id}:thread:${threadId}`;
32
- }
33
33
  function createThreadManager(config) {
34
34
  const {
35
35
  redis,
@@ -37,11 +37,13 @@ function createThreadManager(config) {
37
37
  key = "messages",
38
38
  serialize = (m) => JSON.stringify(m),
39
39
  deserialize = (raw) => JSON.parse(raw),
40
- idOf
40
+ idOf,
41
+ ttlSeconds = THREAD_TTL_SECONDS
41
42
  } = config;
42
43
  const redisKey = getThreadListKey(key, threadId);
43
44
  const metaKey = getThreadMetaKey(key, threadId);
44
45
  const stateKey = getThreadStateKey(key, threadId);
46
+ const dedupKey = (id) => getThreadDedupKey(threadId, id);
45
47
  async function assertThreadExists() {
46
48
  const exists = await redis.exists(metaKey);
47
49
  if (!exists) {
@@ -51,7 +53,7 @@ function createThreadManager(config) {
51
53
  return {
52
54
  async initialize() {
53
55
  await redis.del(redisKey);
54
- await redis.set(metaKey, "1", "EX", THREAD_TTL_SECONDS);
56
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
55
57
  },
56
58
  async load() {
57
59
  await assertThreadExists();
@@ -63,18 +65,17 @@ function createThreadManager(config) {
63
65
  await assertThreadExists();
64
66
  if (idOf) {
65
67
  const dedupId = messages.map(idOf).join(":");
66
- const dedupKey = getDedupKey(threadId, dedupId);
67
68
  await redis.eval(
68
69
  APPEND_IDEMPOTENT_SCRIPT,
69
70
  2,
70
- dedupKey,
71
+ dedupKey(dedupId),
71
72
  redisKey,
72
- String(THREAD_TTL_SECONDS),
73
+ String(ttlSeconds),
73
74
  ...messages.map(serialize)
74
75
  );
75
76
  } else {
76
77
  await redis.rpush(redisKey, ...messages.map(serialize));
77
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
78
+ await redis.expire(redisKey, ttlSeconds);
78
79
  }
79
80
  },
80
81
  async fork(newThreadId) {
@@ -89,11 +90,11 @@ function createThreadManager(config) {
89
90
  if (data.length > 0) {
90
91
  const newKey = getThreadListKey(key, newThreadId);
91
92
  await redis.rpush(newKey, ...data);
92
- await redis.expire(newKey, THREAD_TTL_SECONDS);
93
+ await redis.expire(newKey, ttlSeconds);
93
94
  }
94
95
  if (stateRaw != null) {
95
96
  const newStateKey = getThreadStateKey(key, newThreadId);
96
- await redis.set(newStateKey, stateRaw, "EX", THREAD_TTL_SECONDS);
97
+ await redis.set(newStateKey, stateRaw, "EX", ttlSeconds);
97
98
  }
98
99
  return forked;
99
100
  },
@@ -108,15 +109,13 @@ function createThreadManager(config) {
108
109
  const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
109
110
  await redis.del(redisKey);
110
111
  if (existingIds.length > 0) {
111
- await redis.del(
112
- ...existingIds.map((id) => getDedupKey(threadId, id))
113
- );
112
+ await redis.del(...existingIds.map(dedupKey));
114
113
  }
115
114
  if (messages.length > 0) {
116
115
  await redis.rpush(redisKey, ...messages.map(serialize));
117
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
116
+ await redis.expire(redisKey, ttlSeconds);
118
117
  }
119
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
118
+ await redis.expire(metaKey, ttlSeconds);
120
119
  },
121
120
  async delete() {
122
121
  await redis.del(redisKey, metaKey, stateKey);
@@ -128,12 +127,7 @@ function createThreadManager(config) {
128
127
  },
129
128
  async saveState(state) {
130
129
  await assertThreadExists();
131
- await redis.set(
132
- stateKey,
133
- JSON.stringify(state),
134
- "EX",
135
- THREAD_TTL_SECONDS
136
- );
130
+ await redis.set(stateKey, JSON.stringify(state), "EX", ttlSeconds);
137
131
  },
138
132
  async deleteState() {
139
133
  await redis.del(stateKey);
@@ -162,20 +156,133 @@ function createThreadManager(config) {
162
156
  if (idx === -1) return;
163
157
  if (idx === 0) {
164
158
  await redis.del(redisKey);
165
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
159
+ await redis.expire(metaKey, ttlSeconds);
166
160
  } else {
167
161
  await redis.ltrim(redisKey, 0, idx - 1);
168
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
162
+ await redis.expire(redisKey, ttlSeconds);
169
163
  }
170
164
  if (removedIds.length > 0) {
171
- await redis.del(
172
- ...removedIds.map((id) => getDedupKey(threadId, id))
173
- );
165
+ await redis.del(...removedIds.map(dedupKey));
174
166
  }
175
167
  }
176
168
  };
177
169
  }
178
170
 
171
+ // src/lib/thread/snapshot.ts
172
+ async function encodeSnapshot(config) {
173
+ const { redis, threadKey, threadId, idOf } = config;
174
+ const metaKey = getThreadMetaKey(threadKey, threadId);
175
+ if (await redis.exists(metaKey) === 0) {
176
+ return null;
177
+ }
178
+ const listKey = getThreadListKey(threadKey, threadId);
179
+ const stateKey = getThreadStateKey(threadKey, threadId);
180
+ const messages = await redis.lrange(listKey, 0, -1);
181
+ const stateRaw = await redis.get(stateKey);
182
+ const state = stateRaw == null ? null : JSON.parse(stateRaw);
183
+ const dedupIds = idOf ? messages.map(idOf) : [];
184
+ return { v: 1, messages, state, dedupIds };
185
+ }
186
+ async function applySnapshot(config) {
187
+ const {
188
+ redis,
189
+ threadKey,
190
+ threadId,
191
+ snapshot,
192
+ ttlSeconds = THREAD_TTL_SECONDS
193
+ } = config;
194
+ const metaKey = getThreadMetaKey(threadKey, threadId);
195
+ if (await redis.exists(metaKey) === 1) {
196
+ return;
197
+ }
198
+ const listKey = getThreadListKey(threadKey, threadId);
199
+ const stateKey = getThreadStateKey(threadKey, threadId);
200
+ await redis.del(listKey, stateKey);
201
+ const pipeline = redis.pipeline();
202
+ if (snapshot.messages.length > 0) {
203
+ pipeline.rpush(listKey, ...snapshot.messages);
204
+ pipeline.expire(listKey, ttlSeconds);
205
+ }
206
+ if (snapshot.state != null) {
207
+ pipeline.set(stateKey, JSON.stringify(snapshot.state), "EX", ttlSeconds);
208
+ }
209
+ for (const id of snapshot.dedupIds) {
210
+ pipeline.set(getThreadDedupKey(threadId, id), "1", "EX", ttlSeconds);
211
+ }
212
+ const results = await pipeline.exec();
213
+ if (results) {
214
+ const firstErr = results.find(([err]) => err)?.[0] ?? null;
215
+ if (firstErr) {
216
+ await redis.del(
217
+ listKey,
218
+ stateKey,
219
+ ...snapshot.dedupIds.map((id) => getThreadDedupKey(threadId, id))
220
+ ).catch(() => void 0);
221
+ throw firstErr;
222
+ }
223
+ }
224
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
225
+ }
226
+ async function clearHotTier(config) {
227
+ const { redis, threadKey, threadId, dedupIds = [] } = config;
228
+ const keys = [
229
+ getThreadListKey(threadKey, threadId),
230
+ getThreadMetaKey(threadKey, threadId),
231
+ getThreadStateKey(threadKey, threadId),
232
+ ...dedupIds.map((id) => getThreadDedupKey(threadId, id))
233
+ ];
234
+ await redis.del(...keys);
235
+ }
236
+
237
+ // src/lib/thread/tiered.ts
238
+ function createTieredThreadManager(config) {
239
+ const {
240
+ redis,
241
+ threadId,
242
+ key = "messages",
243
+ coldStore,
244
+ idOf,
245
+ deserialize = (raw) => JSON.parse(raw),
246
+ ttlSeconds = THREAD_TTL_SECONDS
247
+ } = config;
248
+ const base = createThreadManager(config);
249
+ const rawIdOf = idOf ? (raw) => idOf(deserialize(raw)) : void 0;
250
+ return Object.assign(base, {
251
+ async hydrate() {
252
+ if (!coldStore) return;
253
+ const snapshot = await coldStore.read(key, threadId);
254
+ if (!snapshot) return;
255
+ await applySnapshot({
256
+ redis,
257
+ threadKey: key,
258
+ threadId,
259
+ snapshot,
260
+ ttlSeconds
261
+ });
262
+ },
263
+ async flush(opts) {
264
+ if (!coldStore) return;
265
+ const snapshot = await encodeSnapshot({
266
+ redis,
267
+ threadKey: key,
268
+ threadId,
269
+ ...rawIdOf ? { idOf: rawIdOf } : {}
270
+ });
271
+ if (!snapshot) return;
272
+ await coldStore.write(key, threadId, snapshot);
273
+ const deleteHot = opts?.deleteHot ?? true;
274
+ if (deleteHot) {
275
+ await clearHotTier({
276
+ redis,
277
+ threadKey: key,
278
+ threadId,
279
+ dedupIds: snapshot.dedupIds
280
+ });
281
+ }
282
+ }
283
+ });
284
+ }
285
+
179
286
  // src/adapters/thread/anthropic/thread-manager.ts
180
287
  function storedMessageId(msg) {
181
288
  return msg.id;
@@ -208,7 +315,8 @@ function createAnthropicThreadManager(config) {
208
315
  redis: config.redis,
209
316
  threadId: config.threadId,
210
317
  key: config.key,
211
- idOf: storedMessageId
318
+ idOf: storedMessageId,
319
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
212
320
  };
213
321
  const base = createThreadManager(baseConfig);
214
322
  const helpers = {
@@ -397,46 +505,43 @@ async function invokeAnthropicModel({
397
505
  // src/adapters/thread/anthropic/activities.ts
398
506
  function createAnthropicAdapter(config) {
399
507
  const { redis, client } = config;
508
+ const baseExtras = {
509
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
510
+ };
511
+ const makeProviderThread = (threadId, threadKey) => createAnthropicThreadManager({
512
+ redis,
513
+ threadId,
514
+ key: threadKey,
515
+ ...baseExtras
516
+ });
517
+ const makeTieredBase = (threadId, threadKey) => createTieredThreadManager({
518
+ redis,
519
+ threadId,
520
+ key: threadKey,
521
+ idOf: storedMessageId,
522
+ ...baseExtras,
523
+ ...config.coldStore && { coldStore: config.coldStore }
524
+ });
400
525
  const threadOps = {
401
526
  async initializeThread(threadId, threadKey) {
402
- const thread = createAnthropicThreadManager({
403
- redis,
404
- threadId,
405
- key: threadKey
406
- });
527
+ const thread = makeProviderThread(threadId, threadKey);
407
528
  await thread.initialize();
408
529
  },
409
530
  async appendHumanMessage(threadId, id, content, threadKey) {
410
- const thread = createAnthropicThreadManager({
411
- redis,
412
- threadId,
413
- key: threadKey
414
- });
531
+ const thread = makeProviderThread(threadId, threadKey);
415
532
  await thread.appendUserMessage(id, content);
416
533
  },
417
534
  async appendSystemMessage(threadId, id, content, threadKey) {
418
- const thread = createAnthropicThreadManager({
419
- redis,
420
- threadId,
421
- key: threadKey
422
- });
535
+ const thread = makeProviderThread(threadId, threadKey);
423
536
  await thread.appendSystemMessage(id, content);
424
537
  },
425
538
  async appendToolResult(id, cfg) {
426
539
  const { threadId, threadKey, toolCallId, toolName, content } = cfg;
427
- const thread = createAnthropicThreadManager({
428
- redis,
429
- threadId,
430
- key: threadKey
431
- });
540
+ const thread = makeProviderThread(threadId, threadKey);
432
541
  await thread.appendToolResult(id, toolCallId, toolName, content);
433
542
  },
434
543
  async appendAgentMessage(threadId, id, message, threadKey) {
435
- const thread = createAnthropicThreadManager({
436
- redis,
437
- threadId,
438
- key: threadKey
439
- });
544
+ const thread = makeProviderThread(threadId, threadKey);
440
545
  await thread.appendAssistantMessage(id, message.content);
441
546
  },
442
547
  async forkThread(sourceThreadId, targetThreadId, threadKey) {
@@ -444,29 +549,30 @@ function createAnthropicAdapter(config) {
444
549
  redis,
445
550
  threadId: sourceThreadId,
446
551
  key: threadKey,
447
- hooks: config.hooks
552
+ hooks: config.hooks,
553
+ ...baseExtras
448
554
  });
449
555
  await thread.fork(targetThreadId);
450
556
  },
451
557
  async truncateThread(threadId, messageId, threadKey) {
452
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
558
+ const thread = makeProviderThread(threadId, threadKey);
453
559
  await thread.truncateFromId(messageId);
454
560
  },
455
561
  async loadThreadState(threadId, threadKey) {
456
- const thread = createAnthropicThreadManager({
457
- redis,
458
- threadId,
459
- key: threadKey
460
- });
562
+ const thread = makeProviderThread(threadId, threadKey);
461
563
  return thread.loadState();
462
564
  },
463
565
  async saveThreadState(threadId, state, threadKey) {
464
- const thread = createAnthropicThreadManager({
465
- redis,
466
- threadId,
467
- key: threadKey
468
- });
566
+ const thread = makeProviderThread(threadId, threadKey);
469
567
  await thread.saveState(state);
568
+ },
569
+ async hydrateThread(threadId, threadKey) {
570
+ if (!config.coldStore) return;
571
+ await makeTieredBase(threadId, threadKey).hydrate();
572
+ },
573
+ async flushThread(threadId, threadKey) {
574
+ if (!config.coldStore) return;
575
+ await makeTieredBase(threadId, threadKey).flush();
470
576
  }
471
577
  };
472
578
  function createActivities(scope) {