@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,631 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import {
4
+ AgentTeamsRuntime,
5
+ bootstrapAgentTeams,
6
+ createDelegatedAgentConfigProvider,
7
+ type TeamEvent,
8
+ type TeamTeammateSpec,
9
+ type Tool,
10
+ } from "@clinebot/agents";
11
+ import { resolveSkillsConfigSearchPaths } from "@clinebot/shared/storage";
12
+ import { nanoid } from "nanoid";
13
+ import {
14
+ createUserInstructionConfigWatcher,
15
+ type SkillConfig,
16
+ type UserInstructionConfigWatcher,
17
+ } from "../agents";
18
+ import { buildWorkspaceMetadata } from "../session/workspace-manifest";
19
+ import { createLocalTeamStore } from "../storage/team-store";
20
+ import {
21
+ createBuiltinTools,
22
+ DEFAULT_MODEL_TOOL_ROUTING_RULES,
23
+ resolveToolRoutingConfig,
24
+ type SkillsExecutor,
25
+ type ToolExecutors,
26
+ ToolPresets,
27
+ type ToolRoutingRule,
28
+ } from "../tools";
29
+ import type { CoreAgentMode, CoreSessionConfig } from "../types/config";
30
+ import type {
31
+ RuntimeBuilder,
32
+ RuntimeBuilderInput,
33
+ BuiltRuntime as RuntimeEnvironment,
34
+ } from "./session-runtime";
35
+ import { TeamRuntimeRegistry } from "./team-runtime-registry";
36
+
37
+ type SkillsExecutorMetadataItem = {
38
+ id: string;
39
+ name: string;
40
+ description?: string;
41
+ disabled: boolean;
42
+ };
43
+
44
+ const WORKSPACE_CONFIGURATION_MARKER = "# Workspace Configuration";
45
+
46
+ type SkillsExecutorWithMetadata = SkillsExecutor & {
47
+ configuredSkills?: SkillsExecutorMetadataItem[];
48
+ };
49
+
50
+ export function createTeamName(): string {
51
+ return `team-${nanoid(5)}`;
52
+ }
53
+
54
+ function createBuiltinToolsList(
55
+ cwd: string,
56
+ providerId: string,
57
+ mode: CoreAgentMode,
58
+ modelId: string,
59
+ toolRoutingRules: ToolRoutingRule[] | undefined,
60
+ skillsExecutor?: SkillsExecutorWithMetadata,
61
+ executorOverrides?: Partial<ToolExecutors>,
62
+ ): Tool[] {
63
+ const preset =
64
+ mode === "plan" ? ToolPresets.readonly : ToolPresets.development;
65
+ const toolRoutingConfig = resolveToolRoutingConfig(
66
+ providerId,
67
+ modelId,
68
+ mode,
69
+ toolRoutingRules ?? DEFAULT_MODEL_TOOL_ROUTING_RULES,
70
+ );
71
+
72
+ return createBuiltinTools({
73
+ cwd,
74
+ ...preset,
75
+ ...toolRoutingConfig,
76
+ enableSkills: !!skillsExecutor,
77
+ executors: {
78
+ ...(skillsExecutor
79
+ ? {
80
+ skills: skillsExecutor,
81
+ }
82
+ : {}),
83
+ ...(executorOverrides ?? {}),
84
+ },
85
+ });
86
+ }
87
+
88
+ const SKILL_FILE_NAME = "SKILL.md";
89
+
90
+ function listAvailableSkillNames(
91
+ watcher: UserInstructionConfigWatcher,
92
+ allowedSkillNames?: ReadonlyArray<string>,
93
+ ): string[] {
94
+ return listConfiguredSkills(watcher, allowedSkillNames)
95
+ .filter((skill) => !skill.disabled)
96
+ .map((skill) => skill.name.trim())
97
+ .filter((name) => name.length > 0)
98
+ .sort((a, b) => a.localeCompare(b));
99
+ }
100
+
101
+ function normalizeSkillToken(token: string): string {
102
+ return token.trim().replace(/^\/+/, "").toLowerCase();
103
+ }
104
+
105
+ function toAllowedSkillSet(
106
+ allowedSkillNames?: ReadonlyArray<string>,
107
+ ): Set<string> | undefined {
108
+ if (!allowedSkillNames || allowedSkillNames.length === 0) {
109
+ return undefined;
110
+ }
111
+ const normalized = allowedSkillNames
112
+ .map(normalizeSkillToken)
113
+ .filter((token) => token.length > 0);
114
+ return normalized.length > 0 ? new Set(normalized) : undefined;
115
+ }
116
+
117
+ function isSkillAllowed(
118
+ skillId: string,
119
+ skillName: string,
120
+ allowedSkills?: Set<string>,
121
+ ): boolean {
122
+ if (!allowedSkills) {
123
+ return true;
124
+ }
125
+ const normalizedId = normalizeSkillToken(skillId);
126
+ const normalizedName = normalizeSkillToken(skillName);
127
+ const bareId = normalizedId.includes(":")
128
+ ? (normalizedId.split(":").at(-1) ?? normalizedId)
129
+ : normalizedId;
130
+ const bareName = normalizedName.includes(":")
131
+ ? (normalizedName.split(":").at(-1) ?? normalizedName)
132
+ : normalizedName;
133
+ return (
134
+ allowedSkills.has(normalizedId) ||
135
+ allowedSkills.has(normalizedName) ||
136
+ allowedSkills.has(bareId) ||
137
+ allowedSkills.has(bareName)
138
+ );
139
+ }
140
+
141
+ function listConfiguredSkills(
142
+ watcher: UserInstructionConfigWatcher,
143
+ allowedSkillNames?: ReadonlyArray<string>,
144
+ ): SkillsExecutorMetadataItem[] {
145
+ const allowedSkills = toAllowedSkillSet(allowedSkillNames);
146
+ const snapshot = watcher.getSnapshot("skill");
147
+ return [...snapshot.entries()]
148
+ .map(([id, record]) => {
149
+ const skill = record.item as SkillConfig;
150
+ return {
151
+ id,
152
+ name: skill.name.trim(),
153
+ description: skill.description?.trim(),
154
+ disabled: skill.disabled === true,
155
+ };
156
+ })
157
+ .filter((skill) => isSkillAllowed(skill.id, skill.name, allowedSkills));
158
+ }
159
+
160
+ function hasSkillsFiles(workspacePath: string): boolean {
161
+ for (const directoryPath of resolveSkillsConfigSearchPaths(workspacePath)) {
162
+ if (!existsSync(directoryPath)) {
163
+ continue;
164
+ }
165
+
166
+ const directSkillPath = join(directoryPath, SKILL_FILE_NAME);
167
+ if (existsSync(directSkillPath)) {
168
+ return true;
169
+ }
170
+
171
+ try {
172
+ const entries = readdirSync(directoryPath, { withFileTypes: true });
173
+ for (const entry of entries) {
174
+ if (!entry.isDirectory()) {
175
+ continue;
176
+ }
177
+ if (existsSync(join(directoryPath, entry.name, SKILL_FILE_NAME))) {
178
+ return true;
179
+ }
180
+ }
181
+ } catch {
182
+ // Ignore inaccessible directories while probing for local skills.
183
+ }
184
+ }
185
+
186
+ return false;
187
+ }
188
+
189
+ function resolveSkillRecord(
190
+ watcher: UserInstructionConfigWatcher,
191
+ requestedSkill: string,
192
+ allowedSkillNames?: ReadonlyArray<string>,
193
+ ): { id: string; skill: SkillConfig } | { error: string } {
194
+ const allowedSkills = toAllowedSkillSet(allowedSkillNames);
195
+ const normalized = requestedSkill.trim().replace(/^\/+/, "").toLowerCase();
196
+ if (!normalized) {
197
+ return { error: "Missing skill name." };
198
+ }
199
+
200
+ const snapshot = watcher.getSnapshot("skill");
201
+ const scopedEntries = [...snapshot.entries()].filter(([id, record]) => {
202
+ const skill = record.item as SkillConfig;
203
+ return isSkillAllowed(id, skill.name, allowedSkills);
204
+ });
205
+ const scopedSnapshot = new Map(scopedEntries);
206
+ const exact = scopedSnapshot.get(normalized);
207
+ if (exact) {
208
+ const skill = exact.item as SkillConfig;
209
+ if (skill.disabled === true) {
210
+ return {
211
+ error: `Skill "${skill.name}" is configured but disabled.`,
212
+ };
213
+ }
214
+ return {
215
+ id: normalized,
216
+ skill,
217
+ };
218
+ }
219
+
220
+ const bareName = normalized.includes(":")
221
+ ? (normalized.split(":").at(-1) ?? normalized)
222
+ : normalized;
223
+
224
+ const suffixMatches = [...scopedSnapshot.entries()].filter(([id]) => {
225
+ if (id === bareName) {
226
+ return true;
227
+ }
228
+ return id.endsWith(`:${bareName}`);
229
+ });
230
+
231
+ if (suffixMatches.length === 1) {
232
+ const [id, record] = suffixMatches[0];
233
+ const skill = record.item as SkillConfig;
234
+ if (skill.disabled === true) {
235
+ return {
236
+ error: `Skill "${skill.name}" is configured but disabled.`,
237
+ };
238
+ }
239
+ return {
240
+ id,
241
+ skill,
242
+ };
243
+ }
244
+
245
+ if (suffixMatches.length > 1) {
246
+ return {
247
+ error: `Skill "${requestedSkill}" is ambiguous. Use one of: ${suffixMatches.map(([id]) => id).join(", ")}`,
248
+ };
249
+ }
250
+
251
+ const available = listAvailableSkillNames(watcher, allowedSkillNames);
252
+ return {
253
+ error:
254
+ available.length > 0
255
+ ? `Skill "${requestedSkill}" not found. Available skills: ${available.join(", ")}`
256
+ : "No skills are currently available.",
257
+ };
258
+ }
259
+
260
+ function createSkillsExecutor(
261
+ watcher: UserInstructionConfigWatcher,
262
+ watcherReady: Promise<void>,
263
+ allowedSkillNames?: ReadonlyArray<string>,
264
+ ): SkillsExecutorWithMetadata {
265
+ const runningSkills = new Set<string>();
266
+ const executor: SkillsExecutorWithMetadata = async (skillName, args) => {
267
+ await watcherReady;
268
+ const resolved = resolveSkillRecord(watcher, skillName, allowedSkillNames);
269
+ if ("error" in resolved) {
270
+ return resolved.error;
271
+ }
272
+
273
+ const { id, skill } = resolved;
274
+ if (runningSkills.has(id)) {
275
+ return `Skill "${skill.name}" is already running.`;
276
+ }
277
+
278
+ runningSkills.add(id);
279
+ try {
280
+ const trimmedArgs = args?.trim();
281
+ const argsTag = trimmedArgs
282
+ ? `\n<command-args>${trimmedArgs}</command-args>`
283
+ : "";
284
+ const description = skill.description?.trim()
285
+ ? `Description: ${skill.description.trim()}\n\n`
286
+ : "";
287
+
288
+ return `<command-name>${skill.name}</command-name>${argsTag}\n<command-instructions>\n${description}${skill.instructions}\n</command-instructions>`;
289
+ } finally {
290
+ runningSkills.delete(id);
291
+ }
292
+ };
293
+ Object.defineProperty(executor, "configuredSkills", {
294
+ get: () => listConfiguredSkills(watcher, allowedSkillNames),
295
+ enumerable: true,
296
+ configurable: false,
297
+ });
298
+ return executor;
299
+ }
300
+
301
+ function shutdownTeamRuntime(
302
+ teamRuntime: AgentTeamsRuntime | undefined,
303
+ reason: string,
304
+ ): void {
305
+ if (!teamRuntime) {
306
+ return;
307
+ }
308
+ for (const teammateId of teamRuntime.getTeammateIds()) {
309
+ try {
310
+ teamRuntime.shutdownTeammate(teammateId, reason);
311
+ } catch {
312
+ // Best-effort shutdown for all teammates.
313
+ }
314
+ }
315
+ }
316
+
317
+ function extractWorkspaceMetadataFromSystemPrompt(
318
+ systemPrompt: string,
319
+ ): string | undefined {
320
+ const markerIndex = systemPrompt.lastIndexOf(WORKSPACE_CONFIGURATION_MARKER);
321
+ if (markerIndex < 0) {
322
+ return undefined;
323
+ }
324
+ const metadata = systemPrompt.slice(markerIndex).trim();
325
+ return metadata.length > 0 ? metadata : undefined;
326
+ }
327
+
328
+ function normalizeConfig(
329
+ config: CoreSessionConfig,
330
+ ): Required<
331
+ Pick<
332
+ CoreSessionConfig,
333
+ | "mode"
334
+ | "enableTools"
335
+ | "enableSpawnAgent"
336
+ | "enableAgentTeams"
337
+ | "missionLogIntervalSteps"
338
+ | "missionLogIntervalMs"
339
+ | "sessionId"
340
+ >
341
+ > {
342
+ return {
343
+ sessionId: config.sessionId || "",
344
+ mode: config.mode === "plan" ? "plan" : "act",
345
+ enableTools: config.enableTools !== false,
346
+ enableSpawnAgent: config.enableSpawnAgent !== false,
347
+ enableAgentTeams: config.enableAgentTeams !== false,
348
+ missionLogIntervalSteps:
349
+ typeof config.missionLogIntervalSteps === "number" &&
350
+ Number.isFinite(config.missionLogIntervalSteps)
351
+ ? config.missionLogIntervalSteps
352
+ : 3,
353
+ missionLogIntervalMs:
354
+ typeof config.missionLogIntervalMs === "number" &&
355
+ Number.isFinite(config.missionLogIntervalMs)
356
+ ? config.missionLogIntervalMs
357
+ : 120000,
358
+ };
359
+ }
360
+
361
+ export class DefaultRuntimeBuilder implements RuntimeBuilder {
362
+ private readonly teamRuntimeRegistry = new TeamRuntimeRegistry();
363
+
364
+ build(input: RuntimeBuilderInput): RuntimeEnvironment {
365
+ const {
366
+ config,
367
+ hooks,
368
+ extensions,
369
+ logger,
370
+ telemetry,
371
+ createSpawnTool,
372
+ onTeamRestored,
373
+ userInstructionWatcher: sharedUserInstructionWatcher,
374
+ defaultToolExecutors,
375
+ } = input;
376
+ const onTeamEvent = input.onTeamEvent ?? (() => {});
377
+ const normalized = normalizeConfig(config);
378
+ const tools: Tool[] = [];
379
+ const effectiveTeamName = config.teamName?.trim() || createTeamName();
380
+ let teamToolsRegistered = false;
381
+ const watcherProvided = Boolean(sharedUserInstructionWatcher);
382
+ let userInstructionWatcher = sharedUserInstructionWatcher;
383
+ let watcherReady = Promise.resolve();
384
+ let skillsExecutor: SkillsExecutorWithMetadata | undefined;
385
+
386
+ if (
387
+ !userInstructionWatcher &&
388
+ normalized.enableTools &&
389
+ hasSkillsFiles(config.cwd)
390
+ ) {
391
+ userInstructionWatcher = createUserInstructionConfigWatcher({
392
+ skills: { workspacePath: config.cwd },
393
+ rules: { workspacePath: config.cwd },
394
+ workflows: { workspacePath: config.cwd },
395
+ });
396
+ watcherReady = userInstructionWatcher.start().catch(() => {});
397
+ }
398
+
399
+ if (
400
+ normalized.enableTools &&
401
+ userInstructionWatcher &&
402
+ (watcherProvided ||
403
+ hasSkillsFiles(config.cwd) ||
404
+ listConfiguredSkills(userInstructionWatcher, config.skills).length > 0)
405
+ ) {
406
+ skillsExecutor = createSkillsExecutor(
407
+ userInstructionWatcher,
408
+ watcherReady,
409
+ config.skills,
410
+ );
411
+ }
412
+
413
+ if (normalized.enableTools) {
414
+ tools.push(
415
+ ...createBuiltinToolsList(
416
+ config.cwd,
417
+ config.providerId,
418
+ normalized.mode,
419
+ config.modelId,
420
+ config.toolRoutingRules,
421
+ skillsExecutor,
422
+ defaultToolExecutors,
423
+ ),
424
+ );
425
+ }
426
+
427
+ let teamRuntime: AgentTeamsRuntime | undefined;
428
+ const teamStore = normalized.enableAgentTeams
429
+ ? createLocalTeamStore()
430
+ : undefined;
431
+ const restoredTeam = teamStore?.loadRuntime(effectiveTeamName);
432
+ const restoredTeamState = restoredTeam?.state;
433
+ const restoredTeammateSpecs = restoredTeam?.teammates ?? [];
434
+ const teammateSpecs = new Map(
435
+ restoredTeammateSpecs.map((spec) => [spec.agentId, spec] as const),
436
+ );
437
+ const registryKey = config.sessionId || effectiveTeamName;
438
+ const delegatedAgentConfigProvider = createDelegatedAgentConfigProvider({
439
+ providerId: config.providerId,
440
+ modelId: config.modelId,
441
+ cwd: config.cwd,
442
+ apiKey: config.apiKey ?? "",
443
+ baseUrl: config.baseUrl,
444
+ headers: config.headers,
445
+ providerConfig: config.providerConfig,
446
+ knownModels: config.knownModels,
447
+ thinking: config.thinking,
448
+ clineWorkspaceMetadata:
449
+ config.providerId === "cline"
450
+ ? extractWorkspaceMetadataFromSystemPrompt(config.systemPrompt)
451
+ : undefined,
452
+ maxIterations: config.maxIterations,
453
+ hooks,
454
+ extensions: extensions ?? config.extensions,
455
+ logger: logger ?? config.logger,
456
+ telemetry: input.telemetry ?? config.telemetry,
457
+ });
458
+ const runtimeMetadata = delegatedAgentConfigProvider.getRuntimeConfig();
459
+ if (
460
+ config.providerId === "cline" &&
461
+ !runtimeMetadata.clineWorkspaceMetadata
462
+ ) {
463
+ buildWorkspaceMetadata(config.cwd ?? "").then((metadata) => {
464
+ const current = delegatedAgentConfigProvider.getRuntimeConfig();
465
+ if (current.clineWorkspaceMetadata) {
466
+ return;
467
+ }
468
+ Object.assign(current, { clineWorkspaceMetadata: metadata });
469
+ });
470
+ }
471
+ this.teamRuntimeRegistry.getOrCreate(registryKey, () => ({
472
+ delegatedAgentConfigProvider,
473
+ }));
474
+
475
+ const ensureTeamRuntime = (): AgentTeamsRuntime | undefined => {
476
+ if (!normalized.enableAgentTeams) {
477
+ return undefined;
478
+ }
479
+
480
+ const registryEntry = this.teamRuntimeRegistry.getOrCreate(
481
+ registryKey,
482
+ () => ({
483
+ delegatedAgentConfigProvider,
484
+ }),
485
+ );
486
+ teamRuntime = registryEntry.runtime;
487
+
488
+ if (!teamRuntime) {
489
+ teamRuntime = new AgentTeamsRuntime({
490
+ teamName: effectiveTeamName,
491
+ leadAgentId: config.sessionId || "lead",
492
+ missionLogIntervalSteps: normalized.missionLogIntervalSteps,
493
+ missionLogIntervalMs: normalized.missionLogIntervalMs,
494
+ onTeamEvent: (event: TeamEvent) => {
495
+ onTeamEvent(event);
496
+ if (teamRuntime && teamStore) {
497
+ if (
498
+ event.type === "teammate_spawned" &&
499
+ event.teammate?.rolePrompt
500
+ ) {
501
+ const spec: TeamTeammateSpec = {
502
+ agentId: event.agentId,
503
+ rolePrompt: event.teammate.rolePrompt,
504
+ modelId: event.teammate.modelId,
505
+ maxIterations: event.teammate.maxIterations,
506
+ };
507
+ teammateSpecs.set(spec.agentId, spec);
508
+ }
509
+ if (event.type === "teammate_shutdown") {
510
+ teammateSpecs.delete(event.agentId);
511
+ }
512
+ teamStore.handleTeamEvent(effectiveTeamName, event);
513
+ teamStore.persistRuntime(
514
+ effectiveTeamName,
515
+ teamRuntime.exportState(),
516
+ Array.from(teammateSpecs.values()),
517
+ );
518
+ }
519
+ },
520
+ });
521
+ if (restoredTeamState) {
522
+ teamRuntime.hydrateState(restoredTeamState);
523
+ teamRuntime.markStaleRunsInterrupted("runtime_recovered");
524
+ }
525
+ registryEntry.runtime = teamRuntime;
526
+ }
527
+
528
+ if (!teamToolsRegistered) {
529
+ if (!teamRuntime) {
530
+ return undefined;
531
+ }
532
+ teamToolsRegistered = true;
533
+
534
+ const leadAgentId = config.sessionId || "lead";
535
+ const teamBootstrap = bootstrapAgentTeams({
536
+ runtime: teamRuntime,
537
+ leadAgentId,
538
+ restoredFromPersistence: Boolean(restoredTeamState),
539
+ restoredTeammates: restoredTeammateSpecs,
540
+ createBaseTools: normalized.enableTools
541
+ ? () =>
542
+ createBuiltinToolsList(
543
+ config.cwd,
544
+ config.providerId,
545
+ normalized.mode,
546
+ config.modelId,
547
+ config.toolRoutingRules,
548
+ skillsExecutor,
549
+ defaultToolExecutors,
550
+ )
551
+ : undefined,
552
+ teammateConfigProvider: delegatedAgentConfigProvider,
553
+ });
554
+
555
+ if (teamBootstrap.restoredFromPersistence) {
556
+ onTeamRestored?.();
557
+ }
558
+ tools.push(...teamBootstrap.tools);
559
+ }
560
+
561
+ return teamRuntime;
562
+ };
563
+
564
+ if (normalized.enableSpawnAgent && createSpawnTool) {
565
+ const spawnTool = createSpawnTool();
566
+ tools.push({
567
+ ...spawnTool,
568
+ execute: async (spawnInput, context) => {
569
+ ensureTeamRuntime();
570
+ return spawnTool.execute(spawnInput, context);
571
+ },
572
+ });
573
+ }
574
+
575
+ if (normalized.enableAgentTeams) {
576
+ ensureTeamRuntime();
577
+ }
578
+
579
+ const completionGuard = normalized.enableAgentTeams
580
+ ? () => {
581
+ const rt = this.teamRuntimeRegistry.get(registryKey)?.runtime;
582
+ if (!rt) return undefined;
583
+ const tasks = rt.listTasks();
584
+ const hasInProgress = tasks.some(
585
+ (t) => t.status === "in_progress" || t.status === "pending",
586
+ );
587
+ const runs = rt.listRuns({});
588
+ const hasActiveRuns = runs.some(
589
+ (r) => r.status === "running" || r.status === "queued",
590
+ );
591
+ if (hasInProgress || hasActiveRuns) {
592
+ const pending = tasks
593
+ .filter(
594
+ (t) => t.status === "in_progress" || t.status === "pending",
595
+ )
596
+ .map((t) => `${t.id} (${t.status}): ${t.title}`)
597
+ .join(", ");
598
+ const activeRunSummary = runs
599
+ .filter((r) => r.status === "running" || r.status === "queued")
600
+ .map((r) => `${r.id} (${r.status})`)
601
+ .join(", ");
602
+ const parts = [];
603
+ if (pending) parts.push(`Unfinished tasks: ${pending}`);
604
+ if (activeRunSummary)
605
+ parts.push(`Active runs: ${activeRunSummary}`);
606
+ return `[SYSTEM] You still have team obligations. ${parts.join(". ")}. Use team_run_task to delegate work, or team_task with action=complete to mark tasks done, or team_await_run / team_await_all_runs to wait for active runs. Do NOT stop until all tasks are completed.`;
607
+ }
608
+ return undefined;
609
+ }
610
+ : undefined;
611
+
612
+ return {
613
+ tools,
614
+ logger: logger ?? config.logger,
615
+ telemetry: telemetry ?? config.telemetry,
616
+ teamRuntime,
617
+ teamRestoredFromPersistence: Boolean(restoredTeamState),
618
+ delegatedAgentConfigProvider:
619
+ this.teamRuntimeRegistry.get(registryKey)
620
+ ?.delegatedAgentConfigProvider ?? delegatedAgentConfigProvider,
621
+ completionGuard,
622
+ shutdown: (reason: string) => {
623
+ shutdownTeamRuntime(teamRuntime, reason);
624
+ this.teamRuntimeRegistry.delete(registryKey);
625
+ if (!watcherProvided) {
626
+ userInstructionWatcher?.stop();
627
+ }
628
+ },
629
+ };
630
+ }
631
+ }