@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,172 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import type { AgentExtension } from "../types.js";
3
+
4
+ const runMock = vi.fn();
5
+ const getAgentIdMock = vi.fn(() => "sub-agent-1");
6
+ const getConversationIdMock = vi.fn(() => "conv-sub-1");
7
+ const agentConstructorSpy = vi.fn();
8
+
9
+ vi.mock("../agent.js", () => ({
10
+ Agent: class MockAgent {
11
+ constructor(config: unknown) {
12
+ agentConstructorSpy(config);
13
+ }
14
+
15
+ getAgentId(): string {
16
+ return getAgentIdMock();
17
+ }
18
+
19
+ getConversationId(): string {
20
+ return getConversationIdMock();
21
+ }
22
+
23
+ async run(input: string): Promise<unknown> {
24
+ return runMock(input);
25
+ }
26
+ },
27
+ }));
28
+
29
+ describe("createSpawnAgentTool", () => {
30
+ beforeEach(() => {
31
+ vi.clearAllMocks();
32
+ });
33
+
34
+ it("creates a sub-agent, forwards callbacks, and returns normalized output", async () => {
35
+ const { createSpawnAgentTool } = await import("./spawn-agent-tool.js");
36
+ runMock.mockResolvedValue({
37
+ text: "sub-agent result",
38
+ iterations: 2,
39
+ finishReason: "completed",
40
+ usage: { inputTokens: 11, outputTokens: 7 },
41
+ });
42
+
43
+ const onSubAgentStart = vi.fn();
44
+ const onSubAgentEnd = vi.fn();
45
+ const createSubAgentTools = vi.fn().mockResolvedValue([]);
46
+ const extensions = [
47
+ {
48
+ name: "sample-ext",
49
+ manifest: { capabilities: ["hooks"], hookStages: ["runtime_event"] },
50
+ onRuntimeEvent: vi.fn(),
51
+ } as AgentExtension,
52
+ ];
53
+
54
+ const tool = createSpawnAgentTool({
55
+ providerId: "anthropic",
56
+ modelId: "mock-model",
57
+ defaultMaxIterations: 4,
58
+ createSubAgentTools,
59
+ extensions,
60
+ onSubAgentStart,
61
+ onSubAgentEnd,
62
+ });
63
+
64
+ const output = await tool.execute(
65
+ {
66
+ systemPrompt: "You are focused",
67
+ task: "Do delegated work",
68
+ },
69
+ {
70
+ agentId: "parent-1",
71
+ conversationId: "conv-parent",
72
+ iteration: 3,
73
+ },
74
+ );
75
+
76
+ expect(createSubAgentTools).toHaveBeenCalledTimes(1);
77
+ expect(runMock).toHaveBeenCalledWith("Do delegated work");
78
+ expect(onSubAgentStart).toHaveBeenCalledTimes(1);
79
+ expect(onSubAgentEnd).toHaveBeenCalledTimes(1);
80
+ expect(output).toEqual({
81
+ text: "sub-agent result",
82
+ iterations: 2,
83
+ finishReason: "completed",
84
+ usage: {
85
+ inputTokens: 11,
86
+ outputTokens: 7,
87
+ },
88
+ });
89
+ expect(agentConstructorSpy).toHaveBeenCalledWith(
90
+ expect.objectContaining({
91
+ parentAgentId: "parent-1",
92
+ maxIterations: 4,
93
+ extensions,
94
+ }),
95
+ );
96
+ });
97
+
98
+ it("propagates sub-agent errors and still reports onSubAgentEnd", async () => {
99
+ const { createSpawnAgentTool } = await import("./spawn-agent-tool.js");
100
+ runMock.mockRejectedValue(new Error("sub-agent failed"));
101
+ const onSubAgentEnd = vi.fn();
102
+
103
+ const tool = createSpawnAgentTool({
104
+ providerId: "anthropic",
105
+ modelId: "mock-model",
106
+ subAgentTools: [],
107
+ onSubAgentEnd,
108
+ });
109
+
110
+ await expect(
111
+ tool.execute(
112
+ {
113
+ systemPrompt: "System",
114
+ task: "Fail task",
115
+ maxIterations: 6,
116
+ },
117
+ {
118
+ agentId: "parent-2",
119
+ conversationId: "conv-parent",
120
+ iteration: 1,
121
+ },
122
+ ),
123
+ ).rejects.toThrow("sub-agent failed");
124
+
125
+ expect(onSubAgentEnd).toHaveBeenCalledTimes(1);
126
+ expect(onSubAgentEnd).toHaveBeenCalledWith(
127
+ expect.objectContaining({
128
+ parentAgentId: "parent-2",
129
+ error: expect.any(Error),
130
+ }),
131
+ );
132
+ expect(agentConstructorSpy).toHaveBeenCalledWith(
133
+ expect.objectContaining({
134
+ maxIterations: 6,
135
+ }),
136
+ );
137
+ });
138
+
139
+ it("leaves maxIterations unset when neither input nor default is provided", async () => {
140
+ const { createSpawnAgentTool } = await import("./spawn-agent-tool.js");
141
+ runMock.mockResolvedValue({
142
+ text: "sub-agent result",
143
+ iterations: 1,
144
+ finishReason: "completed",
145
+ usage: { inputTokens: 1, outputTokens: 1 },
146
+ });
147
+
148
+ const tool = createSpawnAgentTool({
149
+ providerId: "anthropic",
150
+ modelId: "mock-model",
151
+ subAgentTools: [],
152
+ });
153
+
154
+ await tool.execute(
155
+ {
156
+ systemPrompt: "System",
157
+ task: "Do task",
158
+ },
159
+ {
160
+ agentId: "parent-3",
161
+ conversationId: "conv-parent",
162
+ iteration: 1,
163
+ },
164
+ );
165
+
166
+ expect(agentConstructorSpy).toHaveBeenCalledWith(
167
+ expect.objectContaining({
168
+ maxIterations: undefined,
169
+ }),
170
+ );
171
+ });
172
+ });
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Reusable spawn_agent tool for delegating tasks to sub-agents.
3
+ */
4
+
5
+ import type { providers as LlmsProviders } from "@clinebot/llms";
6
+ import type {
7
+ Tool,
8
+ ToolApprovalRequest,
9
+ ToolApprovalResult,
10
+ ToolContext,
11
+ ToolPolicy,
12
+ } from "@clinebot/shared";
13
+ import { Agent } from "../agent.js";
14
+ import { createTool } from "../tools/create.js";
15
+ import type {
16
+ AgentEvent,
17
+ AgentExtension,
18
+ AgentFinishReason,
19
+ AgentHooks,
20
+ BasicLogger,
21
+ HookErrorMode,
22
+ } from "../types.js";
23
+
24
+ export interface SpawnAgentInput {
25
+ systemPrompt: string;
26
+ task: string;
27
+ maxIterations?: number;
28
+ }
29
+
30
+ export interface SpawnAgentOutput {
31
+ text: string;
32
+ iterations: number;
33
+ finishReason: AgentFinishReason;
34
+ usage: {
35
+ inputTokens: number;
36
+ outputTokens: number;
37
+ };
38
+ }
39
+
40
+ export interface SubAgentStartContext {
41
+ subAgentId: string;
42
+ conversationId: string;
43
+ parentAgentId: string;
44
+ input: SpawnAgentInput;
45
+ }
46
+
47
+ export interface SubAgentEndContext {
48
+ subAgentId: string;
49
+ conversationId: string;
50
+ parentAgentId: string;
51
+ input: SpawnAgentInput;
52
+ result?: SpawnAgentOutput;
53
+ error?: Error;
54
+ }
55
+
56
+ export interface SpawnAgentToolConfig {
57
+ providerId: string;
58
+ modelId: string;
59
+ apiKey?: string;
60
+ baseUrl?: string;
61
+ providerConfig?: LlmsProviders.ProviderConfig;
62
+ knownModels?: Record<string, LlmsProviders.ModelInfo>;
63
+ thinking?: boolean;
64
+ defaultMaxIterations?: number;
65
+ subAgentTools?: Tool[];
66
+ createSubAgentTools?: (
67
+ input: SpawnAgentInput,
68
+ context: ToolContext,
69
+ ) => Tool[] | Promise<Tool[]>;
70
+ onSubAgentEvent?: (event: AgentEvent) => void;
71
+ /**
72
+ * Lifecycle hooks forwarded to spawned sub-agent runs.
73
+ */
74
+ hooks?: AgentHooks;
75
+ /**
76
+ * Extension list forwarded to spawned sub-agent runs.
77
+ */
78
+ extensions?: AgentExtension[];
79
+ /**
80
+ * Error handling mode for forwarded lifecycle hooks.
81
+ */
82
+ hookErrorMode?: HookErrorMode;
83
+ /**
84
+ * Called after a sub-agent instance is created and before it starts running.
85
+ * Errors are ignored so lifecycle observers cannot break task execution.
86
+ */
87
+ onSubAgentStart?: (context: SubAgentStartContext) => void | Promise<void>;
88
+ /**
89
+ * Called once a sub-agent run finishes (success or error).
90
+ * Errors are ignored so lifecycle observers cannot break task execution.
91
+ */
92
+ onSubAgentEnd?: (context: SubAgentEndContext) => void | Promise<void>;
93
+ /**
94
+ * Optional per-tool policy for spawned sub-agents.
95
+ */
96
+ toolPolicies?: Record<string, ToolPolicy>;
97
+ /**
98
+ * Optional approval callback for spawned sub-agent tool calls.
99
+ */
100
+ requestToolApproval?: (
101
+ request: ToolApprovalRequest,
102
+ ) => Promise<ToolApprovalResult> | ToolApprovalResult;
103
+ /**
104
+ * Optional logger forwarded to spawned sub-agent runs.
105
+ */
106
+ logger?: BasicLogger;
107
+ }
108
+
109
+ /**
110
+ * Create a spawn_agent tool that can run a delegated task with a focused sub-agent.
111
+ */
112
+ export function createSpawnAgentTool(
113
+ config: SpawnAgentToolConfig,
114
+ ): Tool<SpawnAgentInput, SpawnAgentOutput> {
115
+ return createTool<SpawnAgentInput, SpawnAgentOutput>({
116
+ name: "spawn_agent",
117
+ description: `Spawn a sub-agent with a custom system prompt for specialized tasks. Use when delegating work that benefits from focused expertise.`,
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ systemPrompt: {
122
+ type: "string",
123
+ description: "System prompt defining the sub-agent's behavior",
124
+ },
125
+ task: {
126
+ type: "string",
127
+ description: "Task for the sub-agent to complete",
128
+ },
129
+ maxIterations: {
130
+ type: "integer",
131
+ description: "Max iterations for the sub-agent",
132
+ minimum: 1,
133
+ },
134
+ },
135
+ required: ["systemPrompt", "task"],
136
+ },
137
+ execute: async (input, context) => {
138
+ const tools = config.createSubAgentTools
139
+ ? await config.createSubAgentTools(input, context)
140
+ : (config.subAgentTools ?? []);
141
+
142
+ const subAgent = new Agent({
143
+ providerId: config.providerId,
144
+ modelId: config.modelId,
145
+ apiKey: config.apiKey,
146
+ baseUrl: config.baseUrl,
147
+ providerConfig: config.providerConfig,
148
+ knownModels: config.knownModels,
149
+ thinking: config.thinking,
150
+ systemPrompt: input.systemPrompt,
151
+ tools,
152
+ maxIterations: input.maxIterations ?? config.defaultMaxIterations,
153
+ parentAgentId: context.agentId,
154
+ abortSignal: context.abortSignal,
155
+ onEvent: config.onSubAgentEvent,
156
+ hooks: config.hooks,
157
+ extensions: config.extensions,
158
+ hookErrorMode: config.hookErrorMode,
159
+ toolPolicies: config.toolPolicies,
160
+ requestToolApproval: config.requestToolApproval,
161
+ logger: config.logger,
162
+ });
163
+ const subAgentId = subAgent.getAgentId();
164
+ const conversationId = subAgent.getConversationId();
165
+ const parentAgentId = context.agentId;
166
+ if (config.onSubAgentStart) {
167
+ try {
168
+ await config.onSubAgentStart({
169
+ subAgentId,
170
+ conversationId,
171
+ parentAgentId,
172
+ input,
173
+ });
174
+ } catch {
175
+ // Best-effort observer callback.
176
+ }
177
+ }
178
+ try {
179
+ const result = await subAgent.run(input.task);
180
+ const output: SpawnAgentOutput = {
181
+ text: result.text,
182
+ iterations: result.iterations,
183
+ finishReason: result.finishReason,
184
+ usage: {
185
+ inputTokens: result.usage.inputTokens,
186
+ outputTokens: result.usage.outputTokens,
187
+ },
188
+ };
189
+ if (config.onSubAgentEnd) {
190
+ try {
191
+ await config.onSubAgentEnd({
192
+ subAgentId,
193
+ conversationId,
194
+ parentAgentId,
195
+ input,
196
+ result: output,
197
+ });
198
+ } catch {
199
+ // Best-effort observer callback.
200
+ }
201
+ }
202
+ return output;
203
+ } catch (error) {
204
+ if (config.onSubAgentEnd) {
205
+ try {
206
+ await config.onSubAgentEnd({
207
+ subAgentId,
208
+ conversationId,
209
+ parentAgentId,
210
+ input,
211
+ error: error instanceof Error ? error : new Error(String(error)),
212
+ });
213
+ } catch {
214
+ // Best-effort observer callback.
215
+ }
216
+ }
217
+ throw error;
218
+ }
219
+ },
220
+ timeoutMs: 300000,
221
+ retryable: false,
222
+ });
223
+ }