@roj-ai/sdk 0.1.35 → 0.2.1

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 (908) hide show
  1. package/dist/bootstrap.d.ts +420 -26
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/bootstrap.js +55 -24
  4. package/dist/bootstrap.js.map +1 -1
  5. package/dist/bun-platform/fs.d.ts.map +1 -1
  6. package/dist/bun-platform/fs.js +1 -0
  7. package/dist/bun-platform/fs.js.map +1 -1
  8. package/dist/bun-platform/index.d.ts +4 -1
  9. package/dist/bun-platform/index.d.ts.map +1 -1
  10. package/dist/bun-platform/index.js +7 -1
  11. package/dist/bun-platform/index.js.map +1 -1
  12. package/dist/bun-platform/server-lifecycle.d.ts +30 -0
  13. package/dist/bun-platform/server-lifecycle.d.ts.map +1 -0
  14. package/dist/bun-platform/server-lifecycle.js +182 -0
  15. package/dist/bun-platform/server-lifecycle.js.map +1 -0
  16. package/dist/bun-platform/shell.d.ts +33 -0
  17. package/dist/bun-platform/shell.d.ts.map +1 -0
  18. package/dist/bun-platform/shell.js +322 -0
  19. package/dist/bun-platform/shell.js.map +1 -0
  20. package/dist/config.d.ts +34 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +97 -0
  23. package/dist/config.js.map +1 -1
  24. package/dist/core/agents/agent.d.ts +42 -7
  25. package/dist/core/agents/agent.d.ts.map +1 -1
  26. package/dist/core/agents/agent.js +209 -90
  27. package/dist/core/agents/agent.js.map +1 -1
  28. package/dist/core/agents/config.d.ts +8 -1
  29. package/dist/core/agents/config.d.ts.map +1 -1
  30. package/dist/core/agents/config.js +5 -1
  31. package/dist/core/agents/config.js.map +1 -1
  32. package/dist/core/agents/retry.d.ts +1 -1
  33. package/dist/core/agents/retry.d.ts.map +1 -1
  34. package/dist/core/agents/retry.js +14 -16
  35. package/dist/core/agents/retry.js.map +1 -1
  36. package/dist/core/agents/state.d.ts +27 -1
  37. package/dist/core/agents/state.d.ts.map +1 -1
  38. package/dist/core/agents/state.js +1 -1
  39. package/dist/core/agents/state.js.map +1 -1
  40. package/dist/core/context/state.d.ts +3 -3
  41. package/dist/core/context/state.d.ts.map +1 -1
  42. package/dist/core/context/state.js +6 -2
  43. package/dist/core/context/state.js.map +1 -1
  44. package/dist/core/events/base-event-store.d.ts.map +1 -1
  45. package/dist/core/events/base-event-store.js +22 -14
  46. package/dist/core/events/base-event-store.js.map +1 -1
  47. package/dist/core/events/file.d.ts +39 -1
  48. package/dist/core/events/file.d.ts.map +1 -1
  49. package/dist/core/events/file.js +159 -20
  50. package/dist/core/events/file.js.map +1 -1
  51. package/dist/core/events/types.d.ts +1 -1
  52. package/dist/core/file-store/containment.d.ts +19 -0
  53. package/dist/core/file-store/containment.d.ts.map +1 -0
  54. package/dist/core/file-store/containment.js +62 -0
  55. package/dist/core/file-store/containment.js.map +1 -0
  56. package/dist/core/file-store/file-store.d.ts +22 -2
  57. package/dist/core/file-store/file-store.d.ts.map +1 -1
  58. package/dist/core/file-store/file-store.js +88 -20
  59. package/dist/core/file-store/file-store.js.map +1 -1
  60. package/dist/core/file-store/types.d.ts +13 -1
  61. package/dist/core/file-store/types.d.ts.map +1 -1
  62. package/dist/core/image/image-processor.d.ts.map +1 -1
  63. package/dist/core/image/image-processor.js +3 -1
  64. package/dist/core/image/image-processor.js.map +1 -1
  65. package/dist/core/image/types.d.ts +2 -0
  66. package/dist/core/image/types.d.ts.map +1 -1
  67. package/dist/core/image/vips-resizer.d.ts.map +1 -1
  68. package/dist/core/image/vips-resizer.js +81 -33
  69. package/dist/core/image/vips-resizer.js.map +1 -1
  70. package/dist/core/llm/anthropic.d.ts +0 -1
  71. package/dist/core/llm/anthropic.d.ts.map +1 -1
  72. package/dist/core/llm/anthropic.js +101 -74
  73. package/dist/core/llm/anthropic.js.map +1 -1
  74. package/dist/core/llm/logger.d.ts +14 -2
  75. package/dist/core/llm/logger.d.ts.map +1 -1
  76. package/dist/core/llm/logger.js +161 -36
  77. package/dist/core/llm/logger.js.map +1 -1
  78. package/dist/core/llm/openrouter.d.ts +0 -1
  79. package/dist/core/llm/openrouter.d.ts.map +1 -1
  80. package/dist/core/llm/openrouter.js +70 -74
  81. package/dist/core/llm/openrouter.js.map +1 -1
  82. package/dist/core/llm/provider-request.d.ts +10 -0
  83. package/dist/core/llm/provider-request.d.ts.map +1 -0
  84. package/dist/core/llm/provider-request.js +41 -0
  85. package/dist/core/llm/provider-request.js.map +1 -0
  86. package/dist/core/llm/provider.d.ts +45 -3
  87. package/dist/core/llm/provider.d.ts.map +1 -1
  88. package/dist/core/llm/provider.js +26 -0
  89. package/dist/core/llm/provider.js.map +1 -1
  90. package/dist/core/llm/state.d.ts +4 -2
  91. package/dist/core/llm/state.d.ts.map +1 -1
  92. package/dist/core/llm/state.js +15 -4
  93. package/dist/core/llm/state.js.map +1 -1
  94. package/dist/core/plugins/plugin-builder.d.ts +57 -3
  95. package/dist/core/plugins/plugin-builder.d.ts.map +1 -1
  96. package/dist/core/plugins/plugin-builder.js +68 -14
  97. package/dist/core/plugins/plugin-builder.js.map +1 -1
  98. package/dist/core/preset/preset-builder.d.ts +3 -2
  99. package/dist/core/preset/preset-builder.d.ts.map +1 -1
  100. package/dist/core/preset/preset-builder.js +7 -16
  101. package/dist/core/preset/preset-builder.js.map +1 -1
  102. package/dist/core/sessions/context.d.ts +23 -5
  103. package/dist/core/sessions/context.d.ts.map +1 -1
  104. package/dist/core/sessions/runtime-activity.d.ts +33 -0
  105. package/dist/core/sessions/runtime-activity.d.ts.map +1 -0
  106. package/dist/core/sessions/runtime-activity.js +55 -0
  107. package/dist/core/sessions/runtime-activity.js.map +1 -0
  108. package/dist/core/sessions/schema.d.ts +23 -2
  109. package/dist/core/sessions/schema.d.ts.map +1 -1
  110. package/dist/core/sessions/schema.js +24 -3
  111. package/dist/core/sessions/schema.js.map +1 -1
  112. package/dist/core/sessions/session-manager.d.ts +83 -6
  113. package/dist/core/sessions/session-manager.d.ts.map +1 -1
  114. package/dist/core/sessions/session-manager.js +517 -118
  115. package/dist/core/sessions/session-manager.js.map +1 -1
  116. package/dist/core/sessions/session-store.d.ts +18 -0
  117. package/dist/core/sessions/session-store.d.ts.map +1 -1
  118. package/dist/core/sessions/session-store.js +33 -0
  119. package/dist/core/sessions/session-store.js.map +1 -1
  120. package/dist/core/sessions/session.d.ts +42 -6
  121. package/dist/core/sessions/session.d.ts.map +1 -1
  122. package/dist/core/sessions/session.js +176 -44
  123. package/dist/core/sessions/session.js.map +1 -1
  124. package/dist/core/sessions/state.d.ts +10 -1
  125. package/dist/core/sessions/state.d.ts.map +1 -1
  126. package/dist/core/sessions/state.js +16 -1
  127. package/dist/core/sessions/state.js.map +1 -1
  128. package/dist/core/system.d.ts +9 -0
  129. package/dist/core/system.d.ts.map +1 -1
  130. package/dist/core/system.js +4 -1
  131. package/dist/core/system.js.map +1 -1
  132. package/dist/core/tools/executor.d.ts +1 -1
  133. package/dist/core/wake-key.d.ts +34 -0
  134. package/dist/core/wake-key.d.ts.map +1 -0
  135. package/dist/core/wake-key.js +51 -0
  136. package/dist/core/wake-key.js.map +1 -0
  137. package/dist/index.d.ts +24 -13
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +22 -8
  140. package/dist/index.js.map +1 -1
  141. package/dist/lib/archive/archive-inspection.d.ts +48 -0
  142. package/dist/lib/archive/archive-inspection.d.ts.map +1 -0
  143. package/dist/lib/archive/archive-inspection.fixtures.d.ts +7 -0
  144. package/dist/lib/archive/archive-inspection.fixtures.d.ts.map +1 -0
  145. package/dist/lib/archive/archive-inspection.fixtures.js +134 -0
  146. package/dist/lib/archive/archive-inspection.fixtures.js.map +1 -0
  147. package/dist/lib/archive/archive-inspection.js +253 -0
  148. package/dist/lib/archive/archive-inspection.js.map +1 -0
  149. package/dist/lib/archive/index.d.ts +3 -0
  150. package/dist/lib/archive/index.d.ts.map +1 -0
  151. package/dist/lib/archive/index.js +2 -0
  152. package/dist/lib/archive/index.js.map +1 -0
  153. package/dist/lib/logger/console.d.ts +5 -12
  154. package/dist/lib/logger/console.d.ts.map +1 -1
  155. package/dist/lib/logger/console.js +5 -47
  156. package/dist/lib/logger/console.js.map +1 -1
  157. package/dist/lib/logger/file.d.ts +8 -11
  158. package/dist/lib/logger/file.d.ts.map +1 -1
  159. package/dist/lib/logger/file.js +26 -37
  160. package/dist/lib/logger/file.js.map +1 -1
  161. package/dist/lib/logger/index.d.ts +3 -1
  162. package/dist/lib/logger/index.d.ts.map +1 -1
  163. package/dist/lib/logger/index.js +3 -1
  164. package/dist/lib/logger/index.js.map +1 -1
  165. package/dist/lib/logger/jsonl.d.ts +21 -0
  166. package/dist/lib/logger/jsonl.d.ts.map +1 -0
  167. package/dist/lib/logger/jsonl.js +29 -0
  168. package/dist/lib/logger/jsonl.js.map +1 -0
  169. package/dist/lib/logger/logger.d.ts +22 -0
  170. package/dist/lib/logger/logger.d.ts.map +1 -1
  171. package/dist/lib/logger/logger.js +37 -0
  172. package/dist/lib/logger/logger.js.map +1 -1
  173. package/dist/lib/logger/session-log.d.ts +21 -0
  174. package/dist/lib/logger/session-log.d.ts.map +1 -0
  175. package/dist/lib/logger/session-log.js +37 -0
  176. package/dist/lib/logger/session-log.js.map +1 -0
  177. package/dist/lib/utils/concurrency.d.ts +1 -1
  178. package/dist/lib/utils/concurrency.d.ts.map +1 -1
  179. package/dist/lib/utils/concurrency.js +30 -7
  180. package/dist/lib/utils/concurrency.js.map +1 -1
  181. package/dist/lib/utils/fs-batch.d.ts +17 -0
  182. package/dist/lib/utils/fs-batch.d.ts.map +1 -0
  183. package/dist/lib/utils/fs-batch.js +38 -0
  184. package/dist/lib/utils/fs-batch.js.map +1 -0
  185. package/dist/lib/utils/result.d.ts +7 -22
  186. package/dist/lib/utils/result.d.ts.map +1 -1
  187. package/dist/lib/utils/result.js +6 -17
  188. package/dist/lib/utils/result.js.map +1 -1
  189. package/dist/lib/utils/sleep.d.ts +10 -0
  190. package/dist/lib/utils/sleep.d.ts.map +1 -0
  191. package/dist/lib/utils/sleep.js +26 -0
  192. package/dist/lib/utils/sleep.js.map +1 -0
  193. package/dist/platform/fs-revision.d.ts +21 -0
  194. package/dist/platform/fs-revision.d.ts.map +1 -0
  195. package/dist/platform/fs-revision.js +9 -0
  196. package/dist/platform/fs-revision.js.map +1 -0
  197. package/dist/platform/fs.d.ts +104 -1
  198. package/dist/platform/fs.d.ts.map +1 -1
  199. package/dist/platform/git.d.ts +59 -0
  200. package/dist/platform/git.d.ts.map +1 -0
  201. package/dist/platform/git.js +8 -0
  202. package/dist/platform/git.js.map +1 -0
  203. package/dist/platform/index.d.ts +40 -1
  204. package/dist/platform/index.d.ts.map +1 -1
  205. package/dist/platform/index.js +1 -1
  206. package/dist/platform/index.js.map +1 -1
  207. package/dist/platform/llm-call-log.d.ts +80 -0
  208. package/dist/platform/llm-call-log.d.ts.map +1 -0
  209. package/dist/platform/llm-call-log.js +12 -0
  210. package/dist/platform/llm-call-log.js.map +1 -0
  211. package/dist/platform/scheduler.d.ts +32 -0
  212. package/dist/platform/scheduler.d.ts.map +1 -0
  213. package/dist/platform/scheduler.js +46 -0
  214. package/dist/platform/scheduler.js.map +1 -0
  215. package/dist/platform/session-log.d.ts +36 -0
  216. package/dist/platform/session-log.d.ts.map +1 -0
  217. package/dist/platform/session-log.js +12 -0
  218. package/dist/platform/session-log.js.map +1 -0
  219. package/dist/platform/shell.d.ts +83 -0
  220. package/dist/platform/shell.d.ts.map +1 -0
  221. package/dist/platform/shell.js +13 -0
  222. package/dist/platform/shell.js.map +1 -0
  223. package/dist/plugins/agent-status/plugin.d.ts.map +1 -1
  224. package/dist/plugins/agent-status/plugin.js +1 -0
  225. package/dist/plugins/agent-status/plugin.js.map +1 -1
  226. package/dist/plugins/agents/plugin.d.ts.map +1 -1
  227. package/dist/plugins/agents/plugin.js +129 -62
  228. package/dist/plugins/agents/plugin.js.map +1 -1
  229. package/dist/plugins/filesystem/listing.d.ts +3 -0
  230. package/dist/plugins/filesystem/listing.d.ts.map +1 -1
  231. package/dist/plugins/filesystem/listing.js +66 -10
  232. package/dist/plugins/filesystem/listing.js.map +1 -1
  233. package/dist/plugins/filesystem/plugin.d.ts.map +1 -1
  234. package/dist/plugins/filesystem/plugin.js +8 -6
  235. package/dist/plugins/filesystem/plugin.js.map +1 -1
  236. package/dist/plugins/git-status/plugin.d.ts +34 -7
  237. package/dist/plugins/git-status/plugin.d.ts.map +1 -1
  238. package/dist/plugins/git-status/plugin.js +168 -64
  239. package/dist/plugins/git-status/plugin.js.map +1 -1
  240. package/dist/plugins/llm-debug/plugin.d.ts.map +1 -1
  241. package/dist/plugins/llm-debug/plugin.js +1 -0
  242. package/dist/plugins/llm-debug/plugin.js.map +1 -1
  243. package/dist/plugins/logs/plugin.d.ts +1 -4
  244. package/dist/plugins/logs/plugin.d.ts.map +1 -1
  245. package/dist/plugins/logs/plugin.js +7 -0
  246. package/dist/plugins/logs/plugin.js.map +1 -1
  247. package/dist/plugins/mailbox/helpers.d.ts +2 -2
  248. package/dist/plugins/mailbox/helpers.js +1 -1
  249. package/dist/plugins/mailbox/plugin.d.ts.map +1 -1
  250. package/dist/plugins/mailbox/plugin.js +38 -43
  251. package/dist/plugins/mailbox/plugin.js.map +1 -1
  252. package/dist/plugins/mailbox/query.d.ts +7 -2
  253. package/dist/plugins/mailbox/query.d.ts.map +1 -1
  254. package/dist/plugins/mailbox/query.js +14 -7
  255. package/dist/plugins/mailbox/query.js.map +1 -1
  256. package/dist/plugins/mailbox/state.d.ts +5 -4
  257. package/dist/plugins/mailbox/state.d.ts.map +1 -1
  258. package/dist/plugins/mailbox/state.js +6 -5
  259. package/dist/plugins/mailbox/state.js.map +1 -1
  260. package/dist/plugins/resources/filename.d.ts +4 -0
  261. package/dist/plugins/resources/filename.d.ts.map +1 -0
  262. package/dist/plugins/resources/filename.js +18 -0
  263. package/dist/plugins/resources/filename.js.map +1 -0
  264. package/dist/plugins/resources/plugin.d.ts +3 -0
  265. package/dist/plugins/resources/plugin.d.ts.map +1 -1
  266. package/dist/plugins/resources/plugin.js +97 -60
  267. package/dist/plugins/resources/plugin.js.map +1 -1
  268. package/dist/plugins/resources/state.d.ts +1 -1
  269. package/dist/plugins/resources/state.js +1 -1
  270. package/dist/plugins/result-eviction/plugin.d.ts.map +1 -1
  271. package/dist/plugins/result-eviction/plugin.js +6 -1
  272. package/dist/plugins/result-eviction/plugin.js.map +1 -1
  273. package/dist/plugins/services/plugin.d.ts +4 -1
  274. package/dist/plugins/services/plugin.d.ts.map +1 -1
  275. package/dist/plugins/services/plugin.js +251 -100
  276. package/dist/plugins/services/plugin.js.map +1 -1
  277. package/dist/plugins/services/prompt.d.ts.map +1 -1
  278. package/dist/plugins/services/prompt.js +1 -4
  279. package/dist/plugins/services/prompt.js.map +1 -1
  280. package/dist/plugins/services/schema.d.ts +15 -5
  281. package/dist/plugins/services/schema.d.ts.map +1 -1
  282. package/dist/plugins/services/service.d.ts +100 -18
  283. package/dist/plugins/services/service.d.ts.map +1 -1
  284. package/dist/plugins/services/service.js +680 -230
  285. package/dist/plugins/services/service.js.map +1 -1
  286. package/dist/plugins/session-lifecycle/plugin.d.ts +21 -1
  287. package/dist/plugins/session-lifecycle/plugin.d.ts.map +1 -1
  288. package/dist/plugins/session-lifecycle/plugin.js +53 -40
  289. package/dist/plugins/session-lifecycle/plugin.js.map +1 -1
  290. package/dist/plugins/session-state/plugin.d.ts +21 -13
  291. package/dist/plugins/session-state/plugin.d.ts.map +1 -1
  292. package/dist/plugins/session-state/plugin.js +214 -79
  293. package/dist/plugins/session-state/plugin.js.map +1 -1
  294. package/dist/plugins/session-stats/plugin.d.ts.map +1 -1
  295. package/dist/plugins/session-stats/plugin.js +1 -0
  296. package/dist/plugins/session-stats/plugin.js.map +1 -1
  297. package/dist/plugins/shell/executor.d.ts +17 -15
  298. package/dist/plugins/shell/executor.d.ts.map +1 -1
  299. package/dist/plugins/shell/executor.js +130 -195
  300. package/dist/plugins/shell/executor.js.map +1 -1
  301. package/dist/plugins/shell/index.d.ts +2 -2
  302. package/dist/plugins/shell/index.d.ts.map +1 -1
  303. package/dist/plugins/shell/index.js +2 -2
  304. package/dist/plugins/shell/index.js.map +1 -1
  305. package/dist/plugins/shell/plugin.d.ts +5 -4
  306. package/dist/plugins/shell/plugin.d.ts.map +1 -1
  307. package/dist/plugins/shell/plugin.js +4 -8
  308. package/dist/plugins/shell/plugin.js.map +1 -1
  309. package/dist/plugins/skills/discovery.d.ts.map +1 -1
  310. package/dist/plugins/skills/discovery.js +10 -8
  311. package/dist/plugins/skills/discovery.js.map +1 -1
  312. package/dist/plugins/skills/index.d.ts +1 -1
  313. package/dist/plugins/skills/index.d.ts.map +1 -1
  314. package/dist/plugins/skills/plugin.d.ts +1 -1
  315. package/dist/plugins/skills/plugin.d.ts.map +1 -1
  316. package/dist/plugins/skills/plugin.js +23 -30
  317. package/dist/plugins/skills/plugin.js.map +1 -1
  318. package/dist/plugins/skills/prompts.d.ts +3 -3
  319. package/dist/plugins/skills/prompts.d.ts.map +1 -1
  320. package/dist/plugins/skills/prompts.js.map +1 -1
  321. package/dist/plugins/skills/schema.d.ts +4 -2
  322. package/dist/plugins/skills/schema.d.ts.map +1 -1
  323. package/dist/plugins/todo/schema.d.ts +1 -1
  324. package/dist/plugins/uploads/plugin.d.ts +11 -4
  325. package/dist/plugins/uploads/plugin.d.ts.map +1 -1
  326. package/dist/plugins/uploads/plugin.js +891 -225
  327. package/dist/plugins/uploads/plugin.js.map +1 -1
  328. package/dist/plugins/uploads/preprocessor.d.ts +14 -0
  329. package/dist/plugins/uploads/preprocessor.d.ts.map +1 -1
  330. package/dist/plugins/uploads/preprocessor.js +11 -0
  331. package/dist/plugins/uploads/preprocessor.js.map +1 -1
  332. package/dist/plugins/uploads/preprocessors/image-classifier.d.ts +1 -1
  333. package/dist/plugins/uploads/preprocessors/image-classifier.d.ts.map +1 -1
  334. package/dist/plugins/uploads/preprocessors/image-classifier.js +51 -26
  335. package/dist/plugins/uploads/preprocessors/image-classifier.js.map +1 -1
  336. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.d.ts +2 -2
  337. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.d.ts.map +1 -1
  338. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js +41 -16
  339. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.js.map +1 -1
  340. package/dist/plugins/uploads/preprocessors/pdf-preprocessor.d.ts +1 -1
  341. package/dist/plugins/uploads/preprocessors/pdf-preprocessor.d.ts.map +1 -1
  342. package/dist/plugins/uploads/preprocessors/pdf-preprocessor.js +29 -14
  343. package/dist/plugins/uploads/preprocessors/pdf-preprocessor.js.map +1 -1
  344. package/dist/plugins/uploads/preprocessors/zip-preprocessor.d.ts +1 -1
  345. package/dist/plugins/uploads/preprocessors/zip-preprocessor.d.ts.map +1 -1
  346. package/dist/plugins/uploads/preprocessors/zip-preprocessor.js +62 -34
  347. package/dist/plugins/uploads/preprocessors/zip-preprocessor.js.map +1 -1
  348. package/dist/plugins/uploads/schema.d.ts +25 -2
  349. package/dist/plugins/uploads/schema.d.ts.map +1 -1
  350. package/dist/plugins/uploads/schema.js +24 -3
  351. package/dist/plugins/uploads/schema.js.map +1 -1
  352. package/dist/plugins/uploads/state.d.ts +13 -1
  353. package/dist/plugins/uploads/state.d.ts.map +1 -1
  354. package/dist/plugins/uploads/state.js +4 -1
  355. package/dist/plugins/uploads/state.js.map +1 -1
  356. package/dist/plugins/user-chat/plugin.d.ts +12 -5
  357. package/dist/plugins/user-chat/plugin.d.ts.map +1 -1
  358. package/dist/plugins/user-chat/plugin.js +141 -32
  359. package/dist/plugins/user-chat/plugin.js.map +1 -1
  360. package/dist/plugins/workers/context.d.ts +35 -13
  361. package/dist/plugins/workers/context.d.ts.map +1 -1
  362. package/dist/plugins/workers/context.js +82 -32
  363. package/dist/plugins/workers/context.js.map +1 -1
  364. package/dist/plugins/workers/definition.d.ts +7 -0
  365. package/dist/plugins/workers/definition.d.ts.map +1 -1
  366. package/dist/plugins/workers/definition.js.map +1 -1
  367. package/dist/plugins/workers/index.d.ts +4 -4
  368. package/dist/plugins/workers/index.d.ts.map +1 -1
  369. package/dist/plugins/workers/index.js +2 -2
  370. package/dist/plugins/workers/index.js.map +1 -1
  371. package/dist/plugins/workers/plugin.d.ts +10 -57
  372. package/dist/plugins/workers/plugin.d.ts.map +1 -1
  373. package/dist/plugins/workers/plugin.js +385 -123
  374. package/dist/plugins/workers/plugin.js.map +1 -1
  375. package/dist/plugins/workers/state.d.ts +69 -0
  376. package/dist/plugins/workers/state.d.ts.map +1 -0
  377. package/dist/plugins/workers/state.js +52 -0
  378. package/dist/plugins/workers/state.js.map +1 -0
  379. package/dist/plugins/workers/worker.d.ts +15 -4
  380. package/dist/plugins/workers/worker.d.ts.map +1 -1
  381. package/dist/testing/conformance.d.ts +106 -0
  382. package/dist/testing/conformance.d.ts.map +1 -0
  383. package/dist/testing/conformance.js +1463 -0
  384. package/dist/testing/conformance.js.map +1 -0
  385. package/dist/testing/node-platform.d.ts.map +1 -1
  386. package/dist/testing/node-platform.js +5 -0
  387. package/dist/testing/node-platform.js.map +1 -1
  388. package/dist/testing/test-harness.d.ts +11 -1
  389. package/dist/testing/test-harness.d.ts.map +1 -1
  390. package/dist/testing/test-harness.js +17 -37
  391. package/dist/testing/test-harness.js.map +1 -1
  392. package/dist/testing/wait-helpers.d.ts.map +1 -1
  393. package/dist/testing/wait-helpers.js +1 -3
  394. package/dist/testing/wait-helpers.js.map +1 -1
  395. package/dist/transport/adapter/client-adapter.d.ts +6 -0
  396. package/dist/transport/adapter/client-adapter.d.ts.map +1 -1
  397. package/dist/transport/adapter/client-adapter.js +50 -3
  398. package/dist/transport/adapter/client-adapter.js.map +1 -1
  399. package/dist/transport/adapter/heartbeat.d.ts +16 -0
  400. package/dist/transport/adapter/heartbeat.d.ts.map +1 -0
  401. package/dist/transport/adapter/heartbeat.js +41 -0
  402. package/dist/transport/adapter/heartbeat.js.map +1 -0
  403. package/dist/transport/adapter/server-adapter.d.ts.map +1 -1
  404. package/dist/transport/adapter/server-adapter.js +6 -0
  405. package/dist/transport/adapter/server-adapter.js.map +1 -1
  406. package/dist/transport/http/app.d.ts +3 -30
  407. package/dist/transport/http/app.d.ts.map +1 -1
  408. package/dist/transport/http/app.js +3 -7
  409. package/dist/transport/http/app.js.map +1 -1
  410. package/dist/transport/http/context.d.ts +40 -0
  411. package/dist/transport/http/context.d.ts.map +1 -0
  412. package/dist/transport/http/context.js +17 -0
  413. package/dist/transport/http/context.js.map +1 -0
  414. package/dist/transport/http/fetch-guard.d.ts +49 -0
  415. package/dist/transport/http/fetch-guard.d.ts.map +1 -0
  416. package/dist/transport/http/fetch-guard.js +312 -0
  417. package/dist/transport/http/fetch-guard.js.map +1 -0
  418. package/dist/transport/http/index.d.ts +1 -1
  419. package/dist/transport/http/index.d.ts.map +1 -1
  420. package/dist/transport/http/middleware/bearer-auth.d.ts +1 -1
  421. package/dist/transport/http/middleware/bearer-auth.d.ts.map +1 -1
  422. package/dist/transport/http/middleware/error-handler.d.ts +2 -2
  423. package/dist/transport/http/middleware/error-handler.d.ts.map +1 -1
  424. package/dist/transport/http/path-containment.d.ts +20 -0
  425. package/dist/transport/http/path-containment.d.ts.map +1 -0
  426. package/dist/transport/http/path-containment.js +31 -0
  427. package/dist/transport/http/path-containment.js.map +1 -0
  428. package/dist/transport/http/responses.d.ts +30 -0
  429. package/dist/transport/http/responses.d.ts.map +1 -0
  430. package/dist/transport/http/responses.js +5 -0
  431. package/dist/transport/http/responses.js.map +1 -0
  432. package/dist/transport/http/routes/files.d.ts +1 -1
  433. package/dist/transport/http/routes/files.d.ts.map +1 -1
  434. package/dist/transport/http/routes/files.js +54 -155
  435. package/dist/transport/http/routes/files.js.map +1 -1
  436. package/dist/transport/http/routes/resources.d.ts +1 -1
  437. package/dist/transport/http/routes/resources.d.ts.map +1 -1
  438. package/dist/transport/http/routes/resources.js +92 -57
  439. package/dist/transport/http/routes/resources.js.map +1 -1
  440. package/dist/transport/http/routes/rpc.d.ts +6 -1
  441. package/dist/transport/http/routes/rpc.d.ts.map +1 -1
  442. package/dist/transport/http/routes/rpc.js +51 -21
  443. package/dist/transport/http/routes/rpc.js.map +1 -1
  444. package/dist/transport/http/routes/upload.d.ts +1 -1
  445. package/dist/transport/http/routes/upload.d.ts.map +1 -1
  446. package/dist/transport/http/routes/upload.js +256 -169
  447. package/dist/transport/http/routes/upload.js.map +1 -1
  448. package/dist/user-config.d.ts +34 -7
  449. package/dist/user-config.d.ts.map +1 -1
  450. package/dist/user-config.js +71 -0
  451. package/dist/user-config.js.map +1 -1
  452. package/package.json +18 -8
  453. package/src/bootstrap.ts +119 -35
  454. package/src/bun-platform/fs.ts +1 -0
  455. package/src/bun-platform/index.ts +18 -1
  456. package/src/bun-platform/server-lifecycle.ts +218 -0
  457. package/src/bun-platform/shell.ts +362 -0
  458. package/src/config.ts +130 -0
  459. package/src/core/agents/agent.ts +219 -95
  460. package/src/core/agents/config.ts +8 -0
  461. package/src/core/agents/retry.ts +16 -17
  462. package/src/core/agents/state.ts +28 -0
  463. package/src/core/context/state.ts +4 -0
  464. package/src/core/events/base-event-store.ts +24 -16
  465. package/src/core/events/file.ts +198 -22
  466. package/src/core/file-store/containment.ts +75 -0
  467. package/src/core/file-store/file-store.ts +106 -25
  468. package/src/core/file-store/types.ts +14 -1
  469. package/src/core/image/image-processor.ts +3 -1
  470. package/src/core/image/types.ts +2 -0
  471. package/src/core/image/vips-resizer.ts +102 -37
  472. package/src/core/llm/anthropic.ts +127 -82
  473. package/src/core/llm/logger.ts +175 -37
  474. package/src/core/llm/openrouter.ts +82 -81
  475. package/src/core/llm/provider-request.ts +40 -0
  476. package/src/core/llm/provider.ts +72 -3
  477. package/src/core/llm/state.ts +12 -0
  478. package/src/core/plugins/plugin-builder.ts +106 -14
  479. package/src/core/preset/preset-builder.ts +12 -22
  480. package/src/core/sessions/context.ts +18 -0
  481. package/src/core/sessions/runtime-activity.ts +78 -0
  482. package/src/core/sessions/schema.ts +29 -3
  483. package/src/core/sessions/session-manager.ts +560 -140
  484. package/src/core/sessions/session-store.ts +38 -0
  485. package/src/core/sessions/session.ts +187 -47
  486. package/src/core/sessions/state.ts +16 -0
  487. package/src/core/system.ts +15 -0
  488. package/src/core/wake-key.ts +65 -0
  489. package/src/index.ts +31 -12
  490. package/src/lib/archive/archive-inspection.fixtures.ts +138 -0
  491. package/src/lib/archive/archive-inspection.ts +359 -0
  492. package/src/lib/archive/index.ts +17 -0
  493. package/src/lib/logger/console.ts +7 -57
  494. package/src/lib/logger/file.ts +28 -50
  495. package/src/lib/logger/index.ts +3 -1
  496. package/src/lib/logger/jsonl.ts +36 -0
  497. package/src/lib/logger/logger.ts +48 -0
  498. package/src/lib/logger/session-log.ts +42 -0
  499. package/src/lib/utils/concurrency.ts +37 -8
  500. package/src/lib/utils/fs-batch.ts +49 -0
  501. package/src/lib/utils/result.ts +7 -31
  502. package/src/lib/utils/sleep.ts +23 -0
  503. package/src/platform/fs-revision.ts +21 -0
  504. package/src/platform/fs.ts +109 -1
  505. package/src/platform/git.ts +67 -0
  506. package/src/platform/index.ts +51 -1
  507. package/src/platform/llm-call-log.ts +86 -0
  508. package/src/platform/scheduler.ts +71 -0
  509. package/src/platform/session-log.ts +39 -0
  510. package/src/platform/shell.ts +89 -0
  511. package/src/plugins/agent-status/plugin.ts +1 -0
  512. package/src/plugins/agents/plugin.ts +134 -81
  513. package/src/plugins/filesystem/listing.ts +70 -12
  514. package/src/plugins/filesystem/plugin.ts +8 -6
  515. package/src/plugins/git-status/plugin.ts +204 -77
  516. package/src/plugins/llm-debug/plugin.ts +1 -0
  517. package/src/plugins/logs/plugin.ts +9 -0
  518. package/src/plugins/mailbox/plugin.ts +46 -64
  519. package/src/plugins/mailbox/query.ts +15 -7
  520. package/src/plugins/mailbox/state.ts +1 -0
  521. package/src/plugins/resources/filename.ts +19 -0
  522. package/src/plugins/resources/plugin.ts +111 -59
  523. package/src/plugins/result-eviction/plugin.ts +6 -1
  524. package/src/plugins/services/plugin.ts +254 -98
  525. package/src/plugins/services/prompt.ts +1 -6
  526. package/src/plugins/services/schema.ts +17 -3
  527. package/src/plugins/services/service.ts +702 -252
  528. package/src/plugins/session-lifecycle/plugin.ts +56 -41
  529. package/src/plugins/session-state/plugin.ts +242 -88
  530. package/src/plugins/session-stats/plugin.ts +1 -0
  531. package/src/plugins/shell/executor.ts +144 -226
  532. package/src/plugins/shell/index.ts +1 -4
  533. package/src/plugins/shell/plugin.ts +7 -9
  534. package/src/plugins/skills/discovery.ts +10 -8
  535. package/src/plugins/skills/index.ts +1 -1
  536. package/src/plugins/skills/plugin.ts +19 -27
  537. package/src/plugins/skills/prompts.ts +3 -3
  538. package/src/plugins/skills/schema.ts +5 -2
  539. package/src/plugins/uploads/plugin.ts +991 -273
  540. package/src/plugins/uploads/preprocessor.ts +25 -0
  541. package/src/plugins/uploads/preprocessors/image-classifier.ts +56 -25
  542. package/src/plugins/uploads/preprocessors/markitdown-preprocessor.ts +41 -15
  543. package/src/plugins/uploads/preprocessors/pdf-preprocessor.ts +32 -16
  544. package/src/plugins/uploads/preprocessors/zip-preprocessor.ts +67 -38
  545. package/src/plugins/uploads/schema.ts +30 -3
  546. package/src/plugins/uploads/state.ts +12 -0
  547. package/src/plugins/user-chat/plugin.ts +196 -40
  548. package/src/plugins/workers/context.ts +102 -39
  549. package/src/plugins/workers/definition.ts +7 -0
  550. package/src/plugins/workers/index.ts +4 -4
  551. package/src/plugins/workers/plugin.ts +461 -151
  552. package/src/plugins/workers/state.ts +64 -0
  553. package/src/plugins/workers/worker.ts +15 -4
  554. package/src/testing/conformance.ts +1751 -0
  555. package/src/testing/node-platform.ts +5 -0
  556. package/src/testing/test-harness.ts +21 -38
  557. package/src/testing/wait-helpers.ts +1 -4
  558. package/src/transport/adapter/client-adapter.ts +60 -3
  559. package/src/transport/adapter/heartbeat.ts +50 -0
  560. package/src/transport/adapter/server-adapter.ts +6 -0
  561. package/src/transport/http/app.ts +7 -35
  562. package/src/transport/http/context.ts +46 -0
  563. package/src/transport/http/fetch-guard.ts +318 -0
  564. package/src/transport/http/index.ts +1 -1
  565. package/src/transport/http/middleware/bearer-auth.ts +1 -1
  566. package/src/transport/http/middleware/error-handler.ts +1 -1
  567. package/src/transport/http/path-containment.ts +44 -0
  568. package/src/transport/http/responses.ts +20 -0
  569. package/src/transport/http/routes/files.ts +69 -159
  570. package/src/transport/http/routes/resources.ts +124 -93
  571. package/src/transport/http/routes/rpc.ts +63 -26
  572. package/src/transport/http/routes/upload.ts +327 -258
  573. package/src/user-config.ts +93 -7
  574. package/dist/config.test.d.ts +0 -2
  575. package/dist/config.test.d.ts.map +0 -1
  576. package/dist/config.test.js +0 -155
  577. package/dist/config.test.js.map +0 -1
  578. package/dist/core/agent-loop.integration.test.d.ts +0 -2
  579. package/dist/core/agent-loop.integration.test.d.ts.map +0 -1
  580. package/dist/core/agent-loop.integration.test.js +0 -414
  581. package/dist/core/agent-loop.integration.test.js.map +0 -1
  582. package/dist/core/agents/agent-config.test.d.ts +0 -2
  583. package/dist/core/agents/agent-config.test.d.ts.map +0 -1
  584. package/dist/core/agents/agent-config.test.js +0 -194
  585. package/dist/core/agents/agent-config.test.js.map +0 -1
  586. package/dist/core/agents/agent-shutdown.test.d.ts +0 -6
  587. package/dist/core/agents/agent-shutdown.test.d.ts.map +0 -1
  588. package/dist/core/agents/agent-shutdown.test.js +0 -180
  589. package/dist/core/agents/agent-shutdown.test.js.map +0 -1
  590. package/dist/core/agents/agent.test.d.ts +0 -2
  591. package/dist/core/agents/agent.test.d.ts.map +0 -1
  592. package/dist/core/agents/agent.test.js +0 -313
  593. package/dist/core/agents/agent.test.js.map +0 -1
  594. package/dist/core/agents/communicator.d.ts +0 -3
  595. package/dist/core/agents/communicator.d.ts.map +0 -1
  596. package/dist/core/agents/communicator.js +0 -13
  597. package/dist/core/agents/communicator.js.map +0 -1
  598. package/dist/core/agents/handler-events.test.d.ts +0 -5
  599. package/dist/core/agents/handler-events.test.d.ts.map +0 -1
  600. package/dist/core/agents/handler-events.test.js +0 -115
  601. package/dist/core/agents/handler-events.test.js.map +0 -1
  602. package/dist/core/agents/response-sanitizer.test.d.ts +0 -2
  603. package/dist/core/agents/response-sanitizer.test.d.ts.map +0 -1
  604. package/dist/core/agents/response-sanitizer.test.js +0 -101
  605. package/dist/core/agents/response-sanitizer.test.js.map +0 -1
  606. package/dist/core/error-recovery.integration.test.d.ts +0 -2
  607. package/dist/core/error-recovery.integration.test.d.ts.map +0 -1
  608. package/dist/core/error-recovery.integration.test.js +0 -288
  609. package/dist/core/error-recovery.integration.test.js.map +0 -1
  610. package/dist/core/event-sourcing.integration.test.d.ts +0 -2
  611. package/dist/core/event-sourcing.integration.test.d.ts.map +0 -1
  612. package/dist/core/event-sourcing.integration.test.js +0 -154
  613. package/dist/core/event-sourcing.integration.test.js.map +0 -1
  614. package/dist/core/events/file.test.d.ts +0 -2
  615. package/dist/core/events/file.test.d.ts.map +0 -1
  616. package/dist/core/events/file.test.js +0 -284
  617. package/dist/core/events/file.test.js.map +0 -1
  618. package/dist/core/events/memory.test.d.ts +0 -2
  619. package/dist/core/events/memory.test.d.ts.map +0 -1
  620. package/dist/core/events/memory.test.js +0 -502
  621. package/dist/core/events/memory.test.js.map +0 -1
  622. package/dist/core/image/image-processor.test.d.ts +0 -2
  623. package/dist/core/image/image-processor.test.d.ts.map +0 -1
  624. package/dist/core/image/image-processor.test.js +0 -171
  625. package/dist/core/image/image-processor.test.js.map +0 -1
  626. package/dist/core/image/vips-resizer.test.d.ts +0 -2
  627. package/dist/core/image/vips-resizer.test.d.ts.map +0 -1
  628. package/dist/core/image/vips-resizer.test.js +0 -336
  629. package/dist/core/image/vips-resizer.test.js.map +0 -1
  630. package/dist/core/llm/anthropic.test.d.ts +0 -2
  631. package/dist/core/llm/anthropic.test.d.ts.map +0 -1
  632. package/dist/core/llm/anthropic.test.js +0 -464
  633. package/dist/core/llm/anthropic.test.js.map +0 -1
  634. package/dist/core/llm/cache-breakpoints.test.d.ts +0 -2
  635. package/dist/core/llm/cache-breakpoints.test.d.ts.map +0 -1
  636. package/dist/core/llm/cache-breakpoints.test.js +0 -69
  637. package/dist/core/llm/cache-breakpoints.test.js.map +0 -1
  638. package/dist/core/llm/cache-live.test.d.ts +0 -16
  639. package/dist/core/llm/cache-live.test.d.ts.map +0 -1
  640. package/dist/core/llm/cache-live.test.js +0 -137
  641. package/dist/core/llm/cache-live.test.js.map +0 -1
  642. package/dist/core/llm/logger.test.d.ts +0 -7
  643. package/dist/core/llm/logger.test.d.ts.map +0 -1
  644. package/dist/core/llm/logger.test.js +0 -228
  645. package/dist/core/llm/logger.test.js.map +0 -1
  646. package/dist/core/llm/message-sanitization.test.d.ts +0 -2
  647. package/dist/core/llm/message-sanitization.test.d.ts.map +0 -1
  648. package/dist/core/llm/message-sanitization.test.js +0 -181
  649. package/dist/core/llm/message-sanitization.test.js.map +0 -1
  650. package/dist/core/llm/mock.test.d.ts +0 -2
  651. package/dist/core/llm/mock.test.d.ts.map +0 -1
  652. package/dist/core/llm/mock.test.js +0 -318
  653. package/dist/core/llm/mock.test.js.map +0 -1
  654. package/dist/core/llm/openrouter-mapping.test.d.ts +0 -2
  655. package/dist/core/llm/openrouter-mapping.test.d.ts.map +0 -1
  656. package/dist/core/llm/openrouter-mapping.test.js +0 -172
  657. package/dist/core/llm/openrouter-mapping.test.js.map +0 -1
  658. package/dist/core/llm/openrouter.test.d.ts +0 -2
  659. package/dist/core/llm/openrouter.test.d.ts.map +0 -1
  660. package/dist/core/llm/openrouter.test.js +0 -377
  661. package/dist/core/llm/openrouter.test.js.map +0 -1
  662. package/dist/core/llm/provider-integration.test.d.ts +0 -12
  663. package/dist/core/llm/provider-integration.test.d.ts.map +0 -1
  664. package/dist/core/llm/provider-integration.test.js +0 -350
  665. package/dist/core/llm/provider-integration.test.js.map +0 -1
  666. package/dist/core/llm/routing-provider.test.d.ts +0 -2
  667. package/dist/core/llm/routing-provider.test.d.ts.map +0 -1
  668. package/dist/core/llm/routing-provider.test.js +0 -94
  669. package/dist/core/llm/routing-provider.test.js.map +0 -1
  670. package/dist/core/llm/snapshot-middleware.test.d.ts +0 -2
  671. package/dist/core/llm/snapshot-middleware.test.d.ts.map +0 -1
  672. package/dist/core/llm/snapshot-middleware.test.js +0 -144
  673. package/dist/core/llm/snapshot-middleware.test.js.map +0 -1
  674. package/dist/core/multi-agent.integration.test.d.ts +0 -2
  675. package/dist/core/multi-agent.integration.test.d.ts.map +0 -1
  676. package/dist/core/multi-agent.integration.test.js +0 -298
  677. package/dist/core/multi-agent.integration.test.js.map +0 -1
  678. package/dist/core/plugin-hooks.integration.test.d.ts +0 -2
  679. package/dist/core/plugin-hooks.integration.test.d.ts.map +0 -1
  680. package/dist/core/plugin-hooks.integration.test.js +0 -458
  681. package/dist/core/plugin-hooks.integration.test.js.map +0 -1
  682. package/dist/core/session-lifecycle.integration.test.d.ts +0 -2
  683. package/dist/core/session-lifecycle.integration.test.d.ts.map +0 -1
  684. package/dist/core/session-lifecycle.integration.test.js +0 -159
  685. package/dist/core/session-lifecycle.integration.test.js.map +0 -1
  686. package/dist/core/sessions/fork-utils.test.d.ts +0 -2
  687. package/dist/core/sessions/fork-utils.test.d.ts.map +0 -1
  688. package/dist/core/sessions/fork-utils.test.js +0 -129
  689. package/dist/core/sessions/fork-utils.test.js.map +0 -1
  690. package/dist/core/sessions/session-overrides.integration.test.d.ts +0 -2
  691. package/dist/core/sessions/session-overrides.integration.test.d.ts.map +0 -1
  692. package/dist/core/sessions/session-overrides.integration.test.js +0 -154
  693. package/dist/core/sessions/session-overrides.integration.test.js.map +0 -1
  694. package/dist/core/sessions/session.test.d.ts +0 -2
  695. package/dist/core/sessions/session.test.d.ts.map +0 -1
  696. package/dist/core/sessions/session.test.js +0 -1100
  697. package/dist/core/sessions/session.test.js.map +0 -1
  698. package/dist/core/tools/executor.test.d.ts +0 -2
  699. package/dist/core/tools/executor.test.d.ts.map +0 -1
  700. package/dist/core/tools/executor.test.js +0 -144
  701. package/dist/core/tools/executor.test.js.map +0 -1
  702. package/dist/lib/logger/console.test.d.ts +0 -2
  703. package/dist/lib/logger/console.test.d.ts.map +0 -1
  704. package/dist/lib/logger/console.test.js +0 -258
  705. package/dist/lib/logger/console.test.js.map +0 -1
  706. package/dist/lib/never.d.ts +0 -2
  707. package/dist/lib/never.d.ts.map +0 -1
  708. package/dist/lib/never.js +0 -4
  709. package/dist/lib/never.js.map +0 -1
  710. package/dist/lib/utils/concurrency.test.d.ts +0 -2
  711. package/dist/lib/utils/concurrency.test.d.ts.map +0 -1
  712. package/dist/lib/utils/concurrency.test.js +0 -135
  713. package/dist/lib/utils/concurrency.test.js.map +0 -1
  714. package/dist/plugins/agent-status/agent-status.test.d.ts +0 -2
  715. package/dist/plugins/agent-status/agent-status.test.d.ts.map +0 -1
  716. package/dist/plugins/agent-status/agent-status.test.js +0 -136
  717. package/dist/plugins/agent-status/agent-status.test.js.map +0 -1
  718. package/dist/plugins/agents/agents.integration.test.d.ts +0 -2
  719. package/dist/plugins/agents/agents.integration.test.d.ts.map +0 -1
  720. package/dist/plugins/agents/agents.integration.test.js +0 -830
  721. package/dist/plugins/agents/agents.integration.test.js.map +0 -1
  722. package/dist/plugins/agents/supervision.integration.test.d.ts +0 -2
  723. package/dist/plugins/agents/supervision.integration.test.d.ts.map +0 -1
  724. package/dist/plugins/agents/supervision.integration.test.js +0 -215
  725. package/dist/plugins/agents/supervision.integration.test.js.map +0 -1
  726. package/dist/plugins/context-compact/compaction-live.test.d.ts +0 -17
  727. package/dist/plugins/context-compact/compaction-live.test.d.ts.map +0 -1
  728. package/dist/plugins/context-compact/compaction-live.test.js +0 -177
  729. package/dist/plugins/context-compact/compaction-live.test.js.map +0 -1
  730. package/dist/plugins/context-compact/context-compact.integration.test.d.ts +0 -2
  731. package/dist/plugins/context-compact/context-compact.integration.test.d.ts.map +0 -1
  732. package/dist/plugins/context-compact/context-compact.integration.test.js +0 -364
  733. package/dist/plugins/context-compact/context-compact.integration.test.js.map +0 -1
  734. package/dist/plugins/context-compact/context-compactor.test.d.ts +0 -2
  735. package/dist/plugins/context-compact/context-compactor.test.d.ts.map +0 -1
  736. package/dist/plugins/context-compact/context-compactor.test.js +0 -858
  737. package/dist/plugins/context-compact/context-compactor.test.js.map +0 -1
  738. package/dist/plugins/context-compact/history-offloader.test.d.ts +0 -2
  739. package/dist/plugins/context-compact/history-offloader.test.d.ts.map +0 -1
  740. package/dist/plugins/context-compact/history-offloader.test.js +0 -77
  741. package/dist/plugins/context-compact/history-offloader.test.js.map +0 -1
  742. package/dist/plugins/filesystem/filesystem.integration.test.d.ts +0 -2
  743. package/dist/plugins/filesystem/filesystem.integration.test.d.ts.map +0 -1
  744. package/dist/plugins/filesystem/filesystem.integration.test.js +0 -447
  745. package/dist/plugins/filesystem/filesystem.integration.test.js.map +0 -1
  746. package/dist/plugins/filesystem/schema.d.ts +0 -7
  747. package/dist/plugins/filesystem/schema.d.ts.map +0 -1
  748. package/dist/plugins/filesystem/schema.js +0 -2
  749. package/dist/plugins/filesystem/schema.js.map +0 -1
  750. package/dist/plugins/limits-guard/limit-guard.test.d.ts +0 -2
  751. package/dist/plugins/limits-guard/limit-guard.test.d.ts.map +0 -1
  752. package/dist/plugins/limits-guard/limit-guard.test.js +0 -194
  753. package/dist/plugins/limits-guard/limit-guard.test.js.map +0 -1
  754. package/dist/plugins/limits-guard/limits-guard.integration.test.d.ts +0 -2
  755. package/dist/plugins/limits-guard/limits-guard.integration.test.d.ts.map +0 -1
  756. package/dist/plugins/limits-guard/limits-guard.integration.test.js +0 -887
  757. package/dist/plugins/limits-guard/limits-guard.integration.test.js.map +0 -1
  758. package/dist/plugins/llm-debug/llm-debug.integration.test.d.ts +0 -2
  759. package/dist/plugins/llm-debug/llm-debug.integration.test.d.ts.map +0 -1
  760. package/dist/plugins/llm-debug/llm-debug.integration.test.js +0 -157
  761. package/dist/plugins/llm-debug/llm-debug.integration.test.js.map +0 -1
  762. package/dist/plugins/mailbox/mailbox.integration.test.d.ts +0 -2
  763. package/dist/plugins/mailbox/mailbox.integration.test.d.ts.map +0 -1
  764. package/dist/plugins/mailbox/mailbox.integration.test.js +0 -678
  765. package/dist/plugins/mailbox/mailbox.integration.test.js.map +0 -1
  766. package/dist/plugins/resources/resources.integration.test.d.ts +0 -2
  767. package/dist/plugins/resources/resources.integration.test.d.ts.map +0 -1
  768. package/dist/plugins/resources/resources.integration.test.js +0 -62
  769. package/dist/plugins/resources/resources.integration.test.js.map +0 -1
  770. package/dist/plugins/result-eviction/result-eviction.integration.test.d.ts +0 -2
  771. package/dist/plugins/result-eviction/result-eviction.integration.test.d.ts.map +0 -1
  772. package/dist/plugins/result-eviction/result-eviction.integration.test.js +0 -294
  773. package/dist/plugins/result-eviction/result-eviction.integration.test.js.map +0 -1
  774. package/dist/plugins/services/pid-registry.test.d.ts +0 -2
  775. package/dist/plugins/services/pid-registry.test.d.ts.map +0 -1
  776. package/dist/plugins/services/pid-registry.test.js +0 -161
  777. package/dist/plugins/services/pid-registry.test.js.map +0 -1
  778. package/dist/plugins/services/services.integration.test.d.ts +0 -2
  779. package/dist/plugins/services/services.integration.test.d.ts.map +0 -1
  780. package/dist/plugins/services/services.integration.test.js +0 -1057
  781. package/dist/plugins/services/services.integration.test.js.map +0 -1
  782. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.d.ts +0 -2
  783. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.d.ts.map +0 -1
  784. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.js +0 -498
  785. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.js.map +0 -1
  786. package/dist/plugins/session-stats/plugin.test.d.ts +0 -2
  787. package/dist/plugins/session-stats/plugin.test.d.ts.map +0 -1
  788. package/dist/plugins/session-stats/plugin.test.js +0 -145
  789. package/dist/plugins/session-stats/plugin.test.js.map +0 -1
  790. package/dist/plugins/shell/shell.integration.test.d.ts +0 -2
  791. package/dist/plugins/shell/shell.integration.test.d.ts.map +0 -1
  792. package/dist/plugins/shell/shell.integration.test.js +0 -234
  793. package/dist/plugins/shell/shell.integration.test.js.map +0 -1
  794. package/dist/plugins/shell/shell.test.d.ts +0 -2
  795. package/dist/plugins/shell/shell.test.d.ts.map +0 -1
  796. package/dist/plugins/shell/shell.test.js +0 -236
  797. package/dist/plugins/shell/shell.test.js.map +0 -1
  798. package/dist/plugins/skills/discovery.test.d.ts +0 -2
  799. package/dist/plugins/skills/discovery.test.d.ts.map +0 -1
  800. package/dist/plugins/skills/discovery.test.js +0 -312
  801. package/dist/plugins/skills/discovery.test.js.map +0 -1
  802. package/dist/plugins/skills/skills.integration.test.d.ts +0 -2
  803. package/dist/plugins/skills/skills.integration.test.d.ts.map +0 -1
  804. package/dist/plugins/skills/skills.integration.test.js +0 -475
  805. package/dist/plugins/skills/skills.integration.test.js.map +0 -1
  806. package/dist/plugins/todo/todo.integration.test.d.ts +0 -2
  807. package/dist/plugins/todo/todo.integration.test.d.ts.map +0 -1
  808. package/dist/plugins/todo/todo.integration.test.js +0 -605
  809. package/dist/plugins/todo/todo.integration.test.js.map +0 -1
  810. package/dist/plugins/uploads/preprocessors/image-classifier.test.d.ts +0 -2
  811. package/dist/plugins/uploads/preprocessors/image-classifier.test.d.ts.map +0 -1
  812. package/dist/plugins/uploads/preprocessors/image-classifier.test.js +0 -113
  813. package/dist/plugins/uploads/preprocessors/image-classifier.test.js.map +0 -1
  814. package/dist/plugins/uploads/uploads.integration.test.d.ts +0 -2
  815. package/dist/plugins/uploads/uploads.integration.test.d.ts.map +0 -1
  816. package/dist/plugins/uploads/uploads.integration.test.js +0 -639
  817. package/dist/plugins/uploads/uploads.integration.test.js.map +0 -1
  818. package/dist/plugins/user-chat/user-chat.integration.test.d.ts +0 -2
  819. package/dist/plugins/user-chat/user-chat.integration.test.d.ts.map +0 -1
  820. package/dist/plugins/user-chat/user-chat.integration.test.js +0 -754
  821. package/dist/plugins/user-chat/user-chat.integration.test.js.map +0 -1
  822. package/dist/plugins/workers/workers.integration.test.d.ts +0 -2
  823. package/dist/plugins/workers/workers.integration.test.d.ts.map +0 -1
  824. package/dist/plugins/workers/workers.integration.test.js +0 -629
  825. package/dist/plugins/workers/workers.integration.test.js.map +0 -1
  826. package/dist/prompts/macros.test.d.ts +0 -2
  827. package/dist/prompts/macros.test.d.ts.map +0 -1
  828. package/dist/prompts/macros.test.js +0 -80
  829. package/dist/prompts/macros.test.js.map +0 -1
  830. package/dist/testing/test-harness.test.d.ts +0 -2
  831. package/dist/testing/test-harness.test.d.ts.map +0 -1
  832. package/dist/testing/test-harness.test.js +0 -92
  833. package/dist/testing/test-harness.test.js.map +0 -1
  834. package/dist/transport/http/routes/rpc.integration.test.d.ts +0 -2
  835. package/dist/transport/http/routes/rpc.integration.test.d.ts.map +0 -1
  836. package/dist/transport/http/routes/rpc.integration.test.js +0 -189
  837. package/dist/transport/http/routes/rpc.integration.test.js.map +0 -1
  838. package/dist/transport/http/routes/rpc.test.d.ts +0 -2
  839. package/dist/transport/http/routes/rpc.test.d.ts.map +0 -1
  840. package/dist/transport/http/routes/rpc.test.js +0 -316
  841. package/dist/transport/http/routes/rpc.test.js.map +0 -1
  842. package/src/config.test.ts +0 -174
  843. package/src/core/agent-loop.integration.test.ts +0 -503
  844. package/src/core/agents/agent-config.test.ts +0 -240
  845. package/src/core/agents/agent-shutdown.test.ts +0 -236
  846. package/src/core/agents/agent.test.ts +0 -387
  847. package/src/core/agents/communicator.ts +0 -16
  848. package/src/core/agents/handler-events.test.ts +0 -167
  849. package/src/core/agents/response-sanitizer.test.ts +0 -137
  850. package/src/core/error-recovery.integration.test.ts +0 -343
  851. package/src/core/event-sourcing.integration.test.ts +0 -191
  852. package/src/core/events/file.test.ts +0 -436
  853. package/src/core/events/memory.test.ts +0 -741
  854. package/src/core/image/image-processor.test.ts +0 -218
  855. package/src/core/image/vips-resizer.test.ts +0 -388
  856. package/src/core/llm/anthropic.test.ts +0 -546
  857. package/src/core/llm/cache-breakpoints.test.ts +0 -85
  858. package/src/core/llm/cache-live.test.ts +0 -155
  859. package/src/core/llm/logger.test.ts +0 -270
  860. package/src/core/llm/message-sanitization.test.ts +0 -217
  861. package/src/core/llm/mock.test.ts +0 -402
  862. package/src/core/llm/openrouter-mapping.test.ts +0 -210
  863. package/src/core/llm/openrouter.test.ts +0 -499
  864. package/src/core/llm/provider-integration.test.ts +0 -408
  865. package/src/core/llm/routing-provider.test.ts +0 -113
  866. package/src/core/llm/snapshot-middleware.test.ts +0 -168
  867. package/src/core/multi-agent.integration.test.ts +0 -340
  868. package/src/core/plugin-hooks.integration.test.ts +0 -568
  869. package/src/core/session-lifecycle.integration.test.ts +0 -202
  870. package/src/core/sessions/fork-utils.test.ts +0 -158
  871. package/src/core/sessions/session-overrides.integration.test.ts +0 -210
  872. package/src/core/sessions/session.test.ts +0 -1736
  873. package/src/core/tools/executor.test.ts +0 -161
  874. package/src/lib/logger/console.test.ts +0 -348
  875. package/src/lib/never.ts +0 -3
  876. package/src/lib/utils/concurrency.test.ts +0 -169
  877. package/src/plugins/agent-status/agent-status.test.ts +0 -164
  878. package/src/plugins/agents/agents.integration.test.ts +0 -976
  879. package/src/plugins/agents/supervision.integration.test.ts +0 -249
  880. package/src/plugins/context-compact/compaction-live.test.ts +0 -221
  881. package/src/plugins/context-compact/context-compact.integration.test.ts +0 -418
  882. package/src/plugins/context-compact/context-compactor.test.ts +0 -1036
  883. package/src/plugins/context-compact/history-offloader.test.ts +0 -100
  884. package/src/plugins/filesystem/filesystem.integration.test.ts +0 -529
  885. package/src/plugins/filesystem/schema.ts +0 -6
  886. package/src/plugins/limits-guard/limit-guard.test.ts +0 -253
  887. package/src/plugins/limits-guard/limits-guard.integration.test.ts +0 -1012
  888. package/src/plugins/llm-debug/llm-debug.integration.test.ts +0 -192
  889. package/src/plugins/mailbox/mailbox.integration.test.ts +0 -794
  890. package/src/plugins/resources/resources.integration.test.ts +0 -68
  891. package/src/plugins/result-eviction/result-eviction.integration.test.ts +0 -332
  892. package/src/plugins/services/pid-registry.test.ts +0 -180
  893. package/src/plugins/services/services.integration.test.ts +0 -1223
  894. package/src/plugins/session-lifecycle/session-lifecycle.integration.test.ts +0 -639
  895. package/src/plugins/session-stats/plugin.test.ts +0 -160
  896. package/src/plugins/shell/shell.integration.test.ts +0 -273
  897. package/src/plugins/shell/shell.test.ts +0 -314
  898. package/src/plugins/skills/discovery.test.ts +0 -395
  899. package/src/plugins/skills/skills.integration.test.ts +0 -580
  900. package/src/plugins/todo/todo.integration.test.ts +0 -687
  901. package/src/plugins/uploads/preprocessors/image-classifier.test.ts +0 -142
  902. package/src/plugins/uploads/uploads.integration.test.ts +0 -831
  903. package/src/plugins/user-chat/user-chat.integration.test.ts +0 -893
  904. package/src/plugins/workers/workers.integration.test.ts +0 -778
  905. package/src/prompts/macros.test.ts +0 -107
  906. package/src/testing/test-harness.test.ts +0 -114
  907. package/src/transport/http/routes/rpc.integration.test.ts +0 -243
  908. package/src/transport/http/routes/rpc.test.ts +0 -372
@@ -1,1223 +0,0 @@
1
- import { afterEach, describe, expect, it } from 'bun:test'
2
- import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
- import { MemoryEventStore } from '~/core/events/memory.js'
6
- import { MockLLMProvider } from '~/core/llm/mock.js'
7
- import { selectPluginState } from '~/core/sessions/reducer.js'
8
- import { SessionId } from '~/core/sessions/schema.js'
9
- import { ToolCallId } from '~/core/tools/schema.js'
10
- import { silentLogger } from '~/lib/logger/logger.js'
11
- import { createNodePlatform } from '~/testing/node-platform.js'
12
- import { createTestPreset, TestHarness } from '~/testing/index.js'
13
- import { serviceEvents, servicePlugin } from './plugin.js'
14
- import type { ServiceAgentConfig, ServicePluginConfig } from './plugin.js'
15
- import { PortPool } from './port-pool.js'
16
- import { buildServiceStatusMessage } from './prompt.js'
17
- import type { ServiceCommandArgs, ServiceConfig, ServiceCwdArgs, ServiceEntry, ServiceStatus } from './schema.js'
18
- import type { ServiceStatusChangeDetails } from './service.js'
19
- import { ServiceExecutor } from './service.js'
20
-
21
- // ============================================================================
22
- // Test Service Configs
23
- // ============================================================================
24
-
25
- const quickService: ServiceConfig = {
26
- type: 'quick',
27
- description: 'Quick service that becomes ready immediately',
28
- command: 'echo "service output" && sleep 60',
29
- }
30
-
31
- const readyPatternService: ServiceConfig = {
32
- type: 'ready-service',
33
- description: 'Service with ready pattern',
34
- command: 'echo "Listening READY" && sleep 60',
35
- readyPattern: 'READY',
36
- }
37
-
38
- const commandCallbackService: ServiceConfig = {
39
- type: 'callback-service',
40
- description: 'Service with command callback that uses allocated port',
41
- command: ({ port }) => `echo "Server on port ${port}" && sleep 60`,
42
- readyPattern: 'Server on port',
43
- }
44
-
45
- const failingService: ServiceConfig = {
46
- type: 'failing',
47
- description: 'Service that exits immediately',
48
- command: 'exit 1',
49
- }
50
-
51
- const autoStartService: ServiceConfig = {
52
- type: 'auto-start',
53
- description: 'Auto-starting service',
54
- command: 'sleep 60',
55
- autoStart: true,
56
- }
57
-
58
- // ============================================================================
59
- // Helpers
60
- // ============================================================================
61
-
62
- let currentHarness: TestHarness | undefined
63
-
64
- afterEach(async () => {
65
- if (currentHarness) {
66
- await currentHarness.shutdown()
67
- currentHarness = undefined
68
- }
69
- })
70
-
71
- function createServicesPreset(
72
- services: ServiceConfig[],
73
- agentServices: string[],
74
- portPool: PortPool,
75
- overrides?: Parameters<typeof createTestPreset>[0],
76
- ) {
77
- return createTestPreset({
78
- ...overrides,
79
- plugins: [
80
- servicePlugin.configure({ services, portPool }),
81
- ...(overrides?.plugins ?? []),
82
- ],
83
- orchestratorPlugins: [
84
- servicePlugin.configureAgent({ services: agentServices }),
85
- ...(overrides?.orchestratorPlugins ?? []),
86
- ],
87
- })
88
- }
89
-
90
- function createServicesHarness(options: Omit<ConstructorParameters<typeof TestHarness>[0], 'systemPlugins'>) {
91
- const harness = new TestHarness({ ...options, systemPlugins: [servicePlugin] })
92
- currentHarness = harness
93
- return harness
94
- }
95
-
96
- /** Wait for a service status change event of a specific type */
97
- async function waitForServiceStatus(
98
- session: Awaited<ReturnType<TestHarness['createSession']>>,
99
- serviceType: string,
100
- targetStatus: string,
101
- timeoutMs = 5000,
102
- ): Promise<void> {
103
- const deadline = Date.now() + timeoutMs
104
- while (Date.now() < deadline) {
105
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
106
- if (events.some((e) => e.serviceType === serviceType && e.toStatus === targetStatus)) {
107
- return
108
- }
109
- await new Promise((r) => setTimeout(r, 50))
110
- }
111
- }
112
-
113
- /** Wait for the services plugin state slice to reflect a given status for a serviceType */
114
- async function waitForServiceStateStatus(
115
- session: Awaited<ReturnType<TestHarness['createSession']>>,
116
- serviceType: string,
117
- targetStatus: string,
118
- timeoutMs = 5000,
119
- ): Promise<void> {
120
- const deadline = Date.now() + timeoutMs
121
- while (Date.now() < deadline) {
122
- const entry = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get(serviceType)
123
- if (entry?.status === targetStatus) return
124
- await new Promise((r) => setTimeout(r, 20))
125
- }
126
- }
127
-
128
- // ============================================================================
129
- // Tests
130
- // ============================================================================
131
-
132
- describe('services plugin', () => {
133
- // =========================================================================
134
- // Service lifecycle via methods
135
- // =========================================================================
136
-
137
- describe('service lifecycle via methods', () => {
138
- it('start → status_changed events (starting, ready) → state updated', async () => {
139
- const portPool = new PortPool()
140
- const harness = createServicesHarness({
141
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
142
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
143
- })
144
-
145
- const session = await harness.createSession('test')
146
- await session.sendAndWaitForIdle('Hi')
147
-
148
- const entryAgentId = session.getEntryAgentId()!
149
- const result = await session.callPluginMethod('services.start', {
150
- sessionId: String(session.sessionId),
151
- agentId: String(entryAgentId),
152
- serviceType: 'quick',
153
- })
154
- expect(result.ok).toBe(true)
155
-
156
- // Wait for ready (no readyPattern = immediately ready)
157
- await waitForServiceStatus(session, 'quick', 'ready')
158
-
159
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
160
- const quickEvents = events.filter((e) => e.serviceType === 'quick')
161
- const statuses = quickEvents.map((e) => e.toStatus)
162
- expect(statuses).toContain('starting')
163
- expect(statuses).toContain('ready')
164
-
165
- // Verify state has port from pool
166
- const serviceState = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('quick')
167
- expect(serviceState).toBeDefined()
168
- expect(serviceState!.status).toBe('ready')
169
- expect(serviceState!.port).toBeGreaterThanOrEqual(10000)
170
- expect(serviceState!.port).toBeLessThanOrEqual(49151)
171
- })
172
-
173
- it('stop running service → status stopped', async () => {
174
- const portPool = new PortPool()
175
- const harness = createServicesHarness({
176
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
177
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
178
- })
179
-
180
- const session = await harness.createSession('test')
181
- await session.sendAndWaitForIdle('Hi')
182
-
183
- const entryAgentId = session.getEntryAgentId()!
184
- await session.callPluginMethod('services.start', {
185
- sessionId: String(session.sessionId),
186
- agentId: String(entryAgentId),
187
- serviceType: 'quick',
188
- })
189
- await waitForServiceStatus(session, 'quick', 'ready')
190
-
191
- const stopResult = await session.callPluginMethod('services.stop', {
192
- sessionId: String(session.sessionId),
193
- agentId: String(entryAgentId),
194
- serviceType: 'quick',
195
- })
196
- expect(stopResult.ok).toBe(true)
197
-
198
- await waitForServiceStatus(session, 'quick', 'stopped')
199
-
200
- const serviceState = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('quick')
201
- expect(serviceState!.status).toBe('stopped')
202
- })
203
-
204
- it('start already running service → idempotent', async () => {
205
- const portPool = new PortPool()
206
- const harness = createServicesHarness({
207
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
208
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
209
- })
210
-
211
- const session = await harness.createSession('test')
212
- await session.sendAndWaitForIdle('Hi')
213
-
214
- const entryAgentId = session.getEntryAgentId()!
215
- await session.callPluginMethod('services.start', {
216
- sessionId: String(session.sessionId),
217
- agentId: String(entryAgentId),
218
- serviceType: 'quick',
219
- })
220
- await waitForServiceStatus(session, 'quick', 'ready')
221
-
222
- // Start again — should be idempotent
223
- const result = await session.callPluginMethod('services.start', {
224
- sessionId: String(session.sessionId),
225
- agentId: String(entryAgentId),
226
- serviceType: 'quick',
227
- })
228
- expect(result.ok).toBe(true)
229
- })
230
- })
231
-
232
- // =========================================================================
233
- // Port pool allocation
234
- // =========================================================================
235
-
236
- describe('port pool allocation', () => {
237
- it('starting event includes allocated port', async () => {
238
- const portPool = new PortPool()
239
- const harness = createServicesHarness({
240
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
241
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
242
- })
243
-
244
- const session = await harness.createSession('test')
245
- await session.sendAndWaitForIdle('Hi')
246
-
247
- const entryAgentId = session.getEntryAgentId()!
248
- await session.callPluginMethod('services.start', {
249
- sessionId: String(session.sessionId),
250
- agentId: String(entryAgentId),
251
- serviceType: 'quick',
252
- })
253
- await waitForServiceStatus(session, 'quick', 'ready')
254
-
255
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
256
- const startingEvent = events.find((e) => e.serviceType === 'quick' && e.toStatus === 'starting')
257
- expect(startingEvent).toBeDefined()
258
- expect(startingEvent!.port).toBeGreaterThanOrEqual(10000)
259
- })
260
-
261
- it('command callback receives allocated port', async () => {
262
- const portPool = new PortPool()
263
- const harness = createServicesHarness({
264
- presets: [createServicesPreset([commandCallbackService], ['callback-service'], portPool)],
265
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
266
- })
267
-
268
- const session = await harness.createSession('test')
269
- await session.sendAndWaitForIdle('Hi')
270
-
271
- const entryAgentId = session.getEntryAgentId()!
272
- await session.callPluginMethod('services.start', {
273
- sessionId: String(session.sessionId),
274
- agentId: String(entryAgentId),
275
- serviceType: 'callback-service',
276
- })
277
- await waitForServiceStatus(session, 'callback-service', 'ready')
278
-
279
- // Port should be in state from starting event
280
- const serviceState = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('callback-service')
281
- expect(serviceState!.port).toBeGreaterThanOrEqual(10000)
282
- })
283
-
284
- it('resolves cwd, command, and env callbacks with runtime context', async () => {
285
- const workspaceDir = await mkdtemp(join(tmpdir(), 'roj-svc-runtime-'))
286
- const webDir = join(workspaceDir, 'packages', 'web')
287
- await mkdir(webDir, { recursive: true })
288
-
289
- let cwdArgs: ServiceCwdArgs | undefined
290
- let commandArgs: ServiceCommandArgs | undefined
291
- let envArgs: ServiceCommandArgs | undefined
292
-
293
- const runtimeService: ServiceConfig = {
294
- type: 'runtime',
295
- description: 'Service with runtime resolvers',
296
- cwd: (args) => {
297
- cwdArgs = args
298
- return 'packages/web'
299
- },
300
- command: async (args) => {
301
- commandArgs = args
302
- return `printf "%s" "$RUNTIME_MARKER" > marker.txt && echo "READY ${args.port}" && sleep 60`
303
- },
304
- env: (args) => {
305
- envArgs = args
306
- return {
307
- RUNTIME_MARKER: `${args.sessionId}|${args.workspaceDir}|${args.cwd}|${args.port}`,
308
- }
309
- },
310
- readyPattern: 'READY',
311
- }
312
-
313
- try {
314
- const portPool = new PortPool()
315
- const harness = createServicesHarness({
316
- presets: [createServicesPreset([runtimeService], ['runtime'], portPool, { workspaceDir })],
317
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
318
- })
319
-
320
- const session = await harness.createSession('test')
321
- const entryAgentId = session.getEntryAgentId()!
322
- const startResult = await session.callPluginMethod('services.start', {
323
- sessionId: String(session.sessionId),
324
- agentId: String(entryAgentId),
325
- serviceType: 'runtime',
326
- })
327
- expect(startResult.ok).toBe(true)
328
- await waitForServiceStatus(session, 'runtime', 'ready')
329
-
330
- const serviceState = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('runtime')
331
- expect(cwdArgs?.workspaceDir).toBe(workspaceDir)
332
- expect(cwdArgs?.sessionId).toBe(String(session.sessionId))
333
- expect(commandArgs?.cwd).toBe(webDir)
334
- expect(commandArgs?.workspaceDir).toBe(workspaceDir)
335
- expect(envArgs?.cwd).toBe(webDir)
336
- expect(serviceState?.cwd).toBe(webDir)
337
- expect(serviceState?.command).toContain('RUNTIME_MARKER')
338
-
339
- const marker = await readFile(join(webDir, 'marker.txt'), 'utf-8')
340
- expect(marker).toContain(String(session.sessionId))
341
- expect(marker).toContain(workspaceDir)
342
- expect(marker).toContain(webDir)
343
-
344
- const statusResult = await session.callPluginMethod('services.status', {
345
- sessionId: String(session.sessionId),
346
- agentId: String(entryAgentId),
347
- serviceType: 'runtime',
348
- })
349
- expect(statusResult).toMatchObject({
350
- ok: true,
351
- value: {
352
- status: 'ready',
353
- cwd: webDir,
354
- command: serviceState?.command,
355
- },
356
- })
357
- } finally {
358
- await rm(workspaceDir, { recursive: true, force: true })
359
- }
360
- })
361
- })
362
-
363
- // =========================================================================
364
- // Service tools
365
- // =========================================================================
366
-
367
- describe('service tools', () => {
368
- it('agent calls service_start tool → service starts', async () => {
369
- const portPool = new PortPool()
370
- const harness = createServicesHarness({
371
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
372
- llmProvider: MockLLMProvider.withSequence([
373
- {
374
- toolCalls: [{
375
- id: ToolCallId('tc1'),
376
- name: 'service_start',
377
- input: { serviceType: 'quick' },
378
- }],
379
- },
380
- { content: 'Done', toolCalls: [] },
381
- ]),
382
- })
383
-
384
- const session = await harness.createSession('test')
385
- await session.sendAndWaitForIdle('Start service')
386
-
387
- await waitForServiceStatus(session, 'quick', 'ready')
388
-
389
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
390
- const quickEvents = events.filter((e) => e.serviceType === 'quick')
391
- expect(quickEvents.length).toBeGreaterThanOrEqual(1)
392
- })
393
-
394
- it('agent calls service_status → returns status info', async () => {
395
- const portPool = new PortPool()
396
- const harness = createServicesHarness({
397
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
398
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
399
- })
400
-
401
- const session = await harness.createSession('test')
402
- await session.sendAndWaitForIdle('Hi')
403
-
404
- const entryAgentId = session.getEntryAgentId()!
405
-
406
- // Start service
407
- await session.callPluginMethod('services.start', {
408
- sessionId: String(session.sessionId),
409
- agentId: String(entryAgentId),
410
- serviceType: 'quick',
411
- })
412
- await waitForServiceStatus(session, 'quick', 'ready')
413
-
414
- // Get status
415
- const statusResult = await session.callPluginMethod('services.status', {
416
- sessionId: String(session.sessionId),
417
- agentId: String(entryAgentId),
418
- serviceType: 'quick',
419
- })
420
- expect(statusResult).toMatchObject({ ok: true, value: { status: 'ready' } })
421
- })
422
-
423
- it('service not in agent visible list → error', async () => {
424
- const portPool = new PortPool()
425
- const harness = createServicesHarness({
426
- presets: [createServicesPreset([quickService], [], portPool)], // empty visible list
427
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
428
- })
429
-
430
- const session = await harness.createSession('test')
431
- await session.sendAndWaitForIdle('Hi')
432
-
433
- // No service tools should be available
434
- const lastRequest = harness.llmProvider.getLastRequest()
435
- const toolNames = lastRequest?.tools?.map((t) => t.name) ?? []
436
- expect(toolNames).not.toContain('service_start')
437
- expect(toolNames).not.toContain('service_stop')
438
- })
439
- })
440
-
441
- // =========================================================================
442
- // Auto-start on session ready
443
- // =========================================================================
444
-
445
- describe('auto-start on session ready', () => {
446
- it('service with autoStart: true → started on session creation', async () => {
447
- const portPool = new PortPool()
448
- const harness = createServicesHarness({
449
- presets: [createServicesPreset([autoStartService], ['auto-start'], portPool)],
450
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
451
- })
452
-
453
- const session = await harness.createSession('test')
454
- // Session creation triggers onSessionReady → autoStart services
455
-
456
- await waitForServiceStatus(session, 'auto-start', 'ready')
457
-
458
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
459
- const autoEvents = events.filter((e) => e.serviceType === 'auto-start')
460
- expect(autoEvents.length).toBeGreaterThanOrEqual(1)
461
- })
462
-
463
- it('availableWhen false skips auto-start and explicit start returns an error', async () => {
464
- const unavailableService: ServiceConfig = {
465
- type: 'unavailable',
466
- description: 'Unavailable service',
467
- command: 'sleep 60',
468
- autoStart: true,
469
- availableWhen: () => false,
470
- }
471
- const portPool = new PortPool()
472
- const harness = createServicesHarness({
473
- presets: [createServicesPreset([unavailableService], ['unavailable'], portPool)],
474
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
475
- })
476
-
477
- const session = await harness.createSession('test')
478
- await new Promise((resolve) => setTimeout(resolve, 100))
479
-
480
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
481
- expect(events.some((event) => event.serviceType === 'unavailable')).toBe(false)
482
-
483
- const entryAgentId = session.getEntryAgentId()!
484
- const result = await session.callPluginMethod('services.start', {
485
- sessionId: String(session.sessionId),
486
- agentId: String(entryAgentId),
487
- serviceType: 'unavailable',
488
- })
489
- expect(result.ok).toBe(false)
490
- })
491
- })
492
-
493
- // =========================================================================
494
- // Service failure
495
- // =========================================================================
496
-
497
- describe('service failure', () => {
498
- it('service that exits immediately → status failed with error', async () => {
499
- const portPool = new PortPool()
500
- const harness = createServicesHarness({
501
- presets: [createServicesPreset([failingService], ['failing'], portPool)],
502
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
503
- })
504
-
505
- const session = await harness.createSession('test')
506
- await session.sendAndWaitForIdle('Hi')
507
-
508
- const entryAgentId = session.getEntryAgentId()!
509
- await session.callPluginMethod('services.start', {
510
- sessionId: String(session.sessionId),
511
- agentId: String(entryAgentId),
512
- serviceType: 'failing',
513
- })
514
-
515
- await waitForServiceStatus(session, 'failing', 'failed')
516
-
517
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
518
- const failEvent = events.find((e) => e.serviceType === 'failing' && e.toStatus === 'failed')
519
- expect(failEvent).toBeDefined()
520
- expect(failEvent!.error).toBeDefined()
521
- })
522
- })
523
-
524
- // =========================================================================
525
- // Ready pattern
526
- // =========================================================================
527
-
528
- describe('ready pattern', () => {
529
- it('service with readyPattern → status ready when pattern matches', async () => {
530
- const portPool = new PortPool()
531
- const harness = createServicesHarness({
532
- presets: [createServicesPreset([readyPatternService], ['ready-service'], portPool)],
533
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
534
- })
535
-
536
- const session = await harness.createSession('test')
537
- await session.sendAndWaitForIdle('Hi')
538
-
539
- const entryAgentId = session.getEntryAgentId()!
540
- await session.callPluginMethod('services.start', {
541
- sessionId: String(session.sessionId),
542
- agentId: String(entryAgentId),
543
- serviceType: 'ready-service',
544
- })
545
-
546
- await waitForServiceStatus(session, 'ready-service', 'ready')
547
-
548
- const serviceState = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('ready-service')
549
- expect(serviceState!.status).toBe('ready')
550
- })
551
-
552
- it('readyWhen can mark a service ready without output pattern', async () => {
553
- let checks = 0
554
- const readyWhenService: ServiceConfig = {
555
- type: 'ready-when',
556
- description: 'Service with callback readiness',
557
- command: 'sleep 60',
558
- readyWhen: () => {
559
- checks += 1
560
- return checks > 1
561
- },
562
- readyCheckIntervalMs: 20,
563
- startupTimeoutMs: 1000,
564
- }
565
- const portPool = new PortPool()
566
- const harness = createServicesHarness({
567
- presets: [createServicesPreset([readyWhenService], ['ready-when'], portPool)],
568
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
569
- })
570
-
571
- const session = await harness.createSession('test')
572
- const entryAgentId = session.getEntryAgentId()!
573
- const result = await session.callPluginMethod('services.start', {
574
- sessionId: String(session.sessionId),
575
- agentId: String(entryAgentId),
576
- serviceType: 'ready-when',
577
- })
578
- expect(result.ok).toBe(true)
579
-
580
- await waitForServiceStatus(session, 'ready-when', 'ready')
581
- expect(checks).toBeGreaterThan(1)
582
- })
583
- })
584
-
585
- // =========================================================================
586
- // Server restart reconcile
587
- // =========================================================================
588
-
589
- describe('onSessionReady reconcile after server restart', () => {
590
- it('preserves port in state when service transitions to stopped', async () => {
591
- const portPool = new PortPool()
592
- const harness = createServicesHarness({
593
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
594
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
595
- })
596
-
597
- const session = await harness.createSession('test')
598
- await session.sendAndWaitForIdle('Hi')
599
-
600
- const entryAgentId = session.getEntryAgentId()!
601
- await session.callPluginMethod('services.start', {
602
- sessionId: String(session.sessionId),
603
- agentId: String(entryAgentId),
604
- serviceType: 'quick',
605
- })
606
- await waitForServiceStateStatus(session, 'quick', 'ready')
607
-
608
- const portBefore = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('quick')?.port
609
- expect(portBefore).toBeDefined()
610
-
611
- await session.callPluginMethod('services.stop', {
612
- sessionId: String(session.sessionId),
613
- agentId: String(entryAgentId),
614
- serviceType: 'quick',
615
- })
616
- await waitForServiceStateStatus(session, 'quick', 'stopped')
617
-
618
- const stopped = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('quick')
619
- expect(stopped?.status).toBe('stopped')
620
- expect(stopped?.port).toBe(portBefore!)
621
- expect(stopped?.pid).toBeUndefined()
622
- })
623
-
624
- it('kills orphaned process group from previous server instance', async () => {
625
- const eventStore = new MemoryEventStore()
626
-
627
- // Harness 1: start service, capture pid + port, then "crash" (shutdown
628
- // without running onSessionClose — matches session.shutdown() behavior).
629
- const harness1 = new TestHarness({
630
- presets: [createServicesPreset([quickService], ['quick'], new PortPool())],
631
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
632
- systemPlugins: [servicePlugin],
633
- eventStore,
634
- })
635
-
636
- const session1 = await harness1.createSession('test')
637
- await session1.sendAndWaitForIdle('Hi')
638
-
639
- const entryAgentId = session1.getEntryAgentId()!
640
- await session1.callPluginMethod('services.start', {
641
- sessionId: String(session1.sessionId),
642
- agentId: String(entryAgentId),
643
- serviceType: 'quick',
644
- })
645
- await waitForServiceStateStatus(session1, 'quick', 'ready')
646
-
647
- const stateBefore = selectPluginState<Map<string, ServiceEntry>>(session1.state, 'services')?.get('quick')
648
- const orphanPid = stateBefore?.pid
649
- const orphanPort = stateBefore?.port
650
- expect(orphanPid).toBeDefined()
651
- expect(orphanPort).toBeDefined()
652
-
653
- // Process should be alive before restart
654
- expect(() => process.kill(orphanPid!, 0)).not.toThrow()
655
-
656
- const sessionId = session1.sessionId
657
-
658
- // Simulate server crash: sessionManager.shutdown() clears in-memory state
659
- // but does NOT run onSessionClose, so the detached service process survives.
660
- await harness1.sessionManager.shutdown()
661
-
662
- // Orphan must still be alive after "crash"
663
- expect(() => process.kill(orphanPid!, 0)).not.toThrow()
664
-
665
- // Harness 2: fresh SessionManager over the same event store
666
- const harness2 = new TestHarness({
667
- presets: [createServicesPreset([quickService], ['quick'], new PortPool())],
668
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
669
- systemPlugins: [servicePlugin],
670
- eventStore,
671
- })
672
- currentHarness = harness2
673
-
674
- // Opening the session fires onSessionReady → reconcile kills the orphan
675
- const session2 = await harness2.openSession(sessionId)
676
-
677
- await waitForServiceStateStatus(session2, 'quick', 'stopped', 3000)
678
-
679
- // Give the OS a beat to reap the killed process
680
- for (let i = 0; i < 20; i++) {
681
- try {
682
- process.kill(orphanPid!, 0)
683
- } catch {
684
- break
685
- }
686
- await new Promise((r) => setTimeout(r, 50))
687
- }
688
-
689
- let isAlive = true
690
- try {
691
- process.kill(orphanPid!, 0)
692
- } catch {
693
- isAlive = false
694
- }
695
- // Safety net in case reconcile didn't kill it — don't leave zombies behind
696
- if (isAlive) {
697
- try {
698
- process.kill(-orphanPid!, 'SIGKILL')
699
- } catch {
700
- // already gone
701
- }
702
- }
703
- expect(isAlive).toBe(false)
704
-
705
- // Port preserved in state — next start() would receive it via preferredPort
706
- const stateAfter = selectPluginState<Map<string, ServiceEntry>>(session2.state, 'services')?.get('quick')
707
- expect(stateAfter?.status).toBe('stopped')
708
- expect(stateAfter?.port).toBe(orphanPort!)
709
- expect(stateAfter?.pid).toBeUndefined()
710
- })
711
- })
712
-
713
- describe('session close cleanup', () => {
714
- it('closing session stops running services', async () => {
715
- const portPool = new PortPool()
716
- const harness = createServicesHarness({
717
- presets: [createServicesPreset([quickService], ['quick'], portPool)],
718
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
719
- })
720
-
721
- const session = await harness.createSession('test')
722
- await session.sendAndWaitForIdle('Hi')
723
-
724
- const entryAgentId = session.getEntryAgentId()!
725
- await session.callPluginMethod('services.start', {
726
- sessionId: String(session.sessionId),
727
- agentId: String(entryAgentId),
728
- serviceType: 'quick',
729
- })
730
- await waitForServiceStatus(session, 'quick', 'ready')
731
-
732
- // Close session → should stop services
733
- await session.close()
734
-
735
- // Wait for stopped event
736
- await waitForServiceStatus(session, 'quick', 'stopped', 10000)
737
-
738
- const events = await session.getEventsByType(serviceEvents, 'service_status_changed')
739
- const stoppedEvent = events.find((e) => e.serviceType === 'quick' && e.toStatus === 'stopped')
740
- expect(stoppedEvent).toBeDefined()
741
- })
742
- })
743
-
744
- // =========================================================================
745
- // Port conflict recovery + concurrent start (ServiceExecutor unit-level)
746
- // =========================================================================
747
-
748
- describe('port conflict recovery and concurrent start', () => {
749
- const waitUntil = async (predicate: () => boolean, timeoutMs = 8000): Promise<void> => {
750
- const deadline = Date.now() + timeoutMs
751
- while (Date.now() < deadline) {
752
- if (predicate()) return
753
- await new Promise((r) => setTimeout(r, 20))
754
- }
755
- }
756
-
757
- it('collapses concurrent start() calls onto a single process', async () => {
758
- const platform = createNodePlatform()
759
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
760
- const marker = join(tmpdir(), `roj-svc-mutex-${Date.now()}-${Math.floor(Math.random() * 1e6)}`)
761
- const config: ServiceConfig = {
762
- type: 'concurrent',
763
- description: 'appends one line per spawned process',
764
- command: `echo spawned >> ${marker} && sleep 30`,
765
- }
766
-
767
- try {
768
- const [r1, r2] = await Promise.all([
769
- executor.start(config, SessionId('s-concurrent')),
770
- executor.start(config, SessionId('s-concurrent')),
771
- ])
772
- expect(r1.ok).toBe(true)
773
- expect(r2.ok).toBe(true)
774
-
775
- // Let the shell flush its append.
776
- await new Promise((r) => setTimeout(r, 400))
777
- const lines = (await readFile(marker, 'utf-8')).split('\n').filter((l) => l.length > 0)
778
- // Without the in-flight lock both starts spawn and the file gets two lines.
779
- expect(lines.length).toBe(1)
780
- } finally {
781
- await executor.shutdown()
782
- await rm(marker, { force: true })
783
- }
784
- })
785
-
786
- it('recovers on a fresh port when the chosen port is in use (EADDRINUSE)', async () => {
787
- const platform = createNodePlatform()
788
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
789
- const marker = join(tmpdir(), `roj-svc-eaddr-${Date.now()}-${Math.floor(Math.random() * 1e6)}`)
790
- // First spawn fails with an EADDRINUSE-style message; the marker makes the
791
- // retry (a fresh process) succeed and match the ready pattern.
792
- const config: ServiceConfig = {
793
- type: 'flaky-port',
794
- description: 'EADDRINUSE on first attempt, ready on retry',
795
- command:
796
- `if [ -f ${marker} ]; then echo "listening READY"; sleep 30; else touch ${marker}; echo "error: listen EADDRINUSE: address already in use" 1>&2; exit 1; fi`,
797
- readyPattern: 'READY',
798
- startupTimeoutMs: 5000,
799
- }
800
-
801
- const observed: Array<{ status: ServiceStatus; port?: number }> = []
802
- executor.onStatusChanged = (_sessionId, _serviceType, status, details) => {
803
- observed.push({ status, port: details.port })
804
- }
805
-
806
- try {
807
- const result = await executor.start(config, SessionId('s-flaky'))
808
- expect(result.ok).toBe(true)
809
-
810
- await waitUntil(() => executor.getStatus('flaky-port') === 'ready')
811
- expect(executor.getStatus('flaky-port')).toBe('ready')
812
-
813
- // Two 'starting' events: the conflicted port, then a fresh, different one.
814
- const startingPorts = observed.filter((e) => e.status === 'starting').map((e) => e.port)
815
- expect(startingPorts.length).toBe(2)
816
- expect(startingPorts[0]).not.toBe(startingPorts[1])
817
-
818
- // The transient EADDRINUSE must NOT surface as a terminal failure.
819
- expect(observed.some((e) => e.status === 'failed')).toBe(false)
820
- } finally {
821
- await executor.shutdown()
822
- await rm(marker, { force: true })
823
- }
824
- })
825
- })
826
-
827
- // =========================================================================
828
- // restartPolicy — reviving a service that exited on its own
829
- // =========================================================================
830
-
831
- describe('restartPolicy', () => {
832
- const waitUntil = async (predicate: () => boolean, timeoutMs = 8000): Promise<void> => {
833
- const deadline = Date.now() + timeoutMs
834
- while (Date.now() < deadline) {
835
- if (predicate()) return
836
- await new Promise((r) => setTimeout(r, 20))
837
- }
838
- }
839
-
840
- const waitUntilAsync = async (predicate: () => Promise<boolean>, timeoutMs = 8000): Promise<void> => {
841
- const deadline = Date.now() + timeoutMs
842
- while (Date.now() < deadline) {
843
- if (await predicate()) return
844
- await new Promise((r) => setTimeout(r, 20))
845
- }
846
- }
847
-
848
- const marker = (name: string) => join(tmpdir(), `roj-restart-${name}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`)
849
- /** Records every spawn, then crashes or stays up depending on the run count. */
850
- const crashCommand = (path: string, crashRuns: number) =>
851
- `echo x >> ${path}; runs=$(wc -l < ${path} | tr -d ' '); if [ "$runs" -le ${crashRuns} ]; then exit 1; fi; echo "listening READY"; sleep 30`
852
- const spawnCount = async (path: string): Promise<number> => {
853
- try {
854
- return (await readFile(path, 'utf-8')).split('\n').filter((l) => l.length > 0).length
855
- } catch {
856
- return 0
857
- }
858
- }
859
-
860
- it('revives a crashed service on the port it already holds', async () => {
861
- const platform = createNodePlatform()
862
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
863
- const path = marker('revive')
864
- const config: ServiceConfig = {
865
- type: 'revive-svc',
866
- description: 'crashes once, then stays up',
867
- command: crashCommand(path, 1),
868
- readyPattern: 'READY',
869
- startupTimeoutMs: 5000,
870
- restartPolicy: { maxRetries: 3, initialDelayMs: 20 },
871
- }
872
- const ports: number[] = []
873
- executor.onStatusChanged = (_s, _t, status, details) => {
874
- if (status === 'starting' && details.port !== undefined) ports.push(details.port)
875
- }
876
-
877
- try {
878
- await executor.start(config, SessionId('s-revive'))
879
- await waitUntil(() => executor.getStatus('revive-svc') === 'ready')
880
- expect(executor.getStatus('revive-svc')).toBe('ready')
881
- expect(await spawnCount(path)).toBe(2)
882
- // The preview URL must survive the bounce.
883
- expect(new Set(ports).size).toBe(1)
884
- } finally {
885
- await executor.shutdown()
886
- await rm(path, { force: true })
887
- }
888
- })
889
-
890
- it('leaves the service failed once the retry budget is spent', async () => {
891
- const platform = createNodePlatform()
892
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
893
- const path = marker('budget')
894
- const config: ServiceConfig = {
895
- type: 'budget-svc',
896
- description: 'always crashes',
897
- command: crashCommand(path, 99),
898
- readyPattern: 'READY',
899
- startupTimeoutMs: 5000,
900
- restartPolicy: { maxRetries: 2, initialDelayMs: 20 },
901
- }
902
-
903
- try {
904
- await executor.start(config, SessionId('s-budget'))
905
- // One initial spawn plus two retries, then it stays down.
906
- await waitUntilAsync(async () => (await spawnCount(path)) >= 3 && executor.getStatus('budget-svc') === 'failed')
907
- await new Promise((r) => setTimeout(r, 300))
908
- expect(await spawnCount(path)).toBe(3)
909
- expect(executor.getStatus('budget-svc')).toBe('failed')
910
- } finally {
911
- await executor.shutdown()
912
- await rm(path, { force: true })
913
- }
914
- })
915
-
916
- it('without a policy a crash stays a crash', async () => {
917
- const platform = createNodePlatform()
918
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
919
- const path = marker('nopolicy')
920
- const config: ServiceConfig = {
921
- type: 'nopolicy-svc',
922
- description: 'always crashes, no restartPolicy',
923
- command: crashCommand(path, 99),
924
- readyPattern: 'READY',
925
- startupTimeoutMs: 5000,
926
- }
927
-
928
- try {
929
- await executor.start(config, SessionId('s-nopolicy'))
930
- await waitUntil(() => executor.getStatus('nopolicy-svc') === 'failed')
931
- await new Promise((r) => setTimeout(r, 300))
932
- expect(await spawnCount(path)).toBe(1)
933
- } finally {
934
- await executor.shutdown()
935
- await rm(path, { force: true })
936
- }
937
- })
938
-
939
- it('a queued revival rides along on the failed status change', async () => {
940
- const platform = createNodePlatform()
941
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
942
- const path = marker('event')
943
- const config: ServiceConfig = {
944
- type: 'event-svc',
945
- description: 'crashes once, then stays up',
946
- command: crashCommand(path, 1),
947
- readyPattern: 'READY',
948
- startupTimeoutMs: 5000,
949
- restartPolicy: { maxRetries: 3, initialDelayMs: 20 },
950
- }
951
- const changes: Array<{ status: ServiceStatus; details: ServiceStatusChangeDetails }> = []
952
- executor.onStatusChanged = (_s, _t, status, details) => {
953
- changes.push({ status, details })
954
- }
955
-
956
- try {
957
- await executor.start(config, SessionId('s-event'))
958
- await waitUntil(() => executor.getStatus('event-svc') === 'ready')
959
-
960
- const failedIndex = changes.findIndex((c) => c.status === 'failed')
961
- expect(failedIndex).toBeGreaterThanOrEqual(0)
962
- const failed = changes[failedIndex]!
963
- expect(failed.details.restartAttempt).toBe(1)
964
- expect(failed.details.restartMaxRetries).toBe(3)
965
- expect(failed.details.restartAt).toBeGreaterThan(0)
966
-
967
- // The revival's own start must not carry the intent it just honoured.
968
- const revivalStart = changes.slice(failedIndex + 1).find((c) => c.status === 'starting')
969
- expect(revivalStart).toBeDefined()
970
- expect(revivalStart!.details.restartAt).toBeUndefined()
971
- } finally {
972
- await executor.shutdown()
973
- await rm(path, { force: true })
974
- }
975
- })
976
-
977
- it('the failure that spends the budget carries no revival', async () => {
978
- const platform = createNodePlatform()
979
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
980
- const path = marker('terminal')
981
- const config: ServiceConfig = {
982
- type: 'terminal-svc',
983
- description: 'always crashes',
984
- command: crashCommand(path, 99),
985
- readyPattern: 'READY',
986
- startupTimeoutMs: 5000,
987
- restartPolicy: { maxRetries: 1, initialDelayMs: 20 },
988
- }
989
- const failures: ServiceStatusChangeDetails[] = []
990
- executor.onStatusChanged = (_s, _t, status, details) => {
991
- if (status === 'failed') failures.push(details)
992
- }
993
-
994
- try {
995
- await executor.start(config, SessionId('s-terminal'))
996
- await waitUntilAsync(async () => (await spawnCount(path)) >= 2 && failures.length >= 2)
997
- expect(failures[0]?.restartAttempt).toBe(1)
998
- expect(failures[1]?.restartAt).toBeUndefined()
999
- expect(failures[1]?.restartAttempt).toBeUndefined()
1000
- } finally {
1001
- await executor.shutdown()
1002
- await rm(path, { force: true })
1003
- }
1004
- })
1005
-
1006
- it('session state shows the queued revival and drops it once called off', async () => {
1007
- const portPool = new PortPool()
1008
- const config: ServiceConfig = {
1009
- type: 'reviving',
1010
- description: 'crashes early, revival queued behind a long delay',
1011
- // The sleep keeps the exit from beating the executor's close listener.
1012
- command: 'sleep 0.2; exit 1',
1013
- restartPolicy: { maxRetries: 3, initialDelayMs: 3000 },
1014
- }
1015
- const harness = createServicesHarness({
1016
- presets: [createServicesPreset([config], ['reviving'], portPool)],
1017
- })
1018
- const session = await harness.createSession('test')
1019
-
1020
- await session.callPluginMethod('services.start', { serviceType: 'reviving' })
1021
- await waitForServiceStateStatus(session, 'reviving', 'failed')
1022
-
1023
- const entry = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('reviving')
1024
- expect(entry?.status).toBe('failed')
1025
- expect(entry?.restartAt).toBeGreaterThan(Date.now())
1026
- expect(entry?.restartAttempt).toBe(1)
1027
- expect(entry?.restartMaxRetries).toBe(3)
1028
-
1029
- const stopped = await session.callPluginMethod('services.stop', { serviceType: 'reviving' })
1030
- expect(stopped.ok).toBe(true)
1031
- await waitForServiceStateStatus(session, 'reviving', 'stopped')
1032
-
1033
- const after = selectPluginState<Map<string, ServiceEntry>>(session.state, 'services')?.get('reviving')
1034
- expect(after?.status).toBe('stopped')
1035
- expect(after?.restartAt).toBeUndefined()
1036
- expect(after?.restartAttempt).toBeUndefined()
1037
- })
1038
-
1039
- it('closing the session calls off a queued revival', async () => {
1040
- const portPool = new PortPool()
1041
- const path = marker('close')
1042
- const config: ServiceConfig = {
1043
- type: 'close-svc',
1044
- description: 'crashes early, revival queued behind a delay',
1045
- // The sleep keeps the exit from beating the executor's close listener.
1046
- command: `echo x >> ${path}; sleep 0.2; exit 1`,
1047
- restartPolicy: { maxRetries: 3, initialDelayMs: 800 },
1048
- }
1049
- const harness = createServicesHarness({
1050
- presets: [createServicesPreset([config], ['close-svc'], portPool)],
1051
- })
1052
- const session = await harness.createSession('test')
1053
-
1054
- try {
1055
- await session.callPluginMethod('services.start', { serviceType: 'close-svc' })
1056
- await waitForServiceStateStatus(session, 'close-svc', 'failed')
1057
- expect(await spawnCount(path)).toBe(1)
1058
-
1059
- await session.close()
1060
- // Long enough for the revival to have fired had nothing called it off.
1061
- await new Promise((r) => setTimeout(r, 1500))
1062
- expect(await spawnCount(path)).toBe(1)
1063
- } finally {
1064
- await rm(path, { force: true })
1065
- }
1066
- })
1067
-
1068
- it('an explicit stop calls off a queued revival', async () => {
1069
- const platform = createNodePlatform()
1070
- const executor = new ServiceExecutor(silentLogger, new PortPool(), { fs: platform.fs, process: platform.process })
1071
- const path = marker('stop')
1072
- const config: ServiceConfig = {
1073
- type: 'stop-svc',
1074
- description: 'always crashes, revival queued behind a long delay',
1075
- command: crashCommand(path, 99),
1076
- readyPattern: 'READY',
1077
- startupTimeoutMs: 5000,
1078
- restartPolicy: { maxRetries: 3, initialDelayMs: 1500 },
1079
- }
1080
-
1081
- try {
1082
- await executor.start(config, SessionId('s-stop'))
1083
- await waitUntil(() => executor.getStatus('stop-svc') === 'failed')
1084
- const stopped = await executor.stop('stop-svc', SessionId('s-stop'))
1085
- expect(stopped.ok).toBe(true)
1086
- expect(executor.getStatus('stop-svc')).toBe('stopped')
1087
- await new Promise((r) => setTimeout(r, 2000))
1088
- expect(await spawnCount(path)).toBe(1)
1089
- } finally {
1090
- await executor.shutdown()
1091
- await rm(path, { force: true })
1092
- }
1093
- })
1094
- })
1095
-
1096
- // =========================================================================
1097
- // agentVisible: false — infrastructure services hidden from agents
1098
- // =========================================================================
1099
-
1100
- describe('agentVisible: false services', () => {
1101
- const hiddenService: ServiceConfig = {
1102
- type: 'hidden-svc',
1103
- description: 'Infra service driven by the platform, not agents',
1104
- command: 'sleep 60',
1105
- agentVisible: false,
1106
- }
1107
-
1108
- it('auto-wired agent visibility excludes it: no tool mention, no status line, still method-controllable', async () => {
1109
- const portPool = new PortPool()
1110
- const base = createTestPreset({
1111
- plugins: [servicePlugin.configure({ services: [quickService, hiddenService], portPool })],
1112
- })
1113
- // Services attached on the agent definition (defineAgent-style auto-wiring),
1114
- // NOT via explicit configureAgent — exercises the getAgentConfig filter.
1115
- const preset = { ...base, orchestrator: { ...base.orchestrator, services: [quickService, hiddenService] } }
1116
- const harness = createServicesHarness({
1117
- presets: [preset],
1118
- llmProvider: MockLLMProvider.withFixedResponse({ content: 'Ok', toolCalls: [] }),
1119
- })
1120
-
1121
- const session = await harness.createSession('test')
1122
- await session.sendAndWaitForIdle('Hi')
1123
-
1124
- // Service tools exist (quick is visible) but never mention the hidden service
1125
- const lastRequest = harness.llmProvider.getLastRequest()
1126
- const serviceStart = lastRequest?.tools?.find((t) => t.name === 'service_start')
1127
- expect(serviceStart).toBeDefined()
1128
- expect(serviceStart?.description).toContain('quick')
1129
- expect(serviceStart?.description).not.toContain('hidden-svc')
1130
-
1131
- // The session-context status block skips it too
1132
- const serialized = JSON.stringify(lastRequest?.messages ?? [])
1133
- expect(serialized).not.toContain('hidden-svc')
1134
-
1135
- // Still fully controllable at the session level via services.* methods
1136
- const entryAgentId = session.getEntryAgentId()!
1137
- const startResult = await session.callPluginMethod('services.start', {
1138
- sessionId: String(session.sessionId),
1139
- agentId: String(entryAgentId),
1140
- serviceType: 'hidden-svc',
1141
- })
1142
- expect(startResult).toMatchObject({ ok: true })
1143
- await waitForServiceStatus(session, 'hidden-svc', 'ready')
1144
-
1145
- const statusResult = await session.callPluginMethod('services.status', {
1146
- sessionId: String(session.sessionId),
1147
- agentId: String(entryAgentId),
1148
- serviceType: 'hidden-svc',
1149
- })
1150
- expect(statusResult).toMatchObject({ ok: true, value: { status: 'ready' } })
1151
- })
1152
- })
1153
-
1154
- // =========================================================================
1155
- // Real host paths must not leak into agent-facing surfaces
1156
- // =========================================================================
1157
-
1158
- describe('host path hygiene', () => {
1159
- it('status block omits cwd even when the entry tracks one', () => {
1160
- const message = buildServiceStatusMessage(
1161
- [{
1162
- serviceType: 'quick',
1163
- status: 'ready',
1164
- port: 4321,
1165
- cwd: '/home/user/sessions/0000-real-host-path/packages/web',
1166
- }],
1167
- [quickService],
1168
- )
1169
- expect(message).toContain('quick')
1170
- expect(message).toContain('port 4321')
1171
- expect(message).not.toContain('real-host-path')
1172
- expect(message).not.toContain('cwd')
1173
- })
1174
-
1175
- it('service_status tool output strips cwd', async () => {
1176
- const portPool = new PortPool()
1177
- // Absolute cwd so the tracked entry actually carries a host path to leak.
1178
- const cwdService: ServiceConfig = {
1179
- type: 'cwd-svc',
1180
- description: 'Service with a resolved host cwd',
1181
- command: 'sleep 60',
1182
- cwd: tmpdir(),
1183
- }
1184
- const harness = createServicesHarness({
1185
- presets: [createServicesPreset([cwdService], ['cwd-svc'], portPool)],
1186
- llmProvider: MockLLMProvider.withSequence([
1187
- {
1188
- toolCalls: [{
1189
- id: ToolCallId('tc-start'),
1190
- name: 'service_start',
1191
- input: { serviceType: 'cwd-svc' },
1192
- }],
1193
- },
1194
- {
1195
- toolCalls: [{
1196
- id: ToolCallId('tc-status'),
1197
- name: 'service_status',
1198
- input: { serviceType: 'cwd-svc' },
1199
- }],
1200
- },
1201
- { content: 'Done', toolCalls: [] },
1202
- ]),
1203
- })
1204
-
1205
- const session = await harness.createSession('test')
1206
- await session.sendAndWaitForIdle('Check the service')
1207
- await waitForServiceStatus(session, 'cwd-svc', 'ready')
1208
-
1209
- // The service_status tool result (fed back as a message) must not carry cwd,
1210
- // while the session-level method keeps returning it for platform callers.
1211
- const serialized = JSON.stringify(harness.llmProvider.getLastRequest()?.messages ?? [])
1212
- expect(serialized).not.toContain('"cwd"')
1213
-
1214
- const entryAgentId = session.getEntryAgentId()!
1215
- const statusResult = await session.callPluginMethod('services.status', {
1216
- sessionId: String(session.sessionId),
1217
- agentId: String(entryAgentId),
1218
- serviceType: 'cwd-svc',
1219
- })
1220
- expect(statusResult).toMatchObject({ ok: true, value: { cwd: expect.any(String) } })
1221
- })
1222
- })
1223
- })