zeitlich 0.2.21 → 0.2.23
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/README.md +303 -105
- package/dist/adapters/sandbox/daytona/index.cjs +7 -1
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
- package/dist/adapters/sandbox/daytona/index.js +7 -1
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +33 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/daytona/workflow.d.cts +27 -0
- package/dist/adapters/sandbox/daytona/workflow.d.ts +27 -0
- package/dist/adapters/sandbox/daytona/workflow.js +31 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.cjs +18 -1
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +4 -2
- package/dist/adapters/sandbox/inmemory/index.d.ts +4 -2
- package/dist/adapters/sandbox/inmemory/index.js +18 -1
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +33 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +25 -0
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +25 -0
- package/dist/adapters/sandbox/inmemory/workflow.js +31 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -0
- package/dist/adapters/sandbox/virtual/index.cjs +36 -9
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
- package/dist/adapters/sandbox/virtual/index.d.cts +8 -5
- package/dist/adapters/sandbox/virtual/index.d.ts +8 -5
- package/dist/adapters/sandbox/virtual/index.js +36 -9
- package/dist/adapters/sandbox/virtual/index.js.map +1 -1
- package/dist/adapters/sandbox/virtual/workflow.cjs +33 -0
- package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -0
- package/dist/adapters/sandbox/virtual/workflow.d.cts +27 -0
- package/dist/adapters/sandbox/virtual/workflow.d.ts +27 -0
- package/dist/adapters/sandbox/virtual/workflow.js +31 -0
- package/dist/adapters/sandbox/virtual/workflow.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +9 -1
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +31 -19
- package/dist/adapters/thread/google-genai/index.d.ts +31 -19
- package/dist/adapters/thread/google-genai/index.js +9 -1
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +33 -0
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -0
- package/dist/adapters/thread/google-genai/workflow.d.cts +32 -0
- package/dist/adapters/thread/google-genai/workflow.d.ts +32 -0
- package/dist/adapters/thread/google-genai/workflow.js +31 -0
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -0
- package/dist/adapters/thread/langchain/index.cjs +9 -1
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +27 -16
- package/dist/adapters/thread/langchain/index.d.ts +27 -16
- package/dist/adapters/thread/langchain/index.js +9 -1
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +33 -0
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -0
- package/dist/adapters/thread/langchain/workflow.d.cts +32 -0
- package/dist/adapters/thread/langchain/workflow.d.ts +32 -0
- package/dist/adapters/thread/langchain/workflow.js +31 -0
- package/dist/adapters/thread/langchain/workflow.js.map +1 -0
- package/dist/index.cjs +282 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -16
- package/dist/index.d.ts +38 -16
- package/dist/index.js +281 -87
- package/dist/index.js.map +1 -1
- package/dist/queries-DModcWRy.d.cts +44 -0
- package/dist/queries-byD0jr1Y.d.ts +44 -0
- package/dist/{types-BkAYmc96.d.ts → types-B50pBPEV.d.ts} +190 -38
- package/dist/{types-YbL7JpEA.d.cts → types-Bll19FZJ.d.cts} +7 -0
- package/dist/{types-YbL7JpEA.d.ts → types-Bll19FZJ.d.ts} +7 -0
- package/dist/{queries-6Avfh74U.d.ts → types-BuXdFhaZ.d.cts} +7 -48
- package/dist/{types-BMRzfELQ.d.cts → types-ChAMwU3q.d.cts} +17 -1
- package/dist/{types-BMRzfELQ.d.ts → types-ChAMwU3q.d.ts} +17 -1
- package/dist/{types-CES_30qx.d.cts → types-DQW8l7pY.d.cts} +190 -38
- package/dist/{queries-CHa2iv_I.d.cts → types-GZ76HZSj.d.ts} +7 -48
- package/dist/workflow.cjs +244 -86
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +54 -65
- package/dist/workflow.d.ts +54 -65
- package/dist/workflow.js +243 -83
- package/dist/workflow.js.map +1 -1
- package/package.json +54 -2
- package/src/adapters/sandbox/daytona/filesystem.ts +1 -1
- package/src/adapters/sandbox/daytona/index.ts +8 -0
- package/src/adapters/sandbox/daytona/proxy.ts +56 -0
- package/src/adapters/sandbox/e2b/filesystem.ts +147 -0
- package/src/adapters/sandbox/e2b/index.ts +164 -0
- package/src/adapters/sandbox/e2b/types.ts +23 -0
- package/src/adapters/sandbox/inmemory/index.ts +27 -3
- package/src/adapters/sandbox/inmemory/proxy.ts +53 -0
- package/src/adapters/sandbox/virtual/filesystem.ts +41 -17
- package/src/adapters/sandbox/virtual/provider.ts +9 -1
- package/src/adapters/sandbox/virtual/proxy.ts +53 -0
- package/src/adapters/sandbox/virtual/types.ts +9 -4
- package/src/adapters/thread/google-genai/activities.ts +51 -17
- package/src/adapters/thread/google-genai/index.ts +1 -0
- package/src/adapters/thread/google-genai/proxy.ts +61 -0
- package/src/adapters/thread/langchain/activities.ts +47 -14
- package/src/adapters/thread/langchain/index.ts +1 -0
- package/src/adapters/thread/langchain/proxy.ts +61 -0
- package/src/lib/lifecycle.ts +57 -0
- package/src/lib/sandbox/manager.ts +52 -6
- package/src/lib/sandbox/sandbox.test.ts +12 -11
- package/src/lib/sandbox/types.ts +31 -4
- package/src/lib/session/index.ts +4 -5
- package/src/lib/session/session-edge-cases.integration.test.ts +491 -66
- package/src/lib/session/session.integration.test.ts +92 -80
- package/src/lib/session/session.ts +108 -96
- package/src/lib/session/types.ts +87 -17
- package/src/lib/subagent/define.ts +6 -5
- package/src/lib/subagent/handler.ts +148 -16
- package/src/lib/subagent/index.ts +4 -0
- package/src/lib/subagent/register.ts +10 -3
- package/src/lib/subagent/signals.ts +8 -0
- package/src/lib/subagent/subagent.integration.test.ts +893 -128
- package/src/lib/subagent/tool.ts +2 -2
- package/src/lib/subagent/types.ts +84 -21
- package/src/lib/subagent/workflow.ts +83 -12
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +4 -1
- package/src/lib/tool-router/router.integration.test.ts +141 -5
- package/src/lib/tool-router/router.ts +13 -3
- package/src/lib/tool-router/types.ts +7 -0
- package/src/lib/workflow.test.ts +104 -27
- package/src/lib/workflow.ts +37 -19
- package/src/tools/bash/bash.test.ts +16 -7
- package/src/workflow.ts +11 -14
- package/tsup.config.ts +6 -0
|
@@ -29,7 +29,7 @@ function parentDir(p: string): string {
|
|
|
29
29
|
*/
|
|
30
30
|
function inferDirectories(
|
|
31
31
|
entries: { path: string }[],
|
|
32
|
-
workspaceBase: string
|
|
32
|
+
workspaceBase: string
|
|
33
33
|
): Set<string> {
|
|
34
34
|
const dirs = new Set<string>();
|
|
35
35
|
dirs.add("/");
|
|
@@ -54,8 +54,7 @@ function inferDirectories(
|
|
|
54
54
|
export class VirtualSandboxFileSystem<
|
|
55
55
|
TCtx = unknown,
|
|
56
56
|
TMeta = FileEntryMetadata,
|
|
57
|
-
> implements SandboxFileSystem
|
|
58
|
-
{
|
|
57
|
+
> implements SandboxFileSystem {
|
|
59
58
|
readonly workspaceBase: string;
|
|
60
59
|
private entries: Map<string, FileEntry<TMeta>>;
|
|
61
60
|
private directories: Set<string>;
|
|
@@ -65,11 +64,11 @@ export class VirtualSandboxFileSystem<
|
|
|
65
64
|
tree: FileEntry<TMeta>[],
|
|
66
65
|
private resolver: FileResolver<TCtx, TMeta>,
|
|
67
66
|
private ctx: TCtx,
|
|
68
|
-
workspaceBase = "/"
|
|
67
|
+
workspaceBase = "/"
|
|
69
68
|
) {
|
|
70
69
|
this.workspaceBase = normalisePath(workspaceBase);
|
|
71
70
|
this.entries = new Map(
|
|
72
|
-
tree.map((e) => [normalisePath(e.path, this.workspaceBase), e])
|
|
71
|
+
tree.map((e) => [normalisePath(e.path, this.workspaceBase), e])
|
|
73
72
|
);
|
|
74
73
|
this.directories = inferDirectories(tree, this.workspaceBase);
|
|
75
74
|
}
|
|
@@ -91,13 +90,13 @@ export class VirtualSandboxFileSystem<
|
|
|
91
90
|
async readFile(path: string): Promise<string> {
|
|
92
91
|
const entry = this.entries.get(normalisePath(path, this.workspaceBase));
|
|
93
92
|
if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
|
|
94
|
-
return this.resolver.readFile(entry.id, this.ctx);
|
|
93
|
+
return this.resolver.readFile(entry.id, this.ctx, entry.metadata);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
async readFileBuffer(path: string): Promise<Uint8Array> {
|
|
98
97
|
const entry = this.entries.get(normalisePath(path, this.workspaceBase));
|
|
99
98
|
if (!entry) throw new Error(`ENOENT: no such file: ${path}`);
|
|
100
|
-
return this.resolver.readFileBuffer(entry.id, this.ctx);
|
|
99
|
+
return this.resolver.readFileBuffer(entry.id, this.ctx, entry.metadata);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
// --------------------------------------------------------------------------
|
|
@@ -180,7 +179,12 @@ export class VirtualSandboxFileSystem<
|
|
|
180
179
|
const existing = this.entries.get(norm);
|
|
181
180
|
|
|
182
181
|
if (existing) {
|
|
183
|
-
await this.resolver.writeFile(
|
|
182
|
+
await this.resolver.writeFile(
|
|
183
|
+
existing.id,
|
|
184
|
+
content,
|
|
185
|
+
this.ctx,
|
|
186
|
+
existing.metadata
|
|
187
|
+
);
|
|
184
188
|
const size =
|
|
185
189
|
typeof content === "string"
|
|
186
190
|
? new TextEncoder().encode(content).byteLength
|
|
@@ -209,12 +213,21 @@ export class VirtualSandboxFileSystem<
|
|
|
209
213
|
return this.writeFile(path, content);
|
|
210
214
|
}
|
|
211
215
|
|
|
212
|
-
const current = await this.resolver.readFile(
|
|
216
|
+
const current = await this.resolver.readFile(
|
|
217
|
+
existing.id,
|
|
218
|
+
this.ctx,
|
|
219
|
+
existing.metadata
|
|
220
|
+
);
|
|
213
221
|
const appended =
|
|
214
222
|
typeof content === "string"
|
|
215
223
|
? current + content
|
|
216
224
|
: current + new TextDecoder().decode(content);
|
|
217
|
-
await this.resolver.writeFile(
|
|
225
|
+
await this.resolver.writeFile(
|
|
226
|
+
existing.id,
|
|
227
|
+
appended,
|
|
228
|
+
this.ctx,
|
|
229
|
+
existing.metadata
|
|
230
|
+
);
|
|
218
231
|
|
|
219
232
|
const size = new TextEncoder().encode(appended).byteLength;
|
|
220
233
|
const updated: FileEntry<TMeta> = {
|
|
@@ -226,7 +239,10 @@ export class VirtualSandboxFileSystem<
|
|
|
226
239
|
this.mutations.push({ type: "update", path: norm, entry: updated });
|
|
227
240
|
}
|
|
228
241
|
|
|
229
|
-
async mkdir(
|
|
242
|
+
async mkdir(
|
|
243
|
+
_path: string,
|
|
244
|
+
_options?: { recursive?: boolean }
|
|
245
|
+
): Promise<void> {
|
|
230
246
|
const norm = normalisePath(_path, this.workspaceBase);
|
|
231
247
|
if (this.directories.has(norm)) return;
|
|
232
248
|
|
|
@@ -244,13 +260,13 @@ export class VirtualSandboxFileSystem<
|
|
|
244
260
|
|
|
245
261
|
async rm(
|
|
246
262
|
path: string,
|
|
247
|
-
options?: { recursive?: boolean; force?: boolean }
|
|
263
|
+
options?: { recursive?: boolean; force?: boolean }
|
|
248
264
|
): Promise<void> {
|
|
249
265
|
const norm = normalisePath(path, this.workspaceBase);
|
|
250
266
|
const entry = this.entries.get(norm);
|
|
251
267
|
|
|
252
268
|
if (entry) {
|
|
253
|
-
await this.resolver.deleteFile(entry.id, this.ctx);
|
|
269
|
+
await this.resolver.deleteFile(entry.id, this.ctx, entry.metadata);
|
|
254
270
|
this.entries.delete(norm);
|
|
255
271
|
this.mutations.push({ type: "remove", path: norm });
|
|
256
272
|
return;
|
|
@@ -263,7 +279,7 @@ export class VirtualSandboxFileSystem<
|
|
|
263
279
|
const prefix = norm === "/" ? "/" : norm + "/";
|
|
264
280
|
for (const [p, e] of this.entries) {
|
|
265
281
|
if (p.startsWith(prefix)) {
|
|
266
|
-
await this.resolver.deleteFile(e.id, this.ctx);
|
|
282
|
+
await this.resolver.deleteFile(e.id, this.ctx, e.metadata);
|
|
267
283
|
this.entries.delete(p);
|
|
268
284
|
this.mutations.push({ type: "remove", path: p });
|
|
269
285
|
}
|
|
@@ -283,14 +299,18 @@ export class VirtualSandboxFileSystem<
|
|
|
283
299
|
async cp(
|
|
284
300
|
src: string,
|
|
285
301
|
dest: string,
|
|
286
|
-
_options?: { recursive?: boolean }
|
|
302
|
+
_options?: { recursive?: boolean }
|
|
287
303
|
): Promise<void> {
|
|
288
304
|
const normSrc = normalisePath(src, this.workspaceBase);
|
|
289
305
|
const normDest = normalisePath(dest, this.workspaceBase);
|
|
290
306
|
|
|
291
307
|
const entry = this.entries.get(normSrc);
|
|
292
308
|
if (entry) {
|
|
293
|
-
const content = await this.resolver.readFile(
|
|
309
|
+
const content = await this.resolver.readFile(
|
|
310
|
+
entry.id,
|
|
311
|
+
this.ctx,
|
|
312
|
+
entry.metadata
|
|
313
|
+
);
|
|
294
314
|
await this.writeFile(normDest, content);
|
|
295
315
|
return;
|
|
296
316
|
}
|
|
@@ -306,7 +326,11 @@ export class VirtualSandboxFileSystem<
|
|
|
306
326
|
for (const [p, e] of this.entries) {
|
|
307
327
|
if (p.startsWith(prefix)) {
|
|
308
328
|
const relative = p.slice(normSrc.length);
|
|
309
|
-
const content = await this.resolver.readFile(
|
|
329
|
+
const content = await this.resolver.readFile(
|
|
330
|
+
e.id,
|
|
331
|
+
this.ctx,
|
|
332
|
+
e.metadata
|
|
333
|
+
);
|
|
310
334
|
await this.writeFile(normDest + relative, content);
|
|
311
335
|
}
|
|
312
336
|
}
|
|
@@ -26,7 +26,7 @@ import type {
|
|
|
26
26
|
* const manager = new SandboxManager(provider);
|
|
27
27
|
*
|
|
28
28
|
* export const activities = {
|
|
29
|
-
* ...manager.createActivities(),
|
|
29
|
+
* ...manager.createActivities("CodingAgent"),
|
|
30
30
|
* readFile: withVirtualSandbox(client, provider, readHandler),
|
|
31
31
|
* };
|
|
32
32
|
* ```
|
|
@@ -90,6 +90,14 @@ export class VirtualSandboxProvider<
|
|
|
90
90
|
// No-op — no internal state to clean up
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
async pause(): Promise<void> {
|
|
94
|
+
// No-op — virtual sandbox state lives in workflow AgentState
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async fork(_sandboxId: string): Promise<never> {
|
|
98
|
+
throw new Error("Not implemented");
|
|
99
|
+
}
|
|
100
|
+
|
|
93
101
|
async snapshot(): Promise<never> {
|
|
94
102
|
throw new SandboxNotSupportedError(
|
|
95
103
|
"snapshot (virtual sandbox state lives in workflow AgentState)"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for virtual sandbox operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/sandbox/virtual/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 { proxyVirtualSandboxOps } from 'zeitlich/adapters/sandbox/virtual/workflow';
|
|
13
|
+
*
|
|
14
|
+
* const sandbox = proxyVirtualSandboxOps();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { proxyActivities, workflowInfo } from "@temporalio/workflow";
|
|
18
|
+
import type { SandboxOps } from "../../../lib/sandbox/types";
|
|
19
|
+
import type { VirtualSandboxCreateOptions } from "./types";
|
|
20
|
+
|
|
21
|
+
const ADAPTER_PREFIX = "virtual";
|
|
22
|
+
|
|
23
|
+
export function proxyVirtualSandboxOps(
|
|
24
|
+
scope?: string,
|
|
25
|
+
options?: Parameters<typeof proxyActivities>[0]
|
|
26
|
+
): SandboxOps<VirtualSandboxCreateOptions<unknown>> {
|
|
27
|
+
const resolvedScope = scope ?? workflowInfo().workflowType;
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const acts = proxyActivities<Record<string, (...args: any[]) => any>>(
|
|
31
|
+
options ?? {
|
|
32
|
+
startToCloseTimeout: "30s",
|
|
33
|
+
retry: {
|
|
34
|
+
maximumAttempts: 3,
|
|
35
|
+
initialInterval: "2s",
|
|
36
|
+
maximumInterval: "30s",
|
|
37
|
+
backoffCoefficient: 2,
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const prefix = `${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
|
|
43
|
+
const p = (key: string): string =>
|
|
44
|
+
`${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
createSandbox: acts[p("createSandbox")],
|
|
48
|
+
destroySandbox: acts[p("destroySandbox")],
|
|
49
|
+
pauseSandbox: acts[p("pauseSandbox")],
|
|
50
|
+
snapshotSandbox: acts[p("snapshotSandbox")],
|
|
51
|
+
forkSandbox: acts[p("forkSandbox")],
|
|
52
|
+
} as SandboxOps<VirtualSandboxCreateOptions<unknown>>;
|
|
53
|
+
}
|
|
@@ -56,15 +56,20 @@ export type TreeMutation<TMeta = FileEntryMetadata> =
|
|
|
56
56
|
*/
|
|
57
57
|
export interface FileResolver<TCtx = unknown, TMeta = FileEntryMetadata> {
|
|
58
58
|
resolveEntries(ctx: TCtx): Promise<FileEntry<TMeta>[]>;
|
|
59
|
-
readFile(id: string, ctx: TCtx): Promise<string>;
|
|
60
|
-
readFileBuffer(id: string, ctx: TCtx): Promise<Uint8Array>;
|
|
61
|
-
writeFile(
|
|
59
|
+
readFile(id: string, ctx: TCtx, metadata: TMeta): Promise<string>;
|
|
60
|
+
readFileBuffer(id: string, ctx: TCtx, metadata: TMeta): Promise<Uint8Array>;
|
|
61
|
+
writeFile(
|
|
62
|
+
id: string,
|
|
63
|
+
content: string | Uint8Array,
|
|
64
|
+
ctx: TCtx,
|
|
65
|
+
metadata: TMeta
|
|
66
|
+
): Promise<void>;
|
|
62
67
|
createFile(
|
|
63
68
|
path: string,
|
|
64
69
|
content: string | Uint8Array,
|
|
65
70
|
ctx: TCtx
|
|
66
71
|
): Promise<FileEntry<TMeta>>;
|
|
67
|
-
deleteFile(id: string, ctx: TCtx): Promise<void>;
|
|
72
|
+
deleteFile(id: string, ctx: TCtx, metadata: TMeta): Promise<void>;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
// ============================================================================
|
|
@@ -2,11 +2,20 @@ import type Redis from "ioredis";
|
|
|
2
2
|
import type { GoogleGenAI, Content } from "@google/genai";
|
|
3
3
|
import type { ToolResultConfig } from "../../../lib/types";
|
|
4
4
|
import type { MessageContent } from "../../../lib/types";
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ThreadOps,
|
|
7
|
+
PrefixedThreadOps,
|
|
8
|
+
ScopedPrefix,
|
|
9
|
+
} from "../../../lib/session/types";
|
|
6
10
|
import type { ModelInvoker } from "../../../lib/model";
|
|
7
11
|
import { createGoogleGenAIThreadManager } from "./thread-manager";
|
|
8
12
|
import { createGoogleGenAIModelInvoker } from "./model-invoker";
|
|
9
13
|
|
|
14
|
+
const ADAPTER_PREFIX = "googleGenAI" as const;
|
|
15
|
+
|
|
16
|
+
export type GoogleGenAIThreadOps<TScope extends string = ""> =
|
|
17
|
+
PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>>;
|
|
18
|
+
|
|
10
19
|
export interface GoogleGenAIAdapterConfig {
|
|
11
20
|
redis: Redis;
|
|
12
21
|
client: GoogleGenAI;
|
|
@@ -15,21 +24,37 @@ export interface GoogleGenAIAdapterConfig {
|
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
export interface GoogleGenAIAdapter {
|
|
18
|
-
/** Thread operations (register these as Temporal activities on the worker) */
|
|
19
|
-
threadOps: ThreadOps;
|
|
20
27
|
/** Model invoker using the default model (only available when `model` was provided) */
|
|
21
28
|
invoker: ModelInvoker<Content>;
|
|
22
29
|
/** Create an invoker for a specific model name (for multi-model setups) */
|
|
23
30
|
createModelInvoker(model: string): ModelInvoker<Content>;
|
|
31
|
+
/**
|
|
32
|
+
* Create prefixed thread activities for registration on the worker.
|
|
33
|
+
*
|
|
34
|
+
* @param scope - Workflow name appended to the adapter prefix.
|
|
35
|
+
* Use different scopes for the main agent vs subagents to avoid collisions.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* adapter.createActivities("codingAgent")
|
|
40
|
+
* // → { googleGenAICodingAgentInitializeThread, googleGenAICodingAgentAppendHumanMessage, … }
|
|
41
|
+
*
|
|
42
|
+
* adapter.createActivities("researchAgent")
|
|
43
|
+
* // → { googleGenAIResearchAgentInitializeThread, … }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
createActivities<S extends string = "">(
|
|
47
|
+
scope?: S
|
|
48
|
+
): GoogleGenAIThreadOps<S>;
|
|
24
49
|
}
|
|
25
50
|
|
|
26
51
|
/**
|
|
27
52
|
* Creates a Google GenAI adapter that bundles thread operations and model
|
|
28
53
|
* invocation using the `@google/genai` SDK.
|
|
29
54
|
*
|
|
30
|
-
*
|
|
31
|
-
* the worker. The `invoker` (or invokers created via
|
|
32
|
-
* should be wrapped with `createRunAgentActivity
|
|
55
|
+
* Use `createActivities(scope)` to register scoped thread operations as
|
|
56
|
+
* Temporal activities on the worker. The `invoker` (or invokers created via
|
|
57
|
+
* `createModelInvoker`) should be wrapped with `createRunAgentActivity`.
|
|
33
58
|
*
|
|
34
59
|
* @example
|
|
35
60
|
* ```typescript
|
|
@@ -42,27 +67,23 @@ export interface GoogleGenAIAdapter {
|
|
|
42
67
|
*
|
|
43
68
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
44
69
|
* return {
|
|
45
|
-
* ...adapter.
|
|
46
|
-
*
|
|
70
|
+
* ...adapter.createActivities("codingAgent"),
|
|
71
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
47
72
|
* };
|
|
48
73
|
* }
|
|
49
74
|
* ```
|
|
50
75
|
*
|
|
51
|
-
* @example Multi-
|
|
76
|
+
* @example Multi-agent worker (main + subagent share the adapter)
|
|
52
77
|
* ```typescript
|
|
53
|
-
* const adapter = createGoogleGenAIAdapter({ redis, client });
|
|
54
|
-
*
|
|
55
78
|
* export function createActivities(temporalClient: WorkflowClient) {
|
|
56
79
|
* return {
|
|
57
|
-
* ...adapter.
|
|
80
|
+
* ...adapter.createActivities("codingAgent"),
|
|
81
|
+
* ...adapter.createActivities("researchAgent"),
|
|
82
|
+
* runCodingAgent: createRunAgentActivity(temporalClient, adapter.invoker),
|
|
58
83
|
* runResearchAgent: createRunAgentActivity(
|
|
59
84
|
* temporalClient,
|
|
60
85
|
* adapter.createModelInvoker('gemini-2.5-pro'),
|
|
61
86
|
* ),
|
|
62
|
-
* runFastAgent: createRunAgentActivity(
|
|
63
|
-
* temporalClient,
|
|
64
|
-
* adapter.createModelInvoker('gemini-2.5-flash'),
|
|
65
|
-
* ),
|
|
66
87
|
* };
|
|
67
88
|
* }
|
|
68
89
|
* ```
|
|
@@ -114,6 +135,19 @@ export function createGoogleGenAIAdapter(
|
|
|
114
135
|
},
|
|
115
136
|
};
|
|
116
137
|
|
|
138
|
+
function createActivities<S extends string = "">(
|
|
139
|
+
scope?: S
|
|
140
|
+
): GoogleGenAIThreadOps<S> {
|
|
141
|
+
const prefix = scope
|
|
142
|
+
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
143
|
+
: ADAPTER_PREFIX;
|
|
144
|
+
const cap = (s: string): string =>
|
|
145
|
+
s.charAt(0).toUpperCase() + s.slice(1);
|
|
146
|
+
return Object.fromEntries(
|
|
147
|
+
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
148
|
+
) as GoogleGenAIThreadOps<S>;
|
|
149
|
+
}
|
|
150
|
+
|
|
117
151
|
const makeInvoker = (model: string): ModelInvoker<Content> =>
|
|
118
152
|
createGoogleGenAIModelInvoker({ redis, client, model });
|
|
119
153
|
|
|
@@ -127,7 +161,7 @@ export function createGoogleGenAIAdapter(
|
|
|
127
161
|
}) as unknown as ModelInvoker<Content>);
|
|
128
162
|
|
|
129
163
|
return {
|
|
130
|
-
|
|
164
|
+
createActivities,
|
|
131
165
|
invoker,
|
|
132
166
|
createModelInvoker: makeInvoker,
|
|
133
167
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for Google GenAI thread operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/thread/google-genai/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 { proxyGoogleGenAIThreadOps } from 'zeitlich/adapters/thread/google-genai/workflow';
|
|
13
|
+
*
|
|
14
|
+
* // Auto-scoped to the current workflow name
|
|
15
|
+
* const threadOps = proxyGoogleGenAIThreadOps();
|
|
16
|
+
*
|
|
17
|
+
* // Explicit scope override
|
|
18
|
+
* const threadOps = proxyGoogleGenAIThreadOps("customScope");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import {
|
|
22
|
+
proxyActivities,
|
|
23
|
+
workflowInfo,
|
|
24
|
+
type ActivityInterfaceFor,
|
|
25
|
+
} from "@temporalio/workflow";
|
|
26
|
+
import type { ThreadOps } from "../../../lib/session/types";
|
|
27
|
+
|
|
28
|
+
const ADAPTER_PREFIX = "googleGenAI";
|
|
29
|
+
|
|
30
|
+
export function proxyGoogleGenAIThreadOps(
|
|
31
|
+
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>;
|
|
61
|
+
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type Redis from "ioredis";
|
|
2
2
|
import type { ToolResultConfig } from "../../../lib/types";
|
|
3
3
|
import type { MessageContent } from "@langchain/core/messages";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ThreadOps,
|
|
6
|
+
PrefixedThreadOps,
|
|
7
|
+
ScopedPrefix,
|
|
8
|
+
} from "../../../lib/session/types";
|
|
5
9
|
import type { ModelInvoker } from "../../../lib/model";
|
|
6
10
|
import type { StoredMessage } from "@langchain/core/messages";
|
|
7
11
|
import type { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
8
12
|
import { createLangChainThreadManager } from "./thread-manager";
|
|
9
13
|
import { createLangChainModelInvoker } from "./model-invoker";
|
|
10
14
|
|
|
15
|
+
const ADAPTER_PREFIX = "langChain" as const;
|
|
16
|
+
|
|
17
|
+
export type LangChainThreadOps<TScope extends string = ""> =
|
|
18
|
+
PrefixedThreadOps<ScopedPrefix<TScope, typeof ADAPTER_PREFIX>>;
|
|
19
|
+
|
|
11
20
|
export interface LangChainAdapterConfig {
|
|
12
21
|
redis: Redis;
|
|
13
22
|
/** Optional default model — if omitted, use `createModelInvoker()` to create invokers later */
|
|
@@ -16,22 +25,34 @@ export interface LangChainAdapterConfig {
|
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
export interface LangChainAdapter {
|
|
19
|
-
/** Thread operations (register these as Temporal activities on the worker) */
|
|
20
|
-
threadOps: ThreadOps;
|
|
21
28
|
/** Model invoker using the default model (only available when `model` was provided) */
|
|
22
29
|
invoker: ModelInvoker<StoredMessage>;
|
|
23
30
|
/** Create an invoker for a specific model (for multi-model setups) */
|
|
24
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
32
|
createModelInvoker(model: BaseChatModel<any>): ModelInvoker<StoredMessage>;
|
|
33
|
+
/**
|
|
34
|
+
* Create prefixed thread activities for registration on the worker.
|
|
35
|
+
*
|
|
36
|
+
* @param scope - Workflow name appended to the adapter prefix.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* adapter.createActivities("codingAgent")
|
|
41
|
+
* // → { langChainCodingAgentInitializeThread, langChainCodingAgentAppendHumanMessage, … }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
createActivities<S extends string = "">(
|
|
45
|
+
scope?: S
|
|
46
|
+
): LangChainThreadOps<S>;
|
|
26
47
|
}
|
|
27
48
|
|
|
28
49
|
/**
|
|
29
50
|
* Creates a LangChain adapter that bundles thread operations and model
|
|
30
51
|
* invocation using a consistent message format (StoredMessage).
|
|
31
52
|
*
|
|
32
|
-
*
|
|
33
|
-
* the worker. The `invoker` (or invokers created via
|
|
34
|
-
* should be wrapped with `createRunAgentActivity
|
|
53
|
+
* Use `createActivities(scope)` to register scoped thread operations as
|
|
54
|
+
* Temporal activities on the worker. The `invoker` (or invokers created via
|
|
55
|
+
* `createModelInvoker`) should be wrapped with `createRunAgentActivity`.
|
|
35
56
|
*
|
|
36
57
|
* @example
|
|
37
58
|
* ```typescript
|
|
@@ -42,21 +63,20 @@ export interface LangChainAdapter {
|
|
|
42
63
|
*
|
|
43
64
|
* export function createActivities(client: WorkflowClient) {
|
|
44
65
|
* return {
|
|
45
|
-
* ...adapter.
|
|
46
|
-
*
|
|
66
|
+
* ...adapter.createActivities("codingAgent"),
|
|
67
|
+
* runCodingAgent: createRunAgentActivity(client, adapter.invoker),
|
|
47
68
|
* };
|
|
48
69
|
* }
|
|
49
70
|
* ```
|
|
50
71
|
*
|
|
51
|
-
* @example Multi-
|
|
72
|
+
* @example Multi-agent worker
|
|
52
73
|
* ```typescript
|
|
53
|
-
* const adapter = createLangChainAdapter({ redis });
|
|
54
|
-
*
|
|
55
74
|
* export function createActivities(client: WorkflowClient) {
|
|
56
75
|
* return {
|
|
57
|
-
* ...adapter.
|
|
76
|
+
* ...adapter.createActivities("codingAgent"),
|
|
77
|
+
* ...adapter.createActivities("researchAgent"),
|
|
78
|
+
* runCodingAgent: createRunAgentActivity(client, adapter.invoker),
|
|
58
79
|
* runResearchAgent: createRunAgentActivity(client, adapter.createModelInvoker(claude)),
|
|
59
|
-
* runWriterAgent: createRunAgentActivity(client, adapter.createModelInvoker(gpt4)),
|
|
60
80
|
* };
|
|
61
81
|
* }
|
|
62
82
|
* ```
|
|
@@ -108,6 +128,19 @@ export function createLangChainAdapter(
|
|
|
108
128
|
},
|
|
109
129
|
};
|
|
110
130
|
|
|
131
|
+
function createActivities<S extends string = "">(
|
|
132
|
+
scope?: S
|
|
133
|
+
): LangChainThreadOps<S> {
|
|
134
|
+
const prefix = scope
|
|
135
|
+
? `${ADAPTER_PREFIX}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`
|
|
136
|
+
: ADAPTER_PREFIX;
|
|
137
|
+
const cap = (s: string): string =>
|
|
138
|
+
s.charAt(0).toUpperCase() + s.slice(1);
|
|
139
|
+
return Object.fromEntries(
|
|
140
|
+
Object.entries(threadOps).map(([k, v]) => [`${prefix}${cap(k)}`, v])
|
|
141
|
+
) as LangChainThreadOps<S>;
|
|
142
|
+
}
|
|
143
|
+
|
|
111
144
|
const makeInvoker = (
|
|
112
145
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
146
|
model: BaseChatModel<any>
|
|
@@ -124,7 +157,7 @@ export function createLangChainAdapter(
|
|
|
124
157
|
};
|
|
125
158
|
|
|
126
159
|
return {
|
|
127
|
-
|
|
160
|
+
createActivities,
|
|
128
161
|
invoker,
|
|
129
162
|
createModelInvoker: makeInvoker,
|
|
130
163
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-safe proxy for LangChain thread operations.
|
|
3
|
+
*
|
|
4
|
+
* Import this from `zeitlich/adapters/thread/langchain/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 { proxyLangChainThreadOps } from 'zeitlich/adapters/thread/langchain/workflow';
|
|
13
|
+
*
|
|
14
|
+
* // Auto-scoped to the current workflow name
|
|
15
|
+
* const threadOps = proxyLangChainThreadOps();
|
|
16
|
+
*
|
|
17
|
+
* // Explicit scope override
|
|
18
|
+
* const threadOps = proxyLangChainThreadOps("customScope");
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import {
|
|
22
|
+
proxyActivities,
|
|
23
|
+
workflowInfo,
|
|
24
|
+
type ActivityInterfaceFor,
|
|
25
|
+
} from "@temporalio/workflow";
|
|
26
|
+
import type { ThreadOps } from "../../../lib/session/types";
|
|
27
|
+
|
|
28
|
+
const ADAPTER_PREFIX = "langChain";
|
|
29
|
+
|
|
30
|
+
export function proxyLangChainThreadOps(
|
|
31
|
+
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>;
|
|
61
|
+
}
|