zeitlich 0.2.0 → 0.2.2
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 +5 -2
- package/dist/index.cjs +268 -129
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +244 -109
- package/dist/index.js.map +1 -1
- package/dist/{workflow-uVNF7zoe.d.cts → workflow-BQf5EfNN.d.cts} +236 -55
- package/dist/{workflow-uVNF7zoe.d.ts → workflow-BQf5EfNN.d.ts} +236 -55
- package/dist/workflow.cjs +237 -107
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +4 -2
- package/dist/workflow.d.ts +4 -2
- package/dist/workflow.js +214 -86
- package/dist/workflow.js.map +1 -1
- package/package.json +6 -7
- package/src/index.ts +3 -0
- package/src/lib/session.ts +50 -24
- package/src/lib/state-manager.ts +9 -2
- package/src/lib/tool-router.ts +205 -23
- package/src/lib/types.ts +79 -4
- package/src/tools/ask-user-question/handler.ts +1 -1
- package/src/tools/bash/bash.test.ts +31 -31
- package/src/tools/bash/handler.ts +18 -9
- package/src/tools/bash/tool.ts +19 -3
- package/src/tools/edit/handler.ts +14 -14
- package/src/tools/glob/handler.ts +4 -4
- package/src/tools/task/handler.ts +17 -7
- package/src/tools/task/tool.ts +1 -1
- package/src/tools/task-create/handler.ts +7 -10
- package/src/tools/task-get/handler.ts +4 -4
- package/src/tools/task-list/handler.ts +2 -2
- package/src/tools/task-update/handler.ts +4 -4
- package/src/workflow.ts +3 -1
- package/tsup.config.ts +3 -1
package/src/lib/session.ts
CHANGED
|
@@ -114,7 +114,6 @@ export const createSession = async <T extends ToolMap>({
|
|
|
114
114
|
metadata,
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
|
-
|
|
118
117
|
stateManager.setTools(toolRouter.getToolDefinitions());
|
|
119
118
|
|
|
120
119
|
await initializeThread(threadId);
|
|
@@ -158,29 +157,56 @@ export const createSession = async <T extends ToolMap>({
|
|
|
158
157
|
}
|
|
159
158
|
|
|
160
159
|
const rawToolCalls: RawToolCall[] = await parseToolCalls(message);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
160
|
+
|
|
161
|
+
// Parse tool calls, catching schema errors and returning them to the agent
|
|
162
|
+
const parsedToolCalls: ParsedToolCallUnion<T>[] = [];
|
|
163
|
+
for (const tc of rawToolCalls.filter(
|
|
164
|
+
(tc: RawToolCall) => tc.name !== "Task"
|
|
165
|
+
)) {
|
|
166
|
+
try {
|
|
167
|
+
parsedToolCalls.push(toolRouter.parseToolCall(tc));
|
|
168
|
+
} catch (error) {
|
|
169
|
+
await appendToolResult({
|
|
170
|
+
threadId,
|
|
171
|
+
toolCallId: tc.id ?? "",
|
|
172
|
+
content: JSON.stringify({
|
|
173
|
+
error: `Invalid tool call for "${tc.name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const taskToolCalls: ParsedToolCall<
|
|
180
|
+
"Task",
|
|
181
|
+
TaskToolSchemaType<SubagentConfig[]>
|
|
182
|
+
>[] = [];
|
|
183
|
+
if (subagents && subagents.length > 0) {
|
|
184
|
+
for (const tc of rawToolCalls.filter(
|
|
185
|
+
(tc: RawToolCall) => tc.name === "Task"
|
|
186
|
+
)) {
|
|
187
|
+
try {
|
|
188
|
+
const parsedArgs = createTaskTool(subagents).schema.parse(
|
|
189
|
+
tc.args
|
|
190
|
+
);
|
|
191
|
+
taskToolCalls.push({
|
|
192
|
+
id: tc.id ?? "",
|
|
193
|
+
name: tc.name,
|
|
194
|
+
args: parsedArgs,
|
|
195
|
+
} as ParsedToolCall<
|
|
196
|
+
"Task",
|
|
197
|
+
TaskToolSchemaType<SubagentConfig[]>
|
|
198
|
+
>);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
await appendToolResult({
|
|
201
|
+
threadId,
|
|
202
|
+
toolCallId: tc.id ?? "",
|
|
203
|
+
content: JSON.stringify({
|
|
204
|
+
error: `Invalid tool call for "Task": ${error instanceof Error ? error.message : String(error)}`,
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
184
210
|
|
|
185
211
|
// Hooks can call stateManager.waitForInput() to pause the session
|
|
186
212
|
await toolRouter.processToolCalls(
|
package/src/lib/state-manager.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isTerminalStatus,
|
|
7
7
|
} from "./types";
|
|
8
8
|
import type { ToolDefinition } from "./tool-router";
|
|
9
|
+
import { z } from "zod";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* JSON primitive types that Temporal can serialize
|
|
@@ -105,7 +106,7 @@ export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
|
|
|
105
106
|
/** Delete a task by ID */
|
|
106
107
|
deleteTask(id: string): boolean;
|
|
107
108
|
|
|
108
|
-
/** Set the tools */
|
|
109
|
+
/** Set the tools (converts Zod schemas to JSON Schema for serialization) */
|
|
109
110
|
setTools(newTools: ToolDefinition[]): void;
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -245,7 +246,13 @@ export function createAgentStateManager<
|
|
|
245
246
|
},
|
|
246
247
|
|
|
247
248
|
setTools(newTools: ToolDefinition[]): void {
|
|
248
|
-
tools = newTools
|
|
249
|
+
tools = newTools.map((tool) => ({
|
|
250
|
+
name: tool.name,
|
|
251
|
+
description: tool.description,
|
|
252
|
+
schema: z.toJSONSchema(tool.schema) as Record<string, unknown>,
|
|
253
|
+
strict: tool.strict,
|
|
254
|
+
max_uses: tool.max_uses,
|
|
255
|
+
}));
|
|
249
256
|
},
|
|
250
257
|
|
|
251
258
|
deleteTask(id: string): boolean {
|
package/src/lib/tool-router.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { MessageToolDefinition } from "@langchain/core/messages";
|
|
2
2
|
import type { ToolMessageContent } from "./thread-manager";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
Hooks,
|
|
5
|
+
PostToolUseFailureHookResult,
|
|
6
|
+
PreToolUseHookResult,
|
|
7
|
+
SubagentConfig,
|
|
8
|
+
SubagentHooks,
|
|
9
|
+
ToolHooks,
|
|
10
|
+
ToolResultConfig,
|
|
11
|
+
} from "./types";
|
|
12
|
+
import type { GenericTaskToolSchemaType } from "../tools/task/tool";
|
|
4
13
|
|
|
5
14
|
import type { z } from "zod";
|
|
6
15
|
import { proxyActivities } from "@temporalio/workflow";
|
|
@@ -49,6 +58,8 @@ export interface ToolWithHandler<
|
|
|
49
58
|
handler: ToolHandler<z.infer<TSchema>, TResult, TContext>;
|
|
50
59
|
strict?: boolean;
|
|
51
60
|
max_uses?: number;
|
|
61
|
+
/** Per-tool lifecycle hooks (run in addition to global hooks) */
|
|
62
|
+
hooks?: ToolHooks<z.infer<TSchema>, TResult>;
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
/**
|
|
@@ -68,6 +79,9 @@ export type ToolMap = Record<
|
|
|
68
79
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
69
80
|
strict?: boolean;
|
|
70
81
|
max_uses?: number;
|
|
82
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
83
|
+
hooks?: ToolHooks<any, any>;
|
|
84
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
71
85
|
}
|
|
72
86
|
>;
|
|
73
87
|
|
|
@@ -128,10 +142,10 @@ export type AppendToolResultFn = (config: ToolResultConfig) => Promise<void>;
|
|
|
128
142
|
* Contains the content for the tool message and the result to return from processToolCalls.
|
|
129
143
|
*/
|
|
130
144
|
export interface ToolHandlerResponse<TResult> {
|
|
131
|
-
/** Content
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
|
|
145
|
+
/** Content sent back to the LLM as the tool call response */
|
|
146
|
+
toolResponse: ToolMessageContent;
|
|
147
|
+
/** Data returned to the workflow and hooks for further processing */
|
|
148
|
+
data: TResult | null;
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
/**
|
|
@@ -225,7 +239,7 @@ export interface ToolCallResult<
|
|
|
225
239
|
> {
|
|
226
240
|
toolCallId: string;
|
|
227
241
|
name: TName;
|
|
228
|
-
|
|
242
|
+
data: TResult | null;
|
|
229
243
|
}
|
|
230
244
|
|
|
231
245
|
/**
|
|
@@ -424,9 +438,36 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
424
438
|
}
|
|
425
439
|
|
|
426
440
|
if (options.subagents) {
|
|
441
|
+
// Build per-subagent hook dispatcher keyed by subagent name
|
|
442
|
+
const subagentHooksMap = new Map<string, SubagentHooks>();
|
|
443
|
+
for (const s of options.subagents) {
|
|
444
|
+
if (s.hooks) subagentHooksMap.set(s.name, s.hooks);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const resolveSubagentName = (args: unknown): string =>
|
|
448
|
+
(args as GenericTaskToolSchemaType).subagent;
|
|
449
|
+
|
|
427
450
|
toolMap.set("Task", {
|
|
428
451
|
...createTaskTool(options.subagents),
|
|
429
452
|
handler: createTaskHandler(options.subagents),
|
|
453
|
+
...(subagentHooksMap.size > 0 && {
|
|
454
|
+
hooks: {
|
|
455
|
+
onPreToolUse: async (ctx): Promise<PreToolUseHookResult> => {
|
|
456
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
457
|
+
return hooks?.onPreExecution?.(ctx) ?? {};
|
|
458
|
+
},
|
|
459
|
+
onPostToolUse: async (ctx): Promise<void> => {
|
|
460
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
461
|
+
await hooks?.onPostExecution?.(ctx);
|
|
462
|
+
},
|
|
463
|
+
onPostToolUseFailure: async (
|
|
464
|
+
ctx
|
|
465
|
+
): Promise<PostToolUseFailureHookResult> => {
|
|
466
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
467
|
+
return hooks?.onExecutionFailure?.(ctx) ?? {};
|
|
468
|
+
},
|
|
469
|
+
} satisfies ToolHooks,
|
|
470
|
+
}),
|
|
430
471
|
});
|
|
431
472
|
}
|
|
432
473
|
|
|
@@ -455,8 +496,10 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
455
496
|
handlerContext?: ToolHandlerContext
|
|
456
497
|
): Promise<ToolCallResultUnion<TResults> | null> {
|
|
457
498
|
const startTime = Date.now();
|
|
499
|
+
const tool = toolMap.get(toolCall.name);
|
|
500
|
+
const toolHooks = tool?.hooks;
|
|
458
501
|
|
|
459
|
-
// PreToolUse
|
|
502
|
+
// --- PreToolUse: global then per-tool ---
|
|
460
503
|
let effectiveArgs: unknown = toolCall.args;
|
|
461
504
|
if (options.hooks?.onPreToolUse) {
|
|
462
505
|
const preResult = await options.hooks.onPreToolUse({
|
|
@@ -465,7 +508,6 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
465
508
|
turn,
|
|
466
509
|
});
|
|
467
510
|
if (preResult?.skip) {
|
|
468
|
-
// Skip this tool call - append a skip message and return null
|
|
469
511
|
await appendToolResult({
|
|
470
512
|
threadId: options.threadId,
|
|
471
513
|
toolCallId: toolCall.id,
|
|
@@ -480,10 +522,31 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
480
522
|
effectiveArgs = preResult.modifiedArgs;
|
|
481
523
|
}
|
|
482
524
|
}
|
|
525
|
+
if (toolHooks?.onPreToolUse) {
|
|
526
|
+
const preResult = await toolHooks.onPreToolUse({
|
|
527
|
+
args: effectiveArgs,
|
|
528
|
+
threadId: options.threadId,
|
|
529
|
+
turn,
|
|
530
|
+
});
|
|
531
|
+
if (preResult?.skip) {
|
|
532
|
+
await appendToolResult({
|
|
533
|
+
threadId: options.threadId,
|
|
534
|
+
toolCallId: toolCall.id,
|
|
535
|
+
content: JSON.stringify({
|
|
536
|
+
skipped: true,
|
|
537
|
+
reason: "Skipped by tool PreToolUse hook",
|
|
538
|
+
}),
|
|
539
|
+
});
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
if (preResult?.modifiedArgs !== undefined) {
|
|
543
|
+
effectiveArgs = preResult.modifiedArgs;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
483
546
|
|
|
484
|
-
|
|
547
|
+
// --- Execute handler ---
|
|
485
548
|
let result: unknown;
|
|
486
|
-
let content
|
|
549
|
+
let content!: ToolMessageContent;
|
|
487
550
|
|
|
488
551
|
try {
|
|
489
552
|
if (tool) {
|
|
@@ -491,31 +554,54 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
491
554
|
effectiveArgs as Parameters<typeof tool.handler>[0],
|
|
492
555
|
(handlerContext ?? {}) as Parameters<typeof tool.handler>[1]
|
|
493
556
|
);
|
|
494
|
-
result = response.
|
|
495
|
-
content = response.
|
|
557
|
+
result = response.data;
|
|
558
|
+
content = response.toolResponse;
|
|
496
559
|
} else {
|
|
497
560
|
result = { error: `Unknown tool: ${toolCall.name}` };
|
|
498
561
|
content = JSON.stringify(result, null, 2);
|
|
499
562
|
}
|
|
500
563
|
} catch (error) {
|
|
501
|
-
// PostToolUseFailure
|
|
502
|
-
|
|
564
|
+
// --- PostToolUseFailure: per-tool then global ---
|
|
565
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
566
|
+
let recovered = false;
|
|
567
|
+
|
|
568
|
+
if (toolHooks?.onPostToolUseFailure) {
|
|
569
|
+
const failureResult = await toolHooks.onPostToolUseFailure({
|
|
570
|
+
args: effectiveArgs,
|
|
571
|
+
error: err,
|
|
572
|
+
threadId: options.threadId,
|
|
573
|
+
turn,
|
|
574
|
+
});
|
|
575
|
+
if (failureResult?.fallbackContent !== undefined) {
|
|
576
|
+
content = failureResult.fallbackContent;
|
|
577
|
+
result = { error: String(error), recovered: true };
|
|
578
|
+
recovered = true;
|
|
579
|
+
} else if (failureResult?.suppress) {
|
|
580
|
+
content = JSON.stringify({ error: String(error), suppressed: true });
|
|
581
|
+
result = { error: String(error), suppressed: true };
|
|
582
|
+
recovered = true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (!recovered && options.hooks?.onPostToolUseFailure) {
|
|
503
587
|
const failureResult = await options.hooks.onPostToolUseFailure({
|
|
504
588
|
toolCall,
|
|
505
|
-
error:
|
|
589
|
+
error: err,
|
|
506
590
|
threadId: options.threadId,
|
|
507
591
|
turn,
|
|
508
592
|
});
|
|
509
593
|
if (failureResult?.fallbackContent !== undefined) {
|
|
510
594
|
content = failureResult.fallbackContent;
|
|
511
595
|
result = { error: String(error), recovered: true };
|
|
596
|
+
recovered = true;
|
|
512
597
|
} else if (failureResult?.suppress) {
|
|
513
598
|
content = JSON.stringify({ error: String(error), suppressed: true });
|
|
514
599
|
result = { error: String(error), suppressed: true };
|
|
515
|
-
|
|
516
|
-
throw error;
|
|
600
|
+
recovered = true;
|
|
517
601
|
}
|
|
518
|
-
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (!recovered) {
|
|
519
605
|
throw error;
|
|
520
606
|
}
|
|
521
607
|
}
|
|
@@ -530,12 +616,21 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
530
616
|
const toolResult = {
|
|
531
617
|
toolCallId: toolCall.id,
|
|
532
618
|
name: toolCall.name,
|
|
533
|
-
result,
|
|
619
|
+
data: result,
|
|
534
620
|
} as ToolCallResultUnion<TResults>;
|
|
535
621
|
|
|
536
|
-
// PostToolUse
|
|
622
|
+
// --- PostToolUse: per-tool then global ---
|
|
623
|
+
const durationMs = Date.now() - startTime;
|
|
624
|
+
if (toolHooks?.onPostToolUse) {
|
|
625
|
+
await toolHooks.onPostToolUse({
|
|
626
|
+
args: effectiveArgs,
|
|
627
|
+
result: result,
|
|
628
|
+
threadId: options.threadId,
|
|
629
|
+
turn,
|
|
630
|
+
durationMs,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
537
633
|
if (options.hooks?.onPostToolUse) {
|
|
538
|
-
const durationMs = Date.now() - startTime;
|
|
539
634
|
await options.hooks.onPostToolUse({
|
|
540
635
|
toolCall,
|
|
541
636
|
result: toolResult,
|
|
@@ -654,13 +749,13 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
654
749
|
await appendToolResult({
|
|
655
750
|
threadId: options.threadId,
|
|
656
751
|
toolCallId: toolCall.id,
|
|
657
|
-
content: response.
|
|
752
|
+
content: response.toolResponse,
|
|
658
753
|
});
|
|
659
754
|
|
|
660
755
|
return {
|
|
661
756
|
toolCallId: toolCall.id,
|
|
662
757
|
name: toolCall.name as TName,
|
|
663
|
-
|
|
758
|
+
data: response.data ?? null,
|
|
664
759
|
};
|
|
665
760
|
};
|
|
666
761
|
|
|
@@ -706,6 +801,93 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
706
801
|
};
|
|
707
802
|
}
|
|
708
803
|
|
|
804
|
+
/**
|
|
805
|
+
* Identity function that creates a generic inference context for a tool definition.
|
|
806
|
+
* TypeScript infers TResult from the handler and flows it to hooks automatically.
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* ```typescript
|
|
810
|
+
* tools: {
|
|
811
|
+
* AskUser: defineTool({
|
|
812
|
+
* ...askUserTool,
|
|
813
|
+
* handler: handleAskUser,
|
|
814
|
+
* hooks: {
|
|
815
|
+
* onPostToolUse: ({ result }) => {
|
|
816
|
+
* // result is correctly typed as the handler's return data type
|
|
817
|
+
* },
|
|
818
|
+
* },
|
|
819
|
+
* }),
|
|
820
|
+
* }
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
export function defineTool<
|
|
824
|
+
TName extends string,
|
|
825
|
+
TSchema extends z.ZodType,
|
|
826
|
+
TResult,
|
|
827
|
+
TContext = ToolHandlerContext,
|
|
828
|
+
>(
|
|
829
|
+
tool: ToolWithHandler<TName, TSchema, TResult, TContext>
|
|
830
|
+
): ToolWithHandler<TName, TSchema, TResult, TContext> {
|
|
831
|
+
return tool;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Identity function that provides full type inference for subagent configurations.
|
|
836
|
+
* Verifies the workflow function's input parameters match the configured context,
|
|
837
|
+
* and properly types the lifecycle hooks with Task tool args and inferred result type.
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* ```ts
|
|
841
|
+
* // With typed context — workflow must accept { prompt, context }
|
|
842
|
+
* const researcher = defineSubagent({
|
|
843
|
+
* name: "researcher",
|
|
844
|
+
* description: "Researches topics",
|
|
845
|
+
* workflow: researcherWorkflow, // (input: { prompt: string; context: { apiKey: string } }) => Promise<...>
|
|
846
|
+
* context: { apiKey: "..." },
|
|
847
|
+
* resultSchema: z.object({ findings: z.string() }),
|
|
848
|
+
* hooks: {
|
|
849
|
+
* onPostExecution: ({ result }) => {
|
|
850
|
+
* // result is typed as { findings: string }
|
|
851
|
+
* },
|
|
852
|
+
* },
|
|
853
|
+
* });
|
|
854
|
+
*
|
|
855
|
+
* // Without context — workflow only needs { prompt }
|
|
856
|
+
* const writer = defineSubagent({
|
|
857
|
+
* name: "writer",
|
|
858
|
+
* description: "Writes content",
|
|
859
|
+
* workflow: writerWorkflow, // (input: { prompt: string }) => Promise<...>
|
|
860
|
+
* resultSchema: z.object({ content: z.string() }),
|
|
861
|
+
* });
|
|
862
|
+
* ```
|
|
863
|
+
*/
|
|
864
|
+
// With context — verifies workflow accepts { prompt, context: TContext }
|
|
865
|
+
export function defineSubagent<
|
|
866
|
+
TResult extends z.ZodType = z.ZodType,
|
|
867
|
+
TContext extends Record<string, unknown> = Record<string, unknown>,
|
|
868
|
+
>(
|
|
869
|
+
config: Omit<SubagentConfig<TResult>, "hooks" | "workflow" | "context"> & {
|
|
870
|
+
workflow:
|
|
871
|
+
| string
|
|
872
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
873
|
+
| ((input: { prompt: string; context: TContext }) => Promise<any>);
|
|
874
|
+
context: TContext;
|
|
875
|
+
hooks?: SubagentHooks<GenericTaskToolSchemaType, z.infer<TResult>>;
|
|
876
|
+
}
|
|
877
|
+
): SubagentConfig<TResult>;
|
|
878
|
+
// Without context — verifies workflow accepts { prompt }
|
|
879
|
+
export function defineSubagent<TResult extends z.ZodType = z.ZodType>(
|
|
880
|
+
config: Omit<SubagentConfig<TResult>, "hooks" | "workflow"> & {
|
|
881
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
882
|
+
workflow: string | ((input: { prompt: string }) => Promise<any>);
|
|
883
|
+
hooks?: SubagentHooks<GenericTaskToolSchemaType, z.infer<TResult>>;
|
|
884
|
+
}
|
|
885
|
+
): SubagentConfig<TResult>;
|
|
886
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
887
|
+
export function defineSubagent(config: any): SubagentConfig {
|
|
888
|
+
return config;
|
|
889
|
+
}
|
|
890
|
+
|
|
709
891
|
/**
|
|
710
892
|
* Utility to check if there were no tool calls besides a specific one
|
|
711
893
|
*/
|
package/src/lib/types.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type {
|
|
|
4
4
|
InferToolResults,
|
|
5
5
|
ParsedToolCallUnion,
|
|
6
6
|
ToolCallResultUnion,
|
|
7
|
-
ToolDefinition,
|
|
8
7
|
ToolMap,
|
|
9
8
|
} from "./tool-router";
|
|
10
9
|
|
|
11
10
|
import type { MessageContent, StoredMessage } from "@langchain/core/messages";
|
|
11
|
+
import type { Workflow } from "@temporalio/workflow";
|
|
12
12
|
import type { z } from "zod";
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -25,7 +25,7 @@ export type AgentStatus =
|
|
|
25
25
|
* Base state that all agents must have
|
|
26
26
|
*/
|
|
27
27
|
export interface BaseAgentState {
|
|
28
|
-
tools:
|
|
28
|
+
tools: SerializableToolDefinition[];
|
|
29
29
|
status: AgentStatus;
|
|
30
30
|
version: number;
|
|
31
31
|
turns: number;
|
|
@@ -103,6 +103,19 @@ export interface ZeitlichAgentConfig<T extends ToolMap> {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* A JSON-serializable tool definition for state storage.
|
|
108
|
+
* Uses a plain JSON Schema object instead of a live Zod instance,
|
|
109
|
+
* so it survives Temporal serialization without losing constraints (min, max, etc.).
|
|
110
|
+
*/
|
|
111
|
+
export interface SerializableToolDefinition {
|
|
112
|
+
name: string;
|
|
113
|
+
description: string;
|
|
114
|
+
schema: Record<string, unknown>;
|
|
115
|
+
strict?: boolean;
|
|
116
|
+
max_uses?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
106
119
|
/**
|
|
107
120
|
* Configuration passed to runAgent activity
|
|
108
121
|
*/
|
|
@@ -142,12 +155,44 @@ export interface SubagentConfig<TResult extends z.ZodType = z.ZodType> {
|
|
|
142
155
|
name: string;
|
|
143
156
|
/** Description shown to the parent agent explaining what this subagent does */
|
|
144
157
|
description: string;
|
|
145
|
-
/** Temporal workflow type name (used with executeChild) */
|
|
146
|
-
|
|
158
|
+
/** Temporal workflow function or type name (used with executeChild) */
|
|
159
|
+
workflow: string | Workflow;
|
|
147
160
|
/** Optional task queue - defaults to parent's queue if not specified */
|
|
148
161
|
taskQueue?: string;
|
|
149
162
|
/** Optional Zod schema to validate the child workflow's result. If omitted, result is passed through as-is. */
|
|
150
163
|
resultSchema?: TResult;
|
|
164
|
+
/** Optional static context passed to the subagent on every invocation */
|
|
165
|
+
context?: Record<string, unknown>;
|
|
166
|
+
/** Per-subagent lifecycle hooks */
|
|
167
|
+
hooks?: SubagentHooks;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Per-subagent lifecycle hooks - defined on a SubagentConfig.
|
|
172
|
+
* Runs in addition to global hooks (global pre → subagent pre → execute → subagent post → global post).
|
|
173
|
+
*/
|
|
174
|
+
export interface SubagentHooks<TArgs = unknown, TResult = unknown> {
|
|
175
|
+
/** Called before this subagent executes - can skip or modify args */
|
|
176
|
+
onPreExecution?: (ctx: {
|
|
177
|
+
args: TArgs;
|
|
178
|
+
threadId: string;
|
|
179
|
+
turn: number;
|
|
180
|
+
}) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
|
|
181
|
+
/** Called after this subagent executes successfully */
|
|
182
|
+
onPostExecution?: (ctx: {
|
|
183
|
+
args: TArgs;
|
|
184
|
+
result: TResult;
|
|
185
|
+
threadId: string;
|
|
186
|
+
turn: number;
|
|
187
|
+
durationMs: number;
|
|
188
|
+
}) => void | Promise<void>;
|
|
189
|
+
/** Called when this subagent execution fails */
|
|
190
|
+
onExecutionFailure?: (ctx: {
|
|
191
|
+
args: TArgs;
|
|
192
|
+
error: Error;
|
|
193
|
+
threadId: string;
|
|
194
|
+
turn: number;
|
|
195
|
+
}) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
|
|
151
196
|
}
|
|
152
197
|
|
|
153
198
|
/**
|
|
@@ -156,6 +201,8 @@ export interface SubagentConfig<TResult extends z.ZodType = z.ZodType> {
|
|
|
156
201
|
export interface SubagentInput {
|
|
157
202
|
/** The prompt/task from the parent agent */
|
|
158
203
|
prompt: string;
|
|
204
|
+
/** Optional context parameters passed from the parent agent */
|
|
205
|
+
context?: Record<string, unknown>;
|
|
159
206
|
}
|
|
160
207
|
|
|
161
208
|
// ============================================================================
|
|
@@ -328,6 +375,34 @@ export type SessionEndHook = (
|
|
|
328
375
|
ctx: SessionEndHookContext
|
|
329
376
|
) => void | Promise<void>;
|
|
330
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Per-tool lifecycle hooks - defined directly on a tool definition.
|
|
380
|
+
* Runs in addition to global hooks (global pre → tool pre → execute → tool post → global post).
|
|
381
|
+
*/
|
|
382
|
+
export interface ToolHooks<TArgs = unknown, TResult = unknown> {
|
|
383
|
+
/** Called before this tool executes - can skip or modify args */
|
|
384
|
+
onPreToolUse?: (ctx: {
|
|
385
|
+
args: TArgs;
|
|
386
|
+
threadId: string;
|
|
387
|
+
turn: number;
|
|
388
|
+
}) => PreToolUseHookResult | Promise<PreToolUseHookResult>;
|
|
389
|
+
/** Called after this tool executes successfully */
|
|
390
|
+
onPostToolUse?: (ctx: {
|
|
391
|
+
args: TArgs;
|
|
392
|
+
result: TResult | null;
|
|
393
|
+
threadId: string;
|
|
394
|
+
turn: number;
|
|
395
|
+
durationMs: number;
|
|
396
|
+
}) => void | Promise<void>;
|
|
397
|
+
/** Called when this tool execution fails */
|
|
398
|
+
onPostToolUseFailure?: (ctx: {
|
|
399
|
+
args: TArgs;
|
|
400
|
+
error: Error;
|
|
401
|
+
threadId: string;
|
|
402
|
+
turn: number;
|
|
403
|
+
}) => PostToolUseFailureHookResult | Promise<PostToolUseFailureHookResult>;
|
|
404
|
+
}
|
|
405
|
+
|
|
331
406
|
/**
|
|
332
407
|
* Combined hooks interface for session lifecycle
|
|
333
408
|
*/
|