zeitlich 0.2.2 → 0.2.4
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 +34 -31
- package/dist/index.cjs +330 -399
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -43
- package/dist/index.d.ts +24 -43
- package/dist/index.js +301 -373
- package/dist/index.js.map +1 -1
- package/dist/{workflow-BQf5EfNN.d.cts → workflow-PjeURKw4.d.cts} +265 -258
- package/dist/{workflow-BQf5EfNN.d.ts → workflow-PjeURKw4.d.ts} +265 -258
- package/dist/workflow.cjs +223 -281
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +2 -3
- package/dist/workflow.d.ts +2 -3
- package/dist/workflow.js +198 -258
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -2
- package/src/activities.ts +0 -32
- package/src/index.ts +14 -11
- package/src/lib/model-invoker.ts +7 -1
- package/src/lib/session.ts +54 -109
- package/src/lib/thread-manager.ts +45 -37
- package/src/lib/tool-router.ts +148 -108
- package/src/lib/types.ts +35 -26
- package/src/tools/ask-user-question/handler.ts +5 -5
- package/src/tools/ask-user-question/tool.ts +3 -2
- package/src/tools/bash/bash.test.ts +12 -12
- package/src/tools/bash/handler.ts +5 -5
- package/src/tools/bash/tool.ts +3 -2
- package/src/tools/edit/handler.ts +78 -123
- package/src/tools/edit/tool.ts +3 -2
- package/src/tools/glob/handler.ts +17 -48
- package/src/tools/glob/tool.ts +3 -2
- package/src/tools/grep/tool.ts +3 -2
- package/src/tools/{read → read-file}/tool.ts +3 -2
- package/src/tools/{task → subagent}/handler.ts +18 -31
- package/src/tools/{task → subagent}/tool.ts +13 -20
- package/src/tools/task-create/handler.ts +5 -11
- package/src/tools/task-create/tool.ts +3 -2
- package/src/tools/task-get/handler.ts +5 -10
- package/src/tools/task-get/tool.ts +3 -2
- package/src/tools/task-list/handler.ts +5 -10
- package/src/tools/task-list/tool.ts +3 -2
- package/src/tools/task-update/handler.ts +5 -12
- package/src/tools/task-update/tool.ts +3 -2
- package/src/tools/{write → write-file}/tool.ts +5 -6
- package/src/workflow.ts +24 -21
package/src/lib/tool-router.ts
CHANGED
|
@@ -9,17 +9,11 @@ import type {
|
|
|
9
9
|
ToolHooks,
|
|
10
10
|
ToolResultConfig,
|
|
11
11
|
} from "./types";
|
|
12
|
-
import type {
|
|
12
|
+
import type { SubagentArgs } from "../tools/subagent/tool";
|
|
13
13
|
|
|
14
14
|
import type { z } from "zod";
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import { createTaskTool } from "../tools/task/tool";
|
|
18
|
-
import { createTaskHandler } from "../tools/task/handler";
|
|
19
|
-
import type { createTaskCreateHandler } from "../tools/task-create/handler";
|
|
20
|
-
import { bashTool, createBashToolDescription } from "../tools/bash/tool";
|
|
21
|
-
import { taskCreateTool } from "../tools/task-create/tool";
|
|
22
|
-
import type { handleBashTool } from "../tools/bash/handler";
|
|
15
|
+
import { createSubagentTool } from "../tools/subagent/tool";
|
|
16
|
+
import { createSubagentHandler } from "../tools/subagent/handler";
|
|
23
17
|
|
|
24
18
|
export type { ToolMessageContent };
|
|
25
19
|
|
|
@@ -58,12 +52,19 @@ export interface ToolWithHandler<
|
|
|
58
52
|
handler: ToolHandler<z.infer<TSchema>, TResult, TContext>;
|
|
59
53
|
strict?: boolean;
|
|
60
54
|
max_uses?: number;
|
|
55
|
+
/** Whether this tool is available to the agent (default: true). Disabled tools are excluded from definitions and rejected at parse time. */
|
|
56
|
+
enabled?: boolean;
|
|
61
57
|
/** Per-tool lifecycle hooks (run in addition to global hooks) */
|
|
62
58
|
hooks?: ToolHooks<z.infer<TSchema>, TResult>;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
/**
|
|
66
62
|
* A map of tool keys to tool definitions with handlers.
|
|
63
|
+
*
|
|
64
|
+
* Handler uses `any` intentionally — this is a type-system boundary where heterogeneous
|
|
65
|
+
* tool types are stored together. Type safety for individual tools is enforced by
|
|
66
|
+
* `defineTool()` at the definition site and generic inference utilities like
|
|
67
|
+
* `InferToolResults<T>` at the consumption site.
|
|
67
68
|
*/
|
|
68
69
|
export type ToolMap = Record<
|
|
69
70
|
string,
|
|
@@ -71,17 +72,13 @@ export type ToolMap = Record<
|
|
|
71
72
|
name: string;
|
|
72
73
|
description: string;
|
|
73
74
|
schema: z.ZodType;
|
|
74
|
-
|
|
75
|
-
handler:
|
|
76
|
-
args: any,
|
|
77
|
-
context: any
|
|
78
|
-
) => ToolHandlerResponse<any> | Promise<ToolHandlerResponse<any>>;
|
|
79
|
-
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
handler: ToolHandler<any, any, any>;
|
|
80
77
|
strict?: boolean;
|
|
81
78
|
max_uses?: number;
|
|
82
|
-
|
|
79
|
+
enabled?: boolean;
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
83
81
|
hooks?: ToolHooks<any, any>;
|
|
84
|
-
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
85
82
|
}
|
|
86
83
|
>;
|
|
87
84
|
|
|
@@ -140,39 +137,30 @@ export type AppendToolResultFn = (config: ToolResultConfig) => Promise<void>;
|
|
|
140
137
|
/**
|
|
141
138
|
* The response from a tool handler.
|
|
142
139
|
* Contains the content for the tool message and the result to return from processToolCalls.
|
|
140
|
+
*
|
|
141
|
+
* Tools that don't return additional data should use `data: null` (TResult defaults to null).
|
|
142
|
+
* Tools that may fail to produce data should type TResult as `SomeType | null`.
|
|
143
143
|
*/
|
|
144
|
-
export interface ToolHandlerResponse<TResult> {
|
|
144
|
+
export interface ToolHandlerResponse<TResult = null> {
|
|
145
145
|
/** Content sent back to the LLM as the tool call response */
|
|
146
146
|
toolResponse: ToolMessageContent;
|
|
147
147
|
/** Data returned to the workflow and hooks for further processing */
|
|
148
|
-
data: TResult
|
|
148
|
+
data: TResult;
|
|
149
|
+
/**
|
|
150
|
+
* When true, the tool result has already been appended to the thread
|
|
151
|
+
* by the handler itself (e.g. via `withAutoAppend`), so the router
|
|
152
|
+
* will skip the `appendToolResult` call. This avoids sending large
|
|
153
|
+
* payloads through Temporal's activity payload limit.
|
|
154
|
+
*/
|
|
155
|
+
resultAppended?: boolean;
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
/**
|
|
152
159
|
* Context passed to tool handlers for additional data beyond tool args.
|
|
153
160
|
* Use this to pass workflow state like file trees, user context, etc.
|
|
161
|
+
* Generic so callers can type the context shape, e.g. ToolHandlerContext<ControlTestFsParams>.
|
|
154
162
|
*/
|
|
155
|
-
export
|
|
156
|
-
/** Additional context data - define your own shape */
|
|
157
|
-
[key: string]: unknown;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface BuildInToolDefinitions {
|
|
161
|
-
[bashTool.name]: typeof bashTool & {
|
|
162
|
-
handler: ReturnType<typeof handleBashTool>;
|
|
163
|
-
};
|
|
164
|
-
[taskCreateTool.name]: typeof taskCreateTool & {
|
|
165
|
-
handler: ReturnType<typeof createTaskCreateHandler>;
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export const buildIntoolDefinitions: Record<
|
|
170
|
-
keyof BuildInToolDefinitions,
|
|
171
|
-
ToolDefinition
|
|
172
|
-
> = {
|
|
173
|
-
[bashTool.name]: bashTool,
|
|
174
|
-
[taskCreateTool.name]: taskCreateTool,
|
|
175
|
-
};
|
|
163
|
+
export type ToolHandlerContext<T = Record<string, unknown>> = T;
|
|
176
164
|
|
|
177
165
|
/**
|
|
178
166
|
* A handler function for a specific tool.
|
|
@@ -193,7 +181,7 @@ export type ToolHandler<TArgs, TResult, TContext = ToolHandlerContext> = (
|
|
|
193
181
|
* ```typescript
|
|
194
182
|
* // Filesystem handler with context
|
|
195
183
|
* const readHandler: ActivityToolHandler<
|
|
196
|
-
*
|
|
184
|
+
* FileReadArgs,
|
|
197
185
|
* ReadResult,
|
|
198
186
|
* { scopedNodes: FileNode[]; provider: FileSystemProvider }
|
|
199
187
|
* > = async (args, context) => {
|
|
@@ -239,15 +227,13 @@ export interface ToolCallResult<
|
|
|
239
227
|
> {
|
|
240
228
|
toolCallId: string;
|
|
241
229
|
name: TName;
|
|
242
|
-
data: TResult
|
|
230
|
+
data: TResult;
|
|
243
231
|
}
|
|
244
232
|
|
|
245
233
|
/**
|
|
246
234
|
* Options for creating a tool router.
|
|
247
235
|
*/
|
|
248
236
|
export interface ToolRouterOptions<T extends ToolMap> {
|
|
249
|
-
/** File tree for the agent */
|
|
250
|
-
fileTree: string;
|
|
251
237
|
/** Map of tools with their handlers */
|
|
252
238
|
tools: T;
|
|
253
239
|
/** Thread ID for appending tool results */
|
|
@@ -260,10 +246,6 @@ export interface ToolRouterOptions<T extends ToolMap> {
|
|
|
260
246
|
hooks?: Hooks<T, ToolCallResultUnion<InferToolResults<T>>>;
|
|
261
247
|
/** Subagent configurations */
|
|
262
248
|
subagents?: SubagentConfig[];
|
|
263
|
-
/** Build in tools - accepts raw handlers or proxied activities */
|
|
264
|
-
buildInTools?: {
|
|
265
|
-
[K in keyof BuildInToolDefinitions]?: BuildInToolDefinitions[K]["handler"];
|
|
266
|
-
};
|
|
267
249
|
}
|
|
268
250
|
|
|
269
251
|
/**
|
|
@@ -419,15 +401,7 @@ export interface ToolRouter<T extends ToolMap> {
|
|
|
419
401
|
export function createToolRouter<T extends ToolMap>(
|
|
420
402
|
options: ToolRouterOptions<T>
|
|
421
403
|
): ToolRouter<T> {
|
|
422
|
-
const { appendToolResult } =
|
|
423
|
-
startToCloseTimeout: "2m",
|
|
424
|
-
retry: {
|
|
425
|
-
maximumAttempts: 3,
|
|
426
|
-
initialInterval: "5s",
|
|
427
|
-
maximumInterval: "15m",
|
|
428
|
-
backoffCoefficient: 4,
|
|
429
|
-
},
|
|
430
|
-
});
|
|
404
|
+
const { appendToolResult } = options;
|
|
431
405
|
type TResults = InferToolResults<T>;
|
|
432
406
|
|
|
433
407
|
// Build internal lookup map by tool name
|
|
@@ -437,6 +411,9 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
437
411
|
toolMap.set(tool.name, tool as T[keyof T]);
|
|
438
412
|
}
|
|
439
413
|
|
|
414
|
+
/** Check if a tool is enabled (defaults to true when not specified) */
|
|
415
|
+
const isEnabled = (tool: ToolMap[string]): boolean => tool.enabled !== false;
|
|
416
|
+
|
|
440
417
|
if (options.subagents) {
|
|
441
418
|
// Build per-subagent hook dispatcher keyed by subagent name
|
|
442
419
|
const subagentHooksMap = new Map<string, SubagentHooks>();
|
|
@@ -445,11 +422,11 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
445
422
|
}
|
|
446
423
|
|
|
447
424
|
const resolveSubagentName = (args: unknown): string =>
|
|
448
|
-
(args as
|
|
425
|
+
(args as SubagentArgs).subagent;
|
|
449
426
|
|
|
450
|
-
toolMap.set("
|
|
451
|
-
...
|
|
452
|
-
handler:
|
|
427
|
+
toolMap.set("Subagent", {
|
|
428
|
+
...createSubagentTool(options.subagents),
|
|
429
|
+
handler: createSubagentHandler(options.subagents),
|
|
453
430
|
...(subagentHooksMap.size > 0 && {
|
|
454
431
|
hooks: {
|
|
455
432
|
onPreToolUse: async (ctx): Promise<PreToolUseHookResult> => {
|
|
@@ -471,25 +448,6 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
471
448
|
});
|
|
472
449
|
}
|
|
473
450
|
|
|
474
|
-
if (options.buildInTools) {
|
|
475
|
-
for (const [key, value] of Object.entries(options.buildInTools)) {
|
|
476
|
-
if (key === bashTool.name) {
|
|
477
|
-
toolMap.set(key, {
|
|
478
|
-
...buildIntoolDefinitions[key as keyof BuildInToolDefinitions],
|
|
479
|
-
description: createBashToolDescription({
|
|
480
|
-
fileTree: options.fileTree,
|
|
481
|
-
}),
|
|
482
|
-
handler: value,
|
|
483
|
-
});
|
|
484
|
-
} else {
|
|
485
|
-
toolMap.set(key, {
|
|
486
|
-
...buildIntoolDefinitions[key as keyof BuildInToolDefinitions],
|
|
487
|
-
handler: value,
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
451
|
async function processToolCall(
|
|
494
452
|
toolCall: ParsedToolCallUnion<T>,
|
|
495
453
|
turn: number,
|
|
@@ -511,6 +469,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
511
469
|
await appendToolResult({
|
|
512
470
|
threadId: options.threadId,
|
|
513
471
|
toolCallId: toolCall.id,
|
|
472
|
+
toolName: toolCall.name,
|
|
514
473
|
content: JSON.stringify({
|
|
515
474
|
skipped: true,
|
|
516
475
|
reason: "Skipped by PreToolUse hook",
|
|
@@ -532,6 +491,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
532
491
|
await appendToolResult({
|
|
533
492
|
threadId: options.threadId,
|
|
534
493
|
toolCallId: toolCall.id,
|
|
494
|
+
toolName: toolCall.name,
|
|
535
495
|
content: JSON.stringify({
|
|
536
496
|
skipped: true,
|
|
537
497
|
reason: "Skipped by tool PreToolUse hook",
|
|
@@ -547,15 +507,23 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
547
507
|
// --- Execute handler ---
|
|
548
508
|
let result: unknown;
|
|
549
509
|
let content!: ToolMessageContent;
|
|
510
|
+
let resultAppended = false;
|
|
550
511
|
|
|
551
512
|
try {
|
|
552
513
|
if (tool) {
|
|
514
|
+
const enrichedContext = {
|
|
515
|
+
...(handlerContext ?? {}),
|
|
516
|
+
threadId: options.threadId,
|
|
517
|
+
toolCallId: toolCall.id,
|
|
518
|
+
toolName: toolCall.name,
|
|
519
|
+
};
|
|
553
520
|
const response = await tool.handler(
|
|
554
521
|
effectiveArgs as Parameters<typeof tool.handler>[0],
|
|
555
|
-
|
|
522
|
+
enrichedContext as Parameters<typeof tool.handler>[1]
|
|
556
523
|
);
|
|
557
524
|
result = response.data;
|
|
558
525
|
content = response.toolResponse;
|
|
526
|
+
resultAppended = response.resultAppended === true;
|
|
559
527
|
} else {
|
|
560
528
|
result = { error: `Unknown tool: ${toolCall.name}` };
|
|
561
529
|
content = JSON.stringify(result, null, 2);
|
|
@@ -606,12 +574,15 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
606
574
|
}
|
|
607
575
|
}
|
|
608
576
|
|
|
609
|
-
// Automatically append tool result to thread
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
577
|
+
// Automatically append tool result to thread (unless handler already did)
|
|
578
|
+
if (!resultAppended) {
|
|
579
|
+
await appendToolResult({
|
|
580
|
+
threadId: options.threadId,
|
|
581
|
+
toolCallId: toolCall.id,
|
|
582
|
+
toolName: toolCall.name,
|
|
583
|
+
content,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
615
586
|
|
|
616
587
|
const toolResult = {
|
|
617
588
|
toolCallId: toolCall.id,
|
|
@@ -647,13 +618,13 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
647
618
|
// --- Methods from registry ---
|
|
648
619
|
|
|
649
620
|
hasTools(): boolean {
|
|
650
|
-
return toolMap.
|
|
621
|
+
return Array.from(toolMap.values()).some(isEnabled);
|
|
651
622
|
},
|
|
652
623
|
|
|
653
624
|
parseToolCall(toolCall: RawToolCall): ParsedToolCallUnion<T> {
|
|
654
625
|
const tool = toolMap.get(toolCall.name);
|
|
655
626
|
|
|
656
|
-
if (!tool) {
|
|
627
|
+
if (!tool || !isEnabled(tool)) {
|
|
657
628
|
throw new Error(`Tool ${toolCall.name} not found`);
|
|
658
629
|
}
|
|
659
630
|
|
|
@@ -668,21 +639,26 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
668
639
|
},
|
|
669
640
|
|
|
670
641
|
hasTool(name: string): boolean {
|
|
671
|
-
|
|
642
|
+
const tool = toolMap.get(name);
|
|
643
|
+
return tool !== undefined && isEnabled(tool);
|
|
672
644
|
},
|
|
673
645
|
|
|
674
646
|
getToolNames(): ToolNames<T>[] {
|
|
675
|
-
return Array.from(toolMap.
|
|
647
|
+
return Array.from(toolMap.entries())
|
|
648
|
+
.filter(([, tool]) => isEnabled(tool))
|
|
649
|
+
.map(([name]) => name) as ToolNames<T>[];
|
|
676
650
|
},
|
|
677
651
|
|
|
678
652
|
getToolDefinitions(): ToolDefinition[] {
|
|
679
|
-
return Array.from(toolMap)
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
653
|
+
return Array.from(toolMap)
|
|
654
|
+
.filter(([, tool]) => isEnabled(tool))
|
|
655
|
+
.map(([name, tool]) => ({
|
|
656
|
+
name,
|
|
657
|
+
description: tool.description,
|
|
658
|
+
schema: tool.schema,
|
|
659
|
+
strict: tool.strict,
|
|
660
|
+
max_uses: tool.max_uses,
|
|
661
|
+
}));
|
|
686
662
|
},
|
|
687
663
|
|
|
688
664
|
// --- Methods for processing tool calls ---
|
|
@@ -740,22 +716,31 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
740
716
|
const processOne = async (
|
|
741
717
|
toolCall: ParsedToolCallUnion<T>
|
|
742
718
|
): Promise<ToolCallResult<TName, TResult>> => {
|
|
719
|
+
const enrichedContext = {
|
|
720
|
+
...(handlerContext ?? {}),
|
|
721
|
+
threadId: options.threadId,
|
|
722
|
+
toolCallId: toolCall.id,
|
|
723
|
+
toolName: toolCall.name as TName,
|
|
724
|
+
} as TContext;
|
|
743
725
|
const response = await handler(
|
|
744
726
|
toolCall.args as ToolArgs<T, TName>,
|
|
745
|
-
|
|
727
|
+
enrichedContext
|
|
746
728
|
);
|
|
747
729
|
|
|
748
|
-
// Automatically append tool result to thread
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
730
|
+
// Automatically append tool result to thread (unless handler already did)
|
|
731
|
+
if (!response.resultAppended) {
|
|
732
|
+
await appendToolResult({
|
|
733
|
+
threadId: options.threadId,
|
|
734
|
+
toolCallId: toolCall.id,
|
|
735
|
+
toolName: toolCall.name,
|
|
736
|
+
content: response.toolResponse,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
754
739
|
|
|
755
740
|
return {
|
|
756
741
|
toolCallId: toolCall.id,
|
|
757
742
|
name: toolCall.name as TName,
|
|
758
|
-
data: response.data
|
|
743
|
+
data: response.data,
|
|
759
744
|
};
|
|
760
745
|
};
|
|
761
746
|
|
|
@@ -801,6 +786,61 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
801
786
|
};
|
|
802
787
|
}
|
|
803
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Wraps a tool handler to automatically append its result directly to the
|
|
791
|
+
* thread and sets `resultAppended: true` on the response.
|
|
792
|
+
*
|
|
793
|
+
* Use this for tools whose responses may exceed Temporal's activity payload
|
|
794
|
+
* limit. The wrapper appends to the thread inside the activity (where Redis
|
|
795
|
+
* is available), then replaces `toolResponse` with an empty string so the
|
|
796
|
+
* large payload never travels through the Temporal workflow boundary.
|
|
797
|
+
*
|
|
798
|
+
* @param getThread - Factory that returns a thread manager for the given threadId
|
|
799
|
+
* @param handler - The original tool handler
|
|
800
|
+
* @returns A wrapped handler that auto-appends and flags the response
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```typescript
|
|
804
|
+
* import { withAutoAppend } from '@bead-ai/zeitlich/workflow';
|
|
805
|
+
* import { createThreadManager } from '@bead-ai/zeitlich';
|
|
806
|
+
*
|
|
807
|
+
* const handler = withAutoAppend(
|
|
808
|
+
* (threadId) => createThreadManager({ redis, threadId }),
|
|
809
|
+
* async (args, ctx) => ({
|
|
810
|
+
* toolResponse: JSON.stringify(largeResult), // appended directly to Redis
|
|
811
|
+
* data: { summary: "..." }, // small data for workflow
|
|
812
|
+
* }),
|
|
813
|
+
* );
|
|
814
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
export function withAutoAppend<
|
|
817
|
+
TArgs,
|
|
818
|
+
TResult,
|
|
819
|
+
TContext extends ToolHandlerContext = ToolHandlerContext,
|
|
820
|
+
>(
|
|
821
|
+
threadHandler: (config: ToolResultConfig) => Promise<void>,
|
|
822
|
+
handler: ActivityToolHandler<TArgs, TResult, TContext>
|
|
823
|
+
): ActivityToolHandler<TArgs, TResult, TContext> {
|
|
824
|
+
return async (args: TArgs, context: TContext) => {
|
|
825
|
+
const response = await handler(args, context);
|
|
826
|
+
const threadId = (context as Record<string, unknown>).threadId as string;
|
|
827
|
+
const toolCallId = (context as Record<string, unknown>)
|
|
828
|
+
.toolCallId as string;
|
|
829
|
+
const toolName = (context as Record<string, unknown>).toolName as string;
|
|
830
|
+
|
|
831
|
+
// Append directly (inside the activity, bypassing Temporal payload)
|
|
832
|
+
await threadHandler({
|
|
833
|
+
threadId,
|
|
834
|
+
toolCallId,
|
|
835
|
+
toolName,
|
|
836
|
+
content: response.toolResponse,
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// Return with empty toolResponse to keep the Temporal payload small
|
|
840
|
+
return { toolResponse: "", data: response.data, resultAppended: true };
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
804
844
|
/**
|
|
805
845
|
* Identity function that creates a generic inference context for a tool definition.
|
|
806
846
|
* TypeScript infers TResult from the handler and flows it to hooks automatically.
|
|
@@ -872,7 +912,7 @@ export function defineSubagent<
|
|
|
872
912
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
873
913
|
| ((input: { prompt: string; context: TContext }) => Promise<any>);
|
|
874
914
|
context: TContext;
|
|
875
|
-
hooks?: SubagentHooks<
|
|
915
|
+
hooks?: SubagentHooks<SubagentArgs, z.infer<TResult>>;
|
|
876
916
|
}
|
|
877
917
|
): SubagentConfig<TResult>;
|
|
878
918
|
// Without context — verifies workflow accepts { prompt }
|
|
@@ -880,7 +920,7 @@ export function defineSubagent<TResult extends z.ZodType = z.ZodType>(
|
|
|
880
920
|
config: Omit<SubagentConfig<TResult>, "hooks" | "workflow"> & {
|
|
881
921
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
882
922
|
workflow: string | ((input: { prompt: string }) => Promise<any>);
|
|
883
|
-
hooks?: SubagentHooks<
|
|
923
|
+
hooks?: SubagentHooks<SubagentArgs, z.infer<TResult>>;
|
|
884
924
|
}
|
|
885
925
|
): SubagentConfig<TResult>;
|
|
886
926
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/lib/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ToolMessageContent } from "./thread-manager";
|
|
2
2
|
import type {
|
|
3
|
-
BuildInToolDefinitions,
|
|
4
3
|
InferToolResults,
|
|
5
4
|
ParsedToolCallUnion,
|
|
5
|
+
RawToolCall,
|
|
6
6
|
ToolCallResultUnion,
|
|
7
7
|
ToolMap,
|
|
8
8
|
} from "./tool-router";
|
|
@@ -30,6 +30,7 @@ export interface BaseAgentState {
|
|
|
30
30
|
version: number;
|
|
31
31
|
turns: number;
|
|
32
32
|
tasks: Map<string, WorkflowTask>;
|
|
33
|
+
systemPrompt: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -51,9 +52,9 @@ export interface AgentFile {
|
|
|
51
52
|
/**
|
|
52
53
|
* Agent response from LLM invocation
|
|
53
54
|
*/
|
|
54
|
-
export interface AgentResponse {
|
|
55
|
-
message:
|
|
56
|
-
|
|
55
|
+
export interface AgentResponse<M = StoredMessage> {
|
|
56
|
+
message: M;
|
|
57
|
+
rawToolCalls: RawToolCall[];
|
|
57
58
|
usage?: {
|
|
58
59
|
input_tokens?: number;
|
|
59
60
|
output_tokens?: number;
|
|
@@ -61,17 +62,35 @@ export interface AgentResponse {
|
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Thread operations required by a session.
|
|
67
|
+
* Consumers provide these — typically by wrapping Temporal activities.
|
|
68
|
+
* Use `proxyDefaultThreadOps()` for the default StoredMessage implementation.
|
|
69
|
+
*/
|
|
70
|
+
export interface ThreadOps {
|
|
71
|
+
/** Initialize an empty thread */
|
|
72
|
+
initializeThread(threadId: string): Promise<void>;
|
|
73
|
+
/** Append a human message to the thread */
|
|
74
|
+
appendHumanMessage(
|
|
75
|
+
threadId: string,
|
|
76
|
+
content: string | MessageContent
|
|
77
|
+
): Promise<void>;
|
|
78
|
+
/** Append a tool result to the thread */
|
|
79
|
+
appendToolResult(config: ToolResultConfig): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
|
|
64
82
|
/**
|
|
65
83
|
* Configuration for a Zeitlich agent session
|
|
66
84
|
*/
|
|
67
|
-
export interface ZeitlichAgentConfig<T extends ToolMap> {
|
|
68
|
-
buildFileTree?: () => Promise<string>;
|
|
85
|
+
export interface ZeitlichAgentConfig<T extends ToolMap, M = StoredMessage> {
|
|
69
86
|
threadId: string;
|
|
70
87
|
agentName: string;
|
|
71
88
|
metadata?: Record<string, unknown>;
|
|
72
89
|
maxTurns?: number;
|
|
73
90
|
/** Workflow-specific runAgent activity (with tools pre-bound) */
|
|
74
|
-
runAgent: RunAgentActivity
|
|
91
|
+
runAgent: RunAgentActivity<M>;
|
|
92
|
+
/** Thread operations (initialize, append messages, parse tool calls) */
|
|
93
|
+
threadOps: ThreadOps;
|
|
75
94
|
/** Tool router for processing tool calls (optional if agent has no tools) */
|
|
76
95
|
tools?: T;
|
|
77
96
|
/** Subagent configurations */
|
|
@@ -80,27 +99,11 @@ export interface ZeitlichAgentConfig<T extends ToolMap> {
|
|
|
80
99
|
hooks?: Hooks<T, ToolCallResultUnion<InferToolResults<T>>>;
|
|
81
100
|
/** Whether to process tools in parallel */
|
|
82
101
|
processToolsInParallel?: boolean;
|
|
83
|
-
/**
|
|
84
|
-
* Base system prompt (e.g., Auditron identity).
|
|
85
|
-
* Can be a static string or async function.
|
|
86
|
-
*/
|
|
87
|
-
baseSystemPrompt: string | (() => string | Promise<string>);
|
|
88
|
-
/**
|
|
89
|
-
* Agent-specific instructions prompt.
|
|
90
|
-
* Can be a static string or async function.
|
|
91
|
-
*/
|
|
92
|
-
instructionsPrompt: string | (() => string | Promise<string>);
|
|
93
102
|
/**
|
|
94
103
|
* Build context message content from agent-specific context.
|
|
95
104
|
* Returns MessageContent array for the initial HumanMessage.
|
|
96
105
|
*/
|
|
97
106
|
buildContextMessage: () => MessageContent | Promise<MessageContent>;
|
|
98
|
-
/**
|
|
99
|
-
* Build in tools - accepts raw handlers or proxied activities
|
|
100
|
-
*/
|
|
101
|
-
buildInTools?: {
|
|
102
|
-
[K in keyof BuildInToolDefinitions]?: BuildInToolDefinitions[K]["handler"];
|
|
103
|
-
};
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/**
|
|
@@ -128,15 +131,17 @@ export interface RunAgentConfig {
|
|
|
128
131
|
/**
|
|
129
132
|
* Type signature for workflow-specific runAgent activity
|
|
130
133
|
*/
|
|
131
|
-
export type RunAgentActivity = (
|
|
134
|
+
export type RunAgentActivity<M = StoredMessage> = (
|
|
132
135
|
config: RunAgentConfig
|
|
133
|
-
) => Promise<AgentResponse
|
|
136
|
+
) => Promise<AgentResponse<M>>;
|
|
134
137
|
/**
|
|
135
138
|
* Configuration for appending a tool result
|
|
136
139
|
*/
|
|
137
140
|
export interface ToolResultConfig {
|
|
138
141
|
threadId: string;
|
|
139
142
|
toolCallId: string;
|
|
143
|
+
/** The name of the tool that produced this result */
|
|
144
|
+
toolName: string;
|
|
140
145
|
/** Content for the tool message (string or complex content parts) */
|
|
141
146
|
content: ToolMessageContent;
|
|
142
147
|
}
|
|
@@ -145,6 +150,10 @@ export interface ToolResultConfig {
|
|
|
145
150
|
// Subagent Configuration
|
|
146
151
|
// ============================================================================
|
|
147
152
|
|
|
153
|
+
/** Infer the z.infer'd result type from a SubagentConfig, or null if no schema */
|
|
154
|
+
export type InferSubagentResult<T extends SubagentConfig> =
|
|
155
|
+
T extends SubagentConfig<infer S> ? z.infer<S> : null;
|
|
156
|
+
|
|
148
157
|
/**
|
|
149
158
|
* Configuration for a subagent that can be spawned by the parent workflow.
|
|
150
159
|
*
|
|
@@ -389,7 +398,7 @@ export interface ToolHooks<TArgs = unknown, TResult = unknown> {
|
|
|
389
398
|
/** Called after this tool executes successfully */
|
|
390
399
|
onPostToolUse?: (ctx: {
|
|
391
400
|
args: TArgs;
|
|
392
|
-
result: TResult
|
|
401
|
+
result: TResult;
|
|
393
402
|
threadId: string;
|
|
394
403
|
turn: number;
|
|
395
404
|
durationMs: number;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { AIMessage, type StoredMessage } from "@langchain/core/messages";
|
|
2
2
|
import type { ActivityToolHandler } from "../../lib/tool-router";
|
|
3
|
-
import type {
|
|
3
|
+
import type { AskUserQuestionArgs } from "./tool";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Creates handler for user interaction tool - creates AI messages for display.
|
|
7
7
|
*/
|
|
8
|
-
export const
|
|
9
|
-
|
|
8
|
+
export const createAskUserQuestionHandler = (): ActivityToolHandler<
|
|
9
|
+
AskUserQuestionArgs,
|
|
10
10
|
{ chatMessages: StoredMessage[] }
|
|
11
|
-
>
|
|
11
|
+
> => async (args) => {
|
|
12
12
|
const messages = args.questions.map(
|
|
13
13
|
({ question, header, options, multiSelect }) =>
|
|
14
14
|
new AIMessage({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import z from "zod";
|
|
2
|
+
import type { ToolDefinition } from "../../lib/tool-router";
|
|
2
3
|
|
|
3
4
|
export const askUserQuestionTool = {
|
|
4
5
|
name: "AskUserQuestion" as const,
|
|
@@ -39,8 +40,8 @@ Usage notes:
|
|
|
39
40
|
),
|
|
40
41
|
}),
|
|
41
42
|
strict: true,
|
|
42
|
-
};
|
|
43
|
+
} satisfies ToolDefinition;
|
|
43
44
|
|
|
44
|
-
export type
|
|
45
|
+
export type AskUserQuestionArgs = z.infer<
|
|
45
46
|
typeof askUserQuestionTool.schema
|
|
46
47
|
>;
|