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.
Files changed (109) hide show
  1. package/README.md +137 -11
  2. package/dist/{activities-Coafq5zr.d.cts → activities-CPwKoUlD.d.cts} +22 -2
  3. package/dist/{activities-CrN-ghLo.d.ts → activities-DlaBxNID.d.ts} +22 -2
  4. package/dist/adapters/thread/anthropic/index.cjs +276 -71
  5. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  6. package/dist/adapters/thread/anthropic/index.d.cts +62 -8
  7. package/dist/adapters/thread/anthropic/index.d.ts +62 -8
  8. package/dist/adapters/thread/anthropic/index.js +275 -72
  9. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  10. package/dist/adapters/thread/anthropic/workflow.cjs +38 -20
  11. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  12. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
  13. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
  14. package/dist/adapters/thread/anthropic/workflow.js +38 -20
  15. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  16. package/dist/adapters/thread/google-genai/index.cjs +171 -69
  17. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  18. package/dist/adapters/thread/google-genai/index.d.cts +6 -4
  19. package/dist/adapters/thread/google-genai/index.d.ts +6 -4
  20. package/dist/adapters/thread/google-genai/index.js +171 -69
  21. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  22. package/dist/adapters/thread/google-genai/workflow.cjs +38 -20
  23. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  24. package/dist/adapters/thread/google-genai/workflow.d.cts +7 -4
  25. package/dist/adapters/thread/google-genai/workflow.d.ts +7 -4
  26. package/dist/adapters/thread/google-genai/workflow.js +38 -20
  27. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  28. package/dist/adapters/thread/langchain/index.cjs +170 -66
  29. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  30. package/dist/adapters/thread/langchain/index.d.cts +19 -4
  31. package/dist/adapters/thread/langchain/index.d.ts +19 -4
  32. package/dist/adapters/thread/langchain/index.js +170 -66
  33. package/dist/adapters/thread/langchain/index.js.map +1 -1
  34. package/dist/adapters/thread/langchain/workflow.cjs +38 -20
  35. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  36. package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
  37. package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
  38. package/dist/adapters/thread/langchain/workflow.js +38 -20
  39. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  40. package/dist/cold-store-BDgJpwLI.d.ts +114 -0
  41. package/dist/cold-store-Z2wvK2cV.d.cts +114 -0
  42. package/dist/index.cjs +440 -67
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +150 -8
  45. package/dist/index.d.ts +150 -8
  46. package/dist/index.js +432 -68
  47. package/dist/index.js.map +1 -1
  48. package/dist/proxy-CDh3Rsa7.d.cts +40 -0
  49. package/dist/proxy-Du8ggERu.d.ts +40 -0
  50. package/dist/{thread-manager-wRVVBFgj.d.cts → thread-manager-BjoYYXgd.d.cts} +8 -2
  51. package/dist/{thread-manager-BsLO3Fgc.d.cts → thread-manager-D8zKNFZ9.d.cts} +8 -2
  52. package/dist/{thread-manager-Bi1XlbpJ.d.ts → thread-manager-DtHYws2F.d.ts} +8 -2
  53. package/dist/{thread-manager-BhkOyQ1I.d.ts → thread-manager-Dw96FKH1.d.ts} +8 -2
  54. package/dist/{types-C66-BVBr.d.cts → types-BMJrsHo0.d.cts} +17 -1
  55. package/dist/{types-BkX4HLzi.d.ts → types-CtdOquo3.d.ts} +17 -1
  56. package/dist/{types-CdALEF3z.d.cts → types-DNEl5uxQ.d.cts} +38 -0
  57. package/dist/{types-ChAy_jSP.d.ts → types-qQVZfhoT.d.ts} +38 -0
  58. package/dist/{workflow-DMmiaw6w.d.cts → workflow-BH9ImDGq.d.cts} +48 -2
  59. package/dist/{workflow-BwT5EybR.d.ts → workflow-Cdw3-RNB.d.ts} +48 -2
  60. package/dist/workflow.cjs +47 -4
  61. package/dist/workflow.cjs.map +1 -1
  62. package/dist/workflow.d.cts +2 -2
  63. package/dist/workflow.d.ts +2 -2
  64. package/dist/workflow.js +47 -5
  65. package/dist/workflow.js.map +1 -1
  66. package/package.json +14 -3
  67. package/src/adapters/thread/anthropic/activities.ts +82 -39
  68. package/src/adapters/thread/anthropic/index.ts +8 -0
  69. package/src/adapters/thread/anthropic/model-invoker.test.ts +110 -0
  70. package/src/adapters/thread/anthropic/model-invoker.ts +26 -5
  71. package/src/adapters/thread/anthropic/prompt-cache.test.ts +134 -0
  72. package/src/adapters/thread/anthropic/prompt-cache.ts +163 -0
  73. package/src/adapters/thread/anthropic/proxy.ts +1 -0
  74. package/src/adapters/thread/anthropic/thread-manager.ts +9 -1
  75. package/src/adapters/thread/google-genai/activities.ts +64 -40
  76. package/src/adapters/thread/google-genai/proxy.ts +1 -0
  77. package/src/adapters/thread/google-genai/thread-manager.ts +9 -1
  78. package/src/adapters/thread/langchain/activities.ts +63 -36
  79. package/src/adapters/thread/langchain/proxy.ts +1 -0
  80. package/src/adapters/thread/langchain/thread-manager.ts +9 -1
  81. package/src/index.ts +21 -2
  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 +29 -0
  85. package/src/lib/session/types.ts +22 -0
  86. package/src/lib/subagent/define.ts +1 -0
  87. package/src/lib/subagent/handler.ts +11 -2
  88. package/src/lib/subagent/subagent.integration.test.ts +139 -0
  89. package/src/lib/subagent/types.ts +16 -0
  90. package/src/lib/thread/cold-store.test.ts +221 -0
  91. package/src/lib/thread/cold-store.ts +269 -0
  92. package/src/lib/thread/index.ts +32 -0
  93. package/src/lib/thread/keys.ts +20 -0
  94. package/src/lib/thread/manager.ts +16 -27
  95. package/src/lib/thread/proxy.ts +79 -27
  96. package/src/lib/thread/snapshot.test.ts +443 -0
  97. package/src/lib/thread/snapshot.ts +163 -0
  98. package/src/lib/thread/test-utils.ts +228 -0
  99. package/src/lib/thread/tiered.test.ts +281 -0
  100. package/src/lib/thread/tiered.ts +135 -0
  101. package/src/lib/thread/types.ts +16 -0
  102. package/src/tools/edit/handler.test.ts +177 -0
  103. package/src/tools/edit/handler.ts +249 -47
  104. package/src/tools/edit/tool.ts +40 -0
  105. package/src/tools/task-create/handler.ts +1 -1
  106. package/src/tools/task-update/handler.ts +1 -1
  107. package/src/workflow.ts +2 -2
  108. package/dist/proxy-Bf7uI-Hw.d.cts +0 -24
  109. package/dist/proxy-COqA95FW.d.ts +0 -24
@@ -18,6 +18,9 @@ function getThreadMetaKey(threadKey, threadId) {
18
18
  function getThreadStateKey(threadKey, threadId) {
19
19
  return `${threadKey}:state:thread:${threadId}`;
20
20
  }
21
+ function getThreadDedupKey(threadId, dedupId) {
22
+ return `dedup:${dedupId}:thread:${threadId}`;
23
+ }
21
24
 
22
25
  // src/lib/thread/manager.ts
23
26
  var APPEND_IDEMPOTENT_SCRIPT = `
@@ -31,9 +34,6 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
31
34
  redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
32
35
  return 1
33
36
  `;
34
- function getDedupKey(threadId, id) {
35
- return `dedup:${id}:thread:${threadId}`;
36
- }
37
37
  function createThreadManager(config) {
38
38
  const {
39
39
  redis,
@@ -41,11 +41,13 @@ function createThreadManager(config) {
41
41
  key = "messages",
42
42
  serialize = (m) => JSON.stringify(m),
43
43
  deserialize = (raw) => JSON.parse(raw),
44
- idOf
44
+ idOf,
45
+ ttlSeconds = THREAD_TTL_SECONDS
45
46
  } = config;
46
47
  const redisKey = getThreadListKey(key, threadId);
47
48
  const metaKey = getThreadMetaKey(key, threadId);
48
49
  const stateKey = getThreadStateKey(key, threadId);
50
+ const dedupKey = (id) => getThreadDedupKey(threadId, id);
49
51
  async function assertThreadExists() {
50
52
  const exists = await redis.exists(metaKey);
51
53
  if (!exists) {
@@ -55,7 +57,7 @@ function createThreadManager(config) {
55
57
  return {
56
58
  async initialize() {
57
59
  await redis.del(redisKey);
58
- await redis.set(metaKey, "1", "EX", THREAD_TTL_SECONDS);
60
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
59
61
  },
60
62
  async load() {
61
63
  await assertThreadExists();
@@ -67,18 +69,17 @@ function createThreadManager(config) {
67
69
  await assertThreadExists();
68
70
  if (idOf) {
69
71
  const dedupId = messages.map(idOf).join(":");
70
- const dedupKey = getDedupKey(threadId, dedupId);
71
72
  await redis.eval(
72
73
  APPEND_IDEMPOTENT_SCRIPT,
73
74
  2,
74
- dedupKey,
75
+ dedupKey(dedupId),
75
76
  redisKey,
76
- String(THREAD_TTL_SECONDS),
77
+ String(ttlSeconds),
77
78
  ...messages.map(serialize)
78
79
  );
79
80
  } else {
80
81
  await redis.rpush(redisKey, ...messages.map(serialize));
81
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
82
+ await redis.expire(redisKey, ttlSeconds);
82
83
  }
83
84
  },
84
85
  async fork(newThreadId) {
@@ -93,11 +94,11 @@ function createThreadManager(config) {
93
94
  if (data.length > 0) {
94
95
  const newKey = getThreadListKey(key, newThreadId);
95
96
  await redis.rpush(newKey, ...data);
96
- await redis.expire(newKey, THREAD_TTL_SECONDS);
97
+ await redis.expire(newKey, ttlSeconds);
97
98
  }
98
99
  if (stateRaw != null) {
99
100
  const newStateKey = getThreadStateKey(key, newThreadId);
100
- await redis.set(newStateKey, stateRaw, "EX", THREAD_TTL_SECONDS);
101
+ await redis.set(newStateKey, stateRaw, "EX", ttlSeconds);
101
102
  }
102
103
  return forked;
103
104
  },
@@ -112,15 +113,13 @@ function createThreadManager(config) {
112
113
  const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
113
114
  await redis.del(redisKey);
114
115
  if (existingIds.length > 0) {
115
- await redis.del(
116
- ...existingIds.map((id) => getDedupKey(threadId, id))
117
- );
116
+ await redis.del(...existingIds.map(dedupKey));
118
117
  }
119
118
  if (messages.length > 0) {
120
119
  await redis.rpush(redisKey, ...messages.map(serialize));
121
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
120
+ await redis.expire(redisKey, ttlSeconds);
122
121
  }
123
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
122
+ await redis.expire(metaKey, ttlSeconds);
124
123
  },
125
124
  async delete() {
126
125
  await redis.del(redisKey, metaKey, stateKey);
@@ -132,12 +131,7 @@ function createThreadManager(config) {
132
131
  },
133
132
  async saveState(state) {
134
133
  await assertThreadExists();
135
- await redis.set(
136
- stateKey,
137
- JSON.stringify(state),
138
- "EX",
139
- THREAD_TTL_SECONDS
140
- );
134
+ await redis.set(stateKey, JSON.stringify(state), "EX", ttlSeconds);
141
135
  },
142
136
  async deleteState() {
143
137
  await redis.del(stateKey);
@@ -166,21 +160,132 @@ function createThreadManager(config) {
166
160
  if (idx === -1) return;
167
161
  if (idx === 0) {
168
162
  await redis.del(redisKey);
169
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
163
+ await redis.expire(metaKey, ttlSeconds);
170
164
  } else {
171
165
  await redis.ltrim(redisKey, 0, idx - 1);
172
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
166
+ await redis.expire(redisKey, ttlSeconds);
173
167
  }
174
168
  if (removedIds.length > 0) {
175
- await redis.del(
176
- ...removedIds.map((id) => getDedupKey(threadId, id))
177
- );
169
+ await redis.del(...removedIds.map(dedupKey));
178
170
  }
179
171
  }
180
172
  };
181
173
  }
182
174
 
183
- // src/adapters/thread/langchain/thread-manager.ts
175
+ // src/lib/thread/snapshot.ts
176
+ async function encodeSnapshot(config) {
177
+ const { redis, threadKey, threadId, idOf } = config;
178
+ const metaKey = getThreadMetaKey(threadKey, threadId);
179
+ if (await redis.exists(metaKey) === 0) {
180
+ return null;
181
+ }
182
+ const listKey = getThreadListKey(threadKey, threadId);
183
+ const stateKey = getThreadStateKey(threadKey, threadId);
184
+ const messages = await redis.lrange(listKey, 0, -1);
185
+ const stateRaw = await redis.get(stateKey);
186
+ const state = stateRaw == null ? null : JSON.parse(stateRaw);
187
+ const dedupIds = idOf ? messages.map(idOf) : [];
188
+ return { v: 1, messages, state, dedupIds };
189
+ }
190
+ async function applySnapshot(config) {
191
+ const {
192
+ redis,
193
+ threadKey,
194
+ threadId,
195
+ snapshot,
196
+ ttlSeconds = THREAD_TTL_SECONDS
197
+ } = config;
198
+ const metaKey = getThreadMetaKey(threadKey, threadId);
199
+ if (await redis.exists(metaKey) === 1) {
200
+ return;
201
+ }
202
+ const listKey = getThreadListKey(threadKey, threadId);
203
+ const stateKey = getThreadStateKey(threadKey, threadId);
204
+ await redis.del(listKey, stateKey);
205
+ const pipeline = redis.pipeline();
206
+ if (snapshot.messages.length > 0) {
207
+ pipeline.rpush(listKey, ...snapshot.messages);
208
+ pipeline.expire(listKey, ttlSeconds);
209
+ }
210
+ if (snapshot.state != null) {
211
+ pipeline.set(stateKey, JSON.stringify(snapshot.state), "EX", ttlSeconds);
212
+ }
213
+ for (const id of snapshot.dedupIds) {
214
+ pipeline.set(getThreadDedupKey(threadId, id), "1", "EX", ttlSeconds);
215
+ }
216
+ const results = await pipeline.exec();
217
+ if (results) {
218
+ const firstErr = results.find(([err]) => err)?.[0] ?? null;
219
+ if (firstErr) {
220
+ await redis.del(
221
+ listKey,
222
+ stateKey,
223
+ ...snapshot.dedupIds.map((id) => getThreadDedupKey(threadId, id))
224
+ ).catch(() => void 0);
225
+ throw firstErr;
226
+ }
227
+ }
228
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
229
+ }
230
+ async function clearHotTier(config) {
231
+ const { redis, threadKey, threadId, dedupIds = [] } = config;
232
+ const keys = [
233
+ getThreadListKey(threadKey, threadId),
234
+ getThreadMetaKey(threadKey, threadId),
235
+ getThreadStateKey(threadKey, threadId),
236
+ ...dedupIds.map((id) => getThreadDedupKey(threadId, id))
237
+ ];
238
+ await redis.del(...keys);
239
+ }
240
+
241
+ // src/lib/thread/tiered.ts
242
+ function createTieredThreadManager(config) {
243
+ const {
244
+ redis,
245
+ threadId,
246
+ key = "messages",
247
+ coldStore,
248
+ idOf,
249
+ deserialize = (raw) => JSON.parse(raw),
250
+ ttlSeconds = THREAD_TTL_SECONDS
251
+ } = config;
252
+ const base = createThreadManager(config);
253
+ const rawIdOf = idOf ? (raw) => idOf(deserialize(raw)) : void 0;
254
+ return Object.assign(base, {
255
+ async hydrate() {
256
+ if (!coldStore) return;
257
+ const snapshot = await coldStore.read(key, threadId);
258
+ if (!snapshot) return;
259
+ await applySnapshot({
260
+ redis,
261
+ threadKey: key,
262
+ threadId,
263
+ snapshot,
264
+ ttlSeconds
265
+ });
266
+ },
267
+ async flush(opts) {
268
+ if (!coldStore) return;
269
+ const snapshot = await encodeSnapshot({
270
+ redis,
271
+ threadKey: key,
272
+ threadId,
273
+ ...rawIdOf ? { idOf: rawIdOf } : {}
274
+ });
275
+ if (!snapshot) return;
276
+ await coldStore.write(key, threadId, snapshot);
277
+ const deleteHot = opts?.deleteHot ?? true;
278
+ if (deleteHot) {
279
+ await clearHotTier({
280
+ redis,
281
+ threadKey: key,
282
+ threadId,
283
+ dedupIds: snapshot.dedupIds
284
+ });
285
+ }
286
+ }
287
+ });
288
+ }
184
289
  function storedMessageId(msg) {
185
290
  if (msg.type === "tool" && msg.data.tool_call_id) {
186
291
  return msg.data.tool_call_id;
@@ -195,7 +300,8 @@ function createLangChainThreadManager(config) {
195
300
  redis: config.redis,
196
301
  threadId: config.threadId,
197
302
  key: config.key,
198
- idOf: storedMessageId
303
+ idOf: storedMessageId,
304
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
199
305
  };
200
306
  const base = createThreadManager(baseConfig);
201
307
  const helpers = {
@@ -373,46 +479,43 @@ async function invokeLangChainModel({
373
479
  // src/adapters/thread/langchain/activities.ts
374
480
  function createLangChainAdapter(config) {
375
481
  const { redis } = config;
482
+ const baseExtras = {
483
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
484
+ };
485
+ const makeProviderThread = (threadId, threadKey) => createLangChainThreadManager({
486
+ redis,
487
+ threadId,
488
+ key: threadKey,
489
+ ...baseExtras
490
+ });
491
+ const makeTieredBase = (threadId, threadKey) => createTieredThreadManager({
492
+ redis,
493
+ threadId,
494
+ key: threadKey,
495
+ idOf: storedMessageId,
496
+ ...baseExtras,
497
+ ...config.coldStore && { coldStore: config.coldStore }
498
+ });
376
499
  const threadOps = {
377
500
  async initializeThread(threadId, threadKey) {
378
- const thread = createLangChainThreadManager({
379
- redis,
380
- threadId,
381
- key: threadKey
382
- });
501
+ const thread = makeProviderThread(threadId, threadKey);
383
502
  await thread.initialize();
384
503
  },
385
504
  async appendHumanMessage(threadId, id, content, threadKey) {
386
- const thread = createLangChainThreadManager({
387
- redis,
388
- threadId,
389
- key: threadKey
390
- });
505
+ const thread = makeProviderThread(threadId, threadKey);
391
506
  await thread.appendUserMessage(id, content);
392
507
  },
393
508
  async appendSystemMessage(threadId, id, content, threadKey) {
394
- const thread = createLangChainThreadManager({
395
- redis,
396
- threadId,
397
- key: threadKey
398
- });
509
+ const thread = makeProviderThread(threadId, threadKey);
399
510
  await thread.appendSystemMessage(id, content);
400
511
  },
401
512
  async appendToolResult(id, cfg) {
402
513
  const { threadId, threadKey, toolCallId, content } = cfg;
403
- const thread = createLangChainThreadManager({
404
- redis,
405
- threadId,
406
- key: threadKey
407
- });
514
+ const thread = makeProviderThread(threadId, threadKey);
408
515
  await thread.appendToolResult(id, toolCallId, "", content);
409
516
  },
410
517
  async appendAgentMessage(threadId, id, message, threadKey) {
411
- const thread = createLangChainThreadManager({
412
- redis,
413
- threadId,
414
- key: threadKey
415
- });
518
+ const thread = makeProviderThread(threadId, threadKey);
416
519
  const patched = { ...message, data: { ...message.data, id } };
417
520
  await thread.append([patched]);
418
521
  },
@@ -421,29 +524,30 @@ function createLangChainAdapter(config) {
421
524
  redis,
422
525
  threadId: sourceThreadId,
423
526
  key: threadKey,
424
- hooks: config.hooks
527
+ hooks: config.hooks,
528
+ ...baseExtras
425
529
  });
426
530
  await thread.fork(targetThreadId);
427
531
  },
428
532
  async truncateThread(threadId, messageId, threadKey) {
429
- const thread = createLangChainThreadManager({ redis, threadId, key: threadKey });
533
+ const thread = makeProviderThread(threadId, threadKey);
430
534
  await thread.truncateFromId(messageId);
431
535
  },
432
536
  async loadThreadState(threadId, threadKey) {
433
- const thread = createLangChainThreadManager({
434
- redis,
435
- threadId,
436
- key: threadKey
437
- });
537
+ const thread = makeProviderThread(threadId, threadKey);
438
538
  return thread.loadState();
439
539
  },
440
540
  async saveThreadState(threadId, state, threadKey) {
441
- const thread = createLangChainThreadManager({
442
- redis,
443
- threadId,
444
- key: threadKey
445
- });
541
+ const thread = makeProviderThread(threadId, threadKey);
446
542
  await thread.saveState(state);
543
+ },
544
+ async hydrateThread(threadId, threadKey) {
545
+ if (!config.coldStore) return;
546
+ await makeTieredBase(threadId, threadKey).hydrate();
547
+ },
548
+ async flushThread(threadId, threadKey) {
549
+ if (!config.coldStore) return;
550
+ await makeTieredBase(threadId, threadKey).flush();
447
551
  }
448
552
  };
449
553
  function createActivities(scope) {