im-hub-pro 0.2.29

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 (384) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +497 -0
  3. package/README.zh-CN.md +496 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +921 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/core/acp-server.d.ts +8 -0
  9. package/dist/core/acp-server.d.ts.map +1 -0
  10. package/dist/core/acp-server.js +266 -0
  11. package/dist/core/acp-server.js.map +1 -0
  12. package/dist/core/agent-base.d.ts +94 -0
  13. package/dist/core/agent-base.d.ts.map +1 -0
  14. package/dist/core/agent-base.js +374 -0
  15. package/dist/core/agent-base.js.map +1 -0
  16. package/dist/core/agent-cwd.d.ts +45 -0
  17. package/dist/core/agent-cwd.d.ts.map +1 -0
  18. package/dist/core/agent-cwd.js +178 -0
  19. package/dist/core/agent-cwd.js.map +1 -0
  20. package/dist/core/agent-cwd.test.d.ts +2 -0
  21. package/dist/core/agent-cwd.test.d.ts.map +1 -0
  22. package/dist/core/agent-cwd.test.js +149 -0
  23. package/dist/core/agent-cwd.test.js.map +1 -0
  24. package/dist/core/approval-bus.d.ts +232 -0
  25. package/dist/core/approval-bus.d.ts.map +1 -0
  26. package/dist/core/approval-bus.js +703 -0
  27. package/dist/core/approval-bus.js.map +1 -0
  28. package/dist/core/approval-bus.synthetic.test.d.ts +2 -0
  29. package/dist/core/approval-bus.synthetic.test.d.ts.map +1 -0
  30. package/dist/core/approval-bus.synthetic.test.js +182 -0
  31. package/dist/core/approval-bus.synthetic.test.js.map +1 -0
  32. package/dist/core/approval-bus.test.d.ts +2 -0
  33. package/dist/core/approval-bus.test.d.ts.map +1 -0
  34. package/dist/core/approval-bus.test.js +537 -0
  35. package/dist/core/approval-bus.test.js.map +1 -0
  36. package/dist/core/approval-router.d.ts +95 -0
  37. package/dist/core/approval-router.d.ts.map +1 -0
  38. package/dist/core/approval-router.js +450 -0
  39. package/dist/core/approval-router.js.map +1 -0
  40. package/dist/core/approval-router.test.d.ts +2 -0
  41. package/dist/core/approval-router.test.d.ts.map +1 -0
  42. package/dist/core/approval-router.test.js +413 -0
  43. package/dist/core/approval-router.test.js.map +1 -0
  44. package/dist/core/audit-log.d.ts +55 -0
  45. package/dist/core/audit-log.d.ts.map +1 -0
  46. package/dist/core/audit-log.js +203 -0
  47. package/dist/core/audit-log.js.map +1 -0
  48. package/dist/core/bgjob-reader.d.ts +65 -0
  49. package/dist/core/bgjob-reader.d.ts.map +1 -0
  50. package/dist/core/bgjob-reader.js +212 -0
  51. package/dist/core/bgjob-reader.js.map +1 -0
  52. package/dist/core/bgjob-reader.test.d.ts +2 -0
  53. package/dist/core/bgjob-reader.test.d.ts.map +1 -0
  54. package/dist/core/bgjob-reader.test.js +178 -0
  55. package/dist/core/bgjob-reader.test.js.map +1 -0
  56. package/dist/core/circuit-breaker.d.ts +37 -0
  57. package/dist/core/circuit-breaker.d.ts.map +1 -0
  58. package/dist/core/circuit-breaker.js +115 -0
  59. package/dist/core/circuit-breaker.js.map +1 -0
  60. package/dist/core/commands/agent.d.ts +4 -0
  61. package/dist/core/commands/agent.d.ts.map +1 -0
  62. package/dist/core/commands/agent.js +21 -0
  63. package/dist/core/commands/agent.js.map +1 -0
  64. package/dist/core/commands/approval.d.ts +3 -0
  65. package/dist/core/commands/approval.d.ts.map +1 -0
  66. package/dist/core/commands/approval.js +44 -0
  67. package/dist/core/commands/approval.js.map +1 -0
  68. package/dist/core/commands/approval.test.d.ts +2 -0
  69. package/dist/core/commands/approval.test.d.ts.map +1 -0
  70. package/dist/core/commands/approval.test.js +85 -0
  71. package/dist/core/commands/approval.test.js.map +1 -0
  72. package/dist/core/commands/audit.d.ts +3 -0
  73. package/dist/core/commands/audit.d.ts.map +1 -0
  74. package/dist/core/commands/audit.js +84 -0
  75. package/dist/core/commands/audit.js.map +1 -0
  76. package/dist/core/commands/builtin.d.ts +3 -0
  77. package/dist/core/commands/builtin.d.ts.map +1 -0
  78. package/dist/core/commands/builtin.js +26 -0
  79. package/dist/core/commands/builtin.js.map +1 -0
  80. package/dist/core/commands/job.d.ts +3 -0
  81. package/dist/core/commands/job.d.ts.map +1 -0
  82. package/dist/core/commands/job.js +195 -0
  83. package/dist/core/commands/job.js.map +1 -0
  84. package/dist/core/commands/model.d.ts +9 -0
  85. package/dist/core/commands/model.d.ts.map +1 -0
  86. package/dist/core/commands/model.js +183 -0
  87. package/dist/core/commands/model.js.map +1 -0
  88. package/dist/core/commands/plan.d.ts +3 -0
  89. package/dist/core/commands/plan.d.ts.map +1 -0
  90. package/dist/core/commands/plan.js +75 -0
  91. package/dist/core/commands/plan.js.map +1 -0
  92. package/dist/core/commands/plan.test.d.ts +2 -0
  93. package/dist/core/commands/plan.test.d.ts.map +1 -0
  94. package/dist/core/commands/plan.test.js +122 -0
  95. package/dist/core/commands/plan.test.js.map +1 -0
  96. package/dist/core/commands/router.d.ts +3 -0
  97. package/dist/core/commands/router.d.ts.map +1 -0
  98. package/dist/core/commands/router.js +71 -0
  99. package/dist/core/commands/router.js.map +1 -0
  100. package/dist/core/commands/schedule.d.ts +3 -0
  101. package/dist/core/commands/schedule.d.ts.map +1 -0
  102. package/dist/core/commands/schedule.js +123 -0
  103. package/dist/core/commands/schedule.js.map +1 -0
  104. package/dist/core/commands/sessions.d.ts +3 -0
  105. package/dist/core/commands/sessions.d.ts.map +1 -0
  106. package/dist/core/commands/sessions.js +88 -0
  107. package/dist/core/commands/sessions.js.map +1 -0
  108. package/dist/core/commands/stats.d.ts +3 -0
  109. package/dist/core/commands/stats.d.ts.map +1 -0
  110. package/dist/core/commands/stats.js +73 -0
  111. package/dist/core/commands/stats.js.map +1 -0
  112. package/dist/core/commands/think.d.ts +3 -0
  113. package/dist/core/commands/think.d.ts.map +1 -0
  114. package/dist/core/commands/think.js +28 -0
  115. package/dist/core/commands/think.js.map +1 -0
  116. package/dist/core/commands/workspaces.d.ts +3 -0
  117. package/dist/core/commands/workspaces.d.ts.map +1 -0
  118. package/dist/core/commands/workspaces.js +47 -0
  119. package/dist/core/commands/workspaces.js.map +1 -0
  120. package/dist/core/config-schema.d.ts +58 -0
  121. package/dist/core/config-schema.d.ts.map +1 -0
  122. package/dist/core/config-schema.js +63 -0
  123. package/dist/core/config-schema.js.map +1 -0
  124. package/dist/core/cron.d.ts +29 -0
  125. package/dist/core/cron.d.ts.map +1 -0
  126. package/dist/core/cron.js +184 -0
  127. package/dist/core/cron.js.map +1 -0
  128. package/dist/core/event-bus.d.ts +80 -0
  129. package/dist/core/event-bus.d.ts.map +1 -0
  130. package/dist/core/event-bus.js +62 -0
  131. package/dist/core/event-bus.js.map +1 -0
  132. package/dist/core/intent-llm.d.ts +27 -0
  133. package/dist/core/intent-llm.d.ts.map +1 -0
  134. package/dist/core/intent-llm.js +170 -0
  135. package/dist/core/intent-llm.js.map +1 -0
  136. package/dist/core/intent.d.ts +12 -0
  137. package/dist/core/intent.d.ts.map +1 -0
  138. package/dist/core/intent.js +187 -0
  139. package/dist/core/intent.js.map +1 -0
  140. package/dist/core/job-board.d.ts +84 -0
  141. package/dist/core/job-board.d.ts.map +1 -0
  142. package/dist/core/job-board.js +379 -0
  143. package/dist/core/job-board.js.map +1 -0
  144. package/dist/core/logger.d.ts +6 -0
  145. package/dist/core/logger.d.ts.map +1 -0
  146. package/dist/core/logger.js +54 -0
  147. package/dist/core/logger.js.map +1 -0
  148. package/dist/core/metrics.d.ts +55 -0
  149. package/dist/core/metrics.d.ts.map +1 -0
  150. package/dist/core/metrics.js +291 -0
  151. package/dist/core/metrics.js.map +1 -0
  152. package/dist/core/onboarding.d.ts +94 -0
  153. package/dist/core/onboarding.d.ts.map +1 -0
  154. package/dist/core/onboarding.js +426 -0
  155. package/dist/core/onboarding.js.map +1 -0
  156. package/dist/core/onboarding.test.d.ts +2 -0
  157. package/dist/core/onboarding.test.d.ts.map +1 -0
  158. package/dist/core/onboarding.test.js +112 -0
  159. package/dist/core/onboarding.test.js.map +1 -0
  160. package/dist/core/rate-limiter.d.ts +44 -0
  161. package/dist/core/rate-limiter.d.ts.map +1 -0
  162. package/dist/core/rate-limiter.js +115 -0
  163. package/dist/core/rate-limiter.js.map +1 -0
  164. package/dist/core/registry.d.ts +32 -0
  165. package/dist/core/registry.d.ts.map +1 -0
  166. package/dist/core/registry.js +122 -0
  167. package/dist/core/registry.js.map +1 -0
  168. package/dist/core/router.d.ts +41 -0
  169. package/dist/core/router.d.ts.map +1 -0
  170. package/dist/core/router.js +431 -0
  171. package/dist/core/router.js.map +1 -0
  172. package/dist/core/schedule.d.ts +65 -0
  173. package/dist/core/schedule.d.ts.map +1 -0
  174. package/dist/core/schedule.js +316 -0
  175. package/dist/core/schedule.js.map +1 -0
  176. package/dist/core/session-subtasks.test.d.ts +2 -0
  177. package/dist/core/session-subtasks.test.d.ts.map +1 -0
  178. package/dist/core/session-subtasks.test.js +88 -0
  179. package/dist/core/session-subtasks.test.js.map +1 -0
  180. package/dist/core/session.d.ts +182 -0
  181. package/dist/core/session.d.ts.map +1 -0
  182. package/dist/core/session.js +774 -0
  183. package/dist/core/session.js.map +1 -0
  184. package/dist/core/sqlite-helper.d.ts +37 -0
  185. package/dist/core/sqlite-helper.d.ts.map +1 -0
  186. package/dist/core/sqlite-helper.js +79 -0
  187. package/dist/core/sqlite-helper.js.map +1 -0
  188. package/dist/core/transcribe.d.ts +25 -0
  189. package/dist/core/transcribe.d.ts.map +1 -0
  190. package/dist/core/transcribe.js +217 -0
  191. package/dist/core/transcribe.js.map +1 -0
  192. package/dist/core/transcribe.test.d.ts +2 -0
  193. package/dist/core/transcribe.test.d.ts.map +1 -0
  194. package/dist/core/transcribe.test.js +163 -0
  195. package/dist/core/transcribe.test.js.map +1 -0
  196. package/dist/core/types.d.ts +352 -0
  197. package/dist/core/types.d.ts.map +1 -0
  198. package/dist/core/types.js +3 -0
  199. package/dist/core/types.js.map +1 -0
  200. package/dist/core/workspace.d.ts +67 -0
  201. package/dist/core/workspace.d.ts.map +1 -0
  202. package/dist/core/workspace.js +113 -0
  203. package/dist/core/workspace.js.map +1 -0
  204. package/dist/index.d.ts +5 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +6 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/plugins/agents/acp/acp-adapter.d.ts +16 -0
  209. package/dist/plugins/agents/acp/acp-adapter.d.ts.map +1 -0
  210. package/dist/plugins/agents/acp/acp-adapter.js +49 -0
  211. package/dist/plugins/agents/acp/acp-adapter.js.map +1 -0
  212. package/dist/plugins/agents/acp/acp-client.d.ts +32 -0
  213. package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -0
  214. package/dist/plugins/agents/acp/acp-client.js +175 -0
  215. package/dist/plugins/agents/acp/acp-client.js.map +1 -0
  216. package/dist/plugins/agents/acp/discovery.d.ts +19 -0
  217. package/dist/plugins/agents/acp/discovery.d.ts.map +1 -0
  218. package/dist/plugins/agents/acp/discovery.js +109 -0
  219. package/dist/plugins/agents/acp/discovery.js.map +1 -0
  220. package/dist/plugins/agents/acp/index.d.ts +4 -0
  221. package/dist/plugins/agents/acp/index.d.ts.map +1 -0
  222. package/dist/plugins/agents/acp/index.js +4 -0
  223. package/dist/plugins/agents/acp/index.js.map +1 -0
  224. package/dist/plugins/agents/acp/types.d.ts +62 -0
  225. package/dist/plugins/agents/acp/types.d.ts.map +1 -0
  226. package/dist/plugins/agents/acp/types.js +5 -0
  227. package/dist/plugins/agents/acp/types.js.map +1 -0
  228. package/dist/plugins/agents/claude-code/adapter.test.d.ts +2 -0
  229. package/dist/plugins/agents/claude-code/adapter.test.d.ts.map +1 -0
  230. package/dist/plugins/agents/claude-code/adapter.test.js +195 -0
  231. package/dist/plugins/agents/claude-code/adapter.test.js.map +1 -0
  232. package/dist/plugins/agents/claude-code/index.d.ts +25 -0
  233. package/dist/plugins/agents/claude-code/index.d.ts.map +1 -0
  234. package/dist/plugins/agents/claude-code/index.js +184 -0
  235. package/dist/plugins/agents/claude-code/index.js.map +1 -0
  236. package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +42 -0
  237. package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -0
  238. package/dist/plugins/agents/claude-code/mcp-approval-server.js +235 -0
  239. package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -0
  240. package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts +2 -0
  241. package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts.map +1 -0
  242. package/dist/plugins/agents/claude-code/mcp-approval-server.test.js +188 -0
  243. package/dist/plugins/agents/claude-code/mcp-approval-server.test.js.map +1 -0
  244. package/dist/plugins/agents/codex/adapter.test.d.ts +2 -0
  245. package/dist/plugins/agents/codex/adapter.test.d.ts.map +1 -0
  246. package/dist/plugins/agents/codex/adapter.test.js +192 -0
  247. package/dist/plugins/agents/codex/adapter.test.js.map +1 -0
  248. package/dist/plugins/agents/codex/index.d.ts +37 -0
  249. package/dist/plugins/agents/codex/index.d.ts.map +1 -0
  250. package/dist/plugins/agents/codex/index.js +254 -0
  251. package/dist/plugins/agents/codex/index.js.map +1 -0
  252. package/dist/plugins/agents/copilot/index.d.ts +35 -0
  253. package/dist/plugins/agents/copilot/index.d.ts.map +1 -0
  254. package/dist/plugins/agents/copilot/index.js +182 -0
  255. package/dist/plugins/agents/copilot/index.js.map +1 -0
  256. package/dist/plugins/agents/opencode/adapter.test.d.ts +2 -0
  257. package/dist/plugins/agents/opencode/adapter.test.d.ts.map +1 -0
  258. package/dist/plugins/agents/opencode/adapter.test.js +139 -0
  259. package/dist/plugins/agents/opencode/adapter.test.js.map +1 -0
  260. package/dist/plugins/agents/opencode/http-adapter.test.d.ts +2 -0
  261. package/dist/plugins/agents/opencode/http-adapter.test.d.ts.map +1 -0
  262. package/dist/plugins/agents/opencode/http-adapter.test.js +492 -0
  263. package/dist/plugins/agents/opencode/http-adapter.test.js.map +1 -0
  264. package/dist/plugins/agents/opencode/index.d.ts +5 -0
  265. package/dist/plugins/agents/opencode/index.d.ts.map +1 -0
  266. package/dist/plugins/agents/opencode/index.js +30 -0
  267. package/dist/plugins/agents/opencode/index.js.map +1 -0
  268. package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts +138 -0
  269. package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -0
  270. package/dist/plugins/agents/opencode/opencode-http-adapter.js +549 -0
  271. package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -0
  272. package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +24 -0
  273. package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -0
  274. package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +103 -0
  275. package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -0
  276. package/dist/plugins/agents/opencode/serve-manager.d.ts +27 -0
  277. package/dist/plugins/agents/opencode/serve-manager.d.ts.map +1 -0
  278. package/dist/plugins/agents/opencode/serve-manager.js +190 -0
  279. package/dist/plugins/agents/opencode/serve-manager.js.map +1 -0
  280. package/dist/plugins/messengers/discord/discord-adapter.d.ts +22 -0
  281. package/dist/plugins/messengers/discord/discord-adapter.d.ts.map +1 -0
  282. package/dist/plugins/messengers/discord/discord-adapter.js +241 -0
  283. package/dist/plugins/messengers/discord/discord-adapter.js.map +1 -0
  284. package/dist/plugins/messengers/discord/discord-adapter.test.d.ts +2 -0
  285. package/dist/plugins/messengers/discord/discord-adapter.test.d.ts.map +1 -0
  286. package/dist/plugins/messengers/discord/discord-adapter.test.js +332 -0
  287. package/dist/plugins/messengers/discord/discord-adapter.test.js.map +1 -0
  288. package/dist/plugins/messengers/discord/index.d.ts +4 -0
  289. package/dist/plugins/messengers/discord/index.d.ts.map +1 -0
  290. package/dist/plugins/messengers/discord/index.js +4 -0
  291. package/dist/plugins/messengers/discord/index.js.map +1 -0
  292. package/dist/plugins/messengers/discord/markdown-to-discord.d.ts +11 -0
  293. package/dist/plugins/messengers/discord/markdown-to-discord.d.ts.map +1 -0
  294. package/dist/plugins/messengers/discord/markdown-to-discord.js +59 -0
  295. package/dist/plugins/messengers/discord/markdown-to-discord.js.map +1 -0
  296. package/dist/plugins/messengers/discord/types.d.ts +9 -0
  297. package/dist/plugins/messengers/discord/types.d.ts.map +1 -0
  298. package/dist/plugins/messengers/discord/types.js +3 -0
  299. package/dist/plugins/messengers/discord/types.js.map +1 -0
  300. package/dist/plugins/messengers/feishu/card-builder.d.ts +23 -0
  301. package/dist/plugins/messengers/feishu/card-builder.d.ts.map +1 -0
  302. package/dist/plugins/messengers/feishu/card-builder.js +89 -0
  303. package/dist/plugins/messengers/feishu/card-builder.js.map +1 -0
  304. package/dist/plugins/messengers/feishu/feishu-adapter.d.ts +33 -0
  305. package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -0
  306. package/dist/plugins/messengers/feishu/feishu-adapter.js +195 -0
  307. package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -0
  308. package/dist/plugins/messengers/feishu/feishu-client.d.ts +44 -0
  309. package/dist/plugins/messengers/feishu/feishu-client.d.ts.map +1 -0
  310. package/dist/plugins/messengers/feishu/feishu-client.js +120 -0
  311. package/dist/plugins/messengers/feishu/feishu-client.js.map +1 -0
  312. package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts +2 -0
  313. package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts.map +1 -0
  314. package/dist/plugins/messengers/feishu/feishu-dedup.test.js +70 -0
  315. package/dist/plugins/messengers/feishu/feishu-dedup.test.js.map +1 -0
  316. package/dist/plugins/messengers/feishu/index.d.ts +4 -0
  317. package/dist/plugins/messengers/feishu/index.d.ts.map +1 -0
  318. package/dist/plugins/messengers/feishu/index.js +4 -0
  319. package/dist/plugins/messengers/feishu/index.js.map +1 -0
  320. package/dist/plugins/messengers/feishu/types.d.ts +113 -0
  321. package/dist/plugins/messengers/feishu/types.d.ts.map +1 -0
  322. package/dist/plugins/messengers/feishu/types.js +4 -0
  323. package/dist/plugins/messengers/feishu/types.js.map +1 -0
  324. package/dist/plugins/messengers/telegram/index.d.ts +4 -0
  325. package/dist/plugins/messengers/telegram/index.d.ts.map +1 -0
  326. package/dist/plugins/messengers/telegram/index.js +4 -0
  327. package/dist/plugins/messengers/telegram/index.js.map +1 -0
  328. package/dist/plugins/messengers/telegram/markdown-to-html.d.ts +5 -0
  329. package/dist/plugins/messengers/telegram/markdown-to-html.d.ts.map +1 -0
  330. package/dist/plugins/messengers/telegram/markdown-to-html.js +186 -0
  331. package/dist/plugins/messengers/telegram/markdown-to-html.js.map +1 -0
  332. package/dist/plugins/messengers/telegram/media-download.d.ts +51 -0
  333. package/dist/plugins/messengers/telegram/media-download.d.ts.map +1 -0
  334. package/dist/plugins/messengers/telegram/media-download.js +224 -0
  335. package/dist/plugins/messengers/telegram/media-download.js.map +1 -0
  336. package/dist/plugins/messengers/telegram/media-download.test.d.ts +2 -0
  337. package/dist/plugins/messengers/telegram/media-download.test.d.ts.map +1 -0
  338. package/dist/plugins/messengers/telegram/media-download.test.js +125 -0
  339. package/dist/plugins/messengers/telegram/media-download.test.js.map +1 -0
  340. package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +62 -0
  341. package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -0
  342. package/dist/plugins/messengers/telegram/telegram-adapter.js +653 -0
  343. package/dist/plugins/messengers/telegram/telegram-adapter.js.map +1 -0
  344. package/dist/plugins/messengers/telegram/types.d.ts +47 -0
  345. package/dist/plugins/messengers/telegram/types.d.ts.map +1 -0
  346. package/dist/plugins/messengers/telegram/types.js +3 -0
  347. package/dist/plugins/messengers/telegram/types.js.map +1 -0
  348. package/dist/plugins/messengers/wechat/ilink-adapter.d.ts +68 -0
  349. package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -0
  350. package/dist/plugins/messengers/wechat/ilink-adapter.js +483 -0
  351. package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -0
  352. package/dist/plugins/messengers/wechat/ilink-client.d.ts +66 -0
  353. package/dist/plugins/messengers/wechat/ilink-client.d.ts.map +1 -0
  354. package/dist/plugins/messengers/wechat/ilink-client.js +288 -0
  355. package/dist/plugins/messengers/wechat/ilink-client.js.map +1 -0
  356. package/dist/plugins/messengers/wechat/ilink-types.d.ts +173 -0
  357. package/dist/plugins/messengers/wechat/ilink-types.d.ts.map +1 -0
  358. package/dist/plugins/messengers/wechat/ilink-types.js +12 -0
  359. package/dist/plugins/messengers/wechat/ilink-types.js.map +1 -0
  360. package/dist/utils/backoff.d.ts +35 -0
  361. package/dist/utils/backoff.d.ts.map +1 -0
  362. package/dist/utils/backoff.js +59 -0
  363. package/dist/utils/backoff.js.map +1 -0
  364. package/dist/utils/cross-platform.d.ts +26 -0
  365. package/dist/utils/cross-platform.d.ts.map +1 -0
  366. package/dist/utils/cross-platform.js +58 -0
  367. package/dist/utils/cross-platform.js.map +1 -0
  368. package/dist/utils/message-split.d.ts +14 -0
  369. package/dist/utils/message-split.d.ts.map +1 -0
  370. package/dist/utils/message-split.js +65 -0
  371. package/dist/utils/message-split.js.map +1 -0
  372. package/dist/utils/safe-equal.d.ts +2 -0
  373. package/dist/utils/safe-equal.d.ts.map +1 -0
  374. package/dist/utils/safe-equal.js +11 -0
  375. package/dist/utils/safe-equal.js.map +1 -0
  376. package/dist/web/public/_app.js +196 -0
  377. package/dist/web/public/index.html +935 -0
  378. package/dist/web/public/settings.html +1181 -0
  379. package/dist/web/public/tasks.html +1827 -0
  380. package/dist/web/server.d.ts +11 -0
  381. package/dist/web/server.d.ts.map +1 -0
  382. package/dist/web/server.js +1820 -0
  383. package/dist/web/server.js.map +1 -0
  384. package/package.json +73 -0
@@ -0,0 +1,62 @@
1
+ // im-hub event bus — fan-out for cross-cutting state changes that the
2
+ // dashboard / future plugins want to observe in real time.
3
+ //
4
+ // Why a dedicated bus instead of just exposing each module's
5
+ // EventEmitter?
6
+ //
7
+ // 1. Type uniformity. Subscribers want to handle *any* event with one
8
+ // handler shape; otherwise tasks.html ends up wiring six different
9
+ // EventEmitters by hand.
10
+ // 2. Cross-module decoupling. audit-log / approval-bus / job-board
11
+ // should not know that a Web SSE consumer exists. They emit on the
12
+ // bus; whoever wants to listen does.
13
+ // 3. Backpressure / fan-out. The /events SSE endpoint multiplexes a
14
+ // single bus subscription out to N browser clients. If a single
15
+ // client is slow we don't want to drag the entire process; the bus
16
+ // only does fan-out, per-connection write-buffering is the SSE
17
+ // handler's responsibility.
18
+ //
19
+ // IMPORTANT: emit is best-effort. Never throw out of an emit() — the
20
+ // caller (audit-log.logInvocation, approvalBus.handleApproval, etc.) is
21
+ // in a hot path. We swallow listener errors here so a buggy subscriber
22
+ // can't break the originating action.
23
+ import { EventEmitter } from 'events';
24
+ class ImhubEventBus extends EventEmitter {
25
+ /** Bounded recent-event log so a freshly-connected SSE client can
26
+ * catch up on what happened in the last few seconds without needing
27
+ * a separate REST poll. Cap at 200 entries — enough for the dashboard
28
+ * to show a meaningful "what just happened" feed without bloating
29
+ * process memory. */
30
+ recent = [];
31
+ static RECENT_CAP = 200;
32
+ constructor() {
33
+ super();
34
+ // EventEmitter defaults to 10 listeners before warning; we expect one
35
+ // handler per SSE connection plus a few internal subscribers, so 100
36
+ // is a comfortable cap that still flags accidental leaks.
37
+ this.setMaxListeners(100);
38
+ }
39
+ /**
40
+ * Publish a typed event. Listeners are called synchronously inside
41
+ * EventEmitter.emit; errors thrown from a listener are caught here so
42
+ * a bad subscriber can't break audit-log / approval-bus / job-board.
43
+ */
44
+ publish(event) {
45
+ this.recent.push(event);
46
+ while (this.recent.length > ImhubEventBus.RECENT_CAP)
47
+ this.recent.shift();
48
+ try {
49
+ this.emit('event', event);
50
+ }
51
+ catch {
52
+ // EventEmitter normally rethrows — caller is in a hot path so we swallow.
53
+ }
54
+ }
55
+ /** Snapshot of the recent ring buffer. Used by SSE handler on
56
+ * connection to replay the last N events to a fresh client. */
57
+ getRecent() {
58
+ return this.recent.slice();
59
+ }
60
+ }
61
+ export const eventBus = new ImhubEventBus();
62
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/core/event-bus.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2DAA2D;AAC3D,EAAE;AACF,6DAA6D;AAC7D,gBAAgB;AAChB,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,8BAA8B;AAC9B,qEAAqE;AACrE,wEAAwE;AACxE,0CAA0C;AAC1C,sEAAsE;AACtE,qEAAqE;AACrE,wEAAwE;AACxE,oEAAoE;AACpE,iCAAiC;AACjC,EAAE;AACF,qEAAqE;AACrE,wEAAwE;AACxE,uEAAuE;AACvE,sCAAsC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAgErC,MAAM,aAAc,SAAQ,YAAY;IACtC;;;;0BAIsB;IACd,MAAM,GAAiB,EAAE,CAAA;IACzB,MAAM,CAAU,UAAU,GAAG,GAAG,CAAA;IAExC;QACE,KAAK,EAAE,CAAA;QACP,sEAAsE;QACtE,qEAAqE;QACrE,0DAA0D;QAC1D,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAiB;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC,UAAU;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;QACzE,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IAED;oEACgE;IAChE,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;IAC5B,CAAC;;AAGH,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAA"}
@@ -0,0 +1,27 @@
1
+ import type { AgentAdapter } from './types.js';
2
+ export interface LLMJudgeConfig {
3
+ /** Agent name used as the judge. Must already be registered. */
4
+ agentName: string;
5
+ /** Score threshold below which the LLM is consulted (default 1.0). */
6
+ threshold?: number;
7
+ /** Override the default 5s response timeout. */
8
+ timeoutMs?: number;
9
+ }
10
+ export declare function configureLLMJudge(cfg: LLMJudgeConfig | null): void;
11
+ /** Read the judge configuration. Used by classifyIntent to know whether
12
+ * to attempt a fallback at all. */
13
+ export declare function getLLMJudge(): LLMJudgeConfig | null;
14
+ /**
15
+ * Ask the judge agent which candidate to use. Returns null on any
16
+ * failure path (timeout, judge unavailable, malformed response) so
17
+ * the caller can keep its rule-based decision.
18
+ */
19
+ export declare function classifyWithLLM(text: string, candidates: string[], resolveAgent: (name: string) => AgentAdapter | undefined): Promise<{
20
+ agent: string;
21
+ reason: string;
22
+ } | null>;
23
+ /** Test-only: clear cache so memoization doesn't leak between cases. */
24
+ export declare function _clearLLMCache(): void;
25
+ /** Test/diagnostic: current cache size. */
26
+ export declare function _cacheSize(): number;
27
+ //# sourceMappingURL=intent-llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-llm.d.ts","sourceRoot":"","sources":["../../src/core/intent-llm.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAsD9C,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;IACjB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAGlE;AAED;oCACoC;AACpC,wBAAgB,WAAW,IAAI,cAAc,GAAG,IAAI,CAEnD;AAgDD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAAE,EACpB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,GACvD,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqDnD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,IAAI,MAAM,CAAsB"}
@@ -0,0 +1,170 @@
1
+ // LLM-backed intent fallback (P2-H).
2
+ //
3
+ // When the rule-based classifier produces a low-confidence result we
4
+ // ask a configured "judge agent" — itself one of the registered agents
5
+ // — to pick the best agent. Opt-in via env (IM_HUB_LLM_JUDGE_AGENT)
6
+ // or programmatic configureLLMJudge() so we never silently spend
7
+ // tokens by default.
8
+ //
9
+ // Caching: identical prompt+candidate-list pairs are memoized within a
10
+ // 10-minute window so repeated chat-room banter doesn't burn budget.
11
+ import { createHash } from 'crypto';
12
+ import { logger as rootLogger } from './logger.js';
13
+ const log = rootLogger.child({ component: 'intent-llm' });
14
+ const CACHE_TTL_MS = 10 * 60 * 1000;
15
+ const CACHE_MAX_ENTRIES = 1000; // LRU upper bound prevents unbounded growth
16
+ const RESPONSE_TIMEOUT_MS = 5_000;
17
+ /**
18
+ * LRU cache: insertion-order Map; on access we delete + re-set to bump
19
+ * the entry to the end. When size exceeds max we evict the head (the
20
+ * oldest unused entry).
21
+ */
22
+ class LRUCache {
23
+ maxSize;
24
+ ttlMs;
25
+ store = new Map();
26
+ constructor(maxSize, ttlMs) {
27
+ this.maxSize = maxSize;
28
+ this.ttlMs = ttlMs;
29
+ }
30
+ get(key) {
31
+ const v = this.store.get(key);
32
+ if (!v)
33
+ return undefined;
34
+ if (Date.now() - v.ts > this.ttlMs) {
35
+ this.store.delete(key);
36
+ return undefined;
37
+ }
38
+ // Touch — move to end of insertion order
39
+ this.store.delete(key);
40
+ this.store.set(key, v);
41
+ return v;
42
+ }
43
+ set(key, value) {
44
+ if (this.store.has(key))
45
+ this.store.delete(key);
46
+ this.store.set(key, value);
47
+ // Evict from the head until under cap
48
+ while (this.store.size > this.maxSize) {
49
+ const first = this.store.keys().next().value;
50
+ if (first === undefined)
51
+ break;
52
+ this.store.delete(first);
53
+ }
54
+ }
55
+ clear() { this.store.clear(); }
56
+ get size() { return this.store.size; }
57
+ }
58
+ const cache = new LRUCache(CACHE_MAX_ENTRIES, CACHE_TTL_MS);
59
+ let config = null;
60
+ export function configureLLMJudge(cfg) {
61
+ config = cfg;
62
+ cache.clear();
63
+ }
64
+ /** Read the judge configuration. Used by classifyIntent to know whether
65
+ * to attempt a fallback at all. */
66
+ export function getLLMJudge() {
67
+ return config;
68
+ }
69
+ /** Probe env on first import so common deployments get auto-config. */
70
+ function autoFromEnv() {
71
+ const name = process.env.IM_HUB_LLM_JUDGE_AGENT;
72
+ if (name && !config) {
73
+ const threshold = parseFloat(process.env.IM_HUB_LLM_JUDGE_THRESHOLD || '1.0');
74
+ config = {
75
+ agentName: name,
76
+ threshold: Number.isFinite(threshold) && threshold > 0 ? threshold : 1.0,
77
+ };
78
+ }
79
+ }
80
+ autoFromEnv();
81
+ /** Hard cap on the prompt portion of the LRU key. Past this length the
82
+ * key collapses, which is fine for a memo cache (occasional re-call is
83
+ * cheap and doesn't affect correctness — the LLM judge runs again). It
84
+ * matters because the key was previously the full prompt concatenated
85
+ * with `|`-joined candidates, so a 100KB prompt + many candidates blew
86
+ * the LRU's per-entry memory cost. */
87
+ const MAX_KEY_TEXT_LEN = 256;
88
+ function makeKey(text, candidates) {
89
+ // Hash the candidate list so the key length is bounded regardless of
90
+ // how many ACP agents are registered. SHA-256/16-hex (64 bits) gives
91
+ // 1-in-2^64 collision probability across the LRU window — irrelevant.
92
+ const candHash = createHash('sha256')
93
+ .update(candidates.slice().sort().join('|'))
94
+ .digest('hex').slice(0, 16);
95
+ const textKey = text.length > MAX_KEY_TEXT_LEN ? text.slice(0, MAX_KEY_TEXT_LEN) : text;
96
+ return `${candHash}||${textKey}`;
97
+ }
98
+ function buildPrompt(text, candidates) {
99
+ return [
100
+ 'You are a router that picks the most appropriate agent for a user request.',
101
+ 'Reply with EXACTLY one agent name from the list — no explanation, no punctuation.',
102
+ '',
103
+ 'Candidates:',
104
+ ...candidates.map((c) => ` - ${c}`),
105
+ '',
106
+ `User request: ${text}`,
107
+ '',
108
+ 'Best agent:',
109
+ ].join('\n');
110
+ }
111
+ /**
112
+ * Ask the judge agent which candidate to use. Returns null on any
113
+ * failure path (timeout, judge unavailable, malformed response) so
114
+ * the caller can keep its rule-based decision.
115
+ */
116
+ export async function classifyWithLLM(text, candidates, resolveAgent) {
117
+ if (!config)
118
+ return null;
119
+ if (candidates.length === 0)
120
+ return null;
121
+ const key = makeKey(text, candidates);
122
+ const cached = cache.get(key);
123
+ if (cached) {
124
+ return { agent: cached.agent, reason: `${cached.reason} (cached)` };
125
+ }
126
+ const judge = resolveAgent(config.agentName);
127
+ if (!judge) {
128
+ log.warn({ judge: config.agentName }, 'LLM judge agent not registered');
129
+ return null;
130
+ }
131
+ const timeoutMs = config.timeoutMs ?? RESPONSE_TIMEOUT_MS;
132
+ const prompt = buildPrompt(text, candidates);
133
+ try {
134
+ let acc = '';
135
+ const generator = judge.sendPrompt(`llm-judge`, prompt, []);
136
+ const collect = (async () => {
137
+ for await (const chunk of generator) {
138
+ acc += chunk;
139
+ if (acc.length > 4096)
140
+ break; // guard runaway output
141
+ }
142
+ })();
143
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('LLM judge timed out')), timeoutMs));
144
+ await Promise.race([collect, timeout]);
145
+ // Parse: take the first line, lowercase, strip punctuation, match
146
+ // against candidates case-insensitively.
147
+ const firstLine = acc.split('\n').map((s) => s.trim()).filter(Boolean)[0] || '';
148
+ const cleaned = firstLine.toLowerCase().replace(/[^a-z0-9_-]/g, '');
149
+ const match = candidates.find((c) => c.toLowerCase() === cleaned)
150
+ ?? candidates.find((c) => cleaned.includes(c.toLowerCase()));
151
+ if (!match) {
152
+ log.warn({ raw: firstLine, candidates }, 'LLM judge produced unrecognized output');
153
+ return null;
154
+ }
155
+ const decision = { agent: match, reason: `LLM judge picked ${match}` };
156
+ cache.set(key, { ...decision, ts: Date.now() });
157
+ return decision;
158
+ }
159
+ catch (err) {
160
+ log.warn({ err: err instanceof Error ? err.message : String(err) }, 'LLM judge call failed');
161
+ return null;
162
+ }
163
+ }
164
+ /** Test-only: clear cache so memoization doesn't leak between cases. */
165
+ export function _clearLLMCache() {
166
+ cache.clear();
167
+ }
168
+ /** Test/diagnostic: current cache size. */
169
+ export function _cacheSize() { return cache.size; }
170
+ //# sourceMappingURL=intent-llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-llm.js","sourceRoot":"","sources":["../../src/core/intent-llm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Logger } from 'pino';
2
+ export interface IntentResult {
3
+ agent: string;
4
+ score: number;
5
+ reason: string;
6
+ triggeredBy: 'explicit' | 'sticky' | 'keyword' | 'topic' | 'fallback';
7
+ }
8
+ /**
9
+ * Classify a user message and return the best agent to handle it.
10
+ */
11
+ export declare function classifyIntent(text: string, stickyAgent?: string, logger?: Logger): IntentResult;
12
+ //# sourceMappingURL=intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAgFlC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAA;CACtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,GACd,YAAY,CA4Hd"}
@@ -0,0 +1,187 @@
1
+ // Intent classifier — rule-based agent routing
2
+ //
3
+ // Phase 2: keyword matching with sticky session bias.
4
+ // Phase 2b (future): small LLM for topic classification.
5
+ import { registry } from './registry.js';
6
+ import { circuitBreaker } from './circuit-breaker.js';
7
+ const PROFILES = {
8
+ 'opencode': {
9
+ keywords: ['代码', 'code', '编程', 'refactor', '重构', 'bug', '修复', 'fix', 'git', 'commit', 'deploy', 'build',
10
+ 'test', '测试', 'api', 'server', '后端', 'frontend', '前端', 'sql', '数据库', 'database',
11
+ 'python', 'typescript', 'javascript', 'rust', 'go', 'java',
12
+ 'shell', 'bash', '命令行', 'terminal', 'config', '配置', 'docker', 'container'],
13
+ // Was 1.2 — same keywords also match claude-code, so the 0.2 advantage
14
+ // would push routing to opencode whenever the message mentioned any coding
15
+ // term. With sticky now an absolute lock (see classifyIntent below) this
16
+ // weight only matters for the very first message on a fresh thread, but we
17
+ // keep weights neutral so ordering comes from topic rules + defaultAgent.
18
+ weight: 1.0,
19
+ },
20
+ 'claude-code': {
21
+ keywords: ['代码', 'code', '编程', 'refactor', '分析', 'analyze', 'architecture', '架构',
22
+ 'design', '设计', 'review', '审查', 'explain', '解释', '文档', 'documentation',
23
+ 'reasoning', '推理', 'complex', '复杂', 'research', '研究'],
24
+ weight: 1.0,
25
+ },
26
+ 'codex': {
27
+ keywords: ['代码', 'code', 'bug', 'fix', '修复', 'quick', '快速', 'small', '简单', 'simple'],
28
+ weight: 0.9,
29
+ },
30
+ 'copilot': {
31
+ keywords: ['代码', 'code', 'suggest', '建议', 'autocomplete', '补全', 'copilot'],
32
+ weight: 0.8,
33
+ },
34
+ };
35
+ // `\b` only matches between ASCII word chars and other chars — it does
36
+ // nothing for CJK. We split keywords into ASCII (uses \b) and CJK
37
+ // (boundary-free includes). Both branches are OR'd into one regex so
38
+ // callers still get a single .test() call.
39
+ function isCJK(s) {
40
+ for (const ch of s) {
41
+ const cp = ch.codePointAt(0) || 0;
42
+ if (cp >= 0x3000)
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function escapeReg(s) {
48
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
49
+ }
50
+ function topic(words, agent, description) {
51
+ const ascii = words.filter((w) => !isCJK(w)).map(escapeReg);
52
+ const cjk = words.filter((w) => isCJK(w)).map(escapeReg);
53
+ const parts = [];
54
+ if (ascii.length)
55
+ parts.push(`\\b(?:${ascii.join('|')})\\b`);
56
+ if (cjk.length)
57
+ parts.push(`(?:${cjk.join('|')})`);
58
+ return { pattern: new RegExp(parts.join('|'), 'iu'), agent, description };
59
+ }
60
+ const TOPIC_RULES = [
61
+ topic(['git', 'commit', 'push', 'pull', 'merge', 'rebase', 'branch', 'stash'], 'opencode', 'git operations'),
62
+ topic(['test', '测试', 'spec', 'coverage', 'mock', 'stub'], 'opencode', 'testing'),
63
+ topic(['docker', 'container', 'kubernetes', 'k8s', 'deploy', 'ci', 'cd'], 'opencode', 'devops'),
64
+ topic(['sql', 'database', 'db', '查询', 'select', 'insert', 'update', 'delete'], 'opencode', 'database'),
65
+ topic(['review', '审查', 'audit', '检查'], 'claude-code', 'code review'),
66
+ topic(['explain', '解释', 'what is', '为什么', 'how does', '怎么回事'], 'claude-code', 'explanation'),
67
+ ];
68
+ /**
69
+ * Classify a user message and return the best agent to handle it.
70
+ */
71
+ export function classifyIntent(text, stickyAgent, logger) {
72
+ const available = registry.listAgents().filter(a => !circuitBreaker.isOpen(a));
73
+ if (available.length === 0) {
74
+ throw new Error('No agents available');
75
+ }
76
+ // Sticky is an absolute lock: once a thread is bound to an agent, only an
77
+ // explicit /<agent> command (or /new) can change it. Without this, a single
78
+ // keyword in a long-running thread could outscore the sticky bonus and
79
+ // silently swap agents — the "agent drift" symptom users hit after long
80
+ // pauses or across days. See router.ts:default → classifyIntent caller.
81
+ if (stickyAgent && available.includes(stickyAgent)) {
82
+ logger?.debug({ event: 'intent.sticky_lock', agent: stickyAgent }, `[intent] sticky lock honored (no classification)`);
83
+ return {
84
+ agent: stickyAgent,
85
+ score: 0,
86
+ reason: 'sticky lock (no classification)',
87
+ triggeredBy: 'sticky',
88
+ };
89
+ }
90
+ // Score per agent. Agents missing from PROFILES (e.g. user-installed
91
+ // custom ACP agents) get a default neutral weight so they remain
92
+ // selectable when no explicit signal favors a profiled agent.
93
+ const DEFAULT_WEIGHT = 0.5;
94
+ const scores = new Map();
95
+ for (const a of available) {
96
+ const profile = PROFILES[a];
97
+ scores.set(a, {
98
+ score: 0,
99
+ reasons: profile ? [] : ['default weight (no profile)'],
100
+ weight: profile?.weight ?? DEFAULT_WEIGHT,
101
+ });
102
+ }
103
+ const lower = text.toLowerCase();
104
+ // 1. Topic rule matching
105
+ for (const rule of TOPIC_RULES) {
106
+ if (rule.pattern.test(text)) {
107
+ const s = scores.get(rule.agent);
108
+ if (s) {
109
+ s.score += 2;
110
+ s.reasons.push(`topic: ${rule.description}`);
111
+ }
112
+ }
113
+ }
114
+ // 2. Keyword matching (uses each agent's own weight; profile-less agents
115
+ // can't keyword-match because they have no keywords list, but they still
116
+ // earn their base weight below).
117
+ for (const [agent, profile] of Object.entries(PROFILES)) {
118
+ const s = scores.get(agent);
119
+ if (!s)
120
+ continue;
121
+ for (const kw of profile.keywords) {
122
+ if (lower.includes(kw.toLowerCase())) {
123
+ s.score += 1 * profile.weight;
124
+ s.reasons.push(`keyword: "${kw}"`);
125
+ }
126
+ }
127
+ }
128
+ // 3. Base weight per agent (so a profile-less custom agent has a non-zero
129
+ // floor and won't be permanently dominated).
130
+ for (const [, s] of scores) {
131
+ s.score += s.weight * 0.5;
132
+ }
133
+ // 4. Sticky session bias
134
+ if (stickyAgent && scores.has(stickyAgent)) {
135
+ const s = scores.get(stickyAgent);
136
+ s.score += 3;
137
+ s.reasons.push('sticky: last used agent');
138
+ }
139
+ // Tie-break order:
140
+ // 1. higher score wins
141
+ // 2. sticky agent (if present in scores)
142
+ // 3. PROFILES declaration order (preserves intentional priority)
143
+ // 4. alphabetical, for full determinism
144
+ const profileOrder = Object.keys(PROFILES);
145
+ const rankOf = (agent) => {
146
+ if (stickyAgent && agent === stickyAgent)
147
+ return -1;
148
+ const idx = profileOrder.indexOf(agent);
149
+ return idx === -1 ? profileOrder.length : idx;
150
+ };
151
+ let best = null;
152
+ for (const [agent, s] of scores) {
153
+ if (!best) {
154
+ best = { agent, score: s.score, reasons: s.reasons };
155
+ continue;
156
+ }
157
+ if (s.score > best.score) {
158
+ best = { agent, score: s.score, reasons: s.reasons };
159
+ continue;
160
+ }
161
+ if (s.score === best.score) {
162
+ const cmp = rankOf(agent) - rankOf(best.agent);
163
+ if (cmp < 0 || (cmp === 0 && agent < best.agent)) {
164
+ best = { agent, score: s.score, reasons: s.reasons };
165
+ }
166
+ }
167
+ }
168
+ if (!best) {
169
+ // fallback to first available
170
+ const agent = available[0];
171
+ return { agent, score: 0, reason: 'fallback (no matches)', triggeredBy: 'fallback' };
172
+ }
173
+ const isSticky = stickyAgent === best.agent && best.reasons.includes('sticky: last used agent');
174
+ const hasTopic = best.reasons.some(r => r.startsWith('topic:'));
175
+ const hasKeyword = best.reasons.some(r => r.startsWith('keyword:'));
176
+ const triggeredBy = hasTopic ? 'topic'
177
+ : hasKeyword ? 'keyword'
178
+ : isSticky ? 'sticky'
179
+ : 'fallback';
180
+ return {
181
+ agent: best.agent,
182
+ score: Math.round(best.score * 100) / 100,
183
+ reason: best.reasons.join(', ') || `base score ${best.score.toFixed(1)}`,
184
+ triggeredBy,
185
+ };
186
+ }
187
+ //# sourceMappingURL=intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../../src/core/intent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import type Database from 'better-sqlite3';
2
+ import type { Logger } from 'pino';
3
+ export type JobStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
4
+ export interface Job {
5
+ id: number;
6
+ status: JobStatus;
7
+ agent: string;
8
+ prompt: string;
9
+ result: string | null;
10
+ error: string | null;
11
+ created_at: string;
12
+ completed_at: string | null;
13
+ /** IM userId of the originating caller, or '' for legacy / system rows. */
14
+ creator_id: string;
15
+ /** Workspace this job was created under, or '' if unknown. */
16
+ workspace_id: string;
17
+ }
18
+ interface JobRunner {
19
+ (job: Job, logger: Logger, signal: AbortSignal): AsyncGenerator<string>;
20
+ }
21
+ /** Stop the retention sweep (used by tests + graceful shutdown). */
22
+ export declare function stopJobRetentionSweep(): void;
23
+ /** Close the underlying SQLite handle. Called by cli.ts on graceful
24
+ * shutdown so the WAL is checkpointed before exit (L2). */
25
+ export declare function closeJobBoardDb(): void;
26
+ /**
27
+ * Filter / scoping options accepted by every owner-sensitive job-board API.
28
+ *
29
+ * - When `creatorId` is set, the query is scoped to rows whose `creator_id`
30
+ * matches the caller OR whose `creator_id` is '' (legacy / ownerless).
31
+ * This preserves visibility of pre-migration jobs while preventing one
32
+ * user from poking at another's freshly-created ones.
33
+ * - When `creatorId` is unset (undefined), no ownership filter is applied
34
+ * — used by REST / ACP / scheduler entry-points that are guarded by the
35
+ * web token, not by per-user identity.
36
+ */
37
+ export interface OwnerOpts {
38
+ creatorId?: string;
39
+ /** Optional agent-name filter. When set, only rows whose `agent` column
40
+ * matches are returned. Used by the dashboard to give per-agent views
41
+ * (claude-code / opencode / codex tabs); IM commands generally don't
42
+ * pass it (they want the user's full job history). */
43
+ agent?: string;
44
+ }
45
+ export interface CreateJobOpts {
46
+ creatorId?: string;
47
+ workspaceId?: string;
48
+ }
49
+ export declare function createJob(agent: string, prompt: string, opts?: CreateJobOpts): number;
50
+ export declare function getJob(id: number, opts?: OwnerOpts): Job | null;
51
+ export declare function listJobs(limit?: number, status?: JobStatus, opts?: OwnerOpts): Job[];
52
+ /**
53
+ * Acquire a job slot. Returns immediately when capacity available, otherwise
54
+ * queues the caller and returns a promise that resolves when a slot frees.
55
+ */
56
+ declare function acquireSlot(): Promise<void>;
57
+ declare function releaseSlot(): void;
58
+ export declare function runJob(id: number, runner: JobRunner, logger: Logger): Promise<void>;
59
+ /** Test/diagnostic accessor: how many runJob invocations are currently in flight. */
60
+ export declare function _activeJobCount(): number;
61
+ /** Test/diagnostic accessor: the wait queue depth. */
62
+ export declare function _waitQueueDepth(): number;
63
+ /** Test-only: directly exercise the concurrency gate without SQLite. */
64
+ export declare const _testSlot: {
65
+ acquire: typeof acquireSlot;
66
+ release: typeof releaseSlot;
67
+ };
68
+ export declare function cancelJob(id: number, opts?: OwnerOpts): boolean;
69
+ /**
70
+ * Delete jobs that finished (completed / failed / cancelled) more than
71
+ * `daysRetention` days ago. 'pending' / 'running' rows are never pruned —
72
+ * a stalled-but-resumable job is the operator's call. Returns the row
73
+ * count so callers / tests can assert.
74
+ */
75
+ export declare function pruneOldJobs(d?: Database.Database, daysRetention?: number): number;
76
+ export declare function getJobStats(): {
77
+ total: number;
78
+ pending: number;
79
+ running: number;
80
+ completed: number;
81
+ failed: number;
82
+ };
83
+ export {};
84
+ //# sourceMappingURL=job-board.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-board.d.ts","sourceRoot":"","sources":["../../src/core/job-board.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAG1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA8DlC,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEpF,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,SAAS,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;CACxE;AA4ED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,IAAI,CAK5C;AAED;4DAC4D;AAC5D,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAMD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;2DAGuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CASzF;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,GAAG,GAAG,IAAI,CAQnE;AAED,wBAAgB,QAAQ,CAAC,KAAK,SAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,GAAE,SAAc,GAAG,GAAG,EAAE,CAepF;AAcD;;;GAGG;AACH,iBAAe,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ1C;AAED,iBAAS,WAAW,IAAI,IAAI,CAI3B;AAED,wBAAsB,MAAM,CAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED,qFAAqF;AACrF,wBAAgB,eAAe,IAAI,MAAM,CAAsB;AAC/D,sDAAsD;AACtD,wBAAgB,eAAe,IAAI,MAAM,CAA4B;AACrE,wEAAwE;AACxE,eAAO,MAAM,SAAS;;;CAAiD,CAAA;AAEvE,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAWnE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAE,QAAQ,CAAC,QAAmB,EAAE,aAAa,GAAE,MAA+B,GAAG,MAAM,CAsBpH;AAED,wBAAgB,WAAW,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAqBpH"}