@xopcai/xopc 0.0.110 → 0.0.111

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 (301) hide show
  1. package/README.md +46 -4
  2. package/README.zh-CN.md +46 -4
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +41 -0
  6. package/dist/gateway/static/root/assets/agent-avatar-dicebear-zlx9l5Yp.js +1 -0
  7. package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +6 -0
  8. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-Bz7JWjRM.js +1 -0
  9. package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +71 -0
  10. package/dist/gateway/static/root/assets/app-management-settings-panel-DaQwdeCq.js +1 -0
  11. package/dist/gateway/static/root/assets/appearance-settings-D2zv6r3P.js +1 -0
  12. package/dist/gateway/static/root/assets/apps-page-BIppe_jj.js +1 -0
  13. package/dist/gateway/static/root/assets/archive-plugin-DEmu8eH7.js +1 -0
  14. package/dist/gateway/static/root/assets/attachment-load-B98lJoN5.js +2 -0
  15. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CybMSIoS.js → attachment-preview-renderer-DonnL2dL.js} +1 -1
  16. package/dist/gateway/static/root/assets/binary-plugins-CL9QKmyo.js +2 -0
  17. package/dist/gateway/static/root/assets/block-editor-BOAhMAhT.js +180 -0
  18. package/dist/gateway/static/root/assets/channels-settings-Br8iRnXp.js +1 -0
  19. package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
  20. package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +1 -0
  21. package/dist/gateway/static/root/assets/config-api-0o1WYdz2.js +3 -0
  22. package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +2 -0
  23. package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +1 -0
  24. package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +1 -0
  25. package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +1 -0
  26. package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +1 -0
  27. package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +1 -0
  28. package/dist/gateway/static/root/assets/dist-CkobR7mk.js +1 -0
  29. package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +1 -0
  30. package/dist/gateway/static/root/assets/dreaming-scene-BHMRVEUS.js +4585 -0
  31. package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +1 -0
  32. package/dist/gateway/static/root/assets/extension-debug-page-Cjx6pZVz.js +1 -0
  33. package/dist/gateway/static/root/assets/{extension-page-W1wiG38F.js → extension-page-BSPNL90V.js} +1 -1
  34. package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +1 -0
  35. package/dist/gateway/static/root/assets/fetch-Bs7in_4F.js +1 -0
  36. package/dist/gateway/static/root/assets/format-interval-label-CftJ5uXu.js +1 -0
  37. package/dist/gateway/static/root/assets/gateway-config-api-Clfck-hW.js +1 -0
  38. package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +1 -0
  39. package/dist/gateway/static/root/assets/gateway-startup-retry-BkjVJc38.js +1 -0
  40. package/dist/gateway/static/root/assets/goal-detail-page-kqJue0sE.js +1 -0
  41. package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +2 -0
  42. package/dist/gateway/static/root/assets/goals-settings-CJnzNSDo.js +1 -0
  43. package/dist/gateway/static/root/assets/heartbeat-config-api-Cp7CPuIn.js +1 -0
  44. package/dist/gateway/static/root/assets/heartbeat-settings-ifSI1Wbh.js +1 -0
  45. package/dist/gateway/static/root/assets/html-workspace-editor-CQLWDiwE.js +1 -0
  46. package/dist/gateway/static/root/assets/index-COkF54xv.js +121 -0
  47. package/dist/gateway/static/root/assets/index-DIaSNGEO.css +1 -0
  48. package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +2 -0
  49. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings--MI1rYl7.js +1 -0
  50. package/dist/gateway/static/root/assets/locale-store-DjbfSsfA.js +1 -0
  51. package/dist/gateway/static/root/assets/logs-page-BwrVNlyW.js +2 -0
  52. package/dist/gateway/static/root/assets/markdown-editor-AssmZFx4.js +2 -0
  53. package/dist/gateway/static/root/assets/markdown-split-B_oc9lJe.js +1 -0
  54. package/dist/gateway/static/root/assets/media-plugins-DLPvB5H3.js +1 -0
  55. package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +134 -0
  56. package/dist/gateway/static/root/assets/messages-Csef1aW7.js +3 -0
  57. package/dist/gateway/static/root/assets/middleware-Pd0e8Igk.js +1 -0
  58. package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +2 -0
  59. package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +2 -0
  60. package/dist/gateway/static/root/assets/note-image-lightbox-rSqAgOi9.js +1 -0
  61. package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +1 -0
  62. package/dist/gateway/static/root/assets/notes-page-C9ACGEty.js +1 -0
  63. package/dist/gateway/static/root/assets/preview-open-alternatives-Bw_N2oZi.js +1 -0
  64. package/dist/gateway/static/root/assets/remote-access-hub-BhcWIKuM.js +1 -0
  65. package/dist/gateway/static/root/assets/save-bar-controls-CNP2jmw0.js +1 -0
  66. package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +1 -0
  67. package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +1 -0
  68. package/dist/gateway/static/root/assets/schema-form-CDPzua9q.js +1 -0
  69. package/dist/gateway/static/root/assets/sessions-page-DzkpSDZr.js +1 -0
  70. package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +1 -0
  71. package/dist/gateway/static/root/assets/settings-collapsible-section-C9bff85B.js +1 -0
  72. package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +1 -0
  73. package/dist/gateway/static/root/assets/settings-page-DWK4Zo7s.js +1 -0
  74. package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +1 -0
  75. package/dist/gateway/static/root/assets/share-preview-page-DnutoY_S.js +2 -0
  76. package/dist/gateway/static/root/assets/shares-settings-6oRgJMpn.js +1 -0
  77. package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +1 -0
  78. package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +2 -0
  79. package/dist/gateway/static/root/assets/system-settings-panel-qP1JDtDd.js +1 -0
  80. package/dist/gateway/static/root/assets/theme-store-CD2wtgZV.js +1 -0
  81. package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +1 -0
  82. package/dist/gateway/static/root/assets/use-blob-object-url-D4BFuzZB.js +1 -0
  83. package/dist/gateway/static/root/assets/use-channel-catalog-C4mg1hjD.js +1 -0
  84. package/dist/gateway/static/root/assets/use-settings-tab-guard-F6C0mpJt.js +1 -0
  85. package/dist/gateway/static/root/assets/vendor-codemirror-CNP1wPe7.js +45 -0
  86. package/dist/gateway/static/root/assets/voice-api-key-field-BnDDb6qO.js +1 -0
  87. package/dist/gateway/static/root/assets/voice-player-store-B2EwIhtY.js +1 -0
  88. package/dist/gateway/static/root/assets/{workflow-page.utils-EYg_xBQS.js → workflow-page.utils-CRB7WX9l.js} +1 -1
  89. package/dist/gateway/static/root/assets/workflows-page-2Fptr0je.js +18 -0
  90. package/dist/gateway/static/root/index.html +12 -10
  91. package/dist/package.js +1 -1
  92. package/dist/src/agent/goals/checklist-judge.d.ts +5 -0
  93. package/dist/src/agent/goals/checklist-judge.js +26 -2
  94. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  95. package/dist/src/agent/goals/evaluate-turn.d.ts +5 -1
  96. package/dist/src/agent/goals/evaluate-turn.js +29 -3
  97. package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
  98. package/dist/src/agent/goals/goal-locale.d.ts +0 -2
  99. package/dist/src/agent/goals/goal-locale.js +2 -15
  100. package/dist/src/agent/goals/goal-locale.js.map +1 -1
  101. package/dist/src/agent/goals/persistent-goal-service.d.ts +9 -0
  102. package/dist/src/agent/goals/persistent-goal-service.js +2 -1
  103. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  104. package/dist/src/agent/goals/post-turn.d.ts +7 -0
  105. package/dist/src/agent/goals/post-turn.js +68 -23
  106. package/dist/src/agent/goals/post-turn.js.map +1 -1
  107. package/dist/src/agent/goals/state.d.ts +0 -5
  108. package/dist/src/agent/goals/state.js +1 -96
  109. package/dist/src/agent/goals/state.js.map +1 -1
  110. package/dist/src/agent/messaging/command-handler.d.ts +4 -0
  111. package/dist/src/agent/messaging/command-handler.js +6 -2
  112. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  113. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  114. package/dist/src/agent/service/direct-turn-helpers.js +3 -2
  115. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  116. package/dist/src/agent/service/process-direct-streaming.js +10 -1
  117. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  118. package/dist/src/agent/service.js +20 -1
  119. package/dist/src/agent/service.js.map +1 -1
  120. package/dist/src/agent/service.types.d.ts +8 -0
  121. package/dist/src/agent/tools/cronjob-tool.d.ts +2 -0
  122. package/dist/src/agent/tools/cronjob-tool.js +27 -4
  123. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  124. package/dist/src/agent/tools/edit.d.ts +2 -0
  125. package/dist/src/agent/tools/edit.js +10 -0
  126. package/dist/src/agent/tools/edit.js.map +1 -1
  127. package/dist/src/agent/tools/factory.js +16 -3
  128. package/dist/src/agent/tools/factory.js.map +1 -1
  129. package/dist/src/agent/tools/goal-evidence-recorder.d.ts +6 -0
  130. package/dist/src/agent/tools/goal-evidence-recorder.js +34 -0
  131. package/dist/src/agent/tools/goal-evidence-recorder.js.map +1 -0
  132. package/dist/src/agent/tools/goal-tool.d.ts +19 -0
  133. package/dist/src/agent/tools/goal-tool.js +101 -0
  134. package/dist/src/agent/tools/goal-tool.js.map +1 -0
  135. package/dist/src/agent/tools/index.d.ts +1 -0
  136. package/dist/src/agent/tools/index.js +2 -1
  137. package/dist/src/agent/tools/shell.d.ts +2 -0
  138. package/dist/src/agent/tools/shell.js +17 -2
  139. package/dist/src/agent/tools/shell.js.map +1 -1
  140. package/dist/src/agent/tools/workflow-tool.d.ts +1 -0
  141. package/dist/src/agent/tools/workflow-tool.js +10 -2
  142. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  143. package/dist/src/agent/tools/write.d.ts +2 -0
  144. package/dist/src/agent/tools/write.js +11 -0
  145. package/dist/src/agent/tools/write.js.map +1 -1
  146. package/dist/src/channels/attachments/inbound-persist.d.ts +7 -0
  147. package/dist/src/channels/attachments/inbound-persist.js +20 -2
  148. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  149. package/dist/src/chat-commands/builtins/goal.d.ts +1 -1
  150. package/dist/src/chat-commands/builtins/goal.js +55 -97
  151. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  152. package/dist/src/chat-commands/builtins/subgoal.d.ts +1 -1
  153. package/dist/src/chat-commands/builtins/subgoal.js +49 -108
  154. package/dist/src/chat-commands/builtins/subgoal.js.map +1 -1
  155. package/dist/src/chat-commands/builtins/workflow.js +132 -8
  156. package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
  157. package/dist/src/chat-commands/context.d.ts +3 -0
  158. package/dist/src/chat-commands/context.js +5 -0
  159. package/dist/src/chat-commands/context.js.map +1 -1
  160. package/dist/src/chat-commands/types.d.ts +5 -0
  161. package/dist/src/cli/command-catalog.js +43 -0
  162. package/dist/src/cli/command-catalog.js.map +1 -1
  163. package/dist/src/cli/command-loaders.js +1 -0
  164. package/dist/src/cli/command-loaders.js.map +1 -1
  165. package/dist/src/cli/command-manifest.js +4 -0
  166. package/dist/src/cli/command-manifest.js.map +1 -1
  167. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  168. package/dist/src/cli/commands/goal.d.ts +4 -0
  169. package/dist/src/cli/commands/goal.js +213 -0
  170. package/dist/src/cli/commands/goal.js.map +1 -0
  171. package/dist/src/cli/commands/onboard.js +11 -5
  172. package/dist/src/cli/commands/onboard.js.map +1 -1
  173. package/dist/src/config/index.js +2 -2
  174. package/dist/src/config/schema.d.ts +115 -0
  175. package/dist/src/config/schema.js +63 -4
  176. package/dist/src/config/schema.js.map +1 -1
  177. package/dist/src/config/web-patch.d.ts +14 -0
  178. package/dist/src/config/web-patch.js +45 -6
  179. package/dist/src/config/web-patch.js.map +1 -1
  180. package/dist/src/cron/executor.d.ts +2 -0
  181. package/dist/src/cron/executor.js +25 -0
  182. package/dist/src/cron/executor.js.map +1 -1
  183. package/dist/src/cron/job-content.js +1 -0
  184. package/dist/src/cron/job-content.js.map +1 -1
  185. package/dist/src/cron/types.d.ts +19 -1
  186. package/dist/src/cron/validation.d.ts +20 -0
  187. package/dist/src/cron/validation.js +7 -0
  188. package/dist/src/cron/validation.js.map +1 -1
  189. package/dist/src/gateway/hono/lib/config-payload.d.ts +14 -0
  190. package/dist/src/gateway/hono/routes/goals.d.ts +1 -1
  191. package/dist/src/gateway/hono/routes/goals.js +670 -173
  192. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  193. package/dist/src/gateway/hono/routes/home.d.ts +5 -0
  194. package/dist/src/gateway/hono/routes/home.js +88 -3
  195. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  196. package/dist/src/gateway/hono/routes/notes.js +20 -14
  197. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  198. package/dist/src/gateway/hono/routes/workflows.js +6 -2
  199. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  200. package/dist/src/gateway/hono/sse.js.map +1 -1
  201. package/dist/src/gateway/service/agent-runner.d.ts +2 -0
  202. package/dist/src/gateway/service/agent-runner.js +6 -2
  203. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  204. package/dist/src/gateway/service/run-gateway-agent.d.ts +3 -0
  205. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  206. package/dist/src/gateway/service.d.ts +8 -0
  207. package/dist/src/gateway/service.js +69 -2
  208. package/dist/src/gateway/service.js.map +1 -1
  209. package/dist/src/goals/goal-notifications.d.ts +29 -0
  210. package/dist/src/goals/goal-notifications.js +154 -0
  211. package/dist/src/goals/goal-notifications.js.map +1 -0
  212. package/dist/src/goals/goal-queue-store.d.ts +16 -0
  213. package/dist/src/goals/goal-queue-store.js +131 -0
  214. package/dist/src/goals/goal-queue-store.js.map +1 -0
  215. package/dist/src/goals/goal-queue-types.d.ts +31 -0
  216. package/dist/src/goals/goal-queue-types.js +1 -0
  217. package/dist/src/goals/goal-runner.d.ts +17 -0
  218. package/dist/src/goals/goal-runner.js +127 -0
  219. package/dist/src/goals/goal-runner.js.map +1 -0
  220. package/dist/src/goals/goal-service.d.ts +77 -0
  221. package/dist/src/goals/goal-service.js +261 -0
  222. package/dist/src/goals/goal-service.js.map +1 -0
  223. package/dist/src/goals/goal-store.d.ts +38 -0
  224. package/dist/src/goals/goal-store.js +404 -0
  225. package/dist/src/goals/goal-store.js.map +1 -0
  226. package/dist/src/goals/goal-workflow-judge.d.ts +13 -0
  227. package/dist/src/goals/goal-workflow-judge.js +147 -0
  228. package/dist/src/goals/goal-workflow-judge.js.map +1 -0
  229. package/dist/src/goals/index.d.ts +10 -0
  230. package/dist/src/goals/index.js +7 -0
  231. package/dist/src/goals/types.d.ts +119 -0
  232. package/dist/src/goals/types.js +1 -0
  233. package/dist/src/storage/sqlite/migrations/006_add_goals.sql +108 -0
  234. package/dist/src/storage/sqlite/migrations/007_goal_run_judge_schema.sql +4 -0
  235. package/dist/src/storage/sqlite/migrations/008_goal_queue_workflow_runs.sql +53 -0
  236. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  237. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  238. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  239. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  240. package/dist/src/storage/sqlite/schema.js +1 -1
  241. package/dist/src/storage/sqlite/schema.sql +163 -0
  242. package/dist/src/utils/logger/stats.d.ts +1 -1
  243. package/dist/src/workflows/domain/run.d.ts +1 -0
  244. package/dist/src/workflows/domain/run.js.map +1 -1
  245. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  246. package/dist/src/workflows/service/workflow-run-service.js +5 -1
  247. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  248. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -0
  249. package/dist/src/workflows/service/workflow-session-bridge.js +7 -0
  250. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  251. package/dist/src/workflows/store/run-index-store.d.ts +8 -0
  252. package/dist/src/workflows/store/run-index-store.js +58 -0
  253. package/dist/src/workflows/store/run-index-store.js.map +1 -0
  254. package/dist/src/workflows/store/run-store.d.ts +2 -0
  255. package/dist/src/workflows/store/run-store.js +14 -21
  256. package/dist/src/workflows/store/run-store.js.map +1 -1
  257. package/package.json +9 -3
  258. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
  259. package/dist/gateway/static/root/assets/agents-CFFioJCI.js +0 -78
  260. package/dist/gateway/static/root/assets/apps-page-BYDmkbzG.js +0 -1
  261. package/dist/gateway/static/root/assets/channels-settings-CsbDaMvd.js +0 -1
  262. package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +0 -1
  263. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +0 -1
  264. package/dist/gateway/static/root/assets/cron-page-Apk-7p4p.js +0 -1
  265. package/dist/gateway/static/root/assets/dist-DmGDgpy2.js +0 -1
  266. package/dist/gateway/static/root/assets/extension-debug-page-pxFC-tUa.js +0 -1
  267. package/dist/gateway/static/root/assets/extension-settings-page-COXSv6xP.js +0 -1
  268. package/dist/gateway/static/root/assets/fetch-CFdKBnE6.js +0 -1
  269. package/dist/gateway/static/root/assets/gateway-startup-retry-DdngdObI.js +0 -1
  270. package/dist/gateway/static/root/assets/heartbeat-config-api-DcmAMYmN.js +0 -1
  271. package/dist/gateway/static/root/assets/index-C_2yKRJX.js +0 -4845
  272. package/dist/gateway/static/root/assets/index-DI5_GsZt.css +0 -1
  273. package/dist/gateway/static/root/assets/logs-page-DvTWUOzd.js +0 -2
  274. package/dist/gateway/static/root/assets/note-detail-page-CZbdugWZ.js +0 -180
  275. package/dist/gateway/static/root/assets/note-time-RdyBZxj3.js +0 -1
  276. package/dist/gateway/static/root/assets/notes-page-mEAwy3e1.js +0 -1
  277. package/dist/gateway/static/root/assets/schema-form-DEvdni_r.js +0 -1
  278. package/dist/gateway/static/root/assets/sessions-page-BsgffzV4.js +0 -1
  279. package/dist/gateway/static/root/assets/settings-advanced-gate-t6v8Dwb8.js +0 -2
  280. package/dist/gateway/static/root/assets/settings-form-section-BoYYZmJG.js +0 -1
  281. package/dist/gateway/static/root/assets/settings-page-ClIthYF8.js +0 -3
  282. package/dist/gateway/static/root/assets/share-preview-page-Dd9et7KD.js +0 -2
  283. package/dist/gateway/static/root/assets/skills-page-DEXtxyfQ.js +0 -2
  284. package/dist/gateway/static/root/assets/theme-store-BZozwAeK.js +0 -1
  285. package/dist/gateway/static/root/assets/url-DQRqHuL-.js +0 -3
  286. package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +0 -45
  287. package/dist/gateway/static/root/assets/voice-api-key-field-CNrKzQrf.js +0 -1
  288. package/dist/gateway/static/root/assets/workflows-page-EMZkzJNN.js +0 -18
  289. package/dist/src/agent/goals/checklist-user.d.ts +0 -24
  290. package/dist/src/agent/goals/checklist-user.js +0 -92
  291. package/dist/src/agent/goals/checklist-user.js.map +0 -1
  292. package/dist/src/agent/goals/goal-run-store.d.ts +0 -19
  293. package/dist/src/agent/goals/goal-run-store.js +0 -138
  294. package/dist/src/agent/goals/goal-run-store.js.map +0 -1
  295. package/dist/src/agent/goals/goal-run-types.d.ts +0 -28
  296. package/dist/src/agent/goals/goal-run-types.js +0 -6
  297. package/dist/src/agent/goals/goal-run-types.js.map +0 -1
  298. package/dist/src/agent/goals/patch-from-user-action.d.ts +0 -20
  299. package/dist/src/agent/goals/patch-from-user-action.js +0 -103
  300. package/dist/src/agent/goals/patch-from-user-action.js.map +0 -1
  301. /package/dist/gateway/static/root/assets/{note-detail-page-D4ZIVQbk.css → block-editor-D4ZIVQbk.css} +0 -0
@@ -0,0 +1,154 @@
1
+ import { init_session_key, parseSessionKey } from "../routing/session-key.js";
2
+ import { createLogger } from "../utils/logger/index.js";
3
+ import { init_logger } from "../utils/logger.js";
4
+ import { GoalService } from "./goal-service.js";
5
+ //#region src/goals/goal-notifications.ts
6
+ init_session_key();
7
+ init_logger();
8
+ const log = createLogger("GoalNotifications");
9
+ function eventForQueueStatus(status) {
10
+ if (status === "failed") return "queue_failed";
11
+ if (status === "retry_waiting") return "queue_retry";
12
+ if (status === "succeeded") return "queue_succeeded";
13
+ if (status === "skipped") return "queue_skipped";
14
+ return null;
15
+ }
16
+ function eventForGoalStatus(status) {
17
+ if (status === "done" || status === "blocked" || status === "needs_input") return status;
18
+ return null;
19
+ }
20
+ function targetKey(target) {
21
+ return [
22
+ target.channel,
23
+ target.chatId,
24
+ target.accountId ?? "",
25
+ target.threadId ?? ""
26
+ ].join("\0");
27
+ }
28
+ function formatGoalNotification(event, goal, opts) {
29
+ const lines = [`${{
30
+ done: "Goal completed",
31
+ blocked: "Goal blocked",
32
+ needs_input: "Goal needs input",
33
+ queue_failed: "Goal run failed",
34
+ queue_retry: "Goal run retry scheduled",
35
+ queue_succeeded: "Goal run finished",
36
+ queue_skipped: "Goal run skipped"
37
+ }[event]}: ${goal.title}`];
38
+ if (goal.blockedReason) lines.push(`Reason: ${goal.blockedReason}`);
39
+ if (goal.nextAction) lines.push(`Next: ${goal.nextAction}`);
40
+ if (goal.latestRun?.reason && goal.latestRun.reason !== goal.blockedReason) lines.push(`Run: ${goal.latestRun.reason}`);
41
+ if (opts?.queueItem?.lastError) lines.push(`Queue: ${opts.queueItem.lastError}`);
42
+ const progress = goal.checklist.length ? goal.checklist.filter((it) => it.status === "completed" || it.status === "impossible").length : 0;
43
+ if (goal.checklist.length) lines.push(`Checklist: ${progress}/${goal.checklist.length}`);
44
+ lines.push(`Turns: ${goal.turnsUsed}/${goal.maxTurns}`);
45
+ return lines.join("\n");
46
+ }
47
+ var GoalNotificationService = class {
48
+ goals = new GoalService();
49
+ sentKeys = /* @__PURE__ */ new Set();
50
+ constructor(opts) {
51
+ this.opts = opts;
52
+ }
53
+ handleGatewayEvent(type, payload) {
54
+ if (type === "goal.queue.updated") {
55
+ this.handleQueueUpdated(payload).catch((err) => {
56
+ log.warn({ err }, "Goal queue notification failed");
57
+ });
58
+ return;
59
+ }
60
+ if (type === "goal.status.updated") this.handleStatusUpdated(payload).catch((err) => {
61
+ log.warn({ err }, "Goal status notification failed");
62
+ });
63
+ }
64
+ async handleQueueUpdated(payload) {
65
+ const item = this.parseQueueItem(payload);
66
+ if (!item) return;
67
+ const event = eventForQueueStatus(item.status);
68
+ if (!event) return;
69
+ const goal = this.goals.get(item.goalId);
70
+ if (!goal) return;
71
+ await this.notify(event, goal, {
72
+ queueItem: item,
73
+ eventKey: `${item.id}:${item.status}:${item.attempts}`
74
+ });
75
+ }
76
+ async handleStatusUpdated(payload) {
77
+ if (!payload || typeof payload !== "object") return;
78
+ const rec = payload;
79
+ const goal = this.isGoal(rec.goal) ? rec.goal : typeof rec.goalId === "string" ? this.goals.get(rec.goalId) : null;
80
+ if (!goal) return;
81
+ const status = typeof rec.status === "string" ? rec.status : goal.status;
82
+ const event = eventForGoalStatus(status);
83
+ if (!event) return;
84
+ if (rec.previousStatus === status) return;
85
+ await this.notify(event, goal, { eventKey: `${goal.id}:${event}:${goal.updatedAt}` });
86
+ }
87
+ async notify(event, goal, opts) {
88
+ const policy = this.opts.getConfig().goals?.notifications;
89
+ if (!policy?.enabled) return;
90
+ if (!policy.events.includes(event)) return;
91
+ const targets = this.resolveTargets(event, goal);
92
+ if (targets.length === 0) return;
93
+ const text = formatGoalNotification(event, goal, { queueItem: opts?.queueItem });
94
+ for (const target of targets) {
95
+ const dedupeKey = `${opts?.eventKey ?? `${goal.id}:${event}`}:${targetKey(target)}`;
96
+ if (this.sentKeys.has(dedupeKey)) continue;
97
+ this.sentKeys.add(dedupeKey);
98
+ await this.opts.send({
99
+ ...target,
100
+ text
101
+ }).catch((err) => {
102
+ this.sentKeys.delete(dedupeKey);
103
+ throw err;
104
+ });
105
+ }
106
+ }
107
+ resolveTargets(event, goal) {
108
+ const policy = this.opts.getConfig().goals?.notifications;
109
+ if (!policy?.enabled) return [];
110
+ const byKey = /* @__PURE__ */ new Map();
111
+ if (policy.includeLinkedSessions !== false && goal.activeSessionKey) {
112
+ const parsed = parseSessionKey(goal.activeSessionKey);
113
+ if (parsed && policy.channels.includes(parsed.source)) {
114
+ const target = {
115
+ channel: parsed.source,
116
+ chatId: parsed.peerId,
117
+ accountId: parsed.accountId,
118
+ ...parsed.threadId ? { threadId: parsed.threadId } : {}
119
+ };
120
+ byKey.set(targetKey(target), target);
121
+ }
122
+ }
123
+ for (const target of policy.targets ?? []) {
124
+ const events = target.events;
125
+ if (events && !events.includes(event)) continue;
126
+ const normalized = {
127
+ channel: target.channel,
128
+ chatId: target.chatId,
129
+ ...target.accountId ? { accountId: target.accountId } : {},
130
+ ...target.threadId != null ? { threadId: target.threadId } : {},
131
+ ...target.silent != null ? { silent: target.silent } : {}
132
+ };
133
+ byKey.set(targetKey(normalized), normalized);
134
+ }
135
+ return [...byKey.values()];
136
+ }
137
+ parseQueueItem(payload) {
138
+ if (!payload || typeof payload !== "object") return null;
139
+ const item = payload.item;
140
+ if (!item || typeof item !== "object") return null;
141
+ const rec = item;
142
+ if (typeof rec.id !== "string" || typeof rec.goalId !== "string" || typeof rec.status !== "string") return null;
143
+ return rec;
144
+ }
145
+ isGoal(value) {
146
+ if (!value || typeof value !== "object") return false;
147
+ const rec = value;
148
+ return typeof rec.id === "string" && typeof rec.title === "string" && Array.isArray(rec.checklist);
149
+ }
150
+ };
151
+ //#endregion
152
+ export { GoalNotificationService };
153
+
154
+ //# sourceMappingURL=goal-notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-notifications.js","names":[],"sources":["../../../src/goals/goal-notifications.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { parseSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { GoalService } from './goal-service.js';\nimport type { GoalWithDetails } from './types.js';\nimport type { GoalQueueItemSnapshot } from './goal-queue-types.js';\n\nconst log = createLogger('GoalNotifications');\n\nexport type GoalNotificationEvent =\n | 'done'\n | 'blocked'\n | 'needs_input'\n | 'queue_failed'\n | 'queue_retry'\n | 'queue_succeeded'\n | 'queue_skipped';\n\nexport interface GoalNotificationTarget {\n channel: string;\n chatId: string;\n accountId?: string;\n threadId?: string | number;\n silent?: boolean;\n}\n\nexport interface GoalNotificationSendInput extends GoalNotificationTarget {\n text: string;\n}\n\nexport interface GoalNotificationServiceOptions {\n getConfig: () => Config;\n send: (input: GoalNotificationSendInput) => Promise<void>;\n}\n\nfunction eventForQueueStatus(status: GoalQueueItemSnapshot['status']): GoalNotificationEvent | null {\n if (status === 'failed') return 'queue_failed';\n if (status === 'retry_waiting') return 'queue_retry';\n if (status === 'succeeded') return 'queue_succeeded';\n if (status === 'skipped') return 'queue_skipped';\n return null;\n}\n\nfunction eventForGoalStatus(status: GoalWithDetails['status']): GoalNotificationEvent | null {\n if (status === 'done' || status === 'blocked' || status === 'needs_input') return status;\n return null;\n}\n\nfunction targetKey(target: GoalNotificationTarget): string {\n return [\n target.channel,\n target.chatId,\n target.accountId ?? '',\n target.threadId ?? '',\n ].join('\\u0000');\n}\n\nfunction formatGoalNotification(event: GoalNotificationEvent, goal: GoalWithDetails, opts?: {\n queueItem?: GoalQueueItemSnapshot;\n}): string {\n const labels: Record<GoalNotificationEvent, string> = {\n done: 'Goal completed',\n blocked: 'Goal blocked',\n needs_input: 'Goal needs input',\n queue_failed: 'Goal run failed',\n queue_retry: 'Goal run retry scheduled',\n queue_succeeded: 'Goal run finished',\n queue_skipped: 'Goal run skipped',\n };\n const lines = [`${labels[event]}: ${goal.title}`];\n if (goal.blockedReason) lines.push(`Reason: ${goal.blockedReason}`);\n if (goal.nextAction) lines.push(`Next: ${goal.nextAction}`);\n if (goal.latestRun?.reason && goal.latestRun.reason !== goal.blockedReason) {\n lines.push(`Run: ${goal.latestRun.reason}`);\n }\n if (opts?.queueItem?.lastError) lines.push(`Queue: ${opts.queueItem.lastError}`);\n const progress = goal.checklist.length\n ? goal.checklist.filter((it) => it.status === 'completed' || it.status === 'impossible').length\n : 0;\n if (goal.checklist.length) lines.push(`Checklist: ${progress}/${goal.checklist.length}`);\n lines.push(`Turns: ${goal.turnsUsed}/${goal.maxTurns}`);\n return lines.join('\\n');\n}\n\nexport class GoalNotificationService {\n private readonly goals = new GoalService();\n private readonly sentKeys = new Set<string>();\n\n constructor(private readonly opts: GoalNotificationServiceOptions) {}\n\n handleGatewayEvent(type: string, payload: unknown): void {\n if (type === 'goal.queue.updated') {\n void this.handleQueueUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal queue notification failed');\n });\n return;\n }\n if (type === 'goal.status.updated') {\n void this.handleStatusUpdated(payload).catch((err) => {\n log.warn({ err }, 'Goal status notification failed');\n });\n }\n }\n\n private async handleQueueUpdated(payload: unknown): Promise<void> {\n const item = this.parseQueueItem(payload);\n if (!item) return;\n const event = eventForQueueStatus(item.status);\n if (!event) return;\n const goal = this.goals.get(item.goalId);\n if (!goal) return;\n await this.notify(event, goal, { queueItem: item, eventKey: `${item.id}:${item.status}:${item.attempts}` });\n }\n\n private async handleStatusUpdated(payload: unknown): Promise<void> {\n if (!payload || typeof payload !== 'object') return;\n const rec = payload as { goal?: unknown; status?: unknown; goalId?: unknown; previousStatus?: unknown };\n const goal =\n this.isGoal(rec.goal)\n ? rec.goal\n : typeof rec.goalId === 'string'\n ? this.goals.get(rec.goalId)\n : null;\n if (!goal) return;\n const status = typeof rec.status === 'string' ? rec.status : goal.status;\n const event = eventForGoalStatus(status as GoalWithDetails['status']);\n if (!event) return;\n if (rec.previousStatus === status) return;\n await this.notify(event, goal, { eventKey: `${goal.id}:${event}:${goal.updatedAt}` });\n }\n\n private async notify(\n event: GoalNotificationEvent,\n goal: GoalWithDetails,\n opts?: { queueItem?: GoalQueueItemSnapshot; eventKey?: string },\n ): Promise<void> {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return;\n if (!policy.events.includes(event)) return;\n\n const targets = this.resolveTargets(event, goal);\n if (targets.length === 0) return;\n\n const text = formatGoalNotification(event, goal, { queueItem: opts?.queueItem });\n for (const target of targets) {\n const dedupeKey = `${opts?.eventKey ?? `${goal.id}:${event}`}:${targetKey(target)}`;\n if (this.sentKeys.has(dedupeKey)) continue;\n this.sentKeys.add(dedupeKey);\n await this.opts.send({ ...target, text }).catch((err) => {\n this.sentKeys.delete(dedupeKey);\n throw err;\n });\n }\n }\n\n private resolveTargets(event: GoalNotificationEvent, goal: GoalWithDetails): GoalNotificationTarget[] {\n const policy = this.opts.getConfig().goals?.notifications;\n if (!policy?.enabled) return [];\n const byKey = new Map<string, GoalNotificationTarget>();\n\n if (policy.includeLinkedSessions !== false && goal.activeSessionKey) {\n const parsed = parseSessionKey(goal.activeSessionKey);\n const eligible = parsed && policy.channels.includes(parsed.source);\n if (eligible) {\n const target: GoalNotificationTarget = {\n channel: parsed.source,\n chatId: parsed.peerId,\n accountId: parsed.accountId,\n ...(parsed.threadId ? { threadId: parsed.threadId } : {}),\n };\n byKey.set(targetKey(target), target);\n }\n }\n\n for (const target of policy.targets ?? []) {\n const events = target.events;\n if (events && !events.includes(event)) continue;\n const normalized: GoalNotificationTarget = {\n channel: target.channel,\n chatId: target.chatId,\n ...(target.accountId ? { accountId: target.accountId } : {}),\n ...(target.threadId != null ? { threadId: target.threadId } : {}),\n ...(target.silent != null ? { silent: target.silent } : {}),\n };\n byKey.set(targetKey(normalized), normalized);\n }\n\n return [...byKey.values()];\n }\n\n private parseQueueItem(payload: unknown): GoalQueueItemSnapshot | null {\n if (!payload || typeof payload !== 'object') return null;\n const item = (payload as { item?: unknown }).item;\n if (!item || typeof item !== 'object') return null;\n const rec = item as Partial<GoalQueueItemSnapshot>;\n if (typeof rec.id !== 'string' || typeof rec.goalId !== 'string' || typeof rec.status !== 'string') return null;\n return rec as GoalQueueItemSnapshot;\n }\n\n private isGoal(value: unknown): value is GoalWithDetails {\n if (!value || typeof value !== 'object') return false;\n const rec = value as Partial<GoalWithDetails>;\n return typeof rec.id === 'string' && typeof rec.title === 'string' && Array.isArray(rec.checklist);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { GoalQueueItemSnapshot, GoalQueueStatus } from './goal-queue-types.js';
2
+ export declare class GoalQueueStore {
3
+ enqueue(input: {
4
+ goalId: string;
5
+ message?: string;
6
+ maxRetries: number;
7
+ source: GoalQueueItemSnapshot['source'];
8
+ }): GoalQueueItemSnapshot;
9
+ claimNext(now?: number): GoalQueueItemSnapshot | null;
10
+ markRetry(itemId: string, error: string, nextRunAt: number): GoalQueueItemSnapshot | null;
11
+ markFinished(itemId: string, status: GoalQueueStatus, error?: string): GoalQueueItemSnapshot | null;
12
+ setSessionKey(itemId: string, sessionKey: string): GoalQueueItemSnapshot | null;
13
+ list(limit?: number): GoalQueueItemSnapshot[];
14
+ resetRunningToRetry(reason?: string): number;
15
+ private update;
16
+ }
@@ -0,0 +1,131 @@
1
+ import { getSqliteDatabase, runSqliteWriteTransaction } from "../storage/sqlite/transaction.js";
2
+ import { randomUUID } from "node:crypto";
3
+ //#region src/goals/goal-queue-store.ts
4
+ function parsePayload(raw) {
5
+ try {
6
+ const parsed = JSON.parse(raw);
7
+ return parsed && typeof parsed === "object" ? parsed : {};
8
+ } catch {
9
+ return {};
10
+ }
11
+ }
12
+ function rowToSnapshot(row) {
13
+ const payload = parsePayload(row.payload_json);
14
+ return {
15
+ id: row.queue_id,
16
+ goalId: row.goal_id,
17
+ status: row.status,
18
+ attempts: row.attempts,
19
+ maxRetries: row.max_retries,
20
+ enqueuedAt: row.enqueued_at,
21
+ startedAt: row.started_at ?? void 0,
22
+ finishedAt: row.finished_at ?? void 0,
23
+ nextRunAt: row.next_run_at ?? void 0,
24
+ sessionKey: row.session_key ?? void 0,
25
+ message: payload.message,
26
+ lastError: row.last_error ?? void 0,
27
+ source: row.source
28
+ };
29
+ }
30
+ var GoalQueueStore = class {
31
+ enqueue(input) {
32
+ return runSqliteWriteTransaction((db) => {
33
+ const existing = db.prepare(`SELECT * FROM goal_queue
34
+ WHERE goal_id = ?
35
+ AND status IN ('queued', 'running', 'retry_waiting')
36
+ ORDER BY enqueued_at DESC
37
+ LIMIT 1`).get(input.goalId);
38
+ if (existing) return rowToSnapshot(existing);
39
+ const now = Date.now();
40
+ const row = {
41
+ queue_id: randomUUID(),
42
+ goal_id: input.goalId,
43
+ status: "queued",
44
+ payload_json: JSON.stringify({ message: input.message }),
45
+ attempts: 0,
46
+ max_retries: input.maxRetries,
47
+ enqueued_at: now,
48
+ started_at: null,
49
+ finished_at: null,
50
+ next_run_at: null,
51
+ session_key: null,
52
+ last_error: null,
53
+ source: input.source
54
+ };
55
+ db.prepare(`INSERT INTO goal_queue (
56
+ queue_id, goal_id, status, payload_json, attempts, max_retries,
57
+ enqueued_at, source
58
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(row.queue_id, row.goal_id, row.status, row.payload_json, row.attempts, row.max_retries, row.enqueued_at, row.source);
59
+ return rowToSnapshot(row);
60
+ });
61
+ }
62
+ claimNext(now = Date.now()) {
63
+ return runSqliteWriteTransaction((db) => {
64
+ const row = db.prepare(`SELECT * FROM goal_queue
65
+ WHERE status = 'queued'
66
+ OR (status = 'retry_waiting' AND COALESCE(next_run_at, 0) <= ?)
67
+ ORDER BY enqueued_at ASC
68
+ LIMIT 1`).get(now);
69
+ if (!row) return null;
70
+ const startedAt = Date.now();
71
+ db.prepare(`UPDATE goal_queue
72
+ SET status = 'running',
73
+ started_at = ?,
74
+ attempts = attempts + 1,
75
+ last_error = NULL
76
+ WHERE queue_id = ?`).run(startedAt, row.queue_id);
77
+ return rowToSnapshot(db.prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`).get(row.queue_id));
78
+ });
79
+ }
80
+ markRetry(itemId, error, nextRunAt) {
81
+ return this.update(itemId, {
82
+ status: "retry_waiting",
83
+ next_run_at: nextRunAt,
84
+ last_error: error,
85
+ finished_at: null
86
+ });
87
+ }
88
+ markFinished(itemId, status, error) {
89
+ return this.update(itemId, {
90
+ status,
91
+ finished_at: Date.now(),
92
+ last_error: error ?? null
93
+ });
94
+ }
95
+ setSessionKey(itemId, sessionKey) {
96
+ return this.update(itemId, { session_key: sessionKey });
97
+ }
98
+ list(limit = 200) {
99
+ const safeLimit = Math.min(500, Math.max(1, Math.floor(limit)));
100
+ return getSqliteDatabase().prepare(`SELECT * FROM goal_queue ORDER BY enqueued_at DESC LIMIT ?`).all(safeLimit).map(rowToSnapshot);
101
+ }
102
+ resetRunningToRetry(reason = "Gateway restarted during goal run") {
103
+ return runSqliteWriteTransaction((db) => {
104
+ const result = db.prepare(`UPDATE goal_queue
105
+ SET status = 'retry_waiting',
106
+ next_run_at = ?,
107
+ last_error = ?
108
+ WHERE status = 'running'`).run(Date.now(), reason);
109
+ return Number(result.changes ?? 0);
110
+ });
111
+ }
112
+ update(itemId, patch) {
113
+ return runSqliteWriteTransaction((db) => {
114
+ const row = db.prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`).get(itemId);
115
+ if (!row) return null;
116
+ const next = {
117
+ ...row,
118
+ ...patch
119
+ };
120
+ db.prepare(`UPDATE goal_queue SET
121
+ status = ?, attempts = ?, started_at = ?, finished_at = ?,
122
+ next_run_at = ?, session_key = ?, last_error = ?
123
+ WHERE queue_id = ?`).run(next.status, next.attempts, next.started_at, next.finished_at, next.next_run_at, next.session_key, next.last_error, itemId);
124
+ return rowToSnapshot(db.prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`).get(itemId));
125
+ });
126
+ }
127
+ };
128
+ //#endregion
129
+ export { GoalQueueStore };
130
+
131
+ //# sourceMappingURL=goal-queue-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-queue-store.js","names":[],"sources":["../../../src/goals/goal-queue-store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { getSqliteDatabase, runSqliteWriteTransaction } from '../storage/sqlite/transaction.js';\n\nimport type { GoalQueueItemSnapshot, GoalQueueStatus } from './goal-queue-types.js';\n\ntype GoalQueueRow = {\n queue_id: string;\n goal_id: string;\n status: GoalQueueStatus;\n payload_json: string;\n attempts: number;\n max_retries: number;\n enqueued_at: number;\n started_at: number | null;\n finished_at: number | null;\n next_run_at: number | null;\n session_key: string | null;\n last_error: string | null;\n source: GoalQueueItemSnapshot['source'];\n};\n\ntype GoalQueuePayload = {\n message?: string;\n};\n\nfunction parsePayload(raw: string): GoalQueuePayload {\n try {\n const parsed = JSON.parse(raw) as GoalQueuePayload;\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction rowToSnapshot(row: GoalQueueRow): GoalQueueItemSnapshot {\n const payload = parsePayload(row.payload_json);\n return {\n id: row.queue_id,\n goalId: row.goal_id,\n status: row.status,\n attempts: row.attempts,\n maxRetries: row.max_retries,\n enqueuedAt: row.enqueued_at,\n startedAt: row.started_at ?? undefined,\n finishedAt: row.finished_at ?? undefined,\n nextRunAt: row.next_run_at ?? undefined,\n sessionKey: row.session_key ?? undefined,\n message: payload.message,\n lastError: row.last_error ?? undefined,\n source: row.source,\n };\n}\n\nexport class GoalQueueStore {\n enqueue(input: {\n goalId: string;\n message?: string;\n maxRetries: number;\n source: GoalQueueItemSnapshot['source'];\n }): GoalQueueItemSnapshot {\n return runSqliteWriteTransaction((db) => {\n const existing = db\n .prepare(\n `SELECT * FROM goal_queue\n WHERE goal_id = ?\n AND status IN ('queued', 'running', 'retry_waiting')\n ORDER BY enqueued_at DESC\n LIMIT 1`,\n )\n .get(input.goalId) as GoalQueueRow | undefined;\n if (existing) return rowToSnapshot(existing);\n\n const now = Date.now();\n const row: GoalQueueRow = {\n queue_id: randomUUID(),\n goal_id: input.goalId,\n status: 'queued',\n payload_json: JSON.stringify({ message: input.message }),\n attempts: 0,\n max_retries: input.maxRetries,\n enqueued_at: now,\n started_at: null,\n finished_at: null,\n next_run_at: null,\n session_key: null,\n last_error: null,\n source: input.source,\n };\n db.prepare(\n `INSERT INTO goal_queue (\n queue_id, goal_id, status, payload_json, attempts, max_retries,\n enqueued_at, source\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(\n row.queue_id,\n row.goal_id,\n row.status,\n row.payload_json,\n row.attempts,\n row.max_retries,\n row.enqueued_at,\n row.source,\n );\n return rowToSnapshot(row);\n });\n }\n\n claimNext(now = Date.now()): GoalQueueItemSnapshot | null {\n return runSqliteWriteTransaction((db) => {\n const row = db\n .prepare(\n `SELECT * FROM goal_queue\n WHERE status = 'queued'\n OR (status = 'retry_waiting' AND COALESCE(next_run_at, 0) <= ?)\n ORDER BY enqueued_at ASC\n LIMIT 1`,\n )\n .get(now) as GoalQueueRow | undefined;\n if (!row) return null;\n\n const startedAt = Date.now();\n db.prepare(\n `UPDATE goal_queue\n SET status = 'running',\n started_at = ?,\n attempts = attempts + 1,\n last_error = NULL\n WHERE queue_id = ?`,\n ).run(startedAt, row.queue_id);\n\n const next = db\n .prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`)\n .get(row.queue_id) as GoalQueueRow;\n return rowToSnapshot(next);\n });\n }\n\n markRetry(itemId: string, error: string, nextRunAt: number): GoalQueueItemSnapshot | null {\n return this.update(itemId, {\n status: 'retry_waiting',\n next_run_at: nextRunAt,\n last_error: error,\n finished_at: null,\n });\n }\n\n markFinished(itemId: string, status: GoalQueueStatus, error?: string): GoalQueueItemSnapshot | null {\n return this.update(itemId, {\n status,\n finished_at: Date.now(),\n last_error: error ?? null,\n });\n }\n\n setSessionKey(itemId: string, sessionKey: string): GoalQueueItemSnapshot | null {\n return this.update(itemId, { session_key: sessionKey });\n }\n\n list(limit = 200): GoalQueueItemSnapshot[] {\n const safeLimit = Math.min(500, Math.max(1, Math.floor(limit)));\n const rows = getSqliteDatabase()\n .prepare(`SELECT * FROM goal_queue ORDER BY enqueued_at DESC LIMIT ?`)\n .all(safeLimit) as GoalQueueRow[];\n return rows.map(rowToSnapshot);\n }\n\n resetRunningToRetry(reason = 'Gateway restarted during goal run'): number {\n return runSqliteWriteTransaction((db) => {\n const result = db\n .prepare(\n `UPDATE goal_queue\n SET status = 'retry_waiting',\n next_run_at = ?,\n last_error = ?\n WHERE status = 'running'`,\n )\n .run(Date.now(), reason);\n return Number(result.changes ?? 0);\n });\n }\n\n private update(itemId: string, patch: Partial<GoalQueueRow>): GoalQueueItemSnapshot | null {\n return runSqliteWriteTransaction((db) => {\n const row = db\n .prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`)\n .get(itemId) as GoalQueueRow | undefined;\n if (!row) return null;\n const next = { ...row, ...patch };\n db.prepare(\n `UPDATE goal_queue SET\n status = ?, attempts = ?, started_at = ?, finished_at = ?,\n next_run_at = ?, session_key = ?, last_error = ?\n WHERE queue_id = ?`,\n ).run(\n next.status,\n next.attempts,\n next.started_at,\n next.finished_at,\n next.next_run_at,\n next.session_key,\n next.last_error,\n itemId,\n );\n const updated = db\n .prepare(`SELECT * FROM goal_queue WHERE queue_id = ?`)\n .get(itemId) as GoalQueueRow;\n return rowToSnapshot(updated);\n });\n }\n}\n\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { GoalWithDetails } from './types.js';
2
+ export type GoalQueueStatus = 'queued' | 'running' | 'retry_waiting' | 'succeeded' | 'failed' | 'skipped';
3
+ export interface GoalQueueItemSnapshot {
4
+ id: string;
5
+ goalId: string;
6
+ status: GoalQueueStatus;
7
+ attempts: number;
8
+ maxRetries: number;
9
+ enqueuedAt: number;
10
+ startedAt?: number;
11
+ finishedAt?: number;
12
+ nextRunAt?: number;
13
+ sessionKey?: string;
14
+ message?: string;
15
+ lastError?: string;
16
+ source: 'api' | 'cron' | 'workflow' | 'system';
17
+ }
18
+ export interface GoalRunnerOptions {
19
+ maxConcurrent?: number;
20
+ defaultMaxRetries?: number;
21
+ retryBaseMs?: number;
22
+ ensureSession: (goal: GoalWithDetails) => Promise<string>;
23
+ hasActiveRun: (sessionKey: string) => boolean;
24
+ runContinuation: (sessionKey: string, message: string) => Promise<void>;
25
+ emit?: (type: string, payload: unknown) => void;
26
+ }
27
+ export interface EnqueueGoalRunOptions {
28
+ message?: string;
29
+ maxRetries?: number;
30
+ source?: GoalQueueItemSnapshot['source'];
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { EnqueueGoalRunOptions, GoalQueueItemSnapshot, GoalRunnerOptions } from './goal-queue-types.js';
2
+ export declare class GoalRunner {
3
+ private readonly opts;
4
+ private readonly goals;
5
+ private readonly store;
6
+ private active;
7
+ private pumpTimer;
8
+ constructor(opts: GoalRunnerOptions);
9
+ enqueue(goalId: string, options?: EnqueueGoalRunOptions): GoalQueueItemSnapshot;
10
+ snapshot(): GoalQueueItemSnapshot[];
11
+ private schedulePump;
12
+ private pump;
13
+ private runItem;
14
+ private retry;
15
+ private finish;
16
+ private emit;
17
+ }
@@ -0,0 +1,127 @@
1
+ import { createLogger } from "../utils/logger/index.js";
2
+ import { init_logger } from "../utils/logger.js";
3
+ import { GoalService } from "./goal-service.js";
4
+ import { GoalQueueStore } from "./goal-queue-store.js";
5
+ //#region src/goals/goal-runner.ts
6
+ init_logger();
7
+ const log = createLogger("GoalRunner");
8
+ function terminalGoalStatus(status) {
9
+ return status === "done" || status === "archived";
10
+ }
11
+ var GoalRunner = class {
12
+ goals = new GoalService();
13
+ store = new GoalQueueStore();
14
+ active = 0;
15
+ pumpTimer = null;
16
+ constructor(opts) {
17
+ this.opts = opts;
18
+ this.store.resetRunningToRetry();
19
+ this.schedulePump(0);
20
+ }
21
+ enqueue(goalId, options = {}) {
22
+ const item = this.store.enqueue({
23
+ goalId,
24
+ maxRetries: Math.max(0, Math.floor(options.maxRetries ?? this.opts.defaultMaxRetries ?? 2)),
25
+ message: options.message?.trim() || void 0,
26
+ source: options.source ?? "api"
27
+ });
28
+ this.emit("goal.queue.updated", item);
29
+ this.schedulePump(0);
30
+ return item;
31
+ }
32
+ snapshot() {
33
+ return this.store.list();
34
+ }
35
+ schedulePump(delayMs) {
36
+ if (this.pumpTimer) return;
37
+ this.pumpTimer = setTimeout(() => {
38
+ this.pumpTimer = null;
39
+ this.pump();
40
+ }, delayMs);
41
+ }
42
+ async pump() {
43
+ const maxConcurrent = Math.max(1, Math.floor(this.opts.maxConcurrent ?? 1));
44
+ while (this.active < maxConcurrent) {
45
+ const item = this.store.claimNext();
46
+ if (!item) break;
47
+ this.active += 1;
48
+ this.runItem(item).finally(() => {
49
+ this.active -= 1;
50
+ this.schedulePump(0);
51
+ });
52
+ }
53
+ const nextRetry = this.store.list().filter((item) => item.status === "retry_waiting" && item.nextRunAt).sort((a, b) => (a.nextRunAt ?? 0) - (b.nextRunAt ?? 0))[0];
54
+ if (nextRetry) this.schedulePump(Math.max(0, (nextRetry.nextRunAt ?? Date.now()) - Date.now()));
55
+ }
56
+ async runItem(item) {
57
+ this.emit("goal.queue.updated", item);
58
+ try {
59
+ const goal = this.goals.get(item.goalId);
60
+ if (!goal) {
61
+ this.finish(item, "failed", "Goal not found");
62
+ return;
63
+ }
64
+ if (terminalGoalStatus(goal.status)) {
65
+ this.finish(item, "skipped", `Goal is ${goal.status}`);
66
+ return;
67
+ }
68
+ const activeGoal = goal.status === "paused" || goal.status === "blocked" || goal.status === "needs_input" ? this.goals.resume(goal.id) : goal;
69
+ if (!activeGoal) {
70
+ this.finish(item, "failed", "Goal not found");
71
+ return;
72
+ }
73
+ const sessionKey = activeGoal.activeSessionKey ?? await this.opts.ensureSession(activeGoal);
74
+ item = this.store.setSessionKey(item.id, sessionKey) ?? {
75
+ ...item,
76
+ sessionKey
77
+ };
78
+ if (this.opts.hasActiveRun(sessionKey)) {
79
+ this.retry(item, "Goal session already has an active run");
80
+ return;
81
+ }
82
+ const message = item.message || activeGoal.nextAction || activeGoal.title;
83
+ await this.opts.runContinuation(sessionKey, message);
84
+ this.finish(item, "succeeded");
85
+ } catch (err) {
86
+ const message = err instanceof Error ? err.message : String(err);
87
+ if (item.attempts <= item.maxRetries) this.retry(item, message);
88
+ else this.finish(item, "failed", message);
89
+ }
90
+ }
91
+ retry(item, error) {
92
+ const base = Math.max(1e3, this.opts.retryBaseMs ?? 5e3);
93
+ item.status = "retry_waiting";
94
+ item.lastError = error;
95
+ item.nextRunAt = Date.now() + base * 2 ** Math.max(0, item.attempts - 1);
96
+ item = this.store.markRetry(item.id, error, item.nextRunAt) ?? item;
97
+ log.warn({
98
+ goalId: item.goalId,
99
+ attempts: item.attempts,
100
+ nextRunAt: item.nextRunAt,
101
+ errorMessage: error
102
+ }, `Goal run queued for retry: ${error}`);
103
+ this.emit("goal.queue.updated", item);
104
+ this.schedulePump(Math.max(0, item.nextRunAt - Date.now()));
105
+ }
106
+ finish(item, status, error) {
107
+ item = this.store.markFinished(item.id, status, error) ?? {
108
+ ...item,
109
+ status,
110
+ finishedAt: Date.now(),
111
+ lastError: error
112
+ };
113
+ if (status === "failed") log.warn({
114
+ goalId: item.goalId,
115
+ attempts: item.attempts,
116
+ errorMessage: error
117
+ }, `Goal run failed: ${error}`);
118
+ this.emit("goal.queue.updated", item);
119
+ }
120
+ emit(type, item) {
121
+ this.opts.emit?.(type, { item: { ...item } });
122
+ }
123
+ };
124
+ //#endregion
125
+ export { GoalRunner };
126
+
127
+ //# sourceMappingURL=goal-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-runner.js","names":[],"sources":["../../../src/goals/goal-runner.ts"],"sourcesContent":["import { GoalQueueStore } from './goal-queue-store.js';\nimport { GoalService } from './goal-service.js';\nimport type { EnqueueGoalRunOptions, GoalQueueItemSnapshot, GoalQueueStatus, GoalRunnerOptions } from './goal-queue-types.js';\nimport type { GoalWithDetails } from './types.js';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('GoalRunner');\n\nfunction terminalGoalStatus(status: GoalWithDetails['status']): boolean {\n return status === 'done' || status === 'archived';\n}\n\nexport class GoalRunner {\n private readonly goals = new GoalService();\n private readonly store = new GoalQueueStore();\n private active = 0;\n private pumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly opts: GoalRunnerOptions) {\n this.store.resetRunningToRetry();\n this.schedulePump(0);\n }\n\n enqueue(goalId: string, options: EnqueueGoalRunOptions = {}): GoalQueueItemSnapshot {\n const item = this.store.enqueue({\n goalId,\n maxRetries: Math.max(0, Math.floor(options.maxRetries ?? this.opts.defaultMaxRetries ?? 2)),\n message: options.message?.trim() || undefined,\n source: options.source ?? 'api',\n });\n this.emit('goal.queue.updated', item);\n this.schedulePump(0);\n return item;\n }\n\n snapshot(): GoalQueueItemSnapshot[] {\n return this.store.list();\n }\n\n private schedulePump(delayMs: number): void {\n if (this.pumpTimer) return;\n this.pumpTimer = setTimeout(() => {\n this.pumpTimer = null;\n void this.pump();\n }, delayMs);\n }\n\n private async pump(): Promise<void> {\n const maxConcurrent = Math.max(1, Math.floor(this.opts.maxConcurrent ?? 1));\n while (this.active < maxConcurrent) {\n const item = this.store.claimNext();\n if (!item) break;\n this.active += 1;\n void this.runItem(item).finally(() => {\n this.active -= 1;\n this.schedulePump(0);\n });\n }\n\n const nextRetry = this.store.list()\n .filter((item) => item.status === 'retry_waiting' && item.nextRunAt)\n .sort((a, b) => (a.nextRunAt ?? 0) - (b.nextRunAt ?? 0))[0];\n if (nextRetry) this.schedulePump(Math.max(0, (nextRetry.nextRunAt ?? Date.now()) - Date.now()));\n }\n\n private async runItem(item: GoalQueueItemSnapshot): Promise<void> {\n this.emit('goal.queue.updated', item);\n try {\n const goal = this.goals.get(item.goalId);\n if (!goal) {\n this.finish(item, 'failed', 'Goal not found');\n return;\n }\n if (terminalGoalStatus(goal.status)) {\n this.finish(item, 'skipped', `Goal is ${goal.status}`);\n return;\n }\n const activeGoal =\n goal.status === 'paused' || goal.status === 'blocked' || goal.status === 'needs_input'\n ? this.goals.resume(goal.id)\n : goal;\n if (!activeGoal) {\n this.finish(item, 'failed', 'Goal not found');\n return;\n }\n const sessionKey = activeGoal.activeSessionKey ?? (await this.opts.ensureSession(activeGoal));\n item = this.store.setSessionKey(item.id, sessionKey) ?? { ...item, sessionKey };\n if (this.opts.hasActiveRun(sessionKey)) {\n this.retry(item, 'Goal session already has an active run');\n return;\n }\n const message = item.message || activeGoal.nextAction || activeGoal.title;\n await this.opts.runContinuation(sessionKey, message);\n this.finish(item, 'succeeded');\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (item.attempts <= item.maxRetries) {\n this.retry(item, message);\n } else {\n this.finish(item, 'failed', message);\n }\n }\n }\n\n private retry(item: GoalQueueItemSnapshot, error: string): void {\n const base = Math.max(1_000, this.opts.retryBaseMs ?? 5_000);\n item.status = 'retry_waiting';\n item.lastError = error;\n item.nextRunAt = Date.now() + base * 2 ** Math.max(0, item.attempts - 1);\n item = this.store.markRetry(item.id, error, item.nextRunAt) ?? item;\n log.warn({ goalId: item.goalId, attempts: item.attempts, nextRunAt: item.nextRunAt, errorMessage: error }, `Goal run queued for retry: ${error}`);\n this.emit('goal.queue.updated', item);\n this.schedulePump(Math.max(0, item.nextRunAt - Date.now()));\n }\n\n private finish(item: GoalQueueItemSnapshot, status: GoalQueueStatus, error?: string): void {\n item = this.store.markFinished(item.id, status, error) ?? { ...item, status, finishedAt: Date.now(), lastError: error };\n if (status === 'failed') {\n log.warn({ goalId: item.goalId, attempts: item.attempts, errorMessage: error }, `Goal run failed: ${error}`);\n }\n this.emit('goal.queue.updated', item);\n }\n\n private emit(type: string, item: GoalQueueItemSnapshot): void {\n this.opts.emit?.(type, { item: { ...item } });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import type { Config } from '../config/schema.js';
2
+ import { GoalStore } from './goal-store.js';
3
+ import type { CreateGoalInput, Goal, GoalChecklistAddedBy, GoalChecklistStatus, GoalJudgeDecision, GoalListQuery, GoalSource, GoalStatus, GoalUiLocale, GoalWithDetails } from './types.js';
4
+ export declare class GoalService {
5
+ private readonly store;
6
+ constructor(store?: GoalStore);
7
+ create(input: Omit<CreateGoalInput, 'maxTurns' | 'agentId'> & {
8
+ maxTurns?: number;
9
+ agentId?: string;
10
+ config?: Config;
11
+ }): Goal;
12
+ get(goalId: string): GoalWithDetails | null;
13
+ getActiveForSession(sessionKey: string): GoalWithDetails | null;
14
+ list(query?: GoalListQuery): GoalWithDetails[];
15
+ update(goalId: string, patch: Partial<Pick<Goal, 'title' | 'description' | 'priority' | 'deadlineAt' | 'maxTurns' | 'judgeModelRef' | 'nextAction' | 'blockedReason' | 'activeSessionKey' | 'uiLocale'>>): GoalWithDetails | null;
16
+ setStatus(goalId: string, status: GoalStatus, opts?: {
17
+ reason?: string;
18
+ }): GoalWithDetails | null;
19
+ pause(goalId: string, reason?: string): GoalWithDetails | null;
20
+ resume(goalId: string): GoalWithDetails | null;
21
+ reopen(goalId: string): GoalWithDetails | null;
22
+ complete(goalId: string): GoalWithDetails | null;
23
+ archive(goalId: string): GoalWithDetails | null;
24
+ unarchive(goalId: string): GoalWithDetails | null;
25
+ attachSession(goalId: string, sessionKey: string): GoalWithDetails | null;
26
+ updateChecklist(goalId: string, op: {
27
+ type: 'add';
28
+ text: string;
29
+ addedBy?: GoalChecklistAddedBy;
30
+ } | {
31
+ type: 'mark';
32
+ itemId: string;
33
+ status: GoalChecklistStatus;
34
+ evidenceSummary?: string;
35
+ } | {
36
+ type: 'remove';
37
+ itemId: string;
38
+ } | {
39
+ type: 'reset';
40
+ }): GoalWithDetails | null;
41
+ recordJudgeDecision(input: {
42
+ goalId: string;
43
+ sessionKey: string;
44
+ source: GoalSource;
45
+ decision: GoalJudgeDecision;
46
+ }): GoalWithDetails | null;
47
+ syncPostTurnState(input: {
48
+ goalId: string;
49
+ sessionKey: string;
50
+ source: GoalSource;
51
+ status: GoalStatus;
52
+ turnsUsed: number;
53
+ maxTurns: number;
54
+ reason?: string;
55
+ nextAction?: string;
56
+ assistantPreview?: string;
57
+ verdict: 'continue' | 'done' | 'blocked' | 'needs_input' | 'decompose';
58
+ confidence?: number;
59
+ missingEvidence?: string[];
60
+ userQuestion?: string;
61
+ completedChecklistItemIds?: string[];
62
+ checklist?: Array<{
63
+ text: string;
64
+ status: 'pending' | 'completed' | 'impossible';
65
+ addedBy: 'user' | 'judge';
66
+ addedAt?: number;
67
+ completedAt?: number;
68
+ evidenceSummary?: string;
69
+ }>;
70
+ }): GoalWithDetails | null;
71
+ listRuns(goalId: string, limit?: number): import("./types.js").GoalRun[];
72
+ listRunsForSession(sessionKey: string, limit?: number): import("./types.js").GoalRun[];
73
+ listEvents(goalId: string, limit?: number): import("./types.js").GoalEvent[];
74
+ addEvidence(input: Parameters<GoalStore['addEvidence']>[0]): import("./types.js").GoalEvidence;
75
+ listEvidence(goalId: string, limit?: number): import("./types.js").GoalEvidence[];
76
+ }
77
+ export declare function normalizeGoalUiLocale(raw: unknown): GoalUiLocale | undefined;