@roj-ai/sdk 0.0.2

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 (763) hide show
  1. package/dist/bootstrap.d.ts +453 -0
  2. package/dist/bootstrap.d.ts.map +1 -0
  3. package/dist/builtin-events.d.ts +16 -0
  4. package/dist/builtin-events.d.ts.map +1 -0
  5. package/dist/bun-platform/fs.d.ts +9 -0
  6. package/dist/bun-platform/fs.d.ts.map +1 -0
  7. package/dist/bun-platform/index.d.ts +12 -0
  8. package/dist/bun-platform/index.d.ts.map +1 -0
  9. package/dist/bun-platform/process.d.ts +6 -0
  10. package/dist/bun-platform/process.d.ts.map +1 -0
  11. package/dist/config.d.ts +38 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.test.d.ts +2 -0
  14. package/dist/config.test.d.ts.map +1 -0
  15. package/dist/core/agent-loop.integration.test.d.ts +2 -0
  16. package/dist/core/agent-loop.integration.test.d.ts.map +1 -0
  17. package/dist/core/agents/agent-config.test.d.ts +2 -0
  18. package/dist/core/agents/agent-config.test.d.ts.map +1 -0
  19. package/dist/core/agents/agent-roles.d.ts +33 -0
  20. package/dist/core/agents/agent-roles.d.ts.map +1 -0
  21. package/dist/core/agents/agent-shutdown.test.d.ts +6 -0
  22. package/dist/core/agents/agent-shutdown.test.d.ts.map +1 -0
  23. package/dist/core/agents/agent.d.ts +234 -0
  24. package/dist/core/agents/agent.d.ts.map +1 -0
  25. package/dist/core/agents/agent.test.d.ts +2 -0
  26. package/dist/core/agents/agent.test.d.ts.map +1 -0
  27. package/dist/core/agents/communicator.d.ts +3 -0
  28. package/dist/core/agents/communicator.d.ts.map +1 -0
  29. package/dist/core/agents/config.d.ts +84 -0
  30. package/dist/core/agents/config.d.ts.map +1 -0
  31. package/dist/core/agents/context.d.ts +16 -0
  32. package/dist/core/agents/context.d.ts.map +1 -0
  33. package/dist/core/agents/debounce.d.ts +52 -0
  34. package/dist/core/agents/debounce.d.ts.map +1 -0
  35. package/dist/core/agents/handler-events.test.d.ts +5 -0
  36. package/dist/core/agents/handler-events.test.d.ts.map +1 -0
  37. package/dist/core/agents/index.d.ts +2 -0
  38. package/dist/core/agents/index.d.ts.map +1 -0
  39. package/dist/core/agents/response-sanitizer.d.ts +25 -0
  40. package/dist/core/agents/response-sanitizer.d.ts.map +1 -0
  41. package/dist/core/agents/response-sanitizer.test.d.ts +2 -0
  42. package/dist/core/agents/response-sanitizer.test.d.ts.map +1 -0
  43. package/dist/core/agents/retry.d.ts +40 -0
  44. package/dist/core/agents/retry.d.ts.map +1 -0
  45. package/dist/core/agents/schema.d.ts +57 -0
  46. package/dist/core/agents/schema.d.ts.map +1 -0
  47. package/dist/core/agents/state.d.ts +255 -0
  48. package/dist/core/agents/state.d.ts.map +1 -0
  49. package/dist/core/context/state.d.ts +28 -0
  50. package/dist/core/context/state.d.ts.map +1 -0
  51. package/dist/core/errors.d.ts +29 -0
  52. package/dist/core/errors.d.ts.map +1 -0
  53. package/dist/core/event-sourcing.integration.test.d.ts +2 -0
  54. package/dist/core/event-sourcing.integration.test.d.ts.map +1 -0
  55. package/dist/core/events/base-event-store.d.ts +55 -0
  56. package/dist/core/events/base-event-store.d.ts.map +1 -0
  57. package/dist/core/events/event-store.d.ts +108 -0
  58. package/dist/core/events/event-store.d.ts.map +1 -0
  59. package/dist/core/events/file.d.ts +50 -0
  60. package/dist/core/events/file.d.ts.map +1 -0
  61. package/dist/core/events/file.test.d.ts +2 -0
  62. package/dist/core/events/file.test.d.ts.map +1 -0
  63. package/dist/core/events/index.d.ts +4 -0
  64. package/dist/core/events/index.d.ts.map +1 -0
  65. package/dist/core/events/memory.d.ts +43 -0
  66. package/dist/core/events/memory.d.ts.map +1 -0
  67. package/dist/core/events/memory.test.d.ts +2 -0
  68. package/dist/core/events/memory.test.d.ts.map +1 -0
  69. package/dist/core/events/metadata-utils.d.ts +27 -0
  70. package/dist/core/events/metadata-utils.d.ts.map +1 -0
  71. package/dist/core/events/test-helpers.d.ts +28 -0
  72. package/dist/core/events/test-helpers.d.ts.map +1 -0
  73. package/dist/core/events/types.d.ts +46 -0
  74. package/dist/core/events/types.d.ts.map +1 -0
  75. package/dist/core/file-store/file-store.d.ts +56 -0
  76. package/dist/core/file-store/file-store.d.ts.map +1 -0
  77. package/dist/core/file-store/types.d.ts +50 -0
  78. package/dist/core/file-store/types.d.ts.map +1 -0
  79. package/dist/core/image/image-processor.d.ts +15 -0
  80. package/dist/core/image/image-processor.d.ts.map +1 -0
  81. package/dist/core/image/image-processor.test.d.ts +2 -0
  82. package/dist/core/image/image-processor.test.d.ts.map +1 -0
  83. package/dist/core/image/index.d.ts +5 -0
  84. package/dist/core/image/index.d.ts.map +1 -0
  85. package/dist/core/image/noop-resizer.d.ts +5 -0
  86. package/dist/core/image/noop-resizer.d.ts.map +1 -0
  87. package/dist/core/image/types.d.ts +20 -0
  88. package/dist/core/image/types.d.ts.map +1 -0
  89. package/dist/core/image/vips-resizer.d.ts +21 -0
  90. package/dist/core/image/vips-resizer.d.ts.map +1 -0
  91. package/dist/core/image/vips-resizer.test.d.ts +2 -0
  92. package/dist/core/image/vips-resizer.test.d.ts.map +1 -0
  93. package/dist/core/llm/anthropic.d.ts +51 -0
  94. package/dist/core/llm/anthropic.d.ts.map +1 -0
  95. package/dist/core/llm/anthropic.test.d.ts +2 -0
  96. package/dist/core/llm/anthropic.test.d.ts.map +1 -0
  97. package/dist/core/llm/cache-breakpoints.d.ts +17 -0
  98. package/dist/core/llm/cache-breakpoints.d.ts.map +1 -0
  99. package/dist/core/llm/cache-live.test.d.ts +16 -0
  100. package/dist/core/llm/cache-live.test.d.ts.map +1 -0
  101. package/dist/core/llm/index.d.ts +16 -0
  102. package/dist/core/llm/index.d.ts.map +1 -0
  103. package/dist/core/llm/llm-log-types.d.ts +148 -0
  104. package/dist/core/llm/llm-log-types.d.ts.map +1 -0
  105. package/dist/core/llm/logger.d.ts +74 -0
  106. package/dist/core/llm/logger.d.ts.map +1 -0
  107. package/dist/core/llm/logger.test.d.ts +7 -0
  108. package/dist/core/llm/logger.test.d.ts.map +1 -0
  109. package/dist/core/llm/logging-provider.d.ts +20 -0
  110. package/dist/core/llm/logging-provider.d.ts.map +1 -0
  111. package/dist/core/llm/middleware.d.ts +79 -0
  112. package/dist/core/llm/middleware.d.ts.map +1 -0
  113. package/dist/core/llm/mock.d.ts +79 -0
  114. package/dist/core/llm/mock.d.ts.map +1 -0
  115. package/dist/core/llm/mock.test.d.ts +2 -0
  116. package/dist/core/llm/mock.test.d.ts.map +1 -0
  117. package/dist/core/llm/openrouter-mapping.test.d.ts +2 -0
  118. package/dist/core/llm/openrouter-mapping.test.d.ts.map +1 -0
  119. package/dist/core/llm/openrouter.d.ts +37 -0
  120. package/dist/core/llm/openrouter.d.ts.map +1 -0
  121. package/dist/core/llm/openrouter.test.d.ts +2 -0
  122. package/dist/core/llm/openrouter.test.d.ts.map +1 -0
  123. package/dist/core/llm/provider-integration.test.d.ts +12 -0
  124. package/dist/core/llm/provider-integration.test.d.ts.map +1 -0
  125. package/dist/core/llm/provider.d.ts +175 -0
  126. package/dist/core/llm/provider.d.ts.map +1 -0
  127. package/dist/core/llm/routing-provider.d.ts +31 -0
  128. package/dist/core/llm/routing-provider.d.ts.map +1 -0
  129. package/dist/core/llm/routing-provider.test.d.ts +2 -0
  130. package/dist/core/llm/routing-provider.test.d.ts.map +1 -0
  131. package/dist/core/llm/schema.d.ts +24 -0
  132. package/dist/core/llm/schema.d.ts.map +1 -0
  133. package/dist/core/llm/snapshot-fetch.d.ts +21 -0
  134. package/dist/core/llm/snapshot-fetch.d.ts.map +1 -0
  135. package/dist/core/llm/snapshot-middleware.d.ts +71 -0
  136. package/dist/core/llm/snapshot-middleware.d.ts.map +1 -0
  137. package/dist/core/llm/snapshot-middleware.test.d.ts +2 -0
  138. package/dist/core/llm/snapshot-middleware.test.d.ts.map +1 -0
  139. package/dist/core/llm/state.d.ts +73 -0
  140. package/dist/core/llm/state.d.ts.map +1 -0
  141. package/dist/core/llm/tokens.d.ts +36 -0
  142. package/dist/core/llm/tokens.d.ts.map +1 -0
  143. package/dist/core/multi-agent.integration.test.d.ts +2 -0
  144. package/dist/core/multi-agent.integration.test.d.ts.map +1 -0
  145. package/dist/core/plugin-hooks.integration.test.d.ts +2 -0
  146. package/dist/core/plugin-hooks.integration.test.d.ts.map +1 -0
  147. package/dist/core/plugins/hook-types.d.ts +55 -0
  148. package/dist/core/plugins/hook-types.d.ts.map +1 -0
  149. package/dist/core/plugins/index.d.ts +23 -0
  150. package/dist/core/plugins/index.d.ts.map +1 -0
  151. package/dist/core/plugins/plugin-builder.d.ts +474 -0
  152. package/dist/core/plugins/plugin-builder.d.ts.map +1 -0
  153. package/dist/core/preset/config.d.ts +55 -0
  154. package/dist/core/preset/config.d.ts.map +1 -0
  155. package/dist/core/preset/index.d.ts +8 -0
  156. package/dist/core/preset/index.d.ts.map +1 -0
  157. package/dist/core/preset/preset-builder.d.ts +44 -0
  158. package/dist/core/preset/preset-builder.d.ts.map +1 -0
  159. package/dist/core/session-lifecycle.integration.test.d.ts +2 -0
  160. package/dist/core/session-lifecycle.integration.test.d.ts.map +1 -0
  161. package/dist/core/sessions/apply-event.d.ts +19 -0
  162. package/dist/core/sessions/apply-event.d.ts.map +1 -0
  163. package/dist/core/sessions/context.d.ts +34 -0
  164. package/dist/core/sessions/context.d.ts.map +1 -0
  165. package/dist/core/sessions/fork-utils.d.ts +20 -0
  166. package/dist/core/sessions/fork-utils.d.ts.map +1 -0
  167. package/dist/core/sessions/fork-utils.test.d.ts +2 -0
  168. package/dist/core/sessions/fork-utils.test.d.ts.map +1 -0
  169. package/dist/core/sessions/reducer.d.ts +50 -0
  170. package/dist/core/sessions/reducer.d.ts.map +1 -0
  171. package/dist/core/sessions/schema.d.ts +82 -0
  172. package/dist/core/sessions/schema.d.ts.map +1 -0
  173. package/dist/core/sessions/session-environment.d.ts +13 -0
  174. package/dist/core/sessions/session-environment.d.ts.map +1 -0
  175. package/dist/core/sessions/session-manager.d.ts +183 -0
  176. package/dist/core/sessions/session-manager.d.ts.map +1 -0
  177. package/dist/core/sessions/session-store.d.ts +69 -0
  178. package/dist/core/sessions/session-store.d.ts.map +1 -0
  179. package/dist/core/sessions/session.d.ts +212 -0
  180. package/dist/core/sessions/session.d.ts.map +1 -0
  181. package/dist/core/sessions/session.test.d.ts +2 -0
  182. package/dist/core/sessions/session.test.d.ts.map +1 -0
  183. package/dist/core/sessions/state.d.ts +110 -0
  184. package/dist/core/sessions/state.d.ts.map +1 -0
  185. package/dist/core/system.d.ts +97 -0
  186. package/dist/core/system.d.ts.map +1 -0
  187. package/dist/core/tools/context.d.ts +3 -0
  188. package/dist/core/tools/context.d.ts.map +1 -0
  189. package/dist/core/tools/definition.d.ts +10 -0
  190. package/dist/core/tools/definition.d.ts.map +1 -0
  191. package/dist/core/tools/executor.d.ts +28 -0
  192. package/dist/core/tools/executor.d.ts.map +1 -0
  193. package/dist/core/tools/executor.test.d.ts +2 -0
  194. package/dist/core/tools/executor.test.d.ts.map +1 -0
  195. package/dist/core/tools/index.d.ts +4 -0
  196. package/dist/core/tools/index.d.ts.map +1 -0
  197. package/dist/core/tools/schema.d.ts +61 -0
  198. package/dist/core/tools/schema.d.ts.map +1 -0
  199. package/dist/core/tools/state.d.ts +28 -0
  200. package/dist/core/tools/state.d.ts.map +1 -0
  201. package/dist/index.d.ts +111 -0
  202. package/dist/index.d.ts.map +1 -0
  203. package/dist/lib/json/index.d.ts +17 -0
  204. package/dist/lib/json/index.d.ts.map +1 -0
  205. package/dist/lib/logger/console.d.ts +35 -0
  206. package/dist/lib/logger/console.d.ts.map +1 -0
  207. package/dist/lib/logger/console.test.d.ts +2 -0
  208. package/dist/lib/logger/console.test.d.ts.map +1 -0
  209. package/dist/lib/logger/file.d.ts +20 -0
  210. package/dist/lib/logger/file.d.ts.map +1 -0
  211. package/dist/lib/logger/index.d.ts +5 -0
  212. package/dist/lib/logger/index.d.ts.map +1 -0
  213. package/dist/lib/logger/logger.d.ts +87 -0
  214. package/dist/lib/logger/logger.d.ts.map +1 -0
  215. package/dist/lib/logger/ring-buffer.d.ts +33 -0
  216. package/dist/lib/logger/ring-buffer.d.ts.map +1 -0
  217. package/dist/lib/logger/tee.d.ts +15 -0
  218. package/dist/lib/logger/tee.d.ts.map +1 -0
  219. package/dist/lib/mime.d.ts +9 -0
  220. package/dist/lib/mime.d.ts.map +1 -0
  221. package/dist/lib/never.d.ts +2 -0
  222. package/dist/lib/never.d.ts.map +1 -0
  223. package/dist/lib/utils/hash.d.ts +19 -0
  224. package/dist/lib/utils/hash.d.ts.map +1 -0
  225. package/dist/lib/utils/result.d.ts +26 -0
  226. package/dist/lib/utils/result.d.ts.map +1 -0
  227. package/dist/platform/fs.d.ts +39 -0
  228. package/dist/platform/fs.d.ts.map +1 -0
  229. package/dist/platform/index.d.ts +21 -0
  230. package/dist/platform/index.d.ts.map +1 -0
  231. package/dist/platform/process.d.ts +24 -0
  232. package/dist/platform/process.d.ts.map +1 -0
  233. package/dist/plugins/agent-status/plugin.d.ts +8 -0
  234. package/dist/plugins/agent-status/plugin.d.ts.map +1 -0
  235. package/dist/plugins/agents/agents.integration.test.d.ts +2 -0
  236. package/dist/plugins/agents/agents.integration.test.d.ts.map +1 -0
  237. package/dist/plugins/agents/index.d.ts +3 -0
  238. package/dist/plugins/agents/index.d.ts.map +1 -0
  239. package/dist/plugins/agents/plugin.d.ts +57 -0
  240. package/dist/plugins/agents/plugin.d.ts.map +1 -0
  241. package/dist/plugins/context-compact/context-compact.integration.test.d.ts +2 -0
  242. package/dist/plugins/context-compact/context-compact.integration.test.d.ts.map +1 -0
  243. package/dist/plugins/context-compact/context-compactor.d.ts +88 -0
  244. package/dist/plugins/context-compact/context-compactor.d.ts.map +1 -0
  245. package/dist/plugins/context-compact/context-compactor.test.d.ts +2 -0
  246. package/dist/plugins/context-compact/context-compactor.test.d.ts.map +1 -0
  247. package/dist/plugins/context-compact/history-offloader.d.ts +17 -0
  248. package/dist/plugins/context-compact/history-offloader.d.ts.map +1 -0
  249. package/dist/plugins/context-compact/history-offloader.test.d.ts +2 -0
  250. package/dist/plugins/context-compact/history-offloader.test.d.ts.map +1 -0
  251. package/dist/plugins/context-compact/index.d.ts +4 -0
  252. package/dist/plugins/context-compact/index.d.ts.map +1 -0
  253. package/dist/plugins/context-compact/plugin.d.ts +15 -0
  254. package/dist/plugins/context-compact/plugin.d.ts.map +1 -0
  255. package/dist/plugins/filesystem/filesystem.integration.test.d.ts +2 -0
  256. package/dist/plugins/filesystem/filesystem.integration.test.d.ts.map +1 -0
  257. package/dist/plugins/filesystem/helpers.d.ts +34 -0
  258. package/dist/plugins/filesystem/helpers.d.ts.map +1 -0
  259. package/dist/plugins/filesystem/index.d.ts +5 -0
  260. package/dist/plugins/filesystem/index.d.ts.map +1 -0
  261. package/dist/plugins/filesystem/listing.d.ts +38 -0
  262. package/dist/plugins/filesystem/listing.d.ts.map +1 -0
  263. package/dist/plugins/filesystem/plugin.d.ts +53 -0
  264. package/dist/plugins/filesystem/plugin.d.ts.map +1 -0
  265. package/dist/plugins/filesystem/schema.d.ts +7 -0
  266. package/dist/plugins/filesystem/schema.d.ts.map +1 -0
  267. package/dist/plugins/git-status/index.d.ts +2 -0
  268. package/dist/plugins/git-status/index.d.ts.map +1 -0
  269. package/dist/plugins/git-status/plugin.d.ts +12 -0
  270. package/dist/plugins/git-status/plugin.d.ts.map +1 -0
  271. package/dist/plugins/limits-guard/config.d.ts +23 -0
  272. package/dist/plugins/limits-guard/config.d.ts.map +1 -0
  273. package/dist/plugins/limits-guard/index.d.ts +5 -0
  274. package/dist/plugins/limits-guard/index.d.ts.map +1 -0
  275. package/dist/plugins/limits-guard/limit-guard.d.ts +40 -0
  276. package/dist/plugins/limits-guard/limit-guard.d.ts.map +1 -0
  277. package/dist/plugins/limits-guard/limit-guard.test.d.ts +2 -0
  278. package/dist/plugins/limits-guard/limit-guard.test.d.ts.map +1 -0
  279. package/dist/plugins/limits-guard/limits-guard.integration.test.d.ts +2 -0
  280. package/dist/plugins/limits-guard/limits-guard.integration.test.d.ts.map +1 -0
  281. package/dist/plugins/limits-guard/plugin.d.ts +39 -0
  282. package/dist/plugins/limits-guard/plugin.d.ts.map +1 -0
  283. package/dist/plugins/llm-debug/index.d.ts +2 -0
  284. package/dist/plugins/llm-debug/index.d.ts.map +1 -0
  285. package/dist/plugins/llm-debug/llm-debug.integration.test.d.ts +2 -0
  286. package/dist/plugins/llm-debug/llm-debug.integration.test.d.ts.map +1 -0
  287. package/dist/plugins/llm-debug/plugin.d.ts +31 -0
  288. package/dist/plugins/llm-debug/plugin.d.ts.map +1 -0
  289. package/dist/plugins/logs/index.d.ts +2 -0
  290. package/dist/plugins/logs/index.d.ts.map +1 -0
  291. package/dist/plugins/logs/plugin.d.ts +10 -0
  292. package/dist/plugins/logs/plugin.d.ts.map +1 -0
  293. package/dist/plugins/mailbox/helpers.d.ts +20 -0
  294. package/dist/plugins/mailbox/helpers.d.ts.map +1 -0
  295. package/dist/plugins/mailbox/index.d.ts +9 -0
  296. package/dist/plugins/mailbox/index.d.ts.map +1 -0
  297. package/dist/plugins/mailbox/mailbox.integration.test.d.ts +2 -0
  298. package/dist/plugins/mailbox/mailbox.integration.test.d.ts.map +1 -0
  299. package/dist/plugins/mailbox/plugin.d.ts +31 -0
  300. package/dist/plugins/mailbox/plugin.d.ts.map +1 -0
  301. package/dist/plugins/mailbox/prompts.d.ts +21 -0
  302. package/dist/plugins/mailbox/prompts.d.ts.map +1 -0
  303. package/dist/plugins/mailbox/query.d.ts +33 -0
  304. package/dist/plugins/mailbox/query.d.ts.map +1 -0
  305. package/dist/plugins/mailbox/schema.d.ts +54 -0
  306. package/dist/plugins/mailbox/schema.d.ts.map +1 -0
  307. package/dist/plugins/mailbox/state.d.ts +40 -0
  308. package/dist/plugins/mailbox/state.d.ts.map +1 -0
  309. package/dist/plugins/resources/index.d.ts +7 -0
  310. package/dist/plugins/resources/index.d.ts.map +1 -0
  311. package/dist/plugins/resources/manifest.d.ts +23 -0
  312. package/dist/plugins/resources/manifest.d.ts.map +1 -0
  313. package/dist/plugins/resources/plugin.d.ts +28 -0
  314. package/dist/plugins/resources/plugin.d.ts.map +1 -0
  315. package/dist/plugins/resources/post-inject.d.ts +39 -0
  316. package/dist/plugins/resources/post-inject.d.ts.map +1 -0
  317. package/dist/plugins/resources/state.d.ts +25 -0
  318. package/dist/plugins/resources/state.d.ts.map +1 -0
  319. package/dist/plugins/result-eviction/index.d.ts +3 -0
  320. package/dist/plugins/result-eviction/index.d.ts.map +1 -0
  321. package/dist/plugins/result-eviction/plugin.d.ts +19 -0
  322. package/dist/plugins/result-eviction/plugin.d.ts.map +1 -0
  323. package/dist/plugins/result-eviction/result-eviction.integration.test.d.ts +2 -0
  324. package/dist/plugins/result-eviction/result-eviction.integration.test.d.ts.map +1 -0
  325. package/dist/plugins/services/plugin.d.ts +85 -0
  326. package/dist/plugins/services/plugin.d.ts.map +1 -0
  327. package/dist/plugins/services/port-pool.d.ts +32 -0
  328. package/dist/plugins/services/port-pool.d.ts.map +1 -0
  329. package/dist/plugins/services/prompt.d.ts +13 -0
  330. package/dist/plugins/services/prompt.d.ts.map +1 -0
  331. package/dist/plugins/services/schema.d.ts +70 -0
  332. package/dist/plugins/services/schema.d.ts.map +1 -0
  333. package/dist/plugins/services/service.d.ts +86 -0
  334. package/dist/plugins/services/service.d.ts.map +1 -0
  335. package/dist/plugins/services/services.integration.test.d.ts +2 -0
  336. package/dist/plugins/services/services.integration.test.d.ts.map +1 -0
  337. package/dist/plugins/session-lifecycle/index.d.ts +2 -0
  338. package/dist/plugins/session-lifecycle/index.d.ts.map +1 -0
  339. package/dist/plugins/session-lifecycle/plugin.d.ts +97 -0
  340. package/dist/plugins/session-lifecycle/plugin.d.ts.map +1 -0
  341. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.d.ts +2 -0
  342. package/dist/plugins/session-lifecycle/session-lifecycle.integration.test.d.ts.map +1 -0
  343. package/dist/plugins/session-state/plugin.d.ts +48 -0
  344. package/dist/plugins/session-state/plugin.d.ts.map +1 -0
  345. package/dist/plugins/session-stats/index.d.ts +4 -0
  346. package/dist/plugins/session-stats/index.d.ts.map +1 -0
  347. package/dist/plugins/session-stats/plugin.d.ts +29 -0
  348. package/dist/plugins/session-stats/plugin.d.ts.map +1 -0
  349. package/dist/plugins/shell/executor.d.ts +78 -0
  350. package/dist/plugins/shell/executor.d.ts.map +1 -0
  351. package/dist/plugins/shell/index.d.ts +6 -0
  352. package/dist/plugins/shell/index.d.ts.map +1 -0
  353. package/dist/plugins/shell/plugin.d.ts +51 -0
  354. package/dist/plugins/shell/plugin.d.ts.map +1 -0
  355. package/dist/plugins/shell/shell.integration.test.d.ts +2 -0
  356. package/dist/plugins/shell/shell.integration.test.d.ts.map +1 -0
  357. package/dist/plugins/shell/shell.test.d.ts +2 -0
  358. package/dist/plugins/shell/shell.test.d.ts.map +1 -0
  359. package/dist/plugins/skills/discovery.d.ts +69 -0
  360. package/dist/plugins/skills/discovery.d.ts.map +1 -0
  361. package/dist/plugins/skills/discovery.test.d.ts +2 -0
  362. package/dist/plugins/skills/discovery.test.d.ts.map +1 -0
  363. package/dist/plugins/skills/index.d.ts +11 -0
  364. package/dist/plugins/skills/index.d.ts.map +1 -0
  365. package/dist/plugins/skills/plugin.d.ts +94 -0
  366. package/dist/plugins/skills/plugin.d.ts.map +1 -0
  367. package/dist/plugins/skills/prompts.d.ts +29 -0
  368. package/dist/plugins/skills/prompts.d.ts.map +1 -0
  369. package/dist/plugins/skills/schema.d.ts +64 -0
  370. package/dist/plugins/skills/schema.d.ts.map +1 -0
  371. package/dist/plugins/skills/skills.integration.test.d.ts +2 -0
  372. package/dist/plugins/skills/skills.integration.test.d.ts.map +1 -0
  373. package/dist/plugins/snapshotting/index.d.ts +4 -0
  374. package/dist/plugins/snapshotting/index.d.ts.map +1 -0
  375. package/dist/plugins/snapshotting/jj-snapshotter.d.ts +27 -0
  376. package/dist/plugins/snapshotting/jj-snapshotter.d.ts.map +1 -0
  377. package/dist/plugins/snapshotting/plugin.d.ts +15 -0
  378. package/dist/plugins/snapshotting/plugin.d.ts.map +1 -0
  379. package/dist/plugins/snapshotting/snapshotter.d.ts +19 -0
  380. package/dist/plugins/snapshotting/snapshotter.d.ts.map +1 -0
  381. package/dist/plugins/todo/index.d.ts +7 -0
  382. package/dist/plugins/todo/index.d.ts.map +1 -0
  383. package/dist/plugins/todo/plugin.d.ts +95 -0
  384. package/dist/plugins/todo/plugin.d.ts.map +1 -0
  385. package/dist/plugins/todo/prompts.d.ts +13 -0
  386. package/dist/plugins/todo/prompts.d.ts.map +1 -0
  387. package/dist/plugins/todo/schema.d.ts +44 -0
  388. package/dist/plugins/todo/schema.d.ts.map +1 -0
  389. package/dist/plugins/todo/todo.integration.test.d.ts +2 -0
  390. package/dist/plugins/todo/todo.integration.test.d.ts.map +1 -0
  391. package/dist/plugins/uploads/index.d.ts +9 -0
  392. package/dist/plugins/uploads/index.d.ts.map +1 -0
  393. package/dist/plugins/uploads/plugin.d.ts +56 -0
  394. package/dist/plugins/uploads/plugin.d.ts.map +1 -0
  395. package/dist/plugins/uploads/preprocessor.d.ts +70 -0
  396. package/dist/plugins/uploads/preprocessor.d.ts.map +1 -0
  397. package/dist/plugins/uploads/preprocessors/image-classifier.d.ts +49 -0
  398. package/dist/plugins/uploads/preprocessors/image-classifier.d.ts.map +1 -0
  399. package/dist/plugins/uploads/preprocessors/index.d.ts +7 -0
  400. package/dist/plugins/uploads/preprocessors/index.d.ts.map +1 -0
  401. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.d.ts +43 -0
  402. package/dist/plugins/uploads/preprocessors/markitdown-preprocessor.d.ts.map +1 -0
  403. package/dist/plugins/uploads/preprocessors/zip-preprocessor.d.ts +30 -0
  404. package/dist/plugins/uploads/preprocessors/zip-preprocessor.d.ts.map +1 -0
  405. package/dist/plugins/uploads/schema.d.ts +72 -0
  406. package/dist/plugins/uploads/schema.d.ts.map +1 -0
  407. package/dist/plugins/uploads/state.d.ts +38 -0
  408. package/dist/plugins/uploads/state.d.ts.map +1 -0
  409. package/dist/plugins/uploads/uploads.integration.test.d.ts +2 -0
  410. package/dist/plugins/uploads/uploads.integration.test.d.ts.map +1 -0
  411. package/dist/plugins/user-chat/index.d.ts +7 -0
  412. package/dist/plugins/user-chat/index.d.ts.map +1 -0
  413. package/dist/plugins/user-chat/plugin.d.ts +221 -0
  414. package/dist/plugins/user-chat/plugin.d.ts.map +1 -0
  415. package/dist/plugins/user-chat/prompts.d.ts +13 -0
  416. package/dist/plugins/user-chat/prompts.d.ts.map +1 -0
  417. package/dist/plugins/user-chat/schema.d.ts +82 -0
  418. package/dist/plugins/user-chat/schema.d.ts.map +1 -0
  419. package/dist/plugins/user-chat/user-chat.integration.test.d.ts +2 -0
  420. package/dist/plugins/user-chat/user-chat.integration.test.d.ts.map +1 -0
  421. package/dist/plugins/workers/context.d.ts +159 -0
  422. package/dist/plugins/workers/context.d.ts.map +1 -0
  423. package/dist/plugins/workers/definition.d.ts +118 -0
  424. package/dist/plugins/workers/definition.d.ts.map +1 -0
  425. package/dist/plugins/workers/index.d.ts +11 -0
  426. package/dist/plugins/workers/index.d.ts.map +1 -0
  427. package/dist/plugins/workers/plugin.d.ts +125 -0
  428. package/dist/plugins/workers/plugin.d.ts.map +1 -0
  429. package/dist/plugins/workers/worker.d.ts +76 -0
  430. package/dist/plugins/workers/worker.d.ts.map +1 -0
  431. package/dist/plugins/workers/workers.integration.test.d.ts +2 -0
  432. package/dist/plugins/workers/workers.integration.test.d.ts.map +1 -0
  433. package/dist/prompts/base.d.ts +75 -0
  434. package/dist/prompts/base.d.ts.map +1 -0
  435. package/dist/prompts/builder.d.ts +73 -0
  436. package/dist/prompts/builder.d.ts.map +1 -0
  437. package/dist/prompts/index.d.ts +10 -0
  438. package/dist/prompts/index.d.ts.map +1 -0
  439. package/dist/prompts/macros.d.ts +15 -0
  440. package/dist/prompts/macros.d.ts.map +1 -0
  441. package/dist/prompts/macros.test.d.ts +2 -0
  442. package/dist/prompts/macros.test.d.ts.map +1 -0
  443. package/dist/testing/bootstrap-for-testing.d.ts +12 -0
  444. package/dist/testing/bootstrap-for-testing.d.ts.map +1 -0
  445. package/dist/testing/index.d.ts +8 -0
  446. package/dist/testing/index.d.ts.map +1 -0
  447. package/dist/testing/node-platform.d.ts +12 -0
  448. package/dist/testing/node-platform.d.ts.map +1 -0
  449. package/dist/testing/notification-collector.d.ts +46 -0
  450. package/dist/testing/notification-collector.d.ts.map +1 -0
  451. package/dist/testing/preset-helpers.d.ts +28 -0
  452. package/dist/testing/preset-helpers.d.ts.map +1 -0
  453. package/dist/testing/test-harness.d.ts +117 -0
  454. package/dist/testing/test-harness.d.ts.map +1 -0
  455. package/dist/testing/test-harness.test.d.ts +2 -0
  456. package/dist/testing/test-harness.test.d.ts.map +1 -0
  457. package/dist/testing/wait-helpers.d.ts +17 -0
  458. package/dist/testing/wait-helpers.d.ts.map +1 -0
  459. package/dist/transport/adapter/client-adapter.d.ts +32 -0
  460. package/dist/transport/adapter/client-adapter.d.ts.map +1 -0
  461. package/dist/transport/adapter/index.d.ts +23 -0
  462. package/dist/transport/adapter/index.d.ts.map +1 -0
  463. package/dist/transport/adapter/server-adapter.d.ts +29 -0
  464. package/dist/transport/adapter/server-adapter.d.ts.map +1 -0
  465. package/dist/transport/adapter/types.d.ts +14 -0
  466. package/dist/transport/adapter/types.d.ts.map +1 -0
  467. package/dist/transport/http/app.d.ts +41 -0
  468. package/dist/transport/http/app.d.ts.map +1 -0
  469. package/dist/transport/http/index.d.ts +7 -0
  470. package/dist/transport/http/index.d.ts.map +1 -0
  471. package/dist/transport/http/middleware/bearer-auth.d.ts +16 -0
  472. package/dist/transport/http/middleware/bearer-auth.d.ts.map +1 -0
  473. package/dist/transport/http/middleware/error-handler.d.ts +21 -0
  474. package/dist/transport/http/middleware/error-handler.d.ts.map +1 -0
  475. package/dist/transport/http/routes/files.d.ts +16 -0
  476. package/dist/transport/http/routes/files.d.ts.map +1 -0
  477. package/dist/transport/http/routes/resources.d.ts +12 -0
  478. package/dist/transport/http/routes/resources.d.ts.map +1 -0
  479. package/dist/transport/http/routes/rpc.d.ts +22 -0
  480. package/dist/transport/http/routes/rpc.d.ts.map +1 -0
  481. package/dist/transport/http/routes/rpc.integration.test.d.ts +2 -0
  482. package/dist/transport/http/routes/rpc.integration.test.d.ts.map +1 -0
  483. package/dist/transport/http/routes/rpc.test.d.ts +2 -0
  484. package/dist/transport/http/routes/rpc.test.d.ts.map +1 -0
  485. package/dist/transport/http/routes/upload.d.ts +14 -0
  486. package/dist/transport/http/routes/upload.d.ts.map +1 -0
  487. package/dist/transport/rpc/index.d.ts +8 -0
  488. package/dist/transport/rpc/index.d.ts.map +1 -0
  489. package/dist/transport/rpc/methods.d.ts +25 -0
  490. package/dist/transport/rpc/methods.d.ts.map +1 -0
  491. package/dist/user-config.d.ts +29 -0
  492. package/dist/user-config.d.ts.map +1 -0
  493. package/package.json +154 -0
  494. package/src/bootstrap.ts +268 -0
  495. package/src/builtin-events.ts +25 -0
  496. package/src/bun-platform/fs.ts +50 -0
  497. package/src/bun-platform/index.ts +21 -0
  498. package/src/bun-platform/process.ts +25 -0
  499. package/src/config.test.ts +174 -0
  500. package/src/config.ts +103 -0
  501. package/src/core/agent-loop.integration.test.ts +503 -0
  502. package/src/core/agents/agent-config.test.ts +240 -0
  503. package/src/core/agents/agent-roles.ts +41 -0
  504. package/src/core/agents/agent-shutdown.test.ts +236 -0
  505. package/src/core/agents/agent.test.ts +387 -0
  506. package/src/core/agents/agent.ts +1472 -0
  507. package/src/core/agents/communicator.ts +16 -0
  508. package/src/core/agents/config.ts +98 -0
  509. package/src/core/agents/context.ts +19 -0
  510. package/src/core/agents/debounce.ts +116 -0
  511. package/src/core/agents/handler-events.test.ts +167 -0
  512. package/src/core/agents/index.ts +1 -0
  513. package/src/core/agents/response-sanitizer.test.ts +137 -0
  514. package/src/core/agents/response-sanitizer.ts +67 -0
  515. package/src/core/agents/retry.ts +164 -0
  516. package/src/core/agents/schema.ts +75 -0
  517. package/src/core/agents/state.ts +272 -0
  518. package/src/core/context/state.ts +38 -0
  519. package/src/core/errors.ts +55 -0
  520. package/src/core/event-sourcing.integration.test.ts +191 -0
  521. package/src/core/events/base-event-store.ts +264 -0
  522. package/src/core/events/event-store.ts +143 -0
  523. package/src/core/events/file.test.ts +436 -0
  524. package/src/core/events/file.ts +372 -0
  525. package/src/core/events/index.ts +3 -0
  526. package/src/core/events/memory.test.ts +741 -0
  527. package/src/core/events/memory.ts +131 -0
  528. package/src/core/events/metadata-utils.ts +133 -0
  529. package/src/core/events/test-helpers.ts +31 -0
  530. package/src/core/events/types.ts +64 -0
  531. package/src/core/file-store/file-store.ts +275 -0
  532. package/src/core/file-store/types.ts +52 -0
  533. package/src/core/image/image-processor.test.ts +218 -0
  534. package/src/core/image/image-processor.ts +127 -0
  535. package/src/core/image/index.ts +4 -0
  536. package/src/core/image/noop-resizer.ts +7 -0
  537. package/src/core/image/types.ts +24 -0
  538. package/src/core/image/vips-resizer.test.ts +377 -0
  539. package/src/core/image/vips-resizer.ts +124 -0
  540. package/src/core/llm/__snapshots__/anthropic-assistant-text-with-tool-calls.json +156 -0
  541. package/src/core/llm/__snapshots__/anthropic-consecutive-tool-results.json +152 -0
  542. package/src/core/llm/__snapshots__/anthropic-image-data-url.json +105 -0
  543. package/src/core/llm/__snapshots__/anthropic-image-http-url.json +104 -0
  544. package/src/core/llm/__snapshots__/anthropic-multiple-images.json +113 -0
  545. package/src/core/llm/__snapshots__/anthropic-multiple-tool-calls-requested.json +151 -0
  546. package/src/core/llm/__snapshots__/anthropic-simple-user-message.json +97 -0
  547. package/src/core/llm/__snapshots__/anthropic-system-message-in-conversation.json +119 -0
  548. package/src/core/llm/__snapshots__/anthropic-tool-call.json +123 -0
  549. package/src/core/llm/__snapshots__/anthropic-tool-error-result.json +139 -0
  550. package/src/core/llm/__snapshots__/anthropic-tool-result-with-image.json +152 -0
  551. package/src/core/llm/__snapshots__/anthropic-tool-roundtrip.json +139 -0
  552. package/src/core/llm/__snapshots__/openrouter-assistant-text-with-tool-calls.json +150 -0
  553. package/src/core/llm/__snapshots__/openrouter-consecutive-tool-results.json +150 -0
  554. package/src/core/llm/__snapshots__/openrouter-image-data-url.json +107 -0
  555. package/src/core/llm/__snapshots__/openrouter-image-http-url.json +107 -0
  556. package/src/core/llm/__snapshots__/openrouter-multiple-images.json +113 -0
  557. package/src/core/llm/__snapshots__/openrouter-multiple-tool-calls-requested.json +158 -0
  558. package/src/core/llm/__snapshots__/openrouter-simple-user-message.json +96 -0
  559. package/src/core/llm/__snapshots__/openrouter-system-message-in-conversation.json +108 -0
  560. package/src/core/llm/__snapshots__/openrouter-tool-call.json +129 -0
  561. package/src/core/llm/__snapshots__/openrouter-tool-error-result.json +137 -0
  562. package/src/core/llm/__snapshots__/openrouter-tool-result-with-image.json +148 -0
  563. package/src/core/llm/__snapshots__/openrouter-tool-roundtrip.json +137 -0
  564. package/src/core/llm/anthropic.test.ts +509 -0
  565. package/src/core/llm/anthropic.ts +578 -0
  566. package/src/core/llm/cache-breakpoints.ts +38 -0
  567. package/src/core/llm/cache-live.test.ts +155 -0
  568. package/src/core/llm/index.ts +30 -0
  569. package/src/core/llm/llm-log-types.ts +167 -0
  570. package/src/core/llm/logger.test.ts +270 -0
  571. package/src/core/llm/logger.ts +306 -0
  572. package/src/core/llm/logging-provider.ts +73 -0
  573. package/src/core/llm/middleware.ts +172 -0
  574. package/src/core/llm/mock.test.ts +402 -0
  575. package/src/core/llm/mock.ts +234 -0
  576. package/src/core/llm/openrouter-mapping.test.ts +153 -0
  577. package/src/core/llm/openrouter.test.ts +499 -0
  578. package/src/core/llm/openrouter.ts +458 -0
  579. package/src/core/llm/provider-integration.test.ts +408 -0
  580. package/src/core/llm/provider.ts +238 -0
  581. package/src/core/llm/routing-provider.test.ts +113 -0
  582. package/src/core/llm/routing-provider.ts +86 -0
  583. package/src/core/llm/schema.ts +47 -0
  584. package/src/core/llm/snapshot-fetch.ts +158 -0
  585. package/src/core/llm/snapshot-middleware.test.ts +168 -0
  586. package/src/core/llm/snapshot-middleware.ts +185 -0
  587. package/src/core/llm/state.ts +92 -0
  588. package/src/core/llm/tokens.ts +61 -0
  589. package/src/core/multi-agent.integration.test.ts +340 -0
  590. package/src/core/plugin-hooks.integration.test.ts +428 -0
  591. package/src/core/plugins/hook-types.ts +49 -0
  592. package/src/core/plugins/index.ts +52 -0
  593. package/src/core/plugins/plugin-builder.ts +967 -0
  594. package/src/core/preset/config.ts +110 -0
  595. package/src/core/preset/index.ts +11 -0
  596. package/src/core/preset/preset-builder.ts +111 -0
  597. package/src/core/session-lifecycle.integration.test.ts +202 -0
  598. package/src/core/sessions/apply-event.ts +46 -0
  599. package/src/core/sessions/context.ts +36 -0
  600. package/src/core/sessions/fork-utils.test.ts +158 -0
  601. package/src/core/sessions/fork-utils.ts +71 -0
  602. package/src/core/sessions/reducer.ts +95 -0
  603. package/src/core/sessions/schema.ts +91 -0
  604. package/src/core/sessions/session-environment.ts +12 -0
  605. package/src/core/sessions/session-manager.ts +883 -0
  606. package/src/core/sessions/session-store.ts +141 -0
  607. package/src/core/sessions/session.test.ts +1730 -0
  608. package/src/core/sessions/session.ts +833 -0
  609. package/src/core/sessions/state.ts +520 -0
  610. package/src/core/system.ts +206 -0
  611. package/src/core/tools/context.ts +3 -0
  612. package/src/core/tools/definition.ts +15 -0
  613. package/src/core/tools/executor.test.ts +160 -0
  614. package/src/core/tools/executor.ts +117 -0
  615. package/src/core/tools/index.ts +3 -0
  616. package/src/core/tools/schema.ts +80 -0
  617. package/src/core/tools/state.ts +34 -0
  618. package/src/index.ts +165 -0
  619. package/src/lib/json/index.ts +20 -0
  620. package/src/lib/logger/console.test.ts +348 -0
  621. package/src/lib/logger/console.ts +185 -0
  622. package/src/lib/logger/file.ts +65 -0
  623. package/src/lib/logger/index.ts +4 -0
  624. package/src/lib/logger/logger.ts +114 -0
  625. package/src/lib/logger/ring-buffer.ts +65 -0
  626. package/src/lib/logger/tee.ts +51 -0
  627. package/src/lib/mime.ts +23 -0
  628. package/src/lib/never.ts +3 -0
  629. package/src/lib/utils/hash.ts +38 -0
  630. package/src/lib/utils/result.ts +35 -0
  631. package/src/platform/fs.ts +38 -0
  632. package/src/platform/index.ts +23 -0
  633. package/src/platform/process.ts +28 -0
  634. package/src/plugins/agent-status/plugin.ts +77 -0
  635. package/src/plugins/agents/agents.integration.test.ts +808 -0
  636. package/src/plugins/agents/index.ts +2 -0
  637. package/src/plugins/agents/plugin.ts +242 -0
  638. package/src/plugins/context-compact/context-compact.integration.test.ts +207 -0
  639. package/src/plugins/context-compact/context-compactor.test.ts +923 -0
  640. package/src/plugins/context-compact/context-compactor.ts +343 -0
  641. package/src/plugins/context-compact/history-offloader.test.ts +100 -0
  642. package/src/plugins/context-compact/history-offloader.ts +47 -0
  643. package/src/plugins/context-compact/index.ts +3 -0
  644. package/src/plugins/context-compact/plugin.ts +62 -0
  645. package/src/plugins/filesystem/filesystem.integration.test.ts +485 -0
  646. package/src/plugins/filesystem/helpers.ts +216 -0
  647. package/src/plugins/filesystem/index.ts +4 -0
  648. package/src/plugins/filesystem/listing.ts +276 -0
  649. package/src/plugins/filesystem/plugin.ts +468 -0
  650. package/src/plugins/filesystem/schema.ts +6 -0
  651. package/src/plugins/git-status/index.ts +1 -0
  652. package/src/plugins/git-status/plugin.ts +166 -0
  653. package/src/plugins/limits-guard/config.ts +26 -0
  654. package/src/plugins/limits-guard/index.ts +4 -0
  655. package/src/plugins/limits-guard/limit-guard.test.ts +161 -0
  656. package/src/plugins/limits-guard/limit-guard.ts +164 -0
  657. package/src/plugins/limits-guard/limits-guard.integration.test.ts +437 -0
  658. package/src/plugins/limits-guard/plugin.ts +306 -0
  659. package/src/plugins/llm-debug/index.ts +1 -0
  660. package/src/plugins/llm-debug/llm-debug.integration.test.ts +192 -0
  661. package/src/plugins/llm-debug/plugin.ts +172 -0
  662. package/src/plugins/logs/index.ts +1 -0
  663. package/src/plugins/logs/plugin.ts +39 -0
  664. package/src/plugins/mailbox/helpers.ts +83 -0
  665. package/src/plugins/mailbox/index.ts +13 -0
  666. package/src/plugins/mailbox/mailbox.integration.test.ts +705 -0
  667. package/src/plugins/mailbox/plugin.ts +270 -0
  668. package/src/plugins/mailbox/prompts.ts +104 -0
  669. package/src/plugins/mailbox/query.ts +53 -0
  670. package/src/plugins/mailbox/schema.ts +81 -0
  671. package/src/plugins/mailbox/state.ts +52 -0
  672. package/src/plugins/resources/index.ts +11 -0
  673. package/src/plugins/resources/manifest.ts +25 -0
  674. package/src/plugins/resources/plugin.ts +201 -0
  675. package/src/plugins/resources/post-inject.ts +74 -0
  676. package/src/plugins/resources/state.ts +30 -0
  677. package/src/plugins/result-eviction/index.ts +2 -0
  678. package/src/plugins/result-eviction/plugin.ts +65 -0
  679. package/src/plugins/result-eviction/result-eviction.integration.test.ts +244 -0
  680. package/src/plugins/services/plugin.ts +485 -0
  681. package/src/plugins/services/port-pool.ts +73 -0
  682. package/src/plugins/services/prompt.ts +53 -0
  683. package/src/plugins/services/schema.ts +83 -0
  684. package/src/plugins/services/service.ts +578 -0
  685. package/src/plugins/services/services.integration.test.ts +595 -0
  686. package/src/plugins/session-lifecycle/index.ts +1 -0
  687. package/src/plugins/session-lifecycle/plugin.ts +302 -0
  688. package/src/plugins/session-lifecycle/session-lifecycle.integration.test.ts +639 -0
  689. package/src/plugins/session-state/plugin.ts +179 -0
  690. package/src/plugins/session-stats/index.ts +3 -0
  691. package/src/plugins/session-stats/plugin.ts +120 -0
  692. package/src/plugins/shell/executor.ts +462 -0
  693. package/src/plugins/shell/index.ts +23 -0
  694. package/src/plugins/shell/plugin.ts +118 -0
  695. package/src/plugins/shell/shell.integration.test.ts +273 -0
  696. package/src/plugins/shell/shell.test.ts +314 -0
  697. package/src/plugins/skills/discovery.test.ts +395 -0
  698. package/src/plugins/skills/discovery.ts +276 -0
  699. package/src/plugins/skills/index.ts +32 -0
  700. package/src/plugins/skills/plugin.ts +381 -0
  701. package/src/plugins/skills/prompts.ts +84 -0
  702. package/src/plugins/skills/schema.ts +88 -0
  703. package/src/plugins/skills/skills.integration.test.ts +580 -0
  704. package/src/plugins/snapshotting/index.ts +3 -0
  705. package/src/plugins/snapshotting/jj-snapshotter.ts +122 -0
  706. package/src/plugins/snapshotting/plugin.ts +36 -0
  707. package/src/plugins/snapshotting/snapshotter.ts +20 -0
  708. package/src/plugins/todo/index.ts +11 -0
  709. package/src/plugins/todo/plugin.ts +363 -0
  710. package/src/plugins/todo/prompts.ts +63 -0
  711. package/src/plugins/todo/schema.ts +71 -0
  712. package/src/plugins/todo/todo.integration.test.ts +687 -0
  713. package/src/plugins/uploads/index.ts +13 -0
  714. package/src/plugins/uploads/plugin.ts +406 -0
  715. package/src/plugins/uploads/preprocessor.ts +111 -0
  716. package/src/plugins/uploads/preprocessors/image-classifier.ts +176 -0
  717. package/src/plugins/uploads/preprocessors/index.ts +7 -0
  718. package/src/plugins/uploads/preprocessors/markitdown-preprocessor.ts +262 -0
  719. package/src/plugins/uploads/preprocessors/zip-preprocessor.ts +210 -0
  720. package/src/plugins/uploads/schema.ts +92 -0
  721. package/src/plugins/uploads/state.ts +42 -0
  722. package/src/plugins/uploads/uploads.integration.test.ts +654 -0
  723. package/src/plugins/user-chat/index.ts +17 -0
  724. package/src/plugins/user-chat/plugin.ts +728 -0
  725. package/src/plugins/user-chat/prompts.ts +30 -0
  726. package/src/plugins/user-chat/schema.ts +80 -0
  727. package/src/plugins/user-chat/user-chat.integration.test.ts +794 -0
  728. package/src/plugins/workers/context.ts +251 -0
  729. package/src/plugins/workers/definition.ts +169 -0
  730. package/src/plugins/workers/index.ts +21 -0
  731. package/src/plugins/workers/plugin.ts +722 -0
  732. package/src/plugins/workers/worker.ts +115 -0
  733. package/src/plugins/workers/workers.integration.test.ts +778 -0
  734. package/src/prompts/base.ts +266 -0
  735. package/src/prompts/builder.ts +194 -0
  736. package/src/prompts/index.ts +36 -0
  737. package/src/prompts/macros.test.ts +107 -0
  738. package/src/prompts/macros.ts +27 -0
  739. package/src/testing/bootstrap-for-testing.ts +39 -0
  740. package/src/testing/index.ts +7 -0
  741. package/src/testing/node-platform.ts +80 -0
  742. package/src/testing/notification-collector.ts +100 -0
  743. package/src/testing/preset-helpers.ts +57 -0
  744. package/src/testing/test-harness.test.ts +63 -0
  745. package/src/testing/test-harness.ts +279 -0
  746. package/src/testing/wait-helpers.ts +84 -0
  747. package/src/transport/adapter/client-adapter.ts +81 -0
  748. package/src/transport/adapter/index.ts +42 -0
  749. package/src/transport/adapter/server-adapter.ts +93 -0
  750. package/src/transport/adapter/types.ts +20 -0
  751. package/src/transport/http/app.ts +129 -0
  752. package/src/transport/http/index.ts +7 -0
  753. package/src/transport/http/middleware/bearer-auth.ts +40 -0
  754. package/src/transport/http/middleware/error-handler.ts +76 -0
  755. package/src/transport/http/routes/files.ts +274 -0
  756. package/src/transport/http/routes/resources.ts +115 -0
  757. package/src/transport/http/routes/rpc.integration.test.ts +243 -0
  758. package/src/transport/http/routes/rpc.test.ts +372 -0
  759. package/src/transport/http/routes/rpc.ts +156 -0
  760. package/src/transport/http/routes/upload.ts +286 -0
  761. package/src/transport/rpc/index.ts +11 -0
  762. package/src/transport/rpc/methods.ts +38 -0
  763. package/src/user-config.ts +33 -0
@@ -0,0 +1,1472 @@
1
+ /**
2
+ * Agent - OOP wrapper for agent orchestration.
3
+ *
4
+ * Responsibilities:
5
+ * - Process mailbox (inference + tool execution loop)
6
+ * - Embedded scheduling with debounce
7
+ * - Spawn child agents
8
+ */
9
+
10
+ import z from 'zod/v4'
11
+ import { DebounceCallback } from '~/core/agents/debounce.js'
12
+ import type { AgentId } from '~/core/agents/schema.js'
13
+ import type { AgentState, HandlerResult } from '~/core/agents/state.js'
14
+ import { agentEvents } from '~/core/agents/state.js'
15
+ import { withSessionId } from '~/core/events/test-helpers.js'
16
+ import type { FileStore } from '~/core/file-store/types.js'
17
+ import { applyCacheBreakpoint } from '~/core/llm/cache-breakpoints.js'
18
+ import type { ToolResultContent } from '~/core/llm/llm-log-types.js'
19
+ import type { InferenceRequest, LLMMessage, LLMProvider } from '~/core/llm/provider.js'
20
+ import { LLMCallId, ModelId } from '~/core/llm/schema.js'
21
+ import type { LLMResponse } from '~/core/llm/state.js'
22
+ import { llmEvents } from '~/core/llm/state.js'
23
+ import type {
24
+ AfterInferenceResult,
25
+ AfterToolCallResult,
26
+ BeforeInferenceResult,
27
+ BeforeToolCallResult,
28
+ HandlerName,
29
+ OnCompleteResult,
30
+ OnErrorResult,
31
+ OnStartResult,
32
+ } from '~/core/plugins/hook-types.js'
33
+ import type {
34
+ AgentPluginConfig,
35
+ BasePluginHookContext,
36
+ ConfiguredPlugin,
37
+ PluginMethodCaller,
38
+ PluginNotification,
39
+ } from '~/core/plugins/plugin-builder.js'
40
+ import { buildPluginDeps } from '~/core/plugins/plugin-builder.js'
41
+ import type { ToolContext } from '~/core/tools/context.js'
42
+ import type { ToolDefinition } from '~/core/tools/definition.js'
43
+ import type { ToolCall } from '~/core/tools/schema.js'
44
+ import { ToolCallId } from '~/core/tools/schema.js'
45
+ import { toolEvents } from '~/core/tools/state.js'
46
+ import { getAgentUnconsumedMailbox, selectMailboxState } from '~/plugins/mailbox/query.js'
47
+ import { AGENT_BASE_BRIEFING } from '~/prompts/base.js'
48
+ import { buildEnvironmentSection } from '~/prompts/builder.js'
49
+ import type { Logger } from '../../lib/logger/logger.js'
50
+ import type { SessionContext } from '../sessions/context.js'
51
+ import type { SessionStore } from '../sessions/session-store.js'
52
+ import type { SessionState } from '../sessions/state.js'
53
+ import type { SessionEnvironment, ToolExecutor } from '../tools'
54
+ import type { AgentContext } from './context.js'
55
+ import { sanitizeLLMResponse } from './response-sanitizer.js'
56
+ import { withLLMRetry } from './retry.js'
57
+
58
+ // ============================================================================
59
+ // Types
60
+ // ============================================================================
61
+
62
+ /**
63
+ * Agent configuration - includes both behavior and debounce settings.
64
+ */
65
+ export interface AgentConfig<TInput = unknown> {
66
+ systemPrompt: string
67
+ model: ModelId
68
+ spawnableAgents: string[]
69
+ // ToolDefinition<any> required: ToolDefinition is contravariant in TInput,
70
+ // so ToolDefinition<SpecificInput> is not assignable to ToolDefinition<unknown>
71
+ /** Preset-level static tools (merged with plugin tools at runtime) */
72
+ tools?: ToolDefinition<any>[]
73
+ /** Debounce time in ms before processing agent mailbox. Default: 500ms */
74
+ debounceMs?: number
75
+ /** Optional callback to determine when to process */
76
+ debounceCallback?: DebounceCallback
77
+ /** Interval in ms for checking debounce callback (default: 100) */
78
+ checkIntervalMs?: number
79
+ /** Optional Zod schema for typed agent input validation */
80
+ input?: z.ZodType<TInput>
81
+ /** Per-plugin agent-level configs */
82
+ plugins?: AgentPluginConfig[]
83
+ }
84
+
85
+ /**
86
+ * Dependencies for creating an Agent.
87
+ */
88
+ export interface AgentDependencies {
89
+ id: AgentId
90
+ sessionContext: SessionContext
91
+ store: SessionStore
92
+ llmProvider: LLMProvider
93
+ /** Named provider instances, passed to InferenceContext for middleware routing */
94
+ llmProviders?: ReadonlyMap<string, LLMProvider>
95
+ toolExecutor: ToolExecutor
96
+ logger: Logger
97
+ config: AgentConfig
98
+ plugins: ConfiguredPlugin[]
99
+ /** Session environment directories for tool context */
100
+ environment: SessionEnvironment
101
+ /** FileStore for resolving agent-visible file:// paths (used by LLM provider) */
102
+ fileStore: FileStore
103
+ /** Plugin contexts created by session-level initPluginContexts(), keyed by plugin name */
104
+ pluginContexts?: ReadonlyMap<string, unknown>
105
+ /** Callback for sending plugin notifications directly to transport */
106
+ sendNotification?: (notification: PluginNotification) => void
107
+ /** Callback for resolving cross-plugin method calls (delegates to session) */
108
+ pluginMethodCaller?: PluginMethodCaller
109
+ /** Callback for scheduling this agent for processing */
110
+ schedule?: () => void
111
+ }
112
+
113
+ // ============================================================================
114
+ // Agent
115
+ // ============================================================================
116
+
117
+ /**
118
+ * Agent handles mailbox processing, inference, and tool execution.
119
+ *
120
+ * Features:
121
+ * - Debounced processing (timer or callback-based)
122
+ * - LLM inference with retry
123
+ * - Context compaction
124
+ * - Tool execution
125
+ */
126
+ export class Agent {
127
+ readonly id: AgentId
128
+ private readonly config: AgentConfig
129
+ private readonly sessionContext: SessionContext
130
+ private readonly store: SessionStore
131
+ private readonly logger: Logger
132
+ private readonly llmProvider: LLMProvider
133
+ private readonly llmProviders: ReadonlyMap<string, LLMProvider>
134
+ private readonly toolExecutor: ToolExecutor
135
+ private readonly plugins: ConfiguredPlugin[]
136
+ private readonly environment: SessionEnvironment
137
+ private readonly fileStore: FileStore
138
+ private readonly pluginContexts: ReadonlyMap<string, unknown>
139
+ private readonly sendNotification?: (notification: PluginNotification) => void
140
+ private readonly pluginMethodCaller?: PluginMethodCaller
141
+ private readonly scheduleCallback?: () => void
142
+
143
+ /** Merged tools map: config tools + plugin tools (plugins override). Rebuilt each processing cycle. */
144
+ private tools: Map<string, ToolDefinition>
145
+
146
+ // Scheduler state (embedded)
147
+ private debounceTimer?: ReturnType<typeof setTimeout>
148
+ private processing = false
149
+ private scheduled = false
150
+ private pendingReschedule = false
151
+ private readonly abortController = new AbortController()
152
+
153
+ /** Track conversation turn number for handler context */
154
+ private turnNumber = 0
155
+
156
+ constructor(deps: AgentDependencies) {
157
+ this.id = deps.id
158
+ this.config = deps.config
159
+ this.sessionContext = deps.sessionContext
160
+ this.store = deps.store
161
+ this.logger = deps.logger
162
+ this.llmProvider = deps.llmProvider
163
+ this.llmProviders = deps.llmProviders ?? new Map()
164
+ this.toolExecutor = deps.toolExecutor
165
+ this.plugins = deps.plugins
166
+ this.environment = deps.environment
167
+ this.fileStore = deps.fileStore
168
+ this.pluginContexts = deps.pluginContexts ?? new Map()
169
+ this.sendNotification = deps.sendNotification
170
+ this.pluginMethodCaller = deps.pluginMethodCaller
171
+ this.scheduleCallback = deps.schedule
172
+ this.tools = this.buildToolsMap()
173
+
174
+ // Initialize turn number from conversation history
175
+ const state = this.state
176
+ if (state) {
177
+ this.turnNumber = state.conversationHistory.filter((m) => m.role === 'assistant').length
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Get the current agent state from store.
183
+ */
184
+ get state(): AgentState | null {
185
+ return this.store.getAgentState(this.id)
186
+ }
187
+
188
+ // ============================================================================
189
+ // Public API
190
+ // ============================================================================
191
+
192
+ /**
193
+ * Unified processing entry point - decides what to do next.
194
+ * Safe to call multiple times; skips if already processing.
195
+ */
196
+ async continue(): Promise<void> {
197
+ if (this.processing) return
198
+ if (this.store.isClosed()) return
199
+ this.processing = true
200
+ this.scheduled = false
201
+
202
+ try {
203
+ while (true) {
204
+ const agentState = this.state
205
+ if (!agentState) break
206
+
207
+ // Rebuild tools map each cycle so plugin-dynamic tools reflect current state
208
+ this.tools = this.buildToolsMap()
209
+
210
+ const decision = this.decide(agentState)
211
+
212
+ switch (decision) {
213
+ case 'idle':
214
+ this.logger.debug('Agent has nothing to do', {
215
+ agentId: this.id,
216
+ status: agentState.status,
217
+ })
218
+ return
219
+
220
+ case 'paused':
221
+ this.logger.debug('Agent is paused, skipping processing', { agentId: this.id })
222
+ return
223
+
224
+ case 'on_start':
225
+ await this.executeOnStart(agentState)
226
+ continue
227
+
228
+ case 'tool_exec':
229
+ this.logger.info('Executing pending tool calls', {
230
+ agentId: this.id,
231
+ count: agentState.pendingToolCalls.length,
232
+ })
233
+ for (const toolCall of agentState.pendingToolCalls) {
234
+ await this.executeToolCall(toolCall)
235
+ }
236
+ // Schedule re-entry via debounce after tool execution
237
+ // (allows debounce callback to wait for child responses, etc.)
238
+ this.scheduleProcessing()
239
+ return
240
+
241
+ case 'resume_from_error':
242
+ await this.store.emit(withSessionId(
243
+ this.store.sessionId,
244
+ agentEvents.create('agent_resumed', { agentId: this.id }),
245
+ ))
246
+ continue
247
+
248
+ case 'infer':
249
+ await this.runInference(agentState)
250
+ continue
251
+
252
+ case 'complete':
253
+ await this.executeOnComplete(agentState)
254
+ return
255
+ }
256
+ }
257
+ } catch (err) {
258
+ if (this.abortController.signal.aborted) {
259
+ this.logger.debug('Agent processing aborted', { agentId: this.id })
260
+ return
261
+ }
262
+ this.logger.error('Unexpected error in agent processing', err instanceof Error ? err : new Error(String(err)), {
263
+ agentId: this.id,
264
+ sessionId: this.store.sessionId,
265
+ })
266
+ } finally {
267
+ this.processing = false
268
+ if (this.pendingReschedule) {
269
+ this.pendingReschedule = false
270
+ this.scheduleProcessing()
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Schedule processing with debounce.
277
+ * Use this when receiving new messages or after tool execution.
278
+ */
279
+ scheduleProcessing(): void {
280
+ if (this.scheduled) return
281
+ if (this.store.isClosed()) return
282
+ if (this.processing) {
283
+ this.pendingReschedule = true
284
+ return
285
+ }
286
+
287
+ this.cancelSchedule()
288
+ this.scheduled = true
289
+
290
+ const agentState = this.state
291
+ if (!agentState) return
292
+
293
+ if (this.config.debounceCallback) {
294
+ // Callback-based debounce using recursive setTimeout
295
+ const checkInterval = this.config.checkIntervalMs ?? 100
296
+
297
+ const scheduleCheck = () => {
298
+ this.debounceTimer = setTimeout(async () => {
299
+ // Re-read state fresh each check — no stale data
300
+ const currentState = this.state
301
+ if (!currentState) {
302
+ this.cancelSchedule()
303
+ return
304
+ }
305
+
306
+ // Guard: schedule could be cancelled between timer fire and here
307
+ if (!this.scheduled) return
308
+
309
+ const sessionState = this.store.getState()
310
+ const unconsumed = getUnconsumedMessages(sessionState, this.id)
311
+ const pendingToolResults = currentState.pendingToolResults
312
+
313
+ // If no messages, no pending tool results, and no plugin pending, nothing to do
314
+ if (unconsumed.length === 0 && pendingToolResults.length === 0 && !this.hasPluginPendingMessages()) {
315
+ this.cancelSchedule()
316
+ return
317
+ }
318
+
319
+ const oldestTimestamp = unconsumed.length > 0
320
+ ? Math.min(...unconsumed.map((m) => m.timestamp))
321
+ : Date.now()
322
+ const oldestWaitingMs = Date.now() - oldestTimestamp
323
+
324
+ const decision = await this.config.debounceCallback!({
325
+ messages: unconsumed,
326
+ oldestWaitingMs,
327
+ totalPending: unconsumed.length,
328
+ pendingToolResults,
329
+ })
330
+
331
+ // Re-check after async callback — schedule could be cancelled during await
332
+ if (!this.scheduled) return
333
+
334
+ if (decision === 'process_now') {
335
+ this.cancelSchedule()
336
+ this.continue().catch((err) => {
337
+ this.logger.error('Unhandled error in continue()', err instanceof Error ? err : undefined, { agentId: this.id })
338
+ })
339
+ } else {
340
+ // Callback said "wait" — schedule next check.
341
+ // Fresh state will be read on next iteration.
342
+ scheduleCheck()
343
+ }
344
+ }, checkInterval)
345
+ }
346
+
347
+ scheduleCheck()
348
+ } else {
349
+ // Timer-based debounce (default: 500ms)
350
+ const debounceMs = this.config.debounceMs ?? 500
351
+ this.debounceTimer = setTimeout(() => {
352
+ this.scheduled = false
353
+ this.debounceTimer = undefined
354
+ this.continue().catch((err) => {
355
+ this.logger.error('Unhandled error in continue()', err instanceof Error ? err : undefined, { agentId: this.id })
356
+ })
357
+ }, debounceMs)
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Shutdown the agent - cancel any scheduled processing.
363
+ */
364
+ shutdown(): void {
365
+ try {
366
+ this.abortController.abort()
367
+ } catch {
368
+ // AbortError may be thrown synchronously by abort signal listeners
369
+ }
370
+ this.cancelSchedule()
371
+ }
372
+
373
+ /**
374
+ * Check if processing is scheduled.
375
+ */
376
+ isScheduled(): boolean {
377
+ return this.scheduled
378
+ }
379
+
380
+ // ============================================================================
381
+ // Private methods - Processing
382
+ // ============================================================================
383
+
384
+ /**
385
+ * Determine the next action based on current agent state.
386
+ */
387
+ private decide(state: AgentState): 'idle' | 'paused' | 'on_start' | 'tool_exec' | 'infer' | 'resume_from_error' | 'complete' {
388
+ if (state.status === 'paused') return 'paused'
389
+ if (!state.onStartCalled) return 'on_start'
390
+ if (state.status === 'tool_exec' && state.pendingToolCalls.length > 0) return 'tool_exec'
391
+ if (state.status === 'pending') {
392
+ if (hasWork(state) || this.hasPluginPendingMessages()) return 'infer'
393
+ return 'complete'
394
+ }
395
+ if (state.status === 'errored') {
396
+ // Errored with new messages (e.g. user sent a message): emit resume event, then retry
397
+ // Only check for new messages, not stale pendingToolResults — those were present when inference failed
398
+ if (this.hasPluginPendingMessages()) return 'resume_from_error'
399
+ return 'complete'
400
+ }
401
+ return 'idle'
402
+ }
403
+
404
+ private static readonly MAX_INFERENCE_RETRIES = 3
405
+
406
+ /**
407
+ * Run inference on agent's mailbox.
408
+ * Runs when there are unconsumed messages OR pending tool results.
409
+ */
410
+ private async runInference(initialAgentState: AgentState, retryCount = 0): Promise<void> {
411
+ let agentState = initialAgentState
412
+ const hasToolResults = agentState.pendingToolResults.length > 0
413
+
414
+ // Collect plugin dequeue messages (includes mailbox messages)
415
+ const pluginDequeued = this.collectPluginMessages()
416
+
417
+ // Need either tool results or plugin messages to process
418
+ if (!hasToolResults && pluginDequeued.length === 0) return
419
+
420
+ this.turnNumber++
421
+
422
+ // 0. beforeInference handler - can skip LLM entirely or pause
423
+ const beforeResult = await this.executeBeforeInference(agentState)
424
+ if (beforeResult !== null) {
425
+ if (beforeResult.action === 'skip') {
426
+ // Skip LLM, use provided response directly
427
+ await this.emitInferenceCompleted(beforeResult.response, undefined)
428
+ return
429
+ }
430
+ if (beforeResult.action === 'pause') {
431
+ return
432
+ }
433
+ }
434
+
435
+ // 1. Context compaction is now handled by the context-compact plugin's beforeInference hook
436
+ // (which runs above). If compaction occurred, agent state was updated via context_compacted event.
437
+ // Re-read agent state to pick up any compacted history.
438
+ const postHookState = this.state
439
+ if (postHookState) {
440
+ agentState = postHookState
441
+ }
442
+
443
+ // 2. Build pending messages (tool results only)
444
+ const pendingMessages = this.buildPendingMessages(agentState)
445
+
446
+ // 2b. Append plugin dequeued messages (includes mailbox messages)
447
+ for (const dequeued of pluginDequeued) {
448
+ pendingMessages.push(...dequeued.messages)
449
+ }
450
+
451
+ // 3. Inference start - emit with pending messages
452
+ await this.store.emit(withSessionId(
453
+ this.store.sessionId,
454
+ llmEvents.create('inference_started', {
455
+ agentId: this.id,
456
+ messages: pendingMessages,
457
+ consumedMessageIds: [],
458
+ }),
459
+ ))
460
+
461
+ // 4. Build LLM messages — re-read state to include inference_started changes
462
+ const preInferenceState = this.state
463
+ if (preInferenceState) {
464
+ agentState = preInferenceState
465
+ }
466
+ const messages = this.buildLLMMessages(agentState, pendingMessages)
467
+
468
+ // 4b. Append ephemeral context (not stored in history, recreated each inference)
469
+ const ephemeralParts: string[] = []
470
+
471
+ // Collect status messages from all plugins
472
+ const pluginStatus = this.getPluginStatus()
473
+ if (pluginStatus) ephemeralParts.push(pluginStatus)
474
+
475
+ if (ephemeralParts.length > 0) {
476
+ messages.push({
477
+ role: 'user',
478
+ content: `<session-context>\n${ephemeralParts.join('\n\n')}\n</session-context>`,
479
+ })
480
+ }
481
+
482
+ // Mark cache breakpoint — ephemeral session-context suffix is excluded
483
+ // so it doesn't invalidate the cache on every inference.
484
+ const cachedMessages = applyCacheBreakpoint(messages, ephemeralParts.length > 0 ? 1 : 0)
485
+
486
+ // 5. LLM inference (with retry)
487
+ const request: InferenceRequest = {
488
+ model: this.config.model,
489
+ systemPrompt: this.buildSystemPrompt(),
490
+ messages: cachedMessages,
491
+ tools: this.tools.size > 0 ? [...this.tools.values()] : undefined,
492
+ // Stop sequences to prevent hallucination of message tags
493
+ stopSequences: ['<message'],
494
+ }
495
+
496
+ this.logger.debug('Running inference', {
497
+ sessionId: this.store.sessionId,
498
+ agentId: this.id,
499
+ messageCount: messages.length,
500
+ })
501
+
502
+ // Capture llmCallId from the logging provider
503
+ let llmCallId: LLMCallId | undefined
504
+
505
+ const llmResponse = await withLLMRetry(
506
+ () =>
507
+ this.llmProvider.inference(request, {
508
+ sessionId: this.store.sessionId,
509
+ agentId: this.id,
510
+ onLLMCallCreated: (callId) => {
511
+ llmCallId = LLMCallId(callId)
512
+ },
513
+ signal: this.abortController.signal,
514
+ fileStore: this.fileStore,
515
+ providers: this.llmProviders,
516
+ }),
517
+ { logger: this.logger, signal: this.abortController.signal },
518
+ )
519
+
520
+ // Mark plugin messages as consumed (regardless of inference outcome —
521
+ // messages are already appended to conversationHistory via inference_started)
522
+ {
523
+ const currentAgentState = this.state
524
+ if (currentAgentState) {
525
+ const ctx = this.buildAgentContext(currentAgentState)
526
+ for (const dequeued of pluginDequeued) {
527
+ if (!dequeued.plugin.dequeue) continue
528
+ const pluginCtx = this.buildPluginHookContext(dequeued.plugin, ctx)
529
+ await dequeued.plugin.dequeue.markConsumed(pluginCtx, dequeued.token)
530
+ }
531
+ }
532
+ }
533
+
534
+ if (!llmResponse.ok) {
535
+ // 4a. Inference failed
536
+ await this.store.emit(withSessionId(
537
+ this.store.sessionId,
538
+ llmEvents.create('inference_failed', {
539
+ agentId: this.id,
540
+ error: llmResponse.error.message,
541
+ llmCallId,
542
+ }),
543
+ ))
544
+ // Notify plugins (e.g. mailbox sends error message to parent)
545
+ const errorState = this.state
546
+ if (errorState) {
547
+ await this.executeOnError(errorState, llmResponse.error.message)
548
+ }
549
+ return
550
+ }
551
+
552
+ // 4c. Sanitize response to prevent hallucination
553
+ const sanitized = sanitizeLLMResponse(llmResponse.value.content)
554
+
555
+ if (sanitized.wasTruncated) {
556
+ this.logger.warn('LLM response was truncated (potential hallucination)', {
557
+ agentId: this.id,
558
+ sessionId: this.store.sessionId,
559
+ })
560
+ }
561
+
562
+ // Build response object
563
+ let response: LLMResponse = {
564
+ content: sanitized.content,
565
+ toolCalls: llmResponse.value.toolCalls.map((tc) => ({
566
+ id: tc.id,
567
+ name: tc.name,
568
+ input: tc.input,
569
+ })),
570
+ }
571
+
572
+ // 4c. afterInference handler - can modify response, request retry, or pause
573
+ // Re-read state: inference events have been processed since last read
574
+ const postInferenceState = this.state
575
+ if (postInferenceState) {
576
+ agentState = postInferenceState
577
+ }
578
+ const afterResult = await this.executeAfterInference(agentState, response)
579
+ if (afterResult !== null) {
580
+ if (afterResult.action === 'pause') {
581
+ // Inference completed and messages were consumed — commit the turn
582
+ // before pausing so pendingMessages move to conversationHistory.
583
+ await this.emitInferenceCompleted(response, llmCallId, llmResponse.value.metrics)
584
+ await this.emitHandlerPause(afterResult.reason)
585
+ return
586
+ }
587
+ if (afterResult.action === 'retry') {
588
+ if (retryCount >= Agent.MAX_INFERENCE_RETRIES) {
589
+ this.logger.warn('afterInference retry limit reached, continuing with current response', {
590
+ agentId: this.id,
591
+ retryCount,
592
+ })
593
+ } else {
594
+ // Retry inference - decrement turn number and recursively call with fresh state
595
+ this.turnNumber--
596
+ const freshState = this.state
597
+ if (!freshState) return
598
+ await this.runInference(freshState, retryCount + 1)
599
+ return
600
+ }
601
+ } else if (afterResult.action === 'modify') {
602
+ response = afterResult.response
603
+ }
604
+ }
605
+
606
+ // 4d. Inference completed
607
+ // Tool calls will be executed in the next continue() cycle
608
+ await this.emitInferenceCompleted(response, llmCallId, llmResponse.value.metrics)
609
+ }
610
+
611
+ /**
612
+ * Emit inference_completed event.
613
+ */
614
+ private async emitInferenceCompleted(
615
+ response: LLMResponse,
616
+ llmCallId: LLMCallId | undefined,
617
+ metrics?: {
618
+ promptTokens: number
619
+ completionTokens: number
620
+ totalTokens: number
621
+ latencyMs: number
622
+ model: string
623
+ provider?: string
624
+ cost?: number
625
+ cachedTokens?: number
626
+ cacheWriteTokens?: number
627
+ },
628
+ ): Promise<void> {
629
+ await this.store.emit(withSessionId(
630
+ this.store.sessionId,
631
+ llmEvents.create('inference_completed', {
632
+ agentId: this.id,
633
+ consumedMessageIds: [],
634
+ response: {
635
+ content: response.content,
636
+ toolCalls: response.toolCalls.map((tc) => ({
637
+ id: ToolCallId(tc.id),
638
+ name: tc.name,
639
+ input: tc.input,
640
+ })),
641
+ },
642
+ metrics: metrics ?? {
643
+ promptTokens: 0,
644
+ completionTokens: 0,
645
+ totalTokens: 0,
646
+ latencyMs: 0,
647
+ model: 'handler-skip',
648
+ },
649
+ llmCallId,
650
+ }),
651
+ ))
652
+ }
653
+
654
+ // ============================================================================
655
+ // Private methods - Scheduling
656
+ // ============================================================================
657
+
658
+ /**
659
+ * Cancel any scheduled processing.
660
+ */
661
+ private cancelSchedule(): void {
662
+ if (this.debounceTimer) {
663
+ clearTimeout(this.debounceTimer)
664
+ this.debounceTimer = undefined
665
+ }
666
+ this.scheduled = false
667
+ this.pendingReschedule = false
668
+ }
669
+
670
+ /**
671
+ * Execute a single tool call.
672
+ */
673
+ private async executeToolCall(toolCall: ToolCall): Promise<void> {
674
+ const agentState = this.state
675
+ if (!agentState) return
676
+
677
+ // beforeToolCall handler - can block, replace, or pause the tool call
678
+ let effectiveToolCall = toolCall
679
+ const beforeResult = await this.executeBeforeToolCall(agentState, toolCall)
680
+ if (beforeResult !== null) {
681
+ if (beforeResult.action === 'pause') {
682
+ return
683
+ }
684
+ if (beforeResult.action === 'block') {
685
+ // Emit tool_failed with the block reason
686
+ await this.store.emit(withSessionId(
687
+ this.store.sessionId,
688
+ toolEvents.create('tool_started', {
689
+ agentId: this.id,
690
+ toolCallId: toolCall.id,
691
+ toolName: toolCall.name,
692
+ input: toInputRecord(toolCall.input),
693
+ }),
694
+ ))
695
+ await this.store.emit(withSessionId(
696
+ this.store.sessionId,
697
+ toolEvents.create('tool_failed', {
698
+ agentId: this.id,
699
+ toolCallId: toolCall.id,
700
+ error: `Tool blocked by handler: ${beforeResult.reason}`,
701
+ }),
702
+ ))
703
+ return
704
+ } else if (beforeResult.action === 'replace') {
705
+ effectiveToolCall = {
706
+ id: ToolCallId(beforeResult.toolCall.id),
707
+ name: beforeResult.toolCall.name,
708
+ input: beforeResult.toolCall.input,
709
+ }
710
+ }
711
+ }
712
+
713
+ // Start event
714
+ await this.store.emit(withSessionId(
715
+ this.store.sessionId,
716
+ toolEvents.create('tool_started', {
717
+ agentId: this.id,
718
+ toolCallId: effectiveToolCall.id,
719
+ toolName: effectiveToolCall.name,
720
+ input: toInputRecord(effectiveToolCall.input),
721
+ }),
722
+ ))
723
+
724
+ const tool = this.tools.get(effectiveToolCall.name)
725
+ if (!tool) {
726
+ await this.store.emit(withSessionId(
727
+ this.store.sessionId,
728
+ toolEvents.create('tool_failed', {
729
+ agentId: this.id,
730
+ toolCallId: effectiveToolCall.id,
731
+ error: `Unknown tool: ${effectiveToolCall.name}`,
732
+ }),
733
+ ))
734
+ return
735
+ }
736
+
737
+ const context: ToolContext = {
738
+ ...this.buildAgentContext(agentState),
739
+ logger: this.logger.child({ toolName: toolCall.name }),
740
+ }
741
+
742
+ const result = await this.toolExecutor.execute(tool, effectiveToolCall.input, context)
743
+
744
+ // Build initial tool result with ToolResultContent
745
+ let toolResult: { isError: boolean; content: ToolResultContent } = result.ok
746
+ ? { isError: false, content: result.value }
747
+ : { isError: true, content: result.error.message }
748
+
749
+ // afterToolCall handler - can modify result or pause
750
+ const currentAgentState = this.state
751
+ if (currentAgentState) {
752
+ const afterResult = await this.executeAfterToolCall(currentAgentState, effectiveToolCall, toolResult)
753
+ if (afterResult !== null) {
754
+ if (afterResult.action === 'pause') {
755
+ return
756
+ }
757
+ if (afterResult.action === 'modify') {
758
+ toolResult = afterResult.result
759
+ }
760
+ }
761
+ }
762
+
763
+ // Result event
764
+ if (!toolResult.isError) {
765
+ await this.store.emit(withSessionId(
766
+ this.store.sessionId,
767
+ toolEvents.create('tool_completed', {
768
+ agentId: this.id,
769
+ toolCallId: effectiveToolCall.id,
770
+ result: toolResult.content,
771
+ }),
772
+ ))
773
+ } else {
774
+ // Convert content to string for tool_failed error field
775
+ const errorMessage = typeof toolResult.content === 'string'
776
+ ? toolResult.content
777
+ : JSON.stringify(toolResult.content)
778
+ await this.store.emit(withSessionId(
779
+ this.store.sessionId,
780
+ toolEvents.create('tool_failed', {
781
+ agentId: this.id,
782
+ toolCallId: effectiveToolCall.id,
783
+ error: errorMessage,
784
+ }),
785
+ ))
786
+ }
787
+ }
788
+
789
+ // ============================================================================
790
+ // Handler execution methods
791
+ // ============================================================================
792
+
793
+ /**
794
+ * Emit agent_paused event with reason 'handler'.
795
+ */
796
+ private async emitHandlerPause(message?: string): Promise<void> {
797
+ await this.store.emit(withSessionId(
798
+ this.store.sessionId,
799
+ agentEvents.create('agent_paused', {
800
+ agentId: this.id,
801
+ reason: 'handler',
802
+ message,
803
+ }),
804
+ ))
805
+ }
806
+
807
+ /**
808
+ * Build base AgentContext for handler/hook calls.
809
+ */
810
+ private buildAgentContext(agentState: AgentState): AgentContext {
811
+ return {
812
+ // SessionContext fields (refreshed from store for up-to-date state)
813
+ sessionId: this.sessionContext.sessionId,
814
+ sessionState: this.store.getState(),
815
+ sessionInput: this.sessionContext.sessionInput,
816
+ environment: this.sessionContext.environment,
817
+ llm: this.sessionContext.llm,
818
+ files: this.sessionContext.files,
819
+ eventStore: this.sessionContext.eventStore,
820
+ llmLogger: this.sessionContext.llmLogger,
821
+ platform: this.sessionContext.platform,
822
+ logger: this.logger,
823
+ emitEvent: this.sessionContext.emitEvent,
824
+ notify: this.sessionContext.notify,
825
+ // AgentContext fields
826
+ agentId: this.id,
827
+ agentState,
828
+ agentConfig: this.config,
829
+ input: agentState.typedInput,
830
+ parentId: agentState.parentId,
831
+ }
832
+ }
833
+
834
+ /**
835
+ * Build PluginHookContext for a specific plugin.
836
+ * Adds pluginConfig, pluginAgentConfig, pluginContext, pluginState, self.
837
+ */
838
+ private buildPluginHookContext(plugin: ConfiguredPlugin, agentContext: AgentContext): BasePluginHookContext {
839
+ const pluginState = plugin.slice
840
+ ? plugin.slice.select(this.store.getState())
841
+ : undefined
842
+
843
+ const self: Record<string, (input: unknown) => Promise<unknown>> = {}
844
+ for (const [methodName] of Object.entries(plugin.methods)) {
845
+ self[methodName] = async (input: unknown) => {
846
+ if (!this.pluginMethodCaller) {
847
+ throw new Error('pluginMethodCaller not available')
848
+ }
849
+ return this.pluginMethodCaller(plugin.name, methodName, input)
850
+ }
851
+ }
852
+
853
+ const sendNotification = this.sendNotification
854
+ const pluginName = plugin.name
855
+
856
+ const deps = this.pluginMethodCaller
857
+ ? buildPluginDeps(plugin.dependencyNames, this.plugins, this.pluginMethodCaller)
858
+ : {}
859
+
860
+ const schedule = this.scheduleCallback ?? (() => {})
861
+
862
+ return {
863
+ ...agentContext,
864
+ pluginConfig: undefined, // injected by plugin builder wrapper
865
+ pluginAgentConfig: this.config.plugins?.find(c => c.pluginName === plugin.name)?.config,
866
+ pluginContext: this.pluginContexts.get(plugin.name),
867
+ pluginState,
868
+ self,
869
+ schedule,
870
+ notify: (type: string, payload: unknown) => {
871
+ sendNotification?.({ pluginName, type, payload })
872
+ },
873
+ deps,
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Emit handler_completed event.
879
+ *
880
+ * Skipped when the handler produced no action (null result) — those events are
881
+ * pure noise (64%+ of a typical session log). onStart is the one exception: the
882
+ * reducer uses its completion event to flip `onStartCalled`, so we always emit
883
+ * it even with a null result.
884
+ */
885
+ private async emitHandlerCompleted(handlerName: HandlerName, result: HandlerResult): Promise<void> {
886
+ if (result === null && handlerName !== 'onStart') return
887
+
888
+ await this.store.emit(withSessionId(
889
+ this.store.sessionId,
890
+ agentEvents.create('handler_completed', {
891
+ agentId: this.id,
892
+ handlerName,
893
+ result,
894
+ }),
895
+ ))
896
+ }
897
+
898
+ /**
899
+ * Execute onStart handler - called once on first inference.
900
+ */
901
+ private async executeOnStart(agentState: AgentState): Promise<OnStartResult> {
902
+ this.logger.debug('Executing onStart handlers', { agentId: this.id })
903
+
904
+ const agentContext = this.buildAgentContext(agentState)
905
+
906
+ // Run all plugin handlers with per-plugin isolation
907
+ // Note: Handlers emit preamble_added events directly via ctx.emitEvent()
908
+ let pauseResult: OnStartResult = null
909
+
910
+ for (const plugin of this.plugins) {
911
+ if (!plugin.agentHooks?.onStart) continue
912
+ try {
913
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
914
+ const result = await plugin.agentHooks.onStart(ctx)
915
+
916
+ if (result !== null && result.action === 'pause') {
917
+ // Record first pause, but continue running other handlers
918
+ if (pauseResult === null) {
919
+ pauseResult = result
920
+ }
921
+ }
922
+ } catch (error) {
923
+ this.logger.error(`Plugin '${plugin.name}' onStart hook failed`, error instanceof Error ? error : undefined, {
924
+ agentId: this.id,
925
+ plugin: plugin.name,
926
+ })
927
+ }
928
+ }
929
+
930
+ await this.emitHandlerCompleted('onStart', pauseResult)
931
+
932
+ if (pauseResult !== null) {
933
+ await this.emitHandlerPause(pauseResult.reason)
934
+ }
935
+
936
+ return pauseResult
937
+ }
938
+
939
+ /**
940
+ * Execute beforeInference handler.
941
+ */
942
+ private async executeBeforeInference(
943
+ agentState: AgentState,
944
+ ): Promise<BeforeInferenceResult> {
945
+ this.logger.debug('Executing beforeInference handlers', {
946
+ agentId: this.id,
947
+ turnNumber: this.turnNumber,
948
+ })
949
+
950
+ const agentContext = this.buildAgentContext(agentState)
951
+
952
+ // Run plugin handlers with per-plugin isolation - first skip/pause wins
953
+ for (const plugin of this.plugins) {
954
+ if (!plugin.agentHooks?.beforeInference) continue
955
+ try {
956
+ const ctx = {
957
+ ...this.buildPluginHookContext(plugin, agentContext),
958
+ pendingMessages: getUnconsumedMessages(this.store.getState(), this.id),
959
+ turnNumber: this.turnNumber,
960
+ }
961
+ const result = await plugin.agentHooks.beforeInference(ctx)
962
+ if (result === null) {
963
+ continue
964
+ }
965
+ switch (result.action) {
966
+ case 'skip':
967
+ await this.emitHandlerCompleted('beforeInference', result)
968
+ return result
969
+ case 'pause':
970
+ await this.emitHandlerCompleted('beforeInference', result)
971
+ await this.emitHandlerPause(result.reason)
972
+ return result
973
+ default:
974
+ throw new Error(`Unhandled beforeInference action: ${(result as { action: string }).action}`)
975
+ }
976
+ } catch (error) {
977
+ this.logger.error(`Plugin '${plugin.name}' beforeInference hook failed`, error instanceof Error ? error : undefined, {
978
+ agentId: this.id,
979
+ plugin: plugin.name,
980
+ })
981
+ }
982
+ }
983
+
984
+ await this.emitHandlerCompleted('beforeInference', null)
985
+ return null
986
+ }
987
+
988
+ /**
989
+ * Execute afterInference handler.
990
+ */
991
+ private async executeAfterInference(
992
+ agentState: AgentState,
993
+ response: LLMResponse,
994
+ ): Promise<AfterInferenceResult> {
995
+ this.logger.debug('Executing afterInference handlers', {
996
+ agentId: this.id,
997
+ turnNumber: this.turnNumber,
998
+ })
999
+
1000
+ const agentContext = this.buildAgentContext(agentState)
1001
+
1002
+ // Run plugin handlers with per-plugin isolation - first retry/modify/pause wins
1003
+ for (const plugin of this.plugins) {
1004
+ if (!plugin.agentHooks?.afterInference) continue
1005
+ try {
1006
+ const ctx = {
1007
+ ...this.buildPluginHookContext(plugin, agentContext),
1008
+ response,
1009
+ turnNumber: this.turnNumber,
1010
+ }
1011
+ const result = await plugin.agentHooks.afterInference(ctx)
1012
+ if (result === null) {
1013
+ continue
1014
+ }
1015
+ switch (result.action) {
1016
+ case 'retry':
1017
+ await this.emitHandlerCompleted('afterInference', result)
1018
+ return result
1019
+ case 'modify':
1020
+ await this.emitHandlerCompleted('afterInference', result)
1021
+ return result
1022
+ case 'pause':
1023
+ await this.emitHandlerCompleted('afterInference', result)
1024
+ // Don't emit agent_paused here — caller commits inference first,
1025
+ // then pauses (so conversationHistory includes the completed turn).
1026
+ return result
1027
+ default:
1028
+ throw new Error(`Unhandled afterInference action: ${(result as { action: string }).action}`)
1029
+ }
1030
+ } catch (error) {
1031
+ this.logger.error(`Plugin '${plugin.name}' afterInference hook failed`, error instanceof Error ? error : undefined, {
1032
+ agentId: this.id,
1033
+ plugin: plugin.name,
1034
+ })
1035
+ }
1036
+ }
1037
+
1038
+ await this.emitHandlerCompleted('afterInference', null)
1039
+ return null
1040
+ }
1041
+
1042
+ /**
1043
+ * Execute beforeToolCall handler.
1044
+ */
1045
+ private async executeBeforeToolCall(
1046
+ agentState: AgentState,
1047
+ toolCall: ToolCall,
1048
+ ): Promise<BeforeToolCallResult | { action: 'replace'; toolCall: ToolCall }> {
1049
+ this.logger.debug('Executing beforeToolCall handlers', {
1050
+ agentId: this.id,
1051
+ toolName: toolCall.name,
1052
+ })
1053
+
1054
+ const agentContext = this.buildAgentContext(agentState)
1055
+
1056
+ // Run plugin handlers with per-plugin isolation - first block/replace/pause wins
1057
+ for (const plugin of this.plugins) {
1058
+ if (!plugin.agentHooks?.beforeToolCall) continue
1059
+ try {
1060
+ const ctx = {
1061
+ ...this.buildPluginHookContext(plugin, agentContext),
1062
+ toolCall: {
1063
+ id: toolCall.id,
1064
+ name: toolCall.name,
1065
+ input: toolCall.input,
1066
+ },
1067
+ }
1068
+ const result = await plugin.agentHooks.beforeToolCall(ctx)
1069
+ if (result === null) {
1070
+ continue
1071
+ }
1072
+ switch (result.action) {
1073
+ case 'block':
1074
+ await this.emitHandlerCompleted('beforeToolCall', result)
1075
+ return result
1076
+ case 'replace':
1077
+ await this.emitHandlerCompleted('beforeToolCall', result)
1078
+ return {
1079
+ action: 'replace',
1080
+ toolCall: {
1081
+ id: result.toolCall.id,
1082
+ name: result.toolCall.name,
1083
+ input: result.toolCall.input,
1084
+ },
1085
+ }
1086
+ case 'pause':
1087
+ await this.emitHandlerCompleted('beforeToolCall', result)
1088
+ await this.emitHandlerPause(result.reason)
1089
+ return result
1090
+ default:
1091
+ throw new Error(`Unhandled beforeToolCall action: ${(result as { action: string }).action}`)
1092
+ }
1093
+ } catch (error) {
1094
+ this.logger.error(`Plugin '${plugin.name}' beforeToolCall hook failed`, error instanceof Error ? error : undefined, {
1095
+ agentId: this.id,
1096
+ plugin: plugin.name,
1097
+ toolName: toolCall.name,
1098
+ })
1099
+ }
1100
+ }
1101
+
1102
+ await this.emitHandlerCompleted('beforeToolCall', null)
1103
+ return null
1104
+ }
1105
+
1106
+ /**
1107
+ * Execute afterToolCall handler.
1108
+ */
1109
+ private async executeAfterToolCall(
1110
+ agentState: AgentState,
1111
+ toolCall: ToolCall,
1112
+ toolResult: { isError: boolean; content: ToolResultContent },
1113
+ ): Promise<AfterToolCallResult> {
1114
+ this.logger.debug('Executing afterToolCall handlers', {
1115
+ agentId: this.id,
1116
+ toolName: toolCall.name,
1117
+ })
1118
+
1119
+ const agentContext = this.buildAgentContext(agentState)
1120
+
1121
+ // Run plugin handlers with per-plugin isolation - first modify/pause wins
1122
+ for (const plugin of this.plugins) {
1123
+ if (!plugin.agentHooks?.afterToolCall) continue
1124
+ try {
1125
+ const ctx = {
1126
+ ...this.buildPluginHookContext(plugin, agentContext),
1127
+ toolCall: {
1128
+ id: toolCall.id,
1129
+ name: toolCall.name,
1130
+ input: toolCall.input,
1131
+ },
1132
+ result: toolResult,
1133
+ }
1134
+ const result = await plugin.agentHooks.afterToolCall(ctx)
1135
+ if (result === null) {
1136
+ continue
1137
+ }
1138
+ switch (result.action) {
1139
+ case 'modify':
1140
+ await this.emitHandlerCompleted('afterToolCall', result)
1141
+ return result
1142
+ case 'pause':
1143
+ await this.emitHandlerCompleted('afterToolCall', result)
1144
+ await this.emitHandlerPause(result.reason)
1145
+ return result
1146
+ default:
1147
+ throw new Error(`Unhandled afterToolCall action: ${(result as { action: string }).action}`)
1148
+ }
1149
+ } catch (error) {
1150
+ this.logger.error(`Plugin '${plugin.name}' afterToolCall hook failed`, error instanceof Error ? error : undefined, {
1151
+ agentId: this.id,
1152
+ plugin: plugin.name,
1153
+ toolName: toolCall.name,
1154
+ })
1155
+ }
1156
+ }
1157
+
1158
+ await this.emitHandlerCompleted('afterToolCall', null)
1159
+ return null
1160
+ }
1161
+
1162
+ /**
1163
+ * Execute onComplete handler.
1164
+ */
1165
+ private async executeOnComplete(agentState: AgentState): Promise<void> {
1166
+ this.logger.debug('Executing onComplete handlers', { agentId: this.id })
1167
+
1168
+ const agentContext = this.buildAgentContext(agentState)
1169
+
1170
+ // Run all plugin handlers with per-plugin isolation - first pause wins
1171
+ for (const plugin of this.plugins) {
1172
+ if (!plugin.agentHooks?.onComplete) continue
1173
+ try {
1174
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1175
+ const pluginResult = await plugin.agentHooks.onComplete(ctx)
1176
+ if (pluginResult === null) {
1177
+ continue
1178
+ }
1179
+ switch (pluginResult.action) {
1180
+ case 'pause':
1181
+ await this.emitHandlerCompleted('onComplete', pluginResult)
1182
+ await this.emitHandlerPause(pluginResult.reason)
1183
+ return
1184
+ default:
1185
+ throw new Error(`Unhandled onComplete action: ${(pluginResult as { action: string }).action}`)
1186
+ }
1187
+ } catch (error) {
1188
+ this.logger.error(`Plugin '${plugin.name}' onComplete hook failed`, error instanceof Error ? error : undefined, {
1189
+ agentId: this.id,
1190
+ plugin: plugin.name,
1191
+ })
1192
+ }
1193
+ }
1194
+
1195
+ await this.emitHandlerCompleted('onComplete', null)
1196
+ }
1197
+
1198
+ /**
1199
+ * Execute onError handler.
1200
+ */
1201
+ private async executeOnError(agentState: AgentState, error: string): Promise<void> {
1202
+ this.logger.debug('Executing onError handlers', { agentId: this.id })
1203
+
1204
+ const agentContext = this.buildAgentContext(agentState)
1205
+
1206
+ // Run all plugin handlers with per-plugin isolation - first pause wins
1207
+ for (const plugin of this.plugins) {
1208
+ if (!plugin.agentHooks?.onError) continue
1209
+ try {
1210
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1211
+ const pluginResult = await plugin.agentHooks.onError({ ...ctx, error })
1212
+ if (pluginResult === null) {
1213
+ continue
1214
+ }
1215
+ switch (pluginResult.action) {
1216
+ case 'pause':
1217
+ await this.emitHandlerCompleted('onError', pluginResult)
1218
+ await this.emitHandlerPause(pluginResult.reason)
1219
+ return
1220
+ default:
1221
+ throw new Error(`Unhandled onError action: ${(pluginResult as { action: string }).action}`)
1222
+ }
1223
+ } catch (error) {
1224
+ this.logger.error(`Plugin '${plugin.name}' onError hook failed`, error instanceof Error ? error : undefined, {
1225
+ agentId: this.id,
1226
+ plugin: plugin.name,
1227
+ })
1228
+ }
1229
+ }
1230
+
1231
+ await this.emitHandlerCompleted('onError', null)
1232
+ }
1233
+
1234
+ // ============================================================================
1235
+ // Message building
1236
+ // ============================================================================
1237
+
1238
+ /**
1239
+ * Build pending messages from tool results.
1240
+ * Mailbox messages are handled by the mailbox plugin's dequeue mechanism.
1241
+ */
1242
+ private buildPendingMessages(agentState: AgentState): LLMMessage[] {
1243
+ const pending: LLMMessage[] = []
1244
+
1245
+ for (const ptr of agentState.pendingToolResults) {
1246
+ pending.push({
1247
+ role: 'tool',
1248
+ toolCallId: ptr.toolCallId,
1249
+ toolName: ptr.toolName,
1250
+ content: ptr.content,
1251
+ isError: ptr.isError,
1252
+ timestamp: ptr.timestamp,
1253
+ })
1254
+ }
1255
+
1256
+ return pending
1257
+ }
1258
+
1259
+ /**
1260
+ * Build LLM messages from agent state and pending messages.
1261
+ * Order: [preamble, conversation history, pending messages]
1262
+ * - Preamble is never compacted (includes skills injected by plugin)
1263
+ * - Conversation history may be compacted
1264
+ * - Pending messages are ephemeral (for current turn)
1265
+ */
1266
+ private buildLLMMessages(
1267
+ agentState: AgentState,
1268
+ pendingMessages: LLMMessage[],
1269
+ ): LLMMessage[] {
1270
+ const messages: LLMMessage[] = []
1271
+
1272
+ // 1. Preamble (ALWAYS prepended, NEVER compacted — includes skills from plugin)
1273
+ messages.push(...agentState.preamble)
1274
+
1275
+ // 2. Conversation history (may be compacted)
1276
+ messages.push(...agentState.conversationHistory)
1277
+
1278
+ // 3. Pending messages
1279
+ messages.push(...pendingMessages)
1280
+
1281
+ return messages
1282
+ }
1283
+
1284
+ // ============================================================================
1285
+ // Plugin system helpers
1286
+ // ============================================================================
1287
+
1288
+ /**
1289
+ * Build merged tools map from config (preset-level) and plugin tools.
1290
+ * Plugin tools override config tools with the same name.
1291
+ */
1292
+ private buildToolsMap(): Map<string, ToolDefinition> {
1293
+ const tools = new Map<string, ToolDefinition>()
1294
+ /** Track which source registered each tool name for collision detection */
1295
+ const toolSources = new Map<string, string>()
1296
+
1297
+ // 1. Static tools from config (preset-level)
1298
+ for (const tool of this.config.tools ?? []) {
1299
+ tools.set(tool.name, tool)
1300
+ toolSources.set(tool.name, 'config')
1301
+ }
1302
+
1303
+ // 2. Plugin tools (override static)
1304
+ const agentState = this.state
1305
+ if (agentState) {
1306
+ const agentContext = this.buildAgentContext(agentState)
1307
+ for (const plugin of this.plugins) {
1308
+ if (!plugin.getTools) continue
1309
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1310
+ for (const tool of plugin.getTools(ctx)) {
1311
+ const existing = toolSources.get(tool.name)
1312
+ if (existing) {
1313
+ this.logger.warn(`Tool name collision: '${tool.name}' from plugin '${plugin.name}' overrides '${existing}'`, {
1314
+ agentId: this.id,
1315
+ toolName: tool.name,
1316
+ })
1317
+ }
1318
+ tools.set(tool.name, tool)
1319
+ toolSources.set(tool.name, `plugin:${plugin.name}`)
1320
+ }
1321
+ }
1322
+ }
1323
+
1324
+ return tools
1325
+ }
1326
+
1327
+ /**
1328
+ * Build composed system prompt from base briefing, plugin sections, environment, and preset prompt.
1329
+ */
1330
+ private buildSystemPrompt(): string {
1331
+ const sections: string[] = []
1332
+
1333
+ // 1. Framework base briefing (always first)
1334
+ sections.push(AGENT_BASE_BRIEFING)
1335
+
1336
+ // 2. Plugin system prompt sections
1337
+ const agentState = this.state
1338
+ if (agentState) {
1339
+ const agentContext = this.buildAgentContext(agentState)
1340
+ for (const plugin of this.plugins) {
1341
+ if (!plugin.getSystemPrompt) continue
1342
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1343
+ const section = plugin.getSystemPrompt(ctx)
1344
+ if (section) sections.push(section)
1345
+ }
1346
+ }
1347
+
1348
+ // 3. Environment section
1349
+ const roots = this.fileStore.getRoots()
1350
+ sections.push(buildEnvironmentSection({
1351
+ sessionPath: roots.session,
1352
+ workspacePath: roots.workspace,
1353
+ }))
1354
+
1355
+ // 4. Custom prompt from preset (last)
1356
+ if (this.config.systemPrompt) {
1357
+ let customPrompt = this.config.systemPrompt
1358
+ customPrompt = customPrompt.replaceAll('{{sessionDir}}', roots.session)
1359
+ if (roots.workspace) {
1360
+ customPrompt = customPrompt.replaceAll('{{workspaceDir}}', roots.workspace)
1361
+ }
1362
+ sections.push(customPrompt)
1363
+ }
1364
+
1365
+ return sections.filter(Boolean).join('\n\n').trim()
1366
+ }
1367
+
1368
+ /**
1369
+ * Get combined status from all plugins.
1370
+ */
1371
+ private getPluginStatus(): string | null {
1372
+ const agentState = this.state
1373
+ if (!agentState) return null
1374
+
1375
+ const agentContext = this.buildAgentContext(agentState)
1376
+ const parts: string[] = []
1377
+
1378
+ for (const plugin of this.plugins) {
1379
+ if (!plugin.getStatus) continue
1380
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1381
+ const status = plugin.getStatus(ctx)
1382
+ if (status) parts.push(status)
1383
+ }
1384
+
1385
+ return parts.length > 0 ? parts.join('\n\n') : null
1386
+ }
1387
+
1388
+ // ============================================================================
1389
+ // Plugin dequeue helpers
1390
+ // ============================================================================
1391
+
1392
+ /**
1393
+ * Check if any plugin has pending messages for this agent.
1394
+ */
1395
+ private hasPluginPendingMessages(): boolean {
1396
+ const agentState = this.state
1397
+ if (!agentState) return false
1398
+
1399
+ const agentContext = this.buildAgentContext(agentState)
1400
+ for (const plugin of this.plugins) {
1401
+ if (!plugin.dequeue) continue
1402
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1403
+ if (plugin.dequeue.hasPendingMessages(ctx)) return true
1404
+ }
1405
+ return false
1406
+ }
1407
+
1408
+ /**
1409
+ * Collect pending messages from all plugins that have dequeue hooks.
1410
+ * Returns array of { plugin, messages, token } for each plugin with pending messages.
1411
+ */
1412
+ private collectPluginMessages(): Array<{
1413
+ plugin: ConfiguredPlugin
1414
+ messages: LLMMessage[]
1415
+ token: unknown
1416
+ }> {
1417
+ const agentState = this.state
1418
+ if (!agentState) return []
1419
+
1420
+ const agentContext = this.buildAgentContext(agentState)
1421
+ const collected: Array<{
1422
+ plugin: ConfiguredPlugin
1423
+ messages: LLMMessage[]
1424
+ token: unknown
1425
+ }> = []
1426
+
1427
+ for (const plugin of this.plugins) {
1428
+ if (!plugin.dequeue) continue
1429
+ const ctx = this.buildPluginHookContext(plugin, agentContext)
1430
+ const result = plugin.dequeue.getPendingMessages(ctx)
1431
+ if (result) {
1432
+ collected.push({
1433
+ plugin,
1434
+ messages: result.messages,
1435
+ token: result.token,
1436
+ })
1437
+ }
1438
+ }
1439
+
1440
+ return collected
1441
+ }
1442
+ }
1443
+
1444
+ // ============================================================================
1445
+ // Local helpers (inlined from @roj-ai/core)
1446
+ // ============================================================================
1447
+
1448
+ function getUnconsumedMessages(sessionState: SessionState, agentId: AgentId) {
1449
+ return getAgentUnconsumedMailbox(selectMailboxState(sessionState), agentId)
1450
+ }
1451
+
1452
+ function hasWork(agent: AgentState): boolean {
1453
+ // Has pending tool results - needs LLM to process
1454
+ if (agent.pendingToolResults.length > 0) return true
1455
+
1456
+ return false
1457
+ }
1458
+
1459
+ /**
1460
+ * Narrow tool call input from `unknown` to `Record<string, unknown>`.
1461
+ * Tool inputs are always validated objects from Zod schemas.
1462
+ */
1463
+ function isRecord(value: unknown): value is Record<string, unknown> {
1464
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
1465
+ }
1466
+
1467
+ function toInputRecord(input: unknown): Record<string, unknown> {
1468
+ if (isRecord(input)) {
1469
+ return input
1470
+ }
1471
+ return {}
1472
+ }