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.
- package/README.md +49 -38
- package/dist/adapters/sandbox/daytona/index.cjs +205 -0
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -0
- package/dist/adapters/sandbox/daytona/index.d.cts +86 -0
- package/dist/adapters/sandbox/daytona/index.d.ts +86 -0
- package/dist/adapters/sandbox/daytona/index.js +202 -0
- package/dist/adapters/sandbox/daytona/index.js.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.cjs +174 -0
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -0
- package/dist/adapters/sandbox/inmemory/index.d.cts +28 -0
- package/dist/adapters/sandbox/inmemory/index.d.ts +28 -0
- package/dist/adapters/sandbox/inmemory/index.js +172 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -0
- package/dist/adapters/sandbox/virtual/index.cjs +405 -0
- package/dist/adapters/sandbox/virtual/index.cjs.map +1 -0
- package/dist/adapters/sandbox/virtual/index.d.cts +85 -0
- package/dist/adapters/sandbox/virtual/index.d.ts +85 -0
- package/dist/adapters/sandbox/virtual/index.js +400 -0
- package/dist/adapters/sandbox/virtual/index.js.map +1 -0
- package/dist/adapters/thread/google-genai/index.cjs +284 -0
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -0
- package/dist/adapters/thread/google-genai/index.d.cts +145 -0
- package/dist/adapters/thread/google-genai/index.d.ts +145 -0
- package/dist/adapters/thread/google-genai/index.js +278 -0
- package/dist/adapters/thread/google-genai/index.js.map +1 -0
- package/dist/adapters/{langchain → thread/langchain}/index.cjs +7 -9
- package/dist/adapters/thread/langchain/index.cjs.map +1 -0
- package/dist/adapters/{langchain → thread/langchain}/index.d.cts +17 -21
- package/dist/adapters/{langchain → thread/langchain}/index.d.ts +17 -21
- package/dist/adapters/{langchain → thread/langchain}/index.js +7 -9
- package/dist/adapters/thread/langchain/index.js.map +1 -0
- package/dist/index.cjs +816 -545
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +235 -74
- package/dist/index.d.ts +235 -74
- package/dist/index.js +804 -540
- package/dist/index.js.map +1 -1
- package/dist/types-B4C9txdq.d.ts +389 -0
- package/dist/{thread-manager-qc0g5Rvd.d.cts → types-B9ljZewB.d.cts} +1 -6
- package/dist/{thread-manager-qc0g5Rvd.d.ts → types-B9ljZewB.d.ts} +1 -6
- package/dist/types-BMXzv7TN.d.cts +476 -0
- package/dist/types-BMXzv7TN.d.ts +476 -0
- package/dist/types-BVP87m_W.d.cts +121 -0
- package/dist/types-CDubRtad.d.cts +115 -0
- package/dist/types-CDubRtad.d.ts +115 -0
- package/dist/types-CwwgQ_9H.d.ts +121 -0
- package/dist/types-GpMU4b0w.d.cts +389 -0
- package/dist/workflow.cjs +444 -318
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +271 -222
- package/dist/workflow.d.ts +271 -222
- package/dist/workflow.js +440 -316
- package/dist/workflow.js.map +1 -1
- package/package.json +59 -6
- package/src/adapters/sandbox/daytona/filesystem.ts +136 -0
- package/src/adapters/sandbox/daytona/index.ts +149 -0
- package/src/adapters/sandbox/daytona/types.ts +34 -0
- package/src/adapters/sandbox/inmemory/index.ts +213 -0
- package/src/adapters/sandbox/virtual/filesystem.ts +345 -0
- package/src/adapters/sandbox/virtual/index.ts +88 -0
- package/src/adapters/sandbox/virtual/mutations.ts +38 -0
- package/src/adapters/sandbox/virtual/provider.ts +101 -0
- package/src/adapters/sandbox/virtual/tree.ts +82 -0
- package/src/adapters/sandbox/virtual/types.ts +127 -0
- package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +523 -0
- package/src/adapters/sandbox/virtual/with-virtual-sandbox.ts +91 -0
- package/src/adapters/thread/google-genai/activities.ts +121 -0
- package/src/adapters/thread/google-genai/index.ts +41 -0
- package/src/adapters/thread/google-genai/model-invoker.ts +154 -0
- package/src/adapters/thread/google-genai/thread-manager.ts +169 -0
- package/src/adapters/{langchain → thread/langchain}/activities.ts +11 -15
- package/src/adapters/{langchain → thread/langchain}/index.ts +1 -1
- package/src/adapters/{langchain → thread/langchain}/model-invoker.ts +15 -18
- package/src/adapters/{langchain → thread/langchain}/thread-manager.ts +1 -1
- package/src/index.ts +32 -24
- package/src/lib/activity.ts +87 -0
- package/src/lib/hooks/index.ts +11 -0
- package/src/lib/hooks/types.ts +98 -0
- package/src/lib/model/helpers.ts +6 -0
- package/src/lib/model/index.ts +13 -0
- package/src/lib/{model-invoker.ts → model/types.ts} +18 -1
- package/src/lib/sandbox/index.ts +19 -0
- package/src/lib/sandbox/manager.ts +76 -0
- package/src/lib/sandbox/sandbox.test.ts +158 -0
- package/src/lib/{fs.ts → sandbox/tree.ts} +6 -6
- package/src/lib/sandbox/types.ts +164 -0
- package/src/lib/session/index.ts +11 -0
- package/src/lib/{session.ts → session/session.ts} +76 -48
- package/src/lib/session/types.ts +93 -0
- package/src/lib/skills/fs-provider.ts +16 -15
- package/src/lib/skills/handler.ts +31 -0
- package/src/lib/skills/index.ts +5 -1
- package/src/lib/skills/register.ts +20 -0
- package/src/lib/skills/tool.ts +47 -0
- package/src/lib/state/index.ts +9 -0
- package/src/lib/{state-manager.ts → state/manager.ts} +10 -147
- package/src/lib/state/types.ts +134 -0
- package/src/lib/subagent/define.ts +71 -0
- package/src/lib/subagent/handler.ts +99 -0
- package/src/lib/subagent/index.ts +13 -0
- package/src/lib/subagent/register.ts +53 -0
- package/src/lib/subagent/tool.ts +80 -0
- package/src/lib/subagent/types.ts +92 -0
- package/src/lib/thread/index.ts +7 -0
- package/src/lib/{thread-manager.ts → thread/manager.ts} +1 -33
- package/src/lib/thread/types.ts +33 -0
- package/src/lib/tool-router/auto-append.ts +55 -0
- package/src/lib/tool-router/index.ts +41 -0
- package/src/lib/tool-router/router.ts +462 -0
- package/src/lib/tool-router/types.ts +478 -0
- package/src/lib/tool-router/with-sandbox.ts +70 -0
- package/src/lib/types.ts +5 -382
- package/src/tools/bash/bash.test.ts +53 -55
- package/src/tools/bash/handler.ts +23 -51
- package/src/tools/edit/handler.ts +67 -81
- package/src/tools/glob/handler.ts +60 -17
- package/src/tools/read-file/handler.ts +67 -0
- package/src/tools/read-skill/handler.ts +1 -31
- package/src/tools/read-skill/tool.ts +5 -47
- package/src/tools/subagent/handler.ts +1 -100
- package/src/tools/subagent/tool.ts +5 -93
- package/src/tools/task-create/handler.ts +1 -1
- package/src/tools/task-get/handler.ts +1 -1
- package/src/tools/task-list/handler.ts +1 -1
- package/src/tools/task-update/handler.ts +1 -1
- package/src/tools/write-file/handler.ts +47 -0
- package/src/workflow.ts +88 -47
- package/tsup.config.ts +8 -1
- package/dist/adapters/langchain/index.cjs.map +0 -1
- package/dist/adapters/langchain/index.js.map +0 -1
- package/dist/model-invoker-y_zlyMqu.d.cts +0 -892
- package/dist/model-invoker-y_zlyMqu.d.ts +0 -892
- package/src/lib/tool-router.ts +0 -977
- package/src/lib/workflow-helpers.ts +0 -50
- /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
|
+
};
|
|
@@ -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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
+
}
|