@aitne/daemon 0.1.7 → 0.1.8

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 (959) hide show
  1. package/dist/adapters/dashboard-adapter.d.ts +18 -2
  2. package/dist/adapters/dashboard-adapter.d.ts.map +1 -1
  3. package/dist/adapters/dashboard-adapter.js +101 -51
  4. package/dist/adapters/dashboard-adapter.js.map +1 -1
  5. package/dist/adapters/discord.d.ts +8 -0
  6. package/dist/adapters/discord.d.ts.map +1 -1
  7. package/dist/adapters/discord.js +100 -21
  8. package/dist/adapters/discord.js.map +1 -1
  9. package/dist/adapters/message-hub.d.ts.map +1 -1
  10. package/dist/adapters/message-hub.js +7 -1
  11. package/dist/adapters/message-hub.js.map +1 -1
  12. package/dist/adapters/notification-manager.d.ts +102 -2
  13. package/dist/adapters/notification-manager.d.ts.map +1 -1
  14. package/dist/adapters/notification-manager.js +228 -8
  15. package/dist/adapters/notification-manager.js.map +1 -1
  16. package/dist/adapters/outbound-text.d.ts +16 -0
  17. package/dist/adapters/outbound-text.d.ts.map +1 -1
  18. package/dist/adapters/outbound-text.js +118 -1
  19. package/dist/adapters/outbound-text.js.map +1 -1
  20. package/dist/adapters/primary-platform-resolver.d.ts +69 -0
  21. package/dist/adapters/primary-platform-resolver.d.ts.map +1 -0
  22. package/dist/adapters/primary-platform-resolver.js +55 -0
  23. package/dist/adapters/primary-platform-resolver.js.map +1 -0
  24. package/dist/adapters/slack-adapter.d.ts +28 -0
  25. package/dist/adapters/slack-adapter.d.ts.map +1 -1
  26. package/dist/adapters/slack-adapter.js +134 -35
  27. package/dist/adapters/slack-adapter.js.map +1 -1
  28. package/dist/adapters/telegram-adapter.d.ts +7 -1
  29. package/dist/adapters/telegram-adapter.d.ts.map +1 -1
  30. package/dist/adapters/telegram-adapter.js +49 -18
  31. package/dist/adapters/telegram-adapter.js.map +1 -1
  32. package/dist/adapters/whatsapp-adapter.d.ts +33 -0
  33. package/dist/adapters/whatsapp-adapter.d.ts.map +1 -1
  34. package/dist/adapters/whatsapp-adapter.js +84 -0
  35. package/dist/adapters/whatsapp-adapter.js.map +1 -1
  36. package/dist/api/directory-picker.d.ts.map +1 -1
  37. package/dist/api/directory-picker.js +14 -0
  38. package/dist/api/directory-picker.js.map +1 -1
  39. package/dist/api/env-writer.d.ts.map +1 -1
  40. package/dist/api/env-writer.js +6 -1
  41. package/dist/api/env-writer.js.map +1 -1
  42. package/dist/api/helpers/agent-errors.d.ts +2600 -0
  43. package/dist/api/helpers/agent-errors.d.ts.map +1 -0
  44. package/dist/api/helpers/agent-errors.js +2506 -0
  45. package/dist/api/helpers/agent-errors.js.map +1 -0
  46. package/dist/api/integration-route-gate.d.ts.map +1 -1
  47. package/dist/api/integration-route-gate.js +10 -3
  48. package/dist/api/integration-route-gate.js.map +1 -1
  49. package/dist/api/routes/agent-schedule-plan-match.d.ts +5 -0
  50. package/dist/api/routes/agent-schedule-plan-match.d.ts.map +1 -0
  51. package/dist/api/routes/agent-schedule-plan-match.js +101 -0
  52. package/dist/api/routes/agent-schedule-plan-match.js.map +1 -0
  53. package/dist/api/routes/agent-schedule.d.ts +4 -0
  54. package/dist/api/routes/agent-schedule.d.ts.map +1 -0
  55. package/dist/api/routes/agent-schedule.js +750 -0
  56. package/dist/api/routes/agent-schedule.js.map +1 -0
  57. package/dist/api/routes/agent.d.ts.map +1 -1
  58. package/dist/api/routes/agent.js +209 -366
  59. package/dist/api/routes/agent.js.map +1 -1
  60. package/dist/api/routes/apple-calendar.d.ts.map +1 -1
  61. package/dist/api/routes/apple-calendar.js +109 -27
  62. package/dist/api/routes/apple-calendar.js.map +1 -1
  63. package/dist/api/routes/attachments.d.ts.map +1 -1
  64. package/dist/api/routes/attachments.js +113 -21
  65. package/dist/api/routes/attachments.js.map +1 -1
  66. package/dist/api/routes/backends.d.ts.map +1 -1
  67. package/dist/api/routes/backends.js +100 -4
  68. package/dist/api/routes/backends.js.map +1 -1
  69. package/dist/api/routes/books.d.ts.map +1 -1
  70. package/dist/api/routes/books.js +58 -18
  71. package/dist/api/routes/books.js.map +1 -1
  72. package/dist/api/routes/calendar.d.ts +3 -2
  73. package/dist/api/routes/calendar.d.ts.map +1 -1
  74. package/dist/api/routes/calendar.js +330 -55
  75. package/dist/api/routes/calendar.js.map +1 -1
  76. package/dist/api/routes/commands.d.ts.map +1 -1
  77. package/dist/api/routes/commands.js +20 -1
  78. package/dist/api/routes/commands.js.map +1 -1
  79. package/dist/api/routes/context/index.d.ts +33 -0
  80. package/dist/api/routes/context/index.d.ts.map +1 -0
  81. package/dist/api/routes/context/index.js +193 -0
  82. package/dist/api/routes/context/index.js.map +1 -0
  83. package/dist/api/routes/context/locks.d.ts +4 -0
  84. package/dist/api/routes/context/locks.d.ts.map +1 -0
  85. package/dist/api/routes/context/locks.js +136 -0
  86. package/dist/api/routes/context/locks.js.map +1 -0
  87. package/dist/api/routes/context/path-resolve.d.ts +15 -0
  88. package/dist/api/routes/context/path-resolve.d.ts.map +1 -0
  89. package/dist/api/routes/context/path-resolve.js +109 -0
  90. package/dist/api/routes/context/path-resolve.js.map +1 -0
  91. package/dist/api/routes/context/permissions.d.ts +35 -0
  92. package/dist/api/routes/context/permissions.d.ts.map +1 -0
  93. package/dist/api/routes/context/permissions.js +192 -0
  94. package/dist/api/routes/context/permissions.js.map +1 -0
  95. package/dist/api/routes/context/read.d.ts +4 -0
  96. package/dist/api/routes/context/read.d.ts.map +1 -0
  97. package/dist/api/routes/context/read.js +295 -0
  98. package/dist/api/routes/context/read.js.map +1 -0
  99. package/dist/api/routes/context/repair.d.ts +4 -0
  100. package/dist/api/routes/context/repair.d.ts.map +1 -0
  101. package/dist/api/routes/context/repair.js +114 -0
  102. package/dist/api/routes/context/repair.js.map +1 -0
  103. package/dist/api/routes/context/snapshots.d.ts +4 -0
  104. package/dist/api/routes/context/snapshots.d.ts.map +1 -0
  105. package/dist/api/routes/context/snapshots.js +177 -0
  106. package/dist/api/routes/context/snapshots.js.map +1 -0
  107. package/dist/api/routes/context/write.d.ts +4 -0
  108. package/dist/api/routes/context/write.d.ts.map +1 -0
  109. package/dist/api/routes/context/write.js +570 -0
  110. package/dist/api/routes/context/write.js.map +1 -0
  111. package/dist/api/routes/context.d.ts +2 -43
  112. package/dist/api/routes/context.d.ts.map +1 -1
  113. package/dist/api/routes/context.js +415 -558
  114. package/dist/api/routes/context.js.map +1 -1
  115. package/dist/api/routes/dashboard/config.d.ts +4 -0
  116. package/dist/api/routes/dashboard/config.d.ts.map +1 -0
  117. package/dist/api/routes/dashboard/config.js +499 -0
  118. package/dist/api/routes/dashboard/config.js.map +1 -0
  119. package/dist/api/routes/dashboard/conversations.d.ts +4 -0
  120. package/dist/api/routes/dashboard/conversations.d.ts.map +1 -0
  121. package/dist/api/routes/dashboard/conversations.js +309 -0
  122. package/dist/api/routes/dashboard/conversations.js.map +1 -0
  123. package/dist/api/routes/dashboard/cost-approvals.d.ts +29 -0
  124. package/dist/api/routes/dashboard/cost-approvals.d.ts.map +1 -0
  125. package/dist/api/routes/dashboard/cost-approvals.js +259 -0
  126. package/dist/api/routes/dashboard/cost-approvals.js.map +1 -0
  127. package/dist/api/routes/dashboard/index.d.ts +27 -0
  128. package/dist/api/routes/dashboard/index.d.ts.map +1 -0
  129. package/dist/api/routes/dashboard/index.js +47 -0
  130. package/dist/api/routes/dashboard/index.js.map +1 -0
  131. package/dist/api/routes/dashboard/messaging.d.ts +4 -0
  132. package/dist/api/routes/dashboard/messaging.d.ts.map +1 -0
  133. package/dist/api/routes/dashboard/messaging.js +351 -0
  134. package/dist/api/routes/dashboard/messaging.js.map +1 -0
  135. package/dist/api/routes/dashboard/notifications.d.ts +4 -0
  136. package/dist/api/routes/dashboard/notifications.d.ts.map +1 -0
  137. package/dist/api/routes/dashboard/notifications.js +109 -0
  138. package/dist/api/routes/dashboard/notifications.js.map +1 -0
  139. package/dist/api/routes/dashboard/oauth-google.d.ts +4 -0
  140. package/dist/api/routes/dashboard/oauth-google.d.ts.map +1 -0
  141. package/dist/api/routes/dashboard/oauth-google.js +293 -0
  142. package/dist/api/routes/dashboard/oauth-google.js.map +1 -0
  143. package/dist/api/routes/dashboard/schedule-readonly.d.ts +4 -0
  144. package/dist/api/routes/dashboard/schedule-readonly.d.ts.map +1 -0
  145. package/dist/api/routes/dashboard/schedule-readonly.js +46 -0
  146. package/dist/api/routes/dashboard/schedule-readonly.js.map +1 -0
  147. package/dist/api/routes/dashboard/secrets.d.ts +24 -0
  148. package/dist/api/routes/dashboard/secrets.d.ts.map +1 -0
  149. package/dist/api/routes/dashboard/secrets.js +307 -0
  150. package/dist/api/routes/dashboard/secrets.js.map +1 -0
  151. package/dist/api/routes/dashboard/snapshots.d.ts +4 -0
  152. package/dist/api/routes/dashboard/snapshots.d.ts.map +1 -0
  153. package/dist/api/routes/dashboard/snapshots.js +33 -0
  154. package/dist/api/routes/dashboard/snapshots.js.map +1 -0
  155. package/dist/api/routes/dashboard.d.ts.map +1 -1
  156. package/dist/api/routes/dashboard.js +20 -12
  157. package/dist/api/routes/dashboard.js.map +1 -1
  158. package/dist/api/routes/delegated.d.ts +5 -4
  159. package/dist/api/routes/delegated.d.ts.map +1 -1
  160. package/dist/api/routes/delegated.js +6 -5
  161. package/dist/api/routes/delegated.js.map +1 -1
  162. package/dist/api/routes/docs.d.ts.map +1 -1
  163. package/dist/api/routes/docs.js +72 -9
  164. package/dist/api/routes/docs.js.map +1 -1
  165. package/dist/api/routes/entities.d.ts.map +1 -1
  166. package/dist/api/routes/entities.js +112 -43
  167. package/dist/api/routes/entities.js.map +1 -1
  168. package/dist/api/routes/fs.d.ts.map +1 -1
  169. package/dist/api/routes/fs.js +27 -12
  170. package/dist/api/routes/fs.js.map +1 -1
  171. package/dist/api/routes/git-templates.d.ts.map +1 -1
  172. package/dist/api/routes/git-templates.js +107 -27
  173. package/dist/api/routes/git-templates.js.map +1 -1
  174. package/dist/api/routes/git.d.ts.map +1 -1
  175. package/dist/api/routes/git.js +55 -11
  176. package/dist/api/routes/git.js.map +1 -1
  177. package/dist/api/routes/github.d.ts.map +1 -1
  178. package/dist/api/routes/github.js +110 -17
  179. package/dist/api/routes/github.js.map +1 -1
  180. package/dist/api/routes/integrations/crud-patch.d.ts +17 -0
  181. package/dist/api/routes/integrations/crud-patch.d.ts.map +1 -0
  182. package/dist/api/routes/integrations/crud-patch.js +600 -0
  183. package/dist/api/routes/integrations/crud-patch.js.map +1 -0
  184. package/dist/api/routes/integrations/crud.d.ts +16 -0
  185. package/dist/api/routes/integrations/crud.d.ts.map +1 -0
  186. package/dist/api/routes/integrations/crud.js +158 -0
  187. package/dist/api/routes/integrations/crud.js.map +1 -0
  188. package/dist/api/routes/integrations/exec.d.ts +23 -0
  189. package/dist/api/routes/integrations/exec.d.ts.map +1 -0
  190. package/dist/api/routes/integrations/exec.js +356 -0
  191. package/dist/api/routes/integrations/exec.js.map +1 -0
  192. package/dist/api/routes/integrations/index.d.ts +62 -0
  193. package/dist/api/routes/integrations/index.d.ts.map +1 -0
  194. package/dist/api/routes/integrations/index.js +70 -0
  195. package/dist/api/routes/integrations/index.js.map +1 -0
  196. package/dist/api/routes/integrations/integrations/crud.d.ts +16 -0
  197. package/dist/api/routes/integrations/integrations/crud.d.ts.map +1 -0
  198. package/dist/api/routes/integrations/integrations/crud.js +158 -0
  199. package/dist/api/routes/integrations/integrations/crud.js.map +1 -0
  200. package/dist/api/routes/integrations/integrations/index.d.ts +55 -0
  201. package/dist/api/routes/integrations/integrations/index.d.ts.map +1 -0
  202. package/dist/api/routes/integrations/integrations/index.js +65 -0
  203. package/dist/api/routes/integrations/integrations/index.js.map +1 -0
  204. package/dist/api/routes/integrations/integrations/invoke.d.ts +38 -0
  205. package/dist/api/routes/integrations/integrations/invoke.d.ts.map +1 -0
  206. package/dist/api/routes/integrations/integrations/invoke.js +320 -0
  207. package/dist/api/routes/integrations/integrations/invoke.js.map +1 -0
  208. package/dist/api/routes/integrations/integrations/probe.d.ts +21 -0
  209. package/dist/api/routes/integrations/integrations/probe.d.ts.map +1 -0
  210. package/dist/api/routes/integrations/integrations/probe.js +247 -0
  211. package/dist/api/routes/integrations/integrations/probe.js.map +1 -0
  212. package/dist/api/routes/integrations/invoke.d.ts +38 -0
  213. package/dist/api/routes/integrations/invoke.d.ts.map +1 -0
  214. package/dist/api/routes/integrations/invoke.js +320 -0
  215. package/dist/api/routes/integrations/invoke.js.map +1 -0
  216. package/dist/api/routes/integrations/probe.d.ts +21 -0
  217. package/dist/api/routes/integrations/probe.d.ts.map +1 -0
  218. package/dist/api/routes/integrations/probe.js +247 -0
  219. package/dist/api/routes/integrations/probe.js.map +1 -0
  220. package/dist/api/routes/integrations.d.ts.map +1 -1
  221. package/dist/api/routes/integrations.js +65 -13
  222. package/dist/api/routes/integrations.js.map +1 -1
  223. package/dist/api/routes/knowledge.d.ts.map +1 -1
  224. package/dist/api/routes/knowledge.js +13 -2
  225. package/dist/api/routes/knowledge.js.map +1 -1
  226. package/dist/api/routes/mail/_pa_wip_mail/app-password.d.ts +4 -0
  227. package/dist/api/routes/mail/_pa_wip_mail/app-password.d.ts.map +1 -0
  228. package/dist/api/routes/mail/_pa_wip_mail/app-password.js +192 -0
  229. package/dist/api/routes/mail/_pa_wip_mail/app-password.js.map +1 -0
  230. package/dist/api/routes/mail/_pa_wip_mail/body-helpers.d.ts +55 -0
  231. package/dist/api/routes/mail/_pa_wip_mail/body-helpers.d.ts.map +1 -0
  232. package/dist/api/routes/mail/_pa_wip_mail/body-helpers.js +91 -0
  233. package/dist/api/routes/mail/_pa_wip_mail/body-helpers.js.map +1 -0
  234. package/dist/api/routes/mail/_pa_wip_mail/dependencies.d.ts +36 -0
  235. package/dist/api/routes/mail/_pa_wip_mail/dependencies.d.ts.map +1 -0
  236. package/dist/api/routes/mail/_pa_wip_mail/dependencies.js +2 -0
  237. package/dist/api/routes/mail/_pa_wip_mail/dependencies.js.map +1 -0
  238. package/dist/api/routes/mail/_pa_wip_mail/gating.d.ts +45 -0
  239. package/dist/api/routes/mail/_pa_wip_mail/gating.d.ts.map +1 -0
  240. package/dist/api/routes/mail/_pa_wip_mail/gating.js +98 -0
  241. package/dist/api/routes/mail/_pa_wip_mail/gating.js.map +1 -0
  242. package/dist/api/routes/mail/_pa_wip_mail/mail/accounts.d.ts +4 -0
  243. package/dist/api/routes/mail/_pa_wip_mail/mail/accounts.d.ts.map +1 -0
  244. package/dist/api/routes/mail/_pa_wip_mail/mail/accounts.js +289 -0
  245. package/dist/api/routes/mail/_pa_wip_mail/mail/accounts.js.map +1 -0
  246. package/dist/api/routes/mail/_pa_wip_mail/mail/app-password.d.ts +4 -0
  247. package/dist/api/routes/mail/_pa_wip_mail/mail/app-password.d.ts.map +1 -0
  248. package/dist/api/routes/mail/_pa_wip_mail/mail/app-password.js +192 -0
  249. package/dist/api/routes/mail/_pa_wip_mail/mail/app-password.js.map +1 -0
  250. package/dist/api/routes/mail/_pa_wip_mail/mail/body-helpers.d.ts +55 -0
  251. package/dist/api/routes/mail/_pa_wip_mail/mail/body-helpers.d.ts.map +1 -0
  252. package/dist/api/routes/mail/_pa_wip_mail/mail/body-helpers.js +91 -0
  253. package/dist/api/routes/mail/_pa_wip_mail/mail/body-helpers.js.map +1 -0
  254. package/dist/api/routes/mail/_pa_wip_mail/mail/dependencies.d.ts +36 -0
  255. package/dist/api/routes/mail/_pa_wip_mail/mail/dependencies.d.ts.map +1 -0
  256. package/dist/api/routes/mail/_pa_wip_mail/mail/dependencies.js +2 -0
  257. package/dist/api/routes/mail/_pa_wip_mail/mail/dependencies.js.map +1 -0
  258. package/dist/api/routes/mail/_pa_wip_mail/mail/drafts.d.ts +5 -0
  259. package/dist/api/routes/mail/_pa_wip_mail/mail/drafts.d.ts.map +1 -0
  260. package/dist/api/routes/mail/_pa_wip_mail/mail/drafts.js +139 -0
  261. package/dist/api/routes/mail/_pa_wip_mail/mail/drafts.js.map +1 -0
  262. package/dist/api/routes/mail/_pa_wip_mail/mail/gating.d.ts +45 -0
  263. package/dist/api/routes/mail/_pa_wip_mail/mail/gating.d.ts.map +1 -0
  264. package/dist/api/routes/mail/_pa_wip_mail/mail/gating.js +98 -0
  265. package/dist/api/routes/mail/_pa_wip_mail/mail/gating.js.map +1 -0
  266. package/dist/api/routes/mail/_pa_wip_mail/mail/index.d.ts +18 -0
  267. package/dist/api/routes/mail/_pa_wip_mail/mail/index.d.ts.map +1 -0
  268. package/dist/api/routes/mail/_pa_wip_mail/mail/index.js +40 -0
  269. package/dist/api/routes/mail/_pa_wip_mail/mail/index.js.map +1 -0
  270. package/dist/api/routes/mail/_pa_wip_mail/mail/messages.d.ts +15 -0
  271. package/dist/api/routes/mail/_pa_wip_mail/mail/messages.d.ts.map +1 -0
  272. package/dist/api/routes/mail/_pa_wip_mail/mail/messages.js +239 -0
  273. package/dist/api/routes/mail/_pa_wip_mail/mail/messages.js.map +1 -0
  274. package/dist/api/routes/mail/_pa_wip_mail/mail/outlook-config.d.ts +4 -0
  275. package/dist/api/routes/mail/_pa_wip_mail/mail/outlook-config.d.ts.map +1 -0
  276. package/dist/api/routes/mail/_pa_wip_mail/mail/outlook-config.js +73 -0
  277. package/dist/api/routes/mail/_pa_wip_mail/mail/outlook-config.js.map +1 -0
  278. package/dist/api/routes/mail/_pa_wip_mail/mail/provider-resolver.d.ts +64 -0
  279. package/dist/api/routes/mail/_pa_wip_mail/mail/provider-resolver.d.ts.map +1 -0
  280. package/dist/api/routes/mail/_pa_wip_mail/mail/provider-resolver.js +286 -0
  281. package/dist/api/routes/mail/_pa_wip_mail/mail/provider-resolver.js.map +1 -0
  282. package/dist/api/routes/mail/_pa_wip_mail/mail/providers.d.ts +4 -0
  283. package/dist/api/routes/mail/_pa_wip_mail/mail/providers.d.ts.map +1 -0
  284. package/dist/api/routes/mail/_pa_wip_mail/mail/providers.js +73 -0
  285. package/dist/api/routes/mail/_pa_wip_mail/mail/providers.js.map +1 -0
  286. package/dist/api/routes/mail/_pa_wip_mail/mail/tags-folders.d.ts +5 -0
  287. package/dist/api/routes/mail/_pa_wip_mail/mail/tags-folders.d.ts.map +1 -0
  288. package/dist/api/routes/mail/_pa_wip_mail/mail/tags-folders.js +35 -0
  289. package/dist/api/routes/mail/_pa_wip_mail/mail/tags-folders.js.map +1 -0
  290. package/dist/api/routes/mail/_pa_wip_mail/mail/validators.d.ts +23 -0
  291. package/dist/api/routes/mail/_pa_wip_mail/mail/validators.d.ts.map +1 -0
  292. package/dist/api/routes/mail/_pa_wip_mail/mail/validators.js +131 -0
  293. package/dist/api/routes/mail/_pa_wip_mail/mail/validators.js.map +1 -0
  294. package/dist/api/routes/mail/_pa_wip_mail/provider-resolver.d.ts +64 -0
  295. package/dist/api/routes/mail/_pa_wip_mail/provider-resolver.d.ts.map +1 -0
  296. package/dist/api/routes/mail/_pa_wip_mail/provider-resolver.js +286 -0
  297. package/dist/api/routes/mail/_pa_wip_mail/provider-resolver.js.map +1 -0
  298. package/dist/api/routes/mail/_pa_wip_mail/search-health.d.ts +4 -0
  299. package/dist/api/routes/mail/_pa_wip_mail/search-health.d.ts.map +1 -0
  300. package/dist/api/routes/mail/_pa_wip_mail/search-health.js +131 -0
  301. package/dist/api/routes/mail/_pa_wip_mail/search-health.js.map +1 -0
  302. package/dist/api/routes/mail/_pa_wip_mail/validators.d.ts +23 -0
  303. package/dist/api/routes/mail/_pa_wip_mail/validators.d.ts.map +1 -0
  304. package/dist/api/routes/mail/_pa_wip_mail/validators.js +131 -0
  305. package/dist/api/routes/mail/_pa_wip_mail/validators.js.map +1 -0
  306. package/dist/api/routes/mail/accounts.d.ts +4 -0
  307. package/dist/api/routes/mail/accounts.d.ts.map +1 -0
  308. package/dist/api/routes/mail/accounts.js +289 -0
  309. package/dist/api/routes/mail/accounts.js.map +1 -0
  310. package/dist/api/routes/mail/app-password.d.ts +4 -0
  311. package/dist/api/routes/mail/app-password.d.ts.map +1 -0
  312. package/dist/api/routes/mail/app-password.js +192 -0
  313. package/dist/api/routes/mail/app-password.js.map +1 -0
  314. package/dist/api/routes/mail/body-helpers.d.ts +56 -0
  315. package/dist/api/routes/mail/body-helpers.d.ts.map +1 -0
  316. package/dist/api/routes/mail/body-helpers.js +91 -0
  317. package/dist/api/routes/mail/body-helpers.js.map +1 -0
  318. package/dist/api/routes/mail/dependencies.d.ts +36 -0
  319. package/dist/api/routes/mail/dependencies.d.ts.map +1 -0
  320. package/dist/api/routes/mail/dependencies.js +2 -0
  321. package/dist/api/routes/mail/dependencies.js.map +1 -0
  322. package/dist/api/routes/mail/drafts.d.ts +5 -0
  323. package/dist/api/routes/mail/drafts.d.ts.map +1 -0
  324. package/dist/api/routes/mail/drafts.js +139 -0
  325. package/dist/api/routes/mail/drafts.js.map +1 -0
  326. package/dist/api/routes/mail/gating.d.ts +45 -0
  327. package/dist/api/routes/mail/gating.d.ts.map +1 -0
  328. package/dist/api/routes/mail/gating.js +99 -0
  329. package/dist/api/routes/mail/gating.js.map +1 -0
  330. package/dist/api/routes/mail/index.d.ts +18 -0
  331. package/dist/api/routes/mail/index.d.ts.map +1 -0
  332. package/dist/api/routes/mail/index.js +40 -0
  333. package/dist/api/routes/mail/index.js.map +1 -0
  334. package/dist/api/routes/mail/messages.d.ts +15 -0
  335. package/dist/api/routes/mail/messages.d.ts.map +1 -0
  336. package/dist/api/routes/mail/messages.js +239 -0
  337. package/dist/api/routes/mail/messages.js.map +1 -0
  338. package/dist/api/routes/mail/outlook-config.d.ts +4 -0
  339. package/dist/api/routes/mail/outlook-config.d.ts.map +1 -0
  340. package/dist/api/routes/mail/outlook-config.js +73 -0
  341. package/dist/api/routes/mail/outlook-config.js.map +1 -0
  342. package/dist/api/routes/mail/provider-resolver.d.ts +64 -0
  343. package/dist/api/routes/mail/provider-resolver.d.ts.map +1 -0
  344. package/dist/api/routes/mail/provider-resolver.js +286 -0
  345. package/dist/api/routes/mail/provider-resolver.js.map +1 -0
  346. package/dist/api/routes/mail/providers.d.ts +4 -0
  347. package/dist/api/routes/mail/providers.d.ts.map +1 -0
  348. package/dist/api/routes/mail/providers.js +73 -0
  349. package/dist/api/routes/mail/providers.js.map +1 -0
  350. package/dist/api/routes/mail/search-health.d.ts +4 -0
  351. package/dist/api/routes/mail/search-health.d.ts.map +1 -0
  352. package/dist/api/routes/mail/search-health.js +131 -0
  353. package/dist/api/routes/mail/search-health.js.map +1 -0
  354. package/dist/api/routes/mail/tags-folders.d.ts +5 -0
  355. package/dist/api/routes/mail/tags-folders.d.ts.map +1 -0
  356. package/dist/api/routes/mail/tags-folders.js +35 -0
  357. package/dist/api/routes/mail/tags-folders.js.map +1 -0
  358. package/dist/api/routes/mail/validators.d.ts +23 -0
  359. package/dist/api/routes/mail/validators.d.ts.map +1 -0
  360. package/dist/api/routes/mail/validators.js +131 -0
  361. package/dist/api/routes/mail/validators.js.map +1 -0
  362. package/dist/api/routes/mail.d.ts.map +1 -1
  363. package/dist/api/routes/mail.js +259 -67
  364. package/dist/api/routes/mail.js.map +1 -1
  365. package/dist/api/routes/managed-tasks.d.ts.map +1 -1
  366. package/dist/api/routes/managed-tasks.js +142 -54
  367. package/dist/api/routes/managed-tasks.js.map +1 -1
  368. package/dist/api/routes/mcp.d.ts.map +1 -1
  369. package/dist/api/routes/mcp.js +115 -47
  370. package/dist/api/routes/mcp.js.map +1 -1
  371. package/dist/api/routes/metrics.d.ts +1 -1
  372. package/dist/api/routes/metrics.js +2 -2
  373. package/dist/api/routes/notion.d.ts.map +1 -1
  374. package/dist/api/routes/notion.js +230 -54
  375. package/dist/api/routes/notion.js.map +1 -1
  376. package/dist/api/routes/observations.d.ts.map +1 -1
  377. package/dist/api/routes/observations.js +40 -169
  378. package/dist/api/routes/observations.js.map +1 -1
  379. package/dist/api/routes/obsidian.d.ts.map +1 -1
  380. package/dist/api/routes/obsidian.js +193 -32
  381. package/dist/api/routes/obsidian.js.map +1 -1
  382. package/dist/api/routes/profile-questions.d.ts.map +1 -1
  383. package/dist/api/routes/profile-questions.js +19 -3
  384. package/dist/api/routes/profile-questions.js.map +1 -1
  385. package/dist/api/routes/receipts.d.ts.map +1 -1
  386. package/dist/api/routes/receipts.js +64 -24
  387. package/dist/api/routes/receipts.js.map +1 -1
  388. package/dist/api/routes/recurring-schedules.d.ts.map +1 -1
  389. package/dist/api/routes/recurring-schedules.js +243 -13
  390. package/dist/api/routes/recurring-schedules.js.map +1 -1
  391. package/dist/api/routes/repositories.d.ts.map +1 -1
  392. package/dist/api/routes/repositories.js +278 -104
  393. package/dist/api/routes/repositories.js.map +1 -1
  394. package/dist/api/routes/schedule-model-resolver.d.ts +153 -0
  395. package/dist/api/routes/schedule-model-resolver.d.ts.map +1 -0
  396. package/dist/api/routes/schedule-model-resolver.js +282 -0
  397. package/dist/api/routes/schedule-model-resolver.js.map +1 -0
  398. package/dist/api/routes/schedule-options.d.ts +25 -0
  399. package/dist/api/routes/schedule-options.d.ts.map +1 -0
  400. package/dist/api/routes/schedule-options.js +77 -0
  401. package/dist/api/routes/schedule-options.js.map +1 -0
  402. package/dist/api/routes/schedule-validation.d.ts +146 -0
  403. package/dist/api/routes/schedule-validation.d.ts.map +1 -0
  404. package/dist/api/routes/schedule-validation.js +153 -0
  405. package/dist/api/routes/schedule-validation.js.map +1 -0
  406. package/dist/api/routes/setup.d.ts.map +1 -1
  407. package/dist/api/routes/setup.js +100 -26
  408. package/dist/api/routes/setup.js.map +1 -1
  409. package/dist/api/routes/skills.d.ts.map +1 -1
  410. package/dist/api/routes/skills.js +81 -30
  411. package/dist/api/routes/skills.js.map +1 -1
  412. package/dist/api/routes/sot-bindings.d.ts.map +1 -1
  413. package/dist/api/routes/sot-bindings.js +46 -11
  414. package/dist/api/routes/sot-bindings.js.map +1 -1
  415. package/dist/api/routes/sse.d.ts.map +1 -1
  416. package/dist/api/routes/sse.js +6 -1
  417. package/dist/api/routes/sse.js.map +1 -1
  418. package/dist/api/routes/system.d.ts.map +1 -1
  419. package/dist/api/routes/system.js +15 -2
  420. package/dist/api/routes/system.js.map +1 -1
  421. package/dist/api/routes/travel-bookings.d.ts.map +1 -1
  422. package/dist/api/routes/travel-bookings.js +26 -9
  423. package/dist/api/routes/travel-bookings.js.map +1 -1
  424. package/dist/api/routes/travel-time.d.ts.map +1 -1
  425. package/dist/api/routes/travel-time.js +50 -10
  426. package/dist/api/routes/travel-time.js.map +1 -1
  427. package/dist/api/routes/wiki.d.ts.map +1 -1
  428. package/dist/api/routes/wiki.js +49 -8
  429. package/dist/api/routes/wiki.js.map +1 -1
  430. package/dist/api/server.d.ts +15 -3
  431. package/dist/api/server.d.ts.map +1 -1
  432. package/dist/api/server.js +32 -10
  433. package/dist/api/server.js.map +1 -1
  434. package/dist/bootstrap/adapters.d.ts.map +1 -1
  435. package/dist/bootstrap/adapters.js +7 -4
  436. package/dist/bootstrap/adapters.js.map +1 -1
  437. package/dist/bootstrap/api.d.ts +205 -0
  438. package/dist/bootstrap/api.d.ts.map +1 -0
  439. package/dist/bootstrap/api.js +443 -0
  440. package/dist/bootstrap/api.js.map +1 -0
  441. package/dist/bootstrap/db.d.ts +119 -0
  442. package/dist/bootstrap/db.d.ts.map +1 -0
  443. package/dist/bootstrap/db.js +294 -0
  444. package/dist/bootstrap/db.js.map +1 -0
  445. package/dist/bootstrap/event-pipeline.d.ts +308 -0
  446. package/dist/bootstrap/event-pipeline.d.ts.map +1 -0
  447. package/dist/bootstrap/event-pipeline.js +704 -0
  448. package/dist/bootstrap/event-pipeline.js.map +1 -0
  449. package/dist/bootstrap/observers.d.ts +148 -0
  450. package/dist/bootstrap/observers.d.ts.map +1 -0
  451. package/dist/bootstrap/observers.js +558 -0
  452. package/dist/bootstrap/observers.js.map +1 -0
  453. package/dist/bootstrap/schedule-helpers.d.ts +122 -0
  454. package/dist/bootstrap/schedule-helpers.d.ts.map +1 -1
  455. package/dist/bootstrap/schedule-helpers.js +202 -4
  456. package/dist/bootstrap/schedule-helpers.js.map +1 -1
  457. package/dist/config.d.ts.map +1 -1
  458. package/dist/config.js +20 -3
  459. package/dist/config.js.map +1 -1
  460. package/dist/core/abort-utils.d.ts +14 -0
  461. package/dist/core/abort-utils.d.ts.map +1 -0
  462. package/dist/core/abort-utils.js +36 -0
  463. package/dist/core/abort-utils.js.map +1 -0
  464. package/dist/core/agent-core.d.ts +22 -3
  465. package/dist/core/agent-core.d.ts.map +1 -1
  466. package/dist/core/agent-core.js +3 -1
  467. package/dist/core/agent-core.js.map +1 -1
  468. package/dist/core/backends/auth-health-monitor.js +2 -3
  469. package/dist/core/backends/auth-health-monitor.js.map +1 -1
  470. package/dist/core/backends/backend-router.d.ts +11 -1
  471. package/dist/core/backends/backend-router.d.ts.map +1 -1
  472. package/dist/core/backends/backend-router.js +97 -2
  473. package/dist/core/backends/backend-router.js.map +1 -1
  474. package/dist/core/backends/claude-code-core.d.ts +51 -0
  475. package/dist/core/backends/claude-code-core.d.ts.map +1 -1
  476. package/dist/core/backends/claude-code-core.js +134 -11
  477. package/dist/core/backends/claude-code-core.js.map +1 -1
  478. package/dist/core/backends/claude-tool-collection.js +1 -1
  479. package/dist/core/backends/codex-core.d.ts +13 -1
  480. package/dist/core/backends/codex-core.d.ts.map +1 -1
  481. package/dist/core/backends/codex-core.js +436 -22
  482. package/dist/core/backends/codex-core.js.map +1 -1
  483. package/dist/core/backends/gemini-cli-core.d.ts +5 -1
  484. package/dist/core/backends/gemini-cli-core.d.ts.map +1 -1
  485. package/dist/core/backends/gemini-cli-core.js +298 -15
  486. package/dist/core/backends/gemini-cli-core.js.map +1 -1
  487. package/dist/core/backends/install-methods.d.ts.map +1 -1
  488. package/dist/core/backends/install-methods.js +22 -0
  489. package/dist/core/backends/install-methods.js.map +1 -1
  490. package/dist/core/backends/model-registry.d.ts +9 -4
  491. package/dist/core/backends/model-registry.d.ts.map +1 -1
  492. package/dist/core/backends/model-registry.js +153 -23
  493. package/dist/core/backends/model-registry.js.map +1 -1
  494. package/dist/core/backends/native-skill-discovery-probe.d.ts +80 -0
  495. package/dist/core/backends/native-skill-discovery-probe.d.ts.map +1 -0
  496. package/dist/core/backends/native-skill-discovery-probe.js +175 -0
  497. package/dist/core/backends/native-skill-discovery-probe.js.map +1 -0
  498. package/dist/core/backends/opencode-basic-auth-fetch.d.ts +27 -0
  499. package/dist/core/backends/opencode-basic-auth-fetch.d.ts.map +1 -0
  500. package/dist/core/backends/opencode-basic-auth-fetch.js +40 -0
  501. package/dist/core/backends/opencode-basic-auth-fetch.js.map +1 -0
  502. package/dist/core/backends/opencode-config-builder.d.ts +86 -0
  503. package/dist/core/backends/opencode-config-builder.d.ts.map +1 -0
  504. package/dist/core/backends/opencode-config-builder.js +172 -0
  505. package/dist/core/backends/opencode-config-builder.js.map +1 -0
  506. package/dist/core/backends/opencode-core.d.ts +316 -0
  507. package/dist/core/backends/opencode-core.d.ts.map +1 -0
  508. package/dist/core/backends/opencode-core.js +1502 -0
  509. package/dist/core/backends/opencode-core.js.map +1 -0
  510. package/dist/core/backends/opencode-event-mapper.d.ts +133 -0
  511. package/dist/core/backends/opencode-event-mapper.d.ts.map +1 -0
  512. package/dist/core/backends/opencode-event-mapper.js +198 -0
  513. package/dist/core/backends/opencode-event-mapper.js.map +1 -0
  514. package/dist/core/backends/opencode-mcp.d.ts +82 -0
  515. package/dist/core/backends/opencode-mcp.d.ts.map +1 -0
  516. package/dist/core/backends/opencode-mcp.js +165 -0
  517. package/dist/core/backends/opencode-mcp.js.map +1 -0
  518. package/dist/core/backends/opencode-server-manager.d.ts +114 -0
  519. package/dist/core/backends/opencode-server-manager.d.ts.map +1 -0
  520. package/dist/core/backends/opencode-server-manager.js +222 -0
  521. package/dist/core/backends/opencode-server-manager.js.map +1 -0
  522. package/dist/core/backends/opencode-types.d.ts +46 -0
  523. package/dist/core/backends/opencode-types.d.ts.map +1 -0
  524. package/dist/core/backends/opencode-types.js +14 -0
  525. package/dist/core/backends/opencode-types.js.map +1 -0
  526. package/dist/core/backends/plan-presets.d.ts +18 -5
  527. package/dist/core/backends/plan-presets.d.ts.map +1 -1
  528. package/dist/core/backends/plan-presets.js +144 -23
  529. package/dist/core/backends/plan-presets.js.map +1 -1
  530. package/dist/core/backends/process-config-cascade.d.ts +35 -0
  531. package/dist/core/backends/process-config-cascade.d.ts.map +1 -1
  532. package/dist/core/backends/process-config-cascade.js +35 -1
  533. package/dist/core/backends/process-config-cascade.js.map +1 -1
  534. package/dist/core/backends/prompt-utils.d.ts.map +1 -1
  535. package/dist/core/backends/prompt-utils.js +0 -2
  536. package/dist/core/backends/prompt-utils.js.map +1 -1
  537. package/dist/core/backends/quota-reset-hints.d.ts +44 -0
  538. package/dist/core/backends/quota-reset-hints.d.ts.map +1 -0
  539. package/dist/core/backends/quota-reset-hints.js +117 -0
  540. package/dist/core/backends/quota-reset-hints.js.map +1 -0
  541. package/dist/core/bang-commands/commands-close.d.ts +24 -0
  542. package/dist/core/bang-commands/commands-close.d.ts.map +1 -0
  543. package/dist/core/bang-commands/commands-close.js +24 -0
  544. package/dist/core/bang-commands/commands-close.js.map +1 -0
  545. package/dist/core/bang-commands/commands-cost.d.ts.map +1 -1
  546. package/dist/core/bang-commands/commands-cost.js +13 -0
  547. package/dist/core/bang-commands/commands-cost.js.map +1 -1
  548. package/dist/core/bang-commands/commands-help.d.ts.map +1 -1
  549. package/dist/core/bang-commands/commands-help.js +44 -5
  550. package/dist/core/bang-commands/commands-help.js.map +1 -1
  551. package/dist/core/bang-commands/commands-report.d.ts.map +1 -1
  552. package/dist/core/bang-commands/commands-report.js +1 -0
  553. package/dist/core/bang-commands/commands-report.js.map +1 -1
  554. package/dist/core/bang-commands/commands-stop-start.d.ts.map +1 -1
  555. package/dist/core/bang-commands/commands-stop-start.js +2 -0
  556. package/dist/core/bang-commands/commands-stop-start.js.map +1 -1
  557. package/dist/core/bang-commands/commands-wiki.d.ts.map +1 -1
  558. package/dist/core/bang-commands/commands-wiki.js +12 -1
  559. package/dist/core/bang-commands/commands-wiki.js.map +1 -1
  560. package/dist/core/bang-commands/format-utils.d.ts +13 -1
  561. package/dist/core/bang-commands/format-utils.d.ts.map +1 -1
  562. package/dist/core/bang-commands/format-utils.js +21 -2
  563. package/dist/core/bang-commands/format-utils.js.map +1 -1
  564. package/dist/core/bang-commands/index.d.ts +1 -0
  565. package/dist/core/bang-commands/index.d.ts.map +1 -1
  566. package/dist/core/bang-commands/index.js +3 -0
  567. package/dist/core/bang-commands/index.js.map +1 -1
  568. package/dist/core/bang-commands/registry.d.ts +50 -0
  569. package/dist/core/bang-commands/registry.d.ts.map +1 -1
  570. package/dist/core/bang-commands/registry.js +164 -19
  571. package/dist/core/bang-commands/registry.js.map +1 -1
  572. package/dist/core/channel-timeline.d.ts +18 -1
  573. package/dist/core/channel-timeline.d.ts.map +1 -1
  574. package/dist/core/channel-timeline.js +44 -0
  575. package/dist/core/channel-timeline.js.map +1 -1
  576. package/dist/core/context/activity-view-runner.js +9 -1
  577. package/dist/core/context/activity-view-runner.js.map +1 -1
  578. package/dist/core/context/default-schedules-runner.js +44 -4
  579. package/dist/core/context/default-schedules-runner.js.map +1 -1
  580. package/dist/core/context/domain-index-runner.js +9 -1
  581. package/dist/core/context/domain-index-runner.js.map +1 -1
  582. package/dist/core/context/entity-source-rename.d.ts.map +1 -1
  583. package/dist/core/context/entity-source-rename.js +9 -1
  584. package/dist/core/context/entity-source-rename.js.map +1 -1
  585. package/dist/core/context/policy-index-runner.js +9 -1
  586. package/dist/core/context/policy-index-runner.js.map +1 -1
  587. package/dist/core/context/reconciler-runner.js +9 -1
  588. package/dist/core/context/reconciler-runner.js.map +1 -1
  589. package/dist/core/context-builder.d.ts +97 -2
  590. package/dist/core/context-builder.d.ts.map +1 -1
  591. package/dist/core/context-builder.js +303 -30
  592. package/dist/core/context-builder.js.map +1 -1
  593. package/dist/core/context-frontmatter.d.ts +6 -0
  594. package/dist/core/context-frontmatter.d.ts.map +1 -1
  595. package/dist/core/context-frontmatter.js +120 -8
  596. package/dist/core/context-frontmatter.js.map +1 -1
  597. package/dist/core/context-health.js +21 -9
  598. package/dist/core/context-health.js.map +1 -1
  599. package/dist/core/context-validation/_pa_wip_context_validation/index.d.ts +19 -0
  600. package/dist/core/context-validation/_pa_wip_context_validation/index.d.ts.map +1 -0
  601. package/dist/core/context-validation/_pa_wip_context_validation/index.js +19 -0
  602. package/dist/core/context-validation/_pa_wip_context_validation/index.js.map +1 -0
  603. package/dist/core/context-validation/_pa_wip_context_validation/prepare-write.d.ts +94 -0
  604. package/dist/core/context-validation/_pa_wip_context_validation/prepare-write.d.ts.map +1 -0
  605. package/dist/core/context-validation/_pa_wip_context_validation/prepare-write.js +130 -0
  606. package/dist/core/context-validation/_pa_wip_context_validation/prepare-write.js.map +1 -0
  607. package/dist/core/context-validation/_pa_wip_context_validation/routine-rulebook.d.ts +60 -0
  608. package/dist/core/context-validation/_pa_wip_context_validation/routine-rulebook.d.ts.map +1 -0
  609. package/dist/core/context-validation/_pa_wip_context_validation/routine-rulebook.js +156 -0
  610. package/dist/core/context-validation/_pa_wip_context_validation/routine-rulebook.js.map +1 -0
  611. package/dist/core/context-validation/_pa_wip_context_validation/section.d.ts +41 -0
  612. package/dist/core/context-validation/_pa_wip_context_validation/section.d.ts.map +1 -0
  613. package/dist/core/context-validation/_pa_wip_context_validation/section.js +82 -0
  614. package/dist/core/context-validation/_pa_wip_context_validation/section.js.map +1 -0
  615. package/dist/core/context-validation/_pa_wip_context_validation/snapshot-debounce.d.ts +52 -0
  616. package/dist/core/context-validation/_pa_wip_context_validation/snapshot-debounce.d.ts.map +1 -0
  617. package/dist/core/context-validation/_pa_wip_context_validation/snapshot-debounce.js +110 -0
  618. package/dist/core/context-validation/_pa_wip_context_validation/snapshot-debounce.js.map +1 -0
  619. package/dist/core/context-validation/_pa_wip_context_validation/today.d.ts +85 -0
  620. package/dist/core/context-validation/_pa_wip_context_validation/today.d.ts.map +1 -0
  621. package/dist/core/context-validation/_pa_wip_context_validation/today.js +162 -0
  622. package/dist/core/context-validation/_pa_wip_context_validation/today.js.map +1 -0
  623. package/dist/core/context-validation/index.d.ts +19 -0
  624. package/dist/core/context-validation/index.d.ts.map +1 -0
  625. package/dist/core/context-validation/index.js +19 -0
  626. package/dist/core/context-validation/index.js.map +1 -0
  627. package/dist/core/context-validation/prepare-write.d.ts +95 -0
  628. package/dist/core/context-validation/prepare-write.d.ts.map +1 -0
  629. package/dist/core/context-validation/prepare-write.js +135 -0
  630. package/dist/core/context-validation/prepare-write.js.map +1 -0
  631. package/dist/core/context-validation/routine-rulebook.d.ts +60 -0
  632. package/dist/core/context-validation/routine-rulebook.d.ts.map +1 -0
  633. package/dist/core/context-validation/routine-rulebook.js +156 -0
  634. package/dist/core/context-validation/routine-rulebook.js.map +1 -0
  635. package/dist/core/context-validation/section.d.ts +41 -0
  636. package/dist/core/context-validation/section.d.ts.map +1 -0
  637. package/dist/core/context-validation/section.js +82 -0
  638. package/dist/core/context-validation/section.js.map +1 -0
  639. package/dist/core/context-validation/snapshot-debounce.d.ts +52 -0
  640. package/dist/core/context-validation/snapshot-debounce.d.ts.map +1 -0
  641. package/dist/core/context-validation/snapshot-debounce.js +110 -0
  642. package/dist/core/context-validation/snapshot-debounce.js.map +1 -0
  643. package/dist/core/context-validation/today.d.ts +85 -0
  644. package/dist/core/context-validation/today.d.ts.map +1 -0
  645. package/dist/core/context-validation/today.js +168 -0
  646. package/dist/core/context-validation/today.js.map +1 -0
  647. package/dist/core/daemon-api-cli.d.ts +6 -5
  648. package/dist/core/daemon-api-cli.d.ts.map +1 -1
  649. package/dist/core/daemon-api-cli.js +73 -32
  650. package/dist/core/daemon-api-cli.js.map +1 -1
  651. package/dist/core/dispatcher-error-handling.d.ts +13 -0
  652. package/dist/core/dispatcher-error-handling.d.ts.map +1 -1
  653. package/dist/core/dispatcher-error-handling.js +69 -0
  654. package/dist/core/dispatcher-error-handling.js.map +1 -1
  655. package/dist/core/dispatcher-hourly-check.d.ts +73 -1
  656. package/dist/core/dispatcher-hourly-check.d.ts.map +1 -1
  657. package/dist/core/dispatcher-hourly-check.js +249 -151
  658. package/dist/core/dispatcher-hourly-check.js.map +1 -1
  659. package/dist/core/dispatcher-message-handler.d.ts +11 -1
  660. package/dist/core/dispatcher-message-handler.d.ts.map +1 -1
  661. package/dist/core/dispatcher-message-handler.js +165 -47
  662. package/dist/core/dispatcher-message-handler.js.map +1 -1
  663. package/dist/core/dispatcher-morning-routine.d.ts +38 -30
  664. package/dist/core/dispatcher-morning-routine.d.ts.map +1 -1
  665. package/dist/core/dispatcher-morning-routine.js +162 -104
  666. package/dist/core/dispatcher-morning-routine.js.map +1 -1
  667. package/dist/core/dispatcher-prompt.d.ts +4 -1
  668. package/dist/core/dispatcher-prompt.d.ts.map +1 -1
  669. package/dist/core/dispatcher-prompt.js +18 -2
  670. package/dist/core/dispatcher-prompt.js.map +1 -1
  671. package/dist/core/dispatcher-repository-helpers.d.ts.map +1 -1
  672. package/dist/core/dispatcher-repository-helpers.js +5 -1
  673. package/dist/core/dispatcher-repository-helpers.js.map +1 -1
  674. package/dist/core/dispatcher-result-processor.d.ts.map +1 -1
  675. package/dist/core/dispatcher-result-processor.js +9 -4
  676. package/dist/core/dispatcher-result-processor.js.map +1 -1
  677. package/dist/core/dispatcher-scheduled-tasks.d.ts +1 -1
  678. package/dist/core/dispatcher-scheduled-tasks.d.ts.map +1 -1
  679. package/dist/core/dispatcher-scheduled-tasks.js +79 -16
  680. package/dist/core/dispatcher-scheduled-tasks.js.map +1 -1
  681. package/dist/core/dispatcher-types.d.ts +84 -12
  682. package/dist/core/dispatcher-types.d.ts.map +1 -1
  683. package/dist/core/dispatcher-types.js.map +1 -1
  684. package/dist/core/dispatcher.d.ts +50 -1
  685. package/dist/core/dispatcher.d.ts.map +1 -1
  686. package/dist/core/dispatcher.js +143 -8
  687. package/dist/core/dispatcher.js.map +1 -1
  688. package/dist/core/dm-freshness-metrics.d.ts +6 -5
  689. package/dist/core/dm-freshness-metrics.d.ts.map +1 -1
  690. package/dist/core/dm-freshness-metrics.js +7 -6
  691. package/dist/core/dm-freshness-metrics.js.map +1 -1
  692. package/dist/core/evening-review-verify.d.ts +164 -0
  693. package/dist/core/evening-review-verify.d.ts.map +1 -0
  694. package/dist/core/evening-review-verify.js +637 -0
  695. package/dist/core/evening-review-verify.js.map +1 -0
  696. package/dist/core/fetch-window-prompt-loader.d.ts +47 -0
  697. package/dist/core/fetch-window-prompt-loader.d.ts.map +1 -0
  698. package/dist/core/fetch-window-prompt-loader.js +72 -0
  699. package/dist/core/fetch-window-prompt-loader.js.map +1 -0
  700. package/dist/core/management-md.d.ts.map +1 -1
  701. package/dist/core/management-md.js +23 -9
  702. package/dist/core/management-md.js.map +1 -1
  703. package/dist/core/management-registry.d.ts +13 -21
  704. package/dist/core/management-registry.d.ts.map +1 -1
  705. package/dist/core/management-registry.js +27 -48
  706. package/dist/core/management-registry.js.map +1 -1
  707. package/dist/core/metrics.d.ts +88 -1
  708. package/dist/core/metrics.d.ts.map +1 -1
  709. package/dist/core/metrics.js +78 -2
  710. package/dist/core/metrics.js.map +1 -1
  711. package/dist/core/morning/agent-journal-appender.d.ts +197 -0
  712. package/dist/core/morning/agent-journal-appender.d.ts.map +1 -0
  713. package/dist/core/morning/agent-journal-appender.js +458 -0
  714. package/dist/core/morning/agent-journal-appender.js.map +1 -0
  715. package/dist/core/morning/handoff-parser.d.ts +45 -0
  716. package/dist/core/morning/handoff-parser.d.ts.map +1 -0
  717. package/dist/core/morning/handoff-parser.js +117 -0
  718. package/dist/core/morning/handoff-parser.js.map +1 -0
  719. package/dist/core/morning/journal-skeleton-builder.d.ts +157 -0
  720. package/dist/core/morning/journal-skeleton-builder.d.ts.map +1 -0
  721. package/dist/core/morning/journal-skeleton-builder.js +303 -0
  722. package/dist/core/morning/journal-skeleton-builder.js.map +1 -0
  723. package/dist/core/morning/orchestrator.d.ts +312 -0
  724. package/dist/core/morning/orchestrator.d.ts.map +1 -0
  725. package/dist/core/morning/orchestrator.js +827 -0
  726. package/dist/core/morning/orchestrator.js.map +1 -0
  727. package/dist/core/morning/parent-audit-emitter.d.ts +82 -0
  728. package/dist/core/morning/parent-audit-emitter.d.ts.map +1 -0
  729. package/dist/core/morning/parent-audit-emitter.js +120 -0
  730. package/dist/core/morning/parent-audit-emitter.js.map +1 -0
  731. package/dist/core/morning/roadmap-skeleton-builder.d.ts +159 -0
  732. package/dist/core/morning/roadmap-skeleton-builder.d.ts.map +1 -0
  733. package/dist/core/morning/roadmap-skeleton-builder.js +338 -0
  734. package/dist/core/morning/roadmap-skeleton-builder.js.map +1 -0
  735. package/dist/core/output-language-policy.js +1 -1
  736. package/dist/core/output-language-policy.js.map +1 -1
  737. package/dist/core/policy-files.d.ts +19 -2
  738. package/dist/core/policy-files.d.ts.map +1 -1
  739. package/dist/core/policy-files.js +65 -7
  740. package/dist/core/policy-files.js.map +1 -1
  741. package/dist/core/pre-pass-freshness.d.ts +28 -0
  742. package/dist/core/pre-pass-freshness.d.ts.map +1 -0
  743. package/dist/core/pre-pass-freshness.js +10 -0
  744. package/dist/core/pre-pass-freshness.js.map +1 -0
  745. package/dist/core/previous-week-digest.d.ts +130 -0
  746. package/dist/core/previous-week-digest.d.ts.map +1 -0
  747. package/dist/core/previous-week-digest.js +257 -0
  748. package/dist/core/previous-week-digest.js.map +1 -0
  749. package/dist/core/prompts.js +3 -3
  750. package/dist/core/prompts.js.map +1 -1
  751. package/dist/core/quiet-hours-sync.d.ts.map +1 -1
  752. package/dist/core/quiet-hours-sync.js +7 -0
  753. package/dist/core/quiet-hours-sync.js.map +1 -1
  754. package/dist/core/recurrence.d.ts +13 -0
  755. package/dist/core/recurrence.d.ts.map +1 -1
  756. package/dist/core/recurrence.js +108 -13
  757. package/dist/core/recurrence.js.map +1 -1
  758. package/dist/core/release-assets.d.ts +21 -1
  759. package/dist/core/release-assets.d.ts.map +1 -1
  760. package/dist/core/release-assets.js +58 -3
  761. package/dist/core/release-assets.js.map +1 -1
  762. package/dist/core/repository-management-docs.d.ts.map +1 -1
  763. package/dist/core/repository-management-docs.js +14 -4
  764. package/dist/core/repository-management-docs.js.map +1 -1
  765. package/dist/core/review-context.d.ts.map +1 -1
  766. package/dist/core/review-context.js +29 -1
  767. package/dist/core/review-context.js.map +1 -1
  768. package/dist/core/roadmap-maintenance.d.ts +213 -0
  769. package/dist/core/roadmap-maintenance.d.ts.map +1 -0
  770. package/dist/core/roadmap-maintenance.js +706 -0
  771. package/dist/core/roadmap-maintenance.js.map +1 -0
  772. package/dist/core/roadmap-validate.d.ts +5 -0
  773. package/dist/core/roadmap-validate.d.ts.map +1 -1
  774. package/dist/core/roadmap-validate.js +6 -69
  775. package/dist/core/roadmap-validate.js.map +1 -1
  776. package/dist/core/routine-acquisition-plan.d.ts +43 -7
  777. package/dist/core/routine-acquisition-plan.d.ts.map +1 -1
  778. package/dist/core/routine-acquisition-plan.js +99 -8
  779. package/dist/core/routine-acquisition-plan.js.map +1 -1
  780. package/dist/core/routine-fetch-window-retry.d.ts +41 -2
  781. package/dist/core/routine-fetch-window-retry.d.ts.map +1 -1
  782. package/dist/core/routine-fetch-window-retry.js +91 -8
  783. package/dist/core/routine-fetch-window-retry.js.map +1 -1
  784. package/dist/core/routine-fetch-window-runner.d.ts +55 -21
  785. package/dist/core/routine-fetch-window-runner.d.ts.map +1 -1
  786. package/dist/core/routine-fetch-window-runner.js +258 -35
  787. package/dist/core/routine-fetch-window-runner.js.map +1 -1
  788. package/dist/core/routine-windows.d.ts +17 -13
  789. package/dist/core/routine-windows.d.ts.map +1 -1
  790. package/dist/core/routine-windows.js +78 -36
  791. package/dist/core/routine-windows.js.map +1 -1
  792. package/dist/core/scheduler.d.ts +121 -37
  793. package/dist/core/scheduler.d.ts.map +1 -1
  794. package/dist/core/scheduler.js +359 -80
  795. package/dist/core/scheduler.js.map +1 -1
  796. package/dist/core/session-manager.d.ts +25 -6
  797. package/dist/core/session-manager.d.ts.map +1 -1
  798. package/dist/core/session-manager.js +32 -15
  799. package/dist/core/session-manager.js.map +1 -1
  800. package/dist/core/skeleton.d.ts.map +1 -1
  801. package/dist/core/skeleton.js +23 -23
  802. package/dist/core/skeleton.js.map +1 -1
  803. package/dist/core/skills-compiler.d.ts +275 -25
  804. package/dist/core/skills-compiler.d.ts.map +1 -1
  805. package/dist/core/skills-compiler.js +844 -205
  806. package/dist/core/skills-compiler.js.map +1 -1
  807. package/dist/core/skills-manifest.d.ts +104 -0
  808. package/dist/core/skills-manifest.d.ts.map +1 -1
  809. package/dist/core/skills-manifest.js +350 -39
  810. package/dist/core/skills-manifest.js.map +1 -1
  811. package/dist/core/wiki/git-precompile.d.ts +9 -0
  812. package/dist/core/wiki/git-precompile.d.ts.map +1 -1
  813. package/dist/core/wiki/git-precompile.js +7 -1
  814. package/dist/core/wiki/git-precompile.js.map +1 -1
  815. package/dist/core/workdir.d.ts +30 -1
  816. package/dist/core/workdir.d.ts.map +1 -1
  817. package/dist/core/workdir.js +140 -15
  818. package/dist/core/workdir.js.map +1 -1
  819. package/dist/db/entities-store.d.ts +5 -5
  820. package/dist/db/entities-store.js +5 -5
  821. package/dist/db/hourly-check-signals.d.ts.map +1 -1
  822. package/dist/db/hourly-check-signals.js +121 -35
  823. package/dist/db/hourly-check-signals.js.map +1 -1
  824. package/dist/db/observations.d.ts +1 -1
  825. package/dist/db/observations.js +1 -1
  826. package/dist/db/observations.js.map +1 -1
  827. package/dist/db/recurring-schedules.d.ts +32 -1
  828. package/dist/db/recurring-schedules.d.ts.map +1 -1
  829. package/dist/db/recurring-schedules.js +29 -10
  830. package/dist/db/recurring-schedules.js.map +1 -1
  831. package/dist/db/repositories-store.d.ts +2 -1
  832. package/dist/db/repositories-store.d.ts.map +1 -1
  833. package/dist/db/repositories-store.js +38 -3
  834. package/dist/db/repositories-store.js.map +1 -1
  835. package/dist/db/schema.d.ts.map +1 -1
  836. package/dist/db/schema.js +157 -51
  837. package/dist/db/schema.js.map +1 -1
  838. package/dist/db/wiki-store.d.ts.map +1 -1
  839. package/dist/db/wiki-store.js +3 -0
  840. package/dist/db/wiki-store.js.map +1 -1
  841. package/dist/index.js +308 -1480
  842. package/dist/index.js.map +1 -1
  843. package/dist/messaging/magic-phrase.d.ts +16 -0
  844. package/dist/messaging/magic-phrase.d.ts.map +1 -1
  845. package/dist/messaging/magic-phrase.js +53 -0
  846. package/dist/messaging/magic-phrase.js.map +1 -1
  847. package/dist/messaging/owner-channels.d.ts +24 -0
  848. package/dist/messaging/owner-channels.d.ts.map +1 -1
  849. package/dist/messaging/owner-channels.js +38 -0
  850. package/dist/messaging/owner-channels.js.map +1 -1
  851. package/dist/observers/calendar-poller.d.ts +2 -0
  852. package/dist/observers/calendar-poller.d.ts.map +1 -1
  853. package/dist/observers/calendar-poller.js +76 -54
  854. package/dist/observers/calendar-poller.js.map +1 -1
  855. package/dist/observers/delegated-sync-worker.d.ts +62 -0
  856. package/dist/observers/delegated-sync-worker.d.ts.map +1 -1
  857. package/dist/observers/delegated-sync-worker.js +128 -1
  858. package/dist/observers/delegated-sync-worker.js.map +1 -1
  859. package/dist/observers/git-watcher.d.ts +22 -0
  860. package/dist/observers/git-watcher.d.ts.map +1 -1
  861. package/dist/observers/git-watcher.js +31 -7
  862. package/dist/observers/git-watcher.js.map +1 -1
  863. package/dist/observers/imminent-event-scheduler.d.ts +2 -0
  864. package/dist/observers/imminent-event-scheduler.d.ts.map +1 -1
  865. package/dist/observers/imminent-event-scheduler.js +29 -0
  866. package/dist/observers/imminent-event-scheduler.js.map +1 -1
  867. package/dist/observers/notion-poller.d.ts +2 -0
  868. package/dist/observers/notion-poller.d.ts.map +1 -1
  869. package/dist/observers/notion-poller.js +44 -6
  870. package/dist/observers/notion-poller.js.map +1 -1
  871. package/dist/observers/poll-guard.d.ts +63 -0
  872. package/dist/observers/poll-guard.d.ts.map +1 -0
  873. package/dist/observers/poll-guard.js +89 -0
  874. package/dist/observers/poll-guard.js.map +1 -0
  875. package/dist/safety/absolute-block-audit.d.ts +17 -0
  876. package/dist/safety/absolute-block-audit.d.ts.map +1 -1
  877. package/dist/safety/absolute-block-audit.js +28 -2
  878. package/dist/safety/absolute-block-audit.js.map +1 -1
  879. package/dist/safety/agent-write-tracker.d.ts +42 -1
  880. package/dist/safety/agent-write-tracker.d.ts.map +1 -1
  881. package/dist/safety/agent-write-tracker.js +81 -1
  882. package/dist/safety/agent-write-tracker.js.map +1 -1
  883. package/dist/safety/always-disallowed.d.ts +34 -0
  884. package/dist/safety/always-disallowed.d.ts.map +1 -1
  885. package/dist/safety/always-disallowed.js +114 -7
  886. package/dist/safety/always-disallowed.js.map +1 -1
  887. package/dist/safety/audit.d.ts +20 -0
  888. package/dist/safety/audit.d.ts.map +1 -1
  889. package/dist/safety/audit.js +126 -0
  890. package/dist/safety/audit.js.map +1 -1
  891. package/dist/safety/risk-classifier.d.ts +17 -1
  892. package/dist/safety/risk-classifier.d.ts.map +1 -1
  893. package/dist/safety/risk-classifier.js +75 -7
  894. package/dist/safety/risk-classifier.js.map +1 -1
  895. package/dist/safety/subprocess-block-scanner.d.ts +89 -0
  896. package/dist/safety/subprocess-block-scanner.d.ts.map +1 -0
  897. package/dist/safety/subprocess-block-scanner.js +177 -0
  898. package/dist/safety/subprocess-block-scanner.js.map +1 -0
  899. package/dist/scheduler/hourly-check-gate.d.ts +23 -9
  900. package/dist/scheduler/hourly-check-gate.d.ts.map +1 -1
  901. package/dist/scheduler/hourly-check-gate.js +11 -6
  902. package/dist/scheduler/hourly-check-gate.js.map +1 -1
  903. package/dist/secrets/backend-api-key-env.d.ts.map +1 -1
  904. package/dist/secrets/backend-api-key-env.js +1 -0
  905. package/dist/secrets/backend-api-key-env.js.map +1 -1
  906. package/dist/services/delegated-backend-invoker.d.ts.map +1 -1
  907. package/dist/services/delegated-backend-invoker.js +8 -1
  908. package/dist/services/delegated-backend-invoker.js.map +1 -1
  909. package/dist/services/delegated-invoker-audit.d.ts.map +1 -1
  910. package/dist/services/delegated-invoker-audit.js +5 -1
  911. package/dist/services/delegated-invoker-audit.js.map +1 -1
  912. package/dist/services/delegated-proxy-config.d.ts +3 -2
  913. package/dist/services/delegated-proxy-config.d.ts.map +1 -1
  914. package/dist/services/delegated-proxy-config.js.map +1 -1
  915. package/dist/services/integrations/extract-write-item-id.d.ts +5 -2
  916. package/dist/services/integrations/extract-write-item-id.d.ts.map +1 -1
  917. package/dist/services/integrations/extract-write-item-id.js.map +1 -1
  918. package/dist/services/mcp/generators/index.d.ts +1 -0
  919. package/dist/services/mcp/generators/index.d.ts.map +1 -1
  920. package/dist/services/mcp/generators/index.js +3 -0
  921. package/dist/services/mcp/generators/index.js.map +1 -1
  922. package/dist/services/mcp/sdk-observations-server.d.ts +60 -0
  923. package/dist/services/mcp/sdk-observations-server.d.ts.map +1 -0
  924. package/dist/services/mcp/sdk-observations-server.js +161 -0
  925. package/dist/services/mcp/sdk-observations-server.js.map +1 -0
  926. package/dist/services/mcp/session-materializer.d.ts.map +1 -1
  927. package/dist/services/mcp/session-materializer.js +13 -9
  928. package/dist/services/mcp/session-materializer.js.map +1 -1
  929. package/dist/services/mcp/types.d.ts +1 -0
  930. package/dist/services/mcp/types.d.ts.map +1 -1
  931. package/dist/services/notion.d.ts +19 -1
  932. package/dist/services/notion.d.ts.map +1 -1
  933. package/dist/services/notion.js +41 -2
  934. package/dist/services/notion.js.map +1 -1
  935. package/dist/services/observations-batch.d.ts +100 -0
  936. package/dist/services/observations-batch.d.ts.map +1 -0
  937. package/dist/services/observations-batch.js +258 -0
  938. package/dist/services/observations-batch.js.map +1 -0
  939. package/dist/services/voice/transcriber.d.ts +23 -0
  940. package/dist/services/voice/transcriber.d.ts.map +1 -1
  941. package/dist/services/voice/transcriber.js +55 -0
  942. package/dist/services/voice/transcriber.js.map +1 -1
  943. package/dist/settings/runtime-settings.d.ts +9 -6
  944. package/dist/settings/runtime-settings.d.ts.map +1 -1
  945. package/dist/settings/runtime-settings.js +50 -17
  946. package/dist/settings/runtime-settings.js.map +1 -1
  947. package/package.json +3 -2
  948. package/dist/api/delegated-proxy-helper.d.ts +0 -33
  949. package/dist/api/delegated-proxy-helper.d.ts.map +0 -1
  950. package/dist/api/delegated-proxy-helper.js +0 -54
  951. package/dist/api/delegated-proxy-helper.js.map +0 -1
  952. package/dist/core/roadmap-merge.d.ts +0 -7
  953. package/dist/core/roadmap-merge.d.ts.map +0 -1
  954. package/dist/core/roadmap-merge.js +0 -187
  955. package/dist/core/roadmap-merge.js.map +0 -1
  956. package/dist/db/test-schemas.d.ts +0 -23
  957. package/dist/db/test-schemas.d.ts.map +0 -1
  958. package/dist/db/test-schemas.js +0 -111
  959. package/dist/db/test-schemas.js.map +0 -1
@@ -0,0 +1,1502 @@
1
+ /**
2
+ * docs/design/appendices/opencode-backend.md §6.2 — `IAgentCore` implementation backed by
3
+ * the `@opencode-ai/sdk` HTTP server.
4
+ *
5
+ * Surface:
6
+ * - `execute()` end-to-end against the managed server (loopback)
7
+ * - `executeResume()` reuses an existing opencode session id so the
8
+ * dispatcher's multi-turn DM / dashboard-chat / docs-QA path can
9
+ * own opencode as main backend (see
10
+ * `docs/design/appendices/opencode-execute-resume.md`)
11
+ * - Streaming text deltas relayed through the StreamCallbacks
12
+ * - Cost / token telemetry from `client.session.get(...).data`,
13
+ * scoped to a per-turn delta when resuming (pre-turn snapshot
14
+ * subtracted from post-turn cumulative)
15
+ * - `summarize()` V11 round-trip
16
+ * - `runDelegatedTask()` via spawnEphemeral (Path A)
17
+ * - Stub for `runDelegatedTool` (by design — opencode delegation
18
+ * routes through `runDelegatedTask`)
19
+ * - Per-execute wall-clock abort via `AbortController` →
20
+ * `client.session.abort()`
21
+ */
22
+ import { randomUUID } from "node:crypto";
23
+ import { defaultApiKeyProvider, isAutonomousProcessKey, isMessageEvent, } from "@aitne/shared";
24
+ import { getContextDir } from "../../config.js";
25
+ import { cleanupSessionWorkdir, createSessionWorkdir, } from "../workdir.js";
26
+ import { auditStreamObservation, extractOpencodeToolUseTarget, } from "../../safety/subprocess-block-scanner.js";
27
+ import { BackendDecisiveFailure, DelegatedToolUnsupportedError, LiveProbeUnsupportedError, TaskModeUnsupportedError, classifyAbortReason, } from "../agent-core.js";
28
+ import { matchRunAllowedToolPattern } from "@aitne/shared";
29
+ import { emptyCost, withDurationMs, } from "../../services/delegated-tool-runtime.js";
30
+ import { DEFAULT_OPENCODE_LITE_MODEL, findRegisteredModel, getModelsForBackend, } from "./model-registry.js";
31
+ import { PriceFetcher } from "./price-fetcher.js";
32
+ import { buildExecutionPrompt } from "./prompt-utils.js";
33
+ import { extractAssistantTextFromParts, extractToolUsesFromParts, isMessageAborted, isTerminal, normalize, } from "./opencode-event-mapper.js";
34
+ import { buildOpencodeRuntimeConfig, defensiveInstructionsFromEnv, } from "./opencode-config-builder.js";
35
+ import { renderOpencodeMcp } from "./opencode-mcp.js";
36
+ import { listMcpServers, resolveMcpSecrets } from "../../services/mcp/registry.js";
37
+ import { buildMcpDisallowedTools } from "../../services/mcp/risk.js";
38
+ import { createLogger } from "../../logging.js";
39
+ const logger = createLogger("opencode-core");
40
+ const EMPTY_USAGE = {
41
+ inputTokens: 0,
42
+ outputTokens: 0,
43
+ cacheCreationInputTokens: 0,
44
+ cacheReadInputTokens: 0,
45
+ };
46
+ /**
47
+ * Provider/model parse for the SDK's `body.model = { providerID, modelID }`
48
+ * shape. opencode encodes the composite as `providerID/modelID` — note
49
+ * the model id itself may contain slashes (e.g. `openai/gpt-oss-20b:free`
50
+ * on OpenRouter), so we only split on the FIRST `/`.
51
+ */
52
+ export function parseModelComposite(composite) {
53
+ const slash = composite.indexOf("/");
54
+ if (slash <= 0 || slash === composite.length - 1) {
55
+ return null;
56
+ }
57
+ return {
58
+ providerID: composite.slice(0, slash),
59
+ modelID: composite.slice(slash + 1),
60
+ };
61
+ }
62
+ /** Five-minute TTL for the live-models cache. The live picker hits this
63
+ * on every dashboard load; 5 min matches the `client.config.providers()`
64
+ * poll cadence and keeps the SDK call out of the hot path. */
65
+ const LIVE_MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
66
+ /**
67
+ * Heuristic tier inference for live models. opencode's catalogue has no
68
+ * native tier concept, so we mirror the daemon's registry thresholds
69
+ * (in/out per 1kTok):
70
+ * - haiku-4.5: $0.001 / $0.005 → lite
71
+ * - gpt-5.4: $0.0025 / $0.015 → medium
72
+ * - sonnet-4.6: $0.003 / $0.015 → medium
73
+ * - gpt-5.5: $0.005 / $0.03 → high (Opus-class, see registry comment)
74
+ * - opus-4.7: $0.015 / $0.075 → high
75
+ *
76
+ * A pure input-rate threshold mis-buckets gpt-5.5 (in=$0.005 sits at the
77
+ * medium boundary but its $30/MTok output is Opus-class). We therefore
78
+ * check input AND output — promote to the higher tier if either rate
79
+ * crosses its threshold.
80
+ *
81
+ * `null` input means cost is unknown (SDK omitted `cost.input`). Treat
82
+ * unknown ≠ free: default to "medium" so an undocumented model is not
83
+ * silently routed to lite-tier surfaces. Free → lite is preserved.
84
+ */
85
+ function inferModelTier(usdPer1kIn, usdPer1kOut, isFree) {
86
+ if (isFree)
87
+ return "lite";
88
+ if (usdPer1kIn === null && usdPer1kOut === null)
89
+ return "medium";
90
+ const inRate = usdPer1kIn ?? 0;
91
+ const outRate = usdPer1kOut ?? 0;
92
+ // High: Opus-class. gpt-5.5 (in $0.005, out $0.03) qualifies on out alone.
93
+ if (inRate >= 0.005 || outRate >= 0.03)
94
+ return "high";
95
+ // Medium: Sonnet-class. Sonnet (in $0.003, out $0.015) qualifies on either.
96
+ if (inRate >= 0.002 || outRate >= 0.01)
97
+ return "medium";
98
+ return "lite";
99
+ }
100
+ export class OpencodeCore {
101
+ config;
102
+ writeTracker;
103
+ serverManager;
104
+ priceFetcher;
105
+ backendId = "opencode";
106
+ liveModelsCache = null;
107
+ /** Coalesce parallel `listLiveModels()` calls onto a single SDK round-trip.
108
+ * Mirrors the inflight pattern in `ManagedOpencodeServerManager.spawn()`
109
+ * so a dashboard burst (picker open + RQ background refresh) doesn't
110
+ * multiply `client.config.providers()` calls. */
111
+ liveModelsInflight = null;
112
+ readToken;
113
+ readTokenManager;
114
+ mcpContext;
115
+ constructor(config, writeTracker, serverManager, priceFetcher = new PriceFetcher(config.dataDir)) {
116
+ this.config = config;
117
+ this.writeTracker = writeTracker;
118
+ this.serverManager = serverManager;
119
+ this.priceFetcher = priceFetcher;
120
+ }
121
+ setReadToken(token) {
122
+ this.readToken = token;
123
+ }
124
+ setReadTokenManager(manager) {
125
+ this.readTokenManager = manager;
126
+ }
127
+ setMcpContext(context) {
128
+ this.mcpContext = context;
129
+ }
130
+ /**
131
+ * Materialises a session workdir, ensures the server is running with
132
+ * the desired runtime config, opens a fresh session via the SDK, then
133
+ * runs prompt + event-stream consumption in parallel. Per-execute
134
+ * timeout cancels the inflight prompt via `client.session.abort()`.
135
+ */
136
+ async execute(params, streamCallbacks) {
137
+ return this.runTurn(params, streamCallbacks);
138
+ }
139
+ /**
140
+ * Resume a prior turn against an existing opencode session id. The
141
+ * dispatcher reaches this when `conversation_sessions.backend_session_id`
142
+ * was previously written by `execute()`. opencode persists sessions on
143
+ * disk, so the same id stays addressable across server restarts; we
144
+ * skip `session.create` and re-enter via `session.prompt({ path: { id } })`.
145
+ *
146
+ * Design: `docs/design/appendices/opencode-execute-resume.md`.
147
+ */
148
+ async executeResume(params, streamCallbacks) {
149
+ if (!params.sessionDir) {
150
+ throw new Error("sessionDir is required for executeResume — workdir holds MCP / skill files the server reads");
151
+ }
152
+ return this.runTurn({
153
+ prompt: params.message,
154
+ context: "",
155
+ // Synthetic event for log telemetry only — resume never re-renders
156
+ // a task-flow prompt (the bareMessage branch sends params.message
157
+ // verbatim through `session.prompt`).
158
+ event: {
159
+ type: "message.received",
160
+ source: "platform",
161
+ priority: 2,
162
+ timestamp: new Date(),
163
+ data: {},
164
+ correlationId: params.eventCorrelationId ?? "resume",
165
+ },
166
+ modelId: params.modelId,
167
+ maxTurns: params.maxTurns ?? 50,
168
+ maxBudgetUsd: params.maxBudgetUsd ?? 1.0,
169
+ sessionDir: params.sessionDir,
170
+ ...(params.sessionDbId !== undefined
171
+ ? { sessionDbId: params.sessionDbId }
172
+ : {}),
173
+ ...(params.turnToken ? { turnToken: params.turnToken } : {}),
174
+ ...(params.stagedAttachments && params.stagedAttachments.length > 0
175
+ ? { stagedAttachments: params.stagedAttachments }
176
+ : {}),
177
+ webSearchEnabled: params.webSearchEnabled ?? false,
178
+ resumeSessionId: params.sessionId,
179
+ }, streamCallbacks);
180
+ }
181
+ /**
182
+ * docs/design/appendices/opencode-backend.md §6.2 / §10 D5 / V11 — opencode-side
183
+ * summarisation flow.
184
+ *
185
+ * 1. Open a transient session so the server has a conversation it
186
+ * can summarise (opencode requires non-empty message history
187
+ * before `session.summarize` will produce a body).
188
+ * 2. Prompt with the conversation text under the lite-tier model
189
+ * (`DEFAULT_OPENCODE_LITE_MODEL`) — small_model isn't honoured
190
+ * here because Aitne disables `task` (V8) so opencode's own
191
+ * subagent path never picks small_model up. Cheap & quick on the
192
+ * same provider as the active turn.
193
+ * 3. Call `client.session.summarize({ path: { id }, body: { providerID, modelID } })`
194
+ * — body is FLAT, NOT nested in a `model` wrapper (V11 contract).
195
+ * Returns `{ data: true }` on HTTP 200; the actual summary lands
196
+ * as a new assistant message with `info.summary === true`.
197
+ * 4. Read `client.session.messages({ path: { id } })` and pick the
198
+ * LAST assistant message with `info.summary === true`. Join its
199
+ * text parts as the markdown summary. (Do NOT read
200
+ * `session.get(...).data.summary` — V11 fixture confirms that
201
+ * field is the diff stat block `{ additions, deletions, files }`,
202
+ * not text.)
203
+ * 5. Best-effort `session.delete()` in a `finally` so the transient
204
+ * session row doesn't accumulate on disk.
205
+ *
206
+ * Falls back to a truncated slice on any failure so a caller flowing
207
+ * through a fallback chain still gets *something* — matching the
208
+ * Codex/Gemini summarize contract (return string, never throw).
209
+ */
210
+ async summarize(text) {
211
+ const truncated = text.length > 4096 ? text.slice(0, 4096) : text;
212
+ if (!text.trim()) {
213
+ return text;
214
+ }
215
+ let client;
216
+ try {
217
+ client = await this.serverManager.client();
218
+ }
219
+ catch (err) {
220
+ logger.warn({ err }, "opencode summarize: server unavailable; returning truncated text");
221
+ return truncated;
222
+ }
223
+ const lite = DEFAULT_OPENCODE_LITE_MODEL;
224
+ const liteParts = parseModelComposite(lite);
225
+ if (!liteParts) {
226
+ // Defensive — DEFAULT_OPENCODE_LITE_MODEL ships in `provider/model`
227
+ // form; a future regression that breaks the format should not
228
+ // wedge summarisation, just degrade to the truncation fallback.
229
+ logger.warn({ lite }, "opencode summarize: lite-model id is malformed; falling back to truncation");
230
+ return truncated;
231
+ }
232
+ let sessionId = null;
233
+ try {
234
+ const created = await client.session.create({
235
+ body: { title: `aitne:summarize:${randomUUID().slice(0, 8)}` },
236
+ });
237
+ sessionId = created.data?.id ?? null;
238
+ if (!sessionId) {
239
+ return truncated;
240
+ }
241
+ // Prime the session with the text we want summarised. `noReply`
242
+ // would be ideal but opencode 1.14.50 still requires a model turn
243
+ // before `session.summarize` produces output, so let the model
244
+ // produce a short ack — its body is discarded, only the
245
+ // subsequent summary message is read.
246
+ await client.session.prompt({
247
+ path: { id: sessionId },
248
+ body: {
249
+ model: liteParts,
250
+ parts: [
251
+ {
252
+ type: "text",
253
+ text: `Acknowledge with "ok" and stop. Conversation to summarise next:\n\n${text}`,
254
+ },
255
+ ],
256
+ },
257
+ });
258
+ // V11 contract — flat body, NOT nested under `model`.
259
+ await client.session.summarize({
260
+ path: { id: sessionId },
261
+ body: {
262
+ providerID: liteParts.providerID,
263
+ modelID: liteParts.modelID,
264
+ },
265
+ });
266
+ const messagesResp = await client.session.messages({
267
+ path: { id: sessionId },
268
+ });
269
+ const messages = messagesResp.data ?? [];
270
+ const summaryMsg = [...messages]
271
+ .reverse()
272
+ .find((m) => m.info?.role === "assistant" && m.info?.summary === true);
273
+ if (!summaryMsg) {
274
+ logger.debug({ sessionId }, "opencode summarize: no assistant summary message found");
275
+ return truncated;
276
+ }
277
+ const summaryText = extractAssistantTextFromParts(summaryMsg.parts).trim();
278
+ return summaryText.length > 0 ? summaryText : truncated;
279
+ }
280
+ catch (err) {
281
+ logger.warn({ err, sessionId }, "opencode summarize: SDK call failed; returning truncated text");
282
+ return truncated;
283
+ }
284
+ finally {
285
+ if (sessionId) {
286
+ try {
287
+ await client.session.delete({ path: { id: sessionId } });
288
+ }
289
+ catch (err) {
290
+ logger.debug({ err, sessionId }, "opencode summarize: transient session.delete failed");
291
+ }
292
+ }
293
+ }
294
+ }
295
+ /**
296
+ * Lightweight auth presence check. Mirrors Codex's pattern: prefer a
297
+ * keychain-mirrored env var (`OPENCODE_SERVER_PASSWORD` for remote);
298
+ * for the managed loopback server, the daemon owns the credentials so
299
+ * "configured" is sufficient. The deeper "is the provider key
300
+ * accepted?" check is exercised by `checkAuthDetailed`.
301
+ */
302
+ async checkAuth() {
303
+ try {
304
+ const client = await this.serverManager.client();
305
+ const providersResult = await client.config.providers();
306
+ if (!providersResult.data) {
307
+ return { ok: false, reason: "opencode server did not return providers" };
308
+ }
309
+ const providerCount = providersResult.data.providers.length;
310
+ if (providerCount === 0) {
311
+ return {
312
+ ok: false,
313
+ reason: "No providers configured on the opencode server. Run `opencode auth login` to add one.",
314
+ };
315
+ }
316
+ // Managed loopback ⇒ the daemon owns the server; an external API key
317
+ // is configured on the opencode side, so we surface that as `api_key`.
318
+ return { ok: true, method: "api_key" };
319
+ }
320
+ catch (err) {
321
+ return {
322
+ ok: false,
323
+ reason: err instanceof Error ? err.message : "opencode auth probe failed",
324
+ };
325
+ }
326
+ }
327
+ async checkAuthDetailed() {
328
+ const inner = await this.checkAuth();
329
+ if (inner.ok) {
330
+ return { ok: true, status: "ok", method: inner.method };
331
+ }
332
+ return {
333
+ ok: false,
334
+ status: "expired",
335
+ method: defaultApiKeyProvider(this.backendId) === "opencode-server"
336
+ ? "api_key"
337
+ : "cli_login",
338
+ detail: inner.reason,
339
+ recoveryCommand: "opencode auth login",
340
+ };
341
+ }
342
+ /**
343
+ * Phase 5 §4.11 — opencode does NOT expose namespaced `mcp__*` tool
344
+ * names through `client.tool.ids()` in 1.14.50 (V7 fixture confirms).
345
+ * Live probe is therefore unsupported on this backend in v1; the
346
+ * route surfaces this as a 501 with a targeted message. Phase 5+ may
347
+ * unlock once `mcp.status()`/`tool.list()` surfaces MCP tools.
348
+ */
349
+ async probeTools() {
350
+ throw new LiveProbeUnsupportedError(this.backendId, "opencode 1.14.50 does not surface MCP tool ids via tool.ids()");
351
+ }
352
+ /**
353
+ * Static-registry first; Phase 6 (§6.6.2 wizard probe) extends this
354
+ * via `serverManager.listModels()` once the live-server enumeration
355
+ * lands. Today the registry seed is sufficient for `BackendRouter`
356
+ * bindings.
357
+ */
358
+ listModels() {
359
+ return getModelsForBackend(this.backendId);
360
+ }
361
+ /**
362
+ * Live model enumeration over `client.config.providers()`. Returns the
363
+ * full catalogue an operator can pick from — every provider the live
364
+ * opencode server has credentials for, with capability + cost metadata.
365
+ * Cached in-memory for 5 minutes so the picker doesn't re-bounce the
366
+ * server on every keystroke.
367
+ *
368
+ * Security: the raw `client.config.providers()` response includes
369
+ * `provider.key` (plaintext API key). This method NEVER returns that
370
+ * field — the caller surfaces what's projected here, not the raw SDK
371
+ * response.
372
+ */
373
+ async listLiveModels(options = {}) {
374
+ const now = Date.now();
375
+ const cached = this.liveModelsCache;
376
+ if (!options.forceRefresh
377
+ && cached
378
+ && now - cached.fetchedAtMs < LIVE_MODELS_CACHE_TTL_MS) {
379
+ return { ...cached.payload, cached: true };
380
+ }
381
+ // Coalesce: if a fetch is already in flight, share its result. This
382
+ // applies to refresh=true as well — getting the freshest in-flight
383
+ // result is strictly better than spawning a redundant SDK call.
384
+ if (this.liveModelsInflight) {
385
+ return this.liveModelsInflight;
386
+ }
387
+ this.liveModelsInflight = this.fetchLiveModels(now);
388
+ try {
389
+ return await this.liveModelsInflight;
390
+ }
391
+ finally {
392
+ this.liveModelsInflight = null;
393
+ }
394
+ }
395
+ async fetchLiveModels(requestedAtMs) {
396
+ const client = await this.serverManager.client();
397
+ const res = await client.config.providers();
398
+ const providers = res.data?.providers ?? [];
399
+ const projected = providers.map((p) => ({
400
+ id: p.id,
401
+ name: p.name,
402
+ source: p.source ?? "unknown",
403
+ models: Object.entries(p.models ?? {})
404
+ .filter(([, m]) => m.status !== "deprecated")
405
+ .map(([mid, mRaw]) => {
406
+ const m = mRaw;
407
+ const usdPer1kIn = typeof m.cost?.input === "number" ? m.cost.input / 1000 : null;
408
+ const usdPer1kOut = typeof m.cost?.output === "number" ? m.cost.output / 1000 : null;
409
+ const isFree = (m.cost?.input ?? 0) === 0 && (m.cost?.output ?? 0) === 0;
410
+ return {
411
+ modelId: `${p.id}/${mid}`,
412
+ shortId: mid,
413
+ name: m.name ?? mid,
414
+ family: m.family ?? "",
415
+ tier: inferModelTier(usdPer1kIn, usdPer1kOut, isFree),
416
+ supportsToolUse: m.capabilities?.toolcall === true,
417
+ supportsAttachment: m.capabilities?.attachment === true,
418
+ supportsReasoning: m.capabilities?.reasoning === true,
419
+ maxInputTokens: m.limit?.context ?? null,
420
+ maxOutputTokens: m.limit?.output ?? null,
421
+ usdPer1kIn,
422
+ usdPer1kOut,
423
+ isFree,
424
+ status: m.status ?? "active",
425
+ };
426
+ })
427
+ .sort((a, b) => a.shortId.localeCompare(b.shortId)),
428
+ }));
429
+ const payload = {
430
+ providers: projected,
431
+ fetchedAt: new Date(requestedAtMs).toISOString(),
432
+ cached: false,
433
+ };
434
+ this.liveModelsCache = { fetchedAtMs: requestedAtMs, payload };
435
+ return payload;
436
+ }
437
+ async runDelegatedTool(_params) {
438
+ throw new DelegatedToolUnsupportedError(this.backendId, "use runDelegatedTask (Phase 4)");
439
+ }
440
+ async runDelegatedTask(params) {
441
+ if (this.serverManager.mode === "remote") {
442
+ // docs/design/appendices/opencode-backend.md §5.9 — Remote mode cannot host
443
+ // tight-permission ephemeral servers and the daemon does not own
444
+ // the agent-file write seat. Surface the canonical sentinel so the
445
+ // delegated-task invoker treats this as 501 / `task_mode_unsupported`
446
+ // and the BackendRouter falls back to a Managed backend.
447
+ throw new TaskModeUnsupportedError(this.backendId);
448
+ }
449
+ return this.runDelegatedTaskAgainstEphemeral(params);
450
+ }
451
+ // ── private ──
452
+ async runTurn(params, streamCallbacks) {
453
+ this.assertPromptWithinMaxBudget(params.prompt, params.maxBudgetUsd, params.modelId);
454
+ const resumeSessionId = params.resumeSessionId;
455
+ const isResume = resumeSessionId !== undefined;
456
+ if (isResume && !params.sessionDir) {
457
+ // executeResume already gates this, but be defensive — runTurn is
458
+ // private and may grow new callers.
459
+ throw new Error("runTurn(resumeSessionId): sessionDir is required when resuming");
460
+ }
461
+ const startMs = Date.now();
462
+ const sessionDir = params.sessionDir ?? createSessionWorkdir(this.config.workspaceDir, params.event.type, `${this.config.dataDir}/skills`, {
463
+ backendId: this.backendId,
464
+ ...(params.processKey ? { processKey: params.processKey } : {}),
465
+ character: this.config.character,
466
+ contextDir: getContextDir(this.config),
467
+ // docs/design/appendices/skills-unification.md Phase 4 — feed the conditional
468
+ // manifest predicates. `db` comes through `setMcpContext`; the
469
+ // inbound DM text (when present) lets the *ForDm trigger-phrase
470
+ // fallback drop `gmail-lifestyle` / `managed-tasks` for DMs that
471
+ // have no DB rows AND no trigger phrase.
472
+ ...(this.mcpContext?.db ? { db: this.mcpContext.db } : {}),
473
+ ...(isMessageEvent(params.event) ? { messageText: params.event.content } : {}),
474
+ });
475
+ const ownsSessionDir = !params.sessionDir;
476
+ // NOTE: opencode SDK runs the server in-process (createOpencode does
477
+ // not spawn a subprocess and exposes no per-tool env hook), so the
478
+ // issued token is not yet injected into bash-tool invocations the way
479
+ // Claude / Codex / Gemini do via `buildDaemonApiCliEnv`. The
480
+ // issue/revoke pair stays correct because event-pipeline.ts wires
481
+ // `setReadTokenManager` on all four cores — when an env-injection
482
+ // path lands here, replace this comment with the wiring rather than
483
+ // re-introducing the silent-undefined gap.
484
+ const issuedReadToken = this.readTokenManager?.issue(sessionDir) ?? this.readToken;
485
+ try {
486
+ const { config: runtimeConfig, warnings: configWarnings } = await this.buildRuntimeConfig(params);
487
+ if (configWarnings.length > 0) {
488
+ // Surface translator + MCP-renderer notices so a same-envelope
489
+ // bounce that drops Edit patterns or rejects MCP server names
490
+ // shows up in operator logs, not just buried in the dashboard
491
+ // tile (which materialises later via /health).
492
+ logger.info({ warnings: configWarnings, eventType: params.event.type }, "opencode runtime config emitted with translator warnings");
493
+ }
494
+ await this.serverManager.ensureConfig(runtimeConfig);
495
+ const client = await this.serverManager.client();
496
+ // Pre-flight: a missing provider key is the most common boot-time
497
+ // failure and surfaces as `session.error` mid-stream. Catch it here
498
+ // so the router fails over to a fallback backend rather than
499
+ // streaming a half-complete response.
500
+ const auth = await this.checkAuth();
501
+ if (!auth.ok) {
502
+ throw new BackendDecisiveFailure(this.backendId, "auth", new Error(auth.reason));
503
+ }
504
+ const modelParts = parseModelComposite(params.modelId);
505
+ if (!modelParts) {
506
+ throw new BackendDecisiveFailure(this.backendId, "model_unavailable", new Error(`opencode model id must be in 'provider/model' form, got '${params.modelId}'`));
507
+ }
508
+ // Session lifecycle — opencode keeps sessions on disk; we delete
509
+ // when we own the workdir (transient sessions). `title` is best-
510
+ // effort metadata only. On resume we re-enter an existing session
511
+ // id; `session.create` is skipped entirely.
512
+ let sessionId;
513
+ if (isResume) {
514
+ sessionId = resumeSessionId;
515
+ }
516
+ else {
517
+ const sessionTitle = `aitne:${params.event.type}:${randomUUID().slice(0, 8)}`;
518
+ const created = await client.session.create({
519
+ body: { title: sessionTitle },
520
+ });
521
+ const createdId = created.data?.id ?? null;
522
+ if (!createdId) {
523
+ throw new BackendDecisiveFailure(this.backendId, "other_non_retryable", new Error("opencode session.create returned no id"));
524
+ }
525
+ sessionId = createdId;
526
+ }
527
+ // Resume cost-delta — snapshot the session's cumulative cost +
528
+ // tokens before sending the new prompt. After the turn completes
529
+ // the post-turn `session.get()` aggregate is subtracted from this
530
+ // baseline so `AgentResult.costUsd` reflects this turn's
531
+ // contribution rather than the whole-session total. For first
532
+ // execute these stay zero and the math collapses.
533
+ let preTurnCost = 0;
534
+ let preTurnUsage = { ...EMPTY_USAGE };
535
+ if (isResume) {
536
+ try {
537
+ const preInfo = await client.session.get({ path: { id: sessionId } });
538
+ const preAgg = preInfo.data;
539
+ if (typeof preAgg?.cost === "number") {
540
+ preTurnCost = preAgg.cost;
541
+ }
542
+ if (preAgg?.tokens) {
543
+ preTurnUsage = {
544
+ inputTokens: preAgg.tokens.input ?? 0,
545
+ outputTokens: preAgg.tokens.output ?? 0,
546
+ cacheCreationInputTokens: preAgg.tokens.cache?.write ?? 0,
547
+ cacheReadInputTokens: preAgg.tokens.cache?.read ?? 0,
548
+ };
549
+ }
550
+ }
551
+ catch (err) {
552
+ // 404 / stale session — let session.prompt below surface the
553
+ // real failure with a clean classification. Pre-snapshot
554
+ // failure is a best-effort cost-attribution miss only.
555
+ logger.debug({ err, sessionId }, "opencode resume: pre-turn session.get failed; cost may include prior turns");
556
+ }
557
+ }
558
+ // AbortController owns both the event stream and the prompt call
559
+ // so per-execute timeout collapses them together.
560
+ const abortController = new AbortController();
561
+ const timeoutMs = this.config.executeTimeoutMinutes * 60 * 1000;
562
+ const timeoutId = setTimeout(async () => {
563
+ abortController.abort(new Error("opencode execute wall-clock timeout"));
564
+ try {
565
+ await client.session.abort({ path: { id: sessionId } });
566
+ }
567
+ catch (err) {
568
+ logger.warn({ err, sessionId }, "opencode session abort failed");
569
+ }
570
+ }, timeoutMs);
571
+ // §5.1 ordering invariant — `event.subscribe()` MUST complete before
572
+ // `session.prompt()` is sent, otherwise early `message.part.delta`
573
+ // events are emitted into a not-yet-bound stream and lost. Awaiting
574
+ // the subscription here (rather than letting `consumeEventStream`
575
+ // do it concurrently with the prompt below) collapses the race.
576
+ let subscription;
577
+ try {
578
+ subscription = await client.event.subscribe({
579
+ signal: abortController.signal,
580
+ });
581
+ }
582
+ catch (err) {
583
+ clearTimeout(timeoutId);
584
+ throw new BackendDecisiveFailure(this.backendId, "other_non_retryable", err instanceof Error
585
+ ? err
586
+ : new Error("opencode event.subscribe failed"));
587
+ }
588
+ let streamedAnyText = false;
589
+ const eventConsumer = this.consumeEventStream({
590
+ stream: subscription.stream,
591
+ sessionId,
592
+ signal: abortController.signal,
593
+ onText: (text) => {
594
+ streamedAnyText = true;
595
+ streamCallbacks?.onText?.(text);
596
+ },
597
+ });
598
+ // Resume sends the user's reply verbatim — the system prompt and
599
+ // conversation history are already inside the opencode server
600
+ // session, so wrapping with `buildExecutionPrompt` would re-emit
601
+ // the task-flow framing and confuse the model.
602
+ const renderedPrompt = isResume
603
+ ? params.prompt
604
+ : buildExecutionPrompt(params.prompt, params.context, params.event, params.conversationHistory);
605
+ // docs/design/appendices/opencode-backend.md §4 / Phase 4 — `routine.hourly_check.triage`
606
+ // returns a strict JSON envelope (`{ "action": "log_only" |
607
+ // "escalate", … }`) parsed by `parseStage2Verdict`. opencode's
608
+ // `format: { type: "json_schema", … }` honours the schema with
609
+ // built-in retryCount=2, and the parsed object lands at
610
+ // `info.structured` (V4 contract — NOT in text parts). We
611
+ // stringify it back to text so the dispatcher's existing parser
612
+ // (text-based across all backends) keeps working.
613
+ const structuredFormat = formatForProcessKey(params.processKey);
614
+ try {
615
+ let promptResult;
616
+ try {
617
+ // Cast through OpencodeAugmentedPromptBody — the SDK's body
618
+ // type omits `format` even though opencode 1.14.50 honours it
619
+ // at runtime (V4). Without the cast TypeScript rejects the
620
+ // optional field.
621
+ const promptBody = {
622
+ model: modelParts,
623
+ parts: [{ type: "text", text: renderedPrompt }],
624
+ ...(structuredFormat ? { format: structuredFormat } : {}),
625
+ };
626
+ promptResult = await client.session.prompt({
627
+ path: { id: sessionId },
628
+ body: promptBody,
629
+ });
630
+ }
631
+ catch (promptErr) {
632
+ // Fast-fail (network error, transport-level failure): without
633
+ // this branch the consumer would await `session.idle` for the
634
+ // full `executeTimeoutMinutes` because the server never started
635
+ // the turn. Abort the subscribe stream so the for-await loop
636
+ // exits and the consumer's promise resolves; THEN drain so any
637
+ // already-buffered tool calls are still observed before we
638
+ // re-throw.
639
+ abortController.abort(promptErr instanceof Error
640
+ ? promptErr
641
+ : new Error("opencode session.prompt failed"));
642
+ await eventConsumer.catch(() => undefined);
643
+ throw promptErr;
644
+ }
645
+ // The stream usually terminates on `session.idle` before the
646
+ // prompt resolves, but in race orderings the prompt response can
647
+ // arrive first. Wait for the stream to drain so we observe every
648
+ // tool call.
649
+ const collected = await eventConsumer;
650
+ clearTimeout(timeoutId);
651
+ // Distinguish a clean abort from a server-side error or successful
652
+ // turn. opencode signals abort via `info.error.name`, not a
653
+ // dedicated event.
654
+ const assistantMessage = promptResult.data?.info;
655
+ if (assistantMessage?.error?.name === "MessageAbortedError"
656
+ || isMessageAborted(collected.terminalEvent)) {
657
+ throw new BackendDecisiveFailure(this.backendId, "timeout", new Error("opencode session aborted (likely wall-clock timeout)"));
658
+ }
659
+ if (assistantMessage?.error) {
660
+ throw classifyAssistantError(assistantMessage.error, this.backendId);
661
+ }
662
+ if (collected.terminalEvent?.kind === "session_error") {
663
+ throw classifyStreamError(collected.terminalEvent.error, this.backendId);
664
+ }
665
+ const partsForExtraction = promptResult.data?.parts ?? [];
666
+ // V4 — when `format.type === "json_schema"` was sent, the
667
+ // validated parsed object lives at `info.structured`, NOT in
668
+ // the text parts (which are empty in that mode). Stringify it
669
+ // back to JSON so the dispatcher's existing text-based parser
670
+ // (`parseStage2Verdict`) sees a clean envelope. A missing or
671
+ // null `structured` after a json_schema request is a terminal
672
+ // failure — opencode tried `retryCount=2` server-side and
673
+ // still couldn't satisfy the schema, so escalating to the
674
+ // model again from our side would just burn budget.
675
+ let structuredText = null;
676
+ if (structuredFormat) {
677
+ const structured = assistantMessage?.structured;
678
+ if (structured == null) {
679
+ throw new BackendDecisiveFailure(this.backendId, "other_non_retryable", new Error("opencode json_schema turn returned no structured payload (info.structured was null)"));
680
+ }
681
+ structuredText = JSON.stringify(structured);
682
+ }
683
+ const finalText = structuredText
684
+ ?? (extractAssistantTextFromParts(partsForExtraction).trim()
685
+ || collected.streamedText.trim());
686
+ // Tool-call audit — Phase 3 wires AgentWriteTracker via the tool
687
+ // extraction path. Phase 2 surfaces the structured list so the
688
+ // dispatcher can decide whether the agent touched files.
689
+ const tools = extractToolUsesFromParts(partsForExtraction);
690
+ recordAgentWritesFromTools(tools, this.writeTracker);
691
+ // EXECUTION-MODE-DESIGN.md §6.3 / OPENCODE_BACKEND_DESIGN §5.8
692
+ // synthetic absolute-block audit. opencode has no PreToolUse hook;
693
+ // each tool call surfaced in the final response is run through the
694
+ // classifier so an `agent_actions.blocked_absolute` row is written
695
+ // with `result='partial'` on a hit. The underlying enforcement
696
+ // happens server-side via the permission JSON — this row is
697
+ // observability only.
698
+ auditOpencodeTools(tools, {
699
+ db: this.mcpContext?.db,
700
+ mode: this.config.opencodeExecutionPermissionMode,
701
+ sessionId: params.sessionDbId ?? null,
702
+ });
703
+ // Cost / token aggregation — V11: `client.session.get()` exposes
704
+ // session-level aggregates that avoid summing per-message rows.
705
+ // Default tag is "sdk" because the primary path is the SDK's
706
+ // typed `info.cost`. Free-tier reconciliation below may override
707
+ // to "litellm" or "hardcoded" depending on the registry source.
708
+ let costSourceTag = "sdk";
709
+ let costUsd = assistantMessage?.cost ?? 0;
710
+ let usage = assistantMessage
711
+ ? assistantMessageToUsage(assistantMessage)
712
+ : { ...EMPTY_USAGE };
713
+ try {
714
+ const sessionInfo = await client.session.get({
715
+ path: { id: sessionId },
716
+ });
717
+ const sessAgg = sessionInfo.data;
718
+ if (sessAgg?.tokens) {
719
+ // Subtract the pre-turn snapshot so resume usage reflects
720
+ // this turn only. Clamp to zero in case opencode reports a
721
+ // non-monotonic counter — never charge negative tokens.
722
+ usage = {
723
+ inputTokens: Math.max(0, (sessAgg.tokens.input ?? 0) - preTurnUsage.inputTokens),
724
+ outputTokens: Math.max(0, (sessAgg.tokens.output ?? 0) - preTurnUsage.outputTokens),
725
+ cacheCreationInputTokens: Math.max(0, (sessAgg.tokens.cache?.write ?? 0)
726
+ - preTurnUsage.cacheCreationInputTokens),
727
+ cacheReadInputTokens: Math.max(0, (sessAgg.tokens.cache?.read ?? 0)
728
+ - preTurnUsage.cacheReadInputTokens),
729
+ };
730
+ }
731
+ if (typeof sessAgg?.cost === "number") {
732
+ costUsd = Math.max(0, sessAgg.cost - preTurnCost);
733
+ }
734
+ }
735
+ catch (err) {
736
+ logger.debug({ err, sessionId }, "opencode session.get failed; falling back to assistant-message tokens");
737
+ }
738
+ // Free-tier reconciliation (§5.7 layer 2): when opencode reports
739
+ // cost=0 but tokens flowed, fall back to registry pricing so the
740
+ // dashboard sees a non-zero cost figure for budgeting.
741
+ if (costUsd === 0
742
+ && usage.inputTokens + usage.outputTokens > 0) {
743
+ const estimate = this.priceFetcher.estimateUsageCost({
744
+ backendId: this.backendId,
745
+ modelId: params.modelId,
746
+ usage,
747
+ fallbackModel: findRegisteredModel(this.backendId, params.modelId),
748
+ });
749
+ costUsd = estimate.costUsd;
750
+ // Preserve `litellm` vs `hardcoded` provenance verbatim — see
751
+ // CodexCore's same pattern (`codex-core.ts:761`). Earlier rev
752
+ // collapsed `litellm` into `sdk`, hiding which estimates came
753
+ // from the LiteLLM cache vs the registry fallback.
754
+ costSourceTag = estimate.costSource;
755
+ }
756
+ this.assertWithinMaxBudget(costUsd, params.maxBudgetUsd, params.modelId);
757
+ if (finalText && !streamedAnyText) {
758
+ streamCallbacks?.onText?.(finalText);
759
+ }
760
+ const durationMs = Date.now() - startMs;
761
+ const stopReason = assistantMessage?.finish ?? null;
762
+ // Best-effort cleanup of the on-disk opencode session row when we
763
+ // own the workdir — keeps disk usage bounded under hourly_check.
764
+ // On resume we must never delete: the dispatcher's next turn
765
+ // needs the same session id to resolve to a live server-side
766
+ // history. (`ownsSessionDir` is already false when sessionDir is
767
+ // provided, but the explicit isResume guard is defense-in-depth
768
+ // against a future caller passing a transient workdir on resume.)
769
+ if (ownsSessionDir && !isResume) {
770
+ try {
771
+ await client.session.delete({ path: { id: sessionId } });
772
+ }
773
+ catch (err) {
774
+ logger.debug({ err, sessionId }, "opencode session.delete failed");
775
+ }
776
+ }
777
+ logger.info({
778
+ eventType: params.event.type,
779
+ model: params.modelId,
780
+ durationMs,
781
+ costUsd,
782
+ sessionId,
783
+ }, "opencode execute completed");
784
+ const actualModelId = assistantMessage?.providerID && assistantMessage?.modelID
785
+ ? `${assistantMessage.providerID}/${assistantMessage.modelID}`
786
+ : params.modelId;
787
+ return {
788
+ output: finalText,
789
+ sessionId,
790
+ backendId: this.backendId,
791
+ modelId: actualModelId,
792
+ costSource: costSourceTag,
793
+ costUsd,
794
+ usage,
795
+ modelUsage: usage.inputTokens || usage.outputTokens
796
+ ? {
797
+ [actualModelId]: {
798
+ inputTokens: usage.inputTokens,
799
+ outputTokens: usage.outputTokens,
800
+ costUsd,
801
+ },
802
+ }
803
+ : {},
804
+ numTurns: 1,
805
+ durationMs,
806
+ durationApiMs: durationMs,
807
+ model: actualModelId,
808
+ isError: false,
809
+ stopReason,
810
+ contextUpdated: false,
811
+ };
812
+ }
813
+ finally {
814
+ clearTimeout(timeoutId);
815
+ }
816
+ }
817
+ finally {
818
+ streamCallbacks?.onEnd?.();
819
+ if (ownsSessionDir) {
820
+ if (issuedReadToken) {
821
+ this.readTokenManager?.revoke(sessionDir);
822
+ }
823
+ cleanupSessionWorkdir(sessionDir);
824
+ }
825
+ }
826
+ }
827
+ /**
828
+ * docs/design/appendices/opencode-backend.md §5.9 / Phase 4 — opencode delegated-task
829
+ * execution.
830
+ *
831
+ * Implementation choice (v1): spawn an isolated ephemeral server
832
+ * with tight permission JSON for each delegated call. The design's
833
+ * preferred default is Path B (long-lived primary + per-agent
834
+ * permission frontmatter written mid-session), but Phase 0's V5
835
+ * fixture left the agent-file hot-reload behaviour an open question
836
+ * (§5.9 "Open item" — agent file was pre-created in V5). spawnEphemeral
837
+ * sidesteps the hot-reload concern entirely with a deterministic
838
+ * ~900 ms p50 spawn cost (V6 measurement); volume on delegated paths
839
+ * is low enough that the cost-isolation trade-off favours
840
+ * predictability. The router's `params.isolation === "ephemeral"`
841
+ * intent is the same as our default here, so callers that explicitly
842
+ * request ephemeral isolation get exactly what they asked for.
843
+ *
844
+ * Path B (long-lived primary + per-agent file reuse) can land
845
+ * incrementally once V5's open question is resolved against a real
846
+ * opencode server — see §5.9 Phase 3-blocked checklist row.
847
+ *
848
+ * Per-call envelope:
849
+ * - Tight `permission` JSON: every triple-keyed write tool
850
+ * (`edit`, `bash`, `webfetch`, `doom_loop`, `external_directory`)
851
+ * defaults to `deny`. The absolute-block layer's bash
852
+ * pattern-map merges on top so destructive shapes stay denied.
853
+ * - `tools.task: false` (V8 — kill subagent spawning) plus
854
+ * `tools.read: false` and `tools.write: false` for the
855
+ * non-MCP read/write tools (no permission triple exists for
856
+ * `read`; hard-disable is the only opencode 1.14.50 surface).
857
+ * - MCP map carries only the integration's connectors per
858
+ * `setMcpContext`; per-tool MCP deny is server-level only in v1
859
+ * (§5.6 v1 strategy) — disallowed connectors are simply not
860
+ * materialised.
861
+ * - `model` set to `params.modelId`.
862
+ *
863
+ * Stream pre-emption: opencode 1.14.50 does NOT emit
864
+ * `message.part.updated` events during a turn (V9 — see §5.3), so
865
+ * tool-call data is only available from the FINAL `session.prompt`
866
+ * response. The pre-emption check therefore happens AFTER the turn
867
+ * completes — `policy_violation` / `loop_aborted` classifications
868
+ * still surface, but the subprocess has already executed any
869
+ * out-of-envelope MCP call by the time we see it. Mitigation:
870
+ * 1. Server-level permission denies non-MCP write tools at
871
+ * enforcement time (the model receives a tool error and stops).
872
+ * 2. The MCP map only carries the integration's allowed
873
+ * connectors, so non-allowed MCP tools are unreachable.
874
+ * 3. The post-turn pre-emption check is the audit layer — it
875
+ * classifies whether the turn violated policy for telemetry.
876
+ * If opencode begins emitting streamed tool-call events, this method
877
+ * migrates to the live pre-emption pattern Codex uses; the post-
878
+ * extraction check stays as the back-stop.
879
+ */
880
+ async runDelegatedTaskAgainstEphemeral(params) {
881
+ const startMs = Date.now();
882
+ const trace = [];
883
+ let writeClassToolFired = false;
884
+ const modelParts = parseModelComposite(params.modelId);
885
+ if (!modelParts) {
886
+ return {
887
+ ok: false,
888
+ errorClass: "subprocess_crashed",
889
+ message: `opencode model id must be in 'provider/model' form, got '${params.modelId}'`,
890
+ cost: withDurationMs(emptyCost(), startMs),
891
+ trace,
892
+ writeClassToolFired,
893
+ };
894
+ }
895
+ // Pre-flight auth gate — same chokepoint runTurn uses; spares us a
896
+ // server spawn on the obvious-401 path.
897
+ const auth = await this.checkAuth();
898
+ if (!auth.ok) {
899
+ return {
900
+ ok: false,
901
+ errorClass: "auth_error",
902
+ message: auth.reason,
903
+ cost: withDurationMs(emptyCost(), startMs),
904
+ trace,
905
+ writeClassToolFired,
906
+ };
907
+ }
908
+ // Build the tight runtime config envelope. Reuses the regular
909
+ // builder so absolute-block + per-session disallowed merges land
910
+ // identically; the per-task tightening goes on top via
911
+ // `extraHardDisable` (`read`/`write` off) and a tightened
912
+ // permission override (every triple key denied).
913
+ const mcpRender = await this.materializeMcp(undefined);
914
+ const built = buildOpencodeRuntimeConfig({
915
+ modelId: params.modelId,
916
+ executionMode: "strict",
917
+ disallowedTools: this.config.disallowedTools ?? [],
918
+ allowedToolsOverride: null,
919
+ mcpDisallowed: mcpRender.mcpDisallowed,
920
+ mcp: mcpRender.mcp,
921
+ defensiveInstructions: defensiveInstructionsFromEnv(),
922
+ extraHardDisable: { read: false, write: false },
923
+ });
924
+ const tightConfig = {
925
+ ...built.config,
926
+ permission: {
927
+ // Triple-keyed denies — the bash pattern-map from absolute-block
928
+ // already merged into built.config.permission.bash, so taking
929
+ // its value here preserves the wildcards. The other four flip
930
+ // to wholesale "deny" because no per-task narrowing applies.
931
+ ...(built.config.permission ?? {}),
932
+ edit: "deny",
933
+ webfetch: "deny",
934
+ doom_loop: "deny",
935
+ external_directory: "deny",
936
+ },
937
+ };
938
+ let handle = null;
939
+ try {
940
+ handle = await this.serverManager.spawnEphemeral(tightConfig);
941
+ }
942
+ catch (err) {
943
+ return {
944
+ ok: false,
945
+ errorClass: "subprocess_crashed",
946
+ message: err instanceof Error
947
+ ? `opencode ephemeral server spawn failed: ${err.message}`
948
+ : "opencode ephemeral server spawn failed",
949
+ cost: withDurationMs(emptyCost(), startMs),
950
+ trace,
951
+ writeClassToolFired,
952
+ };
953
+ }
954
+ const aborter = new AbortController();
955
+ const callerListener = () => {
956
+ aborter.abort(params.abortSignal?.reason);
957
+ };
958
+ if (params.abortSignal) {
959
+ if (params.abortSignal.aborted) {
960
+ aborter.abort(params.abortSignal.reason);
961
+ }
962
+ else {
963
+ params.abortSignal.addEventListener("abort", callerListener, {
964
+ once: true,
965
+ });
966
+ }
967
+ }
968
+ const localTimeout = setTimeout(() => aborter.abort(new Error("opencode delegated task local timeout")), params.timeoutMs + 60_000);
969
+ let sessionId = null;
970
+ try {
971
+ const created = await handle.client.session.create({
972
+ body: {
973
+ title: `aitne:delegated:${randomUUID().slice(0, 8)}`,
974
+ },
975
+ });
976
+ sessionId = created.data?.id ?? null;
977
+ if (!sessionId) {
978
+ return {
979
+ ok: false,
980
+ errorClass: "subprocess_crashed",
981
+ message: "opencode session.create returned no id",
982
+ cost: withDurationMs(emptyCost(), startMs),
983
+ trace,
984
+ writeClassToolFired,
985
+ };
986
+ }
987
+ const promptResult = await handle.client.session.prompt({
988
+ path: { id: sessionId },
989
+ body: {
990
+ model: modelParts,
991
+ parts: [{ type: "text", text: params.systemPrompt }],
992
+ },
993
+ });
994
+ if (aborter.signal.aborted) {
995
+ const errorClass = classifyAbortReason(params.abortSignal?.reason ?? aborter.signal.reason);
996
+ return {
997
+ ok: false,
998
+ errorClass,
999
+ message: errorClass === "timeout"
1000
+ ? "opencode delegated task timed out (wall-clock)"
1001
+ : "opencode delegated task cancelled",
1002
+ cost: withDurationMs(emptyCost(), startMs),
1003
+ trace,
1004
+ writeClassToolFired,
1005
+ };
1006
+ }
1007
+ const assistantMessage = promptResult.data?.info;
1008
+ if (assistantMessage?.error?.name === "MessageAbortedError") {
1009
+ return {
1010
+ ok: false,
1011
+ errorClass: "timeout",
1012
+ message: "opencode delegated session aborted",
1013
+ cost: withDurationMs(emptyCost(), startMs),
1014
+ trace,
1015
+ writeClassToolFired,
1016
+ };
1017
+ }
1018
+ if (assistantMessage?.error) {
1019
+ return {
1020
+ ok: false,
1021
+ errorClass: "tool_failed",
1022
+ message: `opencode assistant error: ${assistantMessage.error.name}`,
1023
+ cost: withDurationMs(emptyCost(), startMs),
1024
+ trace,
1025
+ writeClassToolFired,
1026
+ };
1027
+ }
1028
+ const partsRaw = promptResult.data?.parts ?? [];
1029
+ const finalText = extractAssistantTextFromParts(partsRaw).trim();
1030
+ const toolUses = extractToolUsesFromParts(partsRaw);
1031
+ // Post-extraction policy gate. opencode 1.14.50 cannot stream
1032
+ // tool calls (V9 — see method-level comment), so the check is
1033
+ // best-effort audit + classification, not real-time pre-emption.
1034
+ let policyViolationTool = null;
1035
+ let toolCallCount = 0;
1036
+ const isAllowedTool = (name) => params.allowedTools.some((pattern) => matchRunAllowedToolPattern(pattern, name));
1037
+ const destructiveSet = params.allowDestructive
1038
+ ? new Set()
1039
+ : new Set(params.destructiveTools);
1040
+ const writeClassMatcher = (name) => params.writeClassTools.some((pattern) => matchRunAllowedToolPattern(pattern, name));
1041
+ for (const tool of toolUses) {
1042
+ if (tool.status === "pending" || tool.status === "running")
1043
+ continue;
1044
+ toolCallCount += 1;
1045
+ // opencode 1.14.50 surfaces opencode-built-in tool names without
1046
+ // the `mcp__server__` prefix (the SDK v1 limitation §5.6 v2 will
1047
+ // address). For the allowedTools/destructive comparison we
1048
+ // inspect the verbatim name first, falling back to a synthesised
1049
+ // prefix when the integration key is known. Either way the audit
1050
+ // remains accurate for the same-name match.
1051
+ const fullName = tool.toolName;
1052
+ if (!isAllowedTool(fullName) || destructiveSet.has(fullName)) {
1053
+ policyViolationTool = fullName;
1054
+ }
1055
+ if (writeClassMatcher(fullName)) {
1056
+ writeClassToolFired = true;
1057
+ }
1058
+ const step = {
1059
+ toolName: fullName,
1060
+ toolArgs: tool.input,
1061
+ durationMs: tool.durationMs ?? 0,
1062
+ status: tool.status === "completed" ? "ok" : "error",
1063
+ costUsd: null,
1064
+ tokensInput: null,
1065
+ tokensOutput: null,
1066
+ };
1067
+ if (tool.output !== undefined) {
1068
+ try {
1069
+ step.toolResult = JSON.parse(tool.output);
1070
+ }
1071
+ catch {
1072
+ step.toolResult = tool.output;
1073
+ }
1074
+ }
1075
+ trace.push(step);
1076
+ params.onToolStep?.(step);
1077
+ }
1078
+ // Synthetic absolute-block audit on tool calls — same chokepoint
1079
+ // runTurn uses for execute() (§5.8). Records `blocked_absolute`
1080
+ // rows when a tool target matches the absolute-block layer; the
1081
+ // permission JSON denied the call server-side, this row is
1082
+ // observability only.
1083
+ auditOpencodeTools(toolUses, {
1084
+ db: this.mcpContext?.db,
1085
+ mode: "strict",
1086
+ sessionId: null,
1087
+ });
1088
+ const usage = assistantMessage
1089
+ ? {
1090
+ inputTokens: assistantMessage.tokens?.input ?? 0,
1091
+ outputTokens: assistantMessage.tokens?.output ?? 0,
1092
+ cacheCreationInputTokens: assistantMessage.tokens?.cache?.write ?? 0,
1093
+ cacheReadInputTokens: assistantMessage.tokens?.cache?.read ?? 0,
1094
+ }
1095
+ : { ...EMPTY_USAGE };
1096
+ const costUsd = assistantMessage?.cost ?? 0;
1097
+ const cost = withDurationMs({
1098
+ tokensInput: usage.inputTokens,
1099
+ tokensOutput: usage.outputTokens,
1100
+ cacheCreationTokens: usage.cacheCreationInputTokens,
1101
+ cacheReadTokens: usage.cacheReadInputTokens,
1102
+ costUsd,
1103
+ durationMs: 0,
1104
+ numTurns: 1,
1105
+ }, startMs);
1106
+ // Best-effort cleanup of the on-disk session row. Failure is
1107
+ // benign — the ephemeral server itself will be torn down in
1108
+ // `finally` and its disk state goes with it.
1109
+ try {
1110
+ await handle.client.session.delete({ path: { id: sessionId } });
1111
+ }
1112
+ catch (err) {
1113
+ logger.debug({ err, sessionId }, "opencode delegated session.delete failed");
1114
+ }
1115
+ if (policyViolationTool) {
1116
+ return {
1117
+ ok: false,
1118
+ errorClass: "policy_violation",
1119
+ message: `opencode invoked '${policyViolationTool}' which is outside the per-task allowlist`,
1120
+ rawAssistantText: finalText,
1121
+ cost,
1122
+ trace,
1123
+ writeClassToolFired,
1124
+ };
1125
+ }
1126
+ if (toolCallCount > params.maxToolCalls) {
1127
+ return {
1128
+ ok: false,
1129
+ errorClass: "loop_aborted",
1130
+ message: `opencode exceeded maxToolCalls=${params.maxToolCalls} (observed=${toolCallCount})`,
1131
+ rawAssistantText: finalText,
1132
+ cost,
1133
+ trace,
1134
+ writeClassToolFired,
1135
+ };
1136
+ }
1137
+ if (finalText.length === 0) {
1138
+ return {
1139
+ ok: false,
1140
+ errorClass: "parse_error",
1141
+ message: "opencode assistant produced empty text",
1142
+ rawAssistantText: finalText,
1143
+ cost,
1144
+ trace,
1145
+ writeClassToolFired,
1146
+ };
1147
+ }
1148
+ return {
1149
+ ok: true,
1150
+ rawAssistantText: finalText,
1151
+ cost,
1152
+ trace,
1153
+ writeClassToolFired,
1154
+ };
1155
+ }
1156
+ catch (err) {
1157
+ const message = err instanceof Error ? err.message : String(err);
1158
+ const cost = withDurationMs(emptyCost(), startMs);
1159
+ if (aborter.signal.aborted || params.abortSignal?.aborted) {
1160
+ return {
1161
+ ok: false,
1162
+ errorClass: classifyAbortReason(params.abortSignal?.reason ?? aborter.signal.reason),
1163
+ message,
1164
+ cost,
1165
+ trace,
1166
+ writeClassToolFired,
1167
+ };
1168
+ }
1169
+ return {
1170
+ ok: false,
1171
+ errorClass: "subprocess_crashed",
1172
+ message,
1173
+ cost,
1174
+ trace,
1175
+ writeClassToolFired,
1176
+ };
1177
+ }
1178
+ finally {
1179
+ clearTimeout(localTimeout);
1180
+ if (params.abortSignal && callerListener) {
1181
+ params.abortSignal.removeEventListener("abort", callerListener);
1182
+ }
1183
+ if (handle) {
1184
+ try {
1185
+ await handle.close();
1186
+ }
1187
+ catch (err) {
1188
+ logger.debug({ err }, "opencode delegated ephemeral close threw");
1189
+ }
1190
+ }
1191
+ }
1192
+ }
1193
+ /**
1194
+ * OPENCODE_BACKEND_DESIGN §5.1 / §5.8 — assemble the full per-session
1195
+ * `OpencodeRuntimeConfig` envelope. Async because MCP materialization
1196
+ * resolves secrets via the encrypted blob store.
1197
+ *
1198
+ * Self-contained: every field is set explicitly (or deliberately
1199
+ * omitted), so the server-manager's bounce hash is stable across
1200
+ * same-envelope turns. The §5.1 invariant.
1201
+ */
1202
+ async buildRuntimeConfig(params) {
1203
+ const mcpRender = await this.materializeMcp(params.processKey);
1204
+ const built = buildOpencodeRuntimeConfig({
1205
+ modelId: params.modelId,
1206
+ executionMode: this.config.opencodeExecutionPermissionMode,
1207
+ disallowedTools: this.config.disallowedTools ?? [],
1208
+ allowedToolsOverride: params.allowedToolsOverride ?? this.config.allowedToolsOverride ?? null,
1209
+ mcpDisallowed: mcpRender.mcpDisallowed,
1210
+ mcp: mcpRender.mcp,
1211
+ defensiveInstructions: defensiveInstructionsFromEnv(),
1212
+ });
1213
+ // MCP renderer warnings (server-name lint failures, missing
1214
+ // command/url) must flow through so the dashboard's runtime-config
1215
+ // preview tile sees them alongside permission-translator warnings —
1216
+ // the §5.6 contract says warnings are "collected from both
1217
+ // translators". Without this concatenation they only land in
1218
+ // `logger.warn`, invisible to operators not tailing daemon logs.
1219
+ return {
1220
+ config: built.config,
1221
+ warnings: [...built.warnings, ...mcpRender.warnings],
1222
+ };
1223
+ }
1224
+ /**
1225
+ * Per-session MCP materialization. Unlike Claude / Codex / Gemini —
1226
+ * which write a backend-specific file under `<sessionDir>/.mcp.json`
1227
+ * or `.codex/config.toml` — OpenCode receives MCP servers inline via
1228
+ * `OPENCODE_CONFIG_CONTENT` (§5.1). The daemon therefore renders
1229
+ * directly into a `Record<string, McpLocalConfig | McpRemoteConfig>`
1230
+ * map and feeds it into `buildOpencodeRuntimeConfig`.
1231
+ *
1232
+ * Returns the rendered map + the autonomous-strip disallowedTools so
1233
+ * the config builder can surface those as MCP-server warnings
1234
+ * (per-tool MCP deny isn't expressible in opencode 1.14.50 — §5.6).
1235
+ */
1236
+ async materializeMcp(processKey) {
1237
+ if (!this.mcpContext) {
1238
+ return { mcp: {}, mcpDisallowed: [], warnings: [] };
1239
+ }
1240
+ const allServers = listMcpServers(this.mcpContext.db);
1241
+ const forBackend = allServers.filter((s) => s.enabled && s.backends.includes(this.backendId));
1242
+ if (forBackend.length === 0) {
1243
+ return { mcp: {}, mcpDisallowed: [], warnings: [] };
1244
+ }
1245
+ // Resolve secrets the same way materializeMcpForSession does — keyed
1246
+ // by `<serverId>:<keyName>` so the renderer can pick the subset it
1247
+ // needs without collisions across servers.
1248
+ const scopedSecrets = {};
1249
+ for (const server of forBackend) {
1250
+ const raw = await resolveMcpSecrets(this.mcpContext.blobStore, server);
1251
+ for (const [keyName, value] of Object.entries(raw)) {
1252
+ if (value == null)
1253
+ continue;
1254
+ scopedSecrets[`${server.id}:${keyName}`] = value;
1255
+ }
1256
+ }
1257
+ // Allow mode bypasses the approve-tier strip — mirroring the Codex
1258
+ // / Gemini wiring (see codex-core.ts:230).
1259
+ const allowMode = this.config.opencodeExecutionPermissionMode === "allow";
1260
+ const autonomous = !allowMode && (processKey ? isAutonomousProcessKey(processKey) : false);
1261
+ const mcpDisallowed = buildMcpDisallowedTools({
1262
+ servers: forBackend,
1263
+ autonomous,
1264
+ });
1265
+ const rendered = renderOpencodeMcp({
1266
+ servers: forBackend,
1267
+ secrets: scopedSecrets,
1268
+ });
1269
+ if (rendered.warnings.length > 0) {
1270
+ logger.warn({ warnings: rendered.warnings, contextDir: getContextDir(this.config) }, "opencode-mcp renderer surfaced warnings");
1271
+ }
1272
+ return { mcp: rendered.mcp, mcpDisallowed, warnings: rendered.warnings };
1273
+ }
1274
+ async consumeEventStream(args) {
1275
+ const { stream, sessionId, signal, onText } = args;
1276
+ let streamedText = "";
1277
+ let terminalEvent;
1278
+ try {
1279
+ for await (const rawEvent of stream) {
1280
+ if (signal.aborted)
1281
+ break;
1282
+ const normalized = normalize(rawEvent);
1283
+ // Filter events that don't belong to our session — `server.heartbeat`
1284
+ // and `server.connected` carry no sessionId, but other typed
1285
+ // events do; drop foreign sessions to avoid cross-talk on a
1286
+ // long-lived server.
1287
+ const eventSessionId = "sessionId" in normalized ? normalized.sessionId : "";
1288
+ if (eventSessionId && eventSessionId !== sessionId)
1289
+ continue;
1290
+ if (normalized.kind === "text_delta" && normalized.field === "text") {
1291
+ // Only the `text` field is user-visible final output. opencode
1292
+ // also emits `field: "reasoning"` (and other non-text part
1293
+ // fields) — they must not reach `onText` (chat bubble) and
1294
+ // must not pollute `streamedText`, which is the
1295
+ // `extractAssistantTextFromParts` fallback used when the parts
1296
+ // array carries no `type: "text"` entries.
1297
+ streamedText += normalized.delta;
1298
+ if (normalized.delta) {
1299
+ onText(normalized.delta);
1300
+ }
1301
+ }
1302
+ if (isTerminal(normalized)) {
1303
+ terminalEvent = normalized;
1304
+ break;
1305
+ }
1306
+ }
1307
+ }
1308
+ catch (err) {
1309
+ if (signal.aborted) {
1310
+ // Caller surfaces the timeout (or the prompt fast-fail) via the
1311
+ // abort path; don't escalate the iterator's AbortError to a warn.
1312
+ return { streamedText, terminalEvent };
1313
+ }
1314
+ logger.warn({ err, sessionId }, "opencode event-stream consumption error");
1315
+ }
1316
+ return { streamedText, terminalEvent };
1317
+ }
1318
+ assertPromptWithinMaxBudget(prompt, maxBudgetUsd, modelId) {
1319
+ if (!maxBudgetUsd)
1320
+ return;
1321
+ // Same envelope check Codex uses — defensive only; opencode also
1322
+ // enforces a context limit server-side.
1323
+ const approxInputTokens = Math.ceil(prompt.length / 3.5);
1324
+ const fallback = findRegisteredModel(this.backendId, modelId);
1325
+ if (!fallback?.usdPer1kIn)
1326
+ return;
1327
+ const projected = (approxInputTokens / 1000) * fallback.usdPer1kIn;
1328
+ if (projected > maxBudgetUsd * 2) {
1329
+ throw new BackendDecisiveFailure(this.backendId, "model_unavailable", new Error(`Prompt projected at $${projected.toFixed(4)} exceeds 2× budget cap of $${maxBudgetUsd.toFixed(4)}`));
1330
+ }
1331
+ }
1332
+ assertWithinMaxBudget(costUsd, maxBudgetUsd, modelId) {
1333
+ if (!maxBudgetUsd)
1334
+ return;
1335
+ if (costUsd > maxBudgetUsd) {
1336
+ throw new BackendDecisiveFailure(this.backendId, "model_unavailable", new Error(`opencode execute cost $${costUsd.toFixed(4)} exceeds budget $${maxBudgetUsd.toFixed(4)} (${modelId})`));
1337
+ }
1338
+ }
1339
+ }
1340
+ function assistantMessageToUsage(msg) {
1341
+ const tokens = msg.tokens;
1342
+ return {
1343
+ inputTokens: tokens?.input ?? 0,
1344
+ outputTokens: tokens?.output ?? 0,
1345
+ cacheCreationInputTokens: tokens?.cache?.write ?? 0,
1346
+ cacheReadInputTokens: tokens?.cache?.read ?? 0,
1347
+ };
1348
+ }
1349
+ /**
1350
+ * Best-effort write attribution. Permanent limitation in v1 (V9 / §5.3):
1351
+ *
1352
+ * opencode 1.14.50 does NOT emit `message.part.updated` events during a
1353
+ * turn — tool-call data is only available from the FINAL session.prompt
1354
+ * response. By the time we read it here, the file watcher has already
1355
+ * fired (chokidar latency: milliseconds), so a marker added now is too
1356
+ * late to influence the immediate `actor` classification.
1357
+ *
1358
+ * The practical value of this call is therefore the SECOND-ORDER
1359
+ * protection it gives observers polling slower than 30s (the
1360
+ * AgentWriteTracker default TTL): if the same path is re-observed
1361
+ * within that window, attribution is correct. Real-time chokidar
1362
+ * observers (Obsidian / Git) see opencode tool-driven file writes
1363
+ * without an `actor='agent'` mark — accepted gap, contained because the
1364
+ * context-MD chokepoint forbids `edit`/`write` against
1365
+ * `~/.personal-agent/context/**` server-side (the absolute-block
1366
+ * permission JSON denies the canonical bash exfiltration paths;
1367
+ * `tools.read` hard-disable plus per-session permission denies bar
1368
+ * the opencode `read`/`write` tools from those paths in strict mode).
1369
+ *
1370
+ * If opencode begins emitting streamed `tool_call` events upstream
1371
+ * (gated on a future release), this function migrates to the event
1372
+ * mapper's stream path so the marker lands before the file watcher
1373
+ * fires; the existing extraction logic below remains as the
1374
+ * final-response back-stop.
1375
+ */
1376
+ function recordAgentWritesFromTools(tools, writeTracker) {
1377
+ for (const tool of tools) {
1378
+ if (tool.status !== "completed")
1379
+ continue;
1380
+ const writePath = extractWritePathFromInput(tool.toolName, tool.input);
1381
+ if (writePath) {
1382
+ writeTracker.markWriting(writePath);
1383
+ }
1384
+ }
1385
+ }
1386
+ function extractWritePathFromInput(toolName, input) {
1387
+ if (!input)
1388
+ return null;
1389
+ if (toolName === "write" || toolName === "edit" || toolName === "apply_patch") {
1390
+ const candidate = input.path ?? input.filePath ?? input.file;
1391
+ if (typeof candidate === "string" && candidate.length > 0)
1392
+ return candidate;
1393
+ }
1394
+ return null;
1395
+ }
1396
+ /**
1397
+ * docs/design/appendices/opencode-backend.md §5.8 synthetic absolute-block audit. For
1398
+ * every tool call extracted from the final `session.prompt` response,
1399
+ * route through the shared `auditStreamObservation` chokepoint so a
1400
+ * `blocked_absolute` row with `result='partial'` is written on a match.
1401
+ *
1402
+ * Two-step pipeline keeps the classifier centralised:
1403
+ * 1. `extractOpencodeToolUseTarget` — opencode-specific tool/arg
1404
+ * normalisation (`bash` / `read` / `write` / `edit` / `apply_patch`).
1405
+ * 2. `auditStreamObservation` — runs `classifyAbsoluteBlock` against
1406
+ * the normalised target and writes the audit row when matched.
1407
+ *
1408
+ * Why the `partial` result code: opencode enforces the deny inside the
1409
+ * permission JSON at server side, which the daemon does not directly
1410
+ * observe. The audit row records *that the agent attempted* a pattern
1411
+ * the absolute-block layer covers — useful even if the underlying
1412
+ * enforcement happened correctly server-side, and *critical* if a future
1413
+ * permission-JSON regression silently let the call through.
1414
+ *
1415
+ * Exported for `opencode-core.test.ts` so the synthetic audit can be
1416
+ * exercised without spinning up a full server.
1417
+ */
1418
+ export function auditOpencodeTools(tools, deps) {
1419
+ if (!deps.db)
1420
+ return;
1421
+ for (const tool of tools) {
1422
+ // Match on every tool we can normalise — both completed and errored
1423
+ // calls. Pending/running calls don't carry final input yet.
1424
+ if (tool.status === "pending" || tool.status === "running")
1425
+ continue;
1426
+ const target = extractOpencodeToolUseTarget(tool.toolName, tool.input);
1427
+ if (!target)
1428
+ continue;
1429
+ const match = auditStreamObservation(target, {
1430
+ db: deps.db,
1431
+ backend: "opencode",
1432
+ mode: deps.mode,
1433
+ sessionId: deps.sessionId,
1434
+ });
1435
+ // `auditStreamObservation` already writes the row when match is
1436
+ // non-null; nothing else to do here. The void return lets callers
1437
+ // ignore matches in the hot path while keeping the row in the DB.
1438
+ void match;
1439
+ }
1440
+ }
1441
+ function classifyAssistantError(error, backendId) {
1442
+ if (error.name === "ProviderAuthError") {
1443
+ return new BackendDecisiveFailure(backendId, "auth", new Error("opencode provider auth rejected"));
1444
+ }
1445
+ if (error.name === "MessageOutputLengthError") {
1446
+ return new BackendDecisiveFailure(backendId, "max_turns", new Error("opencode message output length exceeded"));
1447
+ }
1448
+ return new BackendDecisiveFailure(backendId, "other_non_retryable", new Error(`opencode assistant error: ${error.name}`));
1449
+ }
1450
+ function classifyStreamError(payload, backendId) {
1451
+ const status = payload.data?.statusCode;
1452
+ const message = payload.data?.message ?? `opencode stream error: ${payload.name}`;
1453
+ if (status === 401 || status === 403 || payload.name === "ProviderAuthError") {
1454
+ return new BackendDecisiveFailure(backendId, "auth", new Error(message));
1455
+ }
1456
+ if (status === 429) {
1457
+ return new BackendDecisiveFailure(backendId, "quota", new Error(message));
1458
+ }
1459
+ return new BackendDecisiveFailure(backendId, "other_non_retryable", new Error(message));
1460
+ }
1461
+ /**
1462
+ * docs/design/appendices/opencode-backend.md Phase 4 — Stage 2 hourly-check triage
1463
+ * schema. Mirrors the `parseStage2Verdict` text contract
1464
+ * (`dispatcher-types.ts`): the agent must return exactly
1465
+ * `{ "action": "log_only" | "escalate", "reason": string }`. Opencode
1466
+ * validates against this schema with `retryCount: 2` server-side, then
1467
+ * surfaces the parsed object at `info.structured`.
1468
+ *
1469
+ * Exported for the regression test that exercises the json_schema
1470
+ * round-trip without spinning up a real opencode server.
1471
+ */
1472
+ export const STAGE2_TRIAGE_JSON_SCHEMA = {
1473
+ type: "object",
1474
+ properties: {
1475
+ action: {
1476
+ type: "string",
1477
+ enum: ["log_only", "escalate"],
1478
+ },
1479
+ reason: {
1480
+ type: "string",
1481
+ },
1482
+ },
1483
+ required: ["action", "reason"],
1484
+ additionalProperties: false,
1485
+ };
1486
+ /**
1487
+ * Returns the opencode `format` envelope to apply when a given process
1488
+ * key has a strict structured-output contract; null otherwise. v1
1489
+ * covers `routine.hourly_check.triage`; future strict-JSON process
1490
+ * keys (e.g. delegated classifiers) extend this map.
1491
+ */
1492
+ function formatForProcessKey(processKey) {
1493
+ if (processKey === "routine.hourly_check.triage") {
1494
+ return {
1495
+ type: "json_schema",
1496
+ schema: STAGE2_TRIAGE_JSON_SCHEMA,
1497
+ retryCount: 2,
1498
+ };
1499
+ }
1500
+ return null;
1501
+ }
1502
+ //# sourceMappingURL=opencode-core.js.map