zeitlich 0.2.13 → 0.2.14

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.
Files changed (135) hide show
  1. package/README.md +49 -38
  2. package/dist/adapters/sandbox/daytona/index.cjs +205 -0
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
  5. package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
  6. package/dist/adapters/sandbox/daytona/index.js +202 -0
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -0
  8. package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
  9. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
  10. package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
  11. package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
  12. package/dist/adapters/sandbox/inmemory/index.js +172 -0
  13. package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
  14. package/dist/adapters/sandbox/virtual/index.cjs +405 -0
  15. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
  16. package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
  17. package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
  18. package/dist/adapters/sandbox/virtual/index.js +400 -0
  19. package/dist/adapters/sandbox/virtual/index.js.map +1 -0
  20. package/dist/adapters/thread/google-genai/index.cjs +284 -0
  21. package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
  22. package/dist/adapters/thread/google-genai/index.d.cts +145 -0
  23. package/dist/adapters/thread/google-genai/index.d.ts +145 -0
  24. package/dist/adapters/thread/google-genai/index.js +278 -0
  25. package/dist/adapters/thread/google-genai/index.js.map +1 -0
  26. package/dist/adapters/{langchain → thread/langchain}/index.cjs +7 -9
  27. package/dist/adapters/thread/langchain/index.cjs.map +1 -0
  28. package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
  29. package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
  30. package/dist/adapters/{langchain → thread/langchain}/index.js +7 -9
  31. package/dist/adapters/thread/langchain/index.js.map +1 -0
  32. package/dist/index.cjs +816 -545
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +235 -74
  35. package/dist/index.d.ts +235 -74
  36. package/dist/index.js +804 -540
  37. package/dist/index.js.map +1 -1
  38. package/dist/types-B4C9txdq.d.ts +389 -0
  39. package/dist/{thread-manager-qc0g5Rvd.d.cts → types-B9ljZewB.d.cts} +1 -6
  40. package/dist/{thread-manager-qc0g5Rvd.d.ts → types-B9ljZewB.d.ts} +1 -6
  41. package/dist/types-BMXzv7TN.d.cts +476 -0
  42. package/dist/types-BMXzv7TN.d.ts +476 -0
  43. package/dist/types-BVP87m_W.d.cts +121 -0
  44. package/dist/types-CDubRtad.d.cts +115 -0
  45. package/dist/types-CDubRtad.d.ts +115 -0
  46. package/dist/types-CwwgQ_9H.d.ts +121 -0
  47. package/dist/types-GpMU4b0w.d.cts +389 -0
  48. package/dist/workflow.cjs +444 -318
  49. package/dist/workflow.cjs.map +1 -1
  50. package/dist/workflow.d.cts +271 -222
  51. package/dist/workflow.d.ts +271 -222
  52. package/dist/workflow.js +440 -316
  53. package/dist/workflow.js.map +1 -1
  54. package/package.json +59 -6
  55. package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
  56. package/src/adapters/sandbox/daytona/index.ts +149 -0
  57. package/src/adapters/sandbox/daytona/types.ts +34 -0
  58. package/src/adapters/sandbox/inmemory/index.ts +213 -0
  59. package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
  60. package/src/adapters/sandbox/virtual/index.ts +88 -0
  61. package/src/adapters/sandbox/virtual/mutations.ts +38 -0
  62. package/src/adapters/sandbox/virtual/provider.ts +101 -0
  63. package/src/adapters/sandbox/virtual/tree.ts +82 -0
  64. package/src/adapters/sandbox/virtual/types.ts +127 -0
  65. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
  66. package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
  67. package/src/adapters/thread/google-genai/activities.ts +121 -0
  68. package/src/adapters/thread/google-genai/index.ts +41 -0
  69. package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
  70. package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
  71. package/src/adapters/{langchain → thread/langchain}/activities.ts +11 -15
  72. package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
  73. package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
  74. package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
  75. package/src/index.ts +32 -24
  76. package/src/lib/activity.ts +87 -0
  77. package/src/lib/hooks/index.ts +11 -0
  78. package/src/lib/hooks/types.ts +98 -0
  79. package/src/lib/model/helpers.ts +6 -0
  80. package/src/lib/model/index.ts +13 -0
  81. package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
  82. package/src/lib/sandbox/index.ts +19 -0
  83. package/src/lib/sandbox/manager.ts +76 -0
  84. package/src/lib/sandbox/sandbox.test.ts +158 -0
  85. package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
  86. package/src/lib/sandbox/types.ts +164 -0
  87. package/src/lib/session/index.ts +11 -0
  88. package/src/lib/{session.ts → session/session.ts} +76 -48
  89. package/src/lib/session/types.ts +93 -0
  90. package/src/lib/skills/fs-provider.ts +16 -15
  91. package/src/lib/skills/handler.ts +31 -0
  92. package/src/lib/skills/index.ts +5 -1
  93. package/src/lib/skills/register.ts +20 -0
  94. package/src/lib/skills/tool.ts +47 -0
  95. package/src/lib/state/index.ts +9 -0
  96. package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
  97. package/src/lib/state/types.ts +134 -0
  98. package/src/lib/subagent/define.ts +71 -0
  99. package/src/lib/subagent/handler.ts +99 -0
  100. package/src/lib/subagent/index.ts +13 -0
  101. package/src/lib/subagent/register.ts +53 -0
  102. package/src/lib/subagent/tool.ts +80 -0
  103. package/src/lib/subagent/types.ts +92 -0
  104. package/src/lib/thread/index.ts +7 -0
  105. package/src/lib/{thread-manager.ts → thread/manager.ts} +1 -33
  106. package/src/lib/thread/types.ts +33 -0
  107. package/src/lib/tool-router/auto-append.ts +55 -0
  108. package/src/lib/tool-router/index.ts +41 -0
  109. package/src/lib/tool-router/router.ts +462 -0
  110. package/src/lib/tool-router/types.ts +478 -0
  111. package/src/lib/tool-router/with-sandbox.ts +70 -0
  112. package/src/lib/types.ts +5 -382
  113. package/src/tools/bash/bash.test.ts +53 -55
  114. package/src/tools/bash/handler.ts +23 -51
  115. package/src/tools/edit/handler.ts +67 -81
  116. package/src/tools/glob/handler.ts +60 -17
  117. package/src/tools/read-file/handler.ts +67 -0
  118. package/src/tools/read-skill/handler.ts +1 -31
  119. package/src/tools/read-skill/tool.ts +5 -47
  120. package/src/tools/subagent/handler.ts +1 -100
  121. package/src/tools/subagent/tool.ts +5 -93
  122. package/src/tools/task-create/handler.ts +1 -1
  123. package/src/tools/task-get/handler.ts +1 -1
  124. package/src/tools/task-list/handler.ts +1 -1
  125. package/src/tools/task-update/handler.ts +1 -1
  126. package/src/tools/write-file/handler.ts +47 -0
  127. package/src/workflow.ts +88 -47
  128. package/tsup.config.ts +8 -1
  129. package/dist/adapters/langchain/index.cjs.map +0 -1
  130. package/dist/adapters/langchain/index.js.map +0 -1
  131. package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
  132. package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
  133. package/src/lib/tool-router.ts +0 -977
  134. package/src/lib/workflow-helpers.ts +0 -50
  135. /package/src/lib/{thread-id.ts → thread/id.ts} +0 -0
@@ -0,0 +1,47 @@
1
+ import z from "zod";
2
+ import type { Skill } from "./types";
3
+
4
+ export const READ_SKILL_TOOL_NAME = "ReadSkill" as const;
5
+
6
+ function buildReadSkillDescription(skills: Skill[]): string {
7
+ const skillList = skills
8
+ .map((s) => `- **${s.name}**: ${s.description}`)
9
+ .join("\n");
10
+
11
+ return `Load the full instructions for a skill. Read the skill before following its instructions.
12
+
13
+ # Available skills:
14
+ ${skillList}
15
+ `;
16
+ }
17
+
18
+ /**
19
+ * Creates a ReadSkill tool configured with the available skills.
20
+ * The tool description embeds skill metadata so the agent discovers
21
+ * skills purely through the tool definition.
22
+ */
23
+ export function createReadSkillTool(skills: Skill[]): {
24
+ name: string;
25
+ description: string;
26
+ schema: z.ZodObject<{
27
+ skill_name: z.ZodEnum<Record<string, string>>;
28
+ }>;
29
+ } {
30
+ if (skills.length === 0) {
31
+ throw new Error("createReadSkillTool requires at least one skill");
32
+ }
33
+
34
+ const names = skills.map((s) => s.name);
35
+
36
+ return {
37
+ name: READ_SKILL_TOOL_NAME,
38
+ description: buildReadSkillDescription(skills),
39
+ schema: z.object({
40
+ skill_name: z.enum(names).describe("The name of the skill to load"),
41
+ }),
42
+ } as const;
43
+ }
44
+
45
+ export type ReadSkillArgs = {
46
+ skill_name: string;
47
+ };
@@ -0,0 +1,9 @@
1
+ export { createAgentStateManager } from "./manager";
2
+
3
+ export type {
4
+ JsonPrimitive,
5
+ JsonValue,
6
+ JsonSerializable,
7
+ AgentState,
8
+ AgentStateManager,
9
+ } from "./types";
@@ -3,152 +3,17 @@ import {
3
3
  defineQuery,
4
4
  defineUpdate,
5
5
  setHandler,
6
- type QueryDefinition,
7
6
  } from "@temporalio/workflow";
8
- import type { UpdateDefinition } from "@temporalio/common/lib/interfaces";
9
7
  import {
10
8
  type AgentStatus,
11
9
  type BaseAgentState,
12
10
  type WorkflowTask,
13
11
  isTerminalStatus,
14
- agentQueryName,
15
- agentStateChangeUpdateName,
16
- } from "./types";
17
- import type { ToolDefinition } from "./tool-router";
12
+ } from "../types";
13
+ import type { ToolDefinition } from "../tool-router/types";
14
+ import type { AgentState, AgentStateManager, JsonSerializable } from "./types";
18
15
  import { z } from "zod";
19
16
 
20
- /**
21
- * JSON primitive types that Temporal can serialize
22
- */
23
- export type JsonPrimitive = string | number | boolean | null | undefined;
24
-
25
- /**
26
- * JSON-serializable value (recursive type for Temporal compatibility)
27
- */
28
- export type JsonValue =
29
- | JsonPrimitive
30
- | JsonValue[]
31
- | { [key: string]: JsonValue };
32
-
33
- /**
34
- * Type constraint ensuring T only contains JSON-serializable values.
35
- * Use this for custom state to ensure Temporal workflow compatibility.
36
- *
37
- * Allows: primitives, arrays, plain objects, and JsonValue
38
- * Rejects: functions, symbols, undefined, class instances with methods
39
- */
40
- export type JsonSerializable<T> = {
41
- [K in keyof T]: T[K] extends JsonValue
42
- ? T[K]
43
- : T[K] extends JsonPrimitive
44
- ? T[K]
45
- : T[K] extends (infer U)[]
46
- ? U extends JsonValue
47
- ? T[K]
48
- : JsonSerializable<U>[]
49
- : T[K] extends object
50
- ? JsonSerializable<T[K]>
51
- : never;
52
- };
53
-
54
- /**
55
- * Full state type combining base state with custom state
56
- */
57
- export type AgentState<TCustom extends JsonSerializable<TCustom>> =
58
- BaseAgentState & TCustom;
59
-
60
- /**
61
- * Agent state manager interface
62
- * Note: Temporal handlers must be set up in the workflow file due to
63
- * Temporal's workflow isolation requirements. This manager provides
64
- * the state and helpers needed for those handlers.
65
- */
66
- export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
67
- /** Typed query definition registered for this agent's state */
68
- readonly stateQuery: QueryDefinition<AgentState<TCustom>>;
69
- /** Typed update definition registered for waiting on this agent's state change */
70
- readonly stateChangeUpdate: UpdateDefinition<AgentState<TCustom>, [number]>;
71
-
72
- /** Get current status */
73
- getStatus(): AgentStatus;
74
- /** Check if agent is running */
75
- isRunning(): boolean;
76
- /** Check if agent is in terminal state */
77
- isTerminal(): boolean;
78
- /** Get current state version */
79
- getVersion(): number;
80
-
81
- /** Set status to RUNNING */
82
- run(): void;
83
- /** Set status to WAITING_FOR_INPUT */
84
- waitForInput(): void;
85
- /** Set status to COMPLETED */
86
- complete(): void;
87
- /** Set status to FAILED */
88
- fail(): void;
89
- /** Set status to CANCELLED */
90
- cancel(): void;
91
-
92
- /** Increment state version (call after state changes) */
93
- incrementVersion(): void;
94
-
95
- /** Increment turns (call after each turn) */
96
- incrementTurns(): void;
97
-
98
- /** Get current turns */
99
- getTurns(): number;
100
-
101
- /** Get the system prompt */
102
- getSystemPrompt(): string | undefined;
103
-
104
- /** Set the system prompt */
105
- setSystemPrompt(newSystemPrompt: string): void;
106
-
107
- /** Get a custom state value by key */
108
- get<K extends keyof TCustom>(key: K): TCustom[K];
109
-
110
- /** Set a custom state value by key */
111
- set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
112
-
113
- /** Get full state for query handler */
114
- getCurrentState(): AgentState<TCustom>;
115
-
116
- /** Check if should return from waitForStateChange */
117
- shouldReturnFromWait(lastKnownVersion: number): boolean;
118
-
119
- // Task management methods
120
- /** Get all tasks */
121
- getTasks(): WorkflowTask[];
122
- /** Get a task by ID */
123
- getTask(id: string): WorkflowTask | undefined;
124
- /** Add or update a task */
125
- setTask(task: WorkflowTask): void;
126
- /** Delete a task by ID */
127
- deleteTask(id: string): boolean;
128
-
129
- /** Set the tools (converts Zod schemas to JSON Schema for serialization) */
130
- setTools(newTools: ToolDefinition[]): void;
131
-
132
- /** Update the usage */
133
- updateUsage(usage: {
134
- inputTokens?: number;
135
- outputTokens?: number;
136
- cachedWriteTokens?: number;
137
- cachedReadTokens?: number;
138
- reasonTokens?: number;
139
- }): void;
140
-
141
- /** Get the total usage */
142
- getTotalUsage(): {
143
- totalInputTokens: number;
144
- totalOutputTokens: number;
145
- totalCachedWriteTokens: number;
146
- totalCachedReadTokens: number;
147
- totalReasonTokens: number;
148
- turns: number;
149
- };
150
- }
151
-
152
17
  /**
153
18
  * Creates an agent state manager for tracking workflow state.
154
19
  * Automatically registers Temporal query and update handlers for the agent.
@@ -181,12 +46,9 @@ export function createAgentStateManager<
181
46
  TCustom extends JsonSerializable<TCustom> = Record<string, never>,
182
47
  >({
183
48
  initialState,
184
- agentName,
185
49
  }: {
186
50
  initialState?: Partial<BaseAgentState> & TCustom;
187
- agentName: string;
188
51
  }): AgentStateManager<TCustom> {
189
- // Default state (BaseAgentState fields)
190
52
  let status: AgentStatus = initialState?.status ?? "RUNNING";
191
53
  let version = initialState?.version ?? 0;
192
54
  let turns = initialState?.turns ?? 0;
@@ -198,10 +60,8 @@ export function createAgentStateManager<
198
60
  let totalReasonTokens = 0;
199
61
  let systemPrompt = initialState?.systemPrompt;
200
62
 
201
- // Tasks state
202
63
  const tasks = new Map<string, WorkflowTask>(initialState?.tasks);
203
64
 
204
- // Custom state - extract only custom fields (exclude base state keys)
205
65
  const {
206
66
  status: _,
207
67
  version: __,
@@ -222,11 +82,9 @@ export function createAgentStateManager<
222
82
  } as AgentState<TCustom>;
223
83
  }
224
84
 
225
- const stateQuery = defineQuery<AgentState<TCustom>>(
226
- agentQueryName(agentName)
227
- );
85
+ const stateQuery = defineQuery<AgentState<TCustom>>("getAgentState");
228
86
  const stateChangeUpdate = defineUpdate<AgentState<TCustom>, [number]>(
229
- agentStateChangeUpdateName(agentName)
87
+ "waitForAgentStateChange"
230
88
  );
231
89
 
232
90
  setHandler(stateQuery, () => buildState());
@@ -308,6 +166,11 @@ export function createAgentStateManager<
308
166
  version++;
309
167
  },
310
168
 
169
+ mergeUpdate(update: Partial<TCustom>): void {
170
+ Object.assign(customState as object, update);
171
+ version++;
172
+ },
173
+
311
174
  getCurrentState(): AgentState<TCustom> {
312
175
  return buildState();
313
176
  },
@@ -0,0 +1,134 @@
1
+ import type {
2
+ QueryDefinition,
3
+ } from "@temporalio/workflow";
4
+ import type { UpdateDefinition } from "@temporalio/common/lib/interfaces";
5
+ import type { AgentStatus, BaseAgentState, TokenUsage, WorkflowTask } from "../types";
6
+ import type { ToolDefinition } from "../tool-router/types";
7
+
8
+ /**
9
+ * JSON primitive types that Temporal can serialize
10
+ */
11
+ export type JsonPrimitive = string | number | boolean | null | undefined;
12
+
13
+ /**
14
+ * JSON-serializable value (recursive type for Temporal compatibility)
15
+ */
16
+ export type JsonValue =
17
+ | JsonPrimitive
18
+ | JsonValue[]
19
+ | { [key: string]: JsonValue };
20
+
21
+ /**
22
+ * Type constraint ensuring T only contains JSON-serializable values.
23
+ * Use this for custom state to ensure Temporal workflow compatibility.
24
+ *
25
+ * Allows: primitives, arrays, plain objects, and JsonValue
26
+ * Rejects: functions, symbols, undefined, class instances with methods
27
+ */
28
+ export type JsonSerializable<T> = {
29
+ [K in keyof T]: T[K] extends JsonValue
30
+ ? T[K]
31
+ : T[K] extends JsonPrimitive
32
+ ? T[K]
33
+ : T[K] extends (infer U)[]
34
+ ? U extends JsonValue
35
+ ? T[K]
36
+ : JsonSerializable<U>[]
37
+ : T[K] extends object
38
+ ? JsonSerializable<T[K]>
39
+ : never;
40
+ };
41
+
42
+ /**
43
+ * Full state type combining base state with custom state
44
+ */
45
+ export type AgentState<TCustom extends JsonSerializable<TCustom>> =
46
+ BaseAgentState & TCustom;
47
+
48
+ /**
49
+ * Agent state manager interface
50
+ * Note: Temporal handlers must be set up in the workflow file due to
51
+ * Temporal's workflow isolation requirements. This manager provides
52
+ * the state and helpers needed for those handlers.
53
+ */
54
+ export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
55
+ /** Typed query definition registered for this agent's state */
56
+ readonly stateQuery: QueryDefinition<AgentState<TCustom>>;
57
+ /** Typed update definition registered for waiting on this agent's state change */
58
+ readonly stateChangeUpdate: UpdateDefinition<AgentState<TCustom>, [number]>;
59
+
60
+ /** Get current status */
61
+ getStatus(): AgentStatus;
62
+ /** Check if agent is running */
63
+ isRunning(): boolean;
64
+ /** Check if agent is in terminal state */
65
+ isTerminal(): boolean;
66
+ /** Get current state version */
67
+ getVersion(): number;
68
+
69
+ /** Set status to RUNNING */
70
+ run(): void;
71
+ /** Set status to WAITING_FOR_INPUT */
72
+ waitForInput(): void;
73
+ /** Set status to COMPLETED */
74
+ complete(): void;
75
+ /** Set status to FAILED */
76
+ fail(): void;
77
+ /** Set status to CANCELLED */
78
+ cancel(): void;
79
+
80
+ /** Increment state version (call after state changes) */
81
+ incrementVersion(): void;
82
+
83
+ /** Increment turns (call after each turn) */
84
+ incrementTurns(): void;
85
+
86
+ /** Get current turns */
87
+ getTurns(): number;
88
+
89
+ /** Get the system prompt */
90
+ getSystemPrompt(): string | undefined;
91
+
92
+ /** Set the system prompt */
93
+ setSystemPrompt(newSystemPrompt: string): void;
94
+
95
+ /** Get a custom state value by key */
96
+ get<K extends keyof TCustom>(key: K): TCustom[K];
97
+
98
+ /** Set a custom state value by key */
99
+ set<K extends keyof TCustom>(key: K, value: TCustom[K]): void;
100
+
101
+ /** Bulk-merge a partial update into custom state (e.g. from sandbox stateUpdate) */
102
+ mergeUpdate(update: Partial<TCustom>): void;
103
+
104
+ /** Get full state for query handler */
105
+ getCurrentState(): AgentState<TCustom>;
106
+
107
+ /** Check if should return from waitForStateChange */
108
+ shouldReturnFromWait(lastKnownVersion: number): boolean;
109
+
110
+ /** Get all tasks */
111
+ getTasks(): WorkflowTask[];
112
+ /** Get a task by ID */
113
+ getTask(id: string): WorkflowTask | undefined;
114
+ /** Add or update a task */
115
+ setTask(task: WorkflowTask): void;
116
+ /** Delete a task by ID */
117
+ deleteTask(id: string): boolean;
118
+
119
+ /** Set the tools (converts Zod schemas to JSON Schema for serialization) */
120
+ setTools(newTools: ToolDefinition[]): void;
121
+
122
+ /** Update the usage */
123
+ updateUsage(usage: TokenUsage): void;
124
+
125
+ /** Get the total usage */
126
+ getTotalUsage(): {
127
+ totalInputTokens: number;
128
+ totalOutputTokens: number;
129
+ totalCachedWriteTokens: number;
130
+ totalCachedReadTokens: number;
131
+ totalReasonTokens: number;
132
+ turns: number;
133
+ };
134
+ }
@@ -0,0 +1,71 @@
1
+ import type { z } from "zod";
2
+ import type {
3
+ SubagentConfig,
4
+ SubagentHandlerResponse,
5
+ SubagentHooks,
6
+ } from "./types";
7
+ import type { SubagentArgs } from "./tool";
8
+
9
+ /**
10
+ * Identity function that provides full type inference for subagent configurations.
11
+ * Verifies the workflow function's input parameters match the configured context,
12
+ * and properly types the lifecycle hooks with Task tool args and inferred result type.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // With typed context — workflow must accept { prompt, context }
17
+ * const researcher = defineSubagent({
18
+ * name: "researcher",
19
+ * description: "Researches topics",
20
+ * workflow: researcherWorkflow, // (input: { prompt: string; context: { apiKey: string } }) => Promise<...>
21
+ * context: { apiKey: "..." },
22
+ * resultSchema: z.object({ findings: z.string() }),
23
+ * hooks: {
24
+ * onPostExecution: ({ result }) => {
25
+ * // result is typed as { findings: string }
26
+ * },
27
+ * },
28
+ * });
29
+ *
30
+ * // Without context — workflow only needs { prompt }
31
+ * const writer = defineSubagent({
32
+ * name: "writer",
33
+ * description: "Writes content",
34
+ * workflow: writerWorkflow, // (input: { prompt: string }) => Promise<...>
35
+ * resultSchema: z.object({ content: z.string() }),
36
+ * });
37
+ * ```
38
+ */
39
+ // With context — verifies workflow accepts { prompt, context: TContext }
40
+ export function defineSubagent<
41
+ TResult extends z.ZodType = z.ZodType,
42
+ TContext extends Record<string, unknown> = Record<string, unknown>,
43
+ >(
44
+ config: Omit<SubagentConfig<TResult>, "hooks" | "workflow" | "context"> & {
45
+ workflow:
46
+ | string
47
+ | ((input: {
48
+ prompt: string;
49
+ previousThreadId?: string;
50
+ context: TContext;
51
+ }) => Promise<SubagentHandlerResponse<z.infer<TResult> | null>>);
52
+ context: TContext;
53
+ hooks?: SubagentHooks<SubagentArgs, z.infer<TResult>>;
54
+ }
55
+ ): SubagentConfig<TResult>;
56
+ // Without context — verifies workflow accepts { prompt }
57
+ export function defineSubagent<TResult extends z.ZodType = z.ZodType>(
58
+ config: Omit<SubagentConfig<TResult>, "hooks" | "workflow"> & {
59
+ workflow:
60
+ | string
61
+ | ((input: {
62
+ prompt: string;
63
+ previousThreadId?: string;
64
+ }) => Promise<SubagentHandlerResponse<z.infer<TResult> | null>>);
65
+ hooks?: SubagentHooks<SubagentArgs, z.infer<TResult>>;
66
+ }
67
+ ): SubagentConfig<TResult>;
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
+ export function defineSubagent(config: any): SubagentConfig {
70
+ return config;
71
+ }
@@ -0,0 +1,99 @@
1
+ import { executeChild, workflowInfo } from "@temporalio/workflow";
2
+ import { getShortId } from "../thread/id";
3
+ import type { ToolHandlerResponse, RouterContext } from "../tool-router";
4
+ import type { ToolMessageContent } from "../types";
5
+ import type {
6
+ InferSubagentResult,
7
+ SubagentConfig,
8
+ SubagentInput,
9
+ } from "./types";
10
+ import type { SubagentArgs } from "./tool";
11
+ import type { z } from "zod";
12
+
13
+ /**
14
+ * Creates a Subagent tool handler that spawns child workflows for configured subagents.
15
+ *
16
+ * @param subagents - Array of subagent configurations
17
+ * @returns A tool handler function that can be used with the tool router
18
+ */
19
+ export function createSubagentHandler<
20
+ const T extends readonly SubagentConfig[],
21
+ >(subagents: [...T]) {
22
+ const { taskQueue: parentTaskQueue } = workflowInfo();
23
+
24
+ return async (
25
+ args: SubagentArgs,
26
+ context: RouterContext
27
+ ): Promise<ToolHandlerResponse<InferSubagentResult<T[number]> | null>> => {
28
+ const config = subagents.find((s) => s.agentName === args.subagent);
29
+
30
+ if (!config) {
31
+ throw new Error(
32
+ `Unknown subagent: ${args.subagent}. Available: ${subagents.map((s) => s.agentName).join(", ")}`
33
+ );
34
+ }
35
+
36
+ const childWorkflowId = `${args.subagent}-${getShortId()}`;
37
+
38
+ const { sandboxId: parentSandboxId } = context;
39
+ const inheritSandbox = config.sandbox !== "own" && !!parentSandboxId;
40
+
41
+ const input: SubagentInput = {
42
+ prompt: args.prompt,
43
+ ...(config.context && { context: config.context }),
44
+ ...(args.threadId &&
45
+ args.threadId !== null &&
46
+ config.allowThreadContinuation && { previousThreadId: args.threadId }),
47
+ ...(inheritSandbox && { sandboxId: parentSandboxId }),
48
+ };
49
+
50
+ const childOpts = {
51
+ workflowId: childWorkflowId,
52
+ args: [input] as const,
53
+ taskQueue: config.taskQueue ?? parentTaskQueue,
54
+ };
55
+
56
+ const {
57
+ toolResponse,
58
+ data,
59
+ usage,
60
+ threadId: childThreadId,
61
+ } = typeof config.workflow === "string"
62
+ ? await executeChild(config.workflow, childOpts)
63
+ : await executeChild(config.workflow, childOpts);
64
+
65
+ if (!toolResponse) {
66
+ return {
67
+ toolResponse: "Subagent workflow returned no response",
68
+ data: null,
69
+ ...(usage && { usage }),
70
+ };
71
+ }
72
+
73
+ const validated = (
74
+ config.resultSchema ? config.resultSchema.safeParse(data) : null
75
+ ) as z.ZodSafeParseResult<InferSubagentResult<T[number]>> | null;
76
+
77
+ if (validated && !validated.success) {
78
+ return {
79
+ toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
80
+ data: null,
81
+ ...(usage && { usage }),
82
+ };
83
+ }
84
+
85
+ let finalToolResponse: ToolMessageContent = toolResponse;
86
+ if (config.allowThreadContinuation && childThreadId) {
87
+ finalToolResponse =
88
+ typeof toolResponse === "string"
89
+ ? `${toolResponse}\n\n[Thread ID: ${childThreadId}]`
90
+ : toolResponse;
91
+ }
92
+
93
+ return {
94
+ toolResponse: finalToolResponse,
95
+ data: validated ? validated.data : data,
96
+ ...(usage && { usage }),
97
+ };
98
+ };
99
+ }
@@ -0,0 +1,13 @@
1
+ export type {
2
+ SubagentConfig,
3
+ SubagentHooks,
4
+ SubagentInput,
5
+ SubagentHandlerResponse,
6
+ SubagentWorkflow,
7
+ InferSubagentResult,
8
+ } from "./types";
9
+ export { createSubagentTool, SUBAGENT_TOOL_NAME } from "./tool";
10
+ export type { SubagentArgs } from "./tool";
11
+ export { createSubagentHandler } from "./handler";
12
+ export { defineSubagent } from "./define";
13
+ export { buildSubagentRegistration } from "./register";
@@ -0,0 +1,53 @@
1
+ import type {
2
+ PreToolUseHookResult,
3
+ PostToolUseFailureHookResult,
4
+ ToolHooks,
5
+ ToolMap,
6
+ } from "../tool-router/types";
7
+ import type { SubagentConfig, SubagentHooks } from "./types";
8
+ import { createSubagentTool, type SubagentArgs } from "./tool";
9
+ import { createSubagentHandler } from "./handler";
10
+
11
+ /**
12
+ * Builds a fully wired tool entry for the Subagent tool,
13
+ * including per-subagent hook delegation.
14
+ *
15
+ * Returns null if no enabled subagents are configured.
16
+ */
17
+ export function buildSubagentRegistration(
18
+ subagents: SubagentConfig[]
19
+ ): ToolMap[string] | null {
20
+ const enabled = subagents.filter((s) => s.enabled ?? true);
21
+ if (enabled.length === 0) return null;
22
+
23
+ const subagentHooksMap = new Map<string, SubagentHooks>();
24
+ for (const s of enabled) {
25
+ if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
26
+ }
27
+
28
+ const resolveSubagentName = (args: unknown): string =>
29
+ (args as SubagentArgs).subagent;
30
+
31
+ return {
32
+ ...createSubagentTool(enabled),
33
+ handler: createSubagentHandler(enabled),
34
+ ...(subagentHooksMap.size > 0 && {
35
+ hooks: {
36
+ onPreToolUse: async (ctx): Promise<PreToolUseHookResult> => {
37
+ const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
38
+ return hooks?.onPreExecution?.(ctx) ?? {};
39
+ },
40
+ onPostToolUse: async (ctx): Promise<void> => {
41
+ const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
42
+ await hooks?.onPostExecution?.(ctx);
43
+ },
44
+ onPostToolUseFailure: async (
45
+ ctx
46
+ ): Promise<PostToolUseFailureHookResult> => {
47
+ const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
48
+ return hooks?.onExecutionFailure?.(ctx) ?? {};
49
+ },
50
+ } satisfies ToolHooks,
51
+ }),
52
+ };
53
+ }