@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,2559 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import type { AgentResult } from "@clinebot/shared";
6
- import { setClineDir, setHomeDir } from "@clinebot/shared/storage";
7
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
8
- import {
9
- type StartSessionInput,
10
- splitCoreSessionConfig,
11
- } from "../runtime/runtime-host";
12
- import { TelemetryService } from "../services/telemetry/TelemetryService";
13
- import type { SessionManifest } from "../session/session-manifest";
14
- import { SessionSource } from "../types/common";
15
- import type { CoreSessionConfig } from "../types/config";
16
- import { LocalRuntimeHost as RuntimeHostUnderTest } from "./local";
17
-
18
- const distinctId = "test-machine-id";
19
-
20
- function createResult(overrides: Partial<AgentResult> = {}): AgentResult {
21
- return {
22
- text: "ok",
23
- iterations: 1,
24
- finishReason: "completed",
25
- usage: {
26
- inputTokens: 1,
27
- outputTokens: 2,
28
- totalCost: 0,
29
- },
30
- messages: [],
31
- toolCalls: [],
32
- durationMs: 1,
33
- model: {
34
- id: "mock-model",
35
- provider: "mock-provider",
36
- },
37
- startedAt: new Date("2026-01-01T00:00:00.000Z"),
38
- endedAt: new Date("2026-01-01T00:00:01.000Z"),
39
- ...overrides,
40
- };
41
- }
42
-
43
- function createManifest(sessionId: string): SessionManifest {
44
- return {
45
- version: 1,
46
- session_id: sessionId,
47
- source: SessionSource.CLI,
48
- pid: process.pid,
49
- started_at: "2026-01-01T00:00:00.000Z",
50
- status: "running",
51
- interactive: false,
52
- provider: "mock-provider",
53
- model: "mock-model",
54
- cwd: "/tmp/project",
55
- workspace_root: "/tmp/project",
56
- enable_tools: true,
57
- enable_spawn: true,
58
- enable_teams: true,
59
- prompt: "hello",
60
- messages_path: "/tmp/messages.json",
61
- };
62
- }
63
-
64
- type PluginEventTestHarness = {
65
- handlePluginEvent: (
66
- rootSessionId: string,
67
- event: { name: string; payload?: unknown },
68
- ) => Promise<void>;
69
- getPendingPrompts: (
70
- sessionId: string,
71
- ) => Array<{ prompt: string; delivery: "queue" | "steer" }>;
72
- };
73
-
74
- function createPluginEventHarness(
75
- manager: RuntimeHostUnderTest,
76
- ): PluginEventTestHarness {
77
- const target = manager as object;
78
- return {
79
- handlePluginEvent: async (rootSessionId, event) => {
80
- const handler = Reflect.get(target, "handlePluginEvent");
81
- if (typeof handler !== "function") {
82
- throw new Error("handlePluginEvent test hook unavailable");
83
- }
84
- await Reflect.apply(
85
- handler as (
86
- rootSessionId: string,
87
- event: { name: string; payload?: unknown },
88
- ) => Promise<void>,
89
- target,
90
- [rootSessionId, event],
91
- );
92
- },
93
- getPendingPrompts: (sessionId) => {
94
- const getter = Reflect.get(target, "getSessionOrThrow");
95
- if (typeof getter !== "function") {
96
- throw new Error("getSessionOrThrow test hook unavailable");
97
- }
98
- const session = Reflect.apply(
99
- getter as (sessionId: string) => {
100
- pendingPrompts: Array<{
101
- id: string;
102
- prompt: string;
103
- delivery: "queue" | "steer";
104
- userFiles?: unknown;
105
- userImages?: unknown;
106
- }>;
107
- },
108
- target,
109
- [sessionId],
110
- );
111
- return session.pendingPrompts.map(({ prompt, delivery }) => ({
112
- prompt,
113
- delivery,
114
- }));
115
- },
116
- };
117
- }
118
-
119
- function createConfig(
120
- overrides: Partial<CoreSessionConfig> = {},
121
- ): CoreSessionConfig {
122
- return {
123
- providerId: "mock-provider",
124
- modelId: "mock-model",
125
- cwd: "/tmp/project",
126
- systemPrompt: "You are a test agent",
127
- mode: "act",
128
- enableTools: true,
129
- enableSpawnAgent: true,
130
- enableAgentTeams: true,
131
- ...overrides,
132
- };
133
- }
134
-
135
- function normalizeStartInput(
136
- input: Omit<StartSessionInput, "config" | "localRuntime"> & {
137
- config: CoreSessionConfig;
138
- },
139
- ): StartSessionInput {
140
- const split = splitCoreSessionConfig(input.config);
141
- return {
142
- ...input,
143
- ...split,
144
- };
145
- }
146
-
147
- function createGitRepo(cwd: string): void {
148
- execFileSync("git", ["-C", cwd, "init"], { stdio: "pipe" });
149
- execFileSync("git", ["-C", cwd, "config", "user.name", "Codex Test"], {
150
- stdio: "pipe",
151
- });
152
- execFileSync(
153
- "git",
154
- ["-C", cwd, "config", "user.email", "codex@example.com"],
155
- {
156
- stdio: "pipe",
157
- },
158
- );
159
- writeFileSync(join(cwd, "note.txt"), "base\n", "utf8");
160
- execFileSync("git", ["-C", cwd, "add", "note.txt"], { stdio: "pipe" });
161
- execFileSync("git", ["-C", cwd, "commit", "-m", "initial"], {
162
- stdio: "pipe",
163
- });
164
- }
165
-
166
- describe("LocalRuntimeHost", () => {
167
- const envSnapshot = {
168
- HOME: process.env.HOME,
169
- CLINE_DIR: process.env.CLINE_DIR,
170
- };
171
- let isolatedHomeDir = "";
172
-
173
- beforeEach(() => {
174
- isolatedHomeDir = mkdtempSync(join(tmpdir(), "core-session-home-"));
175
- process.env.HOME = isolatedHomeDir;
176
- process.env.CLINE_DIR = join(isolatedHomeDir, ".cline");
177
- setHomeDir(isolatedHomeDir);
178
- setClineDir(process.env.CLINE_DIR);
179
- });
180
-
181
- afterEach(() => {
182
- process.env.HOME = envSnapshot.HOME;
183
- process.env.CLINE_DIR = envSnapshot.CLINE_DIR;
184
- setHomeDir(envSnapshot.HOME ?? "~");
185
- setClineDir(envSnapshot.CLINE_DIR ?? join("~", ".cline"));
186
- rmSync(isolatedHomeDir, { recursive: true, force: true });
187
- });
188
-
189
- it("emits session lifecycle telemetry when configured", async () => {
190
- const sessionId = "sess-telemetry";
191
- const manifest = createManifest(sessionId);
192
- const adapter = {
193
- name: "test",
194
- emit: vi.fn(),
195
- emitRequired: vi.fn(),
196
- recordCounter: vi.fn(),
197
- recordHistogram: vi.fn(),
198
- recordGauge: vi.fn(),
199
- isEnabled: vi.fn(() => true),
200
- flush: vi.fn().mockResolvedValue(undefined),
201
- dispose: vi.fn().mockResolvedValue(undefined),
202
- };
203
- const telemetry = new TelemetryService({
204
- adapters: [adapter],
205
- distinctId: distinctId,
206
- });
207
- const sessionService = {
208
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
209
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
210
- manifestPath: "/tmp/manifest.json",
211
- messagesPath: "/tmp/messages.json",
212
- manifest,
213
- }),
214
- persistSessionMessages: vi.fn(),
215
- updateSessionStatus: vi.fn().mockResolvedValue({
216
- updated: true,
217
- endedAt: "2026-01-01T00:00:05.000Z",
218
- }),
219
- writeSessionManifest: vi.fn(),
220
- listSessions: vi.fn().mockResolvedValue([]),
221
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
222
- };
223
- const runtimeBuilder = {
224
- build: vi.fn().mockReturnValue({
225
- tools: [],
226
- teamRuntime: {
227
- getTeamId: vi.fn().mockReturnValue("team_test-team"),
228
- getTeamName: vi.fn().mockReturnValue("test-team"),
229
- },
230
- teamRestoredFromPersistence: false,
231
- shutdown: vi.fn(),
232
- }),
233
- };
234
- const agent = {
235
- run: vi.fn().mockResolvedValue(createResult()),
236
- continue: vi.fn().mockResolvedValue(createResult()),
237
- getMessages: vi.fn().mockReturnValue([]),
238
- getAgentId: vi.fn().mockReturnValue("agent-root-1"),
239
- getConversationId: vi.fn().mockReturnValue("conv-root-1"),
240
- abort: vi.fn(),
241
- shutdown: vi.fn().mockResolvedValue(undefined),
242
- };
243
- const manager = new RuntimeHostUnderTest({
244
- distinctId,
245
- sessionService: sessionService as never,
246
- runtimeBuilder: runtimeBuilder as never,
247
- createAgent: () => agent as never,
248
- telemetry,
249
- });
250
-
251
- await manager.start(
252
- normalizeStartInput({
253
- config: createConfig({ telemetry, sessionId }),
254
- prompt: "hello",
255
- }),
256
- );
257
-
258
- expect(adapter.emit).toHaveBeenCalledWith(
259
- "session.started",
260
- expect.objectContaining({
261
- sessionId,
262
- agentId: "agent-root-1",
263
- agentKind: "team_lead",
264
- conversationId: "conv-root-1",
265
- teamRole: "lead",
266
- distinct_id: distinctId,
267
- }),
268
- );
269
- expect(adapter.emit).toHaveBeenCalledWith(
270
- "task.agent_created",
271
- expect.objectContaining({
272
- ulid: sessionId,
273
- agentId: "agent-root-1",
274
- agentKind: "team_lead",
275
- conversationId: "conv-root-1",
276
- teamRole: "lead",
277
- distinct_id: distinctId,
278
- }),
279
- );
280
- expect(adapter.emit).toHaveBeenCalledWith(
281
- "task.agent_team_created",
282
- expect.objectContaining({
283
- ulid: sessionId,
284
- leadAgentId: "agent-root-1",
285
- restoredFromPersistence: false,
286
- distinct_id: distinctId,
287
- }),
288
- );
289
- });
290
-
291
- it("persists custom session sources without coercing them to builtin values", async () => {
292
- const sessionId = "sess-kanban";
293
- const manifest = {
294
- ...createManifest(sessionId),
295
- source: "kanban",
296
- };
297
- const sessionService = {
298
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
299
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
300
- manifestPath: "/tmp/manifest.json",
301
- messagesPath: "/tmp/messages.json",
302
- manifest,
303
- }),
304
- persistSessionMessages: vi.fn(),
305
- updateSessionStatus: vi.fn().mockResolvedValue({
306
- updated: true,
307
- endedAt: "2026-01-01T00:00:05.000Z",
308
- }),
309
- writeSessionManifest: vi.fn(),
310
- listSessions: vi.fn().mockResolvedValue([]),
311
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
312
- };
313
- const runtimeBuilder = {
314
- build: vi.fn().mockReturnValue({
315
- tools: [],
316
- teamRuntime: undefined,
317
- teamRestoredFromPersistence: false,
318
- shutdown: vi.fn(),
319
- }),
320
- };
321
- const agent = {
322
- run: vi.fn().mockResolvedValue(createResult()),
323
- continue: vi.fn().mockResolvedValue(createResult()),
324
- getMessages: vi.fn().mockReturnValue([]),
325
- getAgentId: vi.fn().mockReturnValue("agent-root-1"),
326
- getConversationId: vi.fn().mockReturnValue("conv-root-1"),
327
- abort: vi.fn(),
328
- shutdown: vi.fn().mockResolvedValue(undefined),
329
- };
330
- const manager = new RuntimeHostUnderTest({
331
- distinctId,
332
- sessionService: sessionService as never,
333
- runtimeBuilder: runtimeBuilder as never,
334
- createAgent: () => agent as never,
335
- });
336
-
337
- const started = await manager.start(
338
- normalizeStartInput({
339
- source: "kanban",
340
- config: createConfig({ sessionId }),
341
- prompt: "hello",
342
- }),
343
- );
344
-
345
- expect(sessionService.createRootSessionWithArtifacts).toHaveBeenCalledWith(
346
- expect.objectContaining({
347
- sessionId,
348
- source: "kanban",
349
- }),
350
- );
351
- expect(started.manifest.source).toBe("kanban");
352
- });
353
-
354
- it("reuses the persisted team name when resuming a session", async () => {
355
- const sessionId = "sess-team-resume";
356
- const manifest = createManifest(sessionId);
357
- const sessionService = {
358
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
359
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
360
- manifestPath: "/tmp/manifest.json",
361
- messagesPath: "/tmp/messages.json",
362
- manifest,
363
- }),
364
- persistSessionMessages: vi.fn(),
365
- updateSessionStatus: vi.fn().mockResolvedValue({
366
- updated: true,
367
- endedAt: "2026-01-01T00:00:05.000Z",
368
- }),
369
- writeSessionManifest: vi.fn(),
370
- listSessions: vi.fn().mockResolvedValue([
371
- {
372
- sessionId,
373
- source: SessionSource.CLI,
374
- pid: process.pid,
375
- startedAt: "2026-01-01T00:00:00.000Z",
376
- endedAt: null,
377
- exitCode: null,
378
- status: "running",
379
- statusLock: 0,
380
- interactive: true,
381
- provider: "mock-provider",
382
- model: "mock-model",
383
- cwd: "/tmp/project",
384
- workspaceRoot: "/tmp/project",
385
- teamName: "persisted-team",
386
- enableTools: true,
387
- enableSpawn: true,
388
- enableTeams: true,
389
- parentSessionId: null,
390
- parentAgentId: null,
391
- agentId: null,
392
- conversationId: null,
393
- isSubagent: false,
394
- prompt: null,
395
- metadata: null,
396
- messagesPath: "/tmp/messages.json",
397
- updatedAt: "2026-01-01T00:00:00.000Z",
398
- },
399
- ]),
400
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
401
- };
402
- const runtimeBuilder = {
403
- build: vi.fn().mockReturnValue({
404
- tools: [],
405
- teamRuntime: {
406
- getTeamId: vi.fn().mockReturnValue("team_persisted-team"),
407
- getTeamName: vi.fn().mockReturnValue("persisted-team"),
408
- },
409
- teamRestoredFromPersistence: true,
410
- shutdown: vi.fn(),
411
- }),
412
- };
413
- const agent = {
414
- run: vi.fn().mockResolvedValue(createResult()),
415
- continue: vi.fn().mockResolvedValue(createResult()),
416
- getMessages: vi.fn().mockReturnValue([]),
417
- getAgentId: vi.fn().mockReturnValue("agent-root-1"),
418
- getConversationId: vi.fn().mockReturnValue("conv-root-1"),
419
- abort: vi.fn(),
420
- shutdown: vi.fn().mockResolvedValue(undefined),
421
- };
422
- const manager = new RuntimeHostUnderTest({
423
- distinctId,
424
- sessionService: sessionService as never,
425
- runtimeBuilder: runtimeBuilder as never,
426
- createAgent: () => agent as never,
427
- });
428
-
429
- await manager.start(
430
- normalizeStartInput({
431
- config: createConfig({ sessionId, teamName: undefined }),
432
- }),
433
- );
434
-
435
- expect(runtimeBuilder.build).toHaveBeenCalledWith(
436
- expect.objectContaining({
437
- config: expect.objectContaining({
438
- sessionId,
439
- teamName: "persisted-team",
440
- }),
441
- }),
442
- );
443
- expect(
444
- sessionService.createRootSessionWithArtifacts,
445
- ).not.toHaveBeenCalled();
446
- });
447
-
448
- it("runs a non-interactive prompt and persists messages/status", async () => {
449
- const sessionId = "sess-1";
450
- const manifest = createManifest(sessionId);
451
- const createRootSessionWithArtifacts = vi.fn().mockResolvedValue({
452
- manifestPath: "/tmp/manifest.json",
453
- messagesPath: "/tmp/messages.json",
454
- manifest,
455
- });
456
- const persistSessionMessages = vi.fn();
457
- const updateSessionStatus = vi.fn().mockResolvedValue({
458
- updated: true,
459
- endedAt: "2026-01-01T00:00:05.000Z",
460
- });
461
- const writeSessionManifest = vi.fn();
462
- const listSessions = vi.fn().mockResolvedValue([]);
463
- const deleteSession = vi.fn().mockResolvedValue({ deleted: true });
464
- const sessionService = {
465
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
466
- createRootSessionWithArtifacts,
467
- persistSessionMessages,
468
- updateSessionStatus,
469
- writeSessionManifest,
470
- listSessions,
471
- deleteSession,
472
- };
473
-
474
- const shutdown = vi.fn();
475
- const runtimeBuilder = {
476
- build: vi.fn().mockReturnValue({
477
- tools: [],
478
- shutdown,
479
- }),
480
- };
481
- const run = vi.fn().mockResolvedValue(
482
- createResult({
483
- messages: [
484
- { role: "user", content: [{ type: "text", text: "hello" }] },
485
- ],
486
- }),
487
- );
488
- const continueFn = vi.fn();
489
- const agent = {
490
- run,
491
- continue: continueFn,
492
- abort: vi.fn(),
493
- shutdown: vi.fn().mockResolvedValue(undefined),
494
- getMessages: vi.fn().mockReturnValue([]),
495
- messages: [],
496
- };
497
-
498
- const manager = new RuntimeHostUnderTest({
499
- distinctId,
500
- sessionService: sessionService as never,
501
- runtimeBuilder,
502
- createAgent: () => agent as never,
503
- });
504
-
505
- const started = await manager.start(
506
- normalizeStartInput({
507
- config: createConfig({ sessionId }),
508
- prompt: "hello",
509
- interactive: false,
510
- }),
511
- );
512
-
513
- expect(started.sessionId).toBe(sessionId);
514
- expect(started.result?.finishReason).toBe("completed");
515
- expect(run).toHaveBeenCalledTimes(1);
516
- expect(continueFn).not.toHaveBeenCalled();
517
- expect(persistSessionMessages).toHaveBeenCalledTimes(1);
518
- expect(updateSessionStatus).toHaveBeenCalledWith(sessionId, "completed", 0);
519
- expect(writeSessionManifest).toHaveBeenCalledTimes(1);
520
- expect(shutdown).toHaveBeenCalledTimes(1);
521
- });
522
-
523
- it("preserves manifest metadata updates and persists total cost", async () => {
524
- const sessionId = "sess-history-meta";
525
- let storedManifest: SessionManifest = {
526
- ...createManifest(sessionId),
527
- metadata: {
528
- checkpoint: {
529
- latest: {
530
- ref: "abc123",
531
- createdAt: 1,
532
- runCount: 1,
533
- },
534
- history: [
535
- {
536
- ref: "abc123",
537
- createdAt: 1,
538
- runCount: 1,
539
- },
540
- ],
541
- },
542
- },
543
- };
544
- const createRootSessionWithArtifacts = vi.fn().mockResolvedValue({
545
- manifestPath: "/tmp/manifest-history-meta.json",
546
- messagesPath: "/tmp/messages-history-meta.json",
547
- manifest: { ...storedManifest },
548
- });
549
- const persistSessionMessages = vi.fn();
550
- const updateSession = vi.fn().mockImplementation(async (input) => {
551
- storedManifest = {
552
- ...storedManifest,
553
- metadata: input.metadata,
554
- };
555
- return { updated: true };
556
- });
557
- const updateSessionStatus = vi.fn().mockResolvedValue({
558
- updated: true,
559
- endedAt: "2026-01-01T00:00:05.000Z",
560
- });
561
- const readSessionManifest = vi
562
- .fn()
563
- .mockImplementation(() => storedManifest);
564
- const writeSessionManifest = vi
565
- .fn()
566
- .mockImplementation((_path, manifest) => {
567
- storedManifest = manifest;
568
- });
569
- const sessionService = {
570
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
571
- createRootSessionWithArtifacts,
572
- persistSessionMessages,
573
- updateSession,
574
- updateSessionStatus,
575
- readSessionManifest,
576
- writeSessionManifest,
577
- listSessions: vi.fn().mockResolvedValue([]),
578
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
579
- };
580
-
581
- const runtimeBuilder = {
582
- build: vi.fn().mockReturnValue({
583
- tools: [],
584
- shutdown: vi.fn(),
585
- }),
586
- };
587
- const agent = {
588
- run: vi.fn().mockResolvedValue(
589
- createResult({
590
- usage: {
591
- inputTokens: 3,
592
- outputTokens: 4,
593
- totalCost: 0.42,
594
- },
595
- messages: [
596
- { role: "user", content: [{ type: "text", text: "hello" }] },
597
- ],
598
- }),
599
- ),
600
- continue: vi.fn(),
601
- abort: vi.fn(),
602
- shutdown: vi.fn().mockResolvedValue(undefined),
603
- getMessages: vi.fn().mockReturnValue([]),
604
- messages: [],
605
- };
606
-
607
- const manager = new RuntimeHostUnderTest({
608
- distinctId,
609
- sessionService: sessionService as never,
610
- runtimeBuilder,
611
- createAgent: () => agent as never,
612
- });
613
-
614
- await manager.start(
615
- normalizeStartInput({
616
- config: createConfig({ sessionId }),
617
- prompt: "hello",
618
- interactive: false,
619
- }),
620
- );
621
-
622
- expect(updateSession).toHaveBeenCalledWith({
623
- sessionId,
624
- metadata: {
625
- checkpoint: {
626
- latest: {
627
- ref: "abc123",
628
- createdAt: 1,
629
- runCount: 1,
630
- },
631
- history: [
632
- {
633
- ref: "abc123",
634
- createdAt: 1,
635
- runCount: 1,
636
- },
637
- ],
638
- },
639
- totalCost: 0.42,
640
- },
641
- });
642
- expect(writeSessionManifest).toHaveBeenCalledWith(
643
- "/tmp/manifest-history-meta.json",
644
- expect.objectContaining({
645
- metadata: {
646
- checkpoint: {
647
- latest: {
648
- ref: "abc123",
649
- createdAt: 1,
650
- runCount: 1,
651
- },
652
- history: [
653
- {
654
- ref: "abc123",
655
- createdAt: 1,
656
- runCount: 1,
657
- },
658
- ],
659
- },
660
- totalCost: 0.42,
661
- },
662
- status: "completed",
663
- }),
664
- );
665
- });
666
-
667
- it("does not install checkpoint hooks when checkpoint.enabled is not set in config", async () => {
668
- const sessionId = "sess-checkpoint-default-off";
669
- const manifest = createManifest(sessionId);
670
- const updateSession = vi.fn().mockResolvedValue({ updated: true });
671
- const sessionService = {
672
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
673
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
674
- manifestPath: "/tmp/manifest-checkpoint-default-off.json",
675
- messagesPath: "/tmp/messages-checkpoint-default-off.json",
676
- manifest,
677
- }),
678
- persistSessionMessages: vi.fn(),
679
- updateSession,
680
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
681
- writeSessionManifest: vi.fn(),
682
- listSessions: vi.fn().mockResolvedValue([
683
- {
684
- sessionId,
685
- provider: "mock-provider",
686
- model: "mock-model",
687
- cwd: "/tmp/project",
688
- workspaceRoot: "/tmp/project",
689
- createdAt: "2026-01-01T00:00:00.000Z",
690
- updatedAt: "2026-01-01T00:00:00.000Z",
691
- status: "running",
692
- metadata: undefined,
693
- },
694
- ]),
695
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
696
- };
697
- const runtimeBuilder = {
698
- build: vi.fn().mockImplementation(() => {
699
- return {
700
- tools: [],
701
- shutdown: vi.fn(),
702
- };
703
- }),
704
- };
705
- const manager = new RuntimeHostUnderTest({
706
- distinctId,
707
- sessionService: sessionService as never,
708
- runtimeBuilder,
709
- createAgent: (config) =>
710
- ({
711
- run: vi.fn().mockImplementation(async () => {
712
- await config.hooks?.onRunStart?.({
713
- agentId: "agent_1",
714
- conversationId: "conv_1",
715
- parentAgentId: null,
716
- userMessage: "hello",
717
- });
718
- await config.hooks?.onBeforeAgentStart?.({
719
- agentId: "agent_1",
720
- conversationId: "conv_1",
721
- parentAgentId: null,
722
- iteration: 1,
723
- systemPrompt: "system",
724
- messages: [],
725
- });
726
- return createResult();
727
- }),
728
- continue: vi.fn(),
729
- abort: vi.fn(),
730
- shutdown: vi.fn().mockResolvedValue(undefined),
731
- getMessages: vi.fn().mockReturnValue([]),
732
- messages: [],
733
- }) as never,
734
- });
735
-
736
- await manager.start(
737
- normalizeStartInput({
738
- config: createConfig({ sessionId }),
739
- prompt: "hello",
740
- interactive: false,
741
- }),
742
- );
743
- expect(updateSession).toHaveBeenCalledTimes(1);
744
- expect(updateSession).toHaveBeenLastCalledWith({
745
- sessionId,
746
- metadata: {
747
- totalCost: 0,
748
- },
749
- });
750
- });
751
-
752
- it("installs checkpoint hooks when checkpoint.enabled=true in config", async () => {
753
- const sessionId = "sess-checkpoint-config-on";
754
- const repoCwd = mkdtempSync(join(isolatedHomeDir, "checkpoint-repo-"));
755
- createGitRepo(repoCwd);
756
- const manifest = createManifest(sessionId);
757
- const updateSession = vi.fn().mockResolvedValue({ updated: true });
758
- const sessionService = {
759
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
760
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
761
- manifestPath: "/tmp/manifest-checkpoint-env-on.json",
762
- messagesPath: "/tmp/messages-checkpoint-env-on.json",
763
- manifest,
764
- }),
765
- persistSessionMessages: vi.fn(),
766
- updateSession,
767
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
768
- writeSessionManifest: vi.fn(),
769
- listSessions: vi.fn().mockResolvedValue([
770
- {
771
- sessionId,
772
- provider: "mock-provider",
773
- model: "mock-model",
774
- cwd: repoCwd,
775
- workspaceRoot: repoCwd,
776
- createdAt: "2026-01-01T00:00:00.000Z",
777
- updatedAt: "2026-01-01T00:00:00.000Z",
778
- status: "running",
779
- metadata: undefined,
780
- },
781
- ]),
782
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
783
- };
784
- const runtimeBuilder = {
785
- build: vi.fn().mockImplementation(() => {
786
- return {
787
- tools: [],
788
- shutdown: vi.fn(),
789
- };
790
- }),
791
- };
792
- const manager = new RuntimeHostUnderTest({
793
- distinctId,
794
- sessionService: sessionService as never,
795
- runtimeBuilder,
796
- createAgent: (config) =>
797
- ({
798
- run: vi.fn().mockImplementation(async () => {
799
- await config.hooks?.onRunStart?.({
800
- agentId: "agent_1",
801
- conversationId: "conv_1",
802
- parentAgentId: null,
803
- userMessage: "hello",
804
- });
805
- await config.hooks?.onBeforeAgentStart?.({
806
- agentId: "agent_1",
807
- conversationId: "conv_1",
808
- parentAgentId: null,
809
- iteration: 1,
810
- systemPrompt: "system",
811
- messages: [],
812
- });
813
- return createResult();
814
- }),
815
- continue: vi.fn(),
816
- abort: vi.fn(),
817
- shutdown: vi.fn().mockResolvedValue(undefined),
818
- getMessages: vi.fn().mockReturnValue([]),
819
- messages: [],
820
- }) as never,
821
- });
822
-
823
- await manager.start(
824
- normalizeStartInput({
825
- config: {
826
- ...createConfig({ sessionId, cwd: repoCwd }),
827
- checkpoint: { enabled: true },
828
- },
829
- prompt: "hello",
830
- interactive: false,
831
- }),
832
- );
833
- expect(updateSession).toHaveBeenCalledTimes(2);
834
- expect(updateSession).toHaveBeenNthCalledWith(1, {
835
- sessionId,
836
- metadata: expect.objectContaining({
837
- checkpoint: expect.objectContaining({
838
- latest: expect.objectContaining({
839
- ref: expect.stringMatching(/^[0-9a-f]{40}$/),
840
- runCount: 1,
841
- }),
842
- }),
843
- }),
844
- });
845
- expect(updateSession).toHaveBeenNthCalledWith(2, {
846
- sessionId,
847
- metadata: expect.objectContaining({
848
- totalCost: 0,
849
- }),
850
- });
851
- });
852
-
853
- it("persists assistant message metadata for usage and model identity", async () => {
854
- const sessionId = "sess-meta";
855
- const manifest = createManifest(sessionId);
856
- const persistSessionMessages = vi.fn();
857
- const sessionService = {
858
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
859
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
860
- manifestPath: "/tmp/manifest-meta.json",
861
- messagesPath: "/tmp/messages-meta.json",
862
- manifest,
863
- }),
864
- persistSessionMessages,
865
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
866
- writeSessionManifest: vi.fn(),
867
- listSessions: vi.fn().mockResolvedValue([]),
868
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
869
- };
870
- const updateConnectionDefaults = vi.fn();
871
- const runtimeBuilder = {
872
- build: vi.fn().mockReturnValue({
873
- tools: [],
874
- delegatedAgentConfigProvider: {
875
- getRuntimeConfig: vi.fn(),
876
- getConnectionConfig: vi.fn(),
877
- updateConnectionDefaults,
878
- },
879
- shutdown: vi.fn(),
880
- }),
881
- };
882
- const run = vi.fn().mockResolvedValue(
883
- createResult({
884
- usage: {
885
- inputTokens: 33,
886
- outputTokens: 12,
887
- cacheReadTokens: 4,
888
- cacheWriteTokens: 1,
889
- totalCost: 0.42,
890
- },
891
- model: {
892
- id: "claude-sonnet-4-6",
893
- provider: "anthropic",
894
- info: {
895
- id: "claude-sonnet-4-6",
896
- family: "claude-sonnet-4",
897
- },
898
- },
899
- endedAt: new Date("2026-01-01T00:00:02.000Z"),
900
- messages: [
901
- { role: "user", content: [{ type: "text", text: "hello" }] },
902
- { role: "assistant", content: [{ type: "text", text: "world" }] },
903
- ],
904
- }),
905
- );
906
- const manager = new RuntimeHostUnderTest({
907
- distinctId,
908
- sessionService: sessionService as never,
909
- runtimeBuilder,
910
- createAgent: () =>
911
- ({
912
- run,
913
- continue: vi.fn(),
914
- abort: vi.fn(),
915
- shutdown: vi.fn().mockResolvedValue(undefined),
916
- getMessages: vi.fn().mockReturnValue([]),
917
- messages: [],
918
- }) as never,
919
- });
920
-
921
- await manager.start(
922
- normalizeStartInput({
923
- config: createConfig({
924
- sessionId,
925
- providerId: "anthropic",
926
- modelId: "claude-sonnet-4-6",
927
- }),
928
- prompt: "hello",
929
- interactive: false,
930
- }),
931
- );
932
-
933
- expect(persistSessionMessages).toHaveBeenCalledTimes(1);
934
- const persisted = persistSessionMessages.mock.calls[0]?.[1];
935
- expect(Array.isArray(persisted)).toBe(true);
936
- expect(persisted?.[1]).toMatchObject({
937
- role: "assistant",
938
- modelInfo: {
939
- id: "claude-sonnet-4-6",
940
- provider: "anthropic",
941
- family: "claude-sonnet-4",
942
- },
943
- metrics: {
944
- inputTokens: 33,
945
- outputTokens: 12,
946
- cacheReadTokens: 4,
947
- cacheWriteTokens: 1,
948
- cost: 0.42,
949
- },
950
- ts: new Date("2026-01-01T00:00:02.000Z").getTime(),
951
- });
952
- });
953
-
954
- it("queues sandbox steer messages back into the active session", async () => {
955
- const sessionId = "sess-steer";
956
- const manifest = createManifest(sessionId);
957
- const sessionService = {
958
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
959
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
960
- manifestPath: "/tmp/manifest.json",
961
- messagesPath: "/tmp/messages.json",
962
- manifest,
963
- }),
964
- persistSessionMessages: vi.fn(),
965
- updateSessionStatus: vi.fn().mockResolvedValue({
966
- updated: true,
967
- endedAt: "2026-01-01T00:00:05.000Z",
968
- }),
969
- writeSessionManifest: vi.fn(),
970
- listSessions: vi.fn().mockResolvedValue([]),
971
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
972
- };
973
- const updateConnectionDefaults = vi.fn();
974
- const runtimeBuilder = {
975
- build: vi.fn().mockReturnValue({
976
- tools: [],
977
- delegatedAgentConfigProvider: {
978
- getRuntimeConfig: vi.fn(),
979
- getConnectionConfig: vi.fn(),
980
- updateConnectionDefaults,
981
- },
982
- shutdown: vi.fn(),
983
- }),
984
- };
985
- const run = vi.fn().mockResolvedValue(
986
- createResult({
987
- messages: [
988
- { role: "user", content: [{ type: "text", text: "hello" }] },
989
- ],
990
- }),
991
- );
992
- const continueFn = vi.fn().mockResolvedValue(
993
- createResult({
994
- text: "steered",
995
- messages: [
996
- { role: "user", content: [{ type: "text", text: "hello" }] },
997
- {
998
- role: "assistant",
999
- content: [{ type: "text", text: "steered" }],
1000
- },
1001
- ],
1002
- }),
1003
- );
1004
- const agent = {
1005
- run,
1006
- continue: continueFn,
1007
- abort: vi.fn(),
1008
- shutdown: vi.fn().mockResolvedValue(undefined),
1009
- getMessages: vi
1010
- .fn()
1011
- .mockReturnValue([
1012
- { role: "user", content: [{ type: "text", text: "hello" }] },
1013
- ]),
1014
- canStartRun: vi.fn().mockReturnValue(true),
1015
- };
1016
-
1017
- const manager = new RuntimeHostUnderTest({
1018
- distinctId,
1019
- sessionService: sessionService as never,
1020
- runtimeBuilder,
1021
- createAgent: () => agent as never,
1022
- });
1023
-
1024
- await manager.start(
1025
- normalizeStartInput({
1026
- config: createConfig({ sessionId }),
1027
- prompt: "hello",
1028
- interactive: true,
1029
- }),
1030
- );
1031
-
1032
- const harness = createPluginEventHarness(manager);
1033
- await harness.handlePluginEvent(sessionId, {
1034
- name: "steer_message",
1035
- payload: { prompt: "async result" },
1036
- });
1037
- await vi.waitFor(() => {
1038
- expect(continueFn).toHaveBeenCalledTimes(2);
1039
- });
1040
- expect(continueFn).toHaveBeenLastCalledWith(
1041
- '<user_input mode="act">async result</user_input>',
1042
- undefined,
1043
- undefined,
1044
- );
1045
- });
1046
-
1047
- it("promotes queued prompts to the front when they become steer", async () => {
1048
- const sessionId = "sess-steer-priority";
1049
- const manifest = createManifest(sessionId);
1050
- const sessionService = {
1051
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1052
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1053
- manifestPath: "/tmp/manifest.json",
1054
- messagesPath: "/tmp/messages.json",
1055
- manifest,
1056
- }),
1057
- persistSessionMessages: vi.fn(),
1058
- updateSessionStatus: vi.fn().mockResolvedValue({
1059
- updated: true,
1060
- endedAt: "2026-01-01T00:00:05.000Z",
1061
- }),
1062
- writeSessionManifest: vi.fn(),
1063
- listSessions: vi.fn().mockResolvedValue([]),
1064
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1065
- };
1066
- const updateConnectionDefaults = vi.fn();
1067
- const runtimeBuilder = {
1068
- build: vi.fn().mockReturnValue({
1069
- tools: [],
1070
- delegatedAgentConfigProvider: {
1071
- getRuntimeConfig: vi.fn(),
1072
- getConnectionConfig: vi.fn(),
1073
- updateConnectionDefaults,
1074
- },
1075
- shutdown: vi.fn(),
1076
- }),
1077
- };
1078
- const agent = {
1079
- run: vi.fn().mockResolvedValue(createResult()),
1080
- continue: vi.fn().mockResolvedValue(createResult()),
1081
- abort: vi.fn(),
1082
- shutdown: vi.fn().mockResolvedValue(undefined),
1083
- getMessages: vi.fn().mockReturnValue([]),
1084
- canStartRun: vi.fn().mockReturnValue(false),
1085
- };
1086
-
1087
- const manager = new RuntimeHostUnderTest({
1088
- distinctId,
1089
- sessionService: sessionService as never,
1090
- runtimeBuilder,
1091
- createAgent: () => agent as never,
1092
- });
1093
-
1094
- await manager.start(
1095
- normalizeStartInput({
1096
- config: createConfig({ sessionId }),
1097
- prompt: "hello",
1098
- interactive: true,
1099
- }),
1100
- );
1101
-
1102
- const harness = createPluginEventHarness(manager);
1103
-
1104
- await harness.handlePluginEvent(sessionId, {
1105
- name: "queue_message",
1106
- payload: { prompt: "queued first" },
1107
- });
1108
- await harness.handlePluginEvent(sessionId, {
1109
- name: "queue_message",
1110
- payload: { prompt: "queued second" },
1111
- });
1112
- await harness.handlePluginEvent(sessionId, {
1113
- name: "steer_message",
1114
- payload: { prompt: "queued first" },
1115
- });
1116
-
1117
- expect(harness.getPendingPrompts(sessionId)).toEqual([
1118
- { prompt: "queued first", delivery: "steer" },
1119
- { prompt: "queued second", delivery: "queue" },
1120
- ]);
1121
- });
1122
-
1123
- it("drops and ignores queued prompts once a session is aborting", async () => {
1124
- const sessionId = "sess-abort-pending";
1125
- const manifest = createManifest(sessionId);
1126
- const sessionService = {
1127
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1128
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1129
- manifestPath: "/tmp/manifest.json",
1130
- messagesPath: "/tmp/messages.json",
1131
- manifest,
1132
- }),
1133
- persistSessionMessages: vi.fn(),
1134
- updateSessionStatus: vi.fn().mockResolvedValue({
1135
- updated: true,
1136
- endedAt: "2026-01-01T00:00:05.000Z",
1137
- }),
1138
- writeSessionManifest: vi.fn(),
1139
- listSessions: vi.fn().mockResolvedValue([]),
1140
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1141
- };
1142
- const runtimeBuilder = {
1143
- build: vi.fn().mockReturnValue({
1144
- tools: [],
1145
- shutdown: vi.fn(),
1146
- }),
1147
- };
1148
- const agent = {
1149
- run: vi.fn().mockResolvedValue(createResult()),
1150
- continue: vi.fn().mockResolvedValue(createResult()),
1151
- abort: vi.fn(),
1152
- shutdown: vi.fn().mockResolvedValue(undefined),
1153
- getMessages: vi.fn().mockReturnValue([]),
1154
- canStartRun: vi.fn().mockReturnValue(false),
1155
- };
1156
-
1157
- const manager = new RuntimeHostUnderTest({
1158
- distinctId,
1159
- sessionService: sessionService as never,
1160
- runtimeBuilder,
1161
- createAgent: () => agent as never,
1162
- });
1163
-
1164
- await manager.start(
1165
- normalizeStartInput({
1166
- config: createConfig({ sessionId }),
1167
- prompt: "hello",
1168
- interactive: true,
1169
- }),
1170
- );
1171
-
1172
- const harness = createPluginEventHarness(manager);
1173
- await harness.handlePluginEvent(sessionId, {
1174
- name: "queue_message",
1175
- payload: { prompt: "queued before abort" },
1176
- });
1177
- expect(harness.getPendingPrompts(sessionId)).toEqual([
1178
- { prompt: "queued before abort", delivery: "queue" },
1179
- ]);
1180
-
1181
- await manager.abort(sessionId, new Error("test abort"));
1182
- expect(agent.abort).toHaveBeenCalledTimes(1);
1183
- expect(harness.getPendingPrompts(sessionId)).toEqual([]);
1184
-
1185
- await harness.handlePluginEvent(sessionId, {
1186
- name: "queue_message",
1187
- payload: { prompt: "queued after abort" },
1188
- });
1189
- expect(harness.getPendingPrompts(sessionId)).toEqual([]);
1190
- });
1191
-
1192
- it("preserves per-turn metadata on prior assistant messages across turns", async () => {
1193
- const sessionId = "sess-meta-multi";
1194
- const manifest = createManifest(sessionId);
1195
- const persistSessionMessages = vi.fn();
1196
- const runtimeBuilder = {
1197
- build: vi.fn().mockReturnValue({
1198
- tools: [],
1199
- shutdown: vi.fn(),
1200
- }),
1201
- };
1202
- const firstTurnMessages = [
1203
- {
1204
- role: "user" as const,
1205
- content: [{ type: "text" as const, text: "hello" }],
1206
- },
1207
- {
1208
- role: "assistant" as const,
1209
- content: [{ type: "text" as const, text: "world" }],
1210
- },
1211
- ];
1212
- const secondTurnMessages = [
1213
- ...firstTurnMessages,
1214
- {
1215
- role: "user" as const,
1216
- content: [{ type: "text" as const, text: "again" }],
1217
- },
1218
- {
1219
- role: "assistant" as const,
1220
- content: [{ type: "text" as const, text: "still here" }],
1221
- },
1222
- ];
1223
- const run = vi.fn().mockResolvedValue(
1224
- createResult({
1225
- usage: {
1226
- inputTokens: 33,
1227
- outputTokens: 12,
1228
- cacheReadTokens: 4,
1229
- cacheWriteTokens: 1,
1230
- totalCost: 0.42,
1231
- },
1232
- model: {
1233
- id: "claude-sonnet-4-6",
1234
- provider: "anthropic",
1235
- },
1236
- endedAt: new Date("2026-01-01T00:00:02.000Z"),
1237
- messages: firstTurnMessages,
1238
- }),
1239
- );
1240
- const continueFn = vi.fn().mockResolvedValue(
1241
- createResult({
1242
- usage: {
1243
- inputTokens: 10,
1244
- outputTokens: 5,
1245
- cacheReadTokens: 2,
1246
- cacheWriteTokens: 0,
1247
- totalCost: 0.12,
1248
- },
1249
- model: {
1250
- id: "claude-sonnet-4-6",
1251
- provider: "anthropic",
1252
- },
1253
- endedAt: new Date("2026-01-01T00:00:03.000Z"),
1254
- messages: secondTurnMessages,
1255
- }),
1256
- );
1257
- const agent = {
1258
- run,
1259
- continue: continueFn,
1260
- abort: vi.fn(),
1261
- shutdown: vi.fn().mockResolvedValue(undefined),
1262
- restore: vi.fn(),
1263
- getMessages: vi.fn().mockReturnValue([]),
1264
- messages: [],
1265
- };
1266
- const sessionService = {
1267
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1268
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1269
- manifestPath: "/tmp/manifest-meta-multi.json",
1270
- messagesPath: "/tmp/messages-meta-multi.json",
1271
- manifest,
1272
- }),
1273
- persistSessionMessages,
1274
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1275
- writeSessionManifest: vi.fn(),
1276
- listSessions: vi.fn().mockResolvedValue([]),
1277
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1278
- };
1279
- const manager = new RuntimeHostUnderTest({
1280
- distinctId,
1281
- sessionService: sessionService as never,
1282
- runtimeBuilder,
1283
- createAgent: () => agent as never,
1284
- });
1285
-
1286
- await manager.start(
1287
- normalizeStartInput({
1288
- config: createConfig({
1289
- sessionId,
1290
- providerId: "anthropic",
1291
- modelId: "claude-sonnet-4-6",
1292
- }),
1293
- interactive: true,
1294
- }),
1295
- );
1296
-
1297
- await manager.send({ sessionId, prompt: "hello" });
1298
- await manager.send({ sessionId, prompt: "again" });
1299
-
1300
- const persisted = persistSessionMessages.mock.calls[1]?.[1];
1301
- expect(persisted?.[1]).toMatchObject({
1302
- role: "assistant",
1303
- metrics: {
1304
- inputTokens: 33,
1305
- outputTokens: 12,
1306
- cacheReadTokens: 4,
1307
- cacheWriteTokens: 1,
1308
- cost: 0.42,
1309
- },
1310
- });
1311
- expect(persisted?.[3]).toMatchObject({
1312
- role: "assistant",
1313
- metrics: {
1314
- inputTokens: 10,
1315
- outputTokens: 5,
1316
- cacheReadTokens: 2,
1317
- cacheWriteTokens: 0,
1318
- cost: 0.12,
1319
- },
1320
- });
1321
- });
1322
-
1323
- it("persists rendered messages when a turn fails", async () => {
1324
- const sessionId = "sess-failed-turn";
1325
- const manifest = createManifest(sessionId);
1326
- const persistSessionMessages = vi.fn();
1327
- const sessionService = {
1328
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1329
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1330
- manifestPath: "/tmp/manifest-failed-turn.json",
1331
- messagesPath: "/tmp/messages-failed-turn.json",
1332
- manifest,
1333
- }),
1334
- persistSessionMessages,
1335
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1336
- writeSessionManifest: vi.fn(),
1337
- listSessions: vi.fn().mockResolvedValue([]),
1338
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1339
- };
1340
- const runtimeBuilder = {
1341
- build: vi.fn().mockReturnValue({
1342
- tools: [],
1343
- shutdown: vi.fn(),
1344
- }),
1345
- };
1346
- const renderedMessages = [
1347
- { role: "user", content: [{ type: "text", text: "hello" }] },
1348
- { role: "assistant", content: [{ type: "text", text: "partial" }] },
1349
- ];
1350
- const manager = new RuntimeHostUnderTest({
1351
- distinctId,
1352
- sessionService: sessionService as never,
1353
- runtimeBuilder,
1354
- createAgent: () =>
1355
- ({
1356
- run: vi.fn().mockRejectedValue(new Error("boom")),
1357
- continue: vi.fn(),
1358
- abort: vi.fn(),
1359
- restore: vi.fn(),
1360
- shutdown: vi.fn().mockResolvedValue(undefined),
1361
- getMessages: vi
1362
- .fn()
1363
- .mockReturnValueOnce([])
1364
- .mockReturnValue(renderedMessages),
1365
- messages: [],
1366
- }) as never,
1367
- });
1368
-
1369
- await expect(
1370
- manager.start(
1371
- normalizeStartInput({
1372
- config: createConfig({ sessionId }),
1373
- prompt: "hello",
1374
- interactive: false,
1375
- }),
1376
- ),
1377
- ).rejects.toThrow("boom");
1378
-
1379
- expect(persistSessionMessages).toHaveBeenCalledTimes(1);
1380
- expect(persistSessionMessages).toHaveBeenCalledWith(
1381
- sessionId,
1382
- renderedMessages,
1383
- "You are a test agent",
1384
- );
1385
- expect(sessionService.updateSessionStatus).toHaveBeenCalledWith(
1386
- sessionId,
1387
- "failed",
1388
- 1,
1389
- );
1390
- });
1391
-
1392
- it("uses run for first send then continue for subsequent sends", async () => {
1393
- const sessionId = "sess-2";
1394
- const manifest = createManifest(sessionId);
1395
- const sessionService = {
1396
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1397
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1398
- manifestPath: "/tmp/manifest-2.json",
1399
- messagesPath: "/tmp/messages-2.json",
1400
- manifest,
1401
- }),
1402
- persistSessionMessages: vi.fn(),
1403
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1404
- writeSessionManifest: vi.fn(),
1405
- listSessions: vi.fn().mockResolvedValue([]),
1406
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1407
- };
1408
- const runtimeBuilder = {
1409
- build: vi.fn().mockReturnValue({
1410
- tools: [],
1411
- shutdown: vi.fn(),
1412
- }),
1413
- };
1414
- const run = vi.fn().mockResolvedValue(createResult({ text: "first" }));
1415
- const continueFn = vi
1416
- .fn()
1417
- .mockResolvedValue(createResult({ text: "second" }));
1418
- const manager = new RuntimeHostUnderTest({
1419
- distinctId,
1420
- sessionService: sessionService as never,
1421
- runtimeBuilder,
1422
- createAgent: () =>
1423
- ({
1424
- run,
1425
- continue: continueFn,
1426
- abort: vi.fn(),
1427
- shutdown: vi.fn().mockResolvedValue(undefined),
1428
- getMessages: vi.fn().mockReturnValue([]),
1429
- messages: [],
1430
- }) as never,
1431
- });
1432
-
1433
- await manager.start(
1434
- normalizeStartInput({
1435
- config: createConfig({ sessionId }),
1436
- interactive: true,
1437
- }),
1438
- );
1439
- const first = await manager.send({ sessionId, prompt: "first" });
1440
- const second = await manager.send({ sessionId, prompt: "second" });
1441
-
1442
- expect(first?.text).toBe("first");
1443
- expect(second?.text).toBe("second");
1444
- expect(run).toHaveBeenCalledTimes(1);
1445
- expect(continueFn).toHaveBeenCalledTimes(1);
1446
- expect(sessionService.persistSessionMessages).toHaveBeenCalledTimes(2);
1447
- });
1448
-
1449
- it("tracks accumulated usage per session across turns", async () => {
1450
- const sessionId = "sess-usage";
1451
- const manifest = createManifest(sessionId);
1452
- const sessionService = {
1453
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1454
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1455
- manifestPath: "/tmp/manifest-usage.json",
1456
- messagesPath: "/tmp/messages-usage.json",
1457
- manifest,
1458
- }),
1459
- persistSessionMessages: vi.fn(),
1460
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1461
- writeSessionManifest: vi.fn(),
1462
- listSessions: vi.fn().mockResolvedValue([]),
1463
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1464
- };
1465
- const runtimeBuilder = {
1466
- build: vi.fn().mockReturnValue({
1467
- tools: [],
1468
- shutdown: vi.fn(),
1469
- }),
1470
- };
1471
- const run = vi.fn().mockResolvedValue(
1472
- createResult({
1473
- text: "first",
1474
- usage: {
1475
- inputTokens: 10,
1476
- outputTokens: 3,
1477
- cacheReadTokens: 1,
1478
- cacheWriteTokens: 2,
1479
- totalCost: 0.11,
1480
- },
1481
- }),
1482
- );
1483
- const continueFn = vi.fn().mockResolvedValue(
1484
- createResult({
1485
- text: "second",
1486
- usage: {
1487
- inputTokens: 8,
1488
- outputTokens: 4,
1489
- cacheReadTokens: 2,
1490
- cacheWriteTokens: 0,
1491
- totalCost: 0.09,
1492
- },
1493
- }),
1494
- );
1495
- const manager = new RuntimeHostUnderTest({
1496
- distinctId,
1497
- sessionService: sessionService as never,
1498
- runtimeBuilder,
1499
- createAgent: () =>
1500
- ({
1501
- run,
1502
- continue: continueFn,
1503
- abort: vi.fn(),
1504
- shutdown: vi.fn().mockResolvedValue(undefined),
1505
- getMessages: vi.fn().mockReturnValue([]),
1506
- messages: [],
1507
- }) as never,
1508
- });
1509
-
1510
- await manager.start(
1511
- normalizeStartInput({
1512
- config: createConfig({ sessionId }),
1513
- interactive: true,
1514
- }),
1515
- );
1516
-
1517
- await manager.send({ sessionId, prompt: "first" });
1518
- expect(await manager.getAccumulatedUsage(sessionId)).toEqual({
1519
- inputTokens: 10,
1520
- outputTokens: 3,
1521
- cacheReadTokens: 1,
1522
- cacheWriteTokens: 2,
1523
- totalCost: 0.11,
1524
- });
1525
-
1526
- await manager.send({ sessionId, prompt: "second" });
1527
- expect(await manager.getAccumulatedUsage(sessionId)).toEqual({
1528
- inputTokens: 18,
1529
- outputTokens: 7,
1530
- cacheReadTokens: 3,
1531
- cacheWriteTokens: 2,
1532
- totalCost: 0.2,
1533
- });
1534
- });
1535
-
1536
- it("queues sends with explicit queue or steer delivery and emits snapshots", async () => {
1537
- const sessionId = "sess-delivery-queue";
1538
- const manifest = createManifest(sessionId);
1539
- const sessionService = {
1540
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1541
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1542
- manifestPath: "/tmp/manifest-queue.json",
1543
- messagesPath: "/tmp/messages-queue.json",
1544
- manifest,
1545
- }),
1546
- persistSessionMessages: vi.fn(),
1547
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1548
- writeSessionManifest: vi.fn(),
1549
- listSessions: vi.fn().mockResolvedValue([]),
1550
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1551
- };
1552
- const runtimeBuilder = {
1553
- build: vi.fn().mockReturnValue({
1554
- tools: [],
1555
- shutdown: vi.fn(),
1556
- }),
1557
- };
1558
- let canStartRun = false;
1559
- const run = vi.fn().mockResolvedValue(createResult({ text: "first" }));
1560
- const continueFn = vi
1561
- .fn()
1562
- .mockResolvedValue(createResult({ text: "next" }));
1563
- const manager = new RuntimeHostUnderTest({
1564
- distinctId,
1565
- sessionService: sessionService as never,
1566
- runtimeBuilder,
1567
- createAgent: () =>
1568
- ({
1569
- run,
1570
- continue: continueFn,
1571
- canStartRun: vi.fn(() => canStartRun),
1572
- abort: vi.fn(),
1573
- shutdown: vi.fn().mockResolvedValue(undefined),
1574
- getMessages: vi.fn().mockReturnValue([]),
1575
- messages: [],
1576
- }) as never,
1577
- });
1578
- const events: Array<unknown> = [];
1579
- manager.subscribe((event) => {
1580
- events.push(event);
1581
- });
1582
-
1583
- await manager.start(
1584
- normalizeStartInput({
1585
- config: createConfig({ sessionId }),
1586
- interactive: true,
1587
- }),
1588
- );
1589
-
1590
- await expect(
1591
- manager.send({ sessionId, prompt: "queued first", delivery: "queue" }),
1592
- ).resolves.toBeUndefined();
1593
- await expect(
1594
- manager.send({ sessionId, prompt: "queued second", delivery: "steer" }),
1595
- ).resolves.toBeUndefined();
1596
-
1597
- expect(run).not.toHaveBeenCalled();
1598
- expect(continueFn).not.toHaveBeenCalled();
1599
- const promptSnapshots = events
1600
- .filter((event) => {
1601
- return (
1602
- typeof event === "object" &&
1603
- event !== null &&
1604
- "type" in event &&
1605
- event.type === "pending_prompts"
1606
- );
1607
- })
1608
- .map((event) => (event as { payload: { prompts: unknown[] } }).payload);
1609
- expect(promptSnapshots.at(-1)).toEqual({
1610
- prompts: [
1611
- expect.objectContaining({
1612
- prompt: "queued second",
1613
- delivery: "steer",
1614
- attachmentCount: 0,
1615
- }),
1616
- expect.objectContaining({
1617
- prompt: "queued first",
1618
- delivery: "queue",
1619
- attachmentCount: 0,
1620
- }),
1621
- ],
1622
- sessionId,
1623
- });
1624
-
1625
- canStartRun = true;
1626
- await manager.send({ sessionId, prompt: "run now" });
1627
- expect(run).toHaveBeenCalledTimes(1);
1628
- expect(
1629
- events.some((event) => {
1630
- return (
1631
- typeof event === "object" &&
1632
- event !== null &&
1633
- "type" in event &&
1634
- event.type === "pending_prompt_submitted" &&
1635
- "payload" in event &&
1636
- (event.payload as { prompt?: string }).prompt === "queued second"
1637
- );
1638
- }),
1639
- ).toBe(true);
1640
- });
1641
-
1642
- it("returns undefined accumulated usage for unknown sessions", async () => {
1643
- const manager = new RuntimeHostUnderTest({
1644
- distinctId,
1645
- sessionService: {
1646
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1647
- listSessions: vi.fn().mockResolvedValue([]),
1648
- deleteSession: vi.fn().mockResolvedValue({ deleted: false }),
1649
- } as never,
1650
- runtimeBuilder: {
1651
- build: vi.fn().mockReturnValue({
1652
- tools: [],
1653
- shutdown: vi.fn(),
1654
- }),
1655
- },
1656
- createAgent: () =>
1657
- ({
1658
- run: vi.fn(),
1659
- continue: vi.fn(),
1660
- abort: vi.fn(),
1661
- shutdown: vi.fn().mockResolvedValue(undefined),
1662
- getMessages: vi.fn().mockReturnValue([]),
1663
- messages: [],
1664
- }) as never,
1665
- });
1666
-
1667
- expect(
1668
- await manager.getAccumulatedUsage("missing-session"),
1669
- ).toBeUndefined();
1670
- });
1671
-
1672
- it("marks a failed single-run session as failed when run throws", async () => {
1673
- const sessionId = "sess-fail";
1674
- const manifest = createManifest(sessionId);
1675
- const sessionService = {
1676
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1677
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1678
- manifestPath: "/tmp/manifest-fail.json",
1679
- messagesPath: "/tmp/messages-fail.json",
1680
- manifest,
1681
- }),
1682
- persistSessionMessages: vi.fn(),
1683
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1684
- writeSessionManifest: vi.fn(),
1685
- listSessions: vi.fn().mockResolvedValue([]),
1686
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1687
- };
1688
- const runtimeShutdown = vi.fn();
1689
- const runtimeBuilder = {
1690
- build: vi.fn().mockReturnValue({
1691
- tools: [],
1692
- shutdown: runtimeShutdown,
1693
- }),
1694
- };
1695
- const run = vi.fn().mockRejectedValue(new Error("run failed"));
1696
- const agentShutdown = vi.fn().mockResolvedValue(undefined);
1697
- const manager = new RuntimeHostUnderTest({
1698
- distinctId,
1699
- sessionService: sessionService as never,
1700
- runtimeBuilder,
1701
- createAgent: () =>
1702
- ({
1703
- run,
1704
- continue: vi.fn(),
1705
- abort: vi.fn(),
1706
- shutdown: agentShutdown,
1707
- getMessages: vi.fn().mockReturnValue([]),
1708
- messages: [],
1709
- }) as never,
1710
- });
1711
-
1712
- await expect(
1713
- manager.start(
1714
- normalizeStartInput({
1715
- config: createConfig({ sessionId }),
1716
- prompt: "hello",
1717
- interactive: false,
1718
- }),
1719
- ),
1720
- ).rejects.toThrow("run failed");
1721
- expect(sessionService.updateSessionStatus).toHaveBeenCalledWith(
1722
- sessionId,
1723
- "failed",
1724
- 1,
1725
- );
1726
- expect(agentShutdown).toHaveBeenCalledTimes(1);
1727
- expect(runtimeShutdown).toHaveBeenCalledTimes(1);
1728
- });
1729
-
1730
- it("does not persist or emit shutdown hooks when no prompt was submitted", async () => {
1731
- const sessionService = {
1732
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1733
- createRootSessionWithArtifacts: vi.fn(),
1734
- persistSessionMessages: vi.fn(),
1735
- updateSessionStatus: vi.fn(),
1736
- writeSessionManifest: vi.fn(),
1737
- listSessions: vi.fn().mockResolvedValue([]),
1738
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1739
- };
1740
- const runtimeShutdown = vi.fn();
1741
- const runtimeBuilder = {
1742
- build: vi.fn().mockReturnValue({
1743
- tools: [],
1744
- shutdown: runtimeShutdown,
1745
- }),
1746
- };
1747
- const agentShutdown = vi.fn().mockResolvedValue(undefined);
1748
- const manager = new RuntimeHostUnderTest({
1749
- distinctId,
1750
- sessionService: sessionService as never,
1751
- runtimeBuilder,
1752
- createAgent: () =>
1753
- ({
1754
- run: vi.fn(),
1755
- continue: vi.fn(),
1756
- abort: vi.fn(),
1757
- shutdown: agentShutdown,
1758
- getMessages: vi.fn().mockReturnValue([]),
1759
- messages: [],
1760
- }) as never,
1761
- });
1762
-
1763
- const started = await manager.start(
1764
- normalizeStartInput({
1765
- config: createConfig({ sessionId: "sess-no-prompt" }),
1766
- interactive: true,
1767
- }),
1768
- );
1769
- await manager.stop(started.sessionId);
1770
-
1771
- expect(
1772
- sessionService.createRootSessionWithArtifacts,
1773
- ).not.toHaveBeenCalled();
1774
- expect(sessionService.updateSessionStatus).not.toHaveBeenCalled();
1775
- expect(agentShutdown).not.toHaveBeenCalled();
1776
- expect(runtimeShutdown).toHaveBeenCalledTimes(1);
1777
- });
1778
-
1779
- it("updates agent connection with refreshed OAuth key before turn", async () => {
1780
- const sessionId = "sess-oauth";
1781
- const manifest = createManifest(sessionId);
1782
- const sessionService = {
1783
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1784
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1785
- manifestPath: "/tmp/manifest-oauth.json",
1786
- messagesPath: "/tmp/messages-oauth.json",
1787
- manifest,
1788
- }),
1789
- persistSessionMessages: vi.fn(),
1790
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1791
- writeSessionManifest: vi.fn(),
1792
- listSessions: vi.fn().mockResolvedValue([]),
1793
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1794
- };
1795
- const updateConnectionDefaults = vi.fn();
1796
- const runtimeBuilder = {
1797
- build: vi.fn().mockReturnValue({
1798
- tools: [],
1799
- delegatedAgentConfigProvider: {
1800
- getRuntimeConfig: vi.fn(),
1801
- getConnectionConfig: vi.fn(),
1802
- updateConnectionDefaults,
1803
- },
1804
- shutdown: vi.fn(),
1805
- }),
1806
- };
1807
- const run = vi.fn().mockResolvedValue(createResult({ text: "ok" }));
1808
- const updateConnection = vi.fn();
1809
- const manager = new RuntimeHostUnderTest({
1810
- distinctId,
1811
- sessionService: sessionService as never,
1812
- runtimeBuilder,
1813
- oauthTokenManager: {
1814
- resolveProviderApiKey: vi.fn().mockResolvedValue({
1815
- providerId: "openai-codex",
1816
- apiKey: "oauth-access-new",
1817
- refreshed: true,
1818
- }),
1819
- } as never,
1820
- createAgent: () =>
1821
- ({
1822
- run,
1823
- continue: vi.fn(),
1824
- abort: vi.fn(),
1825
- restore: vi.fn(),
1826
- updateConnection,
1827
- shutdown: vi.fn().mockResolvedValue(undefined),
1828
- getMessages: vi.fn().mockReturnValue([]),
1829
- messages: [],
1830
- }) as never,
1831
- });
1832
-
1833
- await manager.start(
1834
- normalizeStartInput({
1835
- config: createConfig({
1836
- sessionId,
1837
- providerId: "openai-codex",
1838
- apiKey: "oauth-access-old",
1839
- }),
1840
- interactive: true,
1841
- }),
1842
- );
1843
- await manager.send({ sessionId, prompt: "hello" });
1844
-
1845
- expect(updateConnectionDefaults).toHaveBeenCalledWith({
1846
- apiKey: "oauth-access-new",
1847
- });
1848
- expect(updateConnection).toHaveBeenCalledWith({
1849
- apiKey: "oauth-access-new",
1850
- });
1851
- expect(run).toHaveBeenCalledTimes(1);
1852
- });
1853
-
1854
- it("hydrates provider-specific config from provider settings", async () => {
1855
- const sessionId = "sess-provider-config";
1856
- const manifest = createManifest(sessionId);
1857
- const sessionService = {
1858
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1859
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1860
- manifestPath: "/tmp/manifest-provider-config.json",
1861
- messagesPath: "/tmp/messages-provider-config.json",
1862
- manifest,
1863
- }),
1864
- persistSessionMessages: vi.fn(),
1865
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1866
- writeSessionManifest: vi.fn(),
1867
- listSessions: vi.fn().mockResolvedValue([]),
1868
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1869
- };
1870
- const run = vi.fn().mockResolvedValue(
1871
- createResult({
1872
- model: {
1873
- id: "claude-sonnet-4@20250514",
1874
- provider: "vertex",
1875
- },
1876
- }),
1877
- );
1878
- const createAgent = vi.fn().mockReturnValue({
1879
- run,
1880
- continue: vi.fn(),
1881
- abort: vi.fn(),
1882
- restore: vi.fn(),
1883
- shutdown: vi.fn().mockResolvedValue(undefined),
1884
- getMessages: vi.fn().mockReturnValue([]),
1885
- messages: [],
1886
- });
1887
- const manager = new RuntimeHostUnderTest({
1888
- distinctId,
1889
- sessionService: sessionService as never,
1890
- runtimeBuilder: {
1891
- build: vi.fn().mockReturnValue({
1892
- tools: [],
1893
- shutdown: vi.fn(),
1894
- }),
1895
- },
1896
- createAgent: createAgent as never,
1897
- providerSettingsManager: {
1898
- getProviderSettings: vi.fn().mockReturnValue({
1899
- provider: "vertex",
1900
- gcp: {
1901
- projectId: "test-project",
1902
- region: "us-central1",
1903
- },
1904
- }),
1905
- } as never,
1906
- });
1907
-
1908
- await manager.start(
1909
- normalizeStartInput({
1910
- config: createConfig({
1911
- sessionId,
1912
- providerId: "vertex",
1913
- modelId: "claude-sonnet-4@20250514",
1914
- }),
1915
- interactive: true,
1916
- }),
1917
- );
1918
- await manager.send({ sessionId, prompt: "hello" });
1919
-
1920
- expect(createAgent).toHaveBeenCalledWith(
1921
- expect.objectContaining({
1922
- providerId: "vertex",
1923
- modelId: "claude-sonnet-4@20250514",
1924
- providerConfig: expect.objectContaining({
1925
- providerId: "vertex",
1926
- modelId: "claude-sonnet-4@20250514",
1927
- gcp: {
1928
- projectId: "test-project",
1929
- region: "us-central1",
1930
- },
1931
- }),
1932
- }),
1933
- );
1934
- });
1935
-
1936
- it("forwards loopDetection config to the agent constructor", async () => {
1937
- const sessionId = "sess-loop-detection";
1938
- const manifest = createManifest(sessionId);
1939
- const sessionService = {
1940
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
1941
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
1942
- manifestPath: "/tmp/manifest-loop.json",
1943
- messagesPath: "/tmp/messages-loop.json",
1944
- manifest,
1945
- }),
1946
- persistSessionMessages: vi.fn(),
1947
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
1948
- writeSessionManifest: vi.fn(),
1949
- listSessions: vi.fn().mockResolvedValue([]),
1950
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
1951
- };
1952
- const run = vi.fn().mockResolvedValue(createResult());
1953
- const createAgent = vi.fn().mockReturnValue({
1954
- run,
1955
- continue: vi.fn(),
1956
- abort: vi.fn(),
1957
- restore: vi.fn(),
1958
- shutdown: vi.fn().mockResolvedValue(undefined),
1959
- getMessages: vi.fn().mockReturnValue([]),
1960
- messages: [],
1961
- });
1962
- const manager = new RuntimeHostUnderTest({
1963
- distinctId,
1964
- sessionService: sessionService as never,
1965
- runtimeBuilder: {
1966
- build: vi.fn().mockReturnValue({
1967
- tools: [],
1968
- shutdown: vi.fn(),
1969
- }),
1970
- },
1971
- createAgent: createAgent as never,
1972
- });
1973
-
1974
- await manager.start(
1975
- normalizeStartInput({
1976
- config: createConfig({
1977
- sessionId,
1978
- execution: {
1979
- loopDetection: { softThreshold: 4, hardThreshold: 8 },
1980
- },
1981
- }),
1982
- interactive: true,
1983
- }),
1984
- );
1985
- await manager.send({ sessionId, prompt: "test" });
1986
-
1987
- expect(createAgent).toHaveBeenCalledWith(
1988
- expect.objectContaining({
1989
- execution: {
1990
- loopDetection: { softThreshold: 4, hardThreshold: 8 },
1991
- },
1992
- }),
1993
- );
1994
- });
1995
-
1996
- it("injects a core-owned compaction prepareTurn callback into the agent constructor", async () => {
1997
- const sessionId = "sess-compaction";
1998
- const manifest = createManifest(sessionId);
1999
- const sessionService = {
2000
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2001
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2002
- manifestPath: "/tmp/manifest-compaction.json",
2003
- messagesPath: "/tmp/messages-compaction.json",
2004
- manifest,
2005
- }),
2006
- persistSessionMessages: vi.fn(),
2007
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2008
- writeSessionManifest: vi.fn(),
2009
- listSessions: vi.fn().mockResolvedValue([]),
2010
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2011
- };
2012
- const run = vi.fn().mockResolvedValue(createResult());
2013
- const createAgent = vi.fn().mockReturnValue({
2014
- run,
2015
- continue: vi.fn(),
2016
- abort: vi.fn(),
2017
- restore: vi.fn(),
2018
- shutdown: vi.fn().mockResolvedValue(undefined),
2019
- getMessages: vi.fn().mockReturnValue([]),
2020
- messages: [],
2021
- });
2022
- const compact = vi.fn();
2023
- const manager = new RuntimeHostUnderTest({
2024
- distinctId,
2025
- sessionService: sessionService as never,
2026
- runtimeBuilder: {
2027
- build: vi.fn().mockReturnValue({
2028
- tools: [],
2029
- shutdown: vi.fn(),
2030
- }),
2031
- },
2032
- createAgent: createAgent as never,
2033
- });
2034
-
2035
- await manager.start(
2036
- normalizeStartInput({
2037
- config: createConfig({
2038
- sessionId,
2039
- compaction: {
2040
- enabled: true,
2041
- strategy: "basic",
2042
- compact,
2043
- },
2044
- }),
2045
- interactive: true,
2046
- }),
2047
- );
2048
- await manager.send({ sessionId, prompt: "test" });
2049
-
2050
- expect(createAgent).toHaveBeenCalledWith(
2051
- expect.objectContaining({
2052
- prepareTurn: expect.any(Function),
2053
- }),
2054
- );
2055
- });
2056
-
2057
- it("formats prompt in core and merges explicit + mention user files", async () => {
2058
- const tempCwd = mkdtempSync(join(tmpdir(), "core-session-format-"));
2059
- try {
2060
- const srcDir = join(tempCwd, "src");
2061
- const docsDir = join(tempCwd, "docs");
2062
- mkdirSync(srcDir, { recursive: true });
2063
- mkdirSync(docsDir, { recursive: true });
2064
- const mentionPath = join(srcDir, "app.ts");
2065
- const explicitPath = join(docsDir, "note.md");
2066
- writeFileSync(mentionPath, "export const v = 1;\n", "utf8");
2067
- writeFileSync(explicitPath, "note\n", "utf8");
2068
-
2069
- const sessionId = "sess-format";
2070
- const manifest = createManifest(sessionId);
2071
- const sessionService = {
2072
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2073
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2074
- manifestPath: "/tmp/manifest-format.json",
2075
- messagesPath: "/tmp/messages-format.json",
2076
- manifest,
2077
- }),
2078
- persistSessionMessages: vi.fn(),
2079
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2080
- writeSessionManifest: vi.fn(),
2081
- listSessions: vi.fn().mockResolvedValue([]),
2082
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2083
- };
2084
- const run = vi.fn().mockResolvedValue(createResult({ text: "ok" }));
2085
- const manager = new RuntimeHostUnderTest({
2086
- distinctId,
2087
- sessionService: sessionService as never,
2088
- runtimeBuilder: {
2089
- build: vi.fn().mockReturnValue({
2090
- tools: [],
2091
- shutdown: vi.fn(),
2092
- }),
2093
- },
2094
- createAgent: () =>
2095
- ({
2096
- run,
2097
- continue: vi.fn(),
2098
- abort: vi.fn(),
2099
- shutdown: vi.fn().mockResolvedValue(undefined),
2100
- getMessages: vi.fn().mockReturnValue([]),
2101
- messages: [],
2102
- }) as never,
2103
- });
2104
-
2105
- await manager.start(
2106
- normalizeStartInput({
2107
- config: createConfig({
2108
- sessionId,
2109
- cwd: join(tempCwd, "docs"),
2110
- workspaceRoot: tempCwd,
2111
- }),
2112
- interactive: true,
2113
- }),
2114
- );
2115
- await manager.send({
2116
- sessionId,
2117
- prompt: '<user_input mode="act">explain @src/app.ts</user_input>',
2118
- userFiles: ["note.md"],
2119
- });
2120
-
2121
- expect(run).toHaveBeenCalledWith(
2122
- '<user_input mode="act">explain @src/app.ts</user_input>',
2123
- undefined,
2124
- expect.arrayContaining([mentionPath, explicitPath]),
2125
- );
2126
- } finally {
2127
- rmSync(tempCwd, { recursive: true, force: true });
2128
- }
2129
- });
2130
-
2131
- it("force refreshes and retries once when turn fails with auth error", async () => {
2132
- const sessionId = "sess-oauth-retry";
2133
- const manifest = createManifest(sessionId);
2134
- const sessionService = {
2135
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2136
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2137
- manifestPath: "/tmp/manifest-oauth-retry.json",
2138
- messagesPath: "/tmp/messages-oauth-retry.json",
2139
- manifest,
2140
- }),
2141
- persistSessionMessages: vi.fn(),
2142
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2143
- writeSessionManifest: vi.fn(),
2144
- listSessions: vi.fn().mockResolvedValue([]),
2145
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2146
- };
2147
- const updateConnectionDefaults = vi.fn();
2148
- const runtimeBuilder = {
2149
- build: vi.fn().mockReturnValue({
2150
- tools: [],
2151
- delegatedAgentConfigProvider: {
2152
- getRuntimeConfig: vi.fn(),
2153
- getConnectionConfig: vi.fn(),
2154
- updateConnectionDefaults,
2155
- },
2156
- shutdown: vi.fn(),
2157
- }),
2158
- };
2159
- const run = vi
2160
- .fn()
2161
- .mockRejectedValueOnce(new Error("401 Unauthorized"))
2162
- .mockResolvedValueOnce(createResult({ text: "retried" }));
2163
- const restore = vi.fn();
2164
- const updateConnection = vi.fn();
2165
- const resolveProviderApiKey = vi
2166
- .fn()
2167
- .mockResolvedValueOnce(null)
2168
- .mockResolvedValueOnce({
2169
- providerId: "openai-codex",
2170
- apiKey: "oauth-access-new",
2171
- refreshed: true,
2172
- });
2173
- const manager = new RuntimeHostUnderTest({
2174
- distinctId,
2175
- sessionService: sessionService as never,
2176
- runtimeBuilder,
2177
- oauthTokenManager: {
2178
- resolveProviderApiKey,
2179
- } as never,
2180
- createAgent: () =>
2181
- ({
2182
- run,
2183
- continue: vi.fn(),
2184
- abort: vi.fn(),
2185
- restore,
2186
- updateConnection,
2187
- shutdown: vi.fn().mockResolvedValue(undefined),
2188
- getMessages: vi.fn().mockReturnValue([]),
2189
- messages: [],
2190
- }) as never,
2191
- });
2192
-
2193
- await manager.start(
2194
- normalizeStartInput({
2195
- config: createConfig({
2196
- sessionId,
2197
- providerId: "openai-codex",
2198
- apiKey: "oauth-access-old",
2199
- }),
2200
- interactive: true,
2201
- }),
2202
- );
2203
- const result = await manager.send({ sessionId, prompt: "hello" });
2204
-
2205
- expect(result?.text).toBe("retried");
2206
- expect(run).toHaveBeenCalledTimes(2);
2207
- expect(restore).toHaveBeenCalledTimes(1);
2208
- expect(resolveProviderApiKey).toHaveBeenNthCalledWith(1, {
2209
- providerId: "openai-codex",
2210
- forceRefresh: undefined,
2211
- });
2212
- expect(resolveProviderApiKey).toHaveBeenNthCalledWith(2, {
2213
- providerId: "openai-codex",
2214
- forceRefresh: true,
2215
- });
2216
- expect(updateConnection).toHaveBeenCalledWith({
2217
- apiKey: "oauth-access-new",
2218
- });
2219
- expect(updateConnectionDefaults).toHaveBeenCalledWith({
2220
- apiKey: "oauth-access-new",
2221
- });
2222
- });
2223
-
2224
- it("auto-continues when async teammate runs complete after lead turn", async () => {
2225
- const sessionId = "sess-team-auto-continue";
2226
- const manifest = createManifest(sessionId);
2227
- const sessionService = {
2228
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2229
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2230
- manifestPath: "/tmp/manifest-team-auto-continue.json",
2231
- messagesPath: "/tmp/messages-team-auto-continue.json",
2232
- manifest,
2233
- }),
2234
- persistSessionMessages: vi.fn(),
2235
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2236
- writeSessionManifest: vi.fn(),
2237
- listSessions: vi.fn().mockResolvedValue([]),
2238
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2239
- };
2240
-
2241
- let onTeamEvent: ((event: unknown) => void) | undefined;
2242
- const runtimeBuilder = {
2243
- build: vi
2244
- .fn()
2245
- .mockImplementation(
2246
- (input: { onTeamEvent?: (event: unknown) => void }) => {
2247
- onTeamEvent = input.onTeamEvent;
2248
- return {
2249
- tools: [],
2250
- shutdown: vi.fn(),
2251
- };
2252
- },
2253
- ),
2254
- };
2255
-
2256
- const run = vi.fn().mockImplementation(async () => {
2257
- onTeamEvent?.({
2258
- type: "run_started",
2259
- run: {
2260
- id: "run_0001",
2261
- agentId: "investigator",
2262
- status: "running",
2263
- message: "Investigate",
2264
- priority: 0,
2265
- retryCount: 0,
2266
- maxRetries: 0,
2267
- startedAt: new Date("2026-01-01T00:00:00.000Z"),
2268
- },
2269
- });
2270
- setTimeout(() => {
2271
- onTeamEvent?.({
2272
- type: "run_completed",
2273
- run: {
2274
- id: "run_0001",
2275
- agentId: "investigator",
2276
- status: "completed",
2277
- message: "Investigate",
2278
- priority: 0,
2279
- retryCount: 0,
2280
- maxRetries: 0,
2281
- startedAt: new Date("2026-01-01T00:00:00.000Z"),
2282
- endedAt: new Date("2026-01-01T00:00:02.000Z"),
2283
- result: createResult({ iterations: 3 }),
2284
- },
2285
- });
2286
- }, 0);
2287
- return createResult({
2288
- text: "lead scheduled teammate",
2289
- messages: [
2290
- { role: "user", content: "run teammate work" },
2291
- { role: "assistant", content: "lead scheduled teammate" },
2292
- ],
2293
- });
2294
- });
2295
- const continueFn = vi.fn().mockResolvedValue(
2296
- createResult({
2297
- text: "lead processed teammate result",
2298
- messages: [
2299
- { role: "user", content: "run teammate work" },
2300
- { role: "assistant", content: "lead scheduled teammate" },
2301
- {
2302
- role: "user",
2303
- content:
2304
- "System-delivered teammate async run updates:\n- investigator completed",
2305
- },
2306
- { role: "assistant", content: "lead processed teammate result" },
2307
- ],
2308
- }),
2309
- );
2310
- const manager = new RuntimeHostUnderTest({
2311
- distinctId,
2312
- sessionService: sessionService as never,
2313
- runtimeBuilder,
2314
- createAgent: () =>
2315
- ({
2316
- run,
2317
- continue: continueFn,
2318
- abort: vi.fn(),
2319
- shutdown: vi.fn().mockResolvedValue(undefined),
2320
- getMessages: vi.fn().mockReturnValue([]),
2321
- messages: [],
2322
- }) as never,
2323
- });
2324
-
2325
- await manager.start(
2326
- normalizeStartInput({
2327
- config: createConfig({ sessionId }),
2328
- interactive: false,
2329
- }),
2330
- );
2331
- const result = await manager.send({
2332
- sessionId,
2333
- prompt: "run teammate work",
2334
- });
2335
-
2336
- expect(result?.text).toBe("lead processed teammate result");
2337
- expect(run).toHaveBeenCalledTimes(1);
2338
- expect(continueFn).toHaveBeenCalledTimes(1);
2339
- expect(continueFn.mock.calls[0]?.[0]).toContain(
2340
- "System-delivered teammate async run updates:",
2341
- );
2342
- const finalPersistedMessages = (
2343
- sessionService.persistSessionMessages as ReturnType<typeof vi.fn>
2344
- ).mock.calls.at(-1)?.[1] as Array<Record<string, unknown>> | undefined;
2345
- expect(finalPersistedMessages?.at(-1)).toMatchObject({
2346
- role: "assistant",
2347
- metrics: {
2348
- inputTokens: 1,
2349
- outputTokens: 2,
2350
- cost: 0,
2351
- },
2352
- modelInfo: {
2353
- id: "mock-model",
2354
- provider: "mock-provider",
2355
- },
2356
- });
2357
- expect(sessionService.updateSessionStatus).toHaveBeenCalledWith(
2358
- sessionId,
2359
- "completed",
2360
- 0,
2361
- );
2362
- });
2363
-
2364
- it("persists failed teammate task messages for team-task sub-sessions", async () => {
2365
- const sessionId = "sess-team-task-failure-messages";
2366
- const manifest = createManifest(sessionId);
2367
- const sessionService = {
2368
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2369
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2370
- manifestPath: "/tmp/manifest-team-task-failure-messages.json",
2371
- messagesPath: "/tmp/messages-team-task-failure-messages.json",
2372
- manifest,
2373
- }),
2374
- persistSessionMessages: vi.fn(),
2375
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2376
- writeSessionManifest: vi.fn(),
2377
- listSessions: vi.fn().mockResolvedValue([]),
2378
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2379
- onTeamTaskStart: vi.fn().mockResolvedValue(undefined),
2380
- onTeamTaskEnd: vi.fn().mockResolvedValue(undefined),
2381
- };
2382
-
2383
- let onTeamEvent: ((event: unknown) => void) | undefined;
2384
- const runtimeBuilder = {
2385
- build: vi
2386
- .fn()
2387
- .mockImplementation(
2388
- (input: { onTeamEvent?: (event: unknown) => void }) => {
2389
- onTeamEvent = input.onTeamEvent;
2390
- return {
2391
- tools: [],
2392
- shutdown: vi.fn(),
2393
- };
2394
- },
2395
- ),
2396
- };
2397
-
2398
- const failedMessages = [
2399
- { role: "user", content: [{ type: "text", text: "delegated prompt" }] },
2400
- { role: "assistant", content: [{ type: "text", text: "partial work" }] },
2401
- ];
2402
- const manager = new RuntimeHostUnderTest({
2403
- distinctId,
2404
- sessionService: sessionService as never,
2405
- runtimeBuilder,
2406
- createAgent: () =>
2407
- ({
2408
- run: vi.fn().mockImplementation(async () => {
2409
- onTeamEvent?.({
2410
- type: "task_start",
2411
- agentId: "providers-investigator",
2412
- message: "Investigate provider boundaries",
2413
- });
2414
- onTeamEvent?.({
2415
- type: "task_end",
2416
- agentId: "providers-investigator",
2417
- error: new Error("401 Unauthorized"),
2418
- messages: failedMessages,
2419
- });
2420
- return createResult({ text: "lead handled failure" });
2421
- }),
2422
- continue: vi.fn(),
2423
- abort: vi.fn(),
2424
- shutdown: vi.fn().mockResolvedValue(undefined),
2425
- getMessages: vi.fn().mockReturnValue([]),
2426
- messages: [],
2427
- }) as never,
2428
- });
2429
-
2430
- await manager.start(
2431
- normalizeStartInput({
2432
- config: createConfig({ sessionId }),
2433
- prompt: "run teammate work",
2434
- interactive: false,
2435
- }),
2436
- );
2437
-
2438
- expect(sessionService.onTeamTaskStart).toHaveBeenCalledTimes(1);
2439
- expect(sessionService.onTeamTaskEnd).toHaveBeenCalledWith(
2440
- sessionId,
2441
- "providers-investigator",
2442
- "failed",
2443
- "[error] 401 Unauthorized",
2444
- undefined,
2445
- failedMessages,
2446
- );
2447
- });
2448
-
2449
- it("persists teammate progress updates for team-task sub-sessions", async () => {
2450
- const sessionId = "sess-team-task-progress";
2451
- const manifest = createManifest(sessionId);
2452
- const sessionService = {
2453
- ensureSessionsDir: vi.fn().mockReturnValue("/tmp/sessions"),
2454
- createRootSessionWithArtifacts: vi.fn().mockResolvedValue({
2455
- manifestPath: "/tmp/manifest-team-task-progress.json",
2456
- messagesPath: "/tmp/messages-team-task-progress.json",
2457
- manifest,
2458
- }),
2459
- persistSessionMessages: vi.fn(),
2460
- updateSessionStatus: vi.fn().mockResolvedValue({ updated: true }),
2461
- writeSessionManifest: vi.fn(),
2462
- listSessions: vi.fn().mockResolvedValue([]),
2463
- deleteSession: vi.fn().mockResolvedValue({ deleted: true }),
2464
- onTeamTaskStart: vi.fn().mockResolvedValue(undefined),
2465
- onTeamTaskEnd: vi.fn().mockResolvedValue(undefined),
2466
- onTeamTaskProgress: vi.fn().mockResolvedValue(undefined),
2467
- };
2468
-
2469
- let onTeamEvent: ((event: unknown) => void) | undefined;
2470
- const runtimeBuilder = {
2471
- build: vi
2472
- .fn()
2473
- .mockImplementation(
2474
- (input: { onTeamEvent?: (event: unknown) => void }) => {
2475
- onTeamEvent = input.onTeamEvent;
2476
- return {
2477
- tools: [],
2478
- shutdown: vi.fn(),
2479
- };
2480
- },
2481
- ),
2482
- };
2483
-
2484
- const manager = new RuntimeHostUnderTest({
2485
- distinctId,
2486
- sessionService: sessionService as never,
2487
- runtimeBuilder,
2488
- createAgent: () =>
2489
- ({
2490
- run: vi.fn().mockImplementation(async () => {
2491
- onTeamEvent?.({
2492
- type: "task_start",
2493
- agentId: "providers-investigator",
2494
- message: "Investigate provider boundaries",
2495
- });
2496
- onTeamEvent?.({
2497
- type: "run_progress",
2498
- run: {
2499
- id: "run_00002",
2500
- agentId: "providers-investigator",
2501
- status: "running",
2502
- message: "Investigate provider boundaries",
2503
- priority: 0,
2504
- retryCount: 0,
2505
- maxRetries: 0,
2506
- continueConversation: false,
2507
- startedAt: new Date("2026-01-01T00:00:00.000Z"),
2508
- lastProgressAt: new Date("2026-01-01T00:00:01.000Z"),
2509
- lastProgressMessage: "heartbeat",
2510
- currentActivity: "heartbeat",
2511
- },
2512
- message: "heartbeat",
2513
- });
2514
- onTeamEvent?.({
2515
- type: "agent_event",
2516
- agentId: "providers-investigator",
2517
- event: {
2518
- type: "content_start",
2519
- contentType: "text",
2520
- text: "Drafting the provider boundary analysis now.",
2521
- },
2522
- });
2523
- onTeamEvent?.({
2524
- type: "task_end",
2525
- agentId: "providers-investigator",
2526
- result: createResult(),
2527
- });
2528
- return createResult({ text: "lead handled progress" });
2529
- }),
2530
- continue: vi.fn(),
2531
- abort: vi.fn(),
2532
- shutdown: vi.fn().mockResolvedValue(undefined),
2533
- getMessages: vi.fn().mockReturnValue([]),
2534
- messages: [],
2535
- }) as never,
2536
- });
2537
-
2538
- await manager.start(
2539
- normalizeStartInput({
2540
- config: createConfig({ sessionId }),
2541
- prompt: "run teammate work",
2542
- interactive: false,
2543
- }),
2544
- );
2545
-
2546
- expect(sessionService.onTeamTaskProgress).toHaveBeenCalledWith(
2547
- sessionId,
2548
- "providers-investigator",
2549
- "heartbeat",
2550
- { kind: "heartbeat" },
2551
- );
2552
- expect(sessionService.onTeamTaskProgress).toHaveBeenCalledWith(
2553
- sessionId,
2554
- "providers-investigator",
2555
- "Drafting the provider boundary analysis now.",
2556
- { kind: "text" },
2557
- );
2558
- });
2559
- });