@clinebot/core 0.0.22 → 0.0.24

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 (260) hide show
  1. package/dist/ClineCore.d.ts +110 -0
  2. package/dist/ClineCore.d.ts.map +1 -0
  3. package/dist/account/cline-account-service.d.ts +2 -1
  4. package/dist/account/cline-account-service.d.ts.map +1 -1
  5. package/dist/account/index.d.ts +1 -1
  6. package/dist/account/index.d.ts.map +1 -1
  7. package/dist/account/rpc.d.ts +3 -1
  8. package/dist/account/rpc.d.ts.map +1 -1
  9. package/dist/account/types.d.ts +3 -0
  10. package/dist/account/types.d.ts.map +1 -1
  11. package/dist/agents/plugin-loader.d.ts.map +1 -1
  12. package/dist/agents/plugin-sandbox-bootstrap.js +17 -17
  13. package/dist/auth/client.d.ts +1 -1
  14. package/dist/auth/client.d.ts.map +1 -1
  15. package/dist/auth/cline.d.ts +1 -1
  16. package/dist/auth/cline.d.ts.map +1 -1
  17. package/dist/auth/codex.d.ts +1 -1
  18. package/dist/auth/codex.d.ts.map +1 -1
  19. package/dist/auth/oca.d.ts +1 -1
  20. package/dist/auth/oca.d.ts.map +1 -1
  21. package/dist/auth/utils.d.ts +2 -2
  22. package/dist/auth/utils.d.ts.map +1 -1
  23. package/dist/index.d.ts +50 -5
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +949 -0
  26. package/dist/providers/local-provider-service.d.ts +4 -4
  27. package/dist/providers/local-provider-service.d.ts.map +1 -1
  28. package/dist/runtime/runtime-builder.d.ts +1 -0
  29. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  30. package/dist/runtime/session-runtime.d.ts +2 -1
  31. package/dist/runtime/session-runtime.d.ts.map +1 -1
  32. package/dist/runtime/team-runtime-registry.d.ts +13 -0
  33. package/dist/runtime/team-runtime-registry.d.ts.map +1 -0
  34. package/dist/session/default-session-manager.d.ts +2 -2
  35. package/dist/session/default-session-manager.d.ts.map +1 -1
  36. package/dist/session/rpc-runtime-ensure.d.ts +53 -0
  37. package/dist/session/rpc-runtime-ensure.d.ts.map +1 -0
  38. package/dist/session/session-config-builder.d.ts +2 -3
  39. package/dist/session/session-config-builder.d.ts.map +1 -1
  40. package/dist/session/session-host.d.ts +8 -18
  41. package/dist/session/session-host.d.ts.map +1 -1
  42. package/dist/session/session-manager.d.ts +1 -1
  43. package/dist/session/session-manager.d.ts.map +1 -1
  44. package/dist/session/session-manifest.d.ts +1 -2
  45. package/dist/session/session-manifest.d.ts.map +1 -1
  46. package/dist/session/unified-session-persistence-service.d.ts +2 -2
  47. package/dist/session/unified-session-persistence-service.d.ts.map +1 -1
  48. package/dist/session/utils/helpers.d.ts +1 -1
  49. package/dist/session/utils/helpers.d.ts.map +1 -1
  50. package/dist/session/utils/types.d.ts +1 -1
  51. package/dist/session/utils/types.d.ts.map +1 -1
  52. package/dist/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  53. package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
  54. package/dist/telemetry/distinct-id.d.ts +2 -0
  55. package/dist/telemetry/distinct-id.d.ts.map +1 -0
  56. package/dist/telemetry/{opentelemetry.d.ts → index.d.ts} +1 -1
  57. package/dist/telemetry/index.d.ts.map +1 -0
  58. package/dist/telemetry/index.js +28 -0
  59. package/dist/tools/constants.d.ts +1 -1
  60. package/dist/tools/constants.d.ts.map +1 -1
  61. package/dist/tools/definitions.d.ts +3 -3
  62. package/dist/tools/definitions.d.ts.map +1 -1
  63. package/dist/tools/executors/apply-patch.d.ts +1 -1
  64. package/dist/tools/executors/apply-patch.d.ts.map +1 -1
  65. package/dist/tools/executors/bash.d.ts +1 -1
  66. package/dist/tools/executors/bash.d.ts.map +1 -1
  67. package/dist/tools/executors/editor.d.ts +1 -1
  68. package/dist/tools/executors/editor.d.ts.map +1 -1
  69. package/dist/tools/executors/file-read.d.ts +1 -1
  70. package/dist/tools/executors/file-read.d.ts.map +1 -1
  71. package/dist/tools/executors/index.d.ts +14 -14
  72. package/dist/tools/executors/index.d.ts.map +1 -1
  73. package/dist/tools/executors/search.d.ts +1 -1
  74. package/dist/tools/executors/search.d.ts.map +1 -1
  75. package/dist/tools/executors/web-fetch.d.ts +1 -1
  76. package/dist/tools/executors/web-fetch.d.ts.map +1 -1
  77. package/dist/tools/helpers.d.ts +1 -1
  78. package/dist/tools/helpers.d.ts.map +1 -1
  79. package/dist/tools/index.d.ts +10 -10
  80. package/dist/tools/index.d.ts.map +1 -1
  81. package/dist/tools/model-tool-routing.d.ts +1 -1
  82. package/dist/tools/model-tool-routing.d.ts.map +1 -1
  83. package/dist/tools/presets.d.ts +1 -1
  84. package/dist/tools/presets.d.ts.map +1 -1
  85. package/dist/types/common.d.ts +17 -8
  86. package/dist/types/common.d.ts.map +1 -1
  87. package/dist/types/config.d.ts +4 -3
  88. package/dist/types/config.d.ts.map +1 -1
  89. package/dist/types/provider-settings.d.ts +1 -1
  90. package/dist/types/provider-settings.d.ts.map +1 -1
  91. package/dist/types.d.ts +5 -2
  92. package/dist/types.d.ts.map +1 -1
  93. package/dist/version.d.ts +2 -0
  94. package/dist/version.d.ts.map +1 -0
  95. package/package.json +44 -38
  96. package/src/ClineCore.ts +137 -0
  97. package/src/account/cline-account-service.test.ts +101 -0
  98. package/src/account/cline-account-service.ts +300 -0
  99. package/src/account/featurebase-token.test.ts +175 -0
  100. package/src/account/index.ts +23 -0
  101. package/src/account/rpc.test.ts +63 -0
  102. package/src/account/rpc.ts +185 -0
  103. package/src/account/types.ts +102 -0
  104. package/src/agents/agent-config-loader.test.ts +236 -0
  105. package/src/agents/agent-config-loader.ts +108 -0
  106. package/src/agents/agent-config-parser.ts +198 -0
  107. package/src/agents/hooks-config-loader.test.ts +20 -0
  108. package/src/agents/hooks-config-loader.ts +118 -0
  109. package/src/agents/index.ts +85 -0
  110. package/src/agents/plugin-config-loader.test.ts +140 -0
  111. package/src/agents/plugin-config-loader.ts +97 -0
  112. package/src/agents/plugin-loader.test.ts +210 -0
  113. package/src/agents/plugin-loader.ts +175 -0
  114. package/src/agents/plugin-sandbox-bootstrap.ts +448 -0
  115. package/src/agents/plugin-sandbox.test.ts +296 -0
  116. package/src/agents/plugin-sandbox.ts +341 -0
  117. package/src/agents/unified-config-file-watcher.test.ts +196 -0
  118. package/src/agents/unified-config-file-watcher.ts +483 -0
  119. package/src/agents/user-instruction-config-loader.test.ts +158 -0
  120. package/src/agents/user-instruction-config-loader.ts +438 -0
  121. package/src/auth/client.test.ts +40 -0
  122. package/src/auth/client.ts +25 -0
  123. package/src/auth/cline.test.ts +130 -0
  124. package/src/auth/cline.ts +420 -0
  125. package/src/auth/codex.test.ts +170 -0
  126. package/src/auth/codex.ts +491 -0
  127. package/src/auth/oca.test.ts +215 -0
  128. package/src/auth/oca.ts +573 -0
  129. package/src/auth/server.ts +216 -0
  130. package/src/auth/types.ts +81 -0
  131. package/src/auth/utils.test.ts +128 -0
  132. package/src/auth/utils.ts +247 -0
  133. package/src/chat/chat-schema.ts +82 -0
  134. package/src/index.ts +479 -0
  135. package/src/input/file-indexer.d.ts +11 -0
  136. package/src/input/file-indexer.test.ts +127 -0
  137. package/src/input/file-indexer.ts +327 -0
  138. package/src/input/index.ts +7 -0
  139. package/src/input/mention-enricher.test.ts +85 -0
  140. package/src/input/mention-enricher.ts +122 -0
  141. package/src/mcp/config-loader.test.ts +238 -0
  142. package/src/mcp/config-loader.ts +219 -0
  143. package/src/mcp/index.ts +26 -0
  144. package/src/mcp/manager.test.ts +106 -0
  145. package/src/mcp/manager.ts +262 -0
  146. package/src/mcp/types.ts +88 -0
  147. package/src/providers/local-provider-registry.ts +232 -0
  148. package/src/providers/local-provider-service.test.ts +783 -0
  149. package/src/providers/local-provider-service.ts +471 -0
  150. package/src/runtime/commands.test.ts +98 -0
  151. package/src/runtime/commands.ts +83 -0
  152. package/src/runtime/hook-file-hooks.test.ts +237 -0
  153. package/src/runtime/hook-file-hooks.ts +859 -0
  154. package/src/runtime/index.ts +37 -0
  155. package/src/runtime/rules.ts +34 -0
  156. package/src/runtime/runtime-builder.team-persistence.test.ts +245 -0
  157. package/src/runtime/runtime-builder.test.ts +371 -0
  158. package/src/runtime/runtime-builder.ts +631 -0
  159. package/src/runtime/runtime-parity.test.ts +143 -0
  160. package/src/runtime/sandbox/subprocess-sandbox.ts +231 -0
  161. package/src/runtime/session-runtime.ts +49 -0
  162. package/src/runtime/skills.ts +44 -0
  163. package/src/runtime/team-runtime-registry.ts +46 -0
  164. package/src/runtime/tool-approval.ts +104 -0
  165. package/src/runtime/workflows.test.ts +119 -0
  166. package/src/runtime/workflows.ts +45 -0
  167. package/src/session/default-session-manager.e2e.test.ts +384 -0
  168. package/src/session/default-session-manager.test.ts +1931 -0
  169. package/src/session/default-session-manager.ts +1422 -0
  170. package/src/session/file-session-service.ts +280 -0
  171. package/src/session/index.ts +45 -0
  172. package/src/session/rpc-runtime-ensure.ts +521 -0
  173. package/src/session/rpc-session-service.ts +107 -0
  174. package/src/session/rpc-spawn-lease.test.ts +49 -0
  175. package/src/session/rpc-spawn-lease.ts +122 -0
  176. package/src/session/runtime-oauth-token-manager.test.ts +137 -0
  177. package/src/session/runtime-oauth-token-manager.ts +272 -0
  178. package/src/session/session-agent-events.ts +248 -0
  179. package/src/session/session-artifacts.ts +106 -0
  180. package/src/session/session-config-builder.ts +113 -0
  181. package/src/session/session-graph.ts +92 -0
  182. package/src/session/session-host.test.ts +89 -0
  183. package/src/session/session-host.ts +205 -0
  184. package/src/session/session-manager.ts +69 -0
  185. package/src/session/session-manifest.ts +29 -0
  186. package/src/session/session-service.team-persistence.test.ts +48 -0
  187. package/src/session/session-service.ts +673 -0
  188. package/src/session/session-team-coordination.ts +229 -0
  189. package/src/session/session-telemetry.ts +100 -0
  190. package/src/session/sqlite-rpc-session-backend.ts +303 -0
  191. package/src/session/unified-session-persistence-service.test.ts +85 -0
  192. package/src/session/unified-session-persistence-service.ts +994 -0
  193. package/src/session/utils/helpers.ts +139 -0
  194. package/src/session/utils/types.ts +57 -0
  195. package/src/session/utils/usage.ts +32 -0
  196. package/src/session/workspace-manager.ts +98 -0
  197. package/src/session/workspace-manifest.ts +100 -0
  198. package/src/storage/artifact-store.ts +1 -0
  199. package/src/storage/file-team-store.ts +257 -0
  200. package/src/storage/index.ts +11 -0
  201. package/src/storage/provider-settings-legacy-migration.test.ts +424 -0
  202. package/src/storage/provider-settings-legacy-migration.ts +826 -0
  203. package/src/storage/provider-settings-manager.test.ts +191 -0
  204. package/src/storage/provider-settings-manager.ts +152 -0
  205. package/src/storage/session-store.ts +1 -0
  206. package/src/storage/sqlite-session-store.ts +275 -0
  207. package/src/storage/sqlite-team-store.ts +454 -0
  208. package/src/storage/team-store.ts +40 -0
  209. package/src/team/index.ts +4 -0
  210. package/src/team/projections.ts +285 -0
  211. package/src/telemetry/ITelemetryAdapter.ts +94 -0
  212. package/src/telemetry/LoggerTelemetryAdapter.test.ts +42 -0
  213. package/src/telemetry/LoggerTelemetryAdapter.ts +114 -0
  214. package/src/telemetry/OpenTelemetryAdapter.test.ts +157 -0
  215. package/src/telemetry/OpenTelemetryAdapter.ts +348 -0
  216. package/src/telemetry/OpenTelemetryProvider.test.ts +113 -0
  217. package/src/telemetry/OpenTelemetryProvider.ts +325 -0
  218. package/src/telemetry/TelemetryService.test.ts +134 -0
  219. package/src/telemetry/TelemetryService.ts +141 -0
  220. package/src/telemetry/core-events.ts +400 -0
  221. package/src/telemetry/distinct-id.test.ts +57 -0
  222. package/src/telemetry/distinct-id.ts +58 -0
  223. package/src/telemetry/index.ts +20 -0
  224. package/src/tools/constants.ts +35 -0
  225. package/src/tools/definitions.test.ts +704 -0
  226. package/src/tools/definitions.ts +709 -0
  227. package/src/tools/executors/apply-patch-parser.ts +520 -0
  228. package/src/tools/executors/apply-patch.ts +359 -0
  229. package/src/tools/executors/bash.test.ts +87 -0
  230. package/src/tools/executors/bash.ts +207 -0
  231. package/src/tools/executors/editor.test.ts +35 -0
  232. package/src/tools/executors/editor.ts +219 -0
  233. package/src/tools/executors/file-read.test.ts +49 -0
  234. package/src/tools/executors/file-read.ts +110 -0
  235. package/src/tools/executors/index.ts +87 -0
  236. package/src/tools/executors/search.ts +278 -0
  237. package/src/tools/executors/web-fetch.ts +259 -0
  238. package/src/tools/helpers.ts +130 -0
  239. package/src/tools/index.ts +169 -0
  240. package/src/tools/model-tool-routing.test.ts +86 -0
  241. package/src/tools/model-tool-routing.ts +132 -0
  242. package/src/tools/presets.test.ts +62 -0
  243. package/src/tools/presets.ts +168 -0
  244. package/src/tools/schemas.ts +327 -0
  245. package/src/tools/types.ts +329 -0
  246. package/src/types/common.ts +26 -0
  247. package/src/types/config.ts +86 -0
  248. package/src/types/events.ts +74 -0
  249. package/src/types/index.ts +24 -0
  250. package/src/types/provider-settings.ts +43 -0
  251. package/src/types/sessions.ts +16 -0
  252. package/src/types/storage.ts +64 -0
  253. package/src/types/workspace.ts +7 -0
  254. package/src/types.ts +132 -0
  255. package/src/version.ts +3 -0
  256. package/dist/index.node.d.ts +0 -47
  257. package/dist/index.node.d.ts.map +0 -1
  258. package/dist/index.node.js +0 -948
  259. package/dist/telemetry/opentelemetry.d.ts.map +0 -1
  260. package/dist/telemetry/opentelemetry.js +0 -27
@@ -0,0 +1,37 @@
1
+ export {
2
+ type AvailableRuntimeCommand,
3
+ listAvailableRuntimeCommandsFromWatcher,
4
+ resolveRuntimeSlashCommandFromWatcher,
5
+ } from "./commands";
6
+ export {
7
+ formatRulesForSystemPrompt,
8
+ isRuleEnabled,
9
+ listEnabledRulesFromWatcher,
10
+ loadRulesForSystemPromptFromWatcher,
11
+ } from "./rules";
12
+ export { createTeamName, DefaultRuntimeBuilder } from "./runtime-builder";
13
+ export {
14
+ type SandboxCallOptions,
15
+ SubprocessSandbox,
16
+ type SubprocessSandboxOptions,
17
+ } from "./sandbox/subprocess-sandbox";
18
+ export type {
19
+ BuiltRuntime,
20
+ RuntimeBuilder,
21
+ RuntimeBuilderInput,
22
+ SessionRuntime,
23
+ } from "./session-runtime";
24
+ export {
25
+ type AvailableSkill,
26
+ listAvailableSkillsFromWatcher,
27
+ resolveSkillsSlashCommandFromWatcher,
28
+ } from "./skills";
29
+ export {
30
+ type DesktopToolApprovalOptions,
31
+ requestDesktopToolApproval,
32
+ } from "./tool-approval";
33
+ export {
34
+ type AvailableWorkflow,
35
+ listAvailableWorkflowsFromWatcher,
36
+ resolveWorkflowSlashCommandFromWatcher,
37
+ } from "./workflows";
@@ -0,0 +1,34 @@
1
+ import type { RuleConfig, UserInstructionConfigWatcher } from "../agents";
2
+
3
+ export function isRuleEnabled(rule: RuleConfig): boolean {
4
+ return rule.disabled !== true;
5
+ }
6
+
7
+ export function formatRulesForSystemPrompt(
8
+ rules: ReadonlyArray<RuleConfig>,
9
+ ): string {
10
+ if (rules.length === 0) {
11
+ return "";
12
+ }
13
+
14
+ const renderedRules = rules
15
+ .map((rule) => `## ${rule.name}\n${rule.instructions}`)
16
+ .join("\n\n");
17
+ return `\n\n# Rules\n${renderedRules}`;
18
+ }
19
+
20
+ export function listEnabledRulesFromWatcher(
21
+ watcher: UserInstructionConfigWatcher,
22
+ ): RuleConfig[] {
23
+ const snapshot = watcher.getSnapshot("rule");
24
+ return [...snapshot.values()]
25
+ .map((record) => record.item as RuleConfig)
26
+ .filter(isRuleEnabled)
27
+ .sort((a, b) => a.name.localeCompare(b.name));
28
+ }
29
+
30
+ export function loadRulesForSystemPromptFromWatcher(
31
+ watcher: UserInstructionConfigWatcher,
32
+ ): string {
33
+ return formatRulesForSystemPrompt(listEnabledRulesFromWatcher(watcher));
34
+ }
@@ -0,0 +1,245 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+
3
+ const createBuiltinToolsMock = vi.fn(() => []);
4
+ const bootstrapAgentTeamsMock = vi.fn(() => ({
5
+ tools: [],
6
+ restoredFromPersistence: true,
7
+ restoredTeammates: ["restored-1"],
8
+ }));
9
+
10
+ let runtimeInstance: MockAgentTeamsRuntime | undefined;
11
+ class MockAgentTeamsRuntime {
12
+ private readonly onTeamEvent?: (event: any) => void;
13
+
14
+ constructor(options: { onTeamEvent?: (event: any) => void }) {
15
+ this.onTeamEvent = options.onTeamEvent;
16
+ runtimeInstance = this;
17
+ }
18
+
19
+ emit(event: any): void {
20
+ this.onTeamEvent?.(event);
21
+ }
22
+
23
+ hydrateState = vi.fn();
24
+ exportState = vi.fn(() => ({
25
+ members: [],
26
+ tasks: [],
27
+ mailbox: [],
28
+ missionLog: [],
29
+ runs: [],
30
+ outcomes: [],
31
+ outcomeFragments: [],
32
+ }));
33
+ markStaleRunsInterrupted = vi.fn();
34
+ getTeammateIds = vi.fn(() => []);
35
+ shutdownTeammate = vi.fn();
36
+ }
37
+
38
+ vi.mock("@clinebot/agents", () => ({
39
+ AgentTeamsRuntime: MockAgentTeamsRuntime,
40
+ bootstrapAgentTeams: bootstrapAgentTeamsMock,
41
+ createDelegatedAgentConfigProvider: (config: Record<string, unknown>) => {
42
+ let runtimeConfig = { ...config };
43
+ return {
44
+ getRuntimeConfig: () => runtimeConfig,
45
+ getConnectionConfig: () => ({
46
+ providerId: runtimeConfig.providerId,
47
+ modelId: runtimeConfig.modelId,
48
+ apiKey: runtimeConfig.apiKey,
49
+ baseUrl: runtimeConfig.baseUrl,
50
+ headers: runtimeConfig.headers,
51
+ providerConfig: runtimeConfig.providerConfig,
52
+ knownModels: runtimeConfig.knownModels,
53
+ thinking: runtimeConfig.thinking,
54
+ }),
55
+ updateConnectionDefaults: (overrides: Record<string, unknown>) => {
56
+ runtimeConfig = { ...runtimeConfig, ...overrides };
57
+ },
58
+ };
59
+ },
60
+ }));
61
+
62
+ vi.mock("../default-tools", () => ({
63
+ ALL_DEFAULT_TOOL_NAMES: [],
64
+ createBuiltinTools: createBuiltinToolsMock,
65
+ ToolPresets: {
66
+ development: {},
67
+ readonly: {},
68
+ },
69
+ }));
70
+
71
+ let teamStoreInstance: MockTeamStore | undefined;
72
+ class MockTeamStore {
73
+ constructor() {
74
+ teamStoreInstance = this;
75
+ }
76
+
77
+ loadRuntime = vi.fn(() => ({
78
+ state: {
79
+ teamId: "team_1",
80
+ teamName: "test",
81
+ members: [],
82
+ tasks: [],
83
+ mailbox: [],
84
+ missionLog: [],
85
+ runs: [],
86
+ outcomes: [],
87
+ outcomeFragments: [],
88
+ },
89
+ teammates: [
90
+ {
91
+ agentId: "restored-1",
92
+ rolePrompt: "Persisted teammate",
93
+ modelId: "claude-sonnet-4-5-20250929",
94
+ maxIterations: 4,
95
+ },
96
+ ],
97
+ interruptedRunIds: ["run_00001"],
98
+ }));
99
+ handleTeamEvent = vi.fn();
100
+ persistRuntime = vi.fn();
101
+ }
102
+
103
+ vi.mock("../storage/team-store", () => ({
104
+ createLocalTeamStore: () => new MockTeamStore(),
105
+ }));
106
+
107
+ describe("DefaultRuntimeBuilder team persistence boundary", () => {
108
+ it("persists teammate specs and runtime state from team events", async () => {
109
+ const { DefaultRuntimeBuilder } = await import("./runtime-builder");
110
+ const onTeamRestored = vi.fn();
111
+
112
+ new DefaultRuntimeBuilder().build({
113
+ config: {
114
+ providerId: "anthropic",
115
+ modelId: "claude-sonnet-4-6",
116
+ apiKey: "key",
117
+ headers: {
118
+ Authorization: "Bearer team-token",
119
+ },
120
+ systemPrompt: "test",
121
+ cwd: process.cwd(),
122
+ enableTools: false,
123
+ enableSpawnAgent: false,
124
+ enableAgentTeams: true,
125
+ },
126
+ onTeamRestored,
127
+ });
128
+
129
+ expect(bootstrapAgentTeamsMock).toHaveBeenCalledWith(
130
+ expect.objectContaining({
131
+ restoredFromPersistence: true,
132
+ restoredTeammates: [expect.objectContaining({ agentId: "restored-1" })],
133
+ teammateConfigProvider: expect.objectContaining({
134
+ getRuntimeConfig: expect.any(Function),
135
+ }),
136
+ }),
137
+ );
138
+ const bootstrapCall = (
139
+ bootstrapAgentTeamsMock.mock.calls as unknown as Array<
140
+ [Record<string, any>]
141
+ >
142
+ )[0]?.[0];
143
+ expect(bootstrapCall).toBeDefined();
144
+ expect(bootstrapCall?.teammateConfigProvider.getRuntimeConfig()).toEqual(
145
+ expect.objectContaining({
146
+ headers: {
147
+ Authorization: "Bearer team-token",
148
+ },
149
+ }),
150
+ );
151
+ expect(onTeamRestored).toHaveBeenCalledTimes(1);
152
+ expect(runtimeInstance).toBeDefined();
153
+ expect(teamStoreInstance).toBeDefined();
154
+ if (!runtimeInstance || !teamStoreInstance) {
155
+ throw new Error("Expected mocked runtime and team store instances");
156
+ }
157
+
158
+ expect(runtimeInstance.markStaleRunsInterrupted).toHaveBeenCalledWith(
159
+ "runtime_recovered",
160
+ );
161
+
162
+ runtimeInstance.emit({
163
+ type: "teammate_spawned",
164
+ agentId: "python-poet",
165
+ teammate: {
166
+ rolePrompt: "Write concise Python-focused haiku",
167
+ modelId: "claude-sonnet-4-5-20250929",
168
+ maxIterations: 7,
169
+ },
170
+ });
171
+ expect(teamStoreInstance.handleTeamEvent).toHaveBeenCalledWith(
172
+ expect.any(String),
173
+ expect.objectContaining({
174
+ type: "teammate_spawned",
175
+ agentId: "python-poet",
176
+ }),
177
+ );
178
+ expect(teamStoreInstance.persistRuntime).toHaveBeenCalled();
179
+
180
+ runtimeInstance.emit({
181
+ type: "teammate_shutdown",
182
+ agentId: "python-poet",
183
+ });
184
+ expect(teamStoreInstance.handleTeamEvent).toHaveBeenCalledWith(
185
+ expect.any(String),
186
+ expect.objectContaining({
187
+ type: "teammate_shutdown",
188
+ agentId: "python-poet",
189
+ }),
190
+ );
191
+ });
192
+
193
+ it("forwards cline workspace metadata to teammate runtime bootstrap config", async () => {
194
+ const { DefaultRuntimeBuilder } = await import("./runtime-builder");
195
+ bootstrapAgentTeamsMock.mockClear();
196
+
197
+ new DefaultRuntimeBuilder().build({
198
+ config: {
199
+ providerId: "cline",
200
+ modelId: "anthropic/claude-sonnet-4.6",
201
+ apiKey: "key",
202
+ systemPrompt: `Base instructions.
203
+
204
+ # Workspace Configuration
205
+ {
206
+ "workspaces": {
207
+ "/repo/demo": {
208
+ "hint": "demo",
209
+ "latestGitBranchName": "main"
210
+ }
211
+ }
212
+ }`,
213
+ cwd: "/repo/demo",
214
+ enableTools: false,
215
+ enableSpawnAgent: false,
216
+ enableAgentTeams: true,
217
+ },
218
+ });
219
+
220
+ expect(bootstrapAgentTeamsMock).toHaveBeenCalledWith(
221
+ expect.objectContaining({
222
+ teammateConfigProvider: expect.objectContaining({
223
+ getRuntimeConfig: expect.any(Function),
224
+ }),
225
+ }),
226
+ );
227
+ const clineBootstrapCall = (
228
+ bootstrapAgentTeamsMock.mock.calls as unknown as Array<
229
+ [Record<string, any>]
230
+ >
231
+ )[0]?.[0];
232
+ expect(clineBootstrapCall).toBeDefined();
233
+ expect(
234
+ clineBootstrapCall?.teammateConfigProvider.getRuntimeConfig(),
235
+ ).toEqual(
236
+ expect.objectContaining({
237
+ providerId: "cline",
238
+ cwd: "/repo/demo",
239
+ clineWorkspaceMetadata: expect.stringContaining(
240
+ "# Workspace Configuration",
241
+ ),
242
+ }),
243
+ );
244
+ });
245
+ });
@@ -0,0 +1,371 @@
1
+ import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import type { Tool } from "@clinebot/agents";
5
+ import { describe, expect, it } from "vitest";
6
+ import { TelemetryService } from "../telemetry/TelemetryService";
7
+ import { DefaultRuntimeBuilder } from "./runtime-builder";
8
+
9
+ function makeSpawnTool(): Tool {
10
+ return {
11
+ name: "spawn_agent",
12
+ description: "Spawn a subagent",
13
+ inputSchema: { type: "object", properties: {}, required: [] },
14
+ execute: async () => ({ ok: true }),
15
+ };
16
+ }
17
+
18
+ describe("DefaultRuntimeBuilder", () => {
19
+ it("includes builtin tools when enabled", () => {
20
+ const runtime = new DefaultRuntimeBuilder().build({
21
+ config: {
22
+ providerId: "anthropic",
23
+ modelId: "claude-sonnet-4-6",
24
+ apiKey: "key",
25
+ systemPrompt: "test",
26
+ cwd: process.cwd(),
27
+ enableTools: true,
28
+ enableSpawnAgent: false,
29
+ enableAgentTeams: false,
30
+ },
31
+ });
32
+
33
+ const names = runtime.tools.map((tool) => tool.name);
34
+ expect(names.length).toBeGreaterThan(0);
35
+ expect(names).not.toContain("spawn_agent");
36
+ });
37
+
38
+ it("forwards runtime logger for downstream agent creation", () => {
39
+ const logger = {
40
+ info: () => {},
41
+ };
42
+ const runtime = new DefaultRuntimeBuilder().build({
43
+ config: {
44
+ providerId: "anthropic",
45
+ modelId: "claude-sonnet-4-6",
46
+ apiKey: "key",
47
+ systemPrompt: "test",
48
+ cwd: process.cwd(),
49
+ enableTools: false,
50
+ enableSpawnAgent: false,
51
+ enableAgentTeams: false,
52
+ logger,
53
+ },
54
+ });
55
+
56
+ expect(runtime.logger).toBe(logger);
57
+ });
58
+
59
+ it("forwards telemetry for downstream runtime consumers", () => {
60
+ const telemetry = new TelemetryService();
61
+ const runtime = new DefaultRuntimeBuilder().build({
62
+ config: {
63
+ providerId: "anthropic",
64
+ modelId: "claude-sonnet-4-6",
65
+ apiKey: "key",
66
+ systemPrompt: "test",
67
+ cwd: process.cwd(),
68
+ enableTools: false,
69
+ enableSpawnAgent: false,
70
+ enableAgentTeams: false,
71
+ telemetry,
72
+ },
73
+ });
74
+
75
+ expect(runtime.telemetry).toBe(telemetry);
76
+ });
77
+
78
+ it("uses readonly preset in plan mode", () => {
79
+ const runtime = new DefaultRuntimeBuilder().build({
80
+ config: {
81
+ providerId: "anthropic",
82
+ modelId: "claude-sonnet-4-6",
83
+ apiKey: "key",
84
+ systemPrompt: "test",
85
+ cwd: process.cwd(),
86
+ mode: "plan",
87
+ enableTools: true,
88
+ enableSpawnAgent: false,
89
+ enableAgentTeams: false,
90
+ },
91
+ });
92
+
93
+ const names = runtime.tools.map((tool) => tool.name);
94
+ expect(names).not.toContain("editor");
95
+ });
96
+
97
+ it("uses apply_patch instead of editor for codex/gpt model IDs in act mode", () => {
98
+ const runtime = new DefaultRuntimeBuilder().build({
99
+ config: {
100
+ providerId: "openai",
101
+ modelId: "openai/gpt-5.4",
102
+ apiKey: "key",
103
+ systemPrompt: "test",
104
+ cwd: process.cwd(),
105
+ mode: "act",
106
+ enableTools: true,
107
+ enableSpawnAgent: false,
108
+ enableAgentTeams: false,
109
+ },
110
+ });
111
+
112
+ const names = runtime.tools.map((tool) => tool.name);
113
+ expect(names).toContain("apply_patch");
114
+ expect(names).not.toContain("editor");
115
+ });
116
+
117
+ it("keeps editor for non-codex/non-gpt model IDs in act mode", () => {
118
+ const runtime = new DefaultRuntimeBuilder().build({
119
+ config: {
120
+ providerId: "anthropic",
121
+ modelId: "claude-sonnet-4-6",
122
+ apiKey: "key",
123
+ systemPrompt: "test",
124
+ cwd: process.cwd(),
125
+ mode: "act",
126
+ enableTools: true,
127
+ enableSpawnAgent: false,
128
+ enableAgentTeams: false,
129
+ },
130
+ });
131
+
132
+ const names = runtime.tools.map((tool) => tool.name);
133
+ expect(names).toContain("editor");
134
+ expect(names).not.toContain("apply_patch");
135
+ });
136
+
137
+ it("applies custom tool routing rules from session config", () => {
138
+ const runtime = new DefaultRuntimeBuilder().build({
139
+ config: {
140
+ providerId: "anthropic",
141
+ modelId: "claude-sonnet-4-6",
142
+ apiKey: "key",
143
+ systemPrompt: "test",
144
+ cwd: process.cwd(),
145
+ mode: "act",
146
+ enableTools: true,
147
+ enableSpawnAgent: false,
148
+ enableAgentTeams: false,
149
+ toolRoutingRules: [
150
+ {
151
+ mode: "act",
152
+ providerIdIncludes: ["anthropic"],
153
+ modelIdIncludes: ["claude"],
154
+ enableTools: ["apply_patch"],
155
+ disableTools: ["editor"],
156
+ },
157
+ ],
158
+ },
159
+ });
160
+
161
+ const names = runtime.tools.map((tool) => tool.name);
162
+ expect(names).toContain("apply_patch");
163
+ expect(names).not.toContain("editor");
164
+ });
165
+
166
+ it("omits builtin tools when disabled", () => {
167
+ const runtime = new DefaultRuntimeBuilder().build({
168
+ config: {
169
+ providerId: "anthropic",
170
+ modelId: "claude-sonnet-4-6",
171
+ apiKey: "key",
172
+ systemPrompt: "test",
173
+ cwd: process.cwd(),
174
+ enableTools: false,
175
+ enableSpawnAgent: false,
176
+ enableAgentTeams: false,
177
+ },
178
+ });
179
+
180
+ expect(runtime.tools).toEqual([]);
181
+ });
182
+
183
+ it("adds spawn tool when enabled", () => {
184
+ const runtime = new DefaultRuntimeBuilder().build({
185
+ config: {
186
+ providerId: "anthropic",
187
+ modelId: "claude-sonnet-4-6",
188
+ apiKey: "key",
189
+ systemPrompt: "test",
190
+ cwd: process.cwd(),
191
+ enableTools: false,
192
+ enableSpawnAgent: true,
193
+ enableAgentTeams: false,
194
+ },
195
+ createSpawnTool: makeSpawnTool,
196
+ });
197
+
198
+ expect(runtime.tools.map((tool) => tool.name)).toContain("spawn_agent");
199
+ });
200
+
201
+ it("provides a shutdown helper", () => {
202
+ const runtime = new DefaultRuntimeBuilder().build({
203
+ config: {
204
+ providerId: "anthropic",
205
+ modelId: "claude-sonnet-4-6",
206
+ apiKey: "key",
207
+ systemPrompt: "test",
208
+ cwd: process.cwd(),
209
+ enableTools: false,
210
+ enableSpawnAgent: false,
211
+ enableAgentTeams: false,
212
+ },
213
+ });
214
+
215
+ expect(() => runtime.shutdown("test")).not.toThrow();
216
+ });
217
+
218
+ it("includes skills tool when workspace skills are available", () => {
219
+ const cwd = mkdtempSync(join(tmpdir(), "runtime-builder-skills-"));
220
+ const skillDir = join(cwd, ".cline", "skills", "commit");
221
+ mkdirSync(skillDir, { recursive: true });
222
+ writeFileSync(
223
+ join(skillDir, "SKILL.md"),
224
+ `---
225
+ name: commit
226
+ description: Create commit message
227
+ ---
228
+ Use conventional commits.`,
229
+ "utf8",
230
+ );
231
+
232
+ const runtime = new DefaultRuntimeBuilder().build({
233
+ config: {
234
+ providerId: "anthropic",
235
+ modelId: "claude-sonnet-4-6",
236
+ apiKey: "key",
237
+ systemPrompt: "test",
238
+ cwd,
239
+ enableTools: true,
240
+ enableSpawnAgent: false,
241
+ enableAgentTeams: false,
242
+ },
243
+ });
244
+
245
+ expect(runtime.tools.map((tool) => tool.name)).toContain("skills");
246
+ runtime.shutdown("test");
247
+ });
248
+
249
+ it("marks configured but disabled skills in executor metadata", async () => {
250
+ const cwd = mkdtempSync(join(tmpdir(), "runtime-builder-skills-disabled-"));
251
+ const enabledDir = join(cwd, ".cline", "skills", "commit");
252
+ const disabledDir = join(cwd, ".cline", "skills", "review");
253
+ mkdirSync(enabledDir, { recursive: true });
254
+ mkdirSync(disabledDir, { recursive: true });
255
+ writeFileSync(
256
+ join(enabledDir, "SKILL.md"),
257
+ `---
258
+ name: commit
259
+ ---
260
+ Enabled skill.`,
261
+ "utf8",
262
+ );
263
+ writeFileSync(
264
+ join(disabledDir, "SKILL.md"),
265
+ `---
266
+ name: review
267
+ disabled: true
268
+ ---
269
+ Disabled skill.`,
270
+ "utf8",
271
+ );
272
+
273
+ const runtime = new DefaultRuntimeBuilder().build({
274
+ config: {
275
+ providerId: "anthropic",
276
+ modelId: "claude-sonnet-4-6",
277
+ apiKey: "key",
278
+ systemPrompt: "test",
279
+ cwd,
280
+ enableTools: true,
281
+ enableSpawnAgent: false,
282
+ enableAgentTeams: false,
283
+ },
284
+ });
285
+
286
+ const skillsTool = runtime.tools.find((tool) => tool.name === "skills");
287
+ expect(skillsTool).toBeDefined();
288
+ if (!skillsTool) {
289
+ throw new Error("Expected skills tool.");
290
+ }
291
+
292
+ const disabledResult = await skillsTool.execute(
293
+ { skill: "review" },
294
+ {
295
+ agentId: "agent-1",
296
+ conversationId: "conv-1",
297
+ iteration: 1,
298
+ },
299
+ );
300
+ expect(disabledResult).toContain("configured but disabled");
301
+
302
+ runtime.shutdown("test");
303
+ });
304
+
305
+ it("scopes skills tool to session-configured skills", async () => {
306
+ const cwd = mkdtempSync(join(tmpdir(), "runtime-builder-skills-scoped-"));
307
+ const commitDir = join(cwd, ".cline", "skills", "commit");
308
+ const reviewDir = join(cwd, ".cline", "skills", "review");
309
+ mkdirSync(commitDir, { recursive: true });
310
+ mkdirSync(reviewDir, { recursive: true });
311
+ writeFileSync(
312
+ join(commitDir, "SKILL.md"),
313
+ `---
314
+ name: commit
315
+ ---
316
+ Commit skill.`,
317
+ "utf8",
318
+ );
319
+ writeFileSync(
320
+ join(reviewDir, "SKILL.md"),
321
+ `---
322
+ name: review
323
+ ---
324
+ Review skill.`,
325
+ "utf8",
326
+ );
327
+
328
+ const runtime = new DefaultRuntimeBuilder().build({
329
+ config: {
330
+ providerId: "anthropic",
331
+ modelId: "claude-sonnet-4-6",
332
+ apiKey: "key",
333
+ systemPrompt: "test",
334
+ cwd,
335
+ enableTools: true,
336
+ enableSpawnAgent: false,
337
+ enableAgentTeams: false,
338
+ skills: ["commit"],
339
+ },
340
+ });
341
+
342
+ const skillsTool = runtime.tools.find((tool) => tool.name === "skills");
343
+ expect(skillsTool).toBeDefined();
344
+ if (!skillsTool) {
345
+ throw new Error("Expected skills tool.");
346
+ }
347
+
348
+ const known = await skillsTool.execute(
349
+ { skill: "commit" },
350
+ {
351
+ agentId: "agent-1",
352
+ conversationId: "conv-1",
353
+ iteration: 1,
354
+ },
355
+ );
356
+ expect(known).toContain("<command-name>commit</command-name>");
357
+
358
+ const blocked = await skillsTool.execute(
359
+ { skill: "review" },
360
+ {
361
+ agentId: "agent-1",
362
+ conversationId: "conv-1",
363
+ iteration: 1,
364
+ },
365
+ );
366
+ expect(blocked).toContain('Skill "review" not found.');
367
+ expect(blocked).toContain("Available skills: commit");
368
+
369
+ runtime.shutdown("test");
370
+ });
371
+ });