zeitlich 0.2.24 → 0.2.26

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 (123) hide show
  1. package/dist/activities-BEJRyDVU.d.cts +137 -0
  2. package/dist/activities-LVQdLF6I.d.ts +137 -0
  3. package/dist/adapters/sandbox/bedrock/index.cjs +35 -10
  4. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  5. package/dist/adapters/sandbox/bedrock/index.d.cts +2 -2
  6. package/dist/adapters/sandbox/bedrock/index.d.ts +2 -2
  7. package/dist/adapters/sandbox/bedrock/index.js +35 -10
  8. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  9. package/dist/adapters/sandbox/bedrock/workflow.d.cts +1 -1
  10. package/dist/adapters/sandbox/bedrock/workflow.d.ts +1 -1
  11. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  12. package/dist/adapters/sandbox/virtual/index.d.cts +8 -7
  13. package/dist/adapters/sandbox/virtual/index.d.ts +8 -7
  14. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  15. package/dist/adapters/sandbox/virtual/workflow.d.cts +3 -2
  16. package/dist/adapters/sandbox/virtual/workflow.d.ts +3 -2
  17. package/dist/adapters/thread/anthropic/index.cjs +356 -0
  18. package/dist/adapters/thread/anthropic/index.cjs.map +1 -0
  19. package/dist/adapters/thread/anthropic/index.d.cts +148 -0
  20. package/dist/adapters/thread/anthropic/index.d.ts +148 -0
  21. package/dist/adapters/thread/anthropic/index.js +351 -0
  22. package/dist/adapters/thread/anthropic/index.js.map +1 -0
  23. package/dist/adapters/thread/anthropic/workflow.cjs +38 -0
  24. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -0
  25. package/dist/adapters/thread/anthropic/workflow.d.cts +37 -0
  26. package/dist/adapters/thread/anthropic/workflow.d.ts +37 -0
  27. package/dist/adapters/thread/anthropic/workflow.js +36 -0
  28. package/dist/adapters/thread/anthropic/workflow.js.map +1 -0
  29. package/dist/adapters/thread/google-genai/index.cjs +95 -97
  30. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  31. package/dist/adapters/thread/google-genai/index.d.cts +9 -111
  32. package/dist/adapters/thread/google-genai/index.d.ts +9 -111
  33. package/dist/adapters/thread/google-genai/index.js +96 -97
  34. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  35. package/dist/adapters/thread/google-genai/workflow.cjs +9 -4
  36. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  37. package/dist/adapters/thread/google-genai/workflow.d.cts +10 -5
  38. package/dist/adapters/thread/google-genai/workflow.d.ts +10 -5
  39. package/dist/adapters/thread/google-genai/workflow.js +9 -4
  40. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  41. package/dist/adapters/thread/langchain/index.cjs +43 -60
  42. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  43. package/dist/adapters/thread/langchain/index.d.cts +24 -38
  44. package/dist/adapters/thread/langchain/index.d.ts +24 -38
  45. package/dist/adapters/thread/langchain/index.js +43 -60
  46. package/dist/adapters/thread/langchain/index.js.map +1 -1
  47. package/dist/adapters/thread/langchain/workflow.cjs +9 -4
  48. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  49. package/dist/adapters/thread/langchain/workflow.d.cts +10 -5
  50. package/dist/adapters/thread/langchain/workflow.d.ts +10 -5
  51. package/dist/adapters/thread/langchain/workflow.js +9 -4
  52. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  53. package/dist/index.cjs +30 -12
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +13 -12
  56. package/dist/index.d.ts +13 -12
  57. package/dist/index.js +31 -13
  58. package/dist/index.js.map +1 -1
  59. package/dist/proxy-BK1ydQt0.d.ts +24 -0
  60. package/dist/proxy-BMAsMHdp.d.cts +24 -0
  61. package/dist/{queries-DwBe2CAA.d.ts → queries-BCgJ9Sr5.d.ts} +1 -1
  62. package/dist/{queries-BYGBImeC.d.cts → queries-DwnE2bu3.d.cts} +1 -1
  63. package/dist/thread-manager-CH9krS3h.d.ts +37 -0
  64. package/dist/thread-manager-Czhpxbt6.d.ts +29 -0
  65. package/dist/thread-manager-DOnQzImf.d.cts +29 -0
  66. package/dist/thread-manager-b4DML-qu.d.cts +37 -0
  67. package/dist/{types-35POpVfa.d.ts → types-BDRDbm3h.d.cts} +22 -1
  68. package/dist/{types-d9NznUqd.d.ts → types-BdCdR41N.d.ts} +10 -0
  69. package/dist/{types-hmferhc2.d.ts → types-CvJyXDYt.d.ts} +44 -123
  70. package/dist/{types-LVKmCNds.d.ts → types-DFUNSYbj.d.ts} +1 -1
  71. package/dist/{types-Bf8KV0Ci.d.cts → types-DRnz-OZp.d.cts} +1 -1
  72. package/dist/{types-7PeMi1bD.d.cts → types-DSOefLpY.d.cts} +44 -123
  73. package/dist/{types-35POpVfa.d.cts → types-WNSeZbWa.d.ts} +22 -1
  74. package/dist/{types-DhTCEMhr.d.cts → types-ZHs2v9Ap.d.cts} +10 -0
  75. package/dist/{types-D_igp10o.d.cts → types-mCVxKIZb.d.cts} +233 -137
  76. package/dist/{types-D_igp10o.d.ts → types-mCVxKIZb.d.ts} +233 -137
  77. package/dist/workflow.cjs +28 -11
  78. package/dist/workflow.cjs.map +1 -1
  79. package/dist/workflow.d.cts +11 -11
  80. package/dist/workflow.d.ts +11 -11
  81. package/dist/workflow.js +29 -12
  82. package/dist/workflow.js.map +1 -1
  83. package/package.json +26 -1
  84. package/src/adapters/sandbox/bedrock/filesystem.ts +43 -10
  85. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +8 -3
  86. package/src/adapters/thread/anthropic/activities.ts +223 -0
  87. package/src/adapters/thread/anthropic/index.ts +44 -0
  88. package/src/adapters/thread/anthropic/model-invoker.ts +124 -0
  89. package/src/adapters/thread/anthropic/proxy.ts +33 -0
  90. package/src/adapters/thread/anthropic/thread-manager.ts +191 -0
  91. package/src/adapters/thread/google-genai/activities.ts +107 -32
  92. package/src/adapters/thread/google-genai/index.ts +3 -1
  93. package/src/adapters/thread/google-genai/model-invoker.ts +7 -40
  94. package/src/adapters/thread/google-genai/proxy.ts +6 -34
  95. package/src/adapters/thread/google-genai/thread-manager.ts +84 -104
  96. package/src/adapters/thread/langchain/activities.ts +53 -20
  97. package/src/adapters/thread/langchain/index.ts +3 -1
  98. package/src/adapters/thread/langchain/model-invoker.ts +7 -9
  99. package/src/adapters/thread/langchain/proxy.ts +6 -34
  100. package/src/adapters/thread/langchain/thread-manager.ts +44 -98
  101. package/src/index.ts +5 -1
  102. package/src/lib/activity.ts +4 -3
  103. package/src/lib/hooks/types.ts +12 -12
  104. package/src/lib/model/types.ts +2 -0
  105. package/src/lib/session/session-edge-cases.integration.test.ts +24 -6
  106. package/src/lib/session/session.ts +18 -14
  107. package/src/lib/session/types.ts +31 -14
  108. package/src/lib/subagent/handler.ts +20 -11
  109. package/src/lib/subagent/subagent.integration.test.ts +36 -4
  110. package/src/lib/subagent/types.ts +3 -2
  111. package/src/lib/thread/index.ts +2 -0
  112. package/src/lib/thread/manager.ts +4 -7
  113. package/src/lib/thread/proxy.ts +57 -0
  114. package/src/lib/thread/types.ts +31 -0
  115. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +54 -0
  116. package/src/lib/tool-router/auto-append.ts +5 -2
  117. package/src/lib/tool-router/router-edge-cases.integration.test.ts +9 -5
  118. package/src/lib/tool-router/router.ts +13 -7
  119. package/src/lib/tool-router/types.ts +20 -13
  120. package/src/lib/tool-router/with-sandbox.ts +4 -3
  121. package/src/lib/types.ts +7 -14
  122. package/src/workflow.ts +0 -4
  123. package/tsup.config.ts +5 -0
@@ -0,0 +1,191 @@
1
+ import type Redis from "ioredis";
2
+ import type Anthropic from "@anthropic-ai/sdk";
3
+ import type { JsonValue } from "../../../lib/state/types";
4
+ import {
5
+ createThreadManager,
6
+ type ProviderThreadManager,
7
+ type ThreadManagerConfig,
8
+ } from "../../../lib/thread";
9
+
10
+ /** SDK-native content type for Anthropic human messages */
11
+ export type AnthropicContent =
12
+ | string
13
+ | Anthropic.Messages.ContentBlockParam[];
14
+
15
+ /** A MessageParam with a unique ID for idempotent Redis storage */
16
+ export interface StoredMessage {
17
+ id: string;
18
+ message: Anthropic.Messages.MessageParam;
19
+ /** System messages are stored separately since Anthropic passes them via config */
20
+ isSystem?: boolean;
21
+ }
22
+
23
+ export interface AnthropicThreadManagerConfig {
24
+ redis: Redis;
25
+ threadId: string;
26
+ /** Thread key, defaults to 'messages' */
27
+ key?: string;
28
+ }
29
+
30
+ /** Prepared payload ready to send to the Anthropic API */
31
+ export interface AnthropicInvocationPayload {
32
+ messages: Anthropic.Messages.MessageParam[];
33
+ system?: string;
34
+ }
35
+
36
+ /** Thread manager with Anthropic MessageParam convenience helpers */
37
+ export interface AnthropicThreadManager
38
+ extends ProviderThreadManager<StoredMessage, AnthropicContent> {
39
+ appendAssistantMessage(
40
+ id: string,
41
+ content: Anthropic.Messages.ContentBlock[],
42
+ ): Promise<void>;
43
+ prepareForInvocation(): Promise<AnthropicInvocationPayload>;
44
+ }
45
+
46
+ function storedMessageId(msg: StoredMessage): string {
47
+ return msg.id;
48
+ }
49
+
50
+ /** Normalise content into an array of ContentBlockParam */
51
+ function toContentBlocks(
52
+ content: AnthropicContent,
53
+ ): Anthropic.Messages.ContentBlockParam[] {
54
+ if (typeof content === "string") {
55
+ return [{ type: "text", text: content }];
56
+ }
57
+ return content;
58
+ }
59
+
60
+ /**
61
+ * Merge consecutive messages with the same role.
62
+ * The Anthropic API requires alternating user/assistant turns; without
63
+ * merging, multiple sequential tool-result messages would violate this.
64
+ */
65
+ function mergeConsecutiveMessages(
66
+ messages: Anthropic.Messages.MessageParam[],
67
+ ): Anthropic.Messages.MessageParam[] {
68
+ const merged: Anthropic.Messages.MessageParam[] = [];
69
+ for (const msg of messages) {
70
+ const last = merged[merged.length - 1];
71
+ if (last && last.role === msg.role) {
72
+ const lastContent = Array.isArray(last.content)
73
+ ? last.content
74
+ : [{ type: "text" as const, text: last.content }];
75
+ const msgContent = Array.isArray(msg.content)
76
+ ? msg.content
77
+ : [{ type: "text" as const, text: msg.content }];
78
+ last.content = [...lastContent, ...msgContent];
79
+ } else {
80
+ merged.push({
81
+ ...msg,
82
+ content: Array.isArray(msg.content)
83
+ ? [...msg.content]
84
+ : msg.content,
85
+ });
86
+ }
87
+ }
88
+ return merged;
89
+ }
90
+
91
+ /**
92
+ * Creates an Anthropic-specific thread manager that stores StoredMessage
93
+ * instances in Redis and provides convenience helpers for creating and
94
+ * appending typed messages.
95
+ */
96
+ export function createAnthropicThreadManager(
97
+ config: AnthropicThreadManagerConfig,
98
+ ): AnthropicThreadManager {
99
+ const baseConfig: ThreadManagerConfig<StoredMessage> = {
100
+ redis: config.redis,
101
+ threadId: config.threadId,
102
+ key: config.key,
103
+ idOf: storedMessageId,
104
+ };
105
+
106
+ const base = createThreadManager(baseConfig);
107
+
108
+ const helpers: Omit<AnthropicThreadManager, keyof typeof base> = {
109
+ async appendUserMessage(
110
+ id: string,
111
+ content: AnthropicContent,
112
+ ): Promise<void> {
113
+ await base.append([{
114
+ id,
115
+ message: { role: "user", content: toContentBlocks(content) },
116
+ }]);
117
+ },
118
+
119
+ async appendSystemMessage(id: string, content: string): Promise<void> {
120
+ await base.initialize();
121
+ await base.append([{
122
+ id,
123
+ message: { role: "user", content },
124
+ isSystem: true,
125
+ }]);
126
+ },
127
+
128
+ async appendAssistantMessage(
129
+ id: string,
130
+ content: Anthropic.Messages.ContentBlock[],
131
+ ): Promise<void> {
132
+ await base.append([{
133
+ id,
134
+ message: {
135
+ role: "assistant",
136
+ content: content as unknown as Anthropic.Messages.ContentBlockParam[],
137
+ },
138
+ }]);
139
+ },
140
+
141
+ async appendToolResult(
142
+ id: string,
143
+ toolCallId: string,
144
+ _toolName: string,
145
+ content: JsonValue,
146
+ ): Promise<void> {
147
+ const toolContent =
148
+ typeof content === "string"
149
+ ? content
150
+ : Array.isArray(content)
151
+ ? (content as unknown as Anthropic.Messages.ToolResultBlockParam["content"])
152
+ : JSON.stringify(content);
153
+ await base.append([{
154
+ id,
155
+ message: {
156
+ role: "user",
157
+ content: [{
158
+ type: "tool_result" as const,
159
+ tool_use_id: toolCallId,
160
+ content: toolContent,
161
+ }],
162
+ },
163
+ }]);
164
+ },
165
+
166
+ async prepareForInvocation(): Promise<AnthropicInvocationPayload> {
167
+ const stored = await base.load();
168
+
169
+ let system: string | undefined;
170
+ const conversationMessages: Anthropic.Messages.MessageParam[] = [];
171
+
172
+ for (const item of stored) {
173
+ if (item.isSystem) {
174
+ system =
175
+ typeof item.message.content === "string"
176
+ ? item.message.content
177
+ : undefined;
178
+ } else {
179
+ conversationMessages.push(item.message);
180
+ }
181
+ }
182
+
183
+ return {
184
+ messages: mergeConsecutiveMessages(conversationMessages),
185
+ ...(system ? { system } : {}),
186
+ };
187
+ },
188
+ };
189
+
190
+ return Object.assign(base, helpers);
191
+ }
@@ -1,33 +1,67 @@
1
1
  import type Redis from "ioredis";
2
- import type { GoogleGenAI, Content } from "@google/genai";
2
+ import type { GoogleGenAI, Content, Part } from "@google/genai";
3
3
  import type { ToolResultConfig } from "../../../lib/types";
4
- import type { MessageContent } from "../../../lib/types";
4
+ import type {
5
+ ActivityToolHandler,
6
+ RouterContext,
7
+ ToolHandlerResponse,
8
+ } from "../../../lib/tool-router/types";
5
9
  import type {
6
10
  ThreadOps,
7
11
  PrefixedThreadOps,
8
12
  ScopedPrefix,
9
13
  } from "../../../lib/session/types";
10
14
  import type { ModelInvoker } from "../../../lib/model";
11
- import { createGoogleGenAIThreadManager } from "./thread-manager";
15
+ import {
16
+ createGoogleGenAIThreadManager,
17
+ type GoogleGenAIContent,
18
+ } from "./thread-manager";
12
19
  import { createGoogleGenAIModelInvoker } from "./model-invoker";
13
20
 
14
21
  const ADAPTER_PREFIX = "googleGenAI" as const;
15
22
 
16
23
  export type GoogleGenAIThreadOps<TScope extends string = ""> =
17
- PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>>;
24
+ PrefixedThreadOps<
25
+ ScopedPrefix<TScope, typeof ADAPTER_PREFIX>,
26
+ GoogleGenAIContent
27
+ >;
18
28
 
19
29
  export interface GoogleGenAIAdapterConfig {
20
30
  redis: Redis;
21
- client: GoogleGenAI;
31
+ client?: GoogleGenAI;
22
32
  /** Default model name (e.g. 'gemini-2.5-flash'). If omitted, use `createModelInvoker()` */
23
33
  model?: string;
24
34
  }
25
35
 
36
+ /**
37
+ * Tool response type accepted by the Google GenAI adapter.
38
+ *
39
+ * Handlers can return:
40
+ * - **`string`** — plain text, wrapped in a `functionResponse` part.
41
+ * - **`Record<string, unknown>`** — structured object used as `functionResponse.response`.
42
+ * - **`Part[]`** — pre-built parts used directly as `Content.parts`.
43
+ * The handler is responsible for building correct Part objects (e.g. `functionResponse`,
44
+ * `inlineData`, `text`). Use `context.toolCallId` and `context.toolName` to construct
45
+ * `functionResponse` parts.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * adapter.wrapHandler(async (args, ctx) => ({
50
+ * toolResponse: [
51
+ * { functionResponse: { id: ctx.toolCallId, name: ctx.toolName, response: { result: "done" } } },
52
+ * { inlineData: { data: base64, mimeType: "image/png" } },
53
+ * ],
54
+ * data: null,
55
+ * }));
56
+ * ```
57
+ */
58
+ export type GoogleGenAIToolResponse = string | Record<string, unknown> | Part[];
59
+
26
60
  export interface GoogleGenAIAdapter {
27
61
  /** Model invoker using the default model (only available when `model` was provided) */
28
62
  invoker: ModelInvoker<Content>;
29
63
  /** Create an invoker for a specific model name (for multi-model setups) */
30
- createModelInvoker(model: string): ModelInvoker<Content>;
64
+ createModelInvoker(model: string, client: GoogleGenAI): ModelInvoker<Content>;
31
65
  /**
32
66
  * Create prefixed thread activities for registration on the worker.
33
67
  *
@@ -43,9 +77,18 @@ export interface GoogleGenAIAdapter {
43
77
  * // → { googleGenAIResearchAgentInitializeThread, … }
44
78
  * ```
45
79
  */
46
- createActivities<S extends string = "">(
47
- scope?: S
48
- ): GoogleGenAIThreadOps<S>;
80
+ createActivities<S extends string = "">(scope?: S): GoogleGenAIThreadOps<S>;
81
+
82
+ /**
83
+ * Identity wrapper that types a tool handler for this adapter.
84
+ * Constrains `toolResponse` to {@link GoogleGenAIToolResponse}.
85
+ */
86
+ wrapHandler<TArgs, TResult, TContext extends RouterContext = RouterContext>(
87
+ handler: (
88
+ args: TArgs,
89
+ context: TContext
90
+ ) => Promise<ToolHandlerResponse<TResult, GoogleGenAIToolResponse>>
91
+ ): ActivityToolHandler<TArgs, TResult, TContext, GoogleGenAIToolResponse>;
49
92
  }
50
93
 
51
94
  /**
@@ -91,45 +134,73 @@ export interface GoogleGenAIAdapter {
91
134
  export function createGoogleGenAIAdapter(
92
135
  config: GoogleGenAIAdapterConfig
93
136
  ): GoogleGenAIAdapter {
94
- const { redis, client } = config;
137
+ const { redis } = config;
95
138
 
96
- const threadOps: ThreadOps = {
97
- async initializeThread(threadId: string): Promise<void> {
98
- const thread = createGoogleGenAIThreadManager({ redis, threadId });
139
+ const threadOps: ThreadOps<GoogleGenAIContent> = {
140
+ async initializeThread(
141
+ threadId: string,
142
+ threadKey?: string
143
+ ): Promise<void> {
144
+ const thread = createGoogleGenAIThreadManager({
145
+ redis,
146
+ threadId,
147
+ key: threadKey,
148
+ });
99
149
  await thread.initialize();
100
150
  },
101
151
 
102
152
  async appendHumanMessage(
103
153
  threadId: string,
104
154
  id: string,
105
- content: string | MessageContent
155
+ content: GoogleGenAIContent,
156
+ threadKey?: string
106
157
  ): Promise<void> {
107
- const thread = createGoogleGenAIThreadManager({ redis, threadId });
158
+ const thread = createGoogleGenAIThreadManager({
159
+ redis,
160
+ threadId,
161
+ key: threadKey,
162
+ });
108
163
  await thread.appendUserMessage(id, content);
109
164
  },
110
165
 
111
166
  async appendSystemMessage(
112
167
  threadId: string,
113
168
  id: string,
114
- content: string
169
+ content: string,
170
+ threadKey?: string
115
171
  ): Promise<void> {
116
- const thread = createGoogleGenAIThreadManager({ redis, threadId });
172
+ const thread = createGoogleGenAIThreadManager({
173
+ redis,
174
+ threadId,
175
+ key: threadKey,
176
+ });
117
177
  await thread.appendSystemMessage(id, content);
118
178
  },
119
179
 
120
180
  async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
121
- const { threadId, toolCallId, toolName, content } = cfg;
122
- const thread = createGoogleGenAIThreadManager({ redis, threadId });
123
- await thread.appendToolResult(id, toolCallId, toolName, content);
181
+ const { threadId, threadKey, toolCallId, toolName, content } = cfg;
182
+ const thread = createGoogleGenAIThreadManager({
183
+ redis,
184
+ threadId,
185
+ key: threadKey,
186
+ });
187
+ await thread.appendToolResult(
188
+ id,
189
+ toolCallId,
190
+ toolName,
191
+ content as GoogleGenAIToolResponse
192
+ );
124
193
  },
125
194
 
126
195
  async forkThread(
127
196
  sourceThreadId: string,
128
- targetThreadId: string
197
+ targetThreadId: string,
198
+ threadKey?: string
129
199
  ): Promise<void> {
130
200
  const thread = createGoogleGenAIThreadManager({
131
201
  redis,
132
202
  threadId: sourceThreadId,
203
+ key: threadKey,
133
204
  });
134
205
  await thread.fork(targetThreadId);
135
206
  },
@@ -141,28 +212,32 @@ export function createGoogleGenAIAdapter(
141
212
  const prefix = scope
142
213
  ? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
143
214
  : ADAPTER_PREFIX;
144
- const cap = (s: string): string =>
145
- s.charAt(0).toUpperCase() + s.slice(1);
215
+ const cap = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);
146
216
  return Object.fromEntries(
147
217
  Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
148
218
  ) as GoogleGenAIThreadOps<S>;
149
219
  }
150
220
 
151
- const makeInvoker = (model: string): ModelInvoker<Content> =>
221
+ const makeInvoker = (
222
+ model: string,
223
+ client: GoogleGenAI
224
+ ): ModelInvoker<Content> =>
152
225
  createGoogleGenAIModelInvoker({ redis, client, model });
153
226
 
154
- const invoker: ModelInvoker<Content> = config.model
155
- ? makeInvoker(config.model)
156
- : ((() => {
157
- throw new Error(
158
- "No default model provided to createGoogleGenAIAdapter. " +
159
- "Either pass `model` in the config or use `createModelInvoker(model)` instead."
160
- );
161
- }) as unknown as ModelInvoker<Content>);
227
+ const invoker: ModelInvoker<Content> =
228
+ config.model && config.client
229
+ ? makeInvoker(config.model, config.client)
230
+ : ((() => {
231
+ throw new Error(
232
+ "No default model provided to createGoogleGenAIAdapter. " +
233
+ "Either pass `model` in the config or use `createModelInvoker(model)` instead."
234
+ );
235
+ }) as unknown as ModelInvoker<Content>);
162
236
 
163
237
  return {
164
238
  createActivities,
165
239
  invoker,
166
240
  createModelInvoker: makeInvoker,
241
+ wrapHandler: (handler) => handler,
167
242
  };
168
243
  }
@@ -23,14 +23,16 @@ export {
23
23
  type GoogleGenAIAdapter,
24
24
  type GoogleGenAIAdapterConfig,
25
25
  type GoogleGenAIThreadOps,
26
+ type GoogleGenAIToolResponse,
26
27
  } from "./activities";
27
28
 
28
29
  // Thread manager
29
30
  export {
30
31
  createGoogleGenAIThreadManager,
31
- messageContentToParts,
32
32
  type GoogleGenAIThreadManager,
33
33
  type GoogleGenAIThreadManagerConfig,
34
+ type GoogleGenAIContent,
35
+ type GoogleGenAIInvocationPayload,
34
36
  type StoredContent,
35
37
  } from "./thread-manager";
36
38
 
@@ -1,8 +1,7 @@
1
1
  import type Redis from "ioredis";
2
2
  import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
3
3
  import type { SerializableToolDefinition } from "../../../lib/types";
4
- import type { AgentResponse } from "../../../lib/model";
5
- import type { ModelInvokerConfig } from "../../../lib/model";
4
+ import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
6
5
  import { createGoogleGenAIThreadManager } from "./thread-manager";
7
6
  import { v4 as uuidv4 } from "uuid";
8
7
 
@@ -13,7 +12,7 @@ export interface GoogleGenAIModelInvokerConfig {
13
12
  }
14
13
 
15
14
  function toFunctionDeclarations(
16
- tools: SerializableToolDefinition[]
15
+ tools: SerializableToolDefinition[],
17
16
  ): FunctionDeclaration[] {
18
17
  return tools.map((t) => ({
19
18
  name: t.name,
@@ -22,24 +21,6 @@ function toFunctionDeclarations(
22
21
  }));
23
22
  }
24
23
 
25
- /**
26
- * Merge consecutive Content objects sharing the same role.
27
- * The Gemini API requires alternating user/model turns; without
28
- * merging, multiple sequential tool-result messages would violate this.
29
- */
30
- function mergeConsecutiveContents(contents: Content[]): Content[] {
31
- const merged: Content[] = [];
32
- for (const content of contents) {
33
- const last = merged[merged.length - 1];
34
- if (last && last.role === content.role) {
35
- last.parts = [...(last.parts ?? []), ...(content.parts ?? [])];
36
- } else {
37
- merged.push({ ...content, parts: [...(content.parts ?? [])] });
38
- }
39
- }
40
- return merged;
41
- }
42
-
43
24
  /**
44
25
  * Creates a Google GenAI model invoker that satisfies the generic
45
26
  * `ModelInvoker<Content>` contract.
@@ -70,27 +51,13 @@ export function createGoogleGenAIModelInvoker({
70
51
  model,
71
52
  }: GoogleGenAIModelInvokerConfig) {
72
53
  return async function invokeGoogleGenAIModel(
73
- config: ModelInvokerConfig
54
+ config: ModelInvokerConfig,
74
55
  ): Promise<AgentResponse<Content>> {
75
- const { threadId, state } = config;
76
-
77
- const thread = createGoogleGenAIThreadManager({ redis, threadId });
78
- const stored = await thread.load();
79
-
80
- // Separate system instructions from conversation content.
81
- // Google GenAI takes system instructions via config, not in the contents array.
82
- let systemInstruction: string | undefined;
83
- const conversationContents: Content[] = [];
84
-
85
- for (const item of stored) {
86
- if (item.content.role === "system") {
87
- systemInstruction = item.content.parts?.[0]?.text;
88
- } else {
89
- conversationContents.push(item.content);
90
- }
91
- }
56
+ const { threadId, threadKey, state } = config;
92
57
 
93
- const contents = mergeConsecutiveContents(conversationContents);
58
+ const thread = createGoogleGenAIThreadManager({ redis, threadId, key: threadKey });
59
+ const { contents, systemInstruction } =
60
+ await thread.prepareForInvocation();
94
61
 
95
62
  const functionDeclarations = toFunctionDeclarations(state.tools);
96
63
  const tools =
@@ -18,44 +18,16 @@
18
18
  * const threadOps = proxyGoogleGenAIThreadOps("customScope");
19
19
  * ```
20
20
  */
21
- import {
22
- proxyActivities,
23
- workflowInfo,
24
- type ActivityInterfaceFor,
25
- } from "@temporalio/workflow";
21
+ import { type ActivityInterfaceFor } from "@temporalio/workflow";
26
22
  import type { ThreadOps } from "../../../lib/session/types";
23
+ import type { GoogleGenAIContent } from "./thread-manager";
24
+ import { createThreadOpsProxy } from "../../../lib/thread/proxy";
27
25
 
28
26
  const ADAPTER_PREFIX = "googleGenAI";
29
27
 
30
28
  export function proxyGoogleGenAIThreadOps(
31
29
  scope?: string,
32
- options?: Parameters<typeof proxyActivities>[0]
33
- ): ActivityInterfaceFor<ThreadOps> {
34
- const resolvedScope = scope ?? workflowInfo().workflowType;
35
-
36
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
- const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
38
- options ?? {
39
- startToCloseTimeout: "10s",
40
- retry: {
41
- maximumAttempts: 6,
42
- initialInterval: "5s",
43
- maximumInterval: "15m",
44
- backoffCoefficient: 4,
45
- },
46
- }
47
- );
48
-
49
- const prefix =
50
- `${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
51
- const p = (key: string): string =>
52
- `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
53
-
54
- return {
55
- initializeThread: acts[p("initializeThread")],
56
- appendHumanMessage: acts[p("appendHumanMessage")],
57
- appendToolResult: acts[p("appendToolResult")],
58
- appendSystemMessage: acts[p("appendSystemMessage")],
59
- forkThread: acts[p("forkThread")],
60
- } as ActivityInterfaceFor<ThreadOps>;
30
+ options?: Parameters<typeof createThreadOpsProxy>[2],
31
+ ): ActivityInterfaceFor<ThreadOps<GoogleGenAIContent>> {
32
+ return createThreadOpsProxy(ADAPTER_PREFIX, scope, options) as ActivityInterfaceFor<ThreadOps<GoogleGenAIContent>>;
61
33
  }