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
package/README.md CHANGED
@@ -103,6 +103,7 @@ npm install zeitlich ioredis \
103
103
  - `@langchain/core` >= 1.0.0 (optional — only when using the LangChain adapter)
104
104
  - `@google/genai` >= 1.0.0 (optional — only when using the Google GenAI adapter)
105
105
  - `@aws-sdk/client-bedrock-agentcore` >= 3.900.0 (optional — only when using the Bedrock adapter)
106
+ - `@aws-sdk/client-s3` >= 3.700.0 (optional — only when using the built-in S3 cold thread tier)
106
107
 
107
108
  > **Why peer deps?** Zeitlich's public API surfaces `@temporalio/*` types
108
109
  > (`UpdateDefinition`, `ChildWorkflowOptions`, `Duration`, etc.) directly. Peer
@@ -666,6 +667,68 @@ const continuedSession = await createSession({
666
667
 
667
668
  `getShortId()` produces compact, workflow-deterministic IDs (~12 base-62 chars) that are more token-efficient than UUIDs.
668
669
 
670
+ #### Tiered Thread Storage (Redis hot + S3 cold)
671
+
672
+ By default every thread lives in Redis with a 90-day TTL — both messages and the persisted state slice. For long-lived agents, that ties up hot memory for inactive conversations and ties durability to your Redis retention. Zeitlich's tiered storage moves cold threads to a durable archive (S3, R2, GCS, …) while keeping Redis as the hot tier only for the duration of a workflow run.
673
+
674
+ | Tier | Backend | Lifetime |
675
+ | ----- | ---------------------- | ------------------------------------------------- |
676
+ | Hot | Redis | Only while a workflow run is active (configurable TTL) |
677
+ | Cold | Pluggable `ColdThreadStore` (built-in S3) | Durable across runs |
678
+
679
+ The session wiring is fully automatic:
680
+
681
+ - On entry — `mode: "continue"` and `mode: "fork"` call `hydrateThread`, which restores the latest cold-tier snapshot into Redis if Redis is cold. Idempotent (safe for Temporal activity retries).
682
+ - In `finally{}` — every exit path calls `flushThread` after `saveThreadState`, which writes the current Redis contents to the cold tier and (by default) deletes the Redis keys. A near-immediate `continue` re-hydrates in a single `GetObject`.
683
+
684
+ ##### Wiring the built-in S3 cold store
685
+
686
+ ```typescript
687
+ import { S3Client } from "@aws-sdk/client-s3";
688
+ import { createS3ColdStore } from "zeitlich";
689
+ import { createAnthropicAdapter } from "zeitlich/adapters/thread/anthropic";
690
+
691
+ const coldStore = createS3ColdStore({
692
+ s3: new S3Client({ region: "us-east-1" }),
693
+ bucket: "my-threads",
694
+ prefix: "prod/threads",
695
+ // gzip: true (default) — message lists compress well
696
+ });
697
+
698
+ const adapter = createAnthropicAdapter({
699
+ redis,
700
+ client: anthropic,
701
+ model: "claude-sonnet-4-20250514",
702
+ coldStore,
703
+ // Recommended: drop the Redis TTL to a small window when cold tiering
704
+ // is enabled. The cold tier is the source of truth.
705
+ ttlSeconds: 60 * 60 * 24, // 24h
706
+ });
707
+ ```
708
+
709
+ That's the only change required — `createSession`, all `ThreadInit` modes, and every adapter activity are already wired for the lifecycle. When `coldStore` is omitted, the adapter behaves identically to the Redis-only baseline.
710
+
711
+ ##### Custom backends
712
+
713
+ `ColdThreadStore` is intentionally minimal:
714
+
715
+ ```typescript
716
+ interface ColdThreadStore {
717
+ read(threadKey: string, threadId: string): Promise<ThreadSnapshot | null>;
718
+ write(threadKey: string, threadId: string, snapshot: ThreadSnapshot): Promise<void>;
719
+ delete(threadKey: string, threadId: string): Promise<void>;
720
+ }
721
+ ```
722
+
723
+ Any backend that can satisfy these three calls — Cloudflare R2, Google Cloud Storage, Postgres, the local filesystem — can plug into the same tiered manager. `ThreadSnapshot` is one JSON-friendly blob per thread; round-trip it however you like.
724
+
725
+ ##### Trade-offs
726
+
727
+ - **Cold writes are session-boundary only.** No per-append S3 traffic. Long-running sessions are durable via Temporal workflow history + Redis TTL.
728
+ - **Single-writer assumed.** Two sessions started simultaneously on the same `threadId` would race on flush. Use distinct `threadId`s or coordinate at a layer above zeitlich.
729
+ - **`deleteHot: true` by default on flush.** Memory drops immediately; the next continue re-hydrates in one `GetObject`. Override per-call via the tiered manager if you want to keep the hot tier warm.
730
+ - **`mode: "new"` overwrites the cold archive for that `threadId`.** A session entered with `mode: "new"` skips `hydrateThread`; on exit `flushThread` writes the fresh snapshot back, silently replacing any prior cold-tier blob at the same `(threadKey, threadId)`. To resume a thread, use `mode: "continue"` or `mode: "fork"` — passing a previously-used `threadId` with `mode: "new"` is destructive by design.
731
+
669
732
  #### Sandbox Initialization (`SandboxInit`)
670
733
 
671
734
  The `sandbox` field controls how a sandbox is created or reused:
@@ -983,16 +1046,21 @@ Safe for use in Temporal workflow files:
983
1046
 
984
1047
  Framework-agnostic utilities for activities, worker setup, and Node.js code:
985
1048
 
986
- | Export | Description |
987
- | ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
988
- | `createRunAgentActivity` | Wraps a handler into a scope-prefixed `RunAgentActivity` with auto-fetched parent workflow state |
989
- | `withParentWorkflowState` | Wraps a tool handler into an `ActivityToolHandler` with auto-fetched parent workflow state |
990
- | `createThreadManager` | Generic Redis-backed thread manager factory |
991
- | `toTree` | Generate file tree string from an `IFileSystem` instance |
992
- | `withSandbox` | Wraps a handler to auto-resolve sandbox from context (pairs with `withAutoAppend`) |
993
- | `NodeFsSandboxFileSystem` | `node:fs` adapter for `SandboxFileSystem` — read skills from the worker's local disk |
994
- | `FileSystemSkillProvider` | Load skills from a directory following the agentskills.io layout |
995
- | Tool handlers | `bashHandler`, `editHandler`, `globHandler`, `readFileHandler`, `writeFileHandler`, `createAskUserQuestionHandler` |
1049
+ | Export | Description |
1050
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------ |
1051
+ | `createRunAgentActivity` | Wraps a handler into a scope-prefixed `RunAgentActivity` with auto-fetched parent workflow state |
1052
+ | `withParentWorkflowState` | Wraps a tool handler into an `ActivityToolHandler` with auto-fetched parent workflow state |
1053
+ | `createThreadManager` | Generic Redis-backed thread manager factory |
1054
+ | `createTieredThreadManager` | Redis hot + pluggable cold tier; adds `hydrate()` / `flush()` to `BaseThreadManager<T>` |
1055
+ | `createS3ColdStore` | Built-in `ColdThreadStore` backed by an `@aws-sdk/client-s3` `S3Client` |
1056
+ | `encodeSnapshot` | Low-level helper that builds a `ThreadSnapshot` from the hot-tier Redis state |
1057
+ | `applySnapshot` | Low-level helper that restores a `ThreadSnapshot` into Redis (idempotent) |
1058
+ | `clearHotTier` | Low-level helper that deletes every Redis key the thread manager wrote for a given `(threadKey, threadId)` |
1059
+ | `toTree` | Generate file tree string from an `IFileSystem` instance |
1060
+ | `withSandbox` | Wraps a handler to auto-resolve sandbox from context (pairs with `withAutoAppend`) |
1061
+ | `NodeFsSandboxFileSystem` | `node:fs` adapter for `SandboxFileSystem` — read skills from the worker's local disk |
1062
+ | `FileSystemSkillProvider` | Load skills from a directory following the agentskills.io layout |
1063
+ | Tool handlers | `bashHandler`, `editHandler`, `globHandler`, `readFileHandler`, `writeFileHandler`, `createAskUserQuestionHandler` |
996
1064
 
997
1065
  ### Thread Adapter Entry Points
998
1066
 
@@ -1,7 +1,8 @@
1
1
  import Redis from 'ioredis';
2
- import { Content, Part, GoogleGenAI } from '@google/genai';
3
- import { a as ModelInvoker, b as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, c as ToolHandlerResponse, d as ActivityToolHandler } from './types-DKsCdAtQ.js';
4
- import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-Clhqautb.js';
2
+ import { Part, Content, GoogleGenAI } from '@google/genai';
3
+ import { a as ModelInvoker, b as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, c as ToolHandlerResponse, d as ActivityToolHandler } from './types-CwN6_tAL.js';
4
+ import { C as ColdThreadStore } from './cold-store-CFHwemBJ.js';
5
+ import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-L5bvbF-n.js';
5
6
  import { A as ADAPTER_ID } from './adapter-id-BB-mmrts.js';
6
7
 
7
8
  /** SDK-native content type for Google GenAI human messages */
@@ -20,6 +21,12 @@ interface GoogleGenAIThreadManagerConfig {
20
21
  /** Thread key, defaults to 'messages' */
21
22
  key?: string;
22
23
  hooks?: GoogleGenAIThreadManagerHooks;
24
+ /**
25
+ * Override the default thread TTL (90 days). When pairing the
26
+ * adapter with a durable cold tier, a shorter TTL (hours) is
27
+ * typically more appropriate.
28
+ */
29
+ ttlSeconds?: number;
23
30
  }
24
31
  /** Prepared payload ready to send to the Google GenAI API */
25
32
  interface GoogleGenAIInvocationPayload {
@@ -45,6 +52,19 @@ interface GoogleGenAIAdapterConfig {
45
52
  /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */
46
53
  model?: string;
47
54
  hooks?: GoogleGenAIThreadManagerHooks;
55
+ /**
56
+ * Optional durable cold tier (e.g. S3, R2, GCS). When provided,
57
+ * the session hydrates the thread on entry (`continue`/`fork`) and
58
+ * flushes it on every exit path. When omitted, the adapter is
59
+ * Redis-only and `hydrateThread`/`flushThread` activities are no-ops.
60
+ */
61
+ coldStore?: ColdThreadStore;
62
+ /**
63
+ * Override the default Redis TTL (90 days). When pairing the
64
+ * adapter with a `coldStore`, a shorter TTL (hours) is typically
65
+ * more appropriate.
66
+ */
67
+ ttlSeconds?: number;
48
68
  }
49
69
  /**
50
70
  * Tool response type accepted by the Google GenAI adapter.
@@ -139,4 +159,4 @@ interface GoogleGenAIAdapter {
139
159
  */
140
160
  declare function createGoogleGenAIAdapter(config: GoogleGenAIAdapterConfig): GoogleGenAIAdapter;
141
161
 
142
- export { type GoogleGenAIThreadManagerHooks as G, type StoredContent as S, type GoogleGenAIAdapter as a, type GoogleGenAIAdapterConfig as b, createGoogleGenAIAdapter as c, type GoogleGenAIThreadOps as d, type GoogleGenAIToolResponse as e, createGoogleGenAIThreadManager as f, type GoogleGenAIThreadManager as g, type GoogleGenAIThreadManagerConfig as h, type GoogleGenAIContent as i, type GoogleGenAIInvocationPayload as j };
162
+ export { type GoogleGenAIThreadManagerHooks as G, type StoredContent as S, type GoogleGenAIAdapter as a, type GoogleGenAIAdapterConfig as b, type GoogleGenAIContent as c, type GoogleGenAIInvocationPayload as d, type GoogleGenAIThreadManager as e, type GoogleGenAIThreadManagerConfig as f, type GoogleGenAIThreadOps as g, type GoogleGenAIToolResponse as h, createGoogleGenAIAdapter as i, createGoogleGenAIThreadManager as j };
@@ -1,7 +1,8 @@
1
1
  import Redis from 'ioredis';
2
- import { Content, Part, GoogleGenAI } from '@google/genai';
3
- import { a as ModelInvoker, b as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, c as ToolHandlerResponse, d as ActivityToolHandler } from './types-4Wmk-wRq.cjs';
4
- import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-DRJt1TMi.cjs';
2
+ import { Part, Content, GoogleGenAI } from '@google/genai';
3
+ import { a as ModelInvoker, b as PrefixedThreadOps, S as ScopedPrefix, R as RouterContext, c as ToolHandlerResponse, d as ActivityToolHandler } from './types-CnuN9T6t.cjs';
4
+ import { C as ColdThreadStore } from './cold-store-BC5L5Z8A.cjs';
5
+ import { T as ThreadManagerHooks, P as ProviderThreadManager } from './types-oxt8GN97.cjs';
5
6
  import { A as ADAPTER_ID } from './adapter-id-BB-mmrts.cjs';
6
7
 
7
8
  /** SDK-native content type for Google GenAI human messages */
@@ -20,6 +21,12 @@ interface GoogleGenAIThreadManagerConfig {
20
21
  /** Thread key, defaults to 'messages' */
21
22
  key?: string;
22
23
  hooks?: GoogleGenAIThreadManagerHooks;
24
+ /**
25
+ * Override the default thread TTL (90 days). When pairing the
26
+ * adapter with a durable cold tier, a shorter TTL (hours) is
27
+ * typically more appropriate.
28
+ */
29
+ ttlSeconds?: number;
23
30
  }
24
31
  /** Prepared payload ready to send to the Google GenAI API */
25
32
  interface GoogleGenAIInvocationPayload {
@@ -45,6 +52,19 @@ interface GoogleGenAIAdapterConfig {
45
52
  /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */
46
53
  model?: string;
47
54
  hooks?: GoogleGenAIThreadManagerHooks;
55
+ /**
56
+ * Optional durable cold tier (e.g. S3, R2, GCS). When provided,
57
+ * the session hydrates the thread on entry (`continue`/`fork`) and
58
+ * flushes it on every exit path. When omitted, the adapter is
59
+ * Redis-only and `hydrateThread`/`flushThread` activities are no-ops.
60
+ */
61
+ coldStore?: ColdThreadStore;
62
+ /**
63
+ * Override the default Redis TTL (90 days). When pairing the
64
+ * adapter with a `coldStore`, a shorter TTL (hours) is typically
65
+ * more appropriate.
66
+ */
67
+ ttlSeconds?: number;
48
68
  }
49
69
  /**
50
70
  * Tool response type accepted by the Google GenAI adapter.
@@ -139,4 +159,4 @@ interface GoogleGenAIAdapter {
139
159
  */
140
160
  declare function createGoogleGenAIAdapter(config: GoogleGenAIAdapterConfig): GoogleGenAIAdapter;
141
161
 
142
- export { type GoogleGenAIThreadManagerHooks as G, type StoredContent as S, type GoogleGenAIAdapter as a, type GoogleGenAIAdapterConfig as b, createGoogleGenAIAdapter as c, type GoogleGenAIThreadOps as d, type GoogleGenAIToolResponse as e, createGoogleGenAIThreadManager as f, type GoogleGenAIThreadManager as g, type GoogleGenAIThreadManagerConfig as h, type GoogleGenAIContent as i, type GoogleGenAIInvocationPayload as j };
162
+ export { type GoogleGenAIThreadManagerHooks as G, type StoredContent as S, type GoogleGenAIAdapter as a, type GoogleGenAIAdapterConfig as b, type GoogleGenAIContent as c, type GoogleGenAIInvocationPayload as d, type GoogleGenAIThreadManager as e, type GoogleGenAIThreadManagerConfig as f, type GoogleGenAIThreadOps as g, type GoogleGenAIToolResponse as h, createGoogleGenAIAdapter as i, createGoogleGenAIThreadManager as j };
@@ -1,6 +1,6 @@
1
1
  import { d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult } from '../../../types-CJ7tCdl6.cjs';
2
- import { D as DaytonaSandboxCreateOptions, a as DaytonaSandbox, b as DaytonaSandboxConfig } from '../../../types-C90VoEpt.cjs';
3
- export { c as DaytonaSandboxFileSystem } from '../../../types-C90VoEpt.cjs';
2
+ import { D as DaytonaSandboxCreateOptions, a as DaytonaSandbox, b as DaytonaSandboxConfig } from '../../../types-CjY93AWZ.cjs';
3
+ export { c as DaytonaSandboxFileSystem } from '../../../types-CjY93AWZ.cjs';
4
4
  import '@temporalio/common';
5
5
  import '@daytonaio/sdk';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult } from '../../../types-CJ7tCdl6.js';
2
- import { D as DaytonaSandboxCreateOptions, a as DaytonaSandbox, b as DaytonaSandboxConfig } from '../../../types-DpFD8ofR.js';
3
- export { c as DaytonaSandboxFileSystem } from '../../../types-DpFD8ofR.js';
2
+ import { D as DaytonaSandboxCreateOptions, a as DaytonaSandbox, b as DaytonaSandboxConfig } from '../../../types-gVa5XCWD.js';
3
+ export { c as DaytonaSandboxFileSystem } from '../../../types-gVa5XCWD.js';
4
4
  import '@temporalio/common';
5
5
  import '@daytonaio/sdk';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { proxyActivities } from '@temporalio/workflow';
2
2
  import { S as SandboxOps } from '../../../types-CJ7tCdl6.cjs';
3
- import { D as DaytonaSandboxCreateOptions } from '../../../types-C90VoEpt.cjs';
3
+ import { D as DaytonaSandboxCreateOptions } from '../../../types-CjY93AWZ.cjs';
4
4
  import '@temporalio/common';
5
5
  import '@daytonaio/sdk';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { proxyActivities } from '@temporalio/workflow';
2
2
  import { S as SandboxOps } from '../../../types-CJ7tCdl6.js';
3
- import { D as DaytonaSandboxCreateOptions } from '../../../types-DpFD8ofR.js';
3
+ import { D as DaytonaSandboxCreateOptions } from '../../../types-gVa5XCWD.js';
4
4
  import '@temporalio/common';
5
5
  import '@daytonaio/sdk';
6
6
 
@@ -1,4 +1,4 @@
1
- import { c as SandboxFileSystem, F as FileStat, D as DirentEntry, a as SandboxCreateOptions, b as Sandbox$1, d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult, g as SandboxSnapshot } from '../../../types-CJ7tCdl6.cjs';
1
+ import { c as SandboxFileSystem, F as FileStat, D as DirentEntry, b as Sandbox$1, a as SandboxCreateOptions, d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult, g as SandboxSnapshot } from '../../../types-CJ7tCdl6.cjs';
2
2
  import { Sandbox } from '@e2b/code-interpreter';
3
3
  import '@temporalio/common';
4
4
 
@@ -1,4 +1,4 @@
1
- import { c as SandboxFileSystem, F as FileStat, D as DirentEntry, a as SandboxCreateOptions, b as Sandbox$1, d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult, g as SandboxSnapshot } from '../../../types-CJ7tCdl6.js';
1
+ import { c as SandboxFileSystem, F as FileStat, D as DirentEntry, b as Sandbox$1, a as SandboxCreateOptions, d as SandboxProvider, e as SandboxCapabilities, f as SandboxCreateResult, g as SandboxSnapshot } from '../../../types-CJ7tCdl6.js';
2
2
  import { Sandbox } from '@e2b/code-interpreter';
3
3
  import '@temporalio/common';
4
4
 
@@ -16,6 +16,9 @@ function getThreadMetaKey(threadKey, threadId) {
16
16
  function getThreadStateKey(threadKey, threadId) {
17
17
  return `${threadKey}:state:thread:${threadId}`;
18
18
  }
19
+ function getThreadDedupKey(threadId, dedupId) {
20
+ return `dedup:${dedupId}:thread:${threadId}`;
21
+ }
19
22
 
20
23
  // src/lib/thread/manager.ts
21
24
  var APPEND_IDEMPOTENT_SCRIPT = `
@@ -29,9 +32,6 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
29
32
  redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
30
33
  return 1
31
34
  `;
32
- function getDedupKey(threadId, id) {
33
- return `dedup:${id}:thread:${threadId}`;
34
- }
35
35
  function createThreadManager(config) {
36
36
  const {
37
37
  redis,
@@ -39,11 +39,13 @@ function createThreadManager(config) {
39
39
  key = "messages",
40
40
  serialize = (m) => JSON.stringify(m),
41
41
  deserialize = (raw) => JSON.parse(raw),
42
- idOf
42
+ idOf,
43
+ ttlSeconds = THREAD_TTL_SECONDS
43
44
  } = config;
44
45
  const redisKey = getThreadListKey(key, threadId);
45
46
  const metaKey = getThreadMetaKey(key, threadId);
46
47
  const stateKey = getThreadStateKey(key, threadId);
48
+ const dedupKey = (id) => getThreadDedupKey(threadId, id);
47
49
  async function assertThreadExists() {
48
50
  const exists = await redis.exists(metaKey);
49
51
  if (!exists) {
@@ -53,7 +55,7 @@ function createThreadManager(config) {
53
55
  return {
54
56
  async initialize() {
55
57
  await redis.del(redisKey);
56
- await redis.set(metaKey, "1", "EX", THREAD_TTL_SECONDS);
58
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
57
59
  },
58
60
  async load() {
59
61
  await assertThreadExists();
@@ -65,18 +67,17 @@ function createThreadManager(config) {
65
67
  await assertThreadExists();
66
68
  if (idOf) {
67
69
  const dedupId = messages.map(idOf).join(":");
68
- const dedupKey = getDedupKey(threadId, dedupId);
69
70
  await redis.eval(
70
71
  APPEND_IDEMPOTENT_SCRIPT,
71
72
  2,
72
- dedupKey,
73
+ dedupKey(dedupId),
73
74
  redisKey,
74
- String(THREAD_TTL_SECONDS),
75
+ String(ttlSeconds),
75
76
  ...messages.map(serialize)
76
77
  );
77
78
  } else {
78
79
  await redis.rpush(redisKey, ...messages.map(serialize));
79
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
80
+ await redis.expire(redisKey, ttlSeconds);
80
81
  }
81
82
  },
82
83
  async fork(newThreadId) {
@@ -91,11 +92,11 @@ function createThreadManager(config) {
91
92
  if (data.length > 0) {
92
93
  const newKey = getThreadListKey(key, newThreadId);
93
94
  await redis.rpush(newKey, ...data);
94
- await redis.expire(newKey, THREAD_TTL_SECONDS);
95
+ await redis.expire(newKey, ttlSeconds);
95
96
  }
96
97
  if (stateRaw != null) {
97
98
  const newStateKey = getThreadStateKey(key, newThreadId);
98
- await redis.set(newStateKey, stateRaw, "EX", THREAD_TTL_SECONDS);
99
+ await redis.set(newStateKey, stateRaw, "EX", ttlSeconds);
99
100
  }
100
101
  return forked;
101
102
  },
@@ -110,15 +111,13 @@ function createThreadManager(config) {
110
111
  const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
111
112
  await redis.del(redisKey);
112
113
  if (existingIds.length > 0) {
113
- await redis.del(
114
- ...existingIds.map((id) => getDedupKey(threadId, id))
115
- );
114
+ await redis.del(...existingIds.map(dedupKey));
116
115
  }
117
116
  if (messages.length > 0) {
118
117
  await redis.rpush(redisKey, ...messages.map(serialize));
119
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
118
+ await redis.expire(redisKey, ttlSeconds);
120
119
  }
121
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
120
+ await redis.expire(metaKey, ttlSeconds);
122
121
  },
123
122
  async delete() {
124
123
  await redis.del(redisKey, metaKey, stateKey);
@@ -130,12 +129,7 @@ function createThreadManager(config) {
130
129
  },
131
130
  async saveState(state) {
132
131
  await assertThreadExists();
133
- await redis.set(
134
- stateKey,
135
- JSON.stringify(state),
136
- "EX",
137
- THREAD_TTL_SECONDS
138
- );
132
+ await redis.set(stateKey, JSON.stringify(state), "EX", ttlSeconds);
139
133
  },
140
134
  async deleteState() {
141
135
  await redis.del(stateKey);
@@ -164,20 +158,133 @@ function createThreadManager(config) {
164
158
  if (idx === -1) return;
165
159
  if (idx === 0) {
166
160
  await redis.del(redisKey);
167
- await redis.expire(metaKey, THREAD_TTL_SECONDS);
161
+ await redis.expire(metaKey, ttlSeconds);
168
162
  } else {
169
163
  await redis.ltrim(redisKey, 0, idx - 1);
170
- await redis.expire(redisKey, THREAD_TTL_SECONDS);
164
+ await redis.expire(redisKey, ttlSeconds);
171
165
  }
172
166
  if (removedIds.length > 0) {
173
- await redis.del(
174
- ...removedIds.map((id) => getDedupKey(threadId, id))
175
- );
167
+ await redis.del(...removedIds.map(dedupKey));
176
168
  }
177
169
  }
178
170
  };
179
171
  }
180
172
 
173
+ // src/lib/thread/snapshot.ts
174
+ async function encodeSnapshot(config) {
175
+ const { redis, threadKey, threadId, idOf } = config;
176
+ const metaKey = getThreadMetaKey(threadKey, threadId);
177
+ if (await redis.exists(metaKey) === 0) {
178
+ return null;
179
+ }
180
+ const listKey = getThreadListKey(threadKey, threadId);
181
+ const stateKey = getThreadStateKey(threadKey, threadId);
182
+ const messages = await redis.lrange(listKey, 0, -1);
183
+ const stateRaw = await redis.get(stateKey);
184
+ const state = stateRaw == null ? null : JSON.parse(stateRaw);
185
+ const dedupIds = idOf ? messages.map(idOf) : [];
186
+ return { v: 1, messages, state, dedupIds };
187
+ }
188
+ async function applySnapshot(config) {
189
+ const {
190
+ redis,
191
+ threadKey,
192
+ threadId,
193
+ snapshot,
194
+ ttlSeconds = THREAD_TTL_SECONDS
195
+ } = config;
196
+ const metaKey = getThreadMetaKey(threadKey, threadId);
197
+ if (await redis.exists(metaKey) === 1) {
198
+ return;
199
+ }
200
+ const listKey = getThreadListKey(threadKey, threadId);
201
+ const stateKey = getThreadStateKey(threadKey, threadId);
202
+ await redis.del(listKey, stateKey);
203
+ const pipeline = redis.pipeline();
204
+ if (snapshot.messages.length > 0) {
205
+ pipeline.rpush(listKey, ...snapshot.messages);
206
+ pipeline.expire(listKey, ttlSeconds);
207
+ }
208
+ if (snapshot.state != null) {
209
+ pipeline.set(stateKey, JSON.stringify(snapshot.state), "EX", ttlSeconds);
210
+ }
211
+ for (const id of snapshot.dedupIds) {
212
+ pipeline.set(getThreadDedupKey(threadId, id), "1", "EX", ttlSeconds);
213
+ }
214
+ const results = await pipeline.exec();
215
+ if (results) {
216
+ const firstErr = results.find(([err]) => err)?.[0] ?? null;
217
+ if (firstErr) {
218
+ await redis.del(
219
+ listKey,
220
+ stateKey,
221
+ ...snapshot.dedupIds.map((id) => getThreadDedupKey(threadId, id))
222
+ ).catch(() => void 0);
223
+ throw firstErr;
224
+ }
225
+ }
226
+ await redis.set(metaKey, "1", "EX", ttlSeconds);
227
+ }
228
+ async function clearHotTier(config) {
229
+ const { redis, threadKey, threadId, dedupIds = [] } = config;
230
+ const keys = [
231
+ getThreadListKey(threadKey, threadId),
232
+ getThreadMetaKey(threadKey, threadId),
233
+ getThreadStateKey(threadKey, threadId),
234
+ ...dedupIds.map((id) => getThreadDedupKey(threadId, id))
235
+ ];
236
+ await redis.del(...keys);
237
+ }
238
+
239
+ // src/lib/thread/tiered.ts
240
+ function createTieredThreadManager(config) {
241
+ const {
242
+ redis,
243
+ threadId,
244
+ key = "messages",
245
+ coldStore,
246
+ idOf,
247
+ deserialize = (raw) => JSON.parse(raw),
248
+ ttlSeconds = THREAD_TTL_SECONDS
249
+ } = config;
250
+ const base = createThreadManager(config);
251
+ const rawIdOf = idOf ? (raw) => idOf(deserialize(raw)) : void 0;
252
+ return Object.assign(base, {
253
+ async hydrate() {
254
+ if (!coldStore) return;
255
+ const snapshot = await coldStore.read(key, threadId);
256
+ if (!snapshot) return;
257
+ await applySnapshot({
258
+ redis,
259
+ threadKey: key,
260
+ threadId,
261
+ snapshot,
262
+ ttlSeconds
263
+ });
264
+ },
265
+ async flush(opts) {
266
+ if (!coldStore) return;
267
+ const snapshot = await encodeSnapshot({
268
+ redis,
269
+ threadKey: key,
270
+ threadId,
271
+ ...rawIdOf ? { idOf: rawIdOf } : {}
272
+ });
273
+ if (!snapshot) return;
274
+ await coldStore.write(key, threadId, snapshot);
275
+ const deleteHot = opts?.deleteHot ?? true;
276
+ if (deleteHot) {
277
+ await clearHotTier({
278
+ redis,
279
+ threadKey: key,
280
+ threadId,
281
+ dedupIds: snapshot.dedupIds
282
+ });
283
+ }
284
+ }
285
+ });
286
+ }
287
+
181
288
  // src/adapters/thread/anthropic/thread-manager.ts
182
289
  function storedMessageId(msg) {
183
290
  return msg.id;
@@ -210,7 +317,8 @@ function createAnthropicThreadManager(config) {
210
317
  redis: config.redis,
211
318
  threadId: config.threadId,
212
319
  key: config.key,
213
- idOf: storedMessageId
320
+ idOf: storedMessageId,
321
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
214
322
  };
215
323
  const base = createThreadManager(baseConfig);
216
324
  const helpers = {
@@ -399,46 +507,43 @@ async function invokeAnthropicModel({
399
507
  // src/adapters/thread/anthropic/activities.ts
400
508
  function createAnthropicAdapter(config) {
401
509
  const { redis, client } = config;
510
+ const baseExtras = {
511
+ ...config.ttlSeconds !== void 0 && { ttlSeconds: config.ttlSeconds }
512
+ };
513
+ const makeProviderThread = (threadId, threadKey) => createAnthropicThreadManager({
514
+ redis,
515
+ threadId,
516
+ key: threadKey,
517
+ ...baseExtras
518
+ });
519
+ const makeTieredBase = (threadId, threadKey) => createTieredThreadManager({
520
+ redis,
521
+ threadId,
522
+ key: threadKey,
523
+ idOf: storedMessageId,
524
+ ...baseExtras,
525
+ ...config.coldStore && { coldStore: config.coldStore }
526
+ });
402
527
  const threadOps = {
403
528
  async initializeThread(threadId, threadKey) {
404
- const thread = createAnthropicThreadManager({
405
- redis,
406
- threadId,
407
- key: threadKey
408
- });
529
+ const thread = makeProviderThread(threadId, threadKey);
409
530
  await thread.initialize();
410
531
  },
411
532
  async appendHumanMessage(threadId, id, content, threadKey) {
412
- const thread = createAnthropicThreadManager({
413
- redis,
414
- threadId,
415
- key: threadKey
416
- });
533
+ const thread = makeProviderThread(threadId, threadKey);
417
534
  await thread.appendUserMessage(id, content);
418
535
  },
419
536
  async appendSystemMessage(threadId, id, content, threadKey) {
420
- const thread = createAnthropicThreadManager({
421
- redis,
422
- threadId,
423
- key: threadKey
424
- });
537
+ const thread = makeProviderThread(threadId, threadKey);
425
538
  await thread.appendSystemMessage(id, content);
426
539
  },
427
540
  async appendToolResult(id, cfg) {
428
541
  const { threadId, threadKey, toolCallId, toolName, content } = cfg;
429
- const thread = createAnthropicThreadManager({
430
- redis,
431
- threadId,
432
- key: threadKey
433
- });
542
+ const thread = makeProviderThread(threadId, threadKey);
434
543
  await thread.appendToolResult(id, toolCallId, toolName, content);
435
544
  },
436
545
  async appendAgentMessage(threadId, id, message, threadKey) {
437
- const thread = createAnthropicThreadManager({
438
- redis,
439
- threadId,
440
- key: threadKey
441
- });
546
+ const thread = makeProviderThread(threadId, threadKey);
442
547
  await thread.appendAssistantMessage(id, message.content);
443
548
  },
444
549
  async forkThread(sourceThreadId, targetThreadId, threadKey) {
@@ -446,29 +551,30 @@ function createAnthropicAdapter(config) {
446
551
  redis,
447
552
  threadId: sourceThreadId,
448
553
  key: threadKey,
449
- hooks: config.hooks
554
+ hooks: config.hooks,
555
+ ...baseExtras
450
556
  });
451
557
  await thread.fork(targetThreadId);
452
558
  },
453
559
  async truncateThread(threadId, messageId, threadKey) {
454
- const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
560
+ const thread = makeProviderThread(threadId, threadKey);
455
561
  await thread.truncateFromId(messageId);
456
562
  },
457
563
  async loadThreadState(threadId, threadKey) {
458
- const thread = createAnthropicThreadManager({
459
- redis,
460
- threadId,
461
- key: threadKey
462
- });
564
+ const thread = makeProviderThread(threadId, threadKey);
463
565
  return thread.loadState();
464
566
  },
465
567
  async saveThreadState(threadId, state, threadKey) {
466
- const thread = createAnthropicThreadManager({
467
- redis,
468
- threadId,
469
- key: threadKey
470
- });
568
+ const thread = makeProviderThread(threadId, threadKey);
471
569
  await thread.saveState(state);
570
+ },
571
+ async hydrateThread(threadId, threadKey) {
572
+ if (!config.coldStore) return;
573
+ await makeTieredBase(threadId, threadKey).hydrate();
574
+ },
575
+ async flushThread(threadId, threadKey) {
576
+ if (!config.coldStore) return;
577
+ await makeTieredBase(threadId, threadKey).flush();
472
578
  }
473
579
  };
474
580
  function createActivities(scope) {