@clinebot/core 0.0.34 → 0.0.36

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