@clinebot/agents 0.0.0 → 0.0.2

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.
@@ -75,6 +75,7 @@ export type TeamEvent = {
75
75
  agentId: string;
76
76
  result?: AgentResult;
77
77
  error?: Error;
78
+ messages?: AgentResult["messages"];
78
79
  } | {
79
80
  type: TeamMessageType.AgentEvent;
80
81
  agentId: string;
@@ -414,6 +415,9 @@ export interface TeamRunRecord {
414
415
  endedAt?: Date;
415
416
  leaseOwner?: string;
416
417
  heartbeatAt?: Date;
418
+ lastProgressAt?: Date;
419
+ lastProgressMessage?: string;
420
+ currentActivity?: string;
417
421
  result?: AgentResult;
418
422
  error?: string;
419
423
  }
@@ -561,6 +565,8 @@ export declare class AgentTeamsRuntime {
561
565
  private requireTask;
562
566
  private assertDependenciesResolved;
563
567
  private trackMeaningfulEvent;
568
+ private recordRunActivityFromAgentEvent;
569
+ private recordRunProgress;
564
570
  private recordProgressStep;
565
571
  private emitEvent;
566
572
  }
@@ -2,13 +2,15 @@
2
2
  * Reusable spawn_agent tool for delegating tasks to sub-agents.
3
3
  */
4
4
  import type { providers as LlmsProviders } from "@clinebot/llms";
5
- import type { Tool, ToolApprovalRequest, ToolApprovalResult, ToolContext, ToolPolicy } from "@clinebot/shared";
5
+ import { type Tool, type ToolApprovalRequest, type ToolApprovalResult, type ToolContext, type ToolPolicy } from "@clinebot/shared";
6
+ import { z } from "zod";
6
7
  import type { AgentEvent, AgentExtension, AgentFinishReason, AgentHooks, BasicLogger, HookErrorMode } from "../types.js";
7
- export interface SpawnAgentInput {
8
- systemPrompt: string;
9
- task: string;
10
- maxIterations?: number;
11
- }
8
+ export declare const SpawnAgentInputSchema: z.ZodObject<{
9
+ systemPrompt: z.ZodString;
10
+ task: z.ZodString;
11
+ maxIterations: z.ZodOptional<z.ZodNumber>;
12
+ }, z.core.$strip>;
13
+ export type SpawnAgentInput = z.infer<typeof SpawnAgentInputSchema>;
12
14
  export interface SpawnAgentOutput {
13
15
  text: string;
14
16
  iterations: number;
@@ -35,11 +37,13 @@ export interface SubAgentEndContext {
35
37
  export interface SpawnAgentToolConfig {
36
38
  providerId: string;
37
39
  modelId: string;
40
+ cwd?: string;
38
41
  apiKey?: string;
39
42
  baseUrl?: string;
40
43
  providerConfig?: LlmsProviders.ProviderConfig;
41
44
  knownModels?: Record<string, LlmsProviders.ModelInfo>;
42
45
  thinking?: boolean;
46
+ clineWorkspaceMetadata?: string;
43
47
  defaultMaxIterations?: number;
44
48
  subAgentTools?: Tool[];
45
49
  createSubAgentTools?: (input: SpawnAgentInput, context: ToolContext) => Tool[] | Promise<Tool[]>;
package/dist/types.d.ts CHANGED
@@ -89,6 +89,8 @@ export interface AgentDoneEvent {
89
89
  text: string;
90
90
  /** Total number of iterations */
91
91
  iterations: number;
92
+ /** Aggregated usage information */
93
+ usage?: AgentUsage;
92
94
  }
93
95
  export interface AgentErrorEvent {
94
96
  type: "error";
@@ -99,6 +101,26 @@ export interface AgentErrorEvent {
99
101
  /** Current iteration when error occurred */
100
102
  iteration: number;
101
103
  }
104
+ export interface ConsecutiveMistakeLimitContext {
105
+ iteration: number;
106
+ consecutiveMistakes: number;
107
+ maxConsecutiveMistakes: number;
108
+ reason: "api_error" | "invalid_tool_call" | "tool_execution_failed";
109
+ details?: string;
110
+ }
111
+ export type ConsecutiveMistakeLimitDecision = {
112
+ action: "continue";
113
+ /**
114
+ * Optional guidance appended as a user message before continuing.
115
+ */
116
+ guidance?: string;
117
+ } | {
118
+ action: "stop";
119
+ /**
120
+ * Optional reason surfaced when stopping due to the limit.
121
+ */
122
+ reason?: string;
123
+ };
102
124
  /**
103
125
  * Hook error handling behavior.
104
126
  * - "ignore": swallow hook errors and continue agent execution
@@ -539,11 +561,12 @@ export declare const AgentResultSchema: z.ZodObject<{
539
561
  /**
540
562
  * Reasoning effort level for capable models
541
563
  */
542
- export type ReasoningEffort = "low" | "medium" | "high";
564
+ export type ReasoningEffort = "low" | "medium" | "high" | "xhigh";
543
565
  export declare const ReasoningEffortSchema: z.ZodEnum<{
544
566
  low: "low";
545
567
  high: "high";
546
568
  medium: "medium";
569
+ xhigh: "xhigh";
547
570
  }>;
548
571
  /**
549
572
  * Configuration for creating an Agent
@@ -603,6 +626,13 @@ export interface AgentConfig {
603
626
  * @default 120000 (2 minutes)
604
627
  */
605
628
  apiTimeoutMs?: number;
629
+ /**
630
+ * Maximum consecutive internal mistakes before escalation.
631
+ * Mistakes include API turn failures, invalid/missing tool-call arguments,
632
+ * and iterations where every executed tool call fails.
633
+ * @default 3
634
+ */
635
+ maxConsecutiveMistakes?: number;
606
636
  /**
607
637
  * Optional runtime file-content loader used when user files are attached.
608
638
  * When omitted, attached files will be represented as loader errors.
@@ -659,6 +689,10 @@ export interface AgentConfig {
659
689
  * Optional callback to request client approval when a tool policy disables auto-approval.
660
690
  */
661
691
  requestToolApproval?: (request: ToolApprovalRequest) => Promise<ToolApprovalResult> | ToolApprovalResult;
692
+ /**
693
+ * Optional callback invoked when consecutive mistakes reach maxConsecutiveMistakes.
694
+ */
695
+ onConsecutiveMistakeLimitReached?: (context: ConsecutiveMistakeLimitContext) => Promise<ConsecutiveMistakeLimitDecision> | ConsecutiveMistakeLimitDecision;
662
696
  /**
663
697
  * Optional logger for tracing agent loop lifecycle and recoverable failures.
664
698
  */
@@ -743,6 +777,7 @@ export declare const AgentConfigSchema: z.ZodObject<{
743
777
  maxParallelToolCalls: z.ZodDefault<z.ZodNumber>;
744
778
  maxTokensPerTurn: z.ZodOptional<z.ZodNumber>;
745
779
  apiTimeoutMs: z.ZodDefault<z.ZodNumber>;
780
+ maxConsecutiveMistakes: z.ZodDefault<z.ZodNumber>;
746
781
  userFileContentLoader: z.ZodOptional<z.ZodFunction<z.core.$ZodTuple<readonly [z.ZodString], z.core.$ZodFunctionOut>, z.ZodPromise<z.ZodString>>>;
747
782
  reminderAfterIterations: z.ZodDefault<z.ZodNumber>;
748
783
  reminderText: z.ZodOptional<z.ZodString>;
@@ -750,6 +785,7 @@ export declare const AgentConfigSchema: z.ZodObject<{
750
785
  low: "low";
751
786
  high: "high";
752
787
  medium: "medium";
788
+ xhigh: "xhigh";
753
789
  }>>;
754
790
  thinkingBudgetTokens: z.ZodOptional<z.ZodNumber>;
755
791
  thinking: z.ZodOptional<z.ZodBoolean>;
@@ -784,6 +820,29 @@ export declare const AgentConfigSchema: z.ZodObject<{
784
820
  approved: z.ZodBoolean;
785
821
  reason: z.ZodOptional<z.ZodString>;
786
822
  }, z.core.$strip>>]>>>;
823
+ onConsecutiveMistakeLimitReached: z.ZodOptional<z.ZodFunction<z.core.$ZodTuple<readonly [z.ZodObject<{
824
+ iteration: z.ZodNumber;
825
+ consecutiveMistakes: z.ZodNumber;
826
+ maxConsecutiveMistakes: z.ZodNumber;
827
+ reason: z.ZodEnum<{
828
+ api_error: "api_error";
829
+ invalid_tool_call: "invalid_tool_call";
830
+ tool_execution_failed: "tool_execution_failed";
831
+ }>;
832
+ details: z.ZodOptional<z.ZodString>;
833
+ }, z.core.$strip>], z.core.$ZodFunctionOut>, z.ZodUnion<readonly [z.ZodObject<{
834
+ action: z.ZodLiteral<"continue">;
835
+ guidance: z.ZodOptional<z.ZodString>;
836
+ }, z.core.$strip>, z.ZodObject<{
837
+ action: z.ZodLiteral<"stop">;
838
+ reason: z.ZodOptional<z.ZodString>;
839
+ }, z.core.$strip>, z.ZodPromise<z.ZodUnion<readonly [z.ZodObject<{
840
+ action: z.ZodLiteral<"continue">;
841
+ guidance: z.ZodOptional<z.ZodString>;
842
+ }, z.core.$strip>, z.ZodObject<{
843
+ action: z.ZodLiteral<"stop">;
844
+ reason: z.ZodOptional<z.ZodString>;
845
+ }, z.core.$strip>]>>]>>>;
787
846
  logger: z.ZodOptional<z.ZodCustom<BasicLogger, BasicLogger>>;
788
847
  abortSignal: z.ZodOptional<z.ZodCustom<AbortSignal, AbortSignal>>;
789
848
  }, z.core.$strip>;
@@ -807,6 +866,12 @@ export interface ProcessedTurn {
807
866
  reasoning?: string;
808
867
  /** Tool calls requested by the model */
809
868
  toolCalls: PendingToolCall[];
869
+ /** Model-emitted tool calls that were invalid or missing required fields */
870
+ invalidToolCalls: Array<{
871
+ id: string;
872
+ name?: string;
873
+ reason: "missing_name" | "missing_arguments" | "invalid_arguments";
874
+ }>;
810
875
  /** Token usage for this turn */
811
876
  usage: {
812
877
  inputTokens: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clinebot/agents",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cline/cline.git",
@@ -8,23 +8,23 @@
8
8
  },
9
9
  "main": "dist/index.js",
10
10
  "dependencies": {
11
- "@clinebot/llms": "workspace:*",
12
- "@clinebot/shared": "workspace:*",
11
+ "@clinebot/llms": "0.0.2",
13
12
  "zod": "^4.3.6"
14
13
  },
15
14
  "exports": {
16
15
  ".": {
17
- "development": "./src/index.ts",
16
+ "browser": "./dist/index.browser.js",
17
+ "development": "./dist/index.js",
18
18
  "types": "./dist/index.d.ts",
19
19
  "import": "./dist/index.js"
20
20
  },
21
21
  "./node": {
22
- "development": "./src/index.node.ts",
23
- "types": "./src/index.node.ts",
22
+ "development": "./dist/index.node.js",
23
+ "types": "./dist/index.node.d.ts",
24
24
  "import": "./dist/index.node.js"
25
25
  },
26
26
  "./browser": {
27
- "development": "./src/index.browser.ts",
27
+ "development": "./dist/index.browser.js",
28
28
  "types": "./dist/index.browser.d.ts",
29
29
  "import": "./dist/index.browser.js"
30
30
  }
package/src/agent.test.ts CHANGED
@@ -197,6 +197,120 @@ describe("Agent", () => {
197
197
  expect(logger.error).not.toHaveBeenCalled();
198
198
  });
199
199
 
200
+ it("fails after reaching max consecutive mistakes by default", async () => {
201
+ const { Agent } = await import("./agent.js");
202
+ const handler = makeHandler([
203
+ [
204
+ {
205
+ type: "done",
206
+ id: "r1",
207
+ success: false,
208
+ error: "upstream api timeout",
209
+ },
210
+ ],
211
+ [
212
+ {
213
+ type: "done",
214
+ id: "r2",
215
+ success: false,
216
+ error: "upstream api timeout",
217
+ },
218
+ ],
219
+ ]);
220
+ createHandlerMock.mockReturnValue(handler);
221
+
222
+ const agent = new Agent({
223
+ providerId: "anthropic",
224
+ modelId: "mock-model",
225
+ systemPrompt: "Handle retries",
226
+ tools: [],
227
+ maxConsecutiveMistakes: 2,
228
+ });
229
+
230
+ await expect(agent.run("retry")).rejects.toThrow("upstream api timeout");
231
+ expect(handler.createMessage).toHaveBeenCalledTimes(2);
232
+ });
233
+
234
+ it("fails immediately on non-recoverable API errors", async () => {
235
+ const { Agent } = await import("./agent.js");
236
+ const handler = makeHandler([
237
+ [
238
+ {
239
+ type: "done",
240
+ id: "r1",
241
+ success: false,
242
+ error:
243
+ '{"error":{"code":404,"message":"models/gemini-flash-latest-1 is not found"}}',
244
+ },
245
+ ],
246
+ ]);
247
+ createHandlerMock.mockReturnValue(handler);
248
+
249
+ const agent = new Agent({
250
+ providerId: "gemini",
251
+ modelId: "gemini-flash-latest-1",
252
+ systemPrompt: "Handle retries",
253
+ tools: [],
254
+ maxConsecutiveMistakes: 3,
255
+ });
256
+
257
+ await expect(agent.run("retry")).rejects.toThrow("404");
258
+ expect(handler.createMessage).toHaveBeenCalledTimes(1);
259
+ expect(
260
+ agent
261
+ .getMessages()
262
+ .some((message) =>
263
+ JSON.stringify(message).includes("previous turn failed"),
264
+ ),
265
+ ).toBe(false);
266
+ });
267
+
268
+ it("continues after mistake limit when callback returns continue", async () => {
269
+ const { Agent } = await import("./agent.js");
270
+ const handler = makeHandler([
271
+ [
272
+ {
273
+ type: "done",
274
+ id: "r1",
275
+ success: false,
276
+ error: "temporary api failure",
277
+ },
278
+ ],
279
+ [
280
+ {
281
+ type: "done",
282
+ id: "r2",
283
+ success: false,
284
+ error: "temporary api failure",
285
+ },
286
+ ],
287
+ [
288
+ { type: "text", id: "r3", text: "Recovered" },
289
+ { type: "usage", id: "r3", inputTokens: 3, outputTokens: 2 },
290
+ { type: "done", id: "r3", success: true },
291
+ ],
292
+ ]);
293
+ createHandlerMock.mockReturnValue(handler);
294
+
295
+ const onConsecutiveMistakeLimitReached = vi.fn().mockResolvedValue({
296
+ action: "continue",
297
+ guidance: "mistake_limit_reached: continue and recover",
298
+ });
299
+ const agent = new Agent({
300
+ providerId: "anthropic",
301
+ modelId: "mock-model",
302
+ systemPrompt: "Handle retries",
303
+ tools: [],
304
+ maxConsecutiveMistakes: 2,
305
+ onConsecutiveMistakeLimitReached,
306
+ });
307
+
308
+ const result = await agent.run("retry");
309
+ expect(onConsecutiveMistakeLimitReached).toHaveBeenCalledTimes(1);
310
+ expect(result.finishReason).toBe("completed");
311
+ expect(result.text).toBe("Recovered");
312
+ });
313
+
200
314
  it("executes tool calls and applies tool policy approval", async () => {
201
315
  const { Agent } = await import("./agent.js");
202
316
  const mathTool: Tool<{ a: number; b: number }, { total: number }> =
@@ -451,6 +565,78 @@ describe("Agent", () => {
451
565
  });
452
566
  });
453
567
 
568
+ it("deduplicates streamed tool calls when function.id and call_id both appear", async () => {
569
+ const { Agent } = await import("./agent.js");
570
+ const executeRunCommands = vi.fn(
571
+ async ({ commands }: { commands: string[] }) => commands,
572
+ );
573
+ const runCommandsTool = createTool({
574
+ name: "run_commands",
575
+ description: "Run commands",
576
+ inputSchema: {
577
+ type: "object",
578
+ properties: {
579
+ commands: {
580
+ type: "array",
581
+ items: { type: "string" },
582
+ },
583
+ },
584
+ required: ["commands"],
585
+ },
586
+ execute: executeRunCommands,
587
+ }) as Tool;
588
+
589
+ const handler = makeHandler([
590
+ [
591
+ {
592
+ type: "tool_calls",
593
+ id: "r1",
594
+ tool_call: {
595
+ function: {
596
+ id: "fc_1",
597
+ name: "run_commands",
598
+ arguments: '{"commands":["pwd"]}',
599
+ },
600
+ },
601
+ },
602
+ {
603
+ type: "tool_calls",
604
+ id: "r1",
605
+ tool_call: {
606
+ call_id: "call_1",
607
+ function: {
608
+ id: "fc_1",
609
+ name: "run_commands",
610
+ arguments: '{"commands":["pwd"]}',
611
+ },
612
+ },
613
+ },
614
+ { type: "usage", id: "r1", inputTokens: 4, outputTokens: 2 },
615
+ { type: "done", id: "r1", success: true },
616
+ ],
617
+ [
618
+ { type: "text", id: "r2", text: "Done" },
619
+ { type: "usage", id: "r2", inputTokens: 2, outputTokens: 1 },
620
+ { type: "done", id: "r2", success: true },
621
+ ],
622
+ ]);
623
+ createHandlerMock.mockReturnValue(handler);
624
+
625
+ const agent = new Agent({
626
+ providerId: "anthropic",
627
+ modelId: "mock-model",
628
+ systemPrompt: "Use tools",
629
+ tools: [runCommandsTool],
630
+ });
631
+
632
+ const result = await agent.run("run a command");
633
+ expect(result.finishReason).toBe("completed");
634
+ expect(result.toolCalls).toHaveLength(1);
635
+ expect(result.toolCalls[0]?.error).toBeUndefined();
636
+ expect(result.toolCalls[0]?.output).toEqual(["pwd"]);
637
+ expect(executeRunCommands).toHaveBeenCalledTimes(1);
638
+ });
639
+
454
640
  it("passes through array-shaped read_files tool args", async () => {
455
641
  const { Agent } = await import("./agent.js");
456
642
  const readFilesTool = createTool({