@clinebot/core 0.0.32 → 0.0.34

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 (85) hide show
  1. package/dist/auth/client.d.ts +19 -0
  2. package/dist/auth/client.d.ts.map +1 -1
  3. package/dist/auth/cline.d.ts.map +1 -1
  4. package/dist/auth/oca.d.ts.map +1 -1
  5. package/dist/auth/server.d.ts +32 -0
  6. package/dist/auth/server.d.ts.map +1 -1
  7. package/dist/auth/types.d.ts +29 -0
  8. package/dist/auth/types.d.ts.map +1 -1
  9. package/dist/extensions/context/agentic-compaction.d.ts.map +1 -1
  10. package/dist/extensions/context/basic-compaction.d.ts.map +1 -1
  11. package/dist/extensions/context/compaction-shared.d.ts +1 -1
  12. package/dist/extensions/context/compaction-shared.d.ts.map +1 -1
  13. package/dist/extensions/context/compaction.d.ts.map +1 -1
  14. package/dist/extensions/index.d.ts +2 -1
  15. package/dist/extensions/index.d.ts.map +1 -1
  16. package/dist/extensions/plugin/plugin-config-loader.d.ts +2 -1
  17. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
  18. package/dist/extensions/plugin/plugin-load-report.d.ts +19 -0
  19. package/dist/extensions/plugin/plugin-load-report.d.ts.map +1 -0
  20. package/dist/extensions/plugin/plugin-loader.d.ts +6 -0
  21. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
  22. package/dist/extensions/plugin/plugin-sandbox.d.ts +2 -1
  23. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
  24. package/dist/extensions/plugin-sandbox-bootstrap.js +148 -148
  25. package/dist/index.d.ts +3 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +227 -229
  28. package/dist/runtime/runtime-builder.d.ts +1 -1
  29. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  30. package/dist/runtime/subprocess-sandbox.d.ts +2 -0
  31. package/dist/runtime/subprocess-sandbox.d.ts.map +1 -1
  32. package/dist/runtime/tool-approval.d.ts.map +1 -1
  33. package/dist/session/default-session-manager.d.ts.map +1 -1
  34. package/dist/session/persistence-service.d.ts.map +1 -1
  35. package/dist/session/session-agent-events.d.ts.map +1 -1
  36. package/dist/session/session-artifacts.d.ts +2 -0
  37. package/dist/session/session-artifacts.d.ts.map +1 -1
  38. package/dist/session/session-config-builder.d.ts.map +1 -1
  39. package/dist/team/team-tools.d.ts.map +1 -1
  40. package/dist/types/config.d.ts +1 -0
  41. package/dist/types/config.d.ts.map +1 -1
  42. package/dist/types/events.d.ts +4 -0
  43. package/dist/types/events.d.ts.map +1 -1
  44. package/package.json +4 -4
  45. package/src/auth/client.test.ts +29 -0
  46. package/src/auth/client.ts +21 -0
  47. package/src/auth/cline.ts +2 -0
  48. package/src/auth/oca.ts +2 -0
  49. package/src/auth/server.test.ts +287 -0
  50. package/src/auth/server.ts +50 -1
  51. package/src/auth/types.ts +29 -0
  52. package/src/extensions/context/agentic-compaction.ts +22 -10
  53. package/src/extensions/context/basic-compaction.ts +43 -18
  54. package/src/extensions/context/compaction-shared.ts +1 -1
  55. package/src/extensions/context/compaction.test.ts +16 -10
  56. package/src/extensions/context/compaction.ts +35 -12
  57. package/src/extensions/index.ts +6 -0
  58. package/src/extensions/plugin/plugin-config-loader.test.ts +37 -0
  59. package/src/extensions/plugin/plugin-config-loader.ts +18 -10
  60. package/src/extensions/plugin/plugin-load-report.ts +20 -0
  61. package/src/extensions/plugin/plugin-loader.test.ts +45 -0
  62. package/src/extensions/plugin/plugin-loader.ts +57 -3
  63. package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +158 -86
  64. package/src/extensions/plugin/plugin-sandbox.test.ts +70 -0
  65. package/src/extensions/plugin/plugin-sandbox.ts +17 -6
  66. package/src/index.ts +11 -0
  67. package/src/providers/local-provider-service.test.ts +4 -4
  68. package/src/runtime/hook-file-hooks.test.ts +42 -7
  69. package/src/runtime/runtime-builder.test.ts +98 -0
  70. package/src/runtime/runtime-builder.ts +112 -65
  71. package/src/runtime/subprocess-sandbox.ts +26 -23
  72. package/src/runtime/tool-approval.ts +13 -15
  73. package/src/session/default-session-manager.ts +1 -3
  74. package/src/session/persistence-service.test.ts +38 -0
  75. package/src/session/persistence-service.ts +16 -1
  76. package/src/session/session-agent-events.ts +9 -1
  77. package/src/session/session-artifacts.ts +16 -0
  78. package/src/session/session-config-builder.ts +46 -0
  79. package/src/team/team-tools.test.ts +104 -0
  80. package/src/team/team-tools.ts +35 -16
  81. package/src/types/config.ts +1 -0
  82. package/src/types/events.ts +4 -0
  83. package/dist/runtime/team-runtime-registry.d.ts +0 -13
  84. package/dist/runtime/team-runtime-registry.d.ts.map +0 -1
  85. package/src/runtime/team-runtime-registry.ts +0 -43
@@ -1,5 +1,9 @@
1
1
  import type { ITelemetryService } from "@clinebot/shared";
2
2
  import { resolveAndLoadAgentPlugins } from "../extensions/plugin/plugin-config-loader";
3
+ import type {
4
+ PluginInitializationFailure,
5
+ PluginInitializationWarning,
6
+ } from "../extensions/plugin/plugin-load-report";
3
7
  import {
4
8
  createHookAuditHooks,
5
9
  createHookConfigFileHooks,
@@ -15,6 +19,43 @@ import {
15
19
  import type { StartSessionInput } from "./session-manager";
16
20
  import { hasRuntimeHooks, mergeAgentExtensions } from "./utils/helpers";
17
21
 
22
+ function formatPluginFailure(failure: PluginInitializationFailure): string {
23
+ const label = failure.pluginName ?? failure.pluginPath;
24
+ return `${label}: ${failure.message}`;
25
+ }
26
+
27
+ function logPluginDiagnostics(
28
+ failures: PluginInitializationFailure[],
29
+ warnings: PluginInitializationWarning[],
30
+ logger: CoreSessionConfig["logger"],
31
+ ): void {
32
+ if (warnings.length > 0) {
33
+ for (const warning of warnings) {
34
+ logger?.log(warning.message, { severity: "warn" });
35
+ }
36
+ }
37
+ if (failures.length === 0) {
38
+ return;
39
+ }
40
+ const preview = failures.slice(0, 3).map(formatPluginFailure).join("; ");
41
+ const suffix = failures.length > 3 ? `; and ${failures.length - 3} more` : "";
42
+ logger?.log(
43
+ `Some plugins failed to initialize. ${preview}${suffix}. Use --verbose for more details.`,
44
+ { severity: "warn" },
45
+ );
46
+ for (const failure of failures) {
47
+ logger?.log(
48
+ `Plugin initialization failed (${failure.phase}) for ${failure.pluginPath}`,
49
+ {
50
+ severity: "warn",
51
+ stack: failure.stack,
52
+ pluginPath: failure.pluginPath,
53
+ pluginName: failure.pluginName,
54
+ },
55
+ );
56
+ }
57
+ }
58
+
18
59
  export function resolveWorkspacePath(config: CoreSessionConfig): string {
19
60
  return config.workspaceRoot ?? config.cwd;
20
61
  }
@@ -61,6 +102,11 @@ export async function buildEffectiveConfig(
61
102
  cwd: input.config.cwd,
62
103
  onEvent: onPluginEvent,
63
104
  });
105
+ logPluginDiagnostics(
106
+ loadedPlugins.failures,
107
+ loadedPlugins.warnings,
108
+ input.config.logger,
109
+ );
64
110
  } catch (error) {
65
111
  const message = error instanceof Error ? error.message : String(error);
66
112
  input.config.logger?.log?.(
@@ -743,6 +743,110 @@ describe("createAgentTeamsTools runtime behavior", () => {
743
743
  expect(awaitAllRuns?.timeoutMs).toBe(60 * 60 * 1000);
744
744
  });
745
745
 
746
+ it("deduplicates concurrent sync team_run_task calls to the same agent", async () => {
747
+ let resolveRoute!: (value: { text: string; iterations: number }) => void;
748
+ const routePromise = new Promise<{ text: string; iterations: number }>(
749
+ (resolve) => {
750
+ resolveRoute = resolve;
751
+ },
752
+ );
753
+ const routeToTeammate = vi.fn(() => routePromise);
754
+ const runtime = {
755
+ routeToTeammate,
756
+ getMemberRole: vi.fn(() => "lead"),
757
+ } as unknown as AgentTeamsRuntime;
758
+
759
+ const tools = createAgentTeamsTools({
760
+ runtime,
761
+ requesterId: "lead",
762
+ teammateConfigProvider: makeTeammateConfigProvider(),
763
+ });
764
+ const runTask = tools.find((tool) => tool.name === "team_run_task");
765
+ expect(runTask).toBeDefined();
766
+ if (!runTask) {
767
+ throw new Error("Expected team_run_task tool to be defined");
768
+ }
769
+
770
+ const ctx = { agentId: "lead", conversationId: "conv-1", iteration: 1 };
771
+ const input = {
772
+ agentId: "educator",
773
+ task: "Explain probability",
774
+ runMode: "sync",
775
+ };
776
+
777
+ // Fire two concurrent sync calls to the same agent
778
+ const call1 = runTask.execute(input, ctx);
779
+ const call2 = runTask.execute(input, ctx);
780
+
781
+ // Second call should throw with duplicate detection error
782
+ await expect(call2).rejects.toThrow(
783
+ 'Duplicate team_run_task call detected for agent "educator"',
784
+ );
785
+
786
+ // Only one routeToTeammate call should have been made
787
+ expect(routeToTeammate).toHaveBeenCalledTimes(1);
788
+
789
+ // Now resolve the first call
790
+ resolveRoute({ text: "Probability explained", iterations: 3 });
791
+ const result1 = (await call1) as { text?: string; iterations?: number };
792
+ expect(result1.text).toBe("Probability explained");
793
+ expect(result1.iterations).toBe(3);
794
+ });
795
+
796
+ it("allows concurrent sync team_run_task calls to different agents", async () => {
797
+ let resolveRoute1!: (value: { text: string; iterations: number }) => void;
798
+ let resolveRoute2!: (value: { text: string; iterations: number }) => void;
799
+ const routeToTeammate = vi.fn((agentId: string) => {
800
+ if (agentId === "educator") {
801
+ return new Promise<{ text: string; iterations: number }>((resolve) => {
802
+ resolveRoute1 = resolve;
803
+ });
804
+ }
805
+ return new Promise<{ text: string; iterations: number }>((resolve) => {
806
+ resolveRoute2 = resolve;
807
+ });
808
+ });
809
+ const runtime = {
810
+ routeToTeammate,
811
+ getMemberRole: vi.fn(() => "lead"),
812
+ } as unknown as AgentTeamsRuntime;
813
+
814
+ const tools = createAgentTeamsTools({
815
+ runtime,
816
+ requesterId: "lead",
817
+ teammateConfigProvider: makeTeammateConfigProvider(),
818
+ });
819
+ const runTask = tools.find((tool) => tool.name === "team_run_task");
820
+ expect(runTask).toBeDefined();
821
+ if (!runTask) {
822
+ throw new Error("Expected team_run_task tool to be defined");
823
+ }
824
+
825
+ const ctx = { agentId: "lead", conversationId: "conv-1", iteration: 1 };
826
+
827
+ // Fire sync calls to two different agents - both should proceed
828
+ const call1 = runTask.execute(
829
+ { agentId: "educator", task: "Explain probability", runMode: "sync" },
830
+ ctx,
831
+ );
832
+ const call2 = runTask.execute(
833
+ { agentId: "assessor", task: "Evaluate answer", runMode: "sync" },
834
+ ctx,
835
+ );
836
+
837
+ // Both should have called routeToTeammate
838
+ expect(routeToTeammate).toHaveBeenCalledTimes(2);
839
+
840
+ // Resolve both
841
+ resolveRoute1({ text: "Explained", iterations: 2 });
842
+ resolveRoute2({ text: "Evaluated", iterations: 1 });
843
+
844
+ const result1 = (await call1) as { text?: string };
845
+ const result2 = (await call2) as { text?: string };
846
+ expect(result1.text).toBe("Explained");
847
+ expect(result2.text).toBe("Evaluated");
848
+ });
849
+
746
850
  it("lists ready-to-claim tasks through team_task list action", async () => {
747
851
  const runtime = new AgentTeamsRuntime({ teamName: "test-team" });
748
852
  const tools = createAgentTeamsTools({
@@ -383,6 +383,11 @@ export function createAgentTeamsTools(
383
383
  }) as Tool,
384
384
  );
385
385
 
386
+ // Track in-flight sync runs per agent for dedup
387
+ // (Claude sometimes emits duplicate tool_use blocks in a single response;
388
+ // we execute the first and return an informative "duplicate ignored" to the rest)
389
+ const pendingSyncRuns = new Set<string>();
390
+
386
391
  tools.push(
387
392
  createTool<
388
393
  TeamRunTaskInput,
@@ -417,22 +422,36 @@ export function createAgentTeamsTools(
417
422
  runId: run.id,
418
423
  };
419
424
  }
420
- const result = await options.runtime.routeToTeammate(
421
- validatedInput.agentId,
422
- validatedInput.task,
423
- {
424
- taskId: validatedInput.taskId || undefined,
425
- fromAgentId: options.requesterId,
426
- continueConversation:
427
- validatedInput.continueConversation || undefined,
428
- },
429
- );
430
- return {
431
- agentId: validatedInput.agentId,
432
- mode: "sync",
433
- text: result.text,
434
- iterations: result.iterations,
435
- };
425
+
426
+ /// Deduplication guard: reject a duplicate sync call for the same agent
427
+ // that was already dispatched in this same parallel tool-call batch.
428
+ if (pendingSyncRuns.has(validatedInput.agentId)) {
429
+ throw new Error(
430
+ `Duplicate team_run_task call detected for agent "${validatedInput.agentId}". ` +
431
+ `Only one call per agent is allowed per turn. Discard this duplicate result.`,
432
+ );
433
+ }
434
+ pendingSyncRuns.add(validatedInput.agentId);
435
+ try {
436
+ const result = await options.runtime.routeToTeammate(
437
+ validatedInput.agentId,
438
+ validatedInput.task,
439
+ {
440
+ taskId: validatedInput.taskId || undefined,
441
+ fromAgentId: options.requesterId,
442
+ continueConversation:
443
+ validatedInput.continueConversation || undefined,
444
+ },
445
+ );
446
+ return {
447
+ agentId: validatedInput.agentId,
448
+ mode: "sync",
449
+ text: result.text,
450
+ iterations: result.iterations,
451
+ };
452
+ } finally {
453
+ pendingSyncRuns.delete(validatedInput.agentId);
454
+ }
436
455
  },
437
456
  }) as Tool,
438
457
  );
@@ -42,6 +42,7 @@ export interface CoreRuntimeFeatures {
42
42
  enableTools: boolean;
43
43
  enableSpawnAgent: boolean;
44
44
  enableAgentTeams: boolean;
45
+ disableMcpSettingsTools?: boolean;
45
46
  yolo?: boolean;
46
47
  }
47
48
 
@@ -61,6 +61,10 @@ export type CoreSessionEvent =
61
61
  payload: {
62
62
  sessionId: string;
63
63
  event: import("@clinebot/shared").AgentEvent;
64
+ /** Identifies the named agent within the team (e.g. "educator", "assessor", "coordinator") for both lead and teammate agents */
65
+ teamAgentId?: string;
66
+ /** Whether this is the lead agent or a teammate */
67
+ teamRole?: "lead" | "teammate";
64
68
  };
65
69
  }
66
70
  | { type: "team_progress"; payload: SessionTeamProgressEvent }
@@ -1,13 +0,0 @@
1
- import type { AgentTeamsRuntime, DelegatedAgentConfigProvider } from "../team";
2
- export interface TeamRuntimeRegistryEntry {
3
- runtime?: AgentTeamsRuntime;
4
- delegatedAgentConfigProvider: DelegatedAgentConfigProvider;
5
- }
6
- export declare class TeamRuntimeRegistry {
7
- private readonly entries;
8
- get(key: string): TeamRuntimeRegistryEntry | undefined;
9
- getOrCreate(key: string, create: () => TeamRuntimeRegistryEntry): TeamRuntimeRegistryEntry;
10
- update(key: string, updateEntry: (entry: TeamRuntimeRegistryEntry) => void): TeamRuntimeRegistryEntry | undefined;
11
- delete(key: string): void;
12
- }
13
- //# sourceMappingURL=team-runtime-registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"team-runtime-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/team-runtime-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,4BAA4B,EAAE,4BAA4B,CAAC;CAC3D;AAED,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+C;IAEvE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS;IAItD,WAAW,CACV,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,wBAAwB,GACpC,wBAAwB;IAU3B,MAAM,CACL,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,GACpD,wBAAwB,GAAG,SAAS;IASvC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAGzB"}
@@ -1,43 +0,0 @@
1
- import type { AgentTeamsRuntime, DelegatedAgentConfigProvider } from "../team";
2
-
3
- export interface TeamRuntimeRegistryEntry {
4
- runtime?: AgentTeamsRuntime;
5
- delegatedAgentConfigProvider: DelegatedAgentConfigProvider;
6
- }
7
-
8
- export class TeamRuntimeRegistry {
9
- private readonly entries = new Map<string, TeamRuntimeRegistryEntry>();
10
-
11
- get(key: string): TeamRuntimeRegistryEntry | undefined {
12
- return this.entries.get(key);
13
- }
14
-
15
- getOrCreate(
16
- key: string,
17
- create: () => TeamRuntimeRegistryEntry,
18
- ): TeamRuntimeRegistryEntry {
19
- const existing = this.entries.get(key);
20
- if (existing) {
21
- return existing;
22
- }
23
- const created = create();
24
- this.entries.set(key, created);
25
- return created;
26
- }
27
-
28
- update(
29
- key: string,
30
- updateEntry: (entry: TeamRuntimeRegistryEntry) => void,
31
- ): TeamRuntimeRegistryEntry | undefined {
32
- const entry = this.entries.get(key);
33
- if (!entry) {
34
- return undefined;
35
- }
36
- updateEntry(entry);
37
- return entry;
38
- }
39
-
40
- delete(key: string): void {
41
- this.entries.delete(key);
42
- }
43
- }