@clinebot/core 0.0.27 → 0.0.29

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 (339) hide show
  1. package/README.md +7 -0
  2. package/dist/ClineCore.d.ts +28 -2
  3. package/dist/ClineCore.d.ts.map +1 -1
  4. package/dist/account/cline-account-service.d.ts +1 -1
  5. package/dist/account/cline-account-service.d.ts.map +1 -1
  6. package/dist/account/index.d.ts +1 -1
  7. package/dist/account/index.d.ts.map +1 -1
  8. package/dist/account/types.d.ts +5 -0
  9. package/dist/account/types.d.ts.map +1 -1
  10. package/dist/auth/bounded-ttl-cache.d.ts +14 -0
  11. package/dist/auth/bounded-ttl-cache.d.ts.map +1 -0
  12. package/dist/auth/cline.d.ts +27 -2
  13. package/dist/auth/cline.d.ts.map +1 -1
  14. package/dist/auth/oca.d.ts.map +1 -1
  15. package/dist/chat/chat-schema.d.ts +8 -8
  16. package/dist/extensions/config/agent-config-loader.d.ts.map +1 -0
  17. package/dist/{agents → extensions/config}/agent-config-parser.d.ts +2 -2
  18. package/dist/extensions/config/agent-config-parser.d.ts.map +1 -0
  19. package/dist/{agents → extensions/config}/hooks-config-loader.d.ts +1 -1
  20. package/dist/extensions/config/hooks-config-loader.d.ts.map +1 -0
  21. package/dist/{agents → extensions/config}/index.d.ts +2 -4
  22. package/dist/extensions/config/index.d.ts.map +1 -0
  23. package/dist/{runtime/commands.d.ts → extensions/config/runtime-commands.d.ts} +2 -3
  24. package/dist/extensions/config/runtime-commands.d.ts.map +1 -0
  25. package/dist/extensions/config/unified-config-file-watcher.d.ts.map +1 -0
  26. package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -0
  27. package/dist/extensions/context/agentic-compaction.d.ts +13 -0
  28. package/dist/extensions/context/agentic-compaction.d.ts.map +1 -0
  29. package/dist/extensions/context/basic-compaction.d.ts +9 -0
  30. package/dist/extensions/context/basic-compaction.d.ts.map +1 -0
  31. package/dist/extensions/context/compaction-shared.d.ts +60 -0
  32. package/dist/extensions/context/compaction-shared.d.ts.map +1 -0
  33. package/dist/extensions/context/compaction.d.ts +20 -0
  34. package/dist/extensions/context/compaction.d.ts.map +1 -0
  35. package/dist/extensions/index.d.ts +5 -0
  36. package/dist/extensions/index.d.ts.map +1 -0
  37. package/dist/extensions/mcp/client.d.ts +3 -0
  38. package/dist/extensions/mcp/client.d.ts.map +1 -0
  39. package/dist/extensions/mcp/config-loader.d.ts.map +1 -0
  40. package/dist/extensions/mcp/index.d.ts +9 -0
  41. package/dist/extensions/mcp/index.d.ts.map +1 -0
  42. package/dist/{mcp → extensions/mcp}/manager.d.ts +1 -2
  43. package/dist/extensions/mcp/manager.d.ts.map +1 -0
  44. package/dist/extensions/mcp/name-transform.d.ts +3 -0
  45. package/dist/extensions/mcp/name-transform.d.ts.map +1 -0
  46. package/dist/extensions/mcp/policies.d.ts +15 -0
  47. package/dist/extensions/mcp/policies.d.ts.map +1 -0
  48. package/dist/extensions/mcp/tools.d.ts +4 -0
  49. package/dist/extensions/mcp/tools.d.ts.map +1 -0
  50. package/dist/{mcp → extensions/mcp}/types.d.ts +29 -1
  51. package/dist/extensions/mcp/types.d.ts.map +1 -0
  52. package/dist/{agents → extensions/plugin}/plugin-config-loader.d.ts +1 -1
  53. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -0
  54. package/dist/{agents → extensions/plugin}/plugin-loader.d.ts +1 -1
  55. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -0
  56. package/dist/extensions/plugin/plugin-module-import.d.ts +5 -0
  57. package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -0
  58. package/dist/{agents → extensions/plugin}/plugin-sandbox.d.ts +1 -1
  59. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -0
  60. package/dist/extensions/plugin-sandbox-bootstrap.js +485 -0
  61. package/dist/hooks/index.d.ts +4 -0
  62. package/dist/hooks/index.d.ts.map +1 -0
  63. package/dist/hooks/persistent.d.ts +64 -0
  64. package/dist/hooks/persistent.d.ts.map +1 -0
  65. package/dist/hooks/subprocess-runner.d.ts +22 -0
  66. package/dist/hooks/subprocess-runner.d.ts.map +1 -0
  67. package/dist/hooks/subprocess.d.ts +189 -0
  68. package/dist/hooks/subprocess.d.ts.map +1 -0
  69. package/dist/index.d.ts +22 -25
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +560 -447
  72. package/dist/prompt/default-system.d.ts +2 -0
  73. package/dist/prompt/default-system.d.ts.map +1 -0
  74. package/dist/providers/local-provider-service.d.ts +1 -1
  75. package/dist/providers/local-provider-service.d.ts.map +1 -1
  76. package/dist/runtime/checkpoint-hooks.d.ts +21 -0
  77. package/dist/runtime/checkpoint-hooks.d.ts.map +1 -0
  78. package/dist/runtime/hook-file-hooks.d.ts +1 -1
  79. package/dist/runtime/hook-file-hooks.d.ts.map +1 -1
  80. package/dist/runtime/rules.d.ts +1 -1
  81. package/dist/runtime/rules.d.ts.map +1 -1
  82. package/dist/runtime/runtime-builder.d.ts +1 -1
  83. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  84. package/dist/runtime/session-runtime.d.ts +25 -5
  85. package/dist/runtime/session-runtime.d.ts.map +1 -1
  86. package/dist/runtime/subprocess-sandbox.d.ts.map +1 -0
  87. package/dist/runtime/team-runtime-registry.d.ts +1 -1
  88. package/dist/runtime/team-runtime-registry.d.ts.map +1 -1
  89. package/dist/runtime/tool-approval.d.ts +1 -1
  90. package/dist/session/default-session-manager.d.ts +4 -3
  91. package/dist/session/default-session-manager.d.ts.map +1 -1
  92. package/dist/session/file-session-service.d.ts +1 -1
  93. package/dist/session/file-session-service.d.ts.map +1 -1
  94. package/dist/session/{unified-session-persistence-service.d.ts → persistence-service.d.ts} +11 -42
  95. package/dist/session/persistence-service.d.ts.map +1 -0
  96. package/dist/session/rpc-session-service.d.ts +1 -1
  97. package/dist/session/rpc-session-service.d.ts.map +1 -1
  98. package/dist/session/session-agent-events.d.ts +1 -1
  99. package/dist/session/session-artifacts.d.ts.map +1 -1
  100. package/dist/session/session-config-builder.d.ts.map +1 -1
  101. package/dist/session/session-graph.d.ts +1 -1
  102. package/dist/session/session-graph.d.ts.map +1 -1
  103. package/dist/session/session-host.d.ts.map +1 -1
  104. package/dist/session/session-manager.d.ts +6 -5
  105. package/dist/session/session-manager.d.ts.map +1 -1
  106. package/dist/session/session-manifest.d.ts +1 -1
  107. package/dist/session/session-service.d.ts +3 -2
  108. package/dist/session/session-service.d.ts.map +1 -1
  109. package/dist/session/session-team-coordination.d.ts +2 -1
  110. package/dist/session/session-team-coordination.d.ts.map +1 -1
  111. package/dist/session/utils/helpers.d.ts +51 -3
  112. package/dist/session/utils/helpers.d.ts.map +1 -1
  113. package/dist/session/utils/types.d.ts +41 -7
  114. package/dist/session/utils/types.d.ts.map +1 -1
  115. package/dist/session/workspace-manager.d.ts +1 -2
  116. package/dist/session/workspace-manager.d.ts.map +1 -1
  117. package/dist/session/workspace-manifest.d.ts +1 -22
  118. package/dist/session/workspace-manifest.d.ts.map +1 -1
  119. package/dist/storage/file-team-store.d.ts +2 -1
  120. package/dist/storage/file-team-store.d.ts.map +1 -1
  121. package/dist/storage/sqlite-team-store.d.ts +4 -1
  122. package/dist/storage/sqlite-team-store.d.ts.map +1 -1
  123. package/dist/storage/team-store.d.ts.map +1 -1
  124. package/dist/team/delegated-agent.d.ts +44 -0
  125. package/dist/team/delegated-agent.d.ts.map +1 -0
  126. package/dist/team/index.d.ts +1 -0
  127. package/dist/team/index.d.ts.map +1 -1
  128. package/dist/team/multi-agent.d.ts +229 -0
  129. package/dist/team/multi-agent.d.ts.map +1 -0
  130. package/dist/team/projections.d.ts +2 -2
  131. package/dist/team/projections.d.ts.map +1 -1
  132. package/dist/team/runtime.d.ts +5 -0
  133. package/dist/team/runtime.d.ts.map +1 -0
  134. package/dist/team/spawn-agent-tool.d.ts +85 -0
  135. package/dist/team/spawn-agent-tool.d.ts.map +1 -0
  136. package/dist/team/subagent-prompts.d.ts +4 -0
  137. package/dist/team/subagent-prompts.d.ts.map +1 -0
  138. package/dist/team/team-tools.d.ts +35 -0
  139. package/dist/team/team-tools.d.ts.map +1 -0
  140. package/dist/telemetry/OpenTelemetryProvider.d.ts +11 -1
  141. package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
  142. package/dist/telemetry/{LoggerTelemetryAdapter.d.ts → TelemetryLoggerSink.d.ts} +10 -4
  143. package/dist/telemetry/TelemetryLoggerSink.d.ts.map +1 -0
  144. package/dist/telemetry/TelemetryService.d.ts.map +1 -1
  145. package/dist/telemetry/index.js +15 -28
  146. package/dist/tools/definitions.d.ts +4 -3
  147. package/dist/tools/definitions.d.ts.map +1 -1
  148. package/dist/tools/index.d.ts +5 -5
  149. package/dist/tools/index.d.ts.map +1 -1
  150. package/dist/tools/model-tool-routing.d.ts.map +1 -1
  151. package/dist/tools/presets.d.ts +26 -0
  152. package/dist/tools/presets.d.ts.map +1 -1
  153. package/dist/tools/schemas.d.ts +8 -0
  154. package/dist/tools/schemas.d.ts.map +1 -1
  155. package/dist/tools/types.d.ts +23 -2
  156. package/dist/tools/types.d.ts.map +1 -1
  157. package/dist/types/config.d.ts +47 -3
  158. package/dist/types/config.d.ts.map +1 -1
  159. package/dist/types/events.d.ts +1 -1
  160. package/dist/types/provider-settings.d.ts +1 -1
  161. package/dist/types/provider-settings.d.ts.map +1 -1
  162. package/dist/types/storage.d.ts +2 -1
  163. package/dist/types/storage.d.ts.map +1 -1
  164. package/dist/types.d.ts +7 -16
  165. package/dist/types.d.ts.map +1 -1
  166. package/package.json +16 -13
  167. package/src/ClineCore.test.ts +150 -0
  168. package/src/ClineCore.ts +114 -8
  169. package/src/account/cline-account-service.test.ts +84 -0
  170. package/src/account/cline-account-service.ts +2 -2
  171. package/src/account/index.ts +1 -0
  172. package/src/account/types.ts +6 -0
  173. package/src/auth/bounded-ttl-cache.test.ts +38 -0
  174. package/src/auth/bounded-ttl-cache.ts +53 -0
  175. package/src/auth/cline.test.ts +173 -36
  176. package/src/auth/cline.ts +395 -93
  177. package/src/auth/oca.test.ts +125 -0
  178. package/src/auth/oca.ts +17 -4
  179. package/src/{agents → extensions/config}/agent-config-loader.test.ts +1 -1
  180. package/src/{agents → extensions/config}/agent-config-parser.ts +2 -2
  181. package/src/{agents → extensions/config}/hooks-config-loader.ts +1 -1
  182. package/src/{agents → extensions/config}/index.ts +7 -11
  183. package/src/{runtime/commands.test.ts → extensions/config/runtime-commands.test.ts} +20 -3
  184. package/src/{runtime/commands.ts → extensions/config/runtime-commands.ts} +1 -8
  185. package/src/{agents → extensions/config}/unified-config-file-watcher.ts +15 -2
  186. package/src/{agents → extensions/config}/user-instruction-config-loader.test.ts +90 -2
  187. package/src/{agents → extensions/config}/user-instruction-config-loader.ts +126 -12
  188. package/src/extensions/context/agentic-compaction.ts +119 -0
  189. package/src/extensions/context/basic-compaction.ts +275 -0
  190. package/src/extensions/context/compaction-shared.ts +458 -0
  191. package/src/extensions/context/compaction.test.ts +477 -0
  192. package/src/extensions/context/compaction.ts +203 -0
  193. package/src/extensions/index.ts +12 -0
  194. package/src/extensions/mcp/client.ts +420 -0
  195. package/src/{mcp → extensions/mcp}/index.ts +16 -0
  196. package/src/{mcp → extensions/mcp}/manager.test.ts +1 -2
  197. package/src/{mcp → extensions/mcp}/manager.ts +3 -5
  198. package/src/extensions/mcp/name-transform.ts +33 -0
  199. package/src/extensions/mcp/policies.ts +47 -0
  200. package/src/extensions/mcp/tools.ts +47 -0
  201. package/src/{mcp → extensions/mcp}/types.ts +35 -7
  202. package/src/{agents → extensions/plugin}/plugin-config-loader.test.ts +18 -13
  203. package/src/{agents → extensions/plugin}/plugin-config-loader.ts +1 -1
  204. package/src/{agents → extensions/plugin}/plugin-loader.test.ts +41 -4
  205. package/src/extensions/plugin/plugin-loader.ts +106 -0
  206. package/src/extensions/plugin/plugin-module-import.ts +278 -0
  207. package/src/{agents → extensions/plugin}/plugin-sandbox-bootstrap.ts +30 -92
  208. package/src/{agents → extensions/plugin}/plugin-sandbox.test.ts +60 -3
  209. package/src/{agents → extensions/plugin}/plugin-sandbox.ts +146 -56
  210. package/src/hooks/index.ts +25 -0
  211. package/src/hooks/persistent.ts +661 -0
  212. package/src/hooks/subprocess-runner.ts +196 -0
  213. package/src/hooks/subprocess.ts +669 -0
  214. package/src/index.ts +200 -118
  215. package/src/prompt/default-system.ts +21 -0
  216. package/src/providers/local-provider-registry.ts +1 -1
  217. package/src/providers/local-provider-service.test.ts +23 -2
  218. package/src/providers/local-provider-service.ts +2 -2
  219. package/src/runtime/checkpoint-hooks.test.ts +167 -0
  220. package/src/runtime/checkpoint-hooks.ts +186 -0
  221. package/src/runtime/hook-file-hooks.test.ts +40 -1
  222. package/src/runtime/hook-file-hooks.ts +35 -16
  223. package/src/runtime/index.ts +4 -19
  224. package/src/runtime/rules.ts +4 -1
  225. package/src/runtime/runtime-builder.team-persistence.test.ts +3 -6
  226. package/src/runtime/runtime-builder.test.ts +266 -160
  227. package/src/runtime/runtime-builder.ts +120 -47
  228. package/src/runtime/runtime-parity.test.ts +22 -22
  229. package/src/runtime/session-runtime.ts +36 -6
  230. package/src/runtime/{sandbox/subprocess-sandbox.ts → subprocess-sandbox.ts} +24 -3
  231. package/src/runtime/team-runtime-registry.ts +1 -4
  232. package/src/runtime/tool-approval.ts +1 -1
  233. package/src/session/default-session-manager.e2e.test.ts +2 -2
  234. package/src/session/default-session-manager.test.ts +553 -9
  235. package/src/session/default-session-manager.ts +140 -46
  236. package/src/session/file-session-service.ts +3 -3
  237. package/src/session/index.ts +6 -6
  238. package/src/session/persistence-service.test.ts +212 -0
  239. package/src/session/{unified-session-persistence-service.ts → persistence-service.ts} +106 -172
  240. package/src/session/rpc-session-service.ts +3 -3
  241. package/src/session/runtime-oauth-token-manager.ts +1 -1
  242. package/src/session/session-agent-events.ts +1 -1
  243. package/src/session/session-artifacts.ts +32 -4
  244. package/src/session/session-config-builder.ts +22 -9
  245. package/src/session/session-graph.ts +1 -1
  246. package/src/session/session-host.ts +19 -11
  247. package/src/session/session-manager.ts +11 -6
  248. package/src/session/session-service.team-persistence.test.ts +1 -1
  249. package/src/session/session-service.ts +6 -9
  250. package/src/session/session-team-coordination.ts +7 -3
  251. package/src/session/session-telemetry.ts +1 -1
  252. package/src/session/utils/helpers.test.ts +160 -0
  253. package/src/session/utils/helpers.ts +289 -42
  254. package/src/session/utils/types.ts +47 -7
  255. package/src/session/workspace-manager.ts +5 -3
  256. package/src/session/workspace-manifest.ts +3 -49
  257. package/src/storage/file-team-store.ts +2 -5
  258. package/src/storage/provider-settings-legacy-migration.ts +2 -2
  259. package/src/storage/provider-settings-manager.test.ts +1 -1
  260. package/src/storage/sqlite-team-store.ts +212 -125
  261. package/src/storage/team-store.ts +1 -5
  262. package/src/team/delegated-agent.ts +131 -0
  263. package/src/team/index.ts +1 -0
  264. package/src/team/multi-agent.lifecycle.test.ts +201 -0
  265. package/src/team/multi-agent.ts +1666 -0
  266. package/src/team/projections.ts +2 -4
  267. package/src/team/runtime.ts +54 -0
  268. package/src/team/spawn-agent-tool.test.ts +387 -0
  269. package/src/team/spawn-agent-tool.ts +207 -0
  270. package/src/team/subagent-prompts.ts +41 -0
  271. package/src/team/team-tools.test.ts +802 -0
  272. package/src/team/team-tools.ts +792 -0
  273. package/src/telemetry/OpenTelemetryProvider.test.ts +25 -3
  274. package/src/telemetry/OpenTelemetryProvider.ts +108 -18
  275. package/src/telemetry/TelemetryLoggerSink.test.ts +42 -0
  276. package/src/telemetry/{LoggerTelemetryAdapter.ts → TelemetryLoggerSink.ts} +21 -14
  277. package/src/telemetry/TelemetryService.test.ts +7 -7
  278. package/src/telemetry/TelemetryService.ts +2 -4
  279. package/src/tools/definitions.test.ts +76 -0
  280. package/src/tools/definitions.ts +41 -2
  281. package/src/tools/executors/apply-patch.ts +1 -1
  282. package/src/tools/executors/editor.ts +1 -1
  283. package/src/tools/executors/file-read.ts +1 -1
  284. package/src/tools/executors/search.ts +1 -1
  285. package/src/tools/executors/web-fetch.ts +1 -1
  286. package/src/tools/index.ts +6 -1
  287. package/src/tools/model-tool-routing.ts +2 -0
  288. package/src/tools/presets.test.ts +8 -0
  289. package/src/tools/presets.ts +40 -2
  290. package/src/tools/schemas.ts +19 -0
  291. package/src/tools/types.ts +31 -2
  292. package/src/types/config.ts +61 -7
  293. package/src/types/events.ts +1 -1
  294. package/src/types/index.ts +0 -1
  295. package/src/types/provider-settings.ts +1 -1
  296. package/src/types/storage.ts +2 -5
  297. package/src/types.ts +32 -44
  298. package/dist/agents/agent-config-loader.d.ts.map +0 -1
  299. package/dist/agents/agent-config-parser.d.ts.map +0 -1
  300. package/dist/agents/hooks-config-loader.d.ts.map +0 -1
  301. package/dist/agents/index.d.ts.map +0 -1
  302. package/dist/agents/plugin-config-loader.d.ts.map +0 -1
  303. package/dist/agents/plugin-loader.d.ts.map +0 -1
  304. package/dist/agents/plugin-sandbox-bootstrap.js +0 -446
  305. package/dist/agents/plugin-sandbox.d.ts.map +0 -1
  306. package/dist/agents/unified-config-file-watcher.d.ts.map +0 -1
  307. package/dist/agents/user-instruction-config-loader.d.ts.map +0 -1
  308. package/dist/mcp/config-loader.d.ts.map +0 -1
  309. package/dist/mcp/index.d.ts +0 -5
  310. package/dist/mcp/index.d.ts.map +0 -1
  311. package/dist/mcp/manager.d.ts.map +0 -1
  312. package/dist/mcp/types.d.ts.map +0 -1
  313. package/dist/runtime/commands.d.ts.map +0 -1
  314. package/dist/runtime/sandbox/subprocess-sandbox.d.ts.map +0 -1
  315. package/dist/runtime/skills.d.ts +0 -14
  316. package/dist/runtime/skills.d.ts.map +0 -1
  317. package/dist/runtime/workflows.d.ts +0 -14
  318. package/dist/runtime/workflows.d.ts.map +0 -1
  319. package/dist/session/unified-session-persistence-service.d.ts.map +0 -1
  320. package/dist/telemetry/LoggerTelemetryAdapter.d.ts.map +0 -1
  321. package/dist/types/workspace.d.ts +0 -8
  322. package/dist/types/workspace.d.ts.map +0 -1
  323. package/src/agents/plugin-loader.ts +0 -175
  324. package/src/runtime/skills.ts +0 -44
  325. package/src/runtime/workflows.test.ts +0 -119
  326. package/src/runtime/workflows.ts +0 -45
  327. package/src/session/unified-session-persistence-service.test.ts +0 -85
  328. package/src/telemetry/LoggerTelemetryAdapter.test.ts +0 -42
  329. package/src/types/workspace.ts +0 -7
  330. /package/dist/{agents → extensions/config}/agent-config-loader.d.ts +0 -0
  331. /package/dist/{agents → extensions/config}/unified-config-file-watcher.d.ts +0 -0
  332. /package/dist/{agents → extensions/config}/user-instruction-config-loader.d.ts +0 -0
  333. /package/dist/{mcp → extensions/mcp}/config-loader.d.ts +0 -0
  334. /package/dist/runtime/{sandbox/subprocess-sandbox.d.ts → subprocess-sandbox.d.ts} +0 -0
  335. /package/src/{agents → extensions/config}/agent-config-loader.ts +0 -0
  336. /package/src/{agents → extensions/config}/hooks-config-loader.test.ts +0 -0
  337. /package/src/{agents → extensions/config}/unified-config-file-watcher.test.ts +0 -0
  338. /package/src/{mcp → extensions/mcp}/config-loader.test.ts +0 -0
  339. /package/src/{mcp → extensions/mcp}/config-loader.ts +0 -0
@@ -1,5 +1,5 @@
1
- import type { AgentResult, TeamEvent } from "@clinebot/agents";
2
- import { formatUserInputBlock } from "@clinebot/shared";
1
+ import { type AgentResult, formatUserInputBlock } from "@clinebot/shared";
2
+ import type { TeamEvent } from "../team";
3
3
  import {
4
4
  buildTeamProgressSummary,
5
5
  toTeamProgressLifecycleEvent,
@@ -36,7 +36,8 @@ export function trackTeamRunState(
36
36
  taskId: event.run.taskId,
37
37
  status: event.type.replace("run_", "") as TeamRunUpdate["status"],
38
38
  error: runError,
39
- iterations: event.run.result?.iterations,
39
+ iterations: (event.run.result as { iterations?: number } | undefined)
40
+ ?.iterations,
40
41
  });
41
42
  notifyTeamRunWaiters(session);
42
43
  break;
@@ -98,6 +99,7 @@ export async function dispatchTeamEventToBackend(
98
99
  event.agentId,
99
100
  "failed",
100
101
  `[error] ${event.error.message}`,
102
+ undefined,
101
103
  event.messages,
102
104
  );
103
105
  } else if (event.result?.finishReason === "aborted") {
@@ -107,6 +109,7 @@ export async function dispatchTeamEventToBackend(
107
109
  event.agentId,
108
110
  "cancelled",
109
111
  "[done] aborted",
112
+ event.result,
110
113
  event.result.messages,
111
114
  );
112
115
  } else {
@@ -116,6 +119,7 @@ export async function dispatchTeamEventToBackend(
116
119
  event.agentId,
117
120
  "completed",
118
121
  `[done] ${event.result?.finishReason ?? "completed"}`,
122
+ event.result,
119
123
  event.result?.messages,
120
124
  );
121
125
  }
@@ -2,7 +2,7 @@ import type { ITelemetryService } from "@clinebot/shared";
2
2
  import {
3
3
  listHookConfigFiles,
4
4
  resolveDocumentsHooksDirectoryPath,
5
- } from "../agents/hooks-config-loader";
5
+ } from "../extensions/config/hooks-config-loader";
6
6
  import type { enrichPromptWithMentions } from "../input";
7
7
  import {
8
8
  captureHookDiscovery,
@@ -0,0 +1,160 @@
1
+ import type { MessageWithMetadata } from "@clinebot/llms";
2
+ import type { AgentResult } from "@clinebot/shared";
3
+ import { describe, expect, it } from "vitest";
4
+ import { withLatestAssistantTurnMetadata } from "./helpers";
5
+
6
+ type LegacyStoredMessage = MessageWithMetadata & {
7
+ providerId?: string;
8
+ modelId?: string;
9
+ };
10
+
11
+ function createResult(overrides: Partial<AgentResult> = {}): AgentResult {
12
+ return {
13
+ text: "ok",
14
+ iterations: 1,
15
+ finishReason: "completed",
16
+ usage: {
17
+ inputTokens: 3,
18
+ outputTokens: 2,
19
+ totalCost: 0.01,
20
+ },
21
+ messages: [],
22
+ toolCalls: [],
23
+ durationMs: 1,
24
+ model: {
25
+ id: "claude-sonnet-4-6",
26
+ provider: "anthropic",
27
+ info: {
28
+ id: "claude-sonnet-4-6",
29
+ family: "claude-sonnet-4",
30
+ },
31
+ },
32
+ startedAt: new Date("2026-01-01T00:00:00.000Z"),
33
+ endedAt: new Date("2026-01-01T00:00:01.000Z"),
34
+ ...overrides,
35
+ };
36
+ }
37
+
38
+ describe("withLatestAssistantTurnMetadata", () => {
39
+ it("normalizes legacy stored provider/model fields into modelInfo", () => {
40
+ const messages = [
41
+ { role: "user", content: "hello" },
42
+ { role: "assistant", content: "first answer" },
43
+ { role: "user", content: "again" },
44
+ { role: "assistant", content: "second answer" },
45
+ ] as const;
46
+
47
+ const previousMessages: LegacyStoredMessage[] = [
48
+ { role: "user", content: "hello" },
49
+ {
50
+ role: "assistant",
51
+ content: "first answer",
52
+ providerId: "anthropic",
53
+ modelId: "claude-sonnet-4-5",
54
+ },
55
+ { role: "user", content: "again" },
56
+ ];
57
+
58
+ const persisted = withLatestAssistantTurnMetadata(
59
+ [...messages],
60
+ createResult(),
61
+ previousMessages,
62
+ );
63
+
64
+ expect(persisted[1]).toEqual({
65
+ id: expect.any(String),
66
+ role: "assistant",
67
+ content: "first answer",
68
+ modelInfo: {
69
+ id: "claude-sonnet-4-5",
70
+ provider: "anthropic",
71
+ },
72
+ });
73
+ expect(persisted[3]).toMatchObject({
74
+ id: expect.any(String),
75
+ role: "assistant",
76
+ content: "second answer",
77
+ modelInfo: {
78
+ id: "claude-sonnet-4-6",
79
+ provider: "anthropic",
80
+ family: "claude-sonnet-4",
81
+ },
82
+ metrics: {
83
+ inputTokens: 3,
84
+ outputTokens: 2,
85
+ cost: 0.01,
86
+ },
87
+ });
88
+ expect("providerId" in (persisted[1] ?? {})).toBe(false);
89
+ expect("modelId" in (persisted[1] ?? {})).toBe(false);
90
+ expect("providerId" in (persisted[3] ?? {})).toBe(false);
91
+ expect("modelId" in (persisted[3] ?? {})).toBe(false);
92
+ });
93
+
94
+ it("applies turn model metadata and usage to every assistant message created in the turn", () => {
95
+ const persisted = withLatestAssistantTurnMetadata(
96
+ [
97
+ { role: "user", content: "spawn a team" },
98
+ {
99
+ role: "assistant",
100
+ content: [{ type: "tool_use", id: "1", name: "spawn", input: {} }],
101
+ },
102
+ {
103
+ role: "user",
104
+ content: [{ type: "tool_result", tool_use_id: "1", content: "ok" }],
105
+ },
106
+ {
107
+ role: "assistant",
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: "Both agents are spawned! Now let me send them tasks.",
112
+ },
113
+ { type: "tool_use", id: "2", name: "run_task", input: {} },
114
+ ],
115
+ },
116
+ ],
117
+ createResult({
118
+ usage: {
119
+ inputTokens: 10,
120
+ outputTokens: 5,
121
+ cacheReadTokens: 2,
122
+ cacheWriteTokens: 1,
123
+ totalCost: 0.12,
124
+ },
125
+ }),
126
+ [],
127
+ );
128
+
129
+ expect(persisted[1]).toMatchObject({
130
+ role: "assistant",
131
+ modelInfo: {
132
+ id: "claude-sonnet-4-6",
133
+ provider: "anthropic",
134
+ family: "claude-sonnet-4",
135
+ },
136
+ metrics: {
137
+ inputTokens: 10,
138
+ outputTokens: 5,
139
+ cacheReadTokens: 2,
140
+ cacheWriteTokens: 1,
141
+ cost: 0.12,
142
+ },
143
+ });
144
+ expect(persisted[3]).toMatchObject({
145
+ role: "assistant",
146
+ modelInfo: {
147
+ id: "claude-sonnet-4-6",
148
+ provider: "anthropic",
149
+ family: "claude-sonnet-4",
150
+ },
151
+ metrics: {
152
+ inputTokens: 10,
153
+ outputTokens: 5,
154
+ cacheReadTokens: 2,
155
+ cacheWriteTokens: 1,
156
+ cost: 0.12,
157
+ },
158
+ });
159
+ });
160
+ });
@@ -1,23 +1,18 @@
1
- import type { AgentConfig, AgentEvent, AgentResult } from "@clinebot/agents";
2
- import type * as LlmsProviders from "@clinebot/llms/providers";
3
- import type { SessionSource } from "../../types/common";
1
+ import { writeFileSync } from "node:fs";
2
+ import type * as LlmsProviders from "@clinebot/llms";
3
+ import type { AgentConfig, AgentEvent, AgentResult } from "@clinebot/shared";
4
+ import { normalizeUserInput } from "@clinebot/shared";
5
+ import { nanoid } from "nanoid";
6
+ import type { SessionSource, SessionStatus } from "../../types/common";
4
7
  import type { SessionRecord } from "../../types/sessions";
8
+ import { parseSubSessionId, parseTeamTaskSubSessionId } from "../session-graph";
9
+ import {
10
+ type SessionManifest,
11
+ SessionManifestSchema,
12
+ } from "../session-manifest";
5
13
  import type { SessionRow } from "../session-service";
6
14
  import type { StoredMessageWithMetadata } from "./types";
7
15
 
8
- const WORKSPACE_CONFIGURATION_MARKER = "# Workspace Configuration";
9
-
10
- export function extractWorkspaceMetadataFromSystemPrompt(
11
- systemPrompt: string,
12
- ): string | undefined {
13
- const markerIndex = systemPrompt.lastIndexOf(WORKSPACE_CONFIGURATION_MARKER);
14
- if (markerIndex < 0) {
15
- return undefined;
16
- }
17
- const metadata = systemPrompt.slice(markerIndex).trim();
18
- return metadata.length > 0 ? metadata : undefined;
19
- }
20
-
21
16
  export function hasRuntimeHooks(hooks: AgentConfig["hooks"]): boolean {
22
17
  if (!hooks) {
23
18
  return false;
@@ -58,6 +53,67 @@ export function serializeAgentEvent(event: AgentEvent): string {
58
53
  });
59
54
  }
60
55
 
56
+ function trimNonEmptyString(value: unknown): string | undefined {
57
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
58
+ }
59
+
60
+ function normalizeStoredMessageModelMetadata(
61
+ message: StoredMessageWithMetadata,
62
+ fallback?: {
63
+ id?: string;
64
+ provider?: string;
65
+ family?: string;
66
+ },
67
+ ): StoredMessageWithMetadata {
68
+ const next = {
69
+ ...(message as StoredMessageWithMetadata & {
70
+ providerId?: string;
71
+ modelId?: string;
72
+ }),
73
+ };
74
+ next.id = trimNonEmptyString(next.id) ?? nanoid();
75
+ const modelInfo =
76
+ next.modelInfo && typeof next.modelInfo === "object"
77
+ ? { ...next.modelInfo }
78
+ : undefined;
79
+ const resolvedModelInfo = {
80
+ id:
81
+ trimNonEmptyString(modelInfo?.id) ??
82
+ trimNonEmptyString(next.modelId) ??
83
+ trimNonEmptyString(fallback?.id),
84
+ provider:
85
+ trimNonEmptyString(modelInfo?.provider) ??
86
+ trimNonEmptyString(next.providerId) ??
87
+ trimNonEmptyString(fallback?.provider),
88
+ family:
89
+ trimNonEmptyString(modelInfo?.family) ??
90
+ trimNonEmptyString(fallback?.family),
91
+ };
92
+
93
+ delete next.providerId;
94
+ delete next.modelId;
95
+
96
+ if (resolvedModelInfo.id && resolvedModelInfo.provider) {
97
+ next.modelInfo = {
98
+ id: resolvedModelInfo.id,
99
+ provider: resolvedModelInfo.provider,
100
+ ...(resolvedModelInfo.family ? { family: resolvedModelInfo.family } : {}),
101
+ };
102
+ } else {
103
+ delete next.modelInfo;
104
+ }
105
+
106
+ return next;
107
+ }
108
+
109
+ export function normalizeStoredMessagesForPersistence(
110
+ messages: LlmsProviders.MessageWithMetadata[],
111
+ ): StoredMessageWithMetadata[] {
112
+ return messages.map((message) =>
113
+ normalizeStoredMessageModelMetadata(message as StoredMessageWithMetadata),
114
+ );
115
+ }
116
+
61
117
  export function withLatestAssistantTurnMetadata(
62
118
  messages: LlmsProviders.Message[],
63
119
  result: AgentResult,
@@ -68,41 +124,51 @@ export function withLatestAssistantTurnMetadata(
68
124
  const sameMessage =
69
125
  previous?.role === message.role &&
70
126
  JSON.stringify(previous.content) === JSON.stringify(message.content);
71
- return sameMessage
127
+ const merged = sameMessage
72
128
  ? ({
73
129
  ...previous,
74
130
  ...message,
75
131
  } as StoredMessageWithMetadata)
76
132
  : ({ ...message } as StoredMessageWithMetadata);
133
+ return normalizeStoredMessageModelMetadata(merged);
77
134
  });
78
- const assistantIndex = [...next]
79
- .reverse()
80
- .findIndex((message) => message.role === "assistant");
81
- if (assistantIndex === -1) {
82
- return next;
135
+ const firstNewMessageIndex = previousMessages.length;
136
+ const assistantIndexes: number[] = [];
137
+ for (let index = firstNewMessageIndex; index < next.length; index += 1) {
138
+ if (next[index]?.role === "assistant") {
139
+ assistantIndexes.push(index);
140
+ }
141
+ }
142
+ if (assistantIndexes.length === 0) {
143
+ const lastAssistantIndex = [...next]
144
+ .reverse()
145
+ .findIndex((message) => message.role === "assistant");
146
+ if (lastAssistantIndex === -1) {
147
+ return next;
148
+ }
149
+ assistantIndexes.push(next.length - 1 - lastAssistantIndex);
83
150
  }
84
151
 
85
- const targetIndex = next.length - 1 - assistantIndex;
86
- const target = next[targetIndex];
87
152
  const usage = result.usage;
88
- next[targetIndex] = {
89
- ...target,
90
- providerId: target.providerId ?? result.model.provider,
91
- modelId: target.modelId ?? result.model.id,
92
- modelInfo: target.modelInfo ?? {
93
- id: result.model.id,
94
- provider: result.model.provider,
95
- },
96
- metrics: {
97
- ...(target.metrics ?? {}),
98
- inputTokens: usage.inputTokens,
99
- outputTokens: usage.outputTokens,
100
- cacheReadTokens: usage.cacheReadTokens,
101
- cacheWriteTokens: usage.cacheWriteTokens,
102
- cost: usage.totalCost,
103
- },
104
- ts: target.ts ?? result.endedAt.getTime(),
105
- };
153
+ for (const targetIndex of assistantIndexes) {
154
+ const target = next[targetIndex];
155
+ next[targetIndex] = {
156
+ ...normalizeStoredMessageModelMetadata(target, {
157
+ id: result.model.id,
158
+ provider: result.model.provider,
159
+ family: result.model.info?.family,
160
+ }),
161
+ metrics: {
162
+ ...(target.metrics ?? {}),
163
+ inputTokens: usage.inputTokens,
164
+ outputTokens: usage.outputTokens,
165
+ cacheReadTokens: usage.cacheReadTokens ?? 0,
166
+ cacheWriteTokens: usage.cacheWriteTokens ?? 0,
167
+ cost: usage.totalCost,
168
+ },
169
+ ts: target.ts ?? result.endedAt.getTime(),
170
+ };
171
+ }
106
172
  return next;
107
173
  }
108
174
 
@@ -137,3 +203,184 @@ export function toSessionRecord(row: SessionRow): SessionRecord {
137
203
  updatedAt: row.updatedAt,
138
204
  };
139
205
  }
206
+
207
+ // ── Metadata helpers ──────────────────────────────────────────────────
208
+
209
+ const MAX_TITLE_LENGTH = 120;
210
+
211
+ export function normalizeTitle(title?: string | null): string | undefined {
212
+ const trimmed = title?.trim();
213
+ return trimmed ? trimmed.slice(0, MAX_TITLE_LENGTH) : undefined;
214
+ }
215
+
216
+ export function deriveTitleFromPrompt(
217
+ prompt?: string | null,
218
+ ): string | undefined {
219
+ const normalized = normalizeUserInput(prompt ?? "").trim();
220
+ if (!normalized) return undefined;
221
+ return normalizeTitle(normalized.split("\n")[0]?.trim());
222
+ }
223
+
224
+ export function sanitizeMetadata(
225
+ metadata: Record<string, unknown> | null | undefined,
226
+ ): Record<string, unknown> | undefined {
227
+ if (!metadata) return undefined;
228
+ const next = { ...metadata };
229
+ const title = normalizeTitle(
230
+ typeof next.title === "string" ? next.title : undefined,
231
+ );
232
+ if (title) {
233
+ next.title = title;
234
+ } else {
235
+ delete next.title;
236
+ }
237
+ return Object.keys(next).length > 0 ? next : undefined;
238
+ }
239
+
240
+ export function resolveMetadataWithTitle(input: {
241
+ metadata?: Record<string, unknown> | null;
242
+ title?: string | null;
243
+ prompt?: string | null;
244
+ }): Record<string, unknown> | undefined {
245
+ const base = sanitizeMetadata(input.metadata) ?? {};
246
+ const title =
247
+ input.title !== undefined
248
+ ? normalizeTitle(input.title)
249
+ : deriveTitleFromPrompt(input.prompt);
250
+ if (title) base.title = title;
251
+ return Object.keys(base).length > 0 ? base : undefined;
252
+ }
253
+
254
+ // ── Messages file helpers ─────────────────────────────────────────────
255
+
256
+ export type MessagesFileContext = {
257
+ agent: "lead" | "subagent" | "teammate";
258
+ sessionId: string;
259
+ taskType?: string;
260
+ };
261
+
262
+ export type MessagesFilePayload = {
263
+ version: number;
264
+ updated_at: string;
265
+ agent: "lead" | "subagent" | "teammate";
266
+ sessionId: string;
267
+ taskType?: string;
268
+ systemPrompt?: string;
269
+ messages: LlmsProviders.Message[];
270
+ };
271
+
272
+ export function buildMessagesFilePayload(input: {
273
+ updatedAt: string;
274
+ context: MessagesFileContext;
275
+ messages: LlmsProviders.Message[];
276
+ systemPrompt?: string;
277
+ }): MessagesFilePayload {
278
+ return {
279
+ version: 1,
280
+ updated_at: input.updatedAt,
281
+ agent: input.context.agent,
282
+ sessionId: input.context.sessionId,
283
+ ...(input.context.taskType ? { taskType: input.context.taskType } : {}),
284
+ ...(input.systemPrompt ? { systemPrompt: input.systemPrompt } : {}),
285
+ messages: input.messages,
286
+ };
287
+ }
288
+
289
+ export function writeEmptyMessagesFile(
290
+ path: string,
291
+ startedAt: string,
292
+ context: MessagesFileContext,
293
+ ): void {
294
+ writeFileSync(
295
+ path,
296
+ `${JSON.stringify(
297
+ buildMessagesFilePayload({
298
+ updatedAt: startedAt,
299
+ context,
300
+ messages: [],
301
+ }),
302
+ null,
303
+ 2,
304
+ )}\n`,
305
+ "utf8",
306
+ );
307
+ }
308
+
309
+ export function resolveMessagesFileContext(
310
+ sessionId: string,
311
+ ): MessagesFileContext {
312
+ const teamTask = parseTeamTaskSubSessionId(sessionId);
313
+ if (teamTask) {
314
+ return {
315
+ agent: "teammate",
316
+ sessionId: teamTask.rootSessionId,
317
+ taskType: "team",
318
+ };
319
+ }
320
+
321
+ const subagent = parseSubSessionId(sessionId);
322
+ if (subagent) {
323
+ return {
324
+ agent: "subagent",
325
+ sessionId: subagent.rootSessionId,
326
+ };
327
+ }
328
+
329
+ return {
330
+ agent: "lead",
331
+ sessionId,
332
+ };
333
+ }
334
+
335
+ // ── Manifest helper ───────────────────────────────────────────────────
336
+
337
+ export function buildManifestFromRow(
338
+ row: SessionRow,
339
+ overrides?: {
340
+ status?: SessionStatus;
341
+ endedAt?: string | null;
342
+ exitCode?: number | null;
343
+ metadata?: Record<string, unknown>;
344
+ },
345
+ ): SessionManifest {
346
+ return SessionManifestSchema.parse({
347
+ version: 1,
348
+ session_id: row.sessionId,
349
+ source: row.source,
350
+ pid: row.pid,
351
+ started_at: row.startedAt,
352
+ ended_at: overrides?.endedAt ?? row.endedAt ?? undefined,
353
+ exit_code: overrides?.exitCode ?? row.exitCode ?? undefined,
354
+ status: overrides?.status ?? row.status,
355
+ interactive: row.interactive,
356
+ provider: row.provider,
357
+ model: row.model,
358
+ cwd: row.cwd,
359
+ workspace_root: row.workspaceRoot,
360
+ team_name: row.teamName ?? undefined,
361
+ enable_tools: row.enableTools,
362
+ enable_spawn: row.enableSpawn,
363
+ enable_teams: row.enableTeams,
364
+ prompt: row.prompt ?? undefined,
365
+ metadata: overrides?.metadata ?? row.metadata ?? undefined,
366
+ messages_path: row.messagesPath ?? undefined,
367
+ });
368
+ }
369
+
370
+ // ── OCC retry helper ──────────────────────────────────────────────────
371
+
372
+ export const OCC_MAX_RETRIES = 4;
373
+
374
+ export async function withOccRetry<T>(
375
+ getRow: () => Promise<{ statusLock: number } | undefined>,
376
+ action: (statusLock: number) => Promise<{ updated: boolean } & T>,
377
+ maxRetries = OCC_MAX_RETRIES,
378
+ ): Promise<({ updated: true } & T) | { updated: false }> {
379
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
380
+ const row = getRow ? await getRow() : undefined;
381
+ if (!row) return { updated: false } as { updated: false };
382
+ const result = await action(row.statusLock);
383
+ if (result.updated) return result as { updated: true } & T;
384
+ }
385
+ return { updated: false } as { updated: false };
386
+ }
@@ -1,10 +1,10 @@
1
1
  import type { Agent } from "@clinebot/agents";
2
- import type * as LlmsProviders from "@clinebot/llms/providers";
2
+ import type * as LlmsProviders from "@clinebot/llms";
3
3
  import type { BuiltRuntime } from "../../runtime/session-runtime";
4
- import type { SessionSource } from "../../types/common";
4
+ import type { SessionSource, SessionStatus } from "../../types/common";
5
5
  import type { CoreSessionConfig } from "../../types/config";
6
6
  import type { SessionAccumulatedUsage } from "../session-manager";
7
- import type { RootSessionArtifacts } from "../session-service";
7
+ import type { RootSessionArtifacts, SessionRow } from "../session-service";
8
8
 
9
9
  export type ActiveSession = {
10
10
  sessionId: string;
@@ -45,13 +45,53 @@ export type TeamRunUpdate = {
45
45
  iterations?: number;
46
46
  };
47
47
 
48
- export type StoredMessageWithMetadata = LlmsProviders.MessageWithMetadata & {
49
- providerId?: string;
50
- modelId?: string;
51
- };
48
+ export type StoredMessageWithMetadata = LlmsProviders.MessageWithMetadata;
52
49
 
53
50
  export type PreparedTurnInput = {
54
51
  prompt: string;
55
52
  userImages?: string[];
56
53
  userFiles?: string[];
57
54
  };
55
+
56
+ // ── Persistence interfaces ────────────────────────────────────────────
57
+
58
+ export interface PersistedSessionUpdateInput {
59
+ sessionId: string;
60
+ expectedStatusLock?: number;
61
+ status?: SessionStatus;
62
+ endedAt?: string | null;
63
+ exitCode?: number | null;
64
+ prompt?: string | null;
65
+ metadata?: Record<string, unknown> | null;
66
+ title?: string | null;
67
+ parentSessionId?: string | null;
68
+ parentAgentId?: string | null;
69
+ agentId?: string | null;
70
+ conversationId?: string | null;
71
+ setRunning?: boolean;
72
+ }
73
+
74
+ export interface SessionPersistenceAdapter {
75
+ ensureSessionsDir(): string;
76
+ upsertSession(row: SessionRow): Promise<void>;
77
+ getSession(sessionId: string): Promise<SessionRow | undefined>;
78
+ listSessions(options: {
79
+ limit: number;
80
+ parentSessionId?: string;
81
+ status?: string;
82
+ }): Promise<SessionRow[]>;
83
+ updateSession(
84
+ input: PersistedSessionUpdateInput,
85
+ ): Promise<{ updated: boolean; statusLock: number }>;
86
+ deleteSession(sessionId: string, cascade: boolean): Promise<boolean>;
87
+ enqueueSpawnRequest(input: {
88
+ rootSessionId: string;
89
+ parentAgentId: string;
90
+ task?: string;
91
+ systemPrompt?: string;
92
+ }): Promise<void>;
93
+ claimSpawnRequest(
94
+ rootSessionId: string,
95
+ parentAgentId: string,
96
+ ): Promise<string | undefined>;
97
+ }
@@ -1,10 +1,12 @@
1
- import type { WorkspaceInfo } from "../types/workspace";
2
1
  import {
3
- generateWorkspaceInfo,
4
- normalizeWorkspacePath,
5
2
  upsertWorkspaceInfo,
3
+ type WorkspaceInfo,
6
4
  type WorkspaceManifest,
7
5
  WorkspaceManifestSchema,
6
+ } from "@clinebot/shared";
7
+ import {
8
+ generateWorkspaceInfo,
9
+ normalizeWorkspacePath,
8
10
  } from "./workspace-manifest";
9
11
 
10
12
  export interface WorkspaceManagerEvent {