zeitlich 0.2.31 → 0.2.32

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 (82) hide show
  1. package/dist/{activities-qPkJDAiq.d.cts → activities-DA-bQM12.d.cts} +2 -2
  2. package/dist/{activities-DRSdt8Y3.d.ts → activities-FIXVz7DT.d.ts} +2 -2
  3. package/dist/adapters/thread/anthropic/index.cjs +4 -48
  4. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  5. package/dist/adapters/thread/anthropic/index.d.cts +6 -6
  6. package/dist/adapters/thread/anthropic/index.d.ts +6 -6
  7. package/dist/adapters/thread/anthropic/index.js +4 -48
  8. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  9. package/dist/adapters/thread/anthropic/workflow.cjs +1 -0
  10. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  11. package/dist/adapters/thread/anthropic/workflow.d.cts +4 -4
  12. package/dist/adapters/thread/anthropic/workflow.d.ts +4 -4
  13. package/dist/adapters/thread/anthropic/workflow.js +1 -0
  14. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  15. package/dist/adapters/thread/google-genai/index.cjs +8 -48
  16. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  17. package/dist/adapters/thread/google-genai/index.d.cts +6 -6
  18. package/dist/adapters/thread/google-genai/index.d.ts +6 -6
  19. package/dist/adapters/thread/google-genai/index.js +8 -48
  20. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  21. package/dist/adapters/thread/google-genai/workflow.cjs +1 -0
  22. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  23. package/dist/adapters/thread/google-genai/workflow.d.cts +4 -4
  24. package/dist/adapters/thread/google-genai/workflow.d.ts +4 -4
  25. package/dist/adapters/thread/google-genai/workflow.js +1 -0
  26. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  27. package/dist/adapters/thread/langchain/index.cjs +5 -1
  28. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  29. package/dist/adapters/thread/langchain/index.d.cts +6 -5
  30. package/dist/adapters/thread/langchain/index.d.ts +6 -5
  31. package/dist/adapters/thread/langchain/index.js +5 -1
  32. package/dist/adapters/thread/langchain/index.js.map +1 -1
  33. package/dist/adapters/thread/langchain/workflow.cjs +1 -0
  34. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  35. package/dist/adapters/thread/langchain/workflow.d.cts +4 -4
  36. package/dist/adapters/thread/langchain/workflow.d.ts +4 -4
  37. package/dist/adapters/thread/langchain/workflow.js +1 -0
  38. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  39. package/dist/index.cjs +8 -5
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +6 -6
  42. package/dist/index.d.ts +6 -6
  43. package/dist/index.js +8 -5
  44. package/dist/index.js.map +1 -1
  45. package/dist/{proxy-BkvkV2oU.d.ts → proxy-Br4unLTC.d.ts} +1 -1
  46. package/dist/{proxy-BDQ3Rj6R.d.cts → proxy-CTCYWjkr.d.cts} +1 -1
  47. package/dist/{thread-manager-BLgvv9Gf.d.cts → thread-manager-CUubPYPH.d.cts} +1 -1
  48. package/dist/{thread-manager-DowU4ntB.d.cts → thread-manager-Cv_BR28i.d.cts} +1 -1
  49. package/dist/{thread-manager-Cv82H1wi.d.ts → thread-manager-DKWxHUzD.d.ts} +1 -1
  50. package/dist/{thread-manager-HsAYkyAV.d.ts → thread-manager-YJLoc1vH.d.ts} +1 -1
  51. package/dist/{types-CjeGWQm1.d.cts → types-Bpq5fDI5.d.cts} +7 -4
  52. package/dist/{types-D6UKZZtj.d.ts → types-BxiT8w9d.d.ts} +1 -1
  53. package/dist/{types-BmS-Huc0.d.ts → types-CheCTLeV.d.ts} +7 -4
  54. package/dist/{types-e_38QaKo.d.cts → types-NJDyMyUx.d.cts} +1 -1
  55. package/dist/{workflow-CNshfqSO.d.cts → workflow-BWKQcz9d.d.cts} +1 -1
  56. package/dist/{workflow-CTcrPZAV.d.ts → workflow-D8wK7TJY.d.ts} +1 -1
  57. package/dist/workflow.cjs +4 -1
  58. package/dist/workflow.cjs.map +1 -1
  59. package/dist/workflow.d.cts +2 -2
  60. package/dist/workflow.d.ts +2 -2
  61. package/dist/workflow.js +4 -1
  62. package/dist/workflow.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/adapters/thread/anthropic/activities.ts +10 -0
  65. package/src/adapters/thread/anthropic/model-invoker.ts +2 -5
  66. package/src/adapters/thread/google-genai/activities.ts +14 -0
  67. package/src/adapters/thread/google-genai/model-invoker.ts +2 -5
  68. package/src/adapters/thread/langchain/activities.ts +11 -0
  69. package/src/adapters/thread/langchain/model-invoker.ts +2 -3
  70. package/src/lib/.env +1 -0
  71. package/src/lib/model/types.ts +3 -2
  72. package/src/lib/session/session-edge-cases.integration.test.ts +6 -0
  73. package/src/lib/session/session.integration.test.ts +3 -0
  74. package/src/lib/session/session.ts +4 -0
  75. package/src/lib/session/types.ts +7 -0
  76. package/src/lib/thread/proxy.ts +1 -0
  77. package/src/lib/types.ts +1 -0
  78. package/src/lib/virtual-fs/manager.ts +3 -3
  79. package/src/lib/virtual-fs/proxy.ts +3 -3
  80. package/src/lib/virtual-fs/types.ts +1 -2
  81. package/src/lib/virtual-fs/with-virtual-fs.ts +4 -4
  82. package/src/tools/bash/.env +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.31",
3
+ "version": "0.2.32",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -164,6 +164,16 @@ export function createAnthropicAdapter(
164
164
  await thread.appendToolResult(id, toolCallId, toolName, content);
165
165
  },
166
166
 
167
+ async appendAgentMessage(
168
+ threadId: string,
169
+ id: string,
170
+ message: Anthropic.Messages.Message,
171
+ threadKey?: string,
172
+ ): Promise<void> {
173
+ const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
174
+ await thread.appendAssistantMessage(id, message.content);
175
+ },
176
+
167
177
  async forkThread(
168
178
  sourceThreadId: string,
169
179
  targetThreadId: string,
@@ -3,7 +3,6 @@ import type Anthropic from "@anthropic-ai/sdk";
3
3
  import type { SerializableToolDefinition } from "../../../lib/types";
4
4
  import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
5
5
  import { createAnthropicThreadManager, type AnthropicThreadManagerHooks } from "./thread-manager";
6
- import { v4 as uuidv4 } from "uuid";
7
6
 
8
7
  export interface AnthropicModelInvokerConfig {
9
8
  redis: Redis;
@@ -29,8 +28,8 @@ function toAnthropicTools(
29
28
  * `ModelInvoker<Anthropic.Messages.Message>` contract.
30
29
  *
31
30
  * Loads the conversation thread from Redis, invokes the Claude model via
32
- * `client.messages.create`, appends the AI response, and returns
33
- * a normalised AgentResponse.
31
+ * `client.messages.create`, and returns a normalised AgentResponse.
32
+ * The caller is responsible for appending the response to the thread.
34
33
  *
35
34
  * @example
36
35
  * ```typescript
@@ -74,8 +73,6 @@ export function createAnthropicModelInvoker({
74
73
  ...(tools ? { tools } : {}),
75
74
  });
76
75
 
77
- await thread.appendAssistantMessage(uuidv4(), response.content);
78
-
79
76
  const toolCalls = response.content.filter(
80
77
  (block): block is Anthropic.Messages.ToolUseBlock =>
81
78
  block.type === "tool_use",
@@ -194,6 +194,20 @@ export function createGoogleGenAIAdapter(
194
194
  );
195
195
  },
196
196
 
197
+ async appendAgentMessage(
198
+ threadId: string,
199
+ id: string,
200
+ message: Content,
201
+ threadKey?: string,
202
+ ): Promise<void> {
203
+ const thread = createGoogleGenAIThreadManager({
204
+ redis,
205
+ threadId,
206
+ key: threadKey,
207
+ });
208
+ await thread.appendModelContent(id, message.parts ?? []);
209
+ },
210
+
197
211
  async forkThread(
198
212
  sourceThreadId: string,
199
213
  targetThreadId: string,
@@ -3,7 +3,6 @@ import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
3
3
  import type { SerializableToolDefinition } from "../../../lib/types";
4
4
  import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
5
5
  import { createGoogleGenAIThreadManager, type GoogleGenAIThreadManagerHooks } from "./thread-manager";
6
- import { v4 as uuidv4 } from "uuid";
7
6
 
8
7
  export interface GoogleGenAIModelInvokerConfig {
9
8
  redis: Redis;
@@ -27,8 +26,8 @@ function toFunctionDeclarations(
27
26
  * `ModelInvoker<Content>` contract.
28
27
  *
29
28
  * Loads the conversation thread from Redis, invokes the Gemini model via
30
- * `client.models.generateContent`, appends the AI response, and returns
31
- * a normalised AgentResponse.
29
+ * `client.models.generateContent`, and returns a normalised AgentResponse.
30
+ * The caller is responsible for appending the response to the thread.
32
31
  *
33
32
  * @example
34
33
  * ```typescript
@@ -77,8 +76,6 @@ export function createGoogleGenAIModelInvoker({
77
76
  const responseParts = response.candidates?.[0]?.content?.parts ?? [];
78
77
  const modelContent: Content = { role: "model", parts: responseParts };
79
78
 
80
- await thread.appendModelContent(uuidv4(), responseParts);
81
-
82
79
  const functionCalls = response.functionCalls ?? [];
83
80
 
84
81
  return {
@@ -148,6 +148,17 @@ export function createLangChainAdapter(
148
148
  await thread.appendToolResult(id, toolCallId, "", content);
149
149
  },
150
150
 
151
+ async appendAgentMessage(
152
+ threadId: string,
153
+ id: string,
154
+ message: StoredMessage,
155
+ threadKey?: string,
156
+ ): Promise<void> {
157
+ const thread = createLangChainThreadManager({ redis, threadId, key: threadKey });
158
+ const patched = { ...message, data: { ...message.data, id } };
159
+ await thread.append([patched]);
160
+ },
161
+
151
162
  async forkThread(
152
163
  sourceThreadId: string,
153
164
  targetThreadId: string,
@@ -17,7 +17,8 @@ export interface LangChainModelInvokerConfig<TModel extends BaseChatModel<any> =
17
17
  * `ModelInvoker<StoredMessage>` contract.
18
18
  *
19
19
  * Loads the conversation thread from Redis, invokes a LangChain chat model,
20
- * appends the AI response, and returns a normalised AgentResponse.
20
+ * and returns a normalised AgentResponse.
21
+ * The caller is responsible for appending the response to the thread.
21
22
  *
22
23
  * @example
23
24
  * ```typescript
@@ -54,8 +55,6 @@ export function createLangChainModelInvoker<TModel extends BaseChatModel<any> =
54
55
  },
55
56
  );
56
57
 
57
- await thread.append([response.toDict()]);
58
-
59
58
  const toolCalls = response.tool_calls ?? [];
60
59
 
61
60
  return {
package/src/lib/.env ADDED
@@ -0,0 +1 @@
1
+ E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9
@@ -33,8 +33,9 @@ export interface ModelInvokerConfig {
33
33
 
34
34
  /**
35
35
  * Generic model invocation contract.
36
- * Implementations load the thread, call the LLM, append the response,
37
- * and return a normalised AgentResponse.
36
+ * Implementations load the thread, call the LLM, and return a normalised
37
+ * AgentResponse. The caller (workflow) is responsible for appending the
38
+ * response to the thread with a deterministic ID.
38
39
  *
39
40
  * Framework adapters (e.g. `zeitlich/langchain`) provide concrete
40
41
  * implementations of this type.
@@ -88,6 +88,9 @@ function createMockThreadOps() {
88
88
  appendSystemMessage: async (threadId, id, content) => {
89
89
  log.push({ op: "appendSystemMessage", args: [threadId, id, content] });
90
90
  },
91
+ appendAgentMessage: async (threadId, id, message) => {
92
+ log.push({ op: "appendAgentMessage", args: [threadId, id, message] });
93
+ },
91
94
  forkThread: async (source, target) => {
92
95
  log.push({ op: "forkThread", args: [source, target] });
93
96
  },
@@ -752,6 +755,9 @@ describe("createSession edge cases", () => {
752
755
  appendSystemMessage: async (threadId, id, content) => {
753
756
  log.push({ op: "appendSystemMessage", args: [threadId, id, content] });
754
757
  },
758
+ appendAgentMessage: async (threadId, id, message) => {
759
+ log.push({ op: "appendAgentMessage", args: [threadId, id, message] });
760
+ },
755
761
  forkThread: async (source, target) => {
756
762
  log.push({ op: "forkThread", args: [source, target] });
757
763
  },
@@ -99,6 +99,9 @@ function createMockThreadOps() {
99
99
  appendSystemMessage: async (threadId, id, content) => {
100
100
  log.push({ op: "appendSystemMessage", args: [threadId, id, content] });
101
101
  },
102
+ appendAgentMessage: async (threadId, id, message) => {
103
+ log.push({ op: "appendAgentMessage", args: [threadId, id, message] });
104
+ },
102
105
  forkThread: async (source, target) => {
103
106
  log.push({ op: "forkThread", args: [source, target] });
104
107
  },
@@ -139,6 +139,7 @@ export async function createSession<
139
139
  appendHumanMessage,
140
140
  initializeThread,
141
141
  appendSystemMessage,
142
+ appendAgentMessage,
142
143
  forkThread,
143
144
  } = threadOps;
144
145
 
@@ -285,6 +286,7 @@ export async function createSession<
285
286
  : result.fileTree;
286
287
  stateManager.mergeUpdate({
287
288
  fileTree,
289
+ virtualFsCtx: virtualFsConfig.ctx,
288
290
  ...(skillFiles && { inlineFiles: skillFiles }),
289
291
  } as Partial<AgentState<TState>>);
290
292
  }
@@ -355,6 +357,8 @@ export async function createSession<
355
357
  metadata,
356
358
  });
357
359
 
360
+ await appendAgentMessage(threadId, uuid4(), message, threadKey);
361
+
358
362
  if (usage) {
359
363
  stateManager.updateUsage(usage);
360
364
  }
@@ -39,6 +39,13 @@ export interface ThreadOps<TContent = string> {
39
39
  ): Promise<void>;
40
40
  /** Append a tool result to the thread */
41
41
  appendToolResult(id: string, config: ToolResultConfig): Promise<void>;
42
+ /** Append the model's response to the thread */
43
+ appendAgentMessage(
44
+ threadId: string,
45
+ id: string,
46
+ message: unknown,
47
+ threadKey?: string
48
+ ): Promise<void>;
42
49
  /** Append a system message to the thread */
43
50
  appendSystemMessage(
44
51
  threadId: string,
@@ -51,6 +51,7 @@ export function createThreadOpsProxy(
51
51
  initializeThread: acts[p("initializeThread")],
52
52
  appendHumanMessage: acts[p("appendHumanMessage")],
53
53
  appendToolResult: acts[p("appendToolResult")],
54
+ appendAgentMessage: acts[p("appendAgentMessage")],
54
55
  appendSystemMessage: acts[p("appendSystemMessage")],
55
56
  forkThread: acts[p("forkThread")],
56
57
  } as ActivityInterfaceFor<ThreadOps>;
package/src/lib/types.ts CHANGED
@@ -27,6 +27,7 @@ export interface BaseAgentState {
27
27
  fileTree: FileEntry[];
28
28
  /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
29
29
  inlineFiles?: Record<string, string>;
30
+ virtualFsCtx?: unknown;
30
31
  systemPrompt?: string;
31
32
  totalInputTokens: number;
32
33
  totalOutputTokens: number;
@@ -30,7 +30,7 @@ export function createVirtualFsActivities<
30
30
  TMeta = FileEntryMetadata,
31
31
  >(
32
32
  resolver: FileResolver<TCtx, TMeta>,
33
- scope: S,
33
+ scope: S
34
34
  ): PrefixedVirtualFsOps<S, TCtx, TMeta> {
35
35
  const ops: VirtualFsOps<TCtx, TMeta> = {
36
36
  resolveFileTree: async (ctx: TCtx) => {
@@ -39,10 +39,10 @@ export function createVirtualFsActivities<
39
39
  },
40
40
  };
41
41
 
42
- const prefix = scope;
42
+ const prefix = `virtualFs${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
43
43
  const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
44
44
 
45
45
  return Object.fromEntries(
46
- Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v]),
46
+ Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
47
47
  ) as PrefixedVirtualFsOps<S, TCtx, TMeta>;
48
48
  }
@@ -18,7 +18,7 @@ import type { VirtualFsOps } from "./types";
18
18
 
19
19
  export function proxyVirtualFsOps<TCtx = unknown>(
20
20
  scope?: string,
21
- options?: Parameters<typeof proxyActivities>[0],
21
+ options?: Parameters<typeof proxyActivities>[0]
22
22
  ): VirtualFsOps<TCtx> {
23
23
  const resolvedScope = scope ?? workflowInfo().workflowType;
24
24
 
@@ -32,10 +32,10 @@ export function proxyVirtualFsOps<TCtx = unknown>(
32
32
  maximumInterval: "30s",
33
33
  backoffCoefficient: 2,
34
34
  },
35
- },
35
+ }
36
36
  );
37
37
 
38
- const prefix = resolvedScope;
38
+ const prefix = `virtualFs${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
39
39
  const p = (key: string): string =>
40
40
  `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
41
41
 
@@ -118,8 +118,7 @@ export type PrefixedVirtualFsOps<
118
118
  */
119
119
  export interface VirtualFsState<TCtx = unknown, TMeta = FileEntryMetadata> {
120
120
  fileTree: FileEntry<TMeta>[];
121
- ctx: TCtx;
122
- workspaceBase?: string;
121
+ virtualFsCtx: TCtx;
123
122
  /** In-memory file contents keyed by path, bypassing the resolver (e.g. skill resources). */
124
123
  inlineFiles?: Record<string, string>;
125
124
  }
@@ -66,7 +66,7 @@ export function withVirtualFs<
66
66
  const state =
67
67
  await queryParentWorkflowState<VirtualFsState<TCtx, TMeta>>(client);
68
68
 
69
- const { fileTree, ctx, workspaceBase, inlineFiles } = state;
69
+ const { fileTree, virtualFsCtx, inlineFiles } = state;
70
70
  if (!fileTree) {
71
71
  return {
72
72
  toolResponse: `Error: No fileTree in agent state. The ${context.toolName} tool requires a virtual filesystem.`,
@@ -77,9 +77,9 @@ export function withVirtualFs<
77
77
  const virtualFs = new VirtualFileSystem(
78
78
  fileTree,
79
79
  resolver,
80
- ctx,
81
- workspaceBase ?? "/",
82
- inlineFiles,
80
+ virtualFsCtx,
81
+ "/",
82
+ inlineFiles
83
83
  );
84
84
  const response = await handler(args, { ...context, virtualFs });
85
85
  const mutations = virtualFs.getMutations();
@@ -0,0 +1 @@
1
+ E2B_API_KEY=e2b_39af116424059782e2aee6942fd70237cc2126c9