zeitlich 0.2.4 → 0.2.6
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 +70 -40
- package/dist/index.cjs +71 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +72 -37
- package/dist/index.js.map +1 -1
- package/dist/{workflow-PjeURKw4.d.cts → workflow-Dg5JMeOC.d.cts} +18 -4
- package/dist/{workflow-PjeURKw4.d.ts → workflow-Dg5JMeOC.d.ts} +18 -4
- package/dist/workflow.cjs +52 -35
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +1 -1
- package/dist/workflow.d.ts +1 -1
- package/dist/workflow.js +52 -35
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/activities.ts +11 -0
- package/src/index.ts +4 -4
- package/src/lib/model-invoker.ts +5 -4
- package/src/lib/session.ts +17 -4
- package/src/lib/thread-manager.ts +15 -3
- package/src/lib/tool-router.ts +40 -31
- package/src/lib/types.ts +10 -2
- package/src/tools/subagent/handler.ts +4 -5
- package/src/tools/subagent/tool.ts +3 -3
- package/src/workflow.ts +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
[](https://www.npmjs.org/package/zeitlich)
|
|
2
2
|
[](https://npm-stat.com/charts.html?package=zeitlich)
|
|
3
|
+
[](https://deepwiki.com/bead-ai/zeitlich)
|
|
3
4
|
|
|
4
5
|
# Zeitlich
|
|
5
6
|
|
|
@@ -51,7 +52,7 @@ const google = new ChatGoogleGenerativeAI({ model: "gemini-1.5-pro" });
|
|
|
51
52
|
|
|
52
53
|
// Pass to invokeModel in your activity
|
|
53
54
|
return {
|
|
54
|
-
runAgent: (config) => invokeModel(
|
|
55
|
+
runAgent: (config) => invokeModel({ config, model: anthropic, redis, client }),
|
|
55
56
|
};
|
|
56
57
|
```
|
|
57
58
|
|
|
@@ -121,17 +122,25 @@ export const searchTool: ToolDefinition<"Search", typeof searchSchema> = {
|
|
|
121
122
|
|
|
122
123
|
```typescript
|
|
123
124
|
import type Redis from "ioredis";
|
|
125
|
+
import type { WorkflowClient } from "@temporalio/client";
|
|
124
126
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
125
|
-
import { invokeModel } from "zeitlich";
|
|
126
|
-
|
|
127
|
-
export const createActivities = (
|
|
127
|
+
import { invokeModel, type InvokeModelConfig } from "zeitlich";
|
|
128
|
+
|
|
129
|
+
export const createActivities = ({
|
|
130
|
+
redis,
|
|
131
|
+
client,
|
|
132
|
+
}: {
|
|
133
|
+
redis: Redis;
|
|
134
|
+
client: WorkflowClient;
|
|
135
|
+
}) => {
|
|
128
136
|
const model = new ChatAnthropic({
|
|
129
137
|
model: "claude-sonnet-4-20250514",
|
|
130
138
|
maxTokens: 4096,
|
|
131
139
|
});
|
|
132
140
|
|
|
133
141
|
return {
|
|
134
|
-
runAgent: (config) =>
|
|
142
|
+
runAgent: (config: InvokeModelConfig) =>
|
|
143
|
+
invokeModel({ config, model, redis, client }),
|
|
135
144
|
|
|
136
145
|
handleSearchResult: async ({ args }) => {
|
|
137
146
|
const results = await performSearch(args.query);
|
|
@@ -147,12 +156,24 @@ export type MyActivities = ReturnType<typeof createActivities>;
|
|
|
147
156
|
|
|
148
157
|
```typescript
|
|
149
158
|
import { proxyActivities, workflowInfo } from "@temporalio/workflow";
|
|
150
|
-
import {
|
|
159
|
+
import {
|
|
160
|
+
createAgentStateManager,
|
|
161
|
+
createSession,
|
|
162
|
+
defineTool,
|
|
163
|
+
proxyDefaultThreadOps,
|
|
164
|
+
} from "zeitlich/workflow";
|
|
151
165
|
import { searchTool } from "./tools";
|
|
152
166
|
import type { MyActivities } from "./activities";
|
|
153
167
|
|
|
154
168
|
const { runAgent, handleSearchResult } = proxyActivities<MyActivities>({
|
|
155
169
|
startToCloseTimeout: "30m",
|
|
170
|
+
retry: {
|
|
171
|
+
maximumAttempts: 6,
|
|
172
|
+
initialInterval: "5s",
|
|
173
|
+
maximumInterval: "15m",
|
|
174
|
+
backoffCoefficient: 4,
|
|
175
|
+
},
|
|
176
|
+
heartbeatTimeout: "5m",
|
|
156
177
|
});
|
|
157
178
|
|
|
158
179
|
export async function myAgentWorkflow({ prompt }: { prompt: string }) {
|
|
@@ -164,13 +185,15 @@ export async function myAgentWorkflow({ prompt }: { prompt: string }) {
|
|
|
164
185
|
threadId: runId,
|
|
165
186
|
agentName: "my-agent",
|
|
166
187
|
maxTurns: 20,
|
|
188
|
+
systemPrompt: "You are a helpful assistant.",
|
|
167
189
|
runAgent,
|
|
190
|
+
threadOps: proxyDefaultThreadOps(),
|
|
168
191
|
buildContextMessage: () => [{ type: "text", text: prompt }],
|
|
169
192
|
tools: {
|
|
170
|
-
Search: {
|
|
193
|
+
Search: defineTool({
|
|
171
194
|
...searchTool,
|
|
172
195
|
handler: handleSearchResult,
|
|
173
|
-
},
|
|
196
|
+
}),
|
|
174
197
|
},
|
|
175
198
|
});
|
|
176
199
|
|
|
@@ -183,6 +206,7 @@ export async function myAgentWorkflow({ prompt }: { prompt: string }) {
|
|
|
183
206
|
|
|
184
207
|
```typescript
|
|
185
208
|
import { Worker, NativeConnection } from "@temporalio/worker";
|
|
209
|
+
import { Client } from "@temporalio/client";
|
|
186
210
|
import { ZeitlichPlugin } from "zeitlich";
|
|
187
211
|
import Redis from "ioredis";
|
|
188
212
|
import { createActivities } from "./activities";
|
|
@@ -191,6 +215,7 @@ async function run() {
|
|
|
191
215
|
const connection = await NativeConnection.connect({
|
|
192
216
|
address: "localhost:7233",
|
|
193
217
|
});
|
|
218
|
+
const client = new Client({ connection });
|
|
194
219
|
const redis = new Redis({ host: "localhost", port: 6379 });
|
|
195
220
|
|
|
196
221
|
const worker = await Worker.create({
|
|
@@ -198,7 +223,7 @@ async function run() {
|
|
|
198
223
|
connection,
|
|
199
224
|
taskQueue: "my-agent",
|
|
200
225
|
workflowsPath: require.resolve("./workflows"),
|
|
201
|
-
activities: createActivities(redis),
|
|
226
|
+
activities: createActivities({ redis, client: client.workflow }),
|
|
202
227
|
});
|
|
203
228
|
|
|
204
229
|
await worker.run();
|
|
@@ -242,14 +267,14 @@ const searchTool: ToolDefinition<"Search", typeof searchSchema> = {
|
|
|
242
267
|
schema: z.object({ query: z.string() }),
|
|
243
268
|
};
|
|
244
269
|
|
|
245
|
-
// In workflow - combine tool definition with handler
|
|
270
|
+
// In workflow - combine tool definition with handler using defineTool()
|
|
246
271
|
const session = await createSession({
|
|
247
272
|
// ... other config
|
|
248
273
|
tools: {
|
|
249
|
-
Search: {
|
|
274
|
+
Search: defineTool({
|
|
250
275
|
...searchTool,
|
|
251
276
|
handler: handleSearchResult, // Activity that implements the tool
|
|
252
|
-
},
|
|
277
|
+
}),
|
|
253
278
|
},
|
|
254
279
|
});
|
|
255
280
|
```
|
|
@@ -288,20 +313,17 @@ const session = await createSession({
|
|
|
288
313
|
Spawn child agents as Temporal child workflows:
|
|
289
314
|
|
|
290
315
|
```typescript
|
|
316
|
+
// Define subagent workflows (each is a Temporal workflow that returns string | null)
|
|
317
|
+
export const researcherSubagent = {
|
|
318
|
+
name: "researcher",
|
|
319
|
+
description: "Researches topics and returns findings",
|
|
320
|
+
workflow: researcherWorkflow,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// In the main agent workflow
|
|
291
324
|
const session = await createSession({
|
|
292
325
|
// ... other config
|
|
293
|
-
subagents: [
|
|
294
|
-
{
|
|
295
|
-
name: "researcher",
|
|
296
|
-
description: "Researches topics and returns findings",
|
|
297
|
-
workflow: "researcherWorkflow",
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
name: "code-reviewer",
|
|
301
|
-
description: "Reviews code for quality and best practices",
|
|
302
|
-
workflow: "codeReviewerWorkflow",
|
|
303
|
-
},
|
|
304
|
-
],
|
|
326
|
+
subagents: [researcherSubagent, codeReviewerSubagent],
|
|
305
327
|
});
|
|
306
328
|
```
|
|
307
329
|
|
|
@@ -327,18 +349,26 @@ const session = await createSession({
|
|
|
327
349
|
});
|
|
328
350
|
```
|
|
329
351
|
|
|
330
|
-
For more advanced file operations, use the built-in tool
|
|
352
|
+
For more advanced file operations, use the built-in tool handler factories:
|
|
331
353
|
|
|
332
354
|
```typescript
|
|
333
|
-
import {
|
|
355
|
+
import { createGlobHandler, createEditHandler, toTree } from "zeitlich";
|
|
334
356
|
|
|
335
357
|
export const createActivities = () => ({
|
|
336
358
|
generateFileTree: () => toTree("/workspace"),
|
|
337
|
-
|
|
338
|
-
|
|
359
|
+
globHandlerActivity: createGlobHandler("/workspace"),
|
|
360
|
+
editHandlerActivity: createEditHandler("/workspace"),
|
|
339
361
|
});
|
|
340
362
|
```
|
|
341
363
|
|
|
364
|
+
`toTree` also accepts an in-memory filesystem object (e.g. from [`just-bash`](https://github.com/nicholasgasior/just-bash)):
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import { toTree } from "zeitlich";
|
|
368
|
+
|
|
369
|
+
const fileTree = toTree(inMemoryFileSystem);
|
|
370
|
+
```
|
|
371
|
+
|
|
342
372
|
### Built-in Tools
|
|
343
373
|
|
|
344
374
|
Zeitlich provides ready-to-use tool definitions and handlers for common agent operations.
|
|
@@ -366,12 +396,12 @@ import {
|
|
|
366
396
|
askUserQuestionTool,
|
|
367
397
|
} from "zeitlich/workflow";
|
|
368
398
|
|
|
369
|
-
// Import
|
|
399
|
+
// Import handler factories in activities
|
|
370
400
|
import {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
401
|
+
createEditHandler,
|
|
402
|
+
createGlobHandler,
|
|
403
|
+
createBashHandler,
|
|
404
|
+
createAskUserQuestionHandler,
|
|
375
405
|
} from "zeitlich";
|
|
376
406
|
```
|
|
377
407
|
|
|
@@ -381,14 +411,14 @@ All tools are passed via `tools`. The Bash tool's description is automatically e
|
|
|
381
411
|
const session = await createSession({
|
|
382
412
|
// ... other config
|
|
383
413
|
tools: {
|
|
384
|
-
AskUserQuestion: {
|
|
414
|
+
AskUserQuestion: defineTool({
|
|
385
415
|
...askUserQuestionTool,
|
|
386
|
-
handler:
|
|
387
|
-
},
|
|
388
|
-
Bash: {
|
|
416
|
+
handler: askUserQuestionHandlerActivity,
|
|
417
|
+
}),
|
|
418
|
+
Bash: defineTool({
|
|
389
419
|
...bashTool,
|
|
390
|
-
handler:
|
|
391
|
-
},
|
|
420
|
+
handler: bashHandlerActivity,
|
|
421
|
+
}),
|
|
392
422
|
},
|
|
393
423
|
});
|
|
394
424
|
```
|
|
@@ -419,7 +449,7 @@ For use in activities, worker setup, and Node.js code:
|
|
|
419
449
|
| `createSharedActivities` | Creates thread management activities |
|
|
420
450
|
| `invokeModel` | Core LLM invocation utility (requires Redis + LangChain) |
|
|
421
451
|
| `toTree` | Generate file tree string from a directory path |
|
|
422
|
-
| Tool handlers | `
|
|
452
|
+
| Tool handlers | `createGlobHandler`, `createEditHandler`, `createBashHandler`, `createAskUserQuestionHandler` |
|
|
423
453
|
|
|
424
454
|
### Types
|
|
425
455
|
|
package/dist/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
|
16
16
|
// src/lib/session.ts
|
|
17
17
|
var SUBAGENT_TOOL = "Subagent";
|
|
18
18
|
function buildSubagentDescription(subagents) {
|
|
19
|
-
const subagentList = subagents.map((s) => `- **${s.
|
|
19
|
+
const subagentList = subagents.map((s) => `- **${s.agentName}**: ${s.description}`).join("\n");
|
|
20
20
|
return `Launch a new agent to handle complex tasks autonomously.
|
|
21
21
|
|
|
22
22
|
The ${SUBAGENT_TOOL} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
@@ -42,7 +42,7 @@ function createSubagentTool(subagents) {
|
|
|
42
42
|
if (subagents.length === 0) {
|
|
43
43
|
throw new Error("createTaskTool requires at least one subagent");
|
|
44
44
|
}
|
|
45
|
-
const names = subagents.map((s) => s.
|
|
45
|
+
const names = subagents.map((s) => s.agentName);
|
|
46
46
|
return {
|
|
47
47
|
name: SUBAGENT_TOOL,
|
|
48
48
|
description: buildSubagentDescription(subagents),
|
|
@@ -54,15 +54,15 @@ function createSubagentTool(subagents) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
function createSubagentHandler(subagents) {
|
|
57
|
-
const {
|
|
57
|
+
const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
|
|
58
58
|
return async (args) => {
|
|
59
|
-
const config = subagents.find((s) => s.
|
|
59
|
+
const config = subagents.find((s) => s.agentName === args.subagent);
|
|
60
60
|
if (!config) {
|
|
61
61
|
throw new Error(
|
|
62
|
-
`Unknown subagent: ${args.subagent}. Available: ${subagents.map((s) => s.
|
|
62
|
+
`Unknown subagent: ${args.subagent}. Available: ${subagents.map((s) => s.agentName).join(", ")}`
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
|
-
const childWorkflowId = `${
|
|
65
|
+
const childWorkflowId = `${args.subagent}-${workflow.uuid4()}`;
|
|
66
66
|
const input = {
|
|
67
67
|
prompt: args.prompt,
|
|
68
68
|
...config.context && { context: config.context }
|
|
@@ -90,31 +90,36 @@ function createToolRouter(options) {
|
|
|
90
90
|
}
|
|
91
91
|
const isEnabled = (tool) => tool.enabled !== false;
|
|
92
92
|
if (options.subagents) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
93
|
+
const enabledSubagents = options.subagents.filter(
|
|
94
|
+
(s) => s.enabled !== false
|
|
95
|
+
);
|
|
96
|
+
if (enabledSubagents.length > 0) {
|
|
97
|
+
const subagentHooksMap = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const s of enabledSubagents) {
|
|
99
|
+
if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
|
|
100
|
+
}
|
|
101
|
+
const resolveSubagentName = (args) => args.subagent;
|
|
102
|
+
toolMap.set("Subagent", {
|
|
103
|
+
...createSubagentTool(enabledSubagents),
|
|
104
|
+
handler: createSubagentHandler(enabledSubagents),
|
|
105
|
+
...subagentHooksMap.size > 0 && {
|
|
106
|
+
hooks: {
|
|
107
|
+
onPreToolUse: async (ctx) => {
|
|
108
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
109
|
+
return hooks?.onPreExecution?.(ctx) ?? {};
|
|
110
|
+
},
|
|
111
|
+
onPostToolUse: async (ctx) => {
|
|
112
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
113
|
+
await hooks?.onPostExecution?.(ctx);
|
|
114
|
+
},
|
|
115
|
+
onPostToolUseFailure: async (ctx) => {
|
|
116
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
117
|
+
return hooks?.onExecutionFailure?.(ctx) ?? {};
|
|
118
|
+
}
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
118
123
|
}
|
|
119
124
|
async function processToolCall(toolCall, turn, handlerContext) {
|
|
120
125
|
const startTime = Date.now();
|
|
@@ -385,7 +390,11 @@ function withAutoAppend(threadHandler, handler) {
|
|
|
385
390
|
toolName,
|
|
386
391
|
content: response.toolResponse
|
|
387
392
|
});
|
|
388
|
-
return {
|
|
393
|
+
return {
|
|
394
|
+
toolResponse: "Response appended via withAutoAppend",
|
|
395
|
+
data: response.data,
|
|
396
|
+
resultAppended: true
|
|
397
|
+
};
|
|
389
398
|
};
|
|
390
399
|
}
|
|
391
400
|
function defineTool(tool) {
|
|
@@ -410,11 +419,19 @@ var createSession = async ({
|
|
|
410
419
|
subagents,
|
|
411
420
|
tools = {},
|
|
412
421
|
processToolsInParallel = true,
|
|
413
|
-
hooks = {}
|
|
422
|
+
hooks = {},
|
|
423
|
+
appendSystemPrompt = true,
|
|
424
|
+
systemPrompt
|
|
414
425
|
}) => {
|
|
426
|
+
const {
|
|
427
|
+
appendToolResult,
|
|
428
|
+
appendHumanMessage,
|
|
429
|
+
initializeThread,
|
|
430
|
+
appendSystemMessage
|
|
431
|
+
} = threadOps ?? proxyDefaultThreadOps();
|
|
415
432
|
const toolRouter = createToolRouter({
|
|
416
433
|
tools,
|
|
417
|
-
appendToolResult
|
|
434
|
+
appendToolResult,
|
|
418
435
|
threadId,
|
|
419
436
|
hooks,
|
|
420
437
|
subagents,
|
|
@@ -441,8 +458,11 @@ var createSession = async ({
|
|
|
441
458
|
});
|
|
442
459
|
}
|
|
443
460
|
stateManager.setTools(toolRouter.getToolDefinitions());
|
|
444
|
-
await
|
|
445
|
-
|
|
461
|
+
await initializeThread(threadId);
|
|
462
|
+
if (appendSystemPrompt && systemPrompt && systemPrompt.trim() !== "") {
|
|
463
|
+
await appendSystemMessage(threadId, systemPrompt);
|
|
464
|
+
}
|
|
465
|
+
await appendHumanMessage(threadId, await buildContextMessage());
|
|
446
466
|
let exitReason = "completed";
|
|
447
467
|
try {
|
|
448
468
|
while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
|
|
@@ -463,7 +483,7 @@ var createSession = async ({
|
|
|
463
483
|
try {
|
|
464
484
|
parsedToolCalls.push(toolRouter.parseToolCall(tc));
|
|
465
485
|
} catch (error) {
|
|
466
|
-
await
|
|
486
|
+
await appendToolResult({
|
|
467
487
|
threadId,
|
|
468
488
|
toolCallId: tc.id ?? "",
|
|
469
489
|
toolName: tc.name,
|
|
@@ -510,7 +530,8 @@ function proxyDefaultThreadOps(options) {
|
|
|
510
530
|
return {
|
|
511
531
|
initializeThread: activities.initializeThread,
|
|
512
532
|
appendHumanMessage: activities.appendHumanMessage,
|
|
513
|
-
appendToolResult: activities.appendToolResult
|
|
533
|
+
appendToolResult: activities.appendToolResult,
|
|
534
|
+
appendSystemMessage: activities.appendSystemMessage
|
|
514
535
|
};
|
|
515
536
|
}
|
|
516
537
|
|
|
@@ -1057,6 +1078,12 @@ function createThreadManager(config) {
|
|
|
1057
1078
|
content
|
|
1058
1079
|
}).toDict();
|
|
1059
1080
|
},
|
|
1081
|
+
createSystemMessage(content) {
|
|
1082
|
+
return new messages.SystemMessage({
|
|
1083
|
+
id: v4_default(),
|
|
1084
|
+
content
|
|
1085
|
+
}).toDict();
|
|
1086
|
+
},
|
|
1060
1087
|
createAIMessage(content, kwargs) {
|
|
1061
1088
|
return new messages.AIMessage({
|
|
1062
1089
|
id: v4_default(),
|
|
@@ -1085,6 +1112,10 @@ function createThreadManager(config) {
|
|
|
1085
1112
|
async appendAIMessage(content) {
|
|
1086
1113
|
const message = helpers.createAIMessage(content);
|
|
1087
1114
|
await base.append([message]);
|
|
1115
|
+
},
|
|
1116
|
+
async appendSystemMessage(content) {
|
|
1117
|
+
const message = helpers.createSystemMessage(content);
|
|
1118
|
+
await base.append([message]);
|
|
1088
1119
|
}
|
|
1089
1120
|
};
|
|
1090
1121
|
return Object.assign(base, helpers);
|
|
@@ -1109,6 +1140,10 @@ function createSharedActivities(redis) {
|
|
|
1109
1140
|
async appendHumanMessage(threadId, content) {
|
|
1110
1141
|
const thread = createThreadManager({ redis, threadId });
|
|
1111
1142
|
await thread.appendHumanMessage(content);
|
|
1143
|
+
},
|
|
1144
|
+
async appendSystemMessage(threadId, content) {
|
|
1145
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1146
|
+
await thread.appendSystemMessage(content);
|
|
1112
1147
|
}
|
|
1113
1148
|
};
|
|
1114
1149
|
}
|