@oni.bot/core 0.6.3 → 1.0.0

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 (736) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +64 -263
  3. package/SECURITY.md +71 -0
  4. package/dist/checkpointers/sqlite.d.ts.map +1 -1
  5. package/dist/checkpointers/sqlite.js +42 -25
  6. package/dist/checkpointers/sqlite.js.map +1 -1
  7. package/dist/circuit-breaker.d.ts +20 -0
  8. package/dist/circuit-breaker.d.ts.map +1 -0
  9. package/dist/circuit-breaker.js +58 -0
  10. package/dist/circuit-breaker.js.map +1 -0
  11. package/dist/cli/build.d.ts +11 -0
  12. package/dist/cli/build.d.ts.map +1 -0
  13. package/dist/cli/build.js +61 -0
  14. package/dist/cli/build.js.map +1 -0
  15. package/dist/cli/dev.d.ts +5 -0
  16. package/dist/cli/dev.d.ts.map +1 -0
  17. package/dist/cli/dev.js +54 -0
  18. package/dist/cli/dev.js.map +1 -0
  19. package/dist/cli/index.d.ts +3 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +21 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/cli/init.d.ts +4 -0
  24. package/dist/cli/init.d.ts.map +1 -0
  25. package/dist/cli/init.js +34 -0
  26. package/dist/cli/init.js.map +1 -0
  27. package/dist/cli/inspect.d.ts +5 -0
  28. package/dist/cli/inspect.d.ts.map +1 -0
  29. package/dist/cli/inspect.js +85 -0
  30. package/dist/cli/inspect.js.map +1 -0
  31. package/dist/cli/router.d.ts +14 -0
  32. package/dist/cli/router.d.ts.map +1 -0
  33. package/dist/cli/router.js +107 -0
  34. package/dist/cli/router.js.map +1 -0
  35. package/dist/cli/run.d.ts +5 -0
  36. package/dist/cli/run.d.ts.map +1 -0
  37. package/dist/cli/run.js +53 -0
  38. package/dist/cli/run.js.map +1 -0
  39. package/dist/cli/templates.d.ts +9 -0
  40. package/dist/cli/templates.d.ts.map +1 -0
  41. package/dist/cli/templates.js +159 -0
  42. package/dist/cli/templates.js.map +1 -0
  43. package/dist/cli/test.d.ts +3 -0
  44. package/dist/cli/test.d.ts.map +1 -0
  45. package/dist/cli/test.js +29 -0
  46. package/dist/cli/test.js.map +1 -0
  47. package/dist/config/index.d.ts +3 -0
  48. package/dist/config/index.d.ts.map +1 -0
  49. package/dist/config/index.js +3 -0
  50. package/dist/config/index.js.map +1 -0
  51. package/dist/config/loader.d.ts +37 -0
  52. package/dist/config/loader.d.ts.map +1 -0
  53. package/dist/config/loader.js +180 -0
  54. package/dist/config/loader.js.map +1 -0
  55. package/dist/config/types.d.ts +74 -0
  56. package/dist/config/types.d.ts.map +1 -0
  57. package/dist/config/types.js +5 -0
  58. package/dist/config/types.js.map +1 -0
  59. package/dist/coordination/pubsub.d.ts +4 -0
  60. package/dist/coordination/pubsub.d.ts.map +1 -1
  61. package/dist/coordination/pubsub.js +8 -0
  62. package/dist/coordination/pubsub.js.map +1 -1
  63. package/dist/coordination/request-reply.d.ts +3 -1
  64. package/dist/coordination/request-reply.d.ts.map +1 -1
  65. package/dist/coordination/request-reply.js +11 -2
  66. package/dist/coordination/request-reply.js.map +1 -1
  67. package/dist/dlq.d.ts +17 -0
  68. package/dist/dlq.d.ts.map +1 -0
  69. package/dist/dlq.js +41 -0
  70. package/dist/dlq.js.map +1 -0
  71. package/dist/errors.d.ts +43 -2
  72. package/dist/errors.d.ts.map +1 -1
  73. package/dist/errors.js +179 -8
  74. package/dist/errors.js.map +1 -1
  75. package/dist/events/bridge.d.ts +17 -0
  76. package/dist/events/bridge.d.ts.map +1 -0
  77. package/dist/events/bridge.js +67 -0
  78. package/dist/events/bridge.js.map +1 -0
  79. package/dist/events/bus.d.ts +9 -0
  80. package/dist/events/bus.d.ts.map +1 -1
  81. package/dist/events/bus.js +39 -4
  82. package/dist/events/bus.js.map +1 -1
  83. package/dist/events/index.d.ts +2 -1
  84. package/dist/events/index.d.ts.map +1 -1
  85. package/dist/events/index.js +2 -0
  86. package/dist/events/index.js.map +1 -1
  87. package/dist/events/types.d.ts +123 -1
  88. package/dist/events/types.d.ts.map +1 -1
  89. package/dist/graph.d.ts +17 -0
  90. package/dist/graph.d.ts.map +1 -1
  91. package/dist/graph.js +29 -10
  92. package/dist/graph.js.map +1 -1
  93. package/dist/guardrails/types.d.ts +1 -1
  94. package/dist/guardrails/types.d.ts.map +1 -1
  95. package/dist/harness/agent-loop.d.ts +8 -0
  96. package/dist/harness/agent-loop.d.ts.map +1 -0
  97. package/dist/harness/agent-loop.js +524 -0
  98. package/dist/harness/agent-loop.js.map +1 -0
  99. package/dist/harness/context-compactor.d.ts +92 -0
  100. package/dist/harness/context-compactor.d.ts.map +1 -0
  101. package/dist/harness/context-compactor.js +201 -0
  102. package/dist/harness/context-compactor.js.map +1 -0
  103. package/dist/harness/harness.d.ts +41 -0
  104. package/dist/harness/harness.d.ts.map +1 -0
  105. package/dist/harness/harness.js +140 -0
  106. package/dist/harness/harness.js.map +1 -0
  107. package/dist/harness/hooks-engine.d.ts +77 -0
  108. package/dist/harness/hooks-engine.d.ts.map +1 -0
  109. package/dist/harness/hooks-engine.js +232 -0
  110. package/dist/harness/hooks-engine.js.map +1 -0
  111. package/dist/harness/index.d.ts +17 -0
  112. package/dist/harness/index.d.ts.map +1 -0
  113. package/dist/harness/index.js +21 -0
  114. package/dist/harness/index.js.map +1 -0
  115. package/dist/harness/safety-gate.d.ts +29 -0
  116. package/dist/harness/safety-gate.d.ts.map +1 -0
  117. package/dist/harness/safety-gate.js +72 -0
  118. package/dist/harness/safety-gate.js.map +1 -0
  119. package/dist/harness/skill-loader.d.ts +76 -0
  120. package/dist/harness/skill-loader.d.ts.map +1 -0
  121. package/dist/harness/skill-loader.js +244 -0
  122. package/dist/harness/skill-loader.js.map +1 -0
  123. package/dist/harness/todo-module.d.ts +39 -0
  124. package/dist/harness/todo-module.d.ts.map +1 -0
  125. package/dist/harness/todo-module.js +179 -0
  126. package/dist/harness/todo-module.js.map +1 -0
  127. package/dist/harness/types.d.ts +100 -0
  128. package/dist/harness/types.d.ts.map +1 -0
  129. package/dist/harness/types.js +9 -0
  130. package/dist/harness/types.js.map +1 -0
  131. package/dist/harness/validate-args.d.ts +16 -0
  132. package/dist/harness/validate-args.d.ts.map +1 -0
  133. package/dist/harness/validate-args.js +132 -0
  134. package/dist/harness/validate-args.js.map +1 -0
  135. package/dist/hitl/interrupt.d.ts.map +1 -1
  136. package/dist/hitl/interrupt.js +7 -6
  137. package/dist/hitl/interrupt.js.map +1 -1
  138. package/dist/index.d.ts +16 -5
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +22 -13
  141. package/dist/index.js.map +1 -1
  142. package/dist/internal/timeout.d.ts +2 -0
  143. package/dist/internal/timeout.d.ts.map +1 -0
  144. package/dist/internal/timeout.js +16 -0
  145. package/dist/internal/timeout.js.map +1 -0
  146. package/dist/lsp/client.d.ts +70 -0
  147. package/dist/lsp/client.d.ts.map +1 -0
  148. package/dist/lsp/client.js +421 -0
  149. package/dist/lsp/client.js.map +1 -0
  150. package/dist/lsp/index.d.ts +77 -0
  151. package/dist/lsp/index.d.ts.map +1 -0
  152. package/dist/lsp/index.js +183 -0
  153. package/dist/lsp/index.js.map +1 -0
  154. package/dist/lsp/servers.d.ts +48 -0
  155. package/dist/lsp/servers.d.ts.map +1 -0
  156. package/dist/lsp/servers.js +108 -0
  157. package/dist/lsp/servers.js.map +1 -0
  158. package/dist/lsp/types.d.ts +142 -0
  159. package/dist/lsp/types.d.ts.map +1 -0
  160. package/dist/lsp/types.js +16 -0
  161. package/dist/lsp/types.js.map +1 -0
  162. package/dist/mcp/client.d.ts +56 -0
  163. package/dist/mcp/client.d.ts.map +1 -0
  164. package/dist/mcp/client.js +170 -0
  165. package/dist/mcp/client.js.map +1 -0
  166. package/dist/mcp/convert.d.ts +26 -0
  167. package/dist/mcp/convert.d.ts.map +1 -0
  168. package/dist/mcp/convert.js +56 -0
  169. package/dist/mcp/convert.js.map +1 -0
  170. package/dist/mcp/index.d.ts +21 -0
  171. package/dist/mcp/index.d.ts.map +1 -0
  172. package/dist/mcp/index.js +19 -0
  173. package/dist/mcp/index.js.map +1 -0
  174. package/dist/mcp/transport.d.ts +56 -0
  175. package/dist/mcp/transport.d.ts.map +1 -0
  176. package/dist/mcp/transport.js +204 -0
  177. package/dist/mcp/transport.js.map +1 -0
  178. package/dist/mcp/types.d.ts +96 -0
  179. package/dist/mcp/types.d.ts.map +1 -0
  180. package/dist/mcp/types.js +11 -0
  181. package/dist/mcp/types.js.map +1 -0
  182. package/dist/models/anthropic.d.ts.map +1 -1
  183. package/dist/models/anthropic.js +78 -41
  184. package/dist/models/anthropic.js.map +1 -1
  185. package/dist/models/google.d.ts.map +1 -1
  186. package/dist/models/google.js +54 -52
  187. package/dist/models/google.js.map +1 -1
  188. package/dist/models/http-error.d.ts +16 -0
  189. package/dist/models/http-error.d.ts.map +1 -0
  190. package/dist/models/http-error.js +67 -0
  191. package/dist/models/http-error.js.map +1 -0
  192. package/dist/models/index.d.ts +5 -0
  193. package/dist/models/index.d.ts.map +1 -1
  194. package/dist/models/index.js +3 -0
  195. package/dist/models/index.js.map +1 -1
  196. package/dist/models/ollama.d.ts.map +1 -1
  197. package/dist/models/ollama.js +4 -3
  198. package/dist/models/ollama.js.map +1 -1
  199. package/dist/models/openai.d.ts.map +1 -1
  200. package/dist/models/openai.js +42 -45
  201. package/dist/models/openai.js.map +1 -1
  202. package/dist/models/openrouter.d.ts +26 -0
  203. package/dist/models/openrouter.d.ts.map +1 -0
  204. package/dist/models/openrouter.js +361 -0
  205. package/dist/models/openrouter.js.map +1 -0
  206. package/dist/models/sse.d.ts +9 -0
  207. package/dist/models/sse.d.ts.map +1 -0
  208. package/dist/models/sse.js +45 -0
  209. package/dist/models/sse.js.map +1 -0
  210. package/dist/models/types.d.ts +10 -0
  211. package/dist/models/types.d.ts.map +1 -1
  212. package/dist/oni-code/agent-registry.d.ts +73 -0
  213. package/dist/oni-code/agent-registry.d.ts.map +1 -0
  214. package/dist/oni-code/agent-registry.js +151 -0
  215. package/dist/oni-code/agent-registry.js.map +1 -0
  216. package/dist/oni-code/bin.d.ts +18 -0
  217. package/dist/oni-code/bin.d.ts.map +1 -0
  218. package/dist/oni-code/bin.js +78 -0
  219. package/dist/oni-code/bin.js.map +1 -0
  220. package/dist/oni-code/change-tracking.d.ts +28 -0
  221. package/dist/oni-code/change-tracking.d.ts.map +1 -0
  222. package/dist/oni-code/change-tracking.js +77 -0
  223. package/dist/oni-code/change-tracking.js.map +1 -0
  224. package/dist/oni-code/cli.d.ts +6 -0
  225. package/dist/oni-code/cli.d.ts.map +1 -0
  226. package/dist/oni-code/cli.js +30 -0
  227. package/dist/oni-code/cli.js.map +1 -0
  228. package/dist/oni-code/conductor.d.ts +203 -0
  229. package/dist/oni-code/conductor.d.ts.map +1 -0
  230. package/dist/oni-code/conductor.js +1547 -0
  231. package/dist/oni-code/conductor.js.map +1 -0
  232. package/dist/oni-code/config.d.ts +130 -0
  233. package/dist/oni-code/config.d.ts.map +1 -0
  234. package/dist/oni-code/config.js +264 -0
  235. package/dist/oni-code/config.js.map +1 -0
  236. package/dist/oni-code/context-files.d.ts +22 -0
  237. package/dist/oni-code/context-files.d.ts.map +1 -0
  238. package/dist/oni-code/context-files.js +156 -0
  239. package/dist/oni-code/context-files.js.map +1 -0
  240. package/dist/oni-code/cron/humanize.d.ts +26 -0
  241. package/dist/oni-code/cron/humanize.d.ts.map +1 -0
  242. package/dist/oni-code/cron/humanize.js +83 -0
  243. package/dist/oni-code/cron/humanize.js.map +1 -0
  244. package/dist/oni-code/cron/index.d.ts +10 -0
  245. package/dist/oni-code/cron/index.d.ts.map +1 -0
  246. package/dist/oni-code/cron/index.js +7 -0
  247. package/dist/oni-code/cron/index.js.map +1 -0
  248. package/dist/oni-code/cron/matcher.d.ts +29 -0
  249. package/dist/oni-code/cron/matcher.d.ts.map +1 -0
  250. package/dist/oni-code/cron/matcher.js +73 -0
  251. package/dist/oni-code/cron/matcher.js.map +1 -0
  252. package/dist/oni-code/cron/parser.d.ts +27 -0
  253. package/dist/oni-code/cron/parser.d.ts.map +1 -0
  254. package/dist/oni-code/cron/parser.js +149 -0
  255. package/dist/oni-code/cron/parser.js.map +1 -0
  256. package/dist/oni-code/cron/persistence.d.ts +21 -0
  257. package/dist/oni-code/cron/persistence.d.ts.map +1 -0
  258. package/dist/oni-code/cron/persistence.js +58 -0
  259. package/dist/oni-code/cron/persistence.js.map +1 -0
  260. package/dist/oni-code/cron/scheduler.d.ts +64 -0
  261. package/dist/oni-code/cron/scheduler.d.ts.map +1 -0
  262. package/dist/oni-code/cron/scheduler.js +188 -0
  263. package/dist/oni-code/cron/scheduler.js.map +1 -0
  264. package/dist/oni-code/cron/store.d.ts +46 -0
  265. package/dist/oni-code/cron/store.d.ts.map +1 -0
  266. package/dist/oni-code/cron/store.js +68 -0
  267. package/dist/oni-code/cron/store.js.map +1 -0
  268. package/dist/oni-code/env.d.ts +17 -0
  269. package/dist/oni-code/env.d.ts.map +1 -0
  270. package/dist/oni-code/env.js +51 -0
  271. package/dist/oni-code/env.js.map +1 -0
  272. package/dist/oni-code/file-reader.d.ts +28 -0
  273. package/dist/oni-code/file-reader.d.ts.map +1 -0
  274. package/dist/oni-code/file-reader.js +368 -0
  275. package/dist/oni-code/file-reader.js.map +1 -0
  276. package/dist/oni-code/file-watcher.d.ts +66 -0
  277. package/dist/oni-code/file-watcher.d.ts.map +1 -0
  278. package/dist/oni-code/file-watcher.js +167 -0
  279. package/dist/oni-code/file-watcher.js.map +1 -0
  280. package/dist/oni-code/loop-detector.d.ts +64 -0
  281. package/dist/oni-code/loop-detector.d.ts.map +1 -0
  282. package/dist/oni-code/loop-detector.js +163 -0
  283. package/dist/oni-code/loop-detector.js.map +1 -0
  284. package/dist/oni-code/permissions.d.ts +43 -0
  285. package/dist/oni-code/permissions.d.ts.map +1 -0
  286. package/dist/oni-code/permissions.js +98 -0
  287. package/dist/oni-code/permissions.js.map +1 -0
  288. package/dist/oni-code/planner.d.ts +26 -0
  289. package/dist/oni-code/planner.d.ts.map +1 -0
  290. package/dist/oni-code/planner.js +86 -0
  291. package/dist/oni-code/planner.js.map +1 -0
  292. package/dist/oni-code/plugin-loader.d.ts +103 -0
  293. package/dist/oni-code/plugin-loader.d.ts.map +1 -0
  294. package/dist/oni-code/plugin-loader.js +171 -0
  295. package/dist/oni-code/plugin-loader.js.map +1 -0
  296. package/dist/oni-code/process-tree.d.ts +44 -0
  297. package/dist/oni-code/process-tree.d.ts.map +1 -0
  298. package/dist/oni-code/process-tree.js +107 -0
  299. package/dist/oni-code/process-tree.js.map +1 -0
  300. package/dist/oni-code/progress-tracker.d.ts +40 -0
  301. package/dist/oni-code/progress-tracker.d.ts.map +1 -0
  302. package/dist/oni-code/progress-tracker.js +375 -0
  303. package/dist/oni-code/progress-tracker.js.map +1 -0
  304. package/dist/oni-code/scheduler.d.ts +56 -0
  305. package/dist/oni-code/scheduler.d.ts.map +1 -0
  306. package/dist/oni-code/scheduler.js +105 -0
  307. package/dist/oni-code/scheduler.js.map +1 -0
  308. package/dist/oni-code/session-fork.d.ts +146 -0
  309. package/dist/oni-code/session-fork.d.ts.map +1 -0
  310. package/dist/oni-code/session-fork.js +238 -0
  311. package/dist/oni-code/session-fork.js.map +1 -0
  312. package/dist/oni-code/session-stats.d.ts +72 -0
  313. package/dist/oni-code/session-stats.d.ts.map +1 -0
  314. package/dist/oni-code/session-stats.js +141 -0
  315. package/dist/oni-code/session-stats.js.map +1 -0
  316. package/dist/oni-code/session-title.d.ts +25 -0
  317. package/dist/oni-code/session-title.d.ts.map +1 -0
  318. package/dist/oni-code/session-title.js +67 -0
  319. package/dist/oni-code/session-title.js.map +1 -0
  320. package/dist/oni-code/shell-parser.d.ts +112 -0
  321. package/dist/oni-code/shell-parser.d.ts.map +1 -0
  322. package/dist/oni-code/shell-parser.js +657 -0
  323. package/dist/oni-code/shell-parser.js.map +1 -0
  324. package/dist/oni-code/summarizer.d.ts +27 -0
  325. package/dist/oni-code/summarizer.d.ts.map +1 -0
  326. package/dist/oni-code/summarizer.js +70 -0
  327. package/dist/oni-code/summarizer.js.map +1 -0
  328. package/dist/oni-code/swarm-checkpoint.d.ts +52 -0
  329. package/dist/oni-code/swarm-checkpoint.d.ts.map +1 -0
  330. package/dist/oni-code/swarm-checkpoint.js +71 -0
  331. package/dist/oni-code/swarm-checkpoint.js.map +1 -0
  332. package/dist/oni-code/swarm-runner.d.ts +173 -0
  333. package/dist/oni-code/swarm-runner.d.ts.map +1 -0
  334. package/dist/oni-code/swarm-runner.js +873 -0
  335. package/dist/oni-code/swarm-runner.js.map +1 -0
  336. package/dist/oni-code/system-prompt.d.ts +13 -0
  337. package/dist/oni-code/system-prompt.d.ts.map +1 -0
  338. package/dist/oni-code/system-prompt.js +64 -0
  339. package/dist/oni-code/system-prompt.js.map +1 -0
  340. package/dist/oni-code/task-evaluator.d.ts +73 -0
  341. package/dist/oni-code/task-evaluator.d.ts.map +1 -0
  342. package/dist/oni-code/task-evaluator.js +172 -0
  343. package/dist/oni-code/task-evaluator.js.map +1 -0
  344. package/dist/oni-code/tools/batch.d.ts +12 -0
  345. package/dist/oni-code/tools/batch.d.ts.map +1 -0
  346. package/dist/oni-code/tools/batch.js +116 -0
  347. package/dist/oni-code/tools/batch.js.map +1 -0
  348. package/dist/oni-code/tools/coding.d.ts +10 -0
  349. package/dist/oni-code/tools/coding.d.ts.map +1 -0
  350. package/dist/oni-code/tools/coding.js +557 -0
  351. package/dist/oni-code/tools/coding.js.map +1 -0
  352. package/dist/oni-code/tools/cron.d.ts +4 -0
  353. package/dist/oni-code/tools/cron.d.ts.map +1 -0
  354. package/dist/oni-code/tools/cron.js +120 -0
  355. package/dist/oni-code/tools/cron.js.map +1 -0
  356. package/dist/oni-code/tools/custom.d.ts +43 -0
  357. package/dist/oni-code/tools/custom.d.ts.map +1 -0
  358. package/dist/oni-code/tools/custom.js +115 -0
  359. package/dist/oni-code/tools/custom.js.map +1 -0
  360. package/dist/oni-code/tools/patch.d.ts +58 -0
  361. package/dist/oni-code/tools/patch.d.ts.map +1 -0
  362. package/dist/oni-code/tools/patch.js +247 -0
  363. package/dist/oni-code/tools/patch.js.map +1 -0
  364. package/dist/oni-code/tools/plan.d.ts +17 -0
  365. package/dist/oni-code/tools/plan.d.ts.map +1 -0
  366. package/dist/oni-code/tools/plan.js +48 -0
  367. package/dist/oni-code/tools/plan.js.map +1 -0
  368. package/dist/oni-code/tools/question.d.ts +17 -0
  369. package/dist/oni-code/tools/question.d.ts.map +1 -0
  370. package/dist/oni-code/tools/question.js +46 -0
  371. package/dist/oni-code/tools/question.js.map +1 -0
  372. package/dist/oni-code/tools/skill.d.ts +36 -0
  373. package/dist/oni-code/tools/skill.d.ts.map +1 -0
  374. package/dist/oni-code/tools/skill.js +132 -0
  375. package/dist/oni-code/tools/skill.js.map +1 -0
  376. package/dist/oni-code/tools/spawn-agents.d.ts +37 -0
  377. package/dist/oni-code/tools/spawn-agents.d.ts.map +1 -0
  378. package/dist/oni-code/tools/spawn-agents.js +91 -0
  379. package/dist/oni-code/tools/spawn-agents.js.map +1 -0
  380. package/dist/oni-code/tools/spawn-swarm.d.ts +70 -0
  381. package/dist/oni-code/tools/spawn-swarm.d.ts.map +1 -0
  382. package/dist/oni-code/tools/spawn-swarm.js +129 -0
  383. package/dist/oni-code/tools/spawn-swarm.js.map +1 -0
  384. package/dist/oni-code/tools/web.d.ts +11 -0
  385. package/dist/oni-code/tools/web.d.ts.map +1 -0
  386. package/dist/oni-code/tools/web.js +375 -0
  387. package/dist/oni-code/tools/web.js.map +1 -0
  388. package/dist/oni-code/topology-agent-builder.d.ts +22 -0
  389. package/dist/oni-code/topology-agent-builder.d.ts.map +1 -0
  390. package/dist/oni-code/topology-agent-builder.js +220 -0
  391. package/dist/oni-code/topology-agent-builder.js.map +1 -0
  392. package/dist/oni-code/topology-selector.d.ts +85 -0
  393. package/dist/oni-code/topology-selector.d.ts.map +1 -0
  394. package/dist/oni-code/topology-selector.js +338 -0
  395. package/dist/oni-code/topology-selector.js.map +1 -0
  396. package/dist/oni-code/ui/App.d.ts +10 -0
  397. package/dist/oni-code/ui/App.d.ts.map +1 -0
  398. package/dist/oni-code/ui/App.js +395 -0
  399. package/dist/oni-code/ui/App.js.map +1 -0
  400. package/dist/oni-code/ui/FooterPanel.d.ts +16 -0
  401. package/dist/oni-code/ui/FooterPanel.d.ts.map +1 -0
  402. package/dist/oni-code/ui/FooterPanel.js +56 -0
  403. package/dist/oni-code/ui/FooterPanel.js.map +1 -0
  404. package/dist/oni-code/ui/Header.d.ts +21 -0
  405. package/dist/oni-code/ui/Header.d.ts.map +1 -0
  406. package/dist/oni-code/ui/Header.js +105 -0
  407. package/dist/oni-code/ui/Header.js.map +1 -0
  408. package/dist/oni-code/ui/InputArea.d.ts +11 -0
  409. package/dist/oni-code/ui/InputArea.d.ts.map +1 -0
  410. package/dist/oni-code/ui/InputArea.js +82 -0
  411. package/dist/oni-code/ui/InputArea.js.map +1 -0
  412. package/dist/oni-code/ui/MessageBlock.d.ts +11 -0
  413. package/dist/oni-code/ui/MessageBlock.d.ts.map +1 -0
  414. package/dist/oni-code/ui/MessageBlock.js +103 -0
  415. package/dist/oni-code/ui/MessageBlock.js.map +1 -0
  416. package/dist/oni-code/ui/OutputPane.d.ts +12 -0
  417. package/dist/oni-code/ui/OutputPane.d.ts.map +1 -0
  418. package/dist/oni-code/ui/OutputPane.js +8 -0
  419. package/dist/oni-code/ui/OutputPane.js.map +1 -0
  420. package/dist/oni-code/ui/PermissionPrompt.d.ts +11 -0
  421. package/dist/oni-code/ui/PermissionPrompt.d.ts.map +1 -0
  422. package/dist/oni-code/ui/PermissionPrompt.js +48 -0
  423. package/dist/oni-code/ui/PermissionPrompt.js.map +1 -0
  424. package/dist/oni-code/ui/QuestionPrompt.d.ts +8 -0
  425. package/dist/oni-code/ui/QuestionPrompt.d.ts.map +1 -0
  426. package/dist/oni-code/ui/QuestionPrompt.js +9 -0
  427. package/dist/oni-code/ui/QuestionPrompt.js.map +1 -0
  428. package/dist/oni-code/ui/StatusLine.d.ts +14 -0
  429. package/dist/oni-code/ui/StatusLine.d.ts.map +1 -0
  430. package/dist/oni-code/ui/StatusLine.js +23 -0
  431. package/dist/oni-code/ui/StatusLine.js.map +1 -0
  432. package/dist/oni-code/ui/SwarmPanel.d.ts +9 -0
  433. package/dist/oni-code/ui/SwarmPanel.d.ts.map +1 -0
  434. package/dist/oni-code/ui/SwarmPanel.js +52 -0
  435. package/dist/oni-code/ui/SwarmPanel.js.map +1 -0
  436. package/dist/oni-code/ui/ToolCallBlock.d.ts +10 -0
  437. package/dist/oni-code/ui/ToolCallBlock.d.ts.map +1 -0
  438. package/dist/oni-code/ui/ToolCallBlock.js +21 -0
  439. package/dist/oni-code/ui/ToolCallBlock.js.map +1 -0
  440. package/dist/oni-code/ui/Toolbar.d.ts +8 -0
  441. package/dist/oni-code/ui/Toolbar.d.ts.map +1 -0
  442. package/dist/oni-code/ui/Toolbar.js +11 -0
  443. package/dist/oni-code/ui/Toolbar.js.map +1 -0
  444. package/dist/oni-code/ui/WelcomeBanner.d.ts +12 -0
  445. package/dist/oni-code/ui/WelcomeBanner.d.ts.map +1 -0
  446. package/dist/oni-code/ui/WelcomeBanner.js +24 -0
  447. package/dist/oni-code/ui/WelcomeBanner.js.map +1 -0
  448. package/dist/oni-code/ui/activity.d.ts +15 -0
  449. package/dist/oni-code/ui/activity.d.ts.map +1 -0
  450. package/dist/oni-code/ui/activity.js +252 -0
  451. package/dist/oni-code/ui/activity.js.map +1 -0
  452. package/dist/oni-code/ui/banner.d.ts +16 -0
  453. package/dist/oni-code/ui/banner.d.ts.map +1 -0
  454. package/dist/oni-code/ui/banner.js +132 -0
  455. package/dist/oni-code/ui/banner.js.map +1 -0
  456. package/dist/oni-code/ui/command-menu.d.ts +7 -0
  457. package/dist/oni-code/ui/command-menu.d.ts.map +1 -0
  458. package/dist/oni-code/ui/command-menu.js +20 -0
  459. package/dist/oni-code/ui/command-menu.js.map +1 -0
  460. package/dist/oni-code/ui/diff.d.ts +17 -0
  461. package/dist/oni-code/ui/diff.d.ts.map +1 -0
  462. package/dist/oni-code/ui/diff.js +37 -0
  463. package/dist/oni-code/ui/diff.js.map +1 -0
  464. package/dist/oni-code/ui/format.d.ts +41 -0
  465. package/dist/oni-code/ui/format.d.ts.map +1 -0
  466. package/dist/oni-code/ui/format.js +223 -0
  467. package/dist/oni-code/ui/format.js.map +1 -0
  468. package/dist/oni-code/ui/input.d.ts +28 -0
  469. package/dist/oni-code/ui/input.d.ts.map +1 -0
  470. package/dist/oni-code/ui/input.js +216 -0
  471. package/dist/oni-code/ui/input.js.map +1 -0
  472. package/dist/oni-code/ui/insights.d.ts +39 -0
  473. package/dist/oni-code/ui/insights.d.ts.map +1 -0
  474. package/dist/oni-code/ui/insights.js +193 -0
  475. package/dist/oni-code/ui/insights.js.map +1 -0
  476. package/dist/oni-code/ui/markdown.d.ts +9 -0
  477. package/dist/oni-code/ui/markdown.d.ts.map +1 -0
  478. package/dist/oni-code/ui/markdown.js +44 -0
  479. package/dist/oni-code/ui/markdown.js.map +1 -0
  480. package/dist/oni-code/ui/panels.d.ts +39 -0
  481. package/dist/oni-code/ui/panels.d.ts.map +1 -0
  482. package/dist/oni-code/ui/panels.js +209 -0
  483. package/dist/oni-code/ui/panels.js.map +1 -0
  484. package/dist/oni-code/ui/paste.d.ts +17 -0
  485. package/dist/oni-code/ui/paste.d.ts.map +1 -0
  486. package/dist/oni-code/ui/paste.js +45 -0
  487. package/dist/oni-code/ui/paste.js.map +1 -0
  488. package/dist/oni-code/ui/raw-spinner.d.ts +37 -0
  489. package/dist/oni-code/ui/raw-spinner.d.ts.map +1 -0
  490. package/dist/oni-code/ui/raw-spinner.js +121 -0
  491. package/dist/oni-code/ui/raw-spinner.js.map +1 -0
  492. package/dist/oni-code/ui/session.d.ts +44 -0
  493. package/dist/oni-code/ui/session.d.ts.map +1 -0
  494. package/dist/oni-code/ui/session.js +93 -0
  495. package/dist/oni-code/ui/session.js.map +1 -0
  496. package/dist/oni-code/ui/spinner.d.ts +7 -0
  497. package/dist/oni-code/ui/spinner.d.ts.map +1 -0
  498. package/dist/oni-code/ui/spinner.js +20 -0
  499. package/dist/oni-code/ui/spinner.js.map +1 -0
  500. package/dist/oni-code/ui/swarm-activity.d.ts +50 -0
  501. package/dist/oni-code/ui/swarm-activity.d.ts.map +1 -0
  502. package/dist/oni-code/ui/swarm-activity.js +233 -0
  503. package/dist/oni-code/ui/swarm-activity.js.map +1 -0
  504. package/dist/oni-code/ui/terminal-size.d.ts +18 -0
  505. package/dist/oni-code/ui/terminal-size.d.ts.map +1 -0
  506. package/dist/oni-code/ui/terminal-size.js +45 -0
  507. package/dist/oni-code/ui/terminal-size.js.map +1 -0
  508. package/dist/oni-code/ui/theme.d.ts +82 -0
  509. package/dist/oni-code/ui/theme.d.ts.map +1 -0
  510. package/dist/oni-code/ui/theme.js +101 -0
  511. package/dist/oni-code/ui/theme.js.map +1 -0
  512. package/dist/oni-code/ui/tool-utils.d.ts +10 -0
  513. package/dist/oni-code/ui/tool-utils.d.ts.map +1 -0
  514. package/dist/oni-code/ui/tool-utils.js +82 -0
  515. package/dist/oni-code/ui/tool-utils.js.map +1 -0
  516. package/dist/oni-code/ui/useTerminalSize.d.ts +3 -0
  517. package/dist/oni-code/ui/useTerminalSize.d.ts.map +1 -0
  518. package/dist/oni-code/ui/useTerminalSize.js +16 -0
  519. package/dist/oni-code/ui/useTerminalSize.js.map +1 -0
  520. package/dist/oni-code/workspace/change-tracker.d.ts +18 -0
  521. package/dist/oni-code/workspace/change-tracker.d.ts.map +1 -0
  522. package/dist/oni-code/workspace/change-tracker.js +67 -0
  523. package/dist/oni-code/workspace/change-tracker.js.map +1 -0
  524. package/dist/oni-code/workspace/conflict-detector.d.ts +12 -0
  525. package/dist/oni-code/workspace/conflict-detector.d.ts.map +1 -0
  526. package/dist/oni-code/workspace/conflict-detector.js +24 -0
  527. package/dist/oni-code/workspace/conflict-detector.js.map +1 -0
  528. package/dist/oni-code/workspace/file-snapshots.d.ts +39 -0
  529. package/dist/oni-code/workspace/file-snapshots.d.ts.map +1 -0
  530. package/dist/oni-code/workspace/file-snapshots.js +77 -0
  531. package/dist/oni-code/workspace/file-snapshots.js.map +1 -0
  532. package/dist/oni-code/workspace/index.d.ts +5 -0
  533. package/dist/oni-code/workspace/index.d.ts.map +1 -0
  534. package/dist/oni-code/workspace/index.js +5 -0
  535. package/dist/oni-code/workspace/index.js.map +1 -0
  536. package/dist/oni-code/workspace/project-map.d.ts +14 -0
  537. package/dist/oni-code/workspace/project-map.d.ts.map +1 -0
  538. package/dist/oni-code/workspace/project-map.js +91 -0
  539. package/dist/oni-code/workspace/project-map.js.map +1 -0
  540. package/dist/prebuilt/tool-node.d.ts.map +1 -1
  541. package/dist/prebuilt/tool-node.js +0 -1
  542. package/dist/prebuilt/tool-node.js.map +1 -1
  543. package/dist/pregel.d.ts +15 -1
  544. package/dist/pregel.d.ts.map +1 -1
  545. package/dist/pregel.js +199 -51
  546. package/dist/pregel.js.map +1 -1
  547. package/dist/retry.d.ts.map +1 -1
  548. package/dist/retry.js +16 -4
  549. package/dist/retry.js.map +1 -1
  550. package/dist/sentinel/config/index.d.ts +2 -0
  551. package/dist/sentinel/config/index.d.ts.map +1 -0
  552. package/dist/sentinel/config/index.js +2 -0
  553. package/dist/sentinel/config/index.js.map +1 -0
  554. package/dist/sentinel/config/schema.d.ts +4 -0
  555. package/dist/sentinel/config/schema.d.ts.map +1 -0
  556. package/dist/sentinel/config/schema.js +42 -0
  557. package/dist/sentinel/config/schema.js.map +1 -0
  558. package/dist/sentinel/debate/index.d.ts +6 -0
  559. package/dist/sentinel/debate/index.d.ts.map +1 -0
  560. package/dist/sentinel/debate/index.js +64 -0
  561. package/dist/sentinel/debate/index.js.map +1 -0
  562. package/dist/sentinel/debate/prompts.d.ts +4 -0
  563. package/dist/sentinel/debate/prompts.d.ts.map +1 -0
  564. package/dist/sentinel/debate/prompts.js +13 -0
  565. package/dist/sentinel/debate/prompts.js.map +1 -0
  566. package/dist/sentinel/fix/index.d.ts +6 -0
  567. package/dist/sentinel/fix/index.d.ts.map +1 -0
  568. package/dist/sentinel/fix/index.js +27 -0
  569. package/dist/sentinel/fix/index.js.map +1 -0
  570. package/dist/sentinel/fix/strategies.d.ts +9 -0
  571. package/dist/sentinel/fix/strategies.d.ts.map +1 -0
  572. package/dist/sentinel/fix/strategies.js +40 -0
  573. package/dist/sentinel/fix/strategies.js.map +1 -0
  574. package/dist/sentinel/index.d.ts +14 -0
  575. package/dist/sentinel/index.d.ts.map +1 -0
  576. package/dist/sentinel/index.js +22 -0
  577. package/dist/sentinel/index.js.map +1 -0
  578. package/dist/sentinel/integrations/cli.d.ts +10 -0
  579. package/dist/sentinel/integrations/cli.d.ts.map +1 -0
  580. package/dist/sentinel/integrations/cli.js +24 -0
  581. package/dist/sentinel/integrations/cli.js.map +1 -0
  582. package/dist/sentinel/integrations/index.d.ts +2 -0
  583. package/dist/sentinel/integrations/index.d.ts.map +1 -0
  584. package/dist/sentinel/integrations/index.js +2 -0
  585. package/dist/sentinel/integrations/index.js.map +1 -0
  586. package/dist/sentinel/memory/index.d.ts +16 -0
  587. package/dist/sentinel/memory/index.d.ts.map +1 -0
  588. package/dist/sentinel/memory/index.js +60 -0
  589. package/dist/sentinel/memory/index.js.map +1 -0
  590. package/dist/sentinel/report/console.d.ts +3 -0
  591. package/dist/sentinel/report/console.d.ts.map +1 -0
  592. package/dist/sentinel/report/console.js +27 -0
  593. package/dist/sentinel/report/console.js.map +1 -0
  594. package/dist/sentinel/report/github.d.ts +3 -0
  595. package/dist/sentinel/report/github.d.ts.map +1 -0
  596. package/dist/sentinel/report/github.js +36 -0
  597. package/dist/sentinel/report/github.js.map +1 -0
  598. package/dist/sentinel/report/index.d.ts +6 -0
  599. package/dist/sentinel/report/index.d.ts.map +1 -0
  600. package/dist/sentinel/report/index.js +15 -0
  601. package/dist/sentinel/report/index.js.map +1 -0
  602. package/dist/sentinel/report/sarif.d.ts +3 -0
  603. package/dist/sentinel/report/sarif.d.ts.map +1 -0
  604. package/dist/sentinel/report/sarif.js +29 -0
  605. package/dist/sentinel/report/sarif.js.map +1 -0
  606. package/dist/sentinel/sentinel.d.ts +17 -0
  607. package/dist/sentinel/sentinel.d.ts.map +1 -0
  608. package/dist/sentinel/sentinel.js +111 -0
  609. package/dist/sentinel/sentinel.js.map +1 -0
  610. package/dist/sentinel/swarm/agents.d.ts +6 -0
  611. package/dist/sentinel/swarm/agents.d.ts.map +1 -0
  612. package/dist/sentinel/swarm/agents.js +36 -0
  613. package/dist/sentinel/swarm/agents.js.map +1 -0
  614. package/dist/sentinel/swarm/index.d.ts +8 -0
  615. package/dist/sentinel/swarm/index.d.ts.map +1 -0
  616. package/dist/sentinel/swarm/index.js +74 -0
  617. package/dist/sentinel/swarm/index.js.map +1 -0
  618. package/dist/sentinel/swarm/prompts.d.ts +2 -0
  619. package/dist/sentinel/swarm/prompts.d.ts.map +1 -0
  620. package/dist/sentinel/swarm/prompts.js +24 -0
  621. package/dist/sentinel/swarm/prompts.js.map +1 -0
  622. package/dist/sentinel/swarm/topology.d.ts +10 -0
  623. package/dist/sentinel/swarm/topology.d.ts.map +1 -0
  624. package/dist/sentinel/swarm/topology.js +38 -0
  625. package/dist/sentinel/swarm/topology.js.map +1 -0
  626. package/dist/sentinel/triage/analyzers/complexity-analyzer.d.ts +7 -0
  627. package/dist/sentinel/triage/analyzers/complexity-analyzer.d.ts.map +1 -0
  628. package/dist/sentinel/triage/analyzers/complexity-analyzer.js +94 -0
  629. package/dist/sentinel/triage/analyzers/complexity-analyzer.js.map +1 -0
  630. package/dist/sentinel/triage/analyzers/custom-analyzer.d.ts +19 -0
  631. package/dist/sentinel/triage/analyzers/custom-analyzer.d.ts.map +1 -0
  632. package/dist/sentinel/triage/analyzers/custom-analyzer.js +268 -0
  633. package/dist/sentinel/triage/analyzers/custom-analyzer.js.map +1 -0
  634. package/dist/sentinel/triage/analyzers/dependency-analyzer.d.ts +26 -0
  635. package/dist/sentinel/triage/analyzers/dependency-analyzer.d.ts.map +1 -0
  636. package/dist/sentinel/triage/analyzers/dependency-analyzer.js +220 -0
  637. package/dist/sentinel/triage/analyzers/dependency-analyzer.js.map +1 -0
  638. package/dist/sentinel/triage/analyzers/diff-analyzer.d.ts +12 -0
  639. package/dist/sentinel/triage/analyzers/diff-analyzer.d.ts.map +1 -0
  640. package/dist/sentinel/triage/analyzers/diff-analyzer.js +19 -0
  641. package/dist/sentinel/triage/analyzers/diff-analyzer.js.map +1 -0
  642. package/dist/sentinel/triage/analyzers/index.d.ts +14 -0
  643. package/dist/sentinel/triage/analyzers/index.d.ts.map +1 -0
  644. package/dist/sentinel/triage/analyzers/index.js +25 -0
  645. package/dist/sentinel/triage/analyzers/index.js.map +1 -0
  646. package/dist/sentinel/triage/analyzers/pattern-analyzer.d.ts +7 -0
  647. package/dist/sentinel/triage/analyzers/pattern-analyzer.d.ts.map +1 -0
  648. package/dist/sentinel/triage/analyzers/pattern-analyzer.js +180 -0
  649. package/dist/sentinel/triage/analyzers/pattern-analyzer.js.map +1 -0
  650. package/dist/sentinel/triage/analyzers/security-analyzer.d.ts +7 -0
  651. package/dist/sentinel/triage/analyzers/security-analyzer.d.ts.map +1 -0
  652. package/dist/sentinel/triage/analyzers/security-analyzer.js +96 -0
  653. package/dist/sentinel/triage/analyzers/security-analyzer.js.map +1 -0
  654. package/dist/sentinel/triage/analyzers/ts-parser.d.ts +71 -0
  655. package/dist/sentinel/triage/analyzers/ts-parser.d.ts.map +1 -0
  656. package/dist/sentinel/triage/analyzers/ts-parser.js +323 -0
  657. package/dist/sentinel/triage/analyzers/ts-parser.js.map +1 -0
  658. package/dist/sentinel/triage/analyzers/typescript-analyzer.d.ts +7 -0
  659. package/dist/sentinel/triage/analyzers/typescript-analyzer.d.ts.map +1 -0
  660. package/dist/sentinel/triage/analyzers/typescript-analyzer.js +68 -0
  661. package/dist/sentinel/triage/analyzers/typescript-analyzer.js.map +1 -0
  662. package/dist/sentinel/triage/index.d.ts +10 -0
  663. package/dist/sentinel/triage/index.d.ts.map +1 -0
  664. package/dist/sentinel/triage/index.js +39 -0
  665. package/dist/sentinel/triage/index.js.map +1 -0
  666. package/dist/sentinel/triage/scorer.d.ts +9 -0
  667. package/dist/sentinel/triage/scorer.d.ts.map +1 -0
  668. package/dist/sentinel/triage/scorer.js +28 -0
  669. package/dist/sentinel/triage/scorer.js.map +1 -0
  670. package/dist/sentinel/types.d.ts +125 -0
  671. package/dist/sentinel/types.d.ts.map +1 -0
  672. package/dist/sentinel/types.js +6 -0
  673. package/dist/sentinel/types.js.map +1 -0
  674. package/dist/store/index.d.ts +5 -0
  675. package/dist/store/index.d.ts.map +1 -1
  676. package/dist/store/index.js +24 -1
  677. package/dist/store/index.js.map +1 -1
  678. package/dist/stream-events.js +2 -2
  679. package/dist/stream-events.js.map +1 -1
  680. package/dist/streaming.d.ts +13 -0
  681. package/dist/streaming.d.ts.map +1 -1
  682. package/dist/streaming.js +42 -0
  683. package/dist/streaming.js.map +1 -1
  684. package/dist/swarm/graph.d.ts +81 -2
  685. package/dist/swarm/graph.d.ts.map +1 -1
  686. package/dist/swarm/graph.js +517 -36
  687. package/dist/swarm/graph.js.map +1 -1
  688. package/dist/swarm/index.d.ts +10 -2
  689. package/dist/swarm/index.d.ts.map +1 -1
  690. package/dist/swarm/index.js +6 -1
  691. package/dist/swarm/index.js.map +1 -1
  692. package/dist/swarm/mermaid.d.ts +10 -0
  693. package/dist/swarm/mermaid.d.ts.map +1 -0
  694. package/dist/swarm/mermaid.js +64 -0
  695. package/dist/swarm/mermaid.js.map +1 -0
  696. package/dist/swarm/pool.d.ts +9 -1
  697. package/dist/swarm/pool.d.ts.map +1 -1
  698. package/dist/swarm/pool.js +58 -10
  699. package/dist/swarm/pool.js.map +1 -1
  700. package/dist/swarm/registry.d.ts +11 -1
  701. package/dist/swarm/registry.d.ts.map +1 -1
  702. package/dist/swarm/registry.js +17 -3
  703. package/dist/swarm/registry.js.map +1 -1
  704. package/dist/swarm/scaling.d.ts +95 -0
  705. package/dist/swarm/scaling.d.ts.map +1 -0
  706. package/dist/swarm/scaling.js +214 -0
  707. package/dist/swarm/scaling.js.map +1 -0
  708. package/dist/swarm/snapshot.d.ts +51 -0
  709. package/dist/swarm/snapshot.d.ts.map +1 -0
  710. package/dist/swarm/snapshot.js +115 -0
  711. package/dist/swarm/snapshot.js.map +1 -0
  712. package/dist/swarm/supervisor.d.ts.map +1 -1
  713. package/dist/swarm/supervisor.js +82 -4
  714. package/dist/swarm/supervisor.js.map +1 -1
  715. package/dist/swarm/tracer.d.ts +57 -0
  716. package/dist/swarm/tracer.d.ts.map +1 -0
  717. package/dist/swarm/tracer.js +138 -0
  718. package/dist/swarm/tracer.js.map +1 -0
  719. package/dist/swarm/types.d.ts +23 -1
  720. package/dist/swarm/types.d.ts.map +1 -1
  721. package/dist/swarm/types.js.map +1 -1
  722. package/dist/telemetry.d.ts +41 -0
  723. package/dist/telemetry.d.ts.map +1 -0
  724. package/dist/telemetry.js +69 -0
  725. package/dist/telemetry.js.map +1 -0
  726. package/dist/testing/index.d.ts +33 -0
  727. package/dist/testing/index.d.ts.map +1 -0
  728. package/dist/testing/index.js +95 -0
  729. package/dist/testing/index.js.map +1 -0
  730. package/dist/tools/types.d.ts +2 -0
  731. package/dist/tools/types.d.ts.map +1 -1
  732. package/dist/types.d.ts +9 -0
  733. package/dist/types.d.ts.map +1 -1
  734. package/dist/types.js +1 -1
  735. package/dist/types.js.map +1 -1
  736. package/package.json +160 -108
@@ -0,0 +1,1547 @@
1
+ /**
2
+ * Conductor Agent — the core integration that wires ONIHarness with coding
3
+ * tools, dispatch tools, workspace awareness, and permissions.
4
+ *
5
+ * The Conductor is the main conversational agent for the `oni` CLI. It handles
6
+ * simple tasks directly and escalates to subagents/swarms for complex work.
7
+ *
8
+ * Unlike single-shot harness usage, the Conductor maintains conversation
9
+ * history across turns so the agent remembers prior context.
10
+ */
11
+ import { agentLoop } from "../harness/agent-loop.js";
12
+ import { ONIHarness } from "../harness/index.js";
13
+ import { TodoModule } from "../harness/todo-module.js";
14
+ import { HooksEngine } from "../harness/hooks-engine.js";
15
+ import { ContextCompactor } from "../harness/context-compactor.js";
16
+ import { defineTool } from "../tools/define.js";
17
+ import { makeCodingTools } from "./tools/coding.js";
18
+ import { LSPManager } from "../lsp/index.js";
19
+ import { makeWebTools } from "./tools/web.js";
20
+ import { makeSpawnAgentsTool } from "./tools/spawn-agents.js";
21
+ import { makeSpawnSwarmTool, } from "./tools/spawn-swarm.js";
22
+ import { makeCreateSkillTool } from "./tools/skill.js";
23
+ import { ProjectMap } from "./workspace/project-map.js";
24
+ import { ChangeTracker } from "./workspace/change-tracker.js";
25
+ import { PermissionManager } from "./permissions.js";
26
+ import { BackgroundScheduler } from "./scheduler.js";
27
+ import { CronScheduler, persistJobs, loadPersistedJobs } from "./cron/index.js";
28
+ import { makeCronTools } from "./tools/cron.js";
29
+ import { LoopDetector } from "./loop-detector.js";
30
+ import { AgentRegistry } from "./agent-registry.js";
31
+ import { makeQuestionTool } from "./tools/question.js";
32
+ import { makePlanTools } from "./tools/plan.js";
33
+ import { makeBatchTool } from "./tools/batch.js";
34
+ import { SessionTree } from "./session-fork.js";
35
+ import { TaskEvaluator } from "./task-evaluator.js";
36
+ import { SessionStats } from "./session-stats.js";
37
+ import { EventBus } from "../events/bus.js";
38
+ import { FileWatcher } from "./file-watcher.js";
39
+ import { SessionTitleGenerator } from "./session-title.js";
40
+ import { FileSnapshots } from "./workspace/file-snapshots.js";
41
+ import { extractFileChange, extractPatchChanges, extractMultiEditChanges } from "./change-tracking.js";
42
+ import { resolve as resolvePath, join as joinPath } from "node:path";
43
+ import { homedir as osHomedir } from "node:os";
44
+ import { SkillLoader } from "../harness/skill-loader.js";
45
+ import { buildSystemPrompt } from "./system-prompt.js";
46
+ import { loadContextFiles } from "./context-files.js";
47
+ import { buildAutoDispatchAgents } from "./topology-agent-builder.js";
48
+ import { summarizeSwarmResult } from "./summarizer.js";
49
+ import { ProgressTracker } from "./progress-tracker.js";
50
+ import { DefaultSwarmRunner } from "./swarm-runner.js";
51
+ // ── Constants ───────────────────────────────────────────────
52
+ /** Tools that mutate files — used to trigger snapshots and agent-change markers. */
53
+ const WRITE_TOOLS = new Set(["write_file", "edit_file", "apply_patch", "multi_edit"]);
54
+ // ── System Prompt ───────────────────────────────────────────
55
+ // Uses buildSystemPrompt() from system-prompt.ts (canonical) +
56
+ // loadContextFiles() from context-files.ts (CLAUDE.md, AGENTS.md, .cursorrules)
57
+ // ── Noop SwarmRunner ────────────────────────────────────────
58
+ const NOOP_SWARM_RUNNER = {
59
+ run: async () => ({
60
+ outcome: "Swarm runner not configured",
61
+ agentSummaries: [],
62
+ filesModified: [],
63
+ duration: 0,
64
+ }),
65
+ };
66
+ // ── Conductor ───────────────────────────────────────────────
67
+ export class Conductor {
68
+ config;
69
+ rootDir;
70
+ permissions;
71
+ changeTracker;
72
+ fileSnapshots;
73
+ swarmRunner;
74
+ swarmRunnerPublishesLifecycle;
75
+ tools;
76
+ todoModule;
77
+ hooksEngine;
78
+ compactor;
79
+ scheduler;
80
+ loopDetector;
81
+ lspManager;
82
+ cronScheduler;
83
+ agentRegistry;
84
+ taskEvaluator;
85
+ sessionStats;
86
+ eventBus;
87
+ fileWatcher;
88
+ titleGenerator;
89
+ skillLoader;
90
+ progressTracker;
91
+ autoCompactionEnabled;
92
+ /** Cached context files content (CLAUDE.md, AGENTS.md, .cursorrules) */
93
+ contextFilesBlock;
94
+ /** Prebuilt tool set for subagents — coding + web + custom, no orchestration tools */
95
+ subagentTools;
96
+ /** Accumulated conversation history across turns */
97
+ conversationHistory = [];
98
+ turnCount = 0;
99
+ /** Session tree for branch-based conversation management */
100
+ sessionTree;
101
+ /** Active agent name — determines system prompt and tool scoping */
102
+ activeAgent = "build";
103
+ /** Cached assembled system prompt (base + context files + skills). Invalidated on agent switch. */
104
+ _cachedSystemPrompt = null;
105
+ _cachedPromptAgent = null;
106
+ _cachedSkillVersion = -1;
107
+ _projectMapDirty = true;
108
+ /** Guard: true while chat() generator is executing. Prevents the background
109
+ * scheduler from compacting conversationHistory while the agentLoop holds
110
+ * a reference to it — otherwise the scheduler's compaction gets silently
111
+ * overwritten when the agentLoop yields finalMessages. */
112
+ _chatActive = false;
113
+ _stopNudgeCount = 0;
114
+ /** Session-level circuit breaker: consecutive chat() calls that ended in inference error.
115
+ * After 3 consecutive failures (9+ total retries), we stop attempting inference and
116
+ * tell the user the provider appears down. Resets on any successful inference. */
117
+ _consecutiveInferenceFailures = 0;
118
+ static INFERENCE_FAILURE_THRESHOLD = 3;
119
+ permissionListener = null;
120
+ questionListener = null;
121
+ toolMetadataListener = null;
122
+ constructor(config) {
123
+ this.config = config;
124
+ this.rootDir = config.rootDir;
125
+ this.permissions = new PermissionManager(config.mode ?? "auto", config.rootDir);
126
+ this.changeTracker = new ChangeTracker();
127
+ this.fileSnapshots = new FileSnapshots();
128
+ this.eventBus = config.eventBus ?? new EventBus();
129
+ // ── LSP manager (shared across all agents) ──────────────
130
+ this.lspManager = new LSPManager(config.rootDir);
131
+ // Apply LSP config if present
132
+ if (config.oniConfig?.lsp === false) {
133
+ this.lspManager.disable();
134
+ }
135
+ else if (config.oniConfig?.lsp && typeof config.oniConfig.lsp === "object") {
136
+ const lspCfg = config.oniConfig.lsp;
137
+ const lspServers = Object.entries(lspCfg).map(([id, cfg]) => ({
138
+ id,
139
+ extensions: cfg.extensions,
140
+ command: cfg.command,
141
+ args: cfg.args,
142
+ languageId: cfg.languageId,
143
+ }));
144
+ this.lspManager.addServers(lspServers);
145
+ }
146
+ // ── Build tools ─────────────────────────────────────────
147
+ const codingTools = makeCodingTools(config.rootDir, {
148
+ lsp: this.lspManager,
149
+ changeTracker: this.changeTracker,
150
+ fileSnapshots: this.fileSnapshots,
151
+ });
152
+ const webTools = makeWebTools({
153
+ searchEndpoint: config.oniConfig?.searchEndpoint,
154
+ searchApiKey: config.oniConfig?.searchApiKey,
155
+ });
156
+ const spawnAgentsTool = makeSpawnAgentsTool({
157
+ runAgent: (task, _index, agentName) => this.runSubagent(task, agentName),
158
+ });
159
+ const shouldCreateDefaultSwarmRunner = !config.swarmRunner && !!config.eventBus;
160
+ const swarmRunner = config.swarmRunner ?? (shouldCreateDefaultSwarmRunner
161
+ ? new DefaultSwarmRunner({
162
+ model: config.model,
163
+ fastModel: config.fastModel,
164
+ rootDir: config.rootDir,
165
+ changeTracker: this.changeTracker,
166
+ maxConcurrency: config.oniConfig?.swarm?.maxConcurrency,
167
+ perAgentMaxTurns: config.oniConfig?.swarm?.perAgentMaxTurns,
168
+ agentTimeout: config.oniConfig?.swarm?.agentTimeout,
169
+ eventBus: this.eventBus,
170
+ })
171
+ : NOOP_SWARM_RUNNER);
172
+ this.swarmRunner = swarmRunner;
173
+ this.swarmRunnerPublishesLifecycle = shouldCreateDefaultSwarmRunner;
174
+ const spawnSwarmTool = makeSpawnSwarmTool(swarmRunner);
175
+ const projectMapTool = this.buildProjectMapTool();
176
+ const questionTool = makeQuestionTool();
177
+ const planTools = makePlanTools({
178
+ switchAgent: (name) => this.switchAgent(name),
179
+ });
180
+ // ── Skill loader — scan .oni/skills/, .claude/skills/, and global dirs ──
181
+ {
182
+ const homedir = osHomedir();
183
+ const skillDirs = [
184
+ joinPath(homedir, ".oni", "skills"),
185
+ joinPath(homedir, ".claude", "skills"),
186
+ joinPath(config.rootDir, ".oni", "skills"),
187
+ joinPath(config.rootDir, ".claude", "skills"),
188
+ joinPath(config.rootDir, ".agents", "skills"),
189
+ ];
190
+ this.skillLoader = SkillLoader.fromDirectories(skillDirs);
191
+ }
192
+ // ── Context files — load CLAUDE.md, AGENTS.md, .cursorrules ──
193
+ this.contextFilesBlock = loadContextFiles(config.rootDir);
194
+ // Build base tool list (batch tool wraps these)
195
+ const skillTool = this.skillLoader.getSkillTool();
196
+ const createSkillTool = makeCreateSkillTool(this.skillLoader);
197
+ const cronTools = makeCronTools(this.cronScheduler);
198
+ const baseTools = [
199
+ ...codingTools,
200
+ ...webTools,
201
+ spawnAgentsTool,
202
+ spawnSwarmTool,
203
+ projectMapTool,
204
+ questionTool,
205
+ ...planTools,
206
+ skillTool,
207
+ createSkillTool,
208
+ ...cronTools,
209
+ ...(config.customTools ?? []),
210
+ ];
211
+ const batchTool = makeBatchTool(baseTools);
212
+ this.tools = [...baseTools, batchTool];
213
+ // Cache subagent-eligible tools (coding + web + custom, no orchestration)
214
+ this.subagentTools = [...codingTools, ...webTools, ...(config.customTools ?? [])];
215
+ // ── Build harness modules ────────────────────────────────
216
+ this.todoModule = new TodoModule();
217
+ this.loopDetector = new LoopDetector();
218
+ this.agentRegistry = new AgentRegistry();
219
+ this.sessionStats = new SessionStats();
220
+ this.titleGenerator = new SessionTitleGenerator(config.fastModel ?? config.model);
221
+ this.progressTracker = new ProgressTracker(config.rootDir);
222
+ this.progressTracker.restoreInto((todos) => this.todoModule.write(todos), this.changeTracker);
223
+ this.todoModule.onChange((state) => {
224
+ if (this.progressTracker.syncTodoState(state)) {
225
+ this.persistProgressSnapshot();
226
+ }
227
+ });
228
+ // ── File watcher (reactive file change detection) ──────
229
+ this.fileWatcher = new FileWatcher({
230
+ rootDir: config.rootDir,
231
+ bus: this.eventBus,
232
+ });
233
+ this.fileWatcher.on(() => {
234
+ this._projectMapDirty = true;
235
+ });
236
+ this.fileWatcher.start();
237
+ // ── Cron scheduler (session-scoped recurring prompts) ──
238
+ this.cronScheduler = new CronScheduler({
239
+ onFire: (prompt, jobId) => {
240
+ // Emit event for the CLI to pick up and inject as a new chat turn
241
+ const event = {
242
+ type: "cron.fired",
243
+ prompt,
244
+ jobId,
245
+ timestamp: Date.now(),
246
+ };
247
+ this.eventBus.emit(event);
248
+ },
249
+ isBusy: () => this._chatActive,
250
+ onMutate: (jobs) => persistJobs(this.rootDir, jobs),
251
+ });
252
+ // ── Restore persisted cron jobs ───────────────────────────
253
+ const persistedJobs = loadPersistedJobs(this.rootDir);
254
+ if (persistedJobs.length > 0) {
255
+ this.cronScheduler.restore(persistedJobs);
256
+ }
257
+ // ── Apply ONICodeConfig (agents, permissions) ─────────
258
+ if (config.oniConfig) {
259
+ // Register custom agents from config
260
+ if (config.oniConfig.agents) {
261
+ for (const [name, agentCfg] of Object.entries(config.oniConfig.agents)) {
262
+ this.agentRegistry.register({
263
+ name,
264
+ mode: agentCfg.mode ?? "subagent",
265
+ description: agentCfg.description ?? name,
266
+ tools: agentCfg.tools,
267
+ systemPrompt: agentCfg.systemPrompt,
268
+ maxTurns: agentCfg.maxTurns,
269
+ });
270
+ }
271
+ }
272
+ // Apply permission rules from config
273
+ if (config.oniConfig.permissions) {
274
+ this.permissions.addRulesFromConfig(config.oniConfig.permissions);
275
+ }
276
+ }
277
+ const userEngine = new HooksEngine();
278
+ const permissionHooks = {
279
+ PreToolUse: [
280
+ {
281
+ handler: async (payload) => {
282
+ const { toolName, input } = payload;
283
+ // Defensive wrapper: if ANY internal check throws (loopDetector,
284
+ // permissionListener, etc.), deny the tool rather than letting
285
+ // the hooks engine's catch treat the exception as "allow".
286
+ try {
287
+ // ── Doom loop detection → permission escalation ──────
288
+ const loopResult = this.loopDetector.check(toolName, input);
289
+ if (loopResult.blocked) {
290
+ // In bypass/auto mode, tool spam (same tool, different args) is
291
+ // normal — e.g., writing 10 different files. Skip escalation.
292
+ const isAutoSpam = this.permissions.getMode() === "auto" &&
293
+ loopResult.type === "spam";
294
+ if (isAutoSpam) {
295
+ // Fall through — allow the call without prompting
296
+ }
297
+ else if (this.permissionListener) {
298
+ const decision = await new Promise((resolve) => {
299
+ this.permissionListener(toolName, input, resolve);
300
+ });
301
+ if (decision === "deny") {
302
+ return {
303
+ decision: "deny",
304
+ reason: loopResult.reason ?? "Doom loop detected",
305
+ };
306
+ }
307
+ // User approved — reset count for this tool+args so execution continues
308
+ this.loopDetector.allowOnce(toolName, input);
309
+ if (decision === "always_allow") {
310
+ this.permissions.addAlwaysAllow(toolName);
311
+ }
312
+ }
313
+ else {
314
+ // No permission listener — block with reason
315
+ return {
316
+ decision: "deny",
317
+ reason: loopResult.reason ?? "Doom loop detected",
318
+ };
319
+ }
320
+ }
321
+ // ── Plan mode enforcement ────────────────────────────
322
+ if (this.activeAgent === "plan") {
323
+ const planDef = this.agentRegistry.get("plan");
324
+ const allowed = planDef?.tools ? new Set([...planDef.tools, "plan_exit"]) : null;
325
+ if (allowed && !allowed.has(toolName)) {
326
+ return {
327
+ decision: "deny",
328
+ reason: `Tool "${toolName}" is not available in plan mode. Use plan_exit to switch back to build mode.`,
329
+ };
330
+ }
331
+ // Block plan_enter when already in plan mode
332
+ if (toolName === "plan_enter") {
333
+ return {
334
+ decision: "deny",
335
+ reason: "Already in plan mode. Use plan_exit to switch to build mode.",
336
+ };
337
+ }
338
+ }
339
+ else {
340
+ // Block plan_exit when in build mode
341
+ if (toolName === "plan_exit") {
342
+ return {
343
+ decision: "deny",
344
+ reason: "Not in plan mode. Use plan_enter to switch to plan mode first.",
345
+ };
346
+ }
347
+ }
348
+ // ── Normal permission check ──────────────────────────
349
+ if (this.permissions.needsApproval(toolName, input)) {
350
+ if (this.permissionListener) {
351
+ this.eventBus.emit({
352
+ type: "permission.asked",
353
+ toolName,
354
+ input,
355
+ agentName: this.activeAgent,
356
+ timestamp: Date.now(),
357
+ });
358
+ const decision = await new Promise((resolve) => {
359
+ this.permissionListener(toolName, input, resolve);
360
+ });
361
+ const busDecision = decision === "always_allow" ? "always"
362
+ : decision === "allow" ? "allow"
363
+ : "deny";
364
+ this.eventBus.emit({
365
+ type: "permission.replied",
366
+ toolName,
367
+ decision: busDecision,
368
+ timestamp: Date.now(),
369
+ });
370
+ if (decision === "deny") {
371
+ return { decision: "deny", reason: "User denied" };
372
+ }
373
+ if (decision === "always_allow") {
374
+ this.permissions.addAlwaysAllow(toolName);
375
+ }
376
+ }
377
+ }
378
+ // ── Snapshot files before mutation (for undo_edit) ──────
379
+ // Also mark agent-initiated file changes so FileWatcher
380
+ // tags the resulting change event as agentInitiated: true
381
+ if (WRITE_TOOLS.has(toolName) && input) {
382
+ const filePath = input.path ?? input.file_path;
383
+ if (typeof filePath === "string") {
384
+ const abs = resolvePath(this.rootDir, filePath);
385
+ this.fileSnapshots.capture(abs);
386
+ this.fileWatcher.markAgentChange(abs);
387
+ }
388
+ // multi_edit has an edits array with individual file paths
389
+ const edits = input.edits;
390
+ if (Array.isArray(edits)) {
391
+ for (const edit of edits) {
392
+ const ep = edit?.path ?? edit?.file_path;
393
+ if (typeof ep === "string") {
394
+ const abs = resolvePath(this.rootDir, ep);
395
+ this.fileSnapshots.capture(abs);
396
+ this.fileWatcher.markAgentChange(abs);
397
+ }
398
+ }
399
+ }
400
+ }
401
+ return { decision: "allow" };
402
+ }
403
+ catch {
404
+ // Internal error in permission/loop/plan checking — deny to
405
+ // prevent silent allow on handler crash.
406
+ return {
407
+ decision: "deny",
408
+ reason: "Permission check failed (internal error)",
409
+ };
410
+ }
411
+ },
412
+ },
413
+ ],
414
+ };
415
+ // ── Post-compaction state reinsertion ────────────────────
416
+ const postCompactHooks = {
417
+ PostCompact: [
418
+ {
419
+ handler: (_payload) => {
420
+ // Each state-gathering call is wrapped individually so a failure
421
+ // in one (e.g. corrupted todo state) doesn't lose the others.
422
+ const parts = [];
423
+ // Reinject todo list so the agent doesn't lose track
424
+ try {
425
+ const todoState = this.todoModule.getState();
426
+ if (todoState.todos.length > 0) {
427
+ parts.push("Current task list:\n" + this.todoModule.toContextString());
428
+ }
429
+ }
430
+ catch { /* partial state is better than no state */ }
431
+ // Reinject change tracker summary
432
+ try {
433
+ const changes = this.changeTracker.getSummary();
434
+ if (changes) {
435
+ parts.push("Files modified this session:\n" + changes);
436
+ }
437
+ }
438
+ catch { /* partial state is better than no state */ }
439
+ // Reinject session stats for budget awareness
440
+ try {
441
+ const statsCtx = this.sessionStats.toContextString();
442
+ if (statsCtx) {
443
+ parts.push("Session so far: " + statsCtx);
444
+ }
445
+ }
446
+ catch { /* partial state is better than no state */ }
447
+ try {
448
+ this.syncProgressSnapshot({ persist: false });
449
+ const progressCtx = this.progressTracker.toContextString();
450
+ if (progressCtx) {
451
+ parts.push(progressCtx);
452
+ }
453
+ }
454
+ catch { /* partial state is better than no state */ }
455
+ if (parts.length === 0) {
456
+ return { decision: "allow" };
457
+ }
458
+ return {
459
+ decision: "allow",
460
+ additionalContext: parts.join("\n\n"),
461
+ };
462
+ },
463
+ },
464
+ ],
465
+ };
466
+ // ── Session stats tracking hooks ─────────────────────────
467
+ const statsHooks = {
468
+ PostToolUse: [
469
+ {
470
+ handler: (payload) => {
471
+ const p = payload;
472
+ this.sessionStats.recordToolCall(p.toolName, p.durationMs ?? 0);
473
+ let changedWorkspace = false;
474
+ // Record file changes in the ChangeTracker
475
+ if (p.toolName === "apply_patch") {
476
+ const changes = extractPatchChanges(p.output);
477
+ for (const c of changes) {
478
+ this.changeTracker.recordChange(c.file, c.type);
479
+ changedWorkspace = true;
480
+ }
481
+ }
482
+ else if (p.toolName === "multi_edit") {
483
+ const changes = extractMultiEditChanges(p.output);
484
+ for (const c of changes) {
485
+ this.changeTracker.recordChange(c.file, c.type);
486
+ changedWorkspace = true;
487
+ }
488
+ }
489
+ else {
490
+ const change = extractFileChange(p.toolName, (p.input ?? {}), p.output);
491
+ if (change) {
492
+ this.changeTracker.recordChange(change.file, change.type);
493
+ changedWorkspace = true;
494
+ }
495
+ }
496
+ if (changedWorkspace) {
497
+ this._projectMapDirty = true;
498
+ }
499
+ this.progressTracker.recordVerification(p.toolName, (p.input ?? {}), p.output);
500
+ this.syncProgressSnapshot();
501
+ return { decision: "allow" };
502
+ },
503
+ },
504
+ ],
505
+ PostToolUseFailure: [
506
+ {
507
+ handler: (payload) => {
508
+ const p = payload;
509
+ const errMsg = p.error instanceof Error ? p.error.message : String(p.error);
510
+ this.sessionStats.recordToolError(p.toolName, errMsg);
511
+ this.syncProgressSnapshot();
512
+ return { decision: "allow" };
513
+ },
514
+ },
515
+ ],
516
+ PreCompact: [
517
+ {
518
+ handler: () => {
519
+ this.sessionStats.recordCompaction();
520
+ return { decision: "allow" };
521
+ },
522
+ },
523
+ ],
524
+ };
525
+ // ── Todo-aware Stop hook (blocks premature exit up to 2 times) ──
526
+ const todoStopHooks = {
527
+ Stop: [
528
+ {
529
+ handler: () => {
530
+ const state = this.todoModule.getState();
531
+ const outstanding = state.todos.filter((t) => t.status === "in_progress" || t.status === "pending");
532
+ if (outstanding.length > 0 && this._stopNudgeCount < 2) {
533
+ this._stopNudgeCount++;
534
+ const names = outstanding.map((t) => t.content).join(", ");
535
+ const reason = this._stopNudgeCount === 1
536
+ ? `Outstanding tasks remain: ${names}. Complete them or mark as completed before stopping.`
537
+ : `You still have ${outstanding.length} uncompleted task(s). Continue working — do not stop until tasks are addressed.`;
538
+ return { decision: "block", reason };
539
+ }
540
+ return { decision: "allow" };
541
+ },
542
+ },
543
+ ],
544
+ };
545
+ userEngine.configure(permissionHooks);
546
+ userEngine.configure(postCompactHooks);
547
+ userEngine.configure(statsHooks);
548
+ userEngine.configure(todoStopHooks);
549
+ this.hooksEngine = HooksEngine.compose(HooksEngine.withSecurityGuardrails(), userEngine);
550
+ this.autoCompactionEnabled = config.oniConfig?.compaction?.auto !== false;
551
+ const summaryModel = config.fastModel ?? config.model;
552
+ this.compactor = new ContextCompactor({
553
+ summaryModel,
554
+ threshold: config.oniConfig?.compaction?.threshold,
555
+ });
556
+ // ── Background scheduler ─────────────────────────────────
557
+ this.scheduler = new BackgroundScheduler();
558
+ // Register proactive compaction check — runs every 30s.
559
+ // If conversation history exceeds compaction threshold between
560
+ // chat calls, compact proactively so the next chat starts clean.
561
+ if (this.autoCompactionEnabled) {
562
+ this.scheduler.register({
563
+ id: "token-budget-monitor",
564
+ intervalMs: 30_000,
565
+ runImmediately: false,
566
+ run: async () => {
567
+ // Skip if chat() is in progress — the agentLoop handles its own
568
+ // compaction internally, and compacting here would race with its
569
+ // finalMessages capture (overwriting the scheduler's compaction).
570
+ if (this._chatActive)
571
+ return;
572
+ if (this.conversationHistory.length < 4)
573
+ return;
574
+ const check = this.compactor.checkCompaction(this.conversationHistory);
575
+ if (!check.needed)
576
+ return;
577
+ this.eventBus.emit({
578
+ type: "session.compacting",
579
+ sessionId: "",
580
+ messageCount: this.conversationHistory.length,
581
+ estimatedTokens: check.estimatedTokens,
582
+ threshold: check.threshold,
583
+ maxTokens: check.maxTokens,
584
+ percentUsed: check.percentUsed,
585
+ timestamp: Date.now(),
586
+ });
587
+ const before = this.conversationHistory.length;
588
+ await this.hooksEngine.fire("PreCompact", {
589
+ sessionId: "",
590
+ messageCount: before,
591
+ estimatedTokens: check.estimatedTokens,
592
+ });
593
+ try {
594
+ const compacted = await this.compactor.compact(this.conversationHistory, { skipInitialCheck: true });
595
+ const summarized = compacted.length <= 2 && before > 2;
596
+ const estimatedTokensAfter = this.compactor.estimateTokens(compacted);
597
+ const percentUsedAfter = check.maxTokens > 0
598
+ ? estimatedTokensAfter / check.maxTokens
599
+ : 0;
600
+ this.conversationHistory = compacted;
601
+ this.eventBus.emit({
602
+ type: "session.compacted",
603
+ sessionId: "",
604
+ beforeCount: before,
605
+ afterCount: compacted.length,
606
+ summarized,
607
+ estimatedTokensBefore: check.estimatedTokens,
608
+ estimatedTokensAfter,
609
+ threshold: check.threshold,
610
+ maxTokens: check.maxTokens,
611
+ percentUsedBefore: check.percentUsed,
612
+ percentUsedAfter,
613
+ timestamp: Date.now(),
614
+ });
615
+ // Fire PostCompact for state reinsertion
616
+ const postResult = await this.hooksEngine.fire("PostCompact", {
617
+ sessionId: "",
618
+ beforeCount: before,
619
+ afterCount: compacted.length,
620
+ estimatedTokensAfter,
621
+ summarized,
622
+ });
623
+ // Merge state reinsertion + continuation into a single user/assistant
624
+ // pair. Two separate pairs would waste ~40-60 tokens per compaction on
625
+ // dead "acknowledgment" turns ("State restored." / "Understood.").
626
+ const focus = this.todoModule.getCurrentFocus();
627
+ const continuationText = focus
628
+ ? `Continue working. Current focus: ${focus.content}`
629
+ : "Continue if you have next steps.";
630
+ const userContent = postResult?.additionalContext
631
+ ? `<system-reminder>\n${postResult.additionalContext}\n</system-reminder>\n\n${continuationText}`
632
+ : continuationText;
633
+ this.conversationHistory.push({ role: "user", content: userContent }, { role: "assistant", content: "Context loaded." });
634
+ }
635
+ catch (err) {
636
+ const errorMsg = err instanceof Error ? err.message : String(err);
637
+ this.eventBus.emit({
638
+ type: "session.compacted",
639
+ sessionId: "",
640
+ beforeCount: before,
641
+ afterCount: before,
642
+ summarized: false,
643
+ estimatedTokensBefore: check.estimatedTokens,
644
+ threshold: check.threshold,
645
+ maxTokens: check.maxTokens,
646
+ percentUsedBefore: check.percentUsed,
647
+ failed: true,
648
+ error: errorMsg,
649
+ timestamp: Date.now(),
650
+ });
651
+ this.eventBus.emit({
652
+ type: "error",
653
+ agent: this.activeAgent,
654
+ error: new Error(`Context compaction failed: ${errorMsg}`),
655
+ timestamp: Date.now(),
656
+ });
657
+ }
658
+ },
659
+ });
660
+ }
661
+ // ── Task evaluator (auto-dispatch) ──────────────────────
662
+ this.taskEvaluator = new TaskEvaluator();
663
+ // ── Session tree ──────────────────────────────────────────
664
+ this.sessionTree = new SessionTree();
665
+ }
666
+ // ── Public: chat (multi-turn with history) ─────────────────
667
+ async *chat(message, opts) {
668
+ this._chatActive = true;
669
+ this._stopNudgeCount = 0;
670
+ this.progressTracker.recordUserTask(message);
671
+ this.syncProgressSnapshot();
672
+ try {
673
+ // ── Resolve active agent config ──────────────────────────
674
+ const agentDef = this.agentRegistry.get(this.activeAgent);
675
+ const env = {
676
+ cwd: this.rootDir,
677
+ platform: process.platform,
678
+ date: new Date().toISOString().split("T")[0],
679
+ };
680
+ // Use cached prompt if agent hasn't changed (saves ~10ms/turn of string construction)
681
+ if (this._cachedSystemPrompt === null || this._cachedPromptAgent !== this.activeAgent || this._cachedSkillVersion !== this.skillLoader.version) {
682
+ let prompt = agentDef?.systemPrompt ?? buildSystemPrompt(env);
683
+ // Append project context files (CLAUDE.md, AGENTS.md, .cursorrules)
684
+ if (this.contextFilesBlock) {
685
+ prompt += "\n\n" + this.contextFilesBlock;
686
+ }
687
+ // Append available skill descriptions (progressive disclosure — names only)
688
+ const skillBlock = this.skillLoader.getDescriptionsForContext();
689
+ if (skillBlock) {
690
+ prompt += "\n\n" + skillBlock;
691
+ }
692
+ const progressBlock = this.progressTracker.toContextString();
693
+ if (progressBlock) {
694
+ prompt += `\n\n# Persistent Progress\n\n${progressBlock}`;
695
+ }
696
+ this._cachedSystemPrompt = prompt;
697
+ this._cachedPromptAgent = this.activeAgent;
698
+ this._cachedSkillVersion = this.skillLoader.version;
699
+ }
700
+ const systemPrompt = this._cachedSystemPrompt;
701
+ const maxTurns = agentDef?.maxTurns ?? this.config.maxTurns ?? 50;
702
+ // Scope tools by active agent's allowed list (Set for O(1) lookup)
703
+ const allTools = [...this.todoModule.getTools(), ...this.tools];
704
+ const scopedTools = agentDef?.tools
705
+ ? (() => {
706
+ const allowed = new Set([...agentDef.tools, "plan_exit"]);
707
+ return allTools.filter((t) => allowed.has(t.name));
708
+ })()
709
+ : allTools;
710
+ const agentBefore = this.activeAgent;
711
+ // ── Emit session.created ──────────────────────────────────
712
+ this.eventBus.emit({
713
+ type: "session.created",
714
+ sessionId: "",
715
+ agentName: this.activeAgent,
716
+ timestamp: Date.now(),
717
+ });
718
+ // ── Auto-dispatch: evaluate for swarm before entering agent loop ──
719
+ const swarmRunner = this.swarmRunner;
720
+ const hasRealSwarmRunner = swarmRunner !== NOOP_SWARM_RUNNER;
721
+ const runnerPublishesSwarmLifecycle = this.swarmRunnerPublishesLifecycle;
722
+ const dispatch = this.taskEvaluator.evaluate(message, this.activeAgent, this.conversationHistory.length, hasRealSwarmRunner);
723
+ if (dispatch.shouldDispatch && dispatch.recommendation && hasRealSwarmRunner) {
724
+ // Hoist variables above try so the catch block can close lifecycle events
725
+ const rec = dispatch.recommendation;
726
+ const agentCount = rec.suggestedAgentCount;
727
+ const swarmId = `swarm_${Date.now().toString(36)}`;
728
+ const swarmStartMs = Date.now();
729
+ // Run swarm directly
730
+ try {
731
+ // Yield step_start so the UI can show swarm progress in the spinner
732
+ yield {
733
+ type: "step_start",
734
+ sessionId: "",
735
+ turn: this.turnCount,
736
+ timestamp: swarmStartMs,
737
+ metadata: {
738
+ swarmTopology: rec.topology,
739
+ agentCount,
740
+ agentName: "swarm",
741
+ },
742
+ };
743
+ if (!runnerPublishesSwarmLifecycle) {
744
+ this.eventBus.emit({
745
+ type: "swarm.started",
746
+ swarmId,
747
+ topology: rec.topology,
748
+ agentCount,
749
+ timestamp: Date.now(),
750
+ });
751
+ }
752
+ // Build topology-aware agent definitions with distinct roles and prompts
753
+ const autoAgents = buildAutoDispatchAgents(rec.topology, message, agentCount);
754
+ const agents = autoAgents.map((a) => ({
755
+ id: a.id,
756
+ role: a.role,
757
+ systemPrompt: a.systemPrompt,
758
+ }));
759
+ const result = await swarmRunner.run({
760
+ topology: rec.topology,
761
+ task: message,
762
+ agents,
763
+ tools: ["read_file", "edit_file", "write_file", "bash", "grep", "glob", "git", "list_dir", "apply_patch", "list_changes", "undo_edit", "multi_edit", "webfetch", "websearch"],
764
+ maxTurns: 30,
765
+ }, undefined, { signal: opts?.signal });
766
+ if (!runnerPublishesSwarmLifecycle) {
767
+ this.eventBus.emit({
768
+ type: "swarm.completed",
769
+ swarmId,
770
+ topology: rec.topology,
771
+ agentCount,
772
+ duration: result.duration,
773
+ durationMs: result.duration * 1000,
774
+ agentResults: { outcome: result.outcome, filesModified: result.filesModified },
775
+ timestamp: Date.now(),
776
+ });
777
+ }
778
+ // Yield step_finish so the UI can clear swarm progress state
779
+ yield {
780
+ type: "step_finish",
781
+ sessionId: "",
782
+ turn: this.turnCount,
783
+ timestamp: Date.now(),
784
+ metadata: {
785
+ stepDurationMs: Date.now() - swarmStartMs,
786
+ swarmTopology: rec.topology,
787
+ agentName: "swarm",
788
+ },
789
+ };
790
+ // Record swarm in session stats so stats reflect auto-dispatched work
791
+ this.sessionStats.recordToolCall("spawn_swarm", result.duration);
792
+ for (const file of result.filesModified) {
793
+ this.changeTracker.recordChange(file, "edit");
794
+ }
795
+ // Record in conversation history using the structured summarizer so
796
+ // the model gets topology, per-agent summaries, files, and duration.
797
+ const assistantContent = summarizeSwarmResult({
798
+ topology: rec.topology,
799
+ task: message,
800
+ outcome: result.outcome,
801
+ agentSummaries: result.agentSummaries,
802
+ filesModified: result.filesModified,
803
+ duration: result.duration,
804
+ });
805
+ this.progressTracker.recordAssistantSummary(assistantContent);
806
+ this.syncProgressSnapshot();
807
+ this.conversationHistory.push({ role: "user", content: message }, { role: "assistant", content: assistantContent });
808
+ this.turnCount++;
809
+ // Emit session.completed for the swarm path too
810
+ this.eventBus.emit({
811
+ type: "session.completed",
812
+ sessionId: "",
813
+ turns: this.turnCount,
814
+ reason: "swarm_completed",
815
+ timestamp: Date.now(),
816
+ });
817
+ // Yield assistant message so the UI renders the swarm outcome.
818
+ // Without this, the outcome only appears in the "result" message
819
+ // whose content is ignored by MessageBlock (returns null).
820
+ yield {
821
+ type: "assistant",
822
+ sessionId: "",
823
+ turn: this.turnCount,
824
+ timestamp: Date.now(),
825
+ content: assistantContent,
826
+ };
827
+ yield {
828
+ type: "result",
829
+ sessionId: "",
830
+ turn: this.turnCount,
831
+ timestamp: Date.now(),
832
+ content: result.outcome,
833
+ metadata: {
834
+ swarmAutoDispatched: true,
835
+ topology: dispatch.recommendation.topology,
836
+ agentCount: dispatch.recommendation.suggestedAgentCount,
837
+ filesModified: result.filesModified,
838
+ duration: result.duration,
839
+ finalMessages: this.conversationHistory,
840
+ },
841
+ };
842
+ return;
843
+ }
844
+ catch (err) {
845
+ // Swarm failed — close lifecycle events, then fall through to agent loop
846
+ const errMsg = err instanceof Error ? err.message : String(err);
847
+ // Close the step_start/step_finish pair so the UI clears swarm spinner
848
+ yield {
849
+ type: "step_finish",
850
+ sessionId: "",
851
+ turn: this.turnCount,
852
+ timestamp: Date.now(),
853
+ metadata: {
854
+ stepDurationMs: Date.now() - swarmStartMs,
855
+ swarmTopology: rec.topology,
856
+ agentName: "swarm",
857
+ error: errMsg,
858
+ },
859
+ };
860
+ // Close the swarm.started EventBus event so subscribers see completion
861
+ if (!runnerPublishesSwarmLifecycle) {
862
+ this.eventBus.emit({
863
+ type: "swarm.completed",
864
+ swarmId,
865
+ topology: rec.topology,
866
+ agentCount,
867
+ durationMs: Date.now() - swarmStartMs,
868
+ agentResults: { error: errMsg },
869
+ timestamp: Date.now(),
870
+ });
871
+ }
872
+ yield {
873
+ type: "error",
874
+ sessionId: "",
875
+ turn: this.turnCount,
876
+ timestamp: Date.now(),
877
+ content: `Swarm auto-dispatch failed, falling back to agent loop: ${errMsg}`,
878
+ };
879
+ // Continue to normal agentLoop below
880
+ }
881
+ }
882
+ const loopConfig = {
883
+ model: this.config.model,
884
+ tools: scopedTools,
885
+ agentName: this.activeAgent,
886
+ systemPrompt,
887
+ maxTurns,
888
+ maxTokens: this.config.maxTokens ?? 16384,
889
+ initialMessages: this.conversationHistory,
890
+ todoModule: this.todoModule,
891
+ hooksEngine: this.hooksEngine,
892
+ compactor: this.autoCompactionEnabled ? this.compactor : undefined,
893
+ skillLoader: this.skillLoader,
894
+ signal: opts?.signal,
895
+ inferenceTimeoutMs: this.config.inferenceTimeoutMs,
896
+ env,
897
+ onQuestion: this.questionListener
898
+ ? (question, resolve) => this.questionListener(question, resolve)
899
+ : undefined,
900
+ onToolMetadata: this.toolMetadataListener
901
+ ? (toolCallId, toolName, update) => this.toolMetadataListener(toolCallId, toolName, update)
902
+ : undefined,
903
+ };
904
+ // ── Session-level circuit breaker ─────────────────────────
905
+ // If inference has failed on the last N consecutive chat() calls,
906
+ // stop attempting and tell the user the provider appears down.
907
+ if (this._consecutiveInferenceFailures >= Conductor.INFERENCE_FAILURE_THRESHOLD) {
908
+ yield {
909
+ type: "error",
910
+ sessionId: "",
911
+ turn: this.turnCount,
912
+ timestamp: Date.now(),
913
+ content: `Model provider appears down — inference has failed ${this._consecutiveInferenceFailures} consecutive times. Please wait and try again, or switch models.`,
914
+ metadata: {
915
+ circuitBreaker: true,
916
+ consecutiveFailures: this._consecutiveInferenceFailures,
917
+ },
918
+ };
919
+ return;
920
+ }
921
+ // Track last terminal message to derive session.completed reason
922
+ let lastTerminalType;
923
+ let lastBudgetExhausted = false;
924
+ for await (const msg of agentLoop(message, loopConfig)) {
925
+ // Track terminal messages (result/error) for session.completed reason
926
+ if (msg.type === "result") {
927
+ lastTerminalType = "result";
928
+ lastBudgetExhausted = !!msg.metadata?.budgetExhausted;
929
+ }
930
+ else if (msg.type === "error") {
931
+ lastTerminalType = "error";
932
+ }
933
+ // Emit agent.start + llm.request for step_start messages
934
+ if (msg.type === "step_start" && msg.metadata) {
935
+ const step = msg.turn ?? 0;
936
+ this.eventBus.emit({
937
+ type: "agent.start",
938
+ agent: this.activeAgent,
939
+ timestamp: Date.now(),
940
+ step,
941
+ });
942
+ this.eventBus.emit({
943
+ type: "llm.request",
944
+ agent: this.activeAgent,
945
+ model: this.config.model.modelId,
946
+ timestamp: Date.now(),
947
+ messageCount: this.conversationHistory.length,
948
+ hasTools: scopedTools.length > 0,
949
+ });
950
+ }
951
+ // Emit agent.end + llm.response for step_finish messages
952
+ if (msg.type === "step_finish" && msg.metadata) {
953
+ const meta = msg.metadata;
954
+ const step = msg.turn ?? 0;
955
+ this.eventBus.emit({
956
+ type: "agent.end",
957
+ agent: this.activeAgent,
958
+ timestamp: Date.now(),
959
+ step,
960
+ duration: meta.stepDurationMs ?? 0,
961
+ usage: meta.usage,
962
+ });
963
+ this.eventBus.emit({
964
+ type: "llm.response",
965
+ agent: this.activeAgent,
966
+ model: this.config.model.modelId,
967
+ timestamp: Date.now(),
968
+ duration: meta.stepDurationMs ?? 0,
969
+ usage: meta.usage ?? { inputTokens: 0, outputTokens: 0 },
970
+ stopReason: meta.stopReason ?? "end",
971
+ hasToolCalls: (meta.toolCount ?? 0) > 0,
972
+ });
973
+ }
974
+ // Emit session.compacting for in-chat compaction start
975
+ if (msg.type === "system" && msg.subtype === "compact_start" && msg.metadata) {
976
+ const meta = msg.metadata;
977
+ this.eventBus.emit({
978
+ type: "session.compacting",
979
+ sessionId: "",
980
+ messageCount: meta.beforeCount ?? 0,
981
+ estimatedTokens: meta.estimatedTokens,
982
+ threshold: meta.threshold,
983
+ maxTokens: meta.maxTokens,
984
+ percentUsed: meta.percentUsed,
985
+ timestamp: Date.now(),
986
+ });
987
+ }
988
+ // Emit session.compacted for in-chat compaction boundary
989
+ if (msg.type === "system" && msg.subtype === "compact_boundary" && msg.metadata) {
990
+ const meta = msg.metadata;
991
+ this.eventBus.emit({
992
+ type: "session.compacted",
993
+ sessionId: "",
994
+ beforeCount: meta.beforeCount ?? 0,
995
+ afterCount: meta.afterCount ?? 0,
996
+ summarized: meta.summarized ?? false,
997
+ estimatedTokensBefore: meta.estimatedTokensBefore,
998
+ estimatedTokensAfter: meta.estimatedTokensAfter,
999
+ threshold: meta.threshold,
1000
+ maxTokens: meta.maxTokens,
1001
+ percentUsedBefore: meta.percentUsedBefore,
1002
+ percentUsedAfter: meta.percentUsedAfter,
1003
+ timestamp: Date.now(),
1004
+ });
1005
+ }
1006
+ // Emit error event for compaction failures so EventBus subscribers
1007
+ // (analytics, UI) can react. The agent loop handles recovery internally
1008
+ // (continues the loop, retries next turn), but consumers should know.
1009
+ if (msg.type === "system" && msg.subtype === "compact_error") {
1010
+ const meta = msg.metadata;
1011
+ this.eventBus.emit({
1012
+ type: "session.compacted",
1013
+ sessionId: "",
1014
+ beforeCount: meta?.beforeCount ?? 0,
1015
+ afterCount: meta?.afterCount ?? meta?.beforeCount ?? 0,
1016
+ summarized: false,
1017
+ estimatedTokensBefore: meta?.estimatedTokensBefore,
1018
+ threshold: meta?.threshold,
1019
+ maxTokens: meta?.maxTokens,
1020
+ percentUsedBefore: meta?.percentUsedBefore,
1021
+ failed: true,
1022
+ error: meta?.error ?? msg.content ?? "Context compaction failed",
1023
+ timestamp: Date.now(),
1024
+ });
1025
+ this.eventBus.emit({
1026
+ type: "error",
1027
+ error: new Error(msg.content ?? "Context compaction failed"),
1028
+ timestamp: Date.now(),
1029
+ });
1030
+ }
1031
+ // Emit inference.retry for retry system messages
1032
+ if (msg.type === "system" && msg.subtype === "inference_retry" && msg.metadata) {
1033
+ const meta = msg.metadata;
1034
+ this.eventBus.emit({
1035
+ type: "inference.retry",
1036
+ attempt: meta.attempt ?? 1,
1037
+ maxRetries: meta.maxRetries ?? 3,
1038
+ delayMs: meta.delayMs ?? 0,
1039
+ error: meta.error ?? msg.content ?? "Unknown error",
1040
+ timestamp: Date.now(),
1041
+ });
1042
+ }
1043
+ // Track token usage from inference calls
1044
+ if (msg.type === "assistant" && msg.metadata?.usage) {
1045
+ const usage = msg.metadata.usage;
1046
+ this.sessionStats.recordInference(usage);
1047
+ this.eventBus.emit({
1048
+ type: "session.updated",
1049
+ sessionId: "",
1050
+ turn: this.turnCount + 1,
1051
+ tokenUsage: usage,
1052
+ timestamp: Date.now(),
1053
+ });
1054
+ }
1055
+ // Emit tool.call for tool_start messages
1056
+ if (msg.type === "tool_start" && msg.metadata) {
1057
+ const toolName = msg.metadata.toolName;
1058
+ const toolArgs = msg.metadata.toolArgs;
1059
+ if (toolName) {
1060
+ this.eventBus.emit({
1061
+ type: "tool.call",
1062
+ agent: this.activeAgent,
1063
+ tool: toolName,
1064
+ timestamp: Date.now(),
1065
+ input: toolArgs ?? {},
1066
+ });
1067
+ }
1068
+ }
1069
+ // Emit tool.result for tool_result messages
1070
+ if (msg.type === "tool_result" && msg.toolResults) {
1071
+ for (const tr of msg.toolResults) {
1072
+ if (tr.isError && tr.content?.startsWith("Tool use denied:")) {
1073
+ this.sessionStats.recordDenied(tr.toolName, tr.content);
1074
+ }
1075
+ this.eventBus.emit({
1076
+ type: "tool.result",
1077
+ agent: this.activeAgent,
1078
+ tool: tr.toolName,
1079
+ timestamp: Date.now(),
1080
+ duration: tr.durationMs ?? 0,
1081
+ status: tr.isError ? "error" : "success",
1082
+ output: tr.isError ? undefined : tr.content,
1083
+ error: tr.isError ? tr.content : undefined,
1084
+ });
1085
+ }
1086
+ }
1087
+ // Capture final messages from result or error to update history
1088
+ if (msg.type === "result" || msg.type === "error") {
1089
+ const finalMessages = msg.metadata?.finalMessages;
1090
+ if (finalMessages) {
1091
+ this.conversationHistory = finalMessages;
1092
+ this.sessionTree.updateMessages(finalMessages);
1093
+ }
1094
+ if (msg.type === "result" && msg.content) {
1095
+ this.progressTracker.recordAssistantSummary(msg.content);
1096
+ this.syncProgressSnapshot();
1097
+ }
1098
+ if (msg.type === "error") {
1099
+ this.eventBus.emit({
1100
+ type: "error",
1101
+ agent: this.activeAgent,
1102
+ error: new Error(msg.content ?? "Unknown error"),
1103
+ timestamp: Date.now(),
1104
+ });
1105
+ }
1106
+ }
1107
+ yield msg;
1108
+ }
1109
+ this.turnCount++;
1110
+ // ── Update session-level circuit breaker ─────────────────
1111
+ if (lastTerminalType === "error") {
1112
+ this._consecutiveInferenceFailures++;
1113
+ }
1114
+ else {
1115
+ this._consecutiveInferenceFailures = 0;
1116
+ }
1117
+ // ── Fire-and-forget title generation on first turn ──────
1118
+ if (this.turnCount === 1) {
1119
+ void this.titleGenerator.generate(message).catch(() => { });
1120
+ }
1121
+ // Emit session.completed with reason derived from actual termination
1122
+ const completedReason = lastTerminalType === "error"
1123
+ ? "error"
1124
+ : lastBudgetExhausted
1125
+ ? "budget_exhausted"
1126
+ : "completed";
1127
+ this.eventBus.emit({
1128
+ type: "session.completed",
1129
+ sessionId: "",
1130
+ turns: this.turnCount,
1131
+ reason: completedReason,
1132
+ timestamp: Date.now(),
1133
+ });
1134
+ // ── Yield agent_switch if agent changed during this turn ──
1135
+ if (this.activeAgent !== agentBefore) {
1136
+ yield {
1137
+ type: "agent_switch",
1138
+ sessionId: "",
1139
+ turn: this.turnCount,
1140
+ timestamp: Date.now(),
1141
+ content: `Agent switched from ${agentBefore} to ${this.activeAgent}`,
1142
+ metadata: {
1143
+ previousAgent: agentBefore,
1144
+ newAgent: this.activeAgent,
1145
+ },
1146
+ };
1147
+ }
1148
+ }
1149
+ finally {
1150
+ this._chatActive = false;
1151
+ }
1152
+ }
1153
+ // ── Public: chatToResult ────────────────────────────────────
1154
+ async chatToResult(message) {
1155
+ let finalResult = "";
1156
+ for await (const msg of this.chat(message)) {
1157
+ if (msg.type === "result") {
1158
+ finalResult = msg.content ?? "";
1159
+ }
1160
+ }
1161
+ return finalResult;
1162
+ }
1163
+ // ── Public: accessors ───────────────────────────────────────
1164
+ getPermissions() {
1165
+ return this.permissions;
1166
+ }
1167
+ setPermissionListener(listener) {
1168
+ this.permissionListener = listener;
1169
+ }
1170
+ setQuestionListener(listener) {
1171
+ this.questionListener = listener;
1172
+ }
1173
+ setToolMetadataListener(listener) {
1174
+ this.toolMetadataListener = listener;
1175
+ }
1176
+ /** Get the active agent name */
1177
+ getActiveAgent() {
1178
+ return this.activeAgent;
1179
+ }
1180
+ /** Switch the active agent. Takes effect on the next chat() call. */
1181
+ switchAgent(name) {
1182
+ const def = this.agentRegistry.get(name);
1183
+ if (!def) {
1184
+ throw new Error(`Unknown agent "${name}". Available: ${this.agentRegistry.names().join(", ")}`);
1185
+ }
1186
+ this.activeAgent = name;
1187
+ this._cachedSystemPrompt = null; // Invalidate prompt cache
1188
+ }
1189
+ /** Get the auto-generated session title (null if not yet generated). */
1190
+ getSessionTitle() {
1191
+ return this.titleGenerator.getTitle();
1192
+ }
1193
+ getChangeTracker() {
1194
+ return this.changeTracker;
1195
+ }
1196
+ getFileSnapshots() {
1197
+ return this.fileSnapshots;
1198
+ }
1199
+ getTurnCount() {
1200
+ return this.turnCount;
1201
+ }
1202
+ /** Get loaded context files content (CLAUDE.md, AGENTS.md, .cursorrules). Empty string if none found. */
1203
+ getContextFiles() {
1204
+ return this.contextFilesBlock;
1205
+ }
1206
+ /** Get the durable session progress snapshot used across restarts/compaction. */
1207
+ getProgressTracker() {
1208
+ return this.progressTracker;
1209
+ }
1210
+ /** Get the skill loader for external skill registration and invocation */
1211
+ getSkillLoader() {
1212
+ return this.skillLoader;
1213
+ }
1214
+ /** Get the currently available tool names for UI display. */
1215
+ getAvailableToolNames() {
1216
+ const names = [...this.todoModule.getTools(), ...this.tools].map((tool) => tool.name);
1217
+ return Array.from(new Set(names)).sort((a, b) => a.localeCompare(b));
1218
+ }
1219
+ /** Get the currently loaded skill names for UI display. */
1220
+ getAvailableSkillNames() {
1221
+ return this.skillLoader.getAll().map((skill) => skill.name).sort((a, b) => a.localeCompare(b));
1222
+ }
1223
+ /** Get configured MCP server names from ONI config. */
1224
+ getConfiguredMCPServerNames() {
1225
+ return Object.keys(this.config.oniConfig?.mcpServers ?? {}).sort((a, b) => a.localeCompare(b));
1226
+ }
1227
+ /** Invoke a skill by name with optional arguments — queues for next turn */
1228
+ invokeSkill(name, args) {
1229
+ return this.skillLoader.invoke(name, args);
1230
+ }
1231
+ getHistoryLength() {
1232
+ return this.conversationHistory.length;
1233
+ }
1234
+ clearHistory() {
1235
+ this.conversationHistory = [];
1236
+ this.turnCount = 0;
1237
+ this.sessionStats.reset();
1238
+ }
1239
+ // ── Session forking ──────────────────────────────────────────
1240
+ /**
1241
+ * Fork the current conversation at a specific message index.
1242
+ * Creates a new branch with messages [0..atIndex-1].
1243
+ * If atIndex is omitted, forks at the current end.
1244
+ * Returns the new branch ID.
1245
+ */
1246
+ forkSession(atIndex, label) {
1247
+ // Ensure tree is synced with current history
1248
+ this.sessionTree.updateMessages(this.conversationHistory);
1249
+ return this.sessionTree.fork(atIndex, label);
1250
+ }
1251
+ /**
1252
+ * Switch to a different conversation branch.
1253
+ * Updates the internal history to the branch's messages.
1254
+ */
1255
+ switchBranch(branchId) {
1256
+ const msgs = this.sessionTree.switchBranch(branchId);
1257
+ this.conversationHistory = msgs;
1258
+ }
1259
+ /**
1260
+ * List all conversation branches with summary info.
1261
+ */
1262
+ listBranches() {
1263
+ return this.sessionTree.listBranches();
1264
+ }
1265
+ /**
1266
+ * Get the active branch ID.
1267
+ */
1268
+ getActiveBranchId() {
1269
+ return this.sessionTree.getActiveBranchId();
1270
+ }
1271
+ /**
1272
+ * Delete a branch. Cannot delete the root.
1273
+ * If cascade is true, also deletes all children.
1274
+ */
1275
+ deleteBranch(branchId, cascade = false) {
1276
+ const deleted = this.sessionTree.deleteBranch(branchId, cascade);
1277
+ // If active branch was deleted, sync history to the new active (root)
1278
+ if (deleted && this.sessionTree.getActiveBranchId() !== branchId) {
1279
+ this.conversationHistory = this.sessionTree.getMessagesForLoop();
1280
+ }
1281
+ return deleted;
1282
+ }
1283
+ /**
1284
+ * Get the session tree for advanced operations (serialization, etc.).
1285
+ */
1286
+ getSessionTree() {
1287
+ return this.sessionTree;
1288
+ }
1289
+ /** Get session statistics (tool calls, tokens, compactions, errors). */
1290
+ getSessionStats() {
1291
+ return this.sessionStats;
1292
+ }
1293
+ /** Get the background scheduler for registering periodic tasks. */
1294
+ getScheduler() {
1295
+ return this.scheduler;
1296
+ }
1297
+ /** Get the loop detector for inspection or configuration. */
1298
+ getLoopDetector() {
1299
+ return this.loopDetector;
1300
+ }
1301
+ /** Get the event bus for subscribing to lifecycle events. */
1302
+ getEventBus() {
1303
+ return this.eventBus;
1304
+ }
1305
+ /** Get the file watcher for reactive file change detection. */
1306
+ getFileWatcher() {
1307
+ return this.fileWatcher;
1308
+ }
1309
+ /** Get the cron scheduler for managing recurring prompts. */
1310
+ getCronScheduler() {
1311
+ return this.cronScheduler;
1312
+ }
1313
+ /** Stop all background tasks and release resources.
1314
+ * Each cleanup step is isolated — a failure in one (e.g. corrupted
1315
+ * file handle, dead LSP process) must not prevent the others from
1316
+ * running, or resources leak (timers, child processes, listeners). */
1317
+ dispose() {
1318
+ try {
1319
+ this.fileWatcher.stop();
1320
+ }
1321
+ catch { /* best-effort */ }
1322
+ try {
1323
+ this.scheduler.dispose();
1324
+ }
1325
+ catch { /* best-effort */ }
1326
+ try {
1327
+ this.cronScheduler.dispose();
1328
+ }
1329
+ catch { /* best-effort */ }
1330
+ try {
1331
+ this.lspManager.disposeAll();
1332
+ }
1333
+ catch { /* best-effort */ }
1334
+ try {
1335
+ this.eventBus.dispose();
1336
+ }
1337
+ catch { /* best-effort */ }
1338
+ }
1339
+ /** Get the LSP manager for inspection or external use. */
1340
+ getLSPManager() {
1341
+ return this.lspManager;
1342
+ }
1343
+ /** Get the task evaluator for inspection or testing. */
1344
+ getTaskEvaluator() {
1345
+ return this.taskEvaluator;
1346
+ }
1347
+ /** Manually trigger context compaction. Returns before/after message counts. */
1348
+ async compactContext() {
1349
+ if (this.conversationHistory.length < 4)
1350
+ return null;
1351
+ const before = this.conversationHistory.length;
1352
+ const check = this.compactor.checkCompaction(this.conversationHistory);
1353
+ this.eventBus.emit({
1354
+ type: "session.compacting",
1355
+ sessionId: "",
1356
+ messageCount: before,
1357
+ estimatedTokens: check.estimatedTokens,
1358
+ threshold: check.threshold,
1359
+ maxTokens: check.maxTokens,
1360
+ percentUsed: check.percentUsed,
1361
+ timestamp: Date.now(),
1362
+ });
1363
+ await this.hooksEngine.fire("PreCompact", {
1364
+ sessionId: "",
1365
+ messageCount: before,
1366
+ estimatedTokens: check.estimatedTokens,
1367
+ });
1368
+ try {
1369
+ const compacted = await this.compactor.compact(this.conversationHistory, { skipInitialCheck: true });
1370
+ const summarized = compacted.length <= 2 && before > 2;
1371
+ const estimatedTokensAfter = this.compactor.estimateTokens(compacted);
1372
+ const percentUsedAfter = check.maxTokens > 0
1373
+ ? estimatedTokensAfter / check.maxTokens
1374
+ : 0;
1375
+ this.conversationHistory = compacted;
1376
+ this.eventBus.emit({
1377
+ type: "session.compacted",
1378
+ sessionId: "",
1379
+ beforeCount: before,
1380
+ afterCount: compacted.length,
1381
+ summarized,
1382
+ estimatedTokensBefore: check.estimatedTokens,
1383
+ estimatedTokensAfter,
1384
+ threshold: check.threshold,
1385
+ maxTokens: check.maxTokens,
1386
+ percentUsedBefore: check.percentUsed,
1387
+ percentUsedAfter,
1388
+ timestamp: Date.now(),
1389
+ });
1390
+ // Fire PostCompact hooks for state reinsertion (todo list, change tracker, stats)
1391
+ const postResult = await this.hooksEngine.fire("PostCompact", {
1392
+ sessionId: "",
1393
+ beforeCount: before,
1394
+ afterCount: compacted.length,
1395
+ estimatedTokensAfter,
1396
+ summarized,
1397
+ });
1398
+ // Inject state reinsertion + continuation prompt
1399
+ const focus = this.todoModule.getCurrentFocus();
1400
+ const continuationText = focus
1401
+ ? `Continue working. Current focus: ${focus.content}`
1402
+ : "Continue if you have next steps.";
1403
+ const userContent = postResult?.additionalContext
1404
+ ? `<system-reminder>\n${postResult.additionalContext}\n</system-reminder>\n\n${continuationText}`
1405
+ : continuationText;
1406
+ this.conversationHistory.push({ role: "user", content: userContent }, { role: "assistant", content: "Context loaded." });
1407
+ return { before, after: compacted.length };
1408
+ }
1409
+ catch (err) {
1410
+ const errorMsg = err instanceof Error ? err.message : String(err);
1411
+ // Compaction failed — close the lifecycle event and degrade gracefully.
1412
+ // Conversation history is unchanged (compact() returns a new array).
1413
+ this.eventBus.emit({
1414
+ type: "session.compacted",
1415
+ sessionId: "",
1416
+ beforeCount: before,
1417
+ afterCount: before,
1418
+ summarized: false,
1419
+ estimatedTokensBefore: check.estimatedTokens,
1420
+ threshold: check.threshold,
1421
+ maxTokens: check.maxTokens,
1422
+ percentUsedBefore: check.percentUsed,
1423
+ failed: true,
1424
+ error: errorMsg,
1425
+ timestamp: Date.now(),
1426
+ });
1427
+ this.eventBus.emit({
1428
+ type: "error",
1429
+ agent: this.activeAgent,
1430
+ error: new Error(`Context compaction failed: ${errorMsg}`),
1431
+ timestamp: Date.now(),
1432
+ });
1433
+ return null;
1434
+ }
1435
+ }
1436
+ // ── Private: runSubagent ────────────────────────────────────
1437
+ async runSubagent(task, agentName) {
1438
+ try {
1439
+ // Resolve agent definition from registry (if named)
1440
+ const agentDef = agentName ? this.agentRegistry.get(agentName) : undefined;
1441
+ // Reuse parent's prebuilt subagent tools (coding + web + custom).
1442
+ // These already share LSPManager, ChangeTracker, and FileSnapshots
1443
+ // so subagent edits are visible to list_changes and undo_edit.
1444
+ const scopedTools = agentDef?.tools
1445
+ ? (() => {
1446
+ const allowed = new Set(agentDef.tools);
1447
+ return this.subagentTools.filter((t) => allowed.has(t.name));
1448
+ })()
1449
+ : this.subagentTools;
1450
+ // Use fastModel for lightweight agents (explore, compaction) to reduce
1451
+ // latency and cost. Falls back to heavy model if no fastModel configured.
1452
+ const agentModel = agentDef?.useFastModel && this.config.fastModel
1453
+ ? this.config.fastModel
1454
+ : this.config.model;
1455
+ const subHarness = ONIHarness.create({
1456
+ model: agentModel,
1457
+ fastModel: this.config.fastModel,
1458
+ sharedTools: scopedTools,
1459
+ ...(agentDef?.systemPrompt ? { soul: agentDef.systemPrompt } : {}),
1460
+ });
1461
+ // Iterate run() manually instead of runToResult() so we can capture
1462
+ // step_finish usage and merge it into the parent's sessionStats,
1463
+ // and surface yielded error messages (e.g. inference failures) to
1464
+ // the parent agent instead of silently discarding them.
1465
+ let result = "";
1466
+ const errors = [];
1467
+ const agentConfig = {
1468
+ name: agentName ?? "subagent",
1469
+ ...(agentDef?.maxTurns ? { maxTurns: agentDef.maxTurns } : {}),
1470
+ };
1471
+ for await (const msg of subHarness.run(task, agentConfig)) {
1472
+ if (msg.type === "result") {
1473
+ result = msg.content ?? "";
1474
+ }
1475
+ else if (msg.type === "error") {
1476
+ // Capture yielded errors so the parent agent knows the subagent failed
1477
+ if (msg.content)
1478
+ errors.push(msg.content);
1479
+ }
1480
+ else if (msg.type === "step_finish") {
1481
+ // Merge subagent inference usage into parent stats
1482
+ const meta = msg.metadata;
1483
+ if (meta?.usage) {
1484
+ this.sessionStats.recordInference(meta.usage);
1485
+ }
1486
+ }
1487
+ }
1488
+ return {
1489
+ result,
1490
+ filesModified: [],
1491
+ errors,
1492
+ };
1493
+ }
1494
+ catch (err) {
1495
+ const message = err instanceof Error ? err.message : String(err);
1496
+ return {
1497
+ result: "",
1498
+ filesModified: [],
1499
+ errors: [message],
1500
+ };
1501
+ }
1502
+ }
1503
+ // ── Private: project_map tool ───────────────────────────────
1504
+ buildProjectMapTool() {
1505
+ let cachedMap = null;
1506
+ let cachedChangeVersion = -1;
1507
+ return defineTool({
1508
+ name: "project_map",
1509
+ description: "Get a map of the project structure — lists all files with sizes. Useful for understanding the workspace layout.",
1510
+ schema: {
1511
+ type: "object",
1512
+ properties: {
1513
+ refresh: {
1514
+ type: "boolean",
1515
+ description: "Force rebuild the project map (default false)",
1516
+ },
1517
+ },
1518
+ },
1519
+ parallelSafe: true,
1520
+ execute: (input) => {
1521
+ const currentVersion = this.changeTracker.getVersion();
1522
+ if (!cachedMap || input.refresh || this._projectMapDirty || cachedChangeVersion !== currentVersion) {
1523
+ cachedMap = ProjectMap.build(this.rootDir);
1524
+ cachedChangeVersion = currentVersion;
1525
+ this._projectMapDirty = false;
1526
+ }
1527
+ return {
1528
+ projectMap: cachedMap.toContextString(),
1529
+ fileCount: cachedMap.files.length,
1530
+ };
1531
+ },
1532
+ });
1533
+ }
1534
+ syncProgressSnapshot(opts) {
1535
+ this.progressTracker.syncTodoState(this.todoModule.getState());
1536
+ this.progressTracker.syncChangeTracker(this.changeTracker);
1537
+ if (opts?.persist !== false) {
1538
+ this.persistProgressSnapshot();
1539
+ }
1540
+ }
1541
+ persistProgressSnapshot() {
1542
+ if (this.progressTracker.persist()) {
1543
+ this._cachedSystemPrompt = null;
1544
+ }
1545
+ }
1546
+ }
1547
+ //# sourceMappingURL=conductor.js.map