@clinebot/core 0.0.35 → 0.0.37

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 (441) hide show
  1. package/README.md +1 -2
  2. package/dist/ClineCore.d.ts +362 -39
  3. package/dist/ClineCore.d.ts.map +1 -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 +6 -6
  8. package/dist/account/rpc.d.ts.map +1 -1
  9. package/dist/cron/cron-event-ingress.d.ts +38 -0
  10. package/dist/cron/cron-event-ingress.d.ts.map +1 -0
  11. package/dist/cron/cron-materializer.d.ts +36 -0
  12. package/dist/cron/cron-materializer.d.ts.map +1 -0
  13. package/dist/cron/cron-reconciler.d.ts +62 -0
  14. package/dist/cron/cron-reconciler.d.ts.map +1 -0
  15. package/dist/cron/cron-report-writer.d.ts +41 -0
  16. package/dist/cron/cron-report-writer.d.ts.map +1 -0
  17. package/dist/cron/cron-runner.d.ts +43 -0
  18. package/dist/cron/cron-runner.d.ts.map +1 -0
  19. package/dist/cron/cron-schema.d.ts +3 -0
  20. package/dist/cron/cron-schema.d.ts.map +1 -0
  21. package/dist/cron/cron-service.d.ts +57 -0
  22. package/dist/cron/cron-service.d.ts.map +1 -0
  23. package/dist/cron/cron-spec-parser.d.ts +27 -0
  24. package/dist/cron/cron-spec-parser.d.ts.map +1 -0
  25. package/dist/cron/cron-watcher.d.ts +23 -0
  26. package/dist/cron/cron-watcher.d.ts.map +1 -0
  27. package/dist/cron/resource-limiter.d.ts +9 -0
  28. package/dist/cron/resource-limiter.d.ts.map +1 -0
  29. package/dist/cron/schedule-command-service.d.ts +10 -0
  30. package/dist/cron/schedule-command-service.d.ts.map +1 -0
  31. package/dist/cron/schedule-service.d.ts +100 -0
  32. package/dist/cron/schedule-service.d.ts.map +1 -0
  33. package/dist/cron/scheduler.d.ts +68 -0
  34. package/dist/cron/scheduler.d.ts.map +1 -0
  35. package/dist/cron/sqlite-cron-store.d.ts +230 -0
  36. package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
  37. package/dist/cron/sqlite-schedule-store.d.ts +52 -0
  38. package/dist/cron/sqlite-schedule-store.d.ts.map +1 -0
  39. package/dist/extensions/config/agent-config-loader.d.ts +4 -3
  40. package/dist/extensions/config/agent-config-loader.d.ts.map +1 -1
  41. package/dist/extensions/config/runtime-commands.d.ts +1 -0
  42. package/dist/extensions/config/runtime-commands.d.ts.map +1 -1
  43. package/dist/extensions/config/user-instruction-config-loader.d.ts +1 -0
  44. package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -1
  45. package/dist/extensions/context/agentic-compaction.d.ts +2 -2
  46. package/dist/extensions/context/agentic-compaction.d.ts.map +1 -1
  47. package/dist/extensions/context/compaction-shared.d.ts +5 -4
  48. package/dist/extensions/context/compaction-shared.d.ts.map +1 -1
  49. package/dist/extensions/context/compaction.d.ts.map +1 -1
  50. package/dist/extensions/plugin/plugin-config-loader.d.ts +15 -2
  51. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
  52. package/dist/extensions/plugin/plugin-loader.d.ts +13 -7
  53. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
  54. package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -1
  55. package/dist/extensions/plugin/plugin-sandbox.d.ts +21 -2
  56. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
  57. package/dist/extensions/plugin/plugin-targeting.d.ts +7 -0
  58. package/dist/extensions/plugin/plugin-targeting.d.ts.map +1 -0
  59. package/dist/extensions/plugin-sandbox-bootstrap.js +237 -276
  60. package/dist/extensions/tools/constants.d.ts +1 -0
  61. package/dist/extensions/tools/constants.d.ts.map +1 -1
  62. package/dist/extensions/tools/definitions.d.ts +3 -4
  63. package/dist/extensions/tools/definitions.d.ts.map +1 -1
  64. package/dist/extensions/tools/executors/apply-patch.d.ts +3 -1
  65. package/dist/extensions/tools/executors/apply-patch.d.ts.map +1 -1
  66. package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
  67. package/dist/extensions/tools/executors/search.d.ts +1 -1
  68. package/dist/extensions/tools/executors/search.d.ts.map +1 -1
  69. package/dist/extensions/tools/helpers.d.ts +1 -0
  70. package/dist/extensions/tools/helpers.d.ts.map +1 -1
  71. package/dist/extensions/tools/index.d.ts +3 -2
  72. package/dist/extensions/tools/index.d.ts.map +1 -1
  73. package/dist/extensions/tools/presets.d.ts +27 -44
  74. package/dist/extensions/tools/presets.d.ts.map +1 -1
  75. package/dist/extensions/tools/runtime.d.ts +25 -0
  76. package/dist/extensions/tools/runtime.d.ts.map +1 -0
  77. package/dist/extensions/tools/schemas.d.ts +25 -3
  78. package/dist/extensions/tools/schemas.d.ts.map +1 -1
  79. package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
  80. package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
  81. package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
  82. package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
  83. package/dist/extensions/tools/team/team-tools.d.ts +1 -0
  84. package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
  85. package/dist/extensions/tools/types.d.ts +0 -5
  86. package/dist/extensions/tools/types.d.ts.map +1 -1
  87. package/dist/hooks/hook-bridge.d.ts +118 -0
  88. package/dist/hooks/hook-bridge.d.ts.map +1 -0
  89. package/dist/hooks/hook-file-hooks.d.ts +6 -2
  90. package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
  91. package/dist/hooks/hook-registry.d.ts +16 -0
  92. package/dist/hooks/hook-registry.d.ts.map +1 -0
  93. package/dist/hooks/index.d.ts +0 -1
  94. package/dist/hooks/index.d.ts.map +1 -1
  95. package/dist/hooks/subprocess.d.ts +8 -1
  96. package/dist/hooks/subprocess.d.ts.map +1 -1
  97. package/dist/hub/browser-websocket.d.ts +18 -0
  98. package/dist/hub/browser-websocket.d.ts.map +1 -0
  99. package/dist/hub/client.d.ts +51 -0
  100. package/dist/hub/client.d.ts.map +1 -0
  101. package/dist/hub/connect.d.ts +15 -0
  102. package/dist/hub/connect.d.ts.map +1 -0
  103. package/dist/hub/daemon-entry.d.ts +2 -0
  104. package/dist/hub/daemon-entry.d.ts.map +1 -0
  105. package/dist/hub/daemon-entry.js +1305 -0
  106. package/dist/hub/daemon.d.ts +5 -0
  107. package/dist/hub/daemon.d.ts.map +1 -0
  108. package/dist/hub/defaults.d.ts +17 -0
  109. package/dist/hub/defaults.d.ts.map +1 -0
  110. package/dist/hub/discovery.d.ts +29 -0
  111. package/dist/hub/discovery.d.ts.map +1 -0
  112. package/dist/hub/index.d.ts +15 -0
  113. package/dist/hub/index.d.ts.map +1 -0
  114. package/dist/hub/index.js +1294 -0
  115. package/dist/hub/native-transport.d.ts +17 -0
  116. package/dist/hub/native-transport.d.ts.map +1 -0
  117. package/dist/hub/runtime-handlers.d.ts +11 -0
  118. package/dist/hub/runtime-handlers.d.ts.map +1 -0
  119. package/dist/hub/server.d.ts +104 -0
  120. package/dist/hub/server.d.ts.map +1 -0
  121. package/dist/hub/session-client.d.ts +90 -0
  122. package/dist/hub/session-client.d.ts.map +1 -0
  123. package/dist/hub/start-shared-server.d.ts +19 -0
  124. package/dist/hub/start-shared-server.d.ts.map +1 -0
  125. package/dist/hub/transport.d.ts +8 -0
  126. package/dist/hub/transport.d.ts.map +1 -0
  127. package/dist/hub/ui-client.d.ts +45 -0
  128. package/dist/hub/ui-client.d.ts.map +1 -0
  129. package/dist/hub/workspace.d.ts +4 -0
  130. package/dist/hub/workspace.d.ts.map +1 -0
  131. package/dist/index.d.ts +29 -16
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +782 -471
  134. package/dist/llms/cline-recommended-models.d.ts +20 -0
  135. package/dist/llms/cline-recommended-models.d.ts.map +1 -0
  136. package/dist/llms/configured-provider-registry.d.ts +28 -0
  137. package/dist/llms/configured-provider-registry.d.ts.map +1 -0
  138. package/dist/llms/handler-factory.d.ts +16 -0
  139. package/dist/llms/handler-factory.d.ts.map +1 -0
  140. package/dist/llms/provider-defaults.d.ts +27 -0
  141. package/dist/llms/provider-defaults.d.ts.map +1 -0
  142. package/dist/llms/provider-settings.d.ts +245 -0
  143. package/dist/llms/provider-settings.d.ts.map +1 -0
  144. package/dist/llms/runtime-config.d.ts +4 -0
  145. package/dist/llms/runtime-config.d.ts.map +1 -0
  146. package/dist/llms/runtime-registry.d.ts +20 -0
  147. package/dist/llms/runtime-registry.d.ts.map +1 -0
  148. package/dist/llms/runtime-types.d.ts +85 -0
  149. package/dist/llms/runtime-types.d.ts.map +1 -0
  150. package/dist/runtime/agent-config-adapter.d.ts +148 -0
  151. package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
  152. package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
  153. package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
  154. package/dist/runtime/history.d.ts +6 -0
  155. package/dist/runtime/history.d.ts.map +1 -1
  156. package/dist/runtime/host.d.ts +1 -2
  157. package/dist/runtime/host.d.ts.map +1 -1
  158. package/dist/runtime/loop-detection.d.ts +59 -0
  159. package/dist/runtime/loop-detection.d.ts.map +1 -0
  160. package/dist/runtime/mistake-tracker.d.ts +69 -0
  161. package/dist/runtime/mistake-tracker.d.ts.map +1 -0
  162. package/dist/runtime/rules.d.ts +1 -0
  163. package/dist/runtime/rules.d.ts.map +1 -1
  164. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  165. package/dist/runtime/runtime-event-adapter.d.ts +102 -0
  166. package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
  167. package/dist/runtime/runtime-host.d.ts +49 -26
  168. package/dist/runtime/runtime-host.d.ts.map +1 -1
  169. package/dist/runtime/runtime-oauth-token-manager.d.ts.map +1 -1
  170. package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
  171. package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
  172. package/dist/runtime/session-runtime.d.ts +16 -21
  173. package/dist/runtime/session-runtime.d.ts.map +1 -1
  174. package/dist/runtime/user-input-builder.d.ts +24 -0
  175. package/dist/runtime/user-input-builder.d.ts.map +1 -0
  176. package/dist/services/global-settings.d.ts +12 -0
  177. package/dist/services/global-settings.d.ts.map +1 -0
  178. package/dist/services/index.js +28 -0
  179. package/dist/services/local-runtime-bootstrap.d.ts +9 -3
  180. package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
  181. package/dist/services/plugin-tools.d.ts +16 -0
  182. package/dist/services/plugin-tools.d.ts.map +1 -0
  183. package/dist/services/providers/local-provider-registry.d.ts +199 -23
  184. package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
  185. package/dist/services/providers/local-provider-service.d.ts +15 -13
  186. package/dist/services/providers/local-provider-service.d.ts.map +1 -1
  187. package/dist/services/session-data.d.ts +1 -1
  188. package/dist/services/session-data.d.ts.map +1 -1
  189. package/dist/services/session-telemetry.d.ts +7 -2
  190. package/dist/services/session-telemetry.d.ts.map +1 -1
  191. package/dist/services/storage/file-team-store.d.ts.map +1 -1
  192. package/dist/services/storage/provider-settings-legacy-migration.d.ts +1 -1
  193. package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  194. package/dist/services/storage/provider-settings-manager.d.ts +1 -0
  195. package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
  196. package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
  197. package/dist/services/workspace-manifest.d.ts +11 -0
  198. package/dist/services/workspace-manifest.d.ts.map +1 -1
  199. package/dist/session/conversation-store.d.ts +30 -0
  200. package/dist/session/conversation-store.d.ts.map +1 -0
  201. package/dist/session/message-builder.d.ts +65 -0
  202. package/dist/session/message-builder.d.ts.map +1 -0
  203. package/dist/session/persistence-service.d.ts +11 -23
  204. package/dist/session/persistence-service.d.ts.map +1 -1
  205. package/dist/session/session-manifest-store.d.ts +22 -0
  206. package/dist/session/session-manifest-store.d.ts.map +1 -0
  207. package/dist/session/session-manifest.d.ts +1 -1
  208. package/dist/session/session-row.d.ts +93 -0
  209. package/dist/session/session-row.d.ts.map +1 -0
  210. package/dist/session/session-service.d.ts +2 -102
  211. package/dist/session/session-service.d.ts.map +1 -1
  212. package/dist/session/subagent-session-manager.d.ts +36 -0
  213. package/dist/session/subagent-session-manager.d.ts.map +1 -0
  214. package/dist/session/team-persistence-store.d.ts +24 -0
  215. package/dist/session/team-persistence-store.d.ts.map +1 -0
  216. package/dist/transports/hub.d.ts +58 -0
  217. package/dist/transports/hub.d.ts.map +1 -0
  218. package/dist/transports/local.d.ts +23 -9
  219. package/dist/transports/local.d.ts.map +1 -1
  220. package/dist/transports/remote.d.ts +10 -0
  221. package/dist/transports/remote.d.ts.map +1 -0
  222. package/dist/transports/runtime-host-support.d.ts +3 -2
  223. package/dist/transports/runtime-host-support.d.ts.map +1 -1
  224. package/dist/types/chat-schema.d.ts +15 -17
  225. package/dist/types/chat-schema.d.ts.map +1 -1
  226. package/dist/types/config.d.ts +17 -7
  227. package/dist/types/config.d.ts.map +1 -1
  228. package/dist/types/events.d.ts +7 -6
  229. package/dist/types/events.d.ts.map +1 -1
  230. package/dist/types/provider-settings.d.ts +4 -5
  231. package/dist/types/provider-settings.d.ts.map +1 -1
  232. package/dist/types/session.d.ts +7 -3
  233. package/dist/types/session.d.ts.map +1 -1
  234. package/dist/types.d.ts +11 -4
  235. package/dist/types.d.ts.map +1 -1
  236. package/package.json +20 -6
  237. package/src/ClineCore.ts +757 -44
  238. package/src/account/cline-account-service.ts +44 -6
  239. package/src/account/index.ts +3 -3
  240. package/src/account/rpc.ts +12 -12
  241. package/src/cron/cron-event-ingress.ts +357 -0
  242. package/src/cron/cron-materializer.ts +97 -0
  243. package/src/cron/cron-reconciler.ts +241 -0
  244. package/src/cron/cron-report-writer.ts +153 -0
  245. package/src/cron/cron-runner.ts +495 -0
  246. package/src/cron/cron-schema.ts +127 -0
  247. package/src/cron/cron-service.ts +163 -0
  248. package/src/cron/cron-spec-parser.ts +489 -0
  249. package/src/cron/cron-watcher.ts +102 -0
  250. package/src/cron/index.ts +15 -0
  251. package/src/cron/resource-limiter.ts +46 -0
  252. package/src/cron/schedule-command-service.ts +193 -0
  253. package/src/cron/schedule-service.ts +703 -0
  254. package/src/cron/scheduler.ts +772 -0
  255. package/src/cron/sqlite-cron-store.ts +1286 -0
  256. package/src/cron/sqlite-schedule-store.ts +708 -0
  257. package/src/extensions/config/agent-config-loader.ts +17 -7
  258. package/src/extensions/config/runtime-commands.ts +6 -0
  259. package/src/extensions/config/user-instruction-config-loader.ts +1 -0
  260. package/src/extensions/context/agentic-compaction.ts +3 -3
  261. package/src/extensions/context/basic-compaction.ts +2 -2
  262. package/src/extensions/context/compaction-shared.ts +5 -4
  263. package/src/extensions/context/compaction.ts +3 -3
  264. package/src/extensions/plugin/plugin-config-loader.ts +37 -2
  265. package/src/extensions/plugin/plugin-loader.ts +69 -9
  266. package/src/extensions/plugin/plugin-module-import.ts +0 -2
  267. package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +243 -39
  268. package/src/extensions/plugin/plugin-sandbox.ts +173 -29
  269. package/src/extensions/plugin/plugin-targeting.ts +32 -0
  270. package/src/extensions/tools/constants.ts +2 -0
  271. package/src/extensions/tools/definitions.ts +61 -71
  272. package/src/extensions/tools/executors/apply-patch.ts +69 -80
  273. package/src/extensions/tools/executors/editor.ts +4 -3
  274. package/src/extensions/tools/executors/search.ts +195 -3
  275. package/src/extensions/tools/helpers.ts +24 -0
  276. package/src/extensions/tools/index.ts +11 -2
  277. package/src/extensions/tools/presets.ts +32 -47
  278. package/src/extensions/tools/runtime.ts +261 -0
  279. package/src/extensions/tools/schemas.ts +17 -20
  280. package/src/extensions/tools/team/delegated-agent.ts +8 -3
  281. package/src/extensions/tools/team/multi-agent.ts +135 -19
  282. package/src/extensions/tools/team/team-tools.ts +172 -91
  283. package/src/extensions/tools/types.ts +0 -6
  284. package/src/hooks/hook-bridge.ts +489 -0
  285. package/src/hooks/hook-file-hooks.ts +66 -5
  286. package/src/hooks/hook-registry.ts +257 -0
  287. package/src/hooks/index.ts +0 -7
  288. package/src/hooks/subprocess-runner.ts +1 -1
  289. package/src/hooks/subprocess.ts +9 -0
  290. package/src/hub/browser-websocket.ts +159 -0
  291. package/src/hub/client.ts +633 -0
  292. package/src/hub/connect.ts +156 -0
  293. package/src/hub/daemon-entry.ts +122 -0
  294. package/src/hub/daemon.ts +284 -0
  295. package/src/hub/defaults.ts +70 -0
  296. package/src/hub/discovery.ts +247 -0
  297. package/src/hub/index.ts +14 -0
  298. package/src/hub/native-transport.ts +31 -0
  299. package/src/hub/runtime-handlers.ts +141 -0
  300. package/src/hub/server.ts +2317 -0
  301. package/src/hub/session-client.ts +502 -0
  302. package/src/hub/start-shared-server.ts +61 -0
  303. package/src/hub/transport.ts +14 -0
  304. package/src/hub/ui-client.ts +126 -0
  305. package/src/hub/workspace.ts +19 -0
  306. package/src/index.ts +169 -68
  307. package/src/llms/cline-recommended-models.ts +167 -0
  308. package/src/llms/configured-provider-registry.ts +193 -0
  309. package/src/llms/handler-factory.ts +56 -0
  310. package/src/llms/provider-defaults.ts +653 -0
  311. package/src/llms/provider-settings.ts +310 -0
  312. package/src/llms/runtime-config.ts +43 -0
  313. package/src/llms/runtime-registry.ts +172 -0
  314. package/src/llms/runtime-types.ts +121 -0
  315. package/src/runtime/agent-config-adapter.ts +636 -0
  316. package/src/runtime/agent-runtime-config-builder.ts +205 -0
  317. package/src/runtime/error-feedback.ts +142 -0
  318. package/src/runtime/history.ts +137 -0
  319. package/src/runtime/host.ts +127 -267
  320. package/src/runtime/index.ts +1 -0
  321. package/src/runtime/loop-detection.ts +162 -0
  322. package/src/runtime/mistake-tracker.ts +221 -0
  323. package/src/runtime/rules.ts +12 -0
  324. package/src/runtime/runtime-builder.ts +85 -13
  325. package/src/runtime/runtime-event-adapter.ts +412 -0
  326. package/src/runtime/runtime-host.ts +134 -62
  327. package/src/runtime/runtime-oauth-token-manager.ts +11 -15
  328. package/src/runtime/session-runtime-orchestrator.ts +1253 -0
  329. package/src/runtime/session-runtime.ts +16 -26
  330. package/src/runtime/user-input-builder.ts +167 -0
  331. package/src/services/global-settings.ts +122 -0
  332. package/src/services/local-runtime-bootstrap.ts +175 -31
  333. package/src/services/plugin-tools.ts +86 -0
  334. package/src/services/providers/local-provider-registry.ts +277 -61
  335. package/src/services/providers/local-provider-service.ts +109 -44
  336. package/src/services/session-data.ts +18 -10
  337. package/src/services/session-telemetry.ts +6 -15
  338. package/src/services/storage/file-team-store.ts +1 -5
  339. package/src/services/storage/provider-settings-legacy-migration.ts +14 -51
  340. package/src/services/storage/provider-settings-manager.ts +17 -2
  341. package/src/services/storage/sqlite-team-store.ts +1 -5
  342. package/src/services/workspace-manifest.ts +18 -0
  343. package/src/session/conversation-store.ts +77 -0
  344. package/src/session/file-session-service.ts +1 -1
  345. package/src/session/index.ts +6 -27
  346. package/src/session/message-builder.ts +941 -0
  347. package/src/session/persistence-service.ts +119 -504
  348. package/src/session/session-manifest-store.ts +158 -0
  349. package/src/session/session-row.ts +199 -0
  350. package/src/session/session-service.ts +17 -376
  351. package/src/session/session-team-coordination.ts +1 -1
  352. package/src/session/subagent-session-manager.ts +397 -0
  353. package/src/session/team-persistence-store.ts +176 -0
  354. package/src/transports/hub.ts +1081 -0
  355. package/src/transports/local.ts +419 -93
  356. package/src/transports/remote.ts +27 -0
  357. package/src/transports/runtime-host-support.ts +63 -9
  358. package/src/types/chat-schema.ts +4 -5
  359. package/src/types/config.ts +17 -7
  360. package/src/types/events.ts +8 -6
  361. package/src/types/index.ts +3 -0
  362. package/src/types/provider-settings.ts +18 -7
  363. package/src/types/session.ts +7 -6
  364. package/src/types.ts +42 -2
  365. package/dist/hooks/persistent.d.ts +0 -64
  366. package/dist/hooks/persistent.d.ts.map +0 -1
  367. package/dist/runtime/rpc-runtime-ensure.d.ts +0 -65
  368. package/dist/runtime/rpc-runtime-ensure.d.ts.map +0 -1
  369. package/dist/runtime/rpc-spawn-lease.d.ts +0 -8
  370. package/dist/runtime/rpc-spawn-lease.d.ts.map +0 -1
  371. package/dist/services/telemetry/index.js +0 -15
  372. package/dist/session/rpc-session-service.d.ts +0 -16
  373. package/dist/session/rpc-session-service.d.ts.map +0 -1
  374. package/dist/session/sqlite-rpc-session-backend.d.ts +0 -31
  375. package/dist/session/sqlite-rpc-session-backend.d.ts.map +0 -1
  376. package/dist/transports/rpc.d.ts +0 -51
  377. package/dist/transports/rpc.d.ts.map +0 -1
  378. package/src/ClineCore.test.ts +0 -226
  379. package/src/account/cline-account-service.test.ts +0 -185
  380. package/src/account/featurebase-token.test.ts +0 -175
  381. package/src/account/rpc.test.ts +0 -63
  382. package/src/auth/bounded-ttl-cache.test.ts +0 -38
  383. package/src/auth/client.test.ts +0 -69
  384. package/src/auth/cline.test.ts +0 -267
  385. package/src/auth/codex.test.ts +0 -170
  386. package/src/auth/oca.test.ts +0 -340
  387. package/src/auth/server.test.ts +0 -287
  388. package/src/auth/utils.test.ts +0 -128
  389. package/src/extensions/config/agent-config-loader.test.ts +0 -236
  390. package/src/extensions/config/hooks-config-loader.test.ts +0 -20
  391. package/src/extensions/config/runtime-commands.test.ts +0 -115
  392. package/src/extensions/config/unified-config-file-watcher.test.ts +0 -196
  393. package/src/extensions/config/user-instruction-config-loader.test.ts +0 -246
  394. package/src/extensions/context/compaction.test.ts +0 -483
  395. package/src/extensions/mcp/config-loader.test.ts +0 -238
  396. package/src/extensions/mcp/manager.test.ts +0 -105
  397. package/src/extensions/plugin/plugin-config-loader.test.ts +0 -184
  398. package/src/extensions/plugin/plugin-loader.test.ts +0 -292
  399. package/src/extensions/plugin/plugin-sandbox.test.ts +0 -423
  400. package/src/extensions/tools/definitions.test.ts +0 -780
  401. package/src/extensions/tools/executors/bash.test.ts +0 -87
  402. package/src/extensions/tools/executors/editor.test.ts +0 -35
  403. package/src/extensions/tools/executors/file-read.test.ts +0 -125
  404. package/src/extensions/tools/model-tool-routing.test.ts +0 -86
  405. package/src/extensions/tools/presets.test.ts +0 -70
  406. package/src/extensions/tools/team/multi-agent.lifecycle.test.ts +0 -455
  407. package/src/extensions/tools/team/spawn-agent-tool.test.ts +0 -381
  408. package/src/extensions/tools/team/team-tools.test.ts +0 -918
  409. package/src/hooks/checkpoint-hooks.test.ts +0 -168
  410. package/src/hooks/hook-file-hooks.test.ts +0 -311
  411. package/src/hooks/persistent.ts +0 -661
  412. package/src/runtime/history.test.ts +0 -114
  413. package/src/runtime/host.test.ts +0 -230
  414. package/src/runtime/rpc-runtime-ensure.test.ts +0 -123
  415. package/src/runtime/rpc-runtime-ensure.ts +0 -659
  416. package/src/runtime/rpc-spawn-lease.test.ts +0 -81
  417. package/src/runtime/rpc-spawn-lease.ts +0 -156
  418. package/src/runtime/runtime-builder.team-persistence.test.ts +0 -245
  419. package/src/runtime/runtime-builder.test.ts +0 -615
  420. package/src/runtime/runtime-oauth-token-manager.test.ts +0 -137
  421. package/src/runtime/runtime-parity.test.ts +0 -143
  422. package/src/services/providers/local-provider-service.test.ts +0 -1062
  423. package/src/services/session-data.test.ts +0 -160
  424. package/src/services/storage/provider-settings-legacy-migration.test.ts +0 -424
  425. package/src/services/storage/provider-settings-manager.test.ts +0 -191
  426. package/src/services/telemetry/OpenTelemetryAdapter.test.ts +0 -157
  427. package/src/services/telemetry/OpenTelemetryProvider.test.ts +0 -326
  428. package/src/services/telemetry/TelemetryLoggerSink.test.ts +0 -42
  429. package/src/services/telemetry/TelemetryService.test.ts +0 -134
  430. package/src/services/telemetry/distinct-id.test.ts +0 -57
  431. package/src/services/workspace/file-indexer.d.ts +0 -11
  432. package/src/services/workspace/file-indexer.test.ts +0 -156
  433. package/src/services/workspace/mention-enricher.test.ts +0 -106
  434. package/src/session/persistence-service.test.ts +0 -300
  435. package/src/session/rpc-session-service.ts +0 -114
  436. package/src/session/session-service.team-persistence.test.ts +0 -48
  437. package/src/session/sqlite-rpc-session-backend.ts +0 -301
  438. package/src/transports/local.e2e.test.ts +0 -380
  439. package/src/transports/local.test.ts +0 -2559
  440. package/src/transports/rpc.test.ts +0 -82
  441. package/src/transports/rpc.ts +0 -665
@@ -1,42 +1,18 @@
1
- import { spawn } from "node:child_process";
2
- import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
4
- import { getRpcServerDefaultAddress, getRpcServerHealth } from "@clinebot/rpc";
5
- import {
6
- augmentNodeCommandForDebug,
7
- withResolvedClineBuildEnv,
8
- } from "@clinebot/shared";
9
- import {
10
- resolveClineDataDir,
11
- resolveSessionDataDir,
12
- } from "@clinebot/shared/storage";
13
1
  import type { ClineCoreOptions } from "../ClineCore";
2
+ import {
3
+ ensureCompatibleLocalHubUrl,
4
+ resolveCompatibleLocalHubUrl,
5
+ } from "../hub/client";
6
+ import { prewarmDetachedHubServer } from "../hub/daemon";
14
7
  import { SqliteSessionStore } from "../services/storage/sqlite-session-store";
15
8
  import { resolveCoreDistinctId } from "../services/telemetry/distinct-id";
16
9
  import { FileSessionService } from "../session/file-session-service";
17
- import { RpcCoreSessionService } from "../session/rpc-session-service";
18
10
  import { CoreSessionService } from "../session/session-service";
11
+ import { HubRuntimeHost } from "../transports/hub";
19
12
  import { LocalRuntimeHost } from "../transports/local";
20
- import { RpcRuntimeHost } from "../transports/rpc";
21
- import {
22
- ensureRpcRuntimeAddress,
23
- RPC_BUILD_ID_ENV,
24
- RPC_DISCOVERY_PATH_ENV,
25
- RPC_OWNER_ID_ENV,
26
- RPC_STARTUP_LOCK_BYPASS_ENV,
27
- type RpcOwnerContext,
28
- resolveRpcOwnerContext,
29
- } from "./rpc-runtime-ensure";
30
- import { tryAcquireRpcSpawnLease } from "./rpc-spawn-lease";
13
+ import { RemoteRuntimeHost } from "../transports/remote";
31
14
  import type { RuntimeHost, RuntimeHostMode } from "./runtime-host";
32
15
 
33
- const DEFAULT_RPC_ADDRESS =
34
- process.env.CLINE_RPC_ADDRESS?.trim() || getRpcServerDefaultAddress();
35
-
36
- function requiresLocalRuntimeHost(options: ClineCoreOptions): boolean {
37
- return !!options.defaultToolExecutors || !!options.teamToolsFactory;
38
- }
39
-
40
16
  function resolveConfiguredBackendMode(
41
17
  options: ClineCoreOptions,
42
18
  ): RuntimeHostMode {
@@ -47,151 +23,41 @@ function resolveConfiguredBackendMode(
47
23
  return "local";
48
24
  }
49
25
  const raw = process.env.CLINE_SESSION_BACKEND_MODE?.trim().toLowerCase();
50
- if (raw === "rpc" || raw === "local") {
26
+ if (raw === "local" || raw === "hub" || raw === "remote") {
51
27
  return raw;
52
28
  }
53
29
  return "auto";
54
30
  }
55
31
 
56
- export type SessionBackend =
57
- | RpcCoreSessionService
58
- | CoreSessionService
59
- | FileSessionService;
32
+ export type SessionBackend = CoreSessionService | FileSessionService;
60
33
 
61
34
  let cachedBackend: SessionBackend | undefined;
62
35
  let backendInitPromise: Promise<SessionBackend> | undefined;
63
36
 
64
- async function reconcileDeadSessionsIfSupported(
65
- backend: SessionBackend,
66
- ): Promise<void> {
67
- const service = backend as SessionBackend & {
68
- reconcileDeadSessions?: (limit?: number) => Promise<number>;
69
- };
70
- await service.reconcileDeadSessions?.().catch(() => {});
71
- }
72
-
73
- function openRpcSidecarLogFile(): { fd: number; logPath: string } | undefined {
74
- try {
75
- const logPath = join(resolveClineDataDir(), "logs", "rpc-sidecar.log");
76
- mkdirSync(dirname(logPath), { recursive: true });
77
- return { fd: openSync(logPath, "a"), logPath };
78
- } catch {
79
- return undefined;
80
- }
81
- }
82
-
83
- function startRpcServerInBackground(
84
- address: string,
85
- owner: RpcOwnerContext,
86
- logger?: ClineCoreOptions["logger"],
37
+ function prewarmLocalHubIfNeeded(
38
+ configuredMode: RuntimeHostMode,
39
+ options: ClineCoreOptions,
87
40
  ): void {
88
- const lease = tryAcquireRpcSpawnLease(address);
89
- if (!lease) {
90
- logger?.log("RPC sidecar spawn skipped", {
91
- address,
92
- reason: "spawn_lease_unavailable",
93
- severity: "warn",
94
- });
95
- return;
96
- }
97
- const launcher = process.execPath;
98
- const entryArg = process.argv[1]?.trim();
99
- if (!entryArg) {
100
- lease.release();
101
- logger?.error?.("RPC sidecar spawn aborted", {
102
- address,
103
- reason: "missing_process_entry_arg",
104
- });
41
+ if (configuredMode !== "auto" && configuredMode !== "hub") {
105
42
  return;
106
43
  }
107
- const entry = resolve(process.cwd(), entryArg);
108
- if (!existsSync(entry)) {
109
- lease.release();
110
- logger?.error?.("RPC sidecar spawn aborted", {
111
- address,
112
- reason: "entrypoint_missing",
113
- entryPath: entry,
114
- });
44
+ if (options.hub?.endpoint?.trim()) {
115
45
  return;
116
46
  }
117
- const conditionsArg = process.execArgv.find((arg) =>
118
- arg.startsWith("--conditions="),
47
+ prewarmDetachedHubServer(
48
+ options.hub?.workspaceRoot?.trim() ||
49
+ options.hub?.cwd?.trim() ||
50
+ process.cwd(),
119
51
  );
120
- const command = augmentNodeCommandForDebug(
121
- [
122
- launcher,
123
- ...(conditionsArg ? [conditionsArg] : []),
124
- entry,
125
- "rpc",
126
- "start",
127
- "--address",
128
- address,
129
- ],
130
- { debugRole: "rpc" },
131
- );
132
- const sidecarLog = openRpcSidecarLogFile();
133
- logger?.log("Launching detached RPC sidecar", {
134
- address,
135
- command: command.join(" "),
136
- commandArgs: command.slice(1),
137
- executable: command[0] ?? launcher,
138
- entryPath: entry,
139
- cwd: process.cwd(),
140
- logPath: sidecarLog?.logPath,
141
- });
142
- try {
143
- const child = spawn(command[0] ?? launcher, command.slice(1), {
144
- detached: true,
145
- stdio: sidecarLog ? ["ignore", sidecarLog.fd, sidecarLog.fd] : "ignore",
146
- env: {
147
- ...withResolvedClineBuildEnv(process.env),
148
- [RPC_STARTUP_LOCK_BYPASS_ENV]: "1",
149
- [RPC_OWNER_ID_ENV]: owner.ownerId,
150
- [RPC_BUILD_ID_ENV]: owner.buildId,
151
- [RPC_DISCOVERY_PATH_ENV]: owner.discoveryPath,
152
- CLINE_NO_INTERACTIVE: "1",
153
- },
154
- cwd: process.cwd(),
155
- });
156
- logger?.log("Detached RPC sidecar spawned", {
157
- address,
158
- childPid: child.pid,
159
- logPath: sidecarLog?.logPath,
160
- });
161
- child.unref();
162
- setTimeout(() => lease.release(), 10_000).unref();
163
- } catch (error) {
164
- lease.release();
165
- logger?.error?.("RPC sidecar spawn failed", {
166
- address,
167
- logPath: sidecarLog?.logPath,
168
- error,
169
- });
170
- throw error;
171
- } finally {
172
- if (sidecarLog) {
173
- closeSync(sidecarLog.fd);
174
- }
175
- }
176
52
  }
177
53
 
178
- async function tryConnectRpcBackend(
179
- address: string,
180
- options: ClineCoreOptions,
181
- ): Promise<RpcCoreSessionService | undefined> {
182
- try {
183
- const health = await getRpcServerHealth(address);
184
- if (!health) {
185
- return undefined;
186
- }
187
- return new RpcCoreSessionService({
188
- address,
189
- sessionsDir: resolveSessionDataDir(),
190
- messagesArtifactUploader: options.messagesArtifactUploader,
191
- });
192
- } catch {
193
- return undefined;
194
- }
54
+ async function reconcileDeadSessionsIfSupported(
55
+ backend: SessionBackend,
56
+ ): Promise<void> {
57
+ const service = backend as SessionBackend & {
58
+ reconcileDeadSessions?: (limit?: number) => Promise<number>;
59
+ };
60
+ await service.reconcileDeadSessions?.().catch(() => {});
195
61
  }
196
62
 
197
63
  function createLocalBackend(options: ClineCoreOptions): SessionBackend {
@@ -202,7 +68,7 @@ function createLocalBackend(options: ClineCoreOptions): SessionBackend {
202
68
  messagesArtifactUploader: options.messagesArtifactUploader,
203
69
  });
204
70
  } catch {
205
- // Fallback to file-based session service if SQLite is unavailable (e.g. due to missing native bindings on certain platforms)
71
+ // Fallback to file-based session service if SQLite is unavailable.
206
72
  options.telemetry?.capture({
207
73
  event: "session_backend_fallback",
208
74
  properties: {
@@ -216,6 +82,23 @@ function createLocalBackend(options: ClineCoreOptions): SessionBackend {
216
82
  }
217
83
  }
218
84
 
85
+ function createLocalRuntimeHost(
86
+ options: ClineCoreOptions,
87
+ distinctId: string,
88
+ backend?: SessionBackend,
89
+ ): LocalRuntimeHost {
90
+ return new LocalRuntimeHost({
91
+ sessionService:
92
+ backend ?? options.sessionService ?? createLocalBackend(options),
93
+ defaultToolExecutors: options.defaultToolExecutors,
94
+ telemetry: options.telemetry,
95
+ toolPolicies: options.toolPolicies,
96
+ requestToolApproval: options.requestToolApproval,
97
+ distinctId,
98
+ fetch: options.fetch,
99
+ });
100
+ }
101
+
219
102
  export async function resolveSessionBackend(
220
103
  options: ClineCoreOptions,
221
104
  ): Promise<SessionBackend> {
@@ -226,83 +109,7 @@ export async function resolveSessionBackend(
226
109
  return await backendInitPromise;
227
110
  }
228
111
 
229
- const mode = resolveConfiguredBackendMode(options);
230
- const requestedAddress = options.rpc?.address?.trim() || DEFAULT_RPC_ADDRESS;
231
- const attempts = Math.max(1, options.rpc?.connectAttempts ?? 5);
232
- const delayMs = Math.max(0, options.rpc?.connectDelayMs ?? 100);
233
- const autoStartRpc = options.rpc?.autoStart !== false;
234
- const logger = options.logger;
235
-
236
112
  backendInitPromise = (async () => {
237
- if (mode === "local") {
238
- cachedBackend = createLocalBackend(options);
239
- await reconcileDeadSessionsIfSupported(cachedBackend);
240
- return cachedBackend;
241
- }
242
-
243
- let address = requestedAddress;
244
- const existingRpcBackend = await tryConnectRpcBackend(address, options);
245
- if (existingRpcBackend) {
246
- logger?.log("Connected to existing RPC session backend", { address });
247
- cachedBackend = existingRpcBackend;
248
- await reconcileDeadSessionsIfSupported(cachedBackend);
249
- return cachedBackend;
250
- }
251
-
252
- if (mode === "rpc") {
253
- throw new Error(`RPC backend unavailable at ${address}`);
254
- }
255
-
256
- if (autoStartRpc) {
257
- try {
258
- logger?.log("Ensuring RPC runtime for auto session backend", {
259
- address,
260
- });
261
- const ensured = await ensureRpcRuntimeAddress(address, {
262
- resolveOwner: () => resolveRpcOwnerContext({ ownerPrefix: "core" }),
263
- spawnIfNeeded: (rpcAddress, owner) => {
264
- startRpcServerInBackground(rpcAddress, owner, logger);
265
- },
266
- });
267
- address = ensured.address;
268
- logger?.log("RPC runtime ensure completed", {
269
- requestedAddress,
270
- address,
271
- action: ensured.action,
272
- });
273
- } catch (error) {
274
- logger?.error?.("RPC backend auto-start failed", {
275
- address,
276
- requestedAddress,
277
- error,
278
- });
279
- }
280
-
281
- for (let attempt = 0; attempt < attempts; attempt += 1) {
282
- const rpcBackend = await tryConnectRpcBackend(address, options);
283
- if (rpcBackend) {
284
- logger?.log("Connected to ensured RPC session backend", {
285
- address,
286
- attempt: attempt + 1,
287
- attempts,
288
- });
289
- cachedBackend = rpcBackend;
290
- await reconcileDeadSessionsIfSupported(cachedBackend);
291
- return cachedBackend;
292
- }
293
- if (delayMs > 0) {
294
- await new Promise((resolve) => setTimeout(resolve, delayMs));
295
- }
296
- }
297
- }
298
-
299
- logger?.log("Falling back to local session backend", {
300
- requestedAddress,
301
- address,
302
- attempts,
303
- delayMs,
304
- severity: "warn",
305
- });
306
113
  cachedBackend = createLocalBackend(options);
307
114
  await reconcileDeadSessionsIfSupported(cachedBackend);
308
115
  return cachedBackend;
@@ -319,44 +126,97 @@ export async function createRuntimeHost(
319
126
  const distinctId = resolveCoreDistinctId(options.distinctId);
320
127
  options.telemetry?.setDistinctId(distinctId);
321
128
  const configuredMode = resolveConfiguredBackendMode(options);
322
- if (requiresLocalRuntimeHost(options)) {
323
- if (configuredMode === "rpc") {
129
+ prewarmLocalHubIfNeeded(configuredMode, options);
130
+ if (configuredMode === "remote") {
131
+ const remoteEndpoint = options.remote?.endpoint?.trim();
132
+ if (!remoteEndpoint) {
324
133
  throw new Error(
325
- "RPC runtime mode does not support local-only runtime host options such as custom tool executors or team tools factories.",
134
+ "Remote runtime mode requires `remote.endpoint` to be configured.",
326
135
  );
327
136
  }
328
- options.logger?.log("Using local runtime host due to local-only options", {
329
- configuredMode,
330
- hasDefaultToolExecutors: !!options.defaultToolExecutors,
331
- hasTeamToolsFactory: !!options.teamToolsFactory,
137
+ options.logger?.log("Using remote runtime host", {
138
+ endpoint: remoteEndpoint,
332
139
  });
333
- const backend = options.sessionService ?? createLocalBackend(options);
334
- return new LocalRuntimeHost({
335
- sessionService: backend,
336
- defaultToolExecutors: options.defaultToolExecutors,
337
- teamToolsFactory: options.teamToolsFactory,
338
- telemetry: options.telemetry,
339
- toolPolicies: options.toolPolicies,
140
+ return new RemoteRuntimeHost({
141
+ endpoint: remoteEndpoint,
142
+ authToken: options.remote?.authToken,
143
+ clientType: options.remote?.clientType,
144
+ displayName: options.remote?.displayName,
145
+ workspaceRoot: options.remote?.workspaceRoot,
146
+ cwd: options.remote?.cwd,
340
147
  requestToolApproval: options.requestToolApproval,
341
- distinctId,
342
148
  });
343
149
  }
344
- const backend =
345
- options.sessionService ?? (await resolveSessionBackend(options));
346
- if (backend instanceof RpcCoreSessionService) {
347
- return new RpcRuntimeHost(
348
- backend,
349
- options.toolPolicies,
350
- options.requestToolApproval,
150
+ if (configuredMode === "hub") {
151
+ const explicitEndpoint = options.hub?.endpoint?.trim();
152
+ const hubUrl =
153
+ explicitEndpoint ||
154
+ (await ensureCompatibleLocalHubUrl({
155
+ strategy: options.hub?.strategy ?? "require-hub",
156
+ workspaceRoot: options.hub?.workspaceRoot,
157
+ cwd: options.hub?.cwd,
158
+ }));
159
+ if (!hubUrl) {
160
+ throw new Error("No compatible hub runtime is available.");
161
+ }
162
+ options.logger?.log("Using hub runtime host", {
163
+ url: hubUrl,
164
+ explicitEndpoint: explicitEndpoint || undefined,
165
+ });
166
+ return new HubRuntimeHost(
167
+ {
168
+ url: hubUrl,
169
+ authToken: options.hub?.authToken,
170
+ clientType: options.hub?.clientType,
171
+ displayName: options.hub?.displayName,
172
+ requestToolApproval: options.requestToolApproval,
173
+ },
174
+ {
175
+ workspaceRoot: options.hub?.workspaceRoot,
176
+ cwd: options.hub?.cwd,
177
+ },
351
178
  );
352
179
  }
353
- return new LocalRuntimeHost({
354
- sessionService: backend,
355
- defaultToolExecutors: options.defaultToolExecutors,
356
- teamToolsFactory: options.teamToolsFactory,
357
- telemetry: options.telemetry,
358
- toolPolicies: options.toolPolicies,
359
- requestToolApproval: options.requestToolApproval,
360
- distinctId,
361
- });
180
+ if (configuredMode === "auto") {
181
+ const hubUrl = await resolveCompatibleLocalHubUrl({
182
+ endpoint: options.hub?.endpoint,
183
+ strategy: options.hub?.strategy ?? "prefer-hub",
184
+ workspaceRoot: options.hub?.workspaceRoot,
185
+ cwd: options.hub?.cwd,
186
+ });
187
+ if (hubUrl) {
188
+ options.logger?.log("Using discovered local hub runtime host", {
189
+ url: hubUrl,
190
+ });
191
+ const host = new HubRuntimeHost(
192
+ {
193
+ url: hubUrl,
194
+ authToken: options.hub?.authToken,
195
+ clientType: options.hub?.clientType,
196
+ displayName: options.hub?.displayName,
197
+ requestToolApproval: options.requestToolApproval,
198
+ },
199
+ {
200
+ workspaceRoot: options.hub?.workspaceRoot,
201
+ cwd: options.hub?.cwd,
202
+ },
203
+ );
204
+ try {
205
+ await host.connect();
206
+ return host;
207
+ } catch (error) {
208
+ options.logger?.log("Falling back to local runtime host", {
209
+ reason: "hub_connect_failed",
210
+ severity: "warn",
211
+ error,
212
+ });
213
+ }
214
+ }
215
+ options.logger?.log("Falling back to local runtime host", {
216
+ reason: "compatible_hub_unavailable",
217
+ severity: "warn",
218
+ });
219
+ return createLocalRuntimeHost(options, distinctId);
220
+ }
221
+ return createLocalRuntimeHost(options, distinctId);
362
222
  }
@@ -3,6 +3,7 @@ export {
3
3
  isRuleEnabled,
4
4
  listEnabledRulesFromWatcher,
5
5
  loadRulesForSystemPromptFromWatcher,
6
+ mergeRulesForSystemPrompt,
6
7
  } from "./rules";
7
8
  export { createTeamName, DefaultRuntimeBuilder } from "./runtime-builder";
8
9
  export type {
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Repeated tool-call loop detection.
3
+ *
4
+ * @see PLAN.md §3.1 — helpers moved from `packages/agents/src/context/loop-detection.ts`.
5
+ * @see PLAN.md §3.2.3 — public surface of `LoopDetectionTracker`.
6
+ *
7
+ * The pure helpers (`createLoopDetectionState`, `resetLoopDetectionState`,
8
+ * `toolCallSignature`, `checkRepeatedToolCall`) are ported verbatim. The
9
+ * `LoopDetectionTracker` class is a thin wrapper that owns a
10
+ * `LoopDetectionState` and exposes the `inspect()` / `reset()` surface that
11
+ * `SessionRuntime` installs as a `beforeTool` hook per §3.2.3.
12
+ */
13
+
14
+ import type { LoopDetectionConfig } from "@clinebot/shared";
15
+
16
+ // =============================================================================
17
+ // Pure helpers (verbatim port)
18
+ // =============================================================================
19
+
20
+ export interface LoopDetectionState {
21
+ lastToolName: string;
22
+ lastToolSignature: string;
23
+ consecutiveIdenticalCount: number;
24
+ }
25
+
26
+ export function createLoopDetectionState(): LoopDetectionState {
27
+ return {
28
+ lastToolName: "",
29
+ lastToolSignature: "",
30
+ consecutiveIdenticalCount: 0,
31
+ };
32
+ }
33
+
34
+ export function resetLoopDetectionState(state: LoopDetectionState): void {
35
+ state.lastToolName = "";
36
+ state.lastToolSignature = "";
37
+ state.consecutiveIdenticalCount = 0;
38
+ }
39
+
40
+ function sortKeys(value: unknown): unknown {
41
+ if (value == null || typeof value !== "object") return value;
42
+ if (Array.isArray(value)) return value.map(sortKeys);
43
+ const sorted: Record<string, unknown> = {};
44
+ for (const key of Object.keys(value as Record<string, unknown>).sort()) {
45
+ sorted[key] = sortKeys((value as Record<string, unknown>)[key]);
46
+ }
47
+ return sorted;
48
+ }
49
+
50
+ export function toolCallSignature(input: unknown): string {
51
+ if (input == null) return "null";
52
+ if (typeof input === "string") return input;
53
+ if (typeof input !== "object") return String(input);
54
+ try {
55
+ return JSON.stringify(sortKeys(input));
56
+ } catch {
57
+ return String(input);
58
+ }
59
+ }
60
+
61
+ export interface LoopCheckResult {
62
+ softWarning: boolean;
63
+ hardEscalation: boolean;
64
+ }
65
+
66
+ export function checkRepeatedToolCall(
67
+ state: LoopDetectionState,
68
+ toolName: string,
69
+ signature: string,
70
+ config: LoopDetectionConfig,
71
+ ): LoopCheckResult {
72
+ if (
73
+ toolName === state.lastToolName &&
74
+ signature === state.lastToolSignature
75
+ ) {
76
+ state.consecutiveIdenticalCount++;
77
+ } else {
78
+ state.consecutiveIdenticalCount = 1;
79
+ }
80
+ state.lastToolName = toolName;
81
+ state.lastToolSignature = signature;
82
+
83
+ return {
84
+ softWarning: state.consecutiveIdenticalCount === config.softThreshold,
85
+ hardEscalation: state.consecutiveIdenticalCount >= config.hardThreshold,
86
+ };
87
+ }
88
+
89
+ // =============================================================================
90
+ // Class wrapper (new — per PLAN.md §3.2.3)
91
+ // =============================================================================
92
+
93
+ /**
94
+ * Verdict returned by {@link LoopDetectionTracker.inspect}.
95
+ *
96
+ * - `"ok"` — no repeated call detected.
97
+ * - `"soft"` — soft-warning threshold reached; SessionRuntime may surface a
98
+ * recovery notice but should not block the call.
99
+ * - `"hard"` — hard-escalation threshold reached; SessionRuntime should
100
+ * stop the run with the provided `message`.
101
+ */
102
+ export interface LoopDetectionVerdict {
103
+ kind: "ok" | "soft" | "hard";
104
+ message?: string;
105
+ }
106
+
107
+ /** Minimal call shape the tracker needs; matches `AgentToolCallPart` subset. */
108
+ export interface LoopDetectionCall {
109
+ name: string;
110
+ input: unknown;
111
+ }
112
+
113
+ const DEFAULT_CONFIG: LoopDetectionConfig = {
114
+ softThreshold: 3,
115
+ hardThreshold: 5,
116
+ };
117
+
118
+ /**
119
+ * Per-session repeated-tool-call detector.
120
+ *
121
+ * `SessionRuntime` owns the instance and installs a `beforeTool` hook
122
+ * (see `AgentRuntimeHooks.beforeTool`) that calls `inspect()` to decide
123
+ * whether to return `{ skip, stop, reason }`.
124
+ */
125
+ export class LoopDetectionTracker {
126
+ private readonly config: LoopDetectionConfig;
127
+ private readonly state: LoopDetectionState = createLoopDetectionState();
128
+
129
+ constructor(config?: Partial<LoopDetectionConfig>) {
130
+ this.config = {
131
+ softThreshold: config?.softThreshold ?? DEFAULT_CONFIG.softThreshold,
132
+ hardThreshold: config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold,
133
+ };
134
+ }
135
+
136
+ inspect(call: LoopDetectionCall): LoopDetectionVerdict {
137
+ const signature = toolCallSignature(call.input);
138
+ const result = checkRepeatedToolCall(
139
+ this.state,
140
+ call.name,
141
+ signature,
142
+ this.config,
143
+ );
144
+ if (result.hardEscalation) {
145
+ return {
146
+ kind: "hard",
147
+ message: `Detected ${this.state.consecutiveIdenticalCount} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`,
148
+ };
149
+ }
150
+ if (result.softWarning) {
151
+ return {
152
+ kind: "soft",
153
+ message: `Detected ${this.state.consecutiveIdenticalCount} consecutive identical calls to \`${call.name}\`; consider trying a different approach.`,
154
+ };
155
+ }
156
+ return { kind: "ok" };
157
+ }
158
+
159
+ reset(): void {
160
+ resetLoopDetectionState(this.state);
161
+ }
162
+ }