@clinebot/agents 0.0.0
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 +145 -0
- package/dist/agent-input.d.ts +2 -0
- package/dist/agent.d.ts +56 -0
- package/dist/extensions.d.ts +21 -0
- package/dist/hooks/engine.d.ts +42 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/lifecycle.d.ts +5 -0
- package/dist/hooks/node.d.ts +2 -0
- package/dist/hooks/subprocess-runner.d.ts +16 -0
- package/dist/hooks/subprocess.d.ts +268 -0
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +49 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +49 -0
- package/dist/index.node.d.ts +5 -0
- package/dist/index.node.js +49 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/policies.d.ts +14 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/types.d.ts +35 -0
- package/dist/message-builder.d.ts +31 -0
- package/dist/prompts/cline.d.ts +1 -0
- package/dist/prompts/index.d.ts +1 -0
- package/dist/runtime/agent-runtime-bus.d.ts +13 -0
- package/dist/runtime/conversation-store.d.ts +16 -0
- package/dist/runtime/lifecycle-orchestrator.d.ts +28 -0
- package/dist/runtime/tool-orchestrator.d.ts +39 -0
- package/dist/runtime/turn-processor.d.ts +21 -0
- package/dist/teams/index.d.ts +3 -0
- package/dist/teams/multi-agent.d.ts +566 -0
- package/dist/teams/spawn-agent-tool.d.ts +85 -0
- package/dist/teams/team-tools.d.ts +51 -0
- package/dist/tools/ask-question.d.ts +12 -0
- package/dist/tools/create.d.ts +59 -0
- package/dist/tools/execution.d.ts +61 -0
- package/dist/tools/formatting.d.ts +20 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/registry.d.ts +26 -0
- package/dist/tools/validation.d.ts +27 -0
- package/dist/types.d.ts +826 -0
- package/package.json +54 -0
- package/src/agent-input.ts +116 -0
- package/src/agent.test.ts +931 -0
- package/src/agent.ts +1050 -0
- package/src/example.test.ts +564 -0
- package/src/extensions.ts +337 -0
- package/src/hooks/engine.test.ts +163 -0
- package/src/hooks/engine.ts +537 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/lifecycle.ts +239 -0
- package/src/hooks/node.ts +18 -0
- package/src/hooks/subprocess-runner.ts +140 -0
- package/src/hooks/subprocess.test.ts +180 -0
- package/src/hooks/subprocess.ts +620 -0
- package/src/index.browser.ts +1 -0
- package/src/index.node.ts +21 -0
- package/src/index.ts +133 -0
- package/src/mcp/index.ts +17 -0
- package/src/mcp/policies.test.ts +51 -0
- package/src/mcp/policies.ts +53 -0
- package/src/mcp/tools.test.ts +76 -0
- package/src/mcp/tools.ts +60 -0
- package/src/mcp/types.ts +41 -0
- package/src/message-builder.test.ts +175 -0
- package/src/message-builder.ts +429 -0
- package/src/prompts/cline.ts +49 -0
- package/src/prompts/index.ts +1 -0
- package/src/runtime/agent-runtime-bus.ts +53 -0
- package/src/runtime/conversation-store.ts +61 -0
- package/src/runtime/lifecycle-orchestrator.ts +90 -0
- package/src/runtime/tool-orchestrator.ts +177 -0
- package/src/runtime/turn-processor.ts +250 -0
- package/src/streaming.test.ts +197 -0
- package/src/streaming.ts +307 -0
- package/src/teams/index.ts +63 -0
- package/src/teams/multi-agent.lifecycle.test.ts +48 -0
- package/src/teams/multi-agent.ts +1866 -0
- package/src/teams/spawn-agent-tool.test.ts +172 -0
- package/src/teams/spawn-agent-tool.ts +223 -0
- package/src/teams/team-tools.test.ts +448 -0
- package/src/teams/team-tools.ts +929 -0
- package/src/tools/ask-question.ts +78 -0
- package/src/tools/create.ts +104 -0
- package/src/tools/execution.ts +311 -0
- package/src/tools/formatting.ts +73 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/registry.ts +52 -0
- package/src/tools/tools.test.ts +292 -0
- package/src/tools/validation.ts +73 -0
- package/src/types.ts +966 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HookSessionContext,
|
|
3
|
+
HookSessionContextProvider,
|
|
4
|
+
} from "@clinebot/shared";
|
|
5
|
+
import { resolveHookSessionContext } from "@clinebot/shared";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import type {
|
|
8
|
+
AgentHookControl,
|
|
9
|
+
AgentHookRunStartContext,
|
|
10
|
+
AgentHookSessionShutdownContext,
|
|
11
|
+
AgentHooks,
|
|
12
|
+
AgentHookToolCallEndContext,
|
|
13
|
+
AgentHookToolCallStartContext,
|
|
14
|
+
AgentHookTurnEndContext,
|
|
15
|
+
ToolCallRecord,
|
|
16
|
+
} from "../types.js";
|
|
17
|
+
import {
|
|
18
|
+
type RunSubprocessEventResult,
|
|
19
|
+
runSubprocessEvent,
|
|
20
|
+
} from "./subprocess-runner.js";
|
|
21
|
+
|
|
22
|
+
export const HookEventNameSchema = z.enum([
|
|
23
|
+
"agent_start",
|
|
24
|
+
"agent_resume",
|
|
25
|
+
"agent_abort",
|
|
26
|
+
"agent_end",
|
|
27
|
+
"tool_call",
|
|
28
|
+
"tool_result",
|
|
29
|
+
"prompt_submit",
|
|
30
|
+
"pre_compact",
|
|
31
|
+
"session_shutdown",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export type HookEventName = z.infer<typeof HookEventNameSchema>;
|
|
35
|
+
|
|
36
|
+
const StringMapSchema = z.record(z.string(), z.string());
|
|
37
|
+
|
|
38
|
+
export interface HookOutput {
|
|
39
|
+
contextModification: string;
|
|
40
|
+
cancel: boolean;
|
|
41
|
+
review?: boolean;
|
|
42
|
+
errorMessage: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const HookOutputSchema = z
|
|
46
|
+
.object({
|
|
47
|
+
contextModification: z.string().optional(),
|
|
48
|
+
cancel: z.boolean().optional(),
|
|
49
|
+
review: z.boolean().optional(),
|
|
50
|
+
errorMessage: z.string().optional(),
|
|
51
|
+
context: z.string().optional(),
|
|
52
|
+
overrideInput: z.unknown().optional(),
|
|
53
|
+
})
|
|
54
|
+
.passthrough();
|
|
55
|
+
|
|
56
|
+
export interface PreToolUseData {
|
|
57
|
+
toolName: string;
|
|
58
|
+
parameters: Record<string, string>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface PostToolUseData {
|
|
62
|
+
toolName: string;
|
|
63
|
+
parameters: Record<string, string>;
|
|
64
|
+
result: string;
|
|
65
|
+
success: boolean;
|
|
66
|
+
executionTimeMs: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface UserPromptSubmitData {
|
|
70
|
+
prompt: string;
|
|
71
|
+
attachments: string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface TaskStartData {
|
|
75
|
+
taskMetadata: Record<string, string>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface TaskResumeData {
|
|
79
|
+
taskMetadata: Record<string, string>;
|
|
80
|
+
previousState: Record<string, string>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface TaskCancelData {
|
|
84
|
+
taskMetadata: Record<string, string>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TaskCompleteData {
|
|
88
|
+
taskMetadata: Record<string, string>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PreCompactData {
|
|
92
|
+
taskId: string;
|
|
93
|
+
ulid: string;
|
|
94
|
+
contextSize: number;
|
|
95
|
+
compactionStrategy: string;
|
|
96
|
+
previousApiReqIndex: number;
|
|
97
|
+
tokensIn: number;
|
|
98
|
+
tokensOut: number;
|
|
99
|
+
tokensInCache: number;
|
|
100
|
+
tokensOutCache: number;
|
|
101
|
+
deletedRangeStart: number;
|
|
102
|
+
deletedRangeEnd: number;
|
|
103
|
+
contextJsonPath: string;
|
|
104
|
+
contextRawPath: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const PreToolUseDataSchema = z.object({
|
|
108
|
+
toolName: z.string(),
|
|
109
|
+
parameters: StringMapSchema,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const PostToolUseDataSchema = z.object({
|
|
113
|
+
toolName: z.string(),
|
|
114
|
+
parameters: StringMapSchema,
|
|
115
|
+
result: z.string(),
|
|
116
|
+
success: z.boolean(),
|
|
117
|
+
executionTimeMs: z.number(),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const UserPromptSubmitDataSchema = z.object({
|
|
121
|
+
prompt: z.string(),
|
|
122
|
+
attachments: z.array(z.string()),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const TaskStartDataSchema = z.object({ taskMetadata: StringMapSchema });
|
|
126
|
+
const TaskResumeDataSchema = z.object({
|
|
127
|
+
taskMetadata: StringMapSchema,
|
|
128
|
+
previousState: StringMapSchema,
|
|
129
|
+
});
|
|
130
|
+
const TaskCancelDataSchema = z.object({ taskMetadata: StringMapSchema });
|
|
131
|
+
const TaskCompleteDataSchema = z.object({ taskMetadata: StringMapSchema });
|
|
132
|
+
|
|
133
|
+
const PreCompactDataSchema = z.object({
|
|
134
|
+
taskId: z.string(),
|
|
135
|
+
ulid: z.string(),
|
|
136
|
+
contextSize: z.number(),
|
|
137
|
+
compactionStrategy: z.string(),
|
|
138
|
+
previousApiReqIndex: z.number(),
|
|
139
|
+
tokensIn: z.number(),
|
|
140
|
+
tokensOut: z.number(),
|
|
141
|
+
tokensInCache: z.number(),
|
|
142
|
+
tokensOutCache: z.number(),
|
|
143
|
+
deletedRangeStart: z.number(),
|
|
144
|
+
deletedRangeEnd: z.number(),
|
|
145
|
+
contextJsonPath: z.string(),
|
|
146
|
+
contextRawPath: z.string(),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export interface HookEventPayloadBase {
|
|
150
|
+
clineVersion: string;
|
|
151
|
+
hookName: HookEventName;
|
|
152
|
+
timestamp: string;
|
|
153
|
+
taskId: string;
|
|
154
|
+
sessionContext?: HookSessionContext;
|
|
155
|
+
workspaceRoots: string[];
|
|
156
|
+
userId: string;
|
|
157
|
+
agent_id: string;
|
|
158
|
+
parent_agent_id: string | null;
|
|
159
|
+
preToolUse?: PreToolUseData | undefined;
|
|
160
|
+
postToolUse?: PostToolUseData | undefined;
|
|
161
|
+
userPromptSubmit?: UserPromptSubmitData | undefined;
|
|
162
|
+
taskStart?: TaskStartData | undefined;
|
|
163
|
+
taskResume?: TaskResumeData | undefined;
|
|
164
|
+
taskCancel?: TaskCancelData | undefined;
|
|
165
|
+
taskComplete?: TaskCompleteData | undefined;
|
|
166
|
+
preCompact?: PreCompactData | undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface ToolCallHookPayload extends HookEventPayloadBase {
|
|
170
|
+
hookName: "tool_call";
|
|
171
|
+
iteration: number;
|
|
172
|
+
tool_call: {
|
|
173
|
+
id: string;
|
|
174
|
+
name: string;
|
|
175
|
+
input: unknown;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ToolResultHookPayload extends HookEventPayloadBase {
|
|
180
|
+
hookName: "tool_result";
|
|
181
|
+
iteration: number;
|
|
182
|
+
tool_result: ToolCallRecord;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface AgentEndHookPayload extends HookEventPayloadBase {
|
|
186
|
+
hookName: "agent_end";
|
|
187
|
+
iteration: number;
|
|
188
|
+
turn: AgentHookTurnEndContext["turn"];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface AgentStartHookPayload extends HookEventPayloadBase {
|
|
192
|
+
hookName: "agent_start";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface AgentResumeHookPayload extends HookEventPayloadBase {
|
|
196
|
+
hookName: "agent_resume";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface AgentAbortHookPayload extends HookEventPayloadBase {
|
|
200
|
+
hookName: "agent_abort";
|
|
201
|
+
reason?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface PromptSubmitHookPayload extends HookEventPayloadBase {
|
|
205
|
+
hookName: "prompt_submit";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface PreCompactHookPayload extends HookEventPayloadBase {
|
|
209
|
+
hookName: "pre_compact";
|
|
210
|
+
preCompact: PreCompactData;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface SessionShutdownHookPayload extends HookEventPayloadBase {
|
|
214
|
+
hookName: "session_shutdown";
|
|
215
|
+
reason?: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export type HookEventPayload =
|
|
219
|
+
| ToolCallHookPayload
|
|
220
|
+
| ToolResultHookPayload
|
|
221
|
+
| AgentStartHookPayload
|
|
222
|
+
| AgentResumeHookPayload
|
|
223
|
+
| AgentAbortHookPayload
|
|
224
|
+
| PromptSubmitHookPayload
|
|
225
|
+
| PreCompactHookPayload
|
|
226
|
+
| AgentEndHookPayload
|
|
227
|
+
| SessionShutdownHookPayload;
|
|
228
|
+
|
|
229
|
+
export const HookEventPayloadSchema = z
|
|
230
|
+
.object({
|
|
231
|
+
clineVersion: z.string(),
|
|
232
|
+
hookName: HookEventNameSchema,
|
|
233
|
+
timestamp: z.string(),
|
|
234
|
+
taskId: z.string(),
|
|
235
|
+
sessionContext: z
|
|
236
|
+
.object({
|
|
237
|
+
rootSessionId: z.string().optional(),
|
|
238
|
+
hookLogPath: z.string().optional(),
|
|
239
|
+
})
|
|
240
|
+
.optional(),
|
|
241
|
+
workspaceRoots: z.array(z.string()),
|
|
242
|
+
userId: z.string(),
|
|
243
|
+
agent_id: z.string(),
|
|
244
|
+
parent_agent_id: z.string().nullable(),
|
|
245
|
+
iteration: z.number().optional(),
|
|
246
|
+
reason: z.string().optional(),
|
|
247
|
+
tool_call: z
|
|
248
|
+
.object({
|
|
249
|
+
id: z.string(),
|
|
250
|
+
name: z.string(),
|
|
251
|
+
input: z.unknown(),
|
|
252
|
+
})
|
|
253
|
+
.optional(),
|
|
254
|
+
tool_result: z.custom<ToolCallRecord>().optional(),
|
|
255
|
+
turn: z.custom<AgentHookTurnEndContext["turn"]>().optional(),
|
|
256
|
+
preToolUse: PreToolUseDataSchema.optional(),
|
|
257
|
+
postToolUse: PostToolUseDataSchema.optional(),
|
|
258
|
+
userPromptSubmit: UserPromptSubmitDataSchema.optional(),
|
|
259
|
+
taskStart: TaskStartDataSchema.optional(),
|
|
260
|
+
taskResume: TaskResumeDataSchema.optional(),
|
|
261
|
+
taskCancel: TaskCancelDataSchema.optional(),
|
|
262
|
+
taskComplete: TaskCompleteDataSchema.optional(),
|
|
263
|
+
preCompact: PreCompactDataSchema.optional(),
|
|
264
|
+
})
|
|
265
|
+
.passthrough();
|
|
266
|
+
|
|
267
|
+
export function parseHookEventPayload(
|
|
268
|
+
value: unknown,
|
|
269
|
+
): HookEventPayload | undefined {
|
|
270
|
+
const parsed = HookEventPayloadSchema.safeParse(value);
|
|
271
|
+
if (!parsed.success) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return parsed.data as HookEventPayload;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface RunHookOptions {
|
|
278
|
+
command?: string[];
|
|
279
|
+
cwd?: string;
|
|
280
|
+
env?: NodeJS.ProcessEnv;
|
|
281
|
+
detached?: boolean;
|
|
282
|
+
timeoutMs?: number;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export type RunHookResult = RunSubprocessEventResult;
|
|
286
|
+
|
|
287
|
+
const DEFAULT_HOOK_COMMAND = ["agent", "hook"];
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Dispatch a single hook event to an external CLI.
|
|
291
|
+
* Payload is serialized as JSON and piped via stdin.
|
|
292
|
+
*/
|
|
293
|
+
export async function runHook(
|
|
294
|
+
payload: HookEventPayload,
|
|
295
|
+
options: RunHookOptions = {},
|
|
296
|
+
): Promise<RunHookResult | undefined> {
|
|
297
|
+
const command = options.command ?? DEFAULT_HOOK_COMMAND;
|
|
298
|
+
return await runSubprocessEvent(payload, {
|
|
299
|
+
command,
|
|
300
|
+
cwd: options.cwd,
|
|
301
|
+
env: options.env,
|
|
302
|
+
detached: options.detached,
|
|
303
|
+
timeoutMs: options.timeoutMs,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export interface SubprocessHooksOptions {
|
|
308
|
+
command?: string[];
|
|
309
|
+
cwd?: string;
|
|
310
|
+
env?: NodeJS.ProcessEnv;
|
|
311
|
+
/**
|
|
312
|
+
* Timeout in milliseconds for blocking tool_call hook invocation.
|
|
313
|
+
*/
|
|
314
|
+
timeoutMs?: number;
|
|
315
|
+
/**
|
|
316
|
+
* Optional callback for non-fatal hook dispatch errors.
|
|
317
|
+
*/
|
|
318
|
+
onDispatchError?: (error: Error, payload: HookEventPayload) => void;
|
|
319
|
+
/**
|
|
320
|
+
* Optional callback invoked after a hook dispatch is attempted.
|
|
321
|
+
*/
|
|
322
|
+
onDispatch?: (event: {
|
|
323
|
+
payload: HookEventPayload;
|
|
324
|
+
result?: RunHookResult;
|
|
325
|
+
detached: boolean;
|
|
326
|
+
}) => void;
|
|
327
|
+
/**
|
|
328
|
+
* Optional context attached to every hook payload.
|
|
329
|
+
* Use this to scope hook events to a root runtime session without global env state.
|
|
330
|
+
*/
|
|
331
|
+
sessionContext?: HookSessionContextProvider;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface SubprocessHookControl {
|
|
335
|
+
hooks: AgentHooks;
|
|
336
|
+
shutdown: (ctx: {
|
|
337
|
+
agentId: string;
|
|
338
|
+
conversationId: string;
|
|
339
|
+
parentAgentId: string | null;
|
|
340
|
+
reason?: string;
|
|
341
|
+
}) => Promise<void>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function toError(error: unknown): Error {
|
|
345
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function toHookControl(value: unknown): AgentHookControl | undefined {
|
|
349
|
+
if (!value || typeof value !== "object") {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
const parsed = HookOutputSchema.safeParse(value);
|
|
353
|
+
if (!parsed.success) {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
const maybe = parsed.data;
|
|
357
|
+
const hasControlKey =
|
|
358
|
+
"cancel" in maybe ||
|
|
359
|
+
"review" in maybe ||
|
|
360
|
+
"context" in maybe ||
|
|
361
|
+
"contextModification" in maybe ||
|
|
362
|
+
"overrideInput" in maybe ||
|
|
363
|
+
"errorMessage" in maybe;
|
|
364
|
+
if (!hasControlKey) {
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
const contextFromHook =
|
|
368
|
+
typeof maybe.context === "string"
|
|
369
|
+
? maybe.context
|
|
370
|
+
: typeof maybe.contextModification === "string"
|
|
371
|
+
? maybe.contextModification
|
|
372
|
+
: typeof maybe.errorMessage === "string" &&
|
|
373
|
+
maybe.errorMessage.length > 0
|
|
374
|
+
? maybe.errorMessage
|
|
375
|
+
: undefined;
|
|
376
|
+
return {
|
|
377
|
+
cancel: typeof maybe.cancel === "boolean" ? maybe.cancel : undefined,
|
|
378
|
+
review: typeof maybe.review === "boolean" ? maybe.review : undefined,
|
|
379
|
+
context: contextFromHook,
|
|
380
|
+
overrideInput: Object.hasOwn(maybe, "overrideInput")
|
|
381
|
+
? maybe.overrideInput
|
|
382
|
+
: undefined,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function mapParams(input: unknown): Record<string, string> {
|
|
387
|
+
if (!input || typeof input !== "object") {
|
|
388
|
+
return {};
|
|
389
|
+
}
|
|
390
|
+
const output: Record<string, string> = {};
|
|
391
|
+
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
|
|
392
|
+
if (typeof value === "string") {
|
|
393
|
+
output[key] = value;
|
|
394
|
+
} else {
|
|
395
|
+
output[key] = JSON.stringify(value);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return output;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function basePayload(
|
|
402
|
+
hookName: HookEventName,
|
|
403
|
+
ctx: {
|
|
404
|
+
agentId: string;
|
|
405
|
+
conversationId: string;
|
|
406
|
+
parentAgentId: string | null;
|
|
407
|
+
},
|
|
408
|
+
options: SubprocessHooksOptions,
|
|
409
|
+
): HookEventPayloadBase {
|
|
410
|
+
const env = options.env ?? process.env;
|
|
411
|
+
const userId = env.CLINE_USER_ID?.trim() || env.USER?.trim() || "unknown";
|
|
412
|
+
const workspaceRoot = options.cwd || process.cwd();
|
|
413
|
+
return {
|
|
414
|
+
clineVersion: env.CLINE_VERSION?.trim() || "",
|
|
415
|
+
hookName,
|
|
416
|
+
timestamp: new Date().toISOString(),
|
|
417
|
+
taskId: ctx.conversationId,
|
|
418
|
+
sessionContext: resolveHookSessionContext(options.sessionContext),
|
|
419
|
+
workspaceRoots: workspaceRoot ? [workspaceRoot] : [],
|
|
420
|
+
userId,
|
|
421
|
+
agent_id: ctx.agentId,
|
|
422
|
+
parent_agent_id: ctx.parentAgentId,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function isAbortReason(reason?: string): boolean {
|
|
427
|
+
const value = String(reason ?? "").toLowerCase();
|
|
428
|
+
return (
|
|
429
|
+
value.includes("cancel") ||
|
|
430
|
+
value.includes("abort") ||
|
|
431
|
+
value.includes("interrupt")
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async function dispatchDetached(
|
|
436
|
+
payload: HookEventPayload,
|
|
437
|
+
options: SubprocessHooksOptions,
|
|
438
|
+
): Promise<void> {
|
|
439
|
+
try {
|
|
440
|
+
const result = await runHook(payload, {
|
|
441
|
+
command: options.command,
|
|
442
|
+
cwd: options.cwd,
|
|
443
|
+
env: options.env,
|
|
444
|
+
detached: true,
|
|
445
|
+
});
|
|
446
|
+
options.onDispatch?.({ payload, result, detached: true });
|
|
447
|
+
} catch (error) {
|
|
448
|
+
options.onDispatchError?.(toError(error), payload);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Create lifecycle hooks that mirror Pi-style hook events:
|
|
454
|
+
* - tool_call (blocking)
|
|
455
|
+
* - tool_result (fire-and-forget)
|
|
456
|
+
* - agent_start (fire-and-forget)
|
|
457
|
+
* - prompt_submit (fire-and-forget)
|
|
458
|
+
* - agent_end (fire-and-forget)
|
|
459
|
+
* - session_shutdown (fire-and-forget via returned `shutdown()`)
|
|
460
|
+
*/
|
|
461
|
+
export function createSubprocessHooks(
|
|
462
|
+
options: SubprocessHooksOptions = {},
|
|
463
|
+
): SubprocessHookControl {
|
|
464
|
+
const onRunStart = async (
|
|
465
|
+
ctx: AgentHookRunStartContext,
|
|
466
|
+
): Promise<AgentHookControl | undefined> => {
|
|
467
|
+
const isResume =
|
|
468
|
+
(options.env ?? process.env).CLINE_HOOK_AGENT_RESUME === "1";
|
|
469
|
+
if (isResume) {
|
|
470
|
+
const resumePayload: AgentResumeHookPayload = {
|
|
471
|
+
...basePayload("agent_resume", ctx, options),
|
|
472
|
+
hookName: "agent_resume",
|
|
473
|
+
taskResume: {
|
|
474
|
+
taskMetadata: {},
|
|
475
|
+
previousState: {},
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
await dispatchDetached(resumePayload, options);
|
|
479
|
+
} else {
|
|
480
|
+
const startPayload: AgentStartHookPayload = {
|
|
481
|
+
...basePayload("agent_start", ctx, options),
|
|
482
|
+
hookName: "agent_start",
|
|
483
|
+
taskStart: { taskMetadata: {} },
|
|
484
|
+
};
|
|
485
|
+
await dispatchDetached(startPayload, options);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const promptPayload: PromptSubmitHookPayload = {
|
|
489
|
+
...basePayload("prompt_submit", ctx, options),
|
|
490
|
+
hookName: "prompt_submit",
|
|
491
|
+
userPromptSubmit: {
|
|
492
|
+
prompt: ctx.userMessage,
|
|
493
|
+
attachments: [],
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
await dispatchDetached(promptPayload, options);
|
|
497
|
+
return undefined;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
const onToolCallStart = async (
|
|
501
|
+
ctx: AgentHookToolCallStartContext,
|
|
502
|
+
): Promise<AgentHookControl | undefined> => {
|
|
503
|
+
const payload: ToolCallHookPayload = {
|
|
504
|
+
...basePayload("tool_call", ctx, options),
|
|
505
|
+
hookName: "tool_call",
|
|
506
|
+
iteration: ctx.iteration,
|
|
507
|
+
tool_call: {
|
|
508
|
+
id: ctx.call.id,
|
|
509
|
+
name: ctx.call.name,
|
|
510
|
+
input: ctx.call.input,
|
|
511
|
+
},
|
|
512
|
+
preToolUse: {
|
|
513
|
+
toolName: ctx.call.name,
|
|
514
|
+
parameters: mapParams(ctx.call.input),
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
const result = await runHook(payload, {
|
|
520
|
+
command: options.command,
|
|
521
|
+
cwd: options.cwd,
|
|
522
|
+
env: options.env,
|
|
523
|
+
detached: false,
|
|
524
|
+
timeoutMs: options.timeoutMs,
|
|
525
|
+
});
|
|
526
|
+
options.onDispatch?.({ payload, result, detached: false });
|
|
527
|
+
if (result?.timedOut) {
|
|
528
|
+
throw new Error("tool_call hook command timed out");
|
|
529
|
+
}
|
|
530
|
+
if (result?.parseError) {
|
|
531
|
+
throw new Error(
|
|
532
|
+
`tool_call hook produced invalid control JSON: ${result.parseError}`,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return toHookControl(result?.parsedJson);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
options.onDispatchError?.(toError(error), payload);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const onToolCallEnd = async (
|
|
543
|
+
ctx: AgentHookToolCallEndContext,
|
|
544
|
+
): Promise<AgentHookControl | undefined> => {
|
|
545
|
+
const payload: ToolResultHookPayload = {
|
|
546
|
+
...basePayload("tool_result", ctx, options),
|
|
547
|
+
hookName: "tool_result",
|
|
548
|
+
iteration: ctx.iteration,
|
|
549
|
+
tool_result: ctx.record,
|
|
550
|
+
postToolUse: {
|
|
551
|
+
toolName: ctx.record.name,
|
|
552
|
+
parameters: mapParams(ctx.record.input),
|
|
553
|
+
result:
|
|
554
|
+
typeof ctx.record.output === "string"
|
|
555
|
+
? ctx.record.output
|
|
556
|
+
: JSON.stringify(ctx.record.output),
|
|
557
|
+
success: !ctx.record.error,
|
|
558
|
+
executionTimeMs: ctx.record.durationMs,
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
await dispatchDetached(payload, options);
|
|
562
|
+
return undefined;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const onTurnEnd = async (
|
|
566
|
+
ctx: AgentHookTurnEndContext,
|
|
567
|
+
): Promise<AgentHookControl | undefined> => {
|
|
568
|
+
const payload: AgentEndHookPayload = {
|
|
569
|
+
...basePayload("agent_end", ctx, options),
|
|
570
|
+
hookName: "agent_end",
|
|
571
|
+
iteration: ctx.iteration,
|
|
572
|
+
turn: ctx.turn,
|
|
573
|
+
taskComplete: { taskMetadata: {} },
|
|
574
|
+
};
|
|
575
|
+
await dispatchDetached(payload, options);
|
|
576
|
+
return undefined;
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
const shutdown = async (ctx: {
|
|
580
|
+
agentId: string;
|
|
581
|
+
conversationId: string;
|
|
582
|
+
parentAgentId: string | null;
|
|
583
|
+
reason?: string;
|
|
584
|
+
}): Promise<void> => {
|
|
585
|
+
if (isAbortReason(ctx.reason)) {
|
|
586
|
+
const abortPayload: AgentAbortHookPayload = {
|
|
587
|
+
...basePayload("agent_abort", ctx, options),
|
|
588
|
+
hookName: "agent_abort",
|
|
589
|
+
reason: ctx.reason,
|
|
590
|
+
taskCancel: { taskMetadata: {} },
|
|
591
|
+
};
|
|
592
|
+
await dispatchDetached(abortPayload, options);
|
|
593
|
+
}
|
|
594
|
+
const payload: SessionShutdownHookPayload = {
|
|
595
|
+
...basePayload("session_shutdown", ctx, options),
|
|
596
|
+
hookName: "session_shutdown",
|
|
597
|
+
reason: ctx.reason,
|
|
598
|
+
};
|
|
599
|
+
await dispatchDetached(payload, options);
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
hooks: {
|
|
604
|
+
onRunStart,
|
|
605
|
+
onToolCallStart,
|
|
606
|
+
onToolCallEnd,
|
|
607
|
+
onTurnEnd,
|
|
608
|
+
onSessionShutdown: async ({
|
|
609
|
+
agentId,
|
|
610
|
+
conversationId,
|
|
611
|
+
parentAgentId,
|
|
612
|
+
reason,
|
|
613
|
+
}: AgentHookSessionShutdownContext) => {
|
|
614
|
+
await shutdown({ agentId, conversationId, parentAgentId, reason });
|
|
615
|
+
return undefined;
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
shutdown,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./index.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @clinebot/agents (Node entrypoint)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
createSubprocessHooks,
|
|
7
|
+
type HookEventName,
|
|
8
|
+
HookEventNameSchema,
|
|
9
|
+
type HookEventPayload,
|
|
10
|
+
HookEventPayloadSchema,
|
|
11
|
+
parseHookEventPayload,
|
|
12
|
+
type RunHookOptions,
|
|
13
|
+
type RunHookResult,
|
|
14
|
+
type RunSubprocessEventOptions,
|
|
15
|
+
type RunSubprocessEventResult,
|
|
16
|
+
runHook,
|
|
17
|
+
runSubprocessEvent,
|
|
18
|
+
type SubprocessHookControl,
|
|
19
|
+
type SubprocessHooksOptions,
|
|
20
|
+
} from "./hooks/node.js";
|
|
21
|
+
export * from "./index.js";
|