@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.
Files changed (90) hide show
  1. package/README.md +145 -0
  2. package/dist/agent-input.d.ts +2 -0
  3. package/dist/agent.d.ts +56 -0
  4. package/dist/extensions.d.ts +21 -0
  5. package/dist/hooks/engine.d.ts +42 -0
  6. package/dist/hooks/index.d.ts +2 -0
  7. package/dist/hooks/lifecycle.d.ts +5 -0
  8. package/dist/hooks/node.d.ts +2 -0
  9. package/dist/hooks/subprocess-runner.d.ts +16 -0
  10. package/dist/hooks/subprocess.d.ts +268 -0
  11. package/dist/index.browser.d.ts +1 -0
  12. package/dist/index.browser.js +49 -0
  13. package/dist/index.d.ts +15 -0
  14. package/dist/index.js +49 -0
  15. package/dist/index.node.d.ts +5 -0
  16. package/dist/index.node.js +49 -0
  17. package/dist/mcp/index.d.ts +4 -0
  18. package/dist/mcp/policies.d.ts +14 -0
  19. package/dist/mcp/tools.d.ts +9 -0
  20. package/dist/mcp/types.d.ts +35 -0
  21. package/dist/message-builder.d.ts +31 -0
  22. package/dist/prompts/cline.d.ts +1 -0
  23. package/dist/prompts/index.d.ts +1 -0
  24. package/dist/runtime/agent-runtime-bus.d.ts +13 -0
  25. package/dist/runtime/conversation-store.d.ts +16 -0
  26. package/dist/runtime/lifecycle-orchestrator.d.ts +28 -0
  27. package/dist/runtime/tool-orchestrator.d.ts +39 -0
  28. package/dist/runtime/turn-processor.d.ts +21 -0
  29. package/dist/teams/index.d.ts +3 -0
  30. package/dist/teams/multi-agent.d.ts +566 -0
  31. package/dist/teams/spawn-agent-tool.d.ts +85 -0
  32. package/dist/teams/team-tools.d.ts +51 -0
  33. package/dist/tools/ask-question.d.ts +12 -0
  34. package/dist/tools/create.d.ts +59 -0
  35. package/dist/tools/execution.d.ts +61 -0
  36. package/dist/tools/formatting.d.ts +20 -0
  37. package/dist/tools/index.d.ts +11 -0
  38. package/dist/tools/registry.d.ts +26 -0
  39. package/dist/tools/validation.d.ts +27 -0
  40. package/dist/types.d.ts +826 -0
  41. package/package.json +54 -0
  42. package/src/agent-input.ts +116 -0
  43. package/src/agent.test.ts +931 -0
  44. package/src/agent.ts +1050 -0
  45. package/src/example.test.ts +564 -0
  46. package/src/extensions.ts +337 -0
  47. package/src/hooks/engine.test.ts +163 -0
  48. package/src/hooks/engine.ts +537 -0
  49. package/src/hooks/index.ts +6 -0
  50. package/src/hooks/lifecycle.ts +239 -0
  51. package/src/hooks/node.ts +18 -0
  52. package/src/hooks/subprocess-runner.ts +140 -0
  53. package/src/hooks/subprocess.test.ts +180 -0
  54. package/src/hooks/subprocess.ts +620 -0
  55. package/src/index.browser.ts +1 -0
  56. package/src/index.node.ts +21 -0
  57. package/src/index.ts +133 -0
  58. package/src/mcp/index.ts +17 -0
  59. package/src/mcp/policies.test.ts +51 -0
  60. package/src/mcp/policies.ts +53 -0
  61. package/src/mcp/tools.test.ts +76 -0
  62. package/src/mcp/tools.ts +60 -0
  63. package/src/mcp/types.ts +41 -0
  64. package/src/message-builder.test.ts +175 -0
  65. package/src/message-builder.ts +429 -0
  66. package/src/prompts/cline.ts +49 -0
  67. package/src/prompts/index.ts +1 -0
  68. package/src/runtime/agent-runtime-bus.ts +53 -0
  69. package/src/runtime/conversation-store.ts +61 -0
  70. package/src/runtime/lifecycle-orchestrator.ts +90 -0
  71. package/src/runtime/tool-orchestrator.ts +177 -0
  72. package/src/runtime/turn-processor.ts +250 -0
  73. package/src/streaming.test.ts +197 -0
  74. package/src/streaming.ts +307 -0
  75. package/src/teams/index.ts +63 -0
  76. package/src/teams/multi-agent.lifecycle.test.ts +48 -0
  77. package/src/teams/multi-agent.ts +1866 -0
  78. package/src/teams/spawn-agent-tool.test.ts +172 -0
  79. package/src/teams/spawn-agent-tool.ts +223 -0
  80. package/src/teams/team-tools.test.ts +448 -0
  81. package/src/teams/team-tools.ts +929 -0
  82. package/src/tools/ask-question.ts +78 -0
  83. package/src/tools/create.ts +104 -0
  84. package/src/tools/execution.ts +311 -0
  85. package/src/tools/formatting.ts +73 -0
  86. package/src/tools/index.ts +45 -0
  87. package/src/tools/registry.ts +52 -0
  88. package/src/tools/tools.test.ts +292 -0
  89. package/src/tools/validation.ts +73 -0
  90. package/src/types.ts +966 -0
@@ -0,0 +1,448 @@
1
+ import { resolveTeamDataDir } from "@clinebot/shared/storage";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ import { AgentTeamsRuntime } from "./multi-agent";
4
+ import { createAgentTeamsTools } from "./team-tools";
5
+
6
+ type EnvSnapshot = {
7
+ CLINE_DATA_DIR: string | undefined;
8
+ CLINE_TEAM_DATA_DIR: string | undefined;
9
+ };
10
+
11
+ function captureEnv(): EnvSnapshot {
12
+ return {
13
+ CLINE_DATA_DIR: process.env.CLINE_DATA_DIR,
14
+ CLINE_TEAM_DATA_DIR: process.env.CLINE_TEAM_DATA_DIR,
15
+ };
16
+ }
17
+
18
+ function restoreEnv(snapshot: EnvSnapshot): void {
19
+ process.env.CLINE_DATA_DIR = snapshot.CLINE_DATA_DIR;
20
+ process.env.CLINE_TEAM_DATA_DIR = snapshot.CLINE_TEAM_DATA_DIR;
21
+ }
22
+
23
+ describe("resolveTeamDataDir", () => {
24
+ let snapshot: EnvSnapshot = captureEnv();
25
+
26
+ afterEach(() => {
27
+ restoreEnv(snapshot);
28
+ });
29
+
30
+ it("uses CLINE_TEAM_DATA_DIR when set", () => {
31
+ snapshot = captureEnv();
32
+ process.env.CLINE_TEAM_DATA_DIR = "/tmp/team-dir";
33
+ process.env.CLINE_DATA_DIR = "/tmp/cline-data";
34
+ expect(resolveTeamDataDir()).toBe("/tmp/team-dir");
35
+ });
36
+
37
+ it("falls back to CLINE_DATA_DIR/teams", () => {
38
+ snapshot = captureEnv();
39
+ delete process.env.CLINE_TEAM_DATA_DIR;
40
+ process.env.CLINE_DATA_DIR = "/tmp/cline-data";
41
+ expect(resolveTeamDataDir()).toBe("/tmp/cline-data/teams");
42
+ });
43
+ });
44
+
45
+ describe("createAgentTeamsTools schema surface", () => {
46
+ it("exposes strict object schemas for split team tools", () => {
47
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
48
+ const tools = createAgentTeamsTools({
49
+ runtime,
50
+ requesterId: "lead",
51
+ teammateRuntime: {
52
+ providerId: "anthropic",
53
+ modelId: "claude-sonnet-4-5-20250929",
54
+ },
55
+ });
56
+
57
+ const spawn = tools.find((tool) => tool.name === "team_spawn_teammate");
58
+ const createTask = tools.find((tool) => tool.name === "team_create_task");
59
+ const send = tools.find((tool) => tool.name === "team_send_message");
60
+ const createOutcome = tools.find(
61
+ (tool) => tool.name === "team_create_outcome",
62
+ );
63
+ const teamAwaitRun = tools.find((tool) => tool.name === "team_await_run");
64
+ const teamLogUpdate = tools.find((tool) => tool.name === "team_log_update");
65
+
66
+ expect(spawn?.inputSchema.type).toBe("object");
67
+ expect(createTask?.inputSchema.type).toBe("object");
68
+ expect(send?.inputSchema.type).toBe("object");
69
+ expect(createOutcome?.inputSchema.type).toBe("object");
70
+ expect(teamAwaitRun?.inputSchema.type).toBe("object");
71
+ const schema = teamLogUpdate?.inputSchema as
72
+ | { properties: Record<string, unknown>; required: unknown[] }
73
+ | undefined;
74
+ expect(schema?.properties.kind).toEqual({
75
+ type: "string",
76
+ enum: ["progress", "handoff", "blocked", "decision", "done", "error"],
77
+ });
78
+ expect(schema?.required).toEqual(["kind", "summary"]);
79
+ });
80
+
81
+ it("rejects extra fields for strict spawn schema", async () => {
82
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
83
+ const tools = createAgentTeamsTools({
84
+ runtime,
85
+ requesterId: "lead",
86
+ teammateRuntime: {
87
+ providerId: "anthropic",
88
+ modelId: "claude-sonnet-4-5-20250929",
89
+ },
90
+ });
91
+ const spawn = tools.find((tool) => tool.name === "team_spawn_teammate");
92
+ expect(spawn).toBeDefined();
93
+
94
+ await expect(
95
+ spawn?.execute(
96
+ {
97
+ agentId: "python-poet",
98
+ rolePrompt: "Write concise Python-focused haiku",
99
+ action: "spawn",
100
+ },
101
+ {
102
+ agentId: "lead",
103
+ conversationId: "conv-1",
104
+ iteration: 1,
105
+ },
106
+ ),
107
+ ).rejects.toThrow("Unrecognized key");
108
+ });
109
+
110
+ it("rejects non-object payloads for task tools", async () => {
111
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
112
+ const tools = createAgentTeamsTools({
113
+ runtime,
114
+ requesterId: "lead",
115
+ teammateRuntime: {
116
+ providerId: "anthropic",
117
+ modelId: "claude-sonnet-4-5-20250929",
118
+ },
119
+ });
120
+ const createTask = tools.find((tool) => tool.name === "team_create_task");
121
+ expect(createTask).toBeDefined();
122
+
123
+ await expect(
124
+ createTask?.execute(["create", "task"], {
125
+ agentId: "lead",
126
+ conversationId: "conv-1",
127
+ iteration: 1,
128
+ }),
129
+ ).rejects.toThrow("expected object");
130
+ });
131
+
132
+ it("rejects null placeholders for required fields", async () => {
133
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
134
+ const tools = createAgentTeamsTools({
135
+ runtime,
136
+ requesterId: "lead",
137
+ teammateRuntime: {
138
+ providerId: "anthropic",
139
+ modelId: "claude-sonnet-4-5-20250929",
140
+ },
141
+ });
142
+ const completeTask = tools.find(
143
+ (tool) => tool.name === "team_complete_task",
144
+ );
145
+ expect(completeTask).toBeDefined();
146
+
147
+ await expect(
148
+ completeTask?.execute(
149
+ {
150
+ taskId: "task_0001",
151
+ summary: null,
152
+ },
153
+ {
154
+ agentId: "lead",
155
+ conversationId: "conv-1",
156
+ iteration: 1,
157
+ },
158
+ ),
159
+ ).rejects.toThrow("expected string");
160
+ });
161
+
162
+ it("rejects null placeholders for optional fields", async () => {
163
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
164
+ const tools = createAgentTeamsTools({
165
+ runtime,
166
+ requesterId: "lead",
167
+ teammateRuntime: {
168
+ providerId: "anthropic",
169
+ modelId: "claude-sonnet-4-5-20250929",
170
+ },
171
+ });
172
+ const createTask = tools.find((tool) => tool.name === "team_create_task");
173
+ expect(createTask).toBeDefined();
174
+
175
+ await expect(
176
+ createTask?.execute(
177
+ {
178
+ title: "Investigate llms boundaries",
179
+ description: "Deep dive models and providers",
180
+ dependsOn: null,
181
+ },
182
+ {
183
+ agentId: "lead",
184
+ conversationId: "conv-1",
185
+ iteration: 1,
186
+ },
187
+ ),
188
+ ).rejects.toThrow("expected array");
189
+ });
190
+
191
+ it("defaults requiredSections for team_create_outcome", async () => {
192
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
193
+ const tools = createAgentTeamsTools({
194
+ runtime,
195
+ requesterId: "lead",
196
+ teammateRuntime: {
197
+ providerId: "anthropic",
198
+ modelId: "claude-sonnet-4-5-20250929",
199
+ },
200
+ });
201
+ const createOutcome = tools.find(
202
+ (tool) => tool.name === "team_create_outcome",
203
+ );
204
+ expect(createOutcome).toBeDefined();
205
+
206
+ await expect(
207
+ createOutcome?.execute(
208
+ {
209
+ title: "LLMS boundary redesign",
210
+ },
211
+ {
212
+ agentId: "lead",
213
+ conversationId: "conv-1",
214
+ iteration: 1,
215
+ },
216
+ ),
217
+ ).resolves.toMatchObject({
218
+ outcomeId: expect.stringMatching(/^out_/),
219
+ status: "draft",
220
+ });
221
+
222
+ const outcomes = runtime.listOutcomes();
223
+ expect(outcomes).toHaveLength(1);
224
+ expect(outcomes[0]?.requiredSections).toEqual([
225
+ "current_state",
226
+ "boundary_analysis",
227
+ "interface_proposal",
228
+ ]);
229
+ });
230
+
231
+ it("can list outcomes via dedicated list tool", async () => {
232
+ const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
233
+ const tools = createAgentTeamsTools({
234
+ runtime,
235
+ requesterId: "lead",
236
+ teammateRuntime: {
237
+ providerId: "anthropic",
238
+ modelId: "claude-sonnet-4-5-20250929",
239
+ },
240
+ });
241
+ const createOutcome = tools.find(
242
+ (tool) => tool.name === "team_create_outcome",
243
+ );
244
+ const listOutcomes = tools.find(
245
+ (tool) => tool.name === "team_list_outcomes",
246
+ );
247
+ expect(createOutcome).toBeDefined();
248
+ expect(listOutcomes).toBeDefined();
249
+
250
+ await createOutcome?.execute(
251
+ { title: "LLMS redesign" },
252
+ {
253
+ agentId: "lead",
254
+ conversationId: "conv-1",
255
+ iteration: 1,
256
+ },
257
+ );
258
+
259
+ await expect(
260
+ listOutcomes?.execute(
261
+ {},
262
+ {
263
+ agentId: "lead",
264
+ conversationId: "conv-1",
265
+ iteration: 1,
266
+ },
267
+ ),
268
+ ).resolves.toEqual(
269
+ expect.arrayContaining([
270
+ expect.objectContaining({
271
+ title: expect.any(String),
272
+ }),
273
+ ]),
274
+ );
275
+ });
276
+ });
277
+
278
+ describe("createAgentTeamsTools runtime behavior", () => {
279
+ it("forwards teammateRuntime headers when spawning teammates", async () => {
280
+ const spawnTeammate = vi.fn();
281
+ const runtime = {
282
+ getMemberRole: vi.fn(() => "lead"),
283
+ isTeammateActive: vi.fn(() => false),
284
+ spawnTeammate,
285
+ } as unknown as AgentTeamsRuntime;
286
+
287
+ const tools = createAgentTeamsTools({
288
+ runtime,
289
+ requesterId: "lead",
290
+ teammateRuntime: {
291
+ providerId: "cline",
292
+ modelId: "anthropic/claude-sonnet-4.6",
293
+ headers: { Authorization: "Bearer token" },
294
+ },
295
+ createBaseTools: () => [],
296
+ });
297
+ const spawnTool = tools.find((tool) => tool.name === "team_spawn_teammate");
298
+ expect(spawnTool).toBeDefined();
299
+
300
+ await spawnTool?.execute(
301
+ {
302
+ agentId: "investigator",
303
+ rolePrompt: "Investigate code boundaries",
304
+ },
305
+ {
306
+ agentId: "lead",
307
+ conversationId: "conv-1",
308
+ iteration: 1,
309
+ },
310
+ );
311
+
312
+ expect(spawnTeammate).toHaveBeenCalledWith(
313
+ expect.objectContaining({
314
+ config: expect.objectContaining({
315
+ headers: { Authorization: "Bearer token" },
316
+ }),
317
+ }),
318
+ );
319
+ });
320
+
321
+ it("injects workspace metadata into cline teammate system prompt", async () => {
322
+ const spawnTeammate = vi.fn();
323
+ const runtime = {
324
+ getMemberRole: vi.fn(() => "lead"),
325
+ isTeammateActive: vi.fn(() => false),
326
+ spawnTeammate,
327
+ } as unknown as AgentTeamsRuntime;
328
+
329
+ const workspaceMetadata = `# Workspace Configuration
330
+ {
331
+ "workspaces": {
332
+ "/repo/app": {
333
+ "hint": "app",
334
+ "latestGitBranchName": "main"
335
+ }
336
+ }
337
+ }`;
338
+
339
+ const tools = createAgentTeamsTools({
340
+ runtime,
341
+ requesterId: "lead",
342
+ teammateRuntime: {
343
+ providerId: "cline",
344
+ modelId: "anthropic/claude-sonnet-4.6",
345
+ cwd: "/repo/app",
346
+ clineWorkspaceMetadata: workspaceMetadata,
347
+ },
348
+ });
349
+ const spawnTool = tools.find((tool) => tool.name === "team_spawn_teammate");
350
+ expect(spawnTool).toBeDefined();
351
+
352
+ await spawnTool?.execute(
353
+ {
354
+ agentId: "researcher",
355
+ rolePrompt: "Investigate runtime boundary regressions.",
356
+ },
357
+ {
358
+ agentId: "lead",
359
+ conversationId: "conv-1",
360
+ iteration: 1,
361
+ },
362
+ );
363
+
364
+ expect(spawnTeammate).toHaveBeenCalledWith(
365
+ expect.objectContaining({
366
+ config: expect.objectContaining({
367
+ systemPrompt: expect.stringContaining(workspaceMetadata),
368
+ }),
369
+ }),
370
+ );
371
+ expect(spawnTeammate).toHaveBeenCalledWith(
372
+ expect.objectContaining({
373
+ config: expect.objectContaining({
374
+ systemPrompt: expect.stringContaining(
375
+ "# Team Teammate Role\nInvestigate runtime boundary regressions.",
376
+ ),
377
+ }),
378
+ }),
379
+ );
380
+ });
381
+
382
+ it("throws from team_await_run when async delegated run fails", async () => {
383
+ const runtime = {
384
+ awaitRun: vi.fn(async () => ({
385
+ id: "run_0001",
386
+ status: "failed",
387
+ error: "Authentication failed",
388
+ })),
389
+ } as unknown as AgentTeamsRuntime;
390
+
391
+ const tools = createAgentTeamsTools({
392
+ runtime,
393
+ requesterId: "lead",
394
+ teammateRuntime: {
395
+ providerId: "anthropic",
396
+ modelId: "claude-sonnet-4-5-20250929",
397
+ },
398
+ });
399
+ const awaitRun = tools.find((tool) => tool.name === "team_await_run");
400
+ expect(awaitRun).toBeDefined();
401
+
402
+ await expect(
403
+ awaitRun?.execute(
404
+ { runId: "run_0001" },
405
+ {
406
+ agentId: "lead",
407
+ conversationId: "conv-1",
408
+ iteration: 1,
409
+ },
410
+ ),
411
+ ).rejects.toThrow('Run "run_0001" failed: Authentication failed');
412
+ });
413
+
414
+ it("throws from team_await_all_runs when any delegated run is not successful", async () => {
415
+ const runtime = {
416
+ awaitAllRuns: vi.fn(async () => [
417
+ { id: "run_ok", status: "completed" },
418
+ { id: "run_bad", status: "failed", error: "Auth expired" },
419
+ ]),
420
+ } as unknown as AgentTeamsRuntime;
421
+
422
+ const tools = createAgentTeamsTools({
423
+ runtime,
424
+ requesterId: "lead",
425
+ teammateRuntime: {
426
+ providerId: "anthropic",
427
+ modelId: "claude-sonnet-4-5-20250929",
428
+ },
429
+ });
430
+ const awaitAllRuns = tools.find(
431
+ (tool) => tool.name === "team_await_all_runs",
432
+ );
433
+ expect(awaitAllRuns).toBeDefined();
434
+
435
+ await expect(
436
+ awaitAllRuns?.execute(
437
+ {},
438
+ {
439
+ agentId: "lead",
440
+ conversationId: "conv-1",
441
+ iteration: 1,
442
+ },
443
+ ),
444
+ ).rejects.toThrow(
445
+ "One or more runs did not complete successfully: run_bad:failed(Auth expired)",
446
+ );
447
+ });
448
+ });