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.
- package/dist/activities-BEJRyDVU.d.cts +137 -0
- package/dist/activities-LVQdLF6I.d.ts +137 -0
- package/dist/adapters/sandbox/bedrock/index.cjs +35 -10
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +2 -2
- package/dist/adapters/sandbox/bedrock/index.d.ts +2 -2
- package/dist/adapters/sandbox/bedrock/index.js +35 -10
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +8 -7
- package/dist/adapters/sandbox/virtual/index.d.ts +8 -7
- package/dist/adapters/sandbox/virtual/index.js.map +1 -1
- package/dist/adapters/sandbox/virtual/workflow.d.cts +3 -2
- package/dist/adapters/sandbox/virtual/workflow.d.ts +3 -2
- package/dist/adapters/thread/anthropic/index.cjs +356 -0
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -0
- package/dist/adapters/thread/anthropic/index.d.cts +148 -0
- package/dist/adapters/thread/anthropic/index.d.ts +148 -0
- package/dist/adapters/thread/anthropic/index.js +351 -0
- package/dist/adapters/thread/anthropic/index.js.map +1 -0
- package/dist/adapters/thread/anthropic/workflow.cjs +38 -0
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -0
- package/dist/adapters/thread/anthropic/workflow.d.cts +37 -0
- package/dist/adapters/thread/anthropic/workflow.d.ts +37 -0
- package/dist/adapters/thread/anthropic/workflow.js +36 -0
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +95 -97
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +9 -111
- package/dist/adapters/thread/google-genai/index.d.ts +9 -111
- package/dist/adapters/thread/google-genai/index.js +96 -97
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +9 -4
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +10 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +10 -5
- package/dist/adapters/thread/google-genai/workflow.js +9 -4
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +43 -60
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +24 -38
- package/dist/adapters/thread/langchain/index.d.ts +24 -38
- package/dist/adapters/thread/langchain/index.js +43 -60
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +9 -4
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +10 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +10 -5
- package/dist/adapters/thread/langchain/workflow.js +9 -4
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +30 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -12
- package/dist/index.d.ts +13 -12
- package/dist/index.js +31 -13
- package/dist/index.js.map +1 -1
- package/dist/proxy-BK1ydQt0.d.ts +24 -0
- package/dist/proxy-BMAsMHdp.d.cts +24 -0
- package/dist/{queries-DwBe2CAA.d.ts → queries-BCgJ9Sr5.d.ts} +1 -1
- package/dist/{queries-BYGBImeC.d.cts → queries-DwnE2bu3.d.cts} +1 -1
- package/dist/thread-manager-CH9krS3h.d.ts +37 -0
- package/dist/thread-manager-Czhpxbt6.d.ts +29 -0
- package/dist/thread-manager-DOnQzImf.d.cts +29 -0
- package/dist/thread-manager-b4DML-qu.d.cts +37 -0
- package/dist/{types-35POpVfa.d.ts → types-BDRDbm3h.d.cts} +22 -1
- package/dist/{types-d9NznUqd.d.ts → types-BdCdR41N.d.ts} +10 -0
- package/dist/{types-hmferhc2.d.ts → types-CvJyXDYt.d.ts} +44 -123
- package/dist/{types-LVKmCNds.d.ts → types-DFUNSYbj.d.ts} +1 -1
- package/dist/{types-Bf8KV0Ci.d.cts → types-DRnz-OZp.d.cts} +1 -1
- package/dist/{types-7PeMi1bD.d.cts → types-DSOefLpY.d.cts} +44 -123
- package/dist/{types-35POpVfa.d.cts → types-WNSeZbWa.d.ts} +22 -1
- package/dist/{types-DhTCEMhr.d.cts → types-ZHs2v9Ap.d.cts} +10 -0
- package/dist/{types-D_igp10o.d.cts → types-mCVxKIZb.d.cts} +233 -137
- package/dist/{types-D_igp10o.d.ts → types-mCVxKIZb.d.ts} +233 -137
- package/dist/workflow.cjs +28 -11
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +11 -11
- package/dist/workflow.d.ts +11 -11
- package/dist/workflow.js +29 -12
- package/dist/workflow.js.map +1 -1
- package/package.json +26 -1
- package/src/adapters/sandbox/bedrock/filesystem.ts +43 -10
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +8 -3
- package/src/adapters/thread/anthropic/activities.ts +223 -0
- package/src/adapters/thread/anthropic/index.ts +44 -0
- package/src/adapters/thread/anthropic/model-invoker.ts +124 -0
- package/src/adapters/thread/anthropic/proxy.ts +33 -0
- package/src/adapters/thread/anthropic/thread-manager.ts +191 -0
- package/src/adapters/thread/google-genai/activities.ts +107 -32
- package/src/adapters/thread/google-genai/index.ts +3 -1
- package/src/adapters/thread/google-genai/model-invoker.ts +7 -40
- package/src/adapters/thread/google-genai/proxy.ts +6 -34
- package/src/adapters/thread/google-genai/thread-manager.ts +84 -104
- package/src/adapters/thread/langchain/activities.ts +53 -20
- package/src/adapters/thread/langchain/index.ts +3 -1
- package/src/adapters/thread/langchain/model-invoker.ts +7 -9
- package/src/adapters/thread/langchain/proxy.ts +6 -34
- package/src/adapters/thread/langchain/thread-manager.ts +44 -98
- package/src/index.ts +5 -1
- package/src/lib/activity.ts +4 -3
- package/src/lib/hooks/types.ts +12 -12
- package/src/lib/model/types.ts +2 -0
- package/src/lib/session/session-edge-cases.integration.test.ts +24 -6
- package/src/lib/session/session.ts +18 -14
- package/src/lib/session/types.ts +31 -14
- package/src/lib/subagent/handler.ts +20 -11
- package/src/lib/subagent/subagent.integration.test.ts +36 -4
- package/src/lib/subagent/types.ts +3 -2
- package/src/lib/thread/index.ts +2 -0
- package/src/lib/thread/manager.ts +4 -7
- package/src/lib/thread/proxy.ts +57 -0
- package/src/lib/thread/types.ts +31 -0
- package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +54 -0
- package/src/lib/tool-router/auto-append.ts +5 -2
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +9 -5
- package/src/lib/tool-router/router.ts +13 -7
- package/src/lib/tool-router/types.ts +20 -13
- package/src/lib/tool-router/with-sandbox.ts +4 -3
- package/src/lib/types.ts +7 -14
- package/src/workflow.ts +0 -4
- package/tsup.config.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zeitlich",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -67,6 +67,26 @@
|
|
|
67
67
|
"default": "./dist/adapters/thread/google-genai/workflow.js"
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
+
"./adapters/thread/anthropic": {
|
|
71
|
+
"import": {
|
|
72
|
+
"types": "./dist/adapters/thread/anthropic/index.d.ts",
|
|
73
|
+
"default": "./dist/adapters/thread/anthropic/index.js"
|
|
74
|
+
},
|
|
75
|
+
"require": {
|
|
76
|
+
"types": "./dist/adapters/thread/anthropic/index.d.ts",
|
|
77
|
+
"default": "./dist/adapters/thread/anthropic/index.js"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"./adapters/thread/anthropic/workflow": {
|
|
81
|
+
"import": {
|
|
82
|
+
"types": "./dist/adapters/thread/anthropic/workflow.d.ts",
|
|
83
|
+
"default": "./dist/adapters/thread/anthropic/workflow.js"
|
|
84
|
+
},
|
|
85
|
+
"require": {
|
|
86
|
+
"types": "./dist/adapters/thread/anthropic/workflow.d.ts",
|
|
87
|
+
"default": "./dist/adapters/thread/anthropic/workflow.js"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
70
90
|
"./adapters/sandbox/inmemory": {
|
|
71
91
|
"import": {
|
|
72
92
|
"types": "./dist/adapters/sandbox/inmemory/index.d.ts",
|
|
@@ -190,6 +210,7 @@
|
|
|
190
210
|
"node": ">=18"
|
|
191
211
|
},
|
|
192
212
|
"devDependencies": {
|
|
213
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
193
214
|
"@aws-sdk/client-bedrock-agentcore": "^3.900.0",
|
|
194
215
|
"@daytonaio/sdk": "^0.149.0",
|
|
195
216
|
"@e2b/code-interpreter": "^2.3.3",
|
|
@@ -210,6 +231,7 @@
|
|
|
210
231
|
"vitest": "^4.0.18"
|
|
211
232
|
},
|
|
212
233
|
"peerDependencies": {
|
|
234
|
+
"@anthropic-ai/sdk": ">=0.50.0",
|
|
213
235
|
"@aws-sdk/client-bedrock-agentcore": "^3.900.0",
|
|
214
236
|
"@daytonaio/sdk": ">=0.153.0",
|
|
215
237
|
"@e2b/code-interpreter": "^2.3.3",
|
|
@@ -225,6 +247,9 @@
|
|
|
225
247
|
"@daytonaio/sdk": {
|
|
226
248
|
"optional": true
|
|
227
249
|
},
|
|
250
|
+
"@anthropic-ai/sdk": {
|
|
251
|
+
"optional": true
|
|
252
|
+
},
|
|
228
253
|
"@google/genai": {
|
|
229
254
|
"optional": true
|
|
230
255
|
},
|
|
@@ -67,8 +67,39 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
67
67
|
this.workspaceBase = posix.resolve("/", workspaceBase);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a caller-supplied path to an absolute path within the workspace.
|
|
72
|
+
* Used for shell commands that need full paths.
|
|
73
|
+
*/
|
|
70
74
|
private normalisePath(path: string): string {
|
|
71
|
-
|
|
75
|
+
if (
|
|
76
|
+
posix.isAbsolute(path) &&
|
|
77
|
+
!path.startsWith(this.workspaceBase + "/") &&
|
|
78
|
+
path !== this.workspaceBase
|
|
79
|
+
) {
|
|
80
|
+
path = path.replace(/^\/+/, "");
|
|
81
|
+
}
|
|
82
|
+
const resolved = posix.resolve(this.workspaceBase, path);
|
|
83
|
+
if (
|
|
84
|
+
!resolved.startsWith(this.workspaceBase + "/") &&
|
|
85
|
+
resolved !== this.workspaceBase
|
|
86
|
+
) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid file path: "${resolved}" escapes workspace "${this.workspaceBase}"`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return resolved;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Return a workspace-relative path for Bedrock tool invocations
|
|
96
|
+
* (`writeFiles`, `readFiles`, `listFiles`, `removeFiles`), which
|
|
97
|
+
* reject absolute paths as "path traversal".
|
|
98
|
+
*/
|
|
99
|
+
private toToolPath(path: string): string {
|
|
100
|
+
const abs = this.normalisePath(path);
|
|
101
|
+
const prefix = this.workspaceBase + "/";
|
|
102
|
+
return abs.startsWith(prefix) ? abs.slice(prefix.length) : abs;
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
private async invoke(
|
|
@@ -103,9 +134,9 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
103
134
|
}
|
|
104
135
|
|
|
105
136
|
async readFile(path: string): Promise<string> {
|
|
106
|
-
const
|
|
137
|
+
const rel = this.toToolPath(path);
|
|
107
138
|
const result = await this.invoke("readFiles" as ToolNameType, {
|
|
108
|
-
paths: [
|
|
139
|
+
paths: [rel],
|
|
109
140
|
});
|
|
110
141
|
|
|
111
142
|
for (const block of result.content ?? []) {
|
|
@@ -116,9 +147,9 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
116
147
|
}
|
|
117
148
|
|
|
118
149
|
async readFileBuffer(path: string): Promise<Uint8Array> {
|
|
119
|
-
const
|
|
150
|
+
const rel = this.toToolPath(path);
|
|
120
151
|
const result = await this.invoke("readFiles" as ToolNameType, {
|
|
121
|
-
paths: [
|
|
152
|
+
paths: [rel],
|
|
122
153
|
});
|
|
123
154
|
|
|
124
155
|
for (const block of result.content ?? []) {
|
|
@@ -132,12 +163,12 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
132
163
|
}
|
|
133
164
|
|
|
134
165
|
async writeFile(path: string, content: string | Uint8Array): Promise<void> {
|
|
135
|
-
const
|
|
166
|
+
const rel = this.toToolPath(path);
|
|
136
167
|
const isText = typeof content === "string";
|
|
137
168
|
const result = await this.invoke("writeFiles" as ToolNameType, {
|
|
138
169
|
content: [
|
|
139
170
|
{
|
|
140
|
-
path:
|
|
171
|
+
path: rel,
|
|
141
172
|
...(isText
|
|
142
173
|
? { text: content as string }
|
|
143
174
|
: { blob: content as Uint8Array }),
|
|
@@ -203,9 +234,9 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
203
234
|
}
|
|
204
235
|
|
|
205
236
|
async readdir(path: string): Promise<string[]> {
|
|
206
|
-
const
|
|
237
|
+
const rel = this.toToolPath(path);
|
|
207
238
|
const result = await this.invoke("listFiles" as ToolNameType, {
|
|
208
|
-
directoryPath:
|
|
239
|
+
directoryPath: rel,
|
|
209
240
|
});
|
|
210
241
|
|
|
211
242
|
const names: string[] = [];
|
|
@@ -215,6 +246,7 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
215
246
|
|
|
216
247
|
if (names.length > 0) return names;
|
|
217
248
|
|
|
249
|
+
const norm = this.normalisePath(path);
|
|
218
250
|
const { stdout, exitCode, stderr } = await this.execShell(
|
|
219
251
|
`ls -1A "${norm}"`
|
|
220
252
|
);
|
|
@@ -264,8 +296,9 @@ export class BedrockSandboxFileSystem implements SandboxFileSystem {
|
|
|
264
296
|
return;
|
|
265
297
|
}
|
|
266
298
|
|
|
299
|
+
const rel = this.toToolPath(path);
|
|
267
300
|
const result = await this.invoke("removeFiles" as ToolNameType, {
|
|
268
|
-
paths: [
|
|
301
|
+
paths: [rel],
|
|
269
302
|
});
|
|
270
303
|
if (result.isError) {
|
|
271
304
|
const msg =
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WorkflowClient } from "@temporalio/client";
|
|
2
2
|
import { queryParentWorkflowState } from "../../../lib/activity";
|
|
3
|
-
import type {
|
|
3
|
+
import type { JsonValue } from "../../../lib/state/types";
|
|
4
|
+
import type { ActivityToolHandler, RouterContext } from "../../../lib/tool-router/types";
|
|
4
5
|
import type {
|
|
5
6
|
FileEntryMetadata,
|
|
6
7
|
TreeMutation,
|
|
@@ -47,17 +48,21 @@ export function withVirtualSandbox<
|
|
|
47
48
|
TResult,
|
|
48
49
|
TCtx,
|
|
49
50
|
TMeta = FileEntryMetadata,
|
|
51
|
+
TToolResponse = JsonValue,
|
|
50
52
|
>(
|
|
51
53
|
client: WorkflowClient,
|
|
52
54
|
provider: VirtualSandboxProvider<TCtx, TMeta>,
|
|
53
55
|
handler: ActivityToolHandler<
|
|
54
56
|
TArgs,
|
|
55
57
|
TResult,
|
|
56
|
-
VirtualSandboxContext<TCtx, TMeta
|
|
58
|
+
VirtualSandboxContext<TCtx, TMeta>,
|
|
59
|
+
TToolResponse
|
|
57
60
|
>
|
|
58
61
|
): ActivityToolHandler<
|
|
59
62
|
TArgs,
|
|
60
|
-
(TResult & { treeMutations: TreeMutation<TMeta>[] }) | null
|
|
63
|
+
(TResult & { treeMutations: TreeMutation<TMeta>[] }) | null,
|
|
64
|
+
RouterContext,
|
|
65
|
+
TToolResponse | string
|
|
61
66
|
> {
|
|
62
67
|
return async (args, context) => {
|
|
63
68
|
const state =
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type Redis from "ioredis";
|
|
2
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
|
+
import type { ToolResultConfig } from "../../../lib/types";
|
|
4
|
+
import type {
|
|
5
|
+
ActivityToolHandler,
|
|
6
|
+
RouterContext,
|
|
7
|
+
ToolHandlerResponse,
|
|
8
|
+
} from "../../../lib/tool-router/types";
|
|
9
|
+
import type {
|
|
10
|
+
ThreadOps,
|
|
11
|
+
PrefixedThreadOps,
|
|
12
|
+
ScopedPrefix,
|
|
13
|
+
} from "../../../lib/session/types";
|
|
14
|
+
import type { ModelInvoker } from "../../../lib/model";
|
|
15
|
+
import {
|
|
16
|
+
createAnthropicThreadManager,
|
|
17
|
+
type AnthropicContent,
|
|
18
|
+
} from "./thread-manager";
|
|
19
|
+
import {
|
|
20
|
+
createAnthropicModelInvoker,
|
|
21
|
+
type AnthropicModelInvokerConfig,
|
|
22
|
+
} from "./model-invoker";
|
|
23
|
+
|
|
24
|
+
const ADAPTER_PREFIX = "anthropic" as const;
|
|
25
|
+
|
|
26
|
+
export type AnthropicThreadOps<TScope extends string = ""> =
|
|
27
|
+
PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>, AnthropicContent>;
|
|
28
|
+
|
|
29
|
+
export interface AnthropicAdapterConfig {
|
|
30
|
+
redis: Redis;
|
|
31
|
+
client: Anthropic;
|
|
32
|
+
/** Default model name (e.g. 'claude-sonnet-4-20250514'). If omitted, use `createModelInvoker()` */
|
|
33
|
+
model?: string;
|
|
34
|
+
/** Maximum tokens to generate. Defaults to 16384. */
|
|
35
|
+
maxTokens?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Tool response type accepted by the Anthropic adapter.
|
|
40
|
+
*
|
|
41
|
+
* Handlers can return:
|
|
42
|
+
* - **`string`** — plain text content for the tool result.
|
|
43
|
+
* - **`Anthropic.Messages.ToolResultBlockParam["content"]`** — array of content blocks
|
|
44
|
+
* (e.g. `{ type: "text", text: "..." }`, `{ type: "image", source: { ... } }`).
|
|
45
|
+
* Passed through as-is to the `tool_result` block.
|
|
46
|
+
*/
|
|
47
|
+
export type AnthropicToolResponse = Anthropic.Messages.ToolResultBlockParam["content"];
|
|
48
|
+
|
|
49
|
+
export interface AnthropicAdapter {
|
|
50
|
+
/** Model invoker using the default model (only available when `model` was provided) */
|
|
51
|
+
invoker: ModelInvoker<Anthropic.Messages.Message>;
|
|
52
|
+
/** Create an invoker for a specific model name (for multi-model setups) */
|
|
53
|
+
createModelInvoker(
|
|
54
|
+
model: string,
|
|
55
|
+
maxTokens?: number,
|
|
56
|
+
): ModelInvoker<Anthropic.Messages.Message>;
|
|
57
|
+
/**
|
|
58
|
+
* Create prefixed thread activities for registration on the worker.
|
|
59
|
+
*
|
|
60
|
+
* @param scope - Workflow name appended to the adapter prefix.
|
|
61
|
+
* Use different scopes for the main agent vs subagents to avoid collisions.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* adapter.createActivities("codingAgent")
|
|
66
|
+
* // → { anthropicCodingAgentInitializeThread, anthropicCodingAgentAppendHumanMessage, … }
|
|
67
|
+
*
|
|
68
|
+
* adapter.createActivities("researchAgent")
|
|
69
|
+
* // → { anthropicResearchAgentInitializeThread, … }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
createActivities<S extends string = "">(
|
|
73
|
+
scope?: S,
|
|
74
|
+
): AnthropicThreadOps<S>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Identity wrapper that types a tool handler for this adapter.
|
|
78
|
+
* Constrains `toolResponse` to {@link AnthropicToolResponse}.
|
|
79
|
+
*/
|
|
80
|
+
wrapHandler<TArgs, TResult, TContext extends RouterContext = RouterContext>(
|
|
81
|
+
handler: (
|
|
82
|
+
args: TArgs,
|
|
83
|
+
context: TContext,
|
|
84
|
+
) => Promise<ToolHandlerResponse<TResult, AnthropicToolResponse>>,
|
|
85
|
+
): ActivityToolHandler<TArgs, TResult, TContext, AnthropicToolResponse>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates an Anthropic adapter that bundles thread operations and model
|
|
90
|
+
* invocation using the `@anthropic-ai/sdk`.
|
|
91
|
+
*
|
|
92
|
+
* Use `createActivities(scope)` to register scoped thread operations as
|
|
93
|
+
* Temporal activities on the worker. The `invoker` (or invokers created via
|
|
94
|
+
* `createModelInvoker`) should be wrapped with `createRunAgentActivity`.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { createAnthropicAdapter } from 'zeitlich/adapters/thread/anthropic';
|
|
99
|
+
* import { createRunAgentActivity } from 'zeitlich';
|
|
100
|
+
* import Anthropic from '@anthropic-ai/sdk';
|
|
101
|
+
*
|
|
102
|
+
* const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
103
|
+
* const adapter = createAnthropicAdapter({ redis, client, model: 'claude-sonnet-4-20250514' });
|
|
104
|
+
*
|
|
105
|
+
* export function createActivities(temporalClient: WorkflowClient) {
|
|
106
|
+
* return {
|
|
107
|
+
* ...adapter.createActivities("codingAgent"),
|
|
108
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
109
|
+
* };
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @example Multi-agent worker (main + subagent share the adapter)
|
|
114
|
+
* ```typescript
|
|
115
|
+
* export function createActivities(temporalClient: WorkflowClient) {
|
|
116
|
+
* return {
|
|
117
|
+
* ...adapter.createActivities("codingAgent"),
|
|
118
|
+
* ...adapter.createActivities("researchAgent"),
|
|
119
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
120
|
+
* runResearchAgent: createRunAgentActivity(
|
|
121
|
+
* temporalClient,
|
|
122
|
+
* adapter.createModelInvoker('claude-sonnet-4-20250514'),
|
|
123
|
+
* ),
|
|
124
|
+
* };
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function createAnthropicAdapter(
|
|
129
|
+
config: AnthropicAdapterConfig,
|
|
130
|
+
): AnthropicAdapter {
|
|
131
|
+
const { redis, client } = config;
|
|
132
|
+
|
|
133
|
+
const threadOps: ThreadOps<AnthropicContent> = {
|
|
134
|
+
async initializeThread(threadId: string, threadKey?: string): Promise<void> {
|
|
135
|
+
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
136
|
+
await thread.initialize();
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async appendHumanMessage(
|
|
140
|
+
threadId: string,
|
|
141
|
+
id: string,
|
|
142
|
+
content: AnthropicContent,
|
|
143
|
+
threadKey?: string,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
146
|
+
await thread.appendUserMessage(id, content);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
async appendSystemMessage(
|
|
150
|
+
threadId: string,
|
|
151
|
+
id: string,
|
|
152
|
+
content: string,
|
|
153
|
+
threadKey?: string,
|
|
154
|
+
): Promise<void> {
|
|
155
|
+
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
156
|
+
await thread.appendSystemMessage(id, content);
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
|
|
160
|
+
const { threadId, threadKey, toolCallId, toolName, content } = cfg;
|
|
161
|
+
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
162
|
+
await thread.appendToolResult(id, toolCallId, toolName, content);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async forkThread(
|
|
166
|
+
sourceThreadId: string,
|
|
167
|
+
targetThreadId: string,
|
|
168
|
+
threadKey?: string,
|
|
169
|
+
): Promise<void> {
|
|
170
|
+
const thread = createAnthropicThreadManager({
|
|
171
|
+
redis,
|
|
172
|
+
threadId: sourceThreadId,
|
|
173
|
+
key: threadKey,
|
|
174
|
+
});
|
|
175
|
+
await thread.fork(targetThreadId);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
function createActivities<S extends string = "">(
|
|
180
|
+
scope?: S,
|
|
181
|
+
): AnthropicThreadOps<S> {
|
|
182
|
+
const prefix = scope
|
|
183
|
+
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
184
|
+
: ADAPTER_PREFIX;
|
|
185
|
+
const cap = (s: string): string =>
|
|
186
|
+
s.charAt(0).toUpperCase() + s.slice(1);
|
|
187
|
+
return Object.fromEntries(
|
|
188
|
+
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v]),
|
|
189
|
+
) as AnthropicThreadOps<S>;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const makeInvoker = (
|
|
193
|
+
model: string,
|
|
194
|
+
maxTokens?: number,
|
|
195
|
+
): ModelInvoker<Anthropic.Messages.Message> => {
|
|
196
|
+
const invokerConfig: AnthropicModelInvokerConfig = {
|
|
197
|
+
redis,
|
|
198
|
+
client,
|
|
199
|
+
model,
|
|
200
|
+
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
201
|
+
...(config.maxTokens !== undefined && maxTokens === undefined
|
|
202
|
+
? { maxTokens: config.maxTokens }
|
|
203
|
+
: {}),
|
|
204
|
+
};
|
|
205
|
+
return createAnthropicModelInvoker(invokerConfig);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const invoker: ModelInvoker<Anthropic.Messages.Message> = config.model
|
|
209
|
+
? makeInvoker(config.model)
|
|
210
|
+
: ((() => {
|
|
211
|
+
throw new Error(
|
|
212
|
+
"No default model provided to createAnthropicAdapter. " +
|
|
213
|
+
"Either pass `model` in the config or use `createModelInvoker(model)` instead.",
|
|
214
|
+
);
|
|
215
|
+
}) as unknown as ModelInvoker<Anthropic.Messages.Message>);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
createActivities,
|
|
219
|
+
invoker,
|
|
220
|
+
createModelInvoker: makeInvoker,
|
|
221
|
+
wrapHandler: (handler) => handler,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic adapter for Zeitlich.
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified adapter that bundles thread management and model
|
|
5
|
+
* invocation using the `@anthropic-ai/sdk`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import {
|
|
10
|
+
* createAnthropicAdapter,
|
|
11
|
+
* createAnthropicThreadManager,
|
|
12
|
+
* } from 'zeitlich/adapters/thread/anthropic';
|
|
13
|
+
* import Anthropic from '@anthropic-ai/sdk';
|
|
14
|
+
*
|
|
15
|
+
* const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
16
|
+
* const adapter = createAnthropicAdapter({ redis, client, model: 'claude-sonnet-4-20250514' });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Adapter (primary API)
|
|
21
|
+
export {
|
|
22
|
+
createAnthropicAdapter,
|
|
23
|
+
type AnthropicAdapter,
|
|
24
|
+
type AnthropicAdapterConfig,
|
|
25
|
+
type AnthropicThreadOps,
|
|
26
|
+
type AnthropicToolResponse,
|
|
27
|
+
} from "./activities";
|
|
28
|
+
|
|
29
|
+
// Thread manager
|
|
30
|
+
export {
|
|
31
|
+
createAnthropicThreadManager,
|
|
32
|
+
type AnthropicThreadManager,
|
|
33
|
+
type AnthropicThreadManagerConfig,
|
|
34
|
+
type AnthropicContent,
|
|
35
|
+
type AnthropicInvocationPayload,
|
|
36
|
+
type StoredMessage,
|
|
37
|
+
} from "./thread-manager";
|
|
38
|
+
|
|
39
|
+
// Model invoker (for advanced use — prefer adapter.createModelInvoker)
|
|
40
|
+
export {
|
|
41
|
+
createAnthropicModelInvoker,
|
|
42
|
+
invokeAnthropicModel,
|
|
43
|
+
type AnthropicModelInvokerConfig,
|
|
44
|
+
} from "./model-invoker";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type Redis from "ioredis";
|
|
2
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
3
|
+
import type { SerializableToolDefinition } from "../../../lib/types";
|
|
4
|
+
import type { AgentResponse, ModelInvokerConfig } from "../../../lib/model";
|
|
5
|
+
import { createAnthropicThreadManager } from "./thread-manager";
|
|
6
|
+
import { v4 as uuidv4 } from "uuid";
|
|
7
|
+
|
|
8
|
+
export interface AnthropicModelInvokerConfig {
|
|
9
|
+
redis: Redis;
|
|
10
|
+
client: Anthropic;
|
|
11
|
+
model: string;
|
|
12
|
+
/** Maximum tokens to generate. Defaults to 16384. */
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function toAnthropicTools(
|
|
17
|
+
tools: SerializableToolDefinition[],
|
|
18
|
+
): Anthropic.Messages.Tool[] {
|
|
19
|
+
return tools.map((t) => ({
|
|
20
|
+
name: t.name,
|
|
21
|
+
description: t.description,
|
|
22
|
+
input_schema: t.schema as Anthropic.Messages.Tool.InputSchema,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates an Anthropic model invoker that satisfies the generic
|
|
28
|
+
* `ModelInvoker<Anthropic.Messages.Message>` contract.
|
|
29
|
+
*
|
|
30
|
+
* Loads the conversation thread from Redis, invokes the Claude model via
|
|
31
|
+
* `client.messages.create`, appends the AI response, and returns
|
|
32
|
+
* a normalised AgentResponse.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { createAnthropicModelInvoker } from 'zeitlich/adapters/thread/anthropic';
|
|
37
|
+
* import { createRunAgentActivity } from 'zeitlich';
|
|
38
|
+
* import Anthropic from '@anthropic-ai/sdk';
|
|
39
|
+
*
|
|
40
|
+
* const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
41
|
+
* const invoker = createAnthropicModelInvoker({
|
|
42
|
+
* redis,
|
|
43
|
+
* client,
|
|
44
|
+
* model: 'claude-sonnet-4-20250514',
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* return { runAgent: createRunAgentActivity(client, invoker) };
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function createAnthropicModelInvoker({
|
|
51
|
+
redis,
|
|
52
|
+
client,
|
|
53
|
+
model,
|
|
54
|
+
maxTokens = 16384,
|
|
55
|
+
}: AnthropicModelInvokerConfig) {
|
|
56
|
+
return async function invokeAnthropicModel(
|
|
57
|
+
config: ModelInvokerConfig,
|
|
58
|
+
): Promise<AgentResponse<Anthropic.Messages.Message>> {
|
|
59
|
+
const { threadId, threadKey, state } = config;
|
|
60
|
+
|
|
61
|
+
const thread = createAnthropicThreadManager({ redis, threadId, key: threadKey });
|
|
62
|
+
const { messages, system } = await thread.prepareForInvocation();
|
|
63
|
+
|
|
64
|
+
const anthropicTools = toAnthropicTools(state.tools);
|
|
65
|
+
const tools = anthropicTools.length > 0 ? anthropicTools : undefined;
|
|
66
|
+
|
|
67
|
+
const response = await client.messages.create({
|
|
68
|
+
model,
|
|
69
|
+
max_tokens: maxTokens,
|
|
70
|
+
messages,
|
|
71
|
+
...(system ? { system } : {}),
|
|
72
|
+
...(tools ? { tools } : {}),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await thread.appendAssistantMessage(uuidv4(), response.content);
|
|
76
|
+
|
|
77
|
+
const toolCalls = response.content.filter(
|
|
78
|
+
(block): block is Anthropic.Messages.ToolUseBlock =>
|
|
79
|
+
block.type === "tool_use",
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
message: response,
|
|
84
|
+
rawToolCalls: toolCalls.map((tc) => ({
|
|
85
|
+
id: tc.id,
|
|
86
|
+
name: tc.name,
|
|
87
|
+
args: (tc.input as Record<string, unknown>) ?? {},
|
|
88
|
+
})),
|
|
89
|
+
usage: {
|
|
90
|
+
inputTokens: response.usage.input_tokens,
|
|
91
|
+
outputTokens: response.usage.output_tokens,
|
|
92
|
+
cachedWriteTokens: response.usage.cache_creation_input_tokens ?? undefined,
|
|
93
|
+
cachedReadTokens: response.usage.cache_read_input_tokens ?? undefined,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Standalone function for one-shot Anthropic model invocation.
|
|
101
|
+
* Convenience wrapper around createAnthropicModelInvoker for cases
|
|
102
|
+
* where you don't need to reuse the invoker.
|
|
103
|
+
*/
|
|
104
|
+
export async function invokeAnthropicModel({
|
|
105
|
+
redis,
|
|
106
|
+
client,
|
|
107
|
+
model,
|
|
108
|
+
maxTokens,
|
|
109
|
+
config,
|
|
110
|
+
}: {
|
|
111
|
+
redis: Redis;
|
|
112
|
+
client: Anthropic;
|
|
113
|
+
model: string;
|
|
114
|
+
maxTokens?: number;
|
|
115
|
+
config: ModelInvokerConfig;
|
|
116
|
+
}): Promise<AgentResponse<Anthropic.Messages.Message>> {
|
|
117
|
+
const invoker = createAnthropicModelInvoker({
|
|
118
|
+
redis,
|
|
119
|
+
client,
|
|
120
|
+
model,
|
|
121
|
+
maxTokens,
|
|
122
|
+
});
|
|
123
|
+
return invoker(config);
|
|
124
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for Anthropic thread operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/thread/anthropic/workflow`
|
|
5
|
+
* in your Temporal workflow files.
|
|
6
|
+
*
|
|
7
|
+
* By default the scope is derived from `workflowInfo().workflowType`,
|
|
8
|
+
* so activities are automatically namespaced per workflow.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { proxyAnthropicThreadOps } from 'zeitlich/adapters/thread/anthropic/workflow';
|
|
13
|
+
*
|
|
14
|
+
* // Auto-scoped to the current workflow name
|
|
15
|
+
* const threadOps = proxyAnthropicThreadOps();
|
|
16
|
+
*
|
|
17
|
+
* // Explicit scope override
|
|
18
|
+
* const threadOps = proxyAnthropicThreadOps("customScope");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { type ActivityInterfaceFor } from "@temporalio/workflow";
|
|
22
|
+
import type { ThreadOps } from "../../../lib/session/types";
|
|
23
|
+
import type { AnthropicContent } from "./thread-manager";
|
|
24
|
+
import { createThreadOpsProxy } from "../../../lib/thread/proxy";
|
|
25
|
+
|
|
26
|
+
const ADAPTER_PREFIX = "anthropic";
|
|
27
|
+
|
|
28
|
+
export function proxyAnthropicThreadOps(
|
|
29
|
+
scope?: string,
|
|
30
|
+
options?: Parameters<typeof createThreadOpsProxy>[2],
|
|
31
|
+
): ActivityInterfaceFor<ThreadOps<AnthropicContent>> {
|
|
32
|
+
return createThreadOpsProxy(ADAPTER_PREFIX, scope, options) as ActivityInterfaceFor<ThreadOps<AnthropicContent>>;
|
|
33
|
+
}
|