@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
@@ -1,7 +1,9 @@
1
1
  import type { AgentTool } from '@earendil-works/pi-agent-core';
2
+ import type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';
2
3
  export interface CreateWriteFileToolOptions {
3
4
  /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */
4
5
  profileMarkdownRoot?: string;
6
+ recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;
5
7
  }
6
8
  export declare function createWriteFileTool(workspace: string, options?: CreateWriteFileToolOptions): AgentTool;
7
9
  export declare const writeFileTool: AgentTool;
@@ -52,6 +52,17 @@ function createWriteFileTool(workspace, options) {
52
52
  const target = writesProfileFile ? resolveProfileMarkdownPathIfBareName(p.path, options.profileMarkdownRoot) : resolvePathUnderWorkspace(p.path, workspace);
53
53
  await mkdir(dirname(target), { recursive: true });
54
54
  await writeFile(target, p.content, "utf-8");
55
+ await options?.recordGoalEvidence?.({
56
+ kind: "file",
57
+ title: `File written: ${target}`,
58
+ summary: `${contentBytes} bytes written`,
59
+ uri: target,
60
+ data: {
61
+ path: target,
62
+ size: contentBytes,
63
+ operation: "write"
64
+ }
65
+ });
55
66
  return {
56
67
  content: [{
57
68
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","names":[],"sources":["../../../../src/agent/tools/write.ts"],"sourcesContent":["// Write file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { writeFile, mkdir } from 'fs/promises';\nimport { dirname } from 'path';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport {\n isBareProfileMarkdownFileName,\n resolveProfileMarkdownPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst WriteFileSchema = Type.Object({\n path: Type.String({ description: 'File path to write' }),\n content: Type.String({ description: 'Content to write' }),\n});\n\ntype WriteFileParams = { path: string; content: string };\n\nexport interface CreateWriteFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */\n profileMarkdownRoot?: string;\n}\n\nexport function createWriteFileTool(\n workspace: string,\n options?: CreateWriteFileToolOptions,\n): AgentTool {\n return {\n name: 'write_file',\n description:\n 'Create or overwrite a file. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is written automatically when given by filename.',\n parameters: WriteFileSchema,\n label: '📝 Write',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n try {\n const p = params as WriteFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n const writesProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = writesProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'write',\n path: p.path,\n workspaceRoot,\n });\n if (!pathPolicy.allowed) {\n return { content: [{ type: 'text', text: `🚫 Sandbox: ${pathPolicy.reason}` }], details: {} };\n }\n\n const contentBytes = Buffer.byteLength(p.content, 'utf-8');\n if (contentBytes > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${contentBytes} bytes` }], details: {} };\n }\n\n const target = writesProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, p.content, 'utf-8');\n return { content: [{ type: 'text', text: `File written: ${target}` }], details: { size: contentBytes } };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n },\n } as any;\n}\n\nexport const writeFileTool: AgentTool = createWriteFileTool(process.cwd());\n"],"mappings":";;;;;;;AAaA,MAAM,gBAAgB,KAAK,OAAO;AAElC,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,OAAO,EAAE,aAAa,sBAAsB,CAAC;CACxD,SAAS,KAAK,OAAO,EAAE,aAAa,oBAAoB,CAAC;CAC1D,CAAC;AASF,SAAgB,oBACd,WACA,SACW;AACX,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,OAAI;IACF,MAAM,IAAI;IACV,MAAM,SAAS,gBAAgB,SAAS,EAAE,KAAK;AAC/C,QAAI,CAAC,OAAO,QACV,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAAM,OAAO;MAAW,CAAC;KAAE,SAAS,EAAE;KAAE;IAGnF,MAAM,oBAAoB,QACxB,SAAS,uBAAuB,8BAA8B,EAAE,KAAK,CACtE;IACD,MAAM,gBAAgB,oBAAoB,QAAS,sBAAuB;IAG1E,MAAM,aAAa,mBAAmB;KACpC,WAAW;KACX,MAAM,EAAE;KACR;KACD,CAAC;AACF,QAAI,CAAC,WAAW,QACd,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe,WAAW;MAAU,CAAC;KAAE,SAAS,EAAE;KAAE;IAG/F,MAAM,eAAe,OAAO,WAAW,EAAE,SAAS,QAAQ;AAC1D,QAAI,eAAe,cACjB,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,sBAAsB,aAAa;MAAS,CAAC;KAAE,SAAS,EAAE;KAAE;IAGvG,MAAM,SAAS,oBACX,qCAAqC,EAAE,MAAM,QAAS,oBAAqB,GAC3E,0BAA0B,EAAE,MAAM,UAAU;AAChD,UAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,UAAM,UAAU,QAAQ,EAAE,SAAS,QAAQ;AAC3C,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAU,CAAC;KAAE,SAAS,EAAE,MAAM,cAAc;KAAE;YACjG,OAAO;AACd,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;MAAI,CAAC;KAAE,SAAS,EAAE;KAAE;;;EAGlI;;AAGH,MAAa,gBAA2B,oBAAoB,QAAQ,KAAK,CAAC"}
1
+ {"version":3,"file":"write.js","names":[],"sources":["../../../../src/agent/tools/write.ts"],"sourcesContent":["// Write file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { writeFile, mkdir } from 'fs/promises';\nimport { dirname } from 'path';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport {\n isBareProfileMarkdownFileName,\n resolveProfileMarkdownPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst WriteFileSchema = Type.Object({\n path: Type.String({ description: 'File path to write' }),\n content: Type.String({ description: 'Content to write' }),\n});\n\ntype WriteFileParams = { path: string; content: string };\n\nexport interface CreateWriteFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */\n profileMarkdownRoot?: string;\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\nexport function createWriteFileTool(\n workspace: string,\n options?: CreateWriteFileToolOptions,\n): AgentTool {\n return {\n name: 'write_file',\n description:\n 'Create or overwrite a file. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is written automatically when given by filename.',\n parameters: WriteFileSchema,\n label: '📝 Write',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n try {\n const p = params as WriteFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n const writesProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = writesProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'write',\n path: p.path,\n workspaceRoot,\n });\n if (!pathPolicy.allowed) {\n return { content: [{ type: 'text', text: `🚫 Sandbox: ${pathPolicy.reason}` }], details: {} };\n }\n\n const contentBytes = Buffer.byteLength(p.content, 'utf-8');\n if (contentBytes > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${contentBytes} bytes` }], details: {} };\n }\n\n const target = writesProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, p.content, 'utf-8');\n await options?.recordGoalEvidence?.({\n kind: 'file',\n title: `File written: ${target}`,\n summary: `${contentBytes} bytes written`,\n uri: target,\n data: { path: target, size: contentBytes, operation: 'write' },\n });\n return { content: [{ type: 'text', text: `File written: ${target}` }], details: { size: contentBytes } };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n },\n } as any;\n}\n\nexport const writeFileTool: AgentTool = createWriteFileTool(process.cwd());\n"],"mappings":";;;;;;;AAcA,MAAM,gBAAgB,KAAK,OAAO;AAElC,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,OAAO,EAAE,aAAa,sBAAsB,CAAC;CACxD,SAAS,KAAK,OAAO,EAAE,aAAa,oBAAoB,CAAC;CAC1D,CAAC;AAUF,SAAgB,oBACd,WACA,SACW;AACX,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,OAAI;IACF,MAAM,IAAI;IACV,MAAM,SAAS,gBAAgB,SAAS,EAAE,KAAK;AAC/C,QAAI,CAAC,OAAO,QACV,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAAM,OAAO;MAAW,CAAC;KAAE,SAAS,EAAE;KAAE;IAGnF,MAAM,oBAAoB,QACxB,SAAS,uBAAuB,8BAA8B,EAAE,KAAK,CACtE;IACD,MAAM,gBAAgB,oBAAoB,QAAS,sBAAuB;IAG1E,MAAM,aAAa,mBAAmB;KACpC,WAAW;KACX,MAAM,EAAE;KACR;KACD,CAAC;AACF,QAAI,CAAC,WAAW,QACd,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe,WAAW;MAAU,CAAC;KAAE,SAAS,EAAE;KAAE;IAG/F,MAAM,eAAe,OAAO,WAAW,EAAE,SAAS,QAAQ;AAC1D,QAAI,eAAe,cACjB,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,sBAAsB,aAAa;MAAS,CAAC;KAAE,SAAS,EAAE;KAAE;IAGvG,MAAM,SAAS,oBACX,qCAAqC,EAAE,MAAM,QAAS,oBAAqB,GAC3E,0BAA0B,EAAE,MAAM,UAAU;AAChD,UAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,UAAM,UAAU,QAAQ,EAAE,SAAS,QAAQ;AAC3C,UAAM,SAAS,qBAAqB;KAClC,MAAM;KACN,OAAO,iBAAiB;KACxB,SAAS,GAAG,aAAa;KACzB,KAAK;KACL,MAAM;MAAE,MAAM;MAAQ,MAAM;MAAc,WAAW;MAAS;KAC/D,CAAC;AACF,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAU,CAAC;KAAE,SAAS,EAAE,MAAM,cAAc;KAAE;YACjG,OAAO;AACd,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;MAAI,CAAC;KAAE,SAAS,EAAE;KAAE;;;EAGlI;;AAGH,MAAa,gBAA2B,oBAAoB,QAAQ,KAAK,CAAC"}
@@ -12,6 +12,13 @@ export declare function decodeInboundAttachmentBase64(data: string): Buffer;
12
12
  */
13
13
  export declare function persistInboundAttachments(attachments: InboundAttachmentInput[] | undefined, opts?: {
14
14
  maxBytes?: number;
15
+ resolveUri?: (uri: string, attachment: InboundAttachmentInput) => Promise<{
16
+ buffer: Buffer;
17
+ path: string;
18
+ mimeType: string;
19
+ name: string;
20
+ size: number;
21
+ } | null>;
15
22
  }): Promise<MediaRef[] | undefined>;
16
23
  /** Read attachment bytes by media URI (voice STT, tools). */
17
24
  export declare function readInboundAttachmentBuffer(uri: string): Promise<Buffer>;
@@ -23,8 +23,26 @@ async function persistInboundAttachments(attachments, opts) {
23
23
  const out = [];
24
24
  for (const att of attachments) {
25
25
  if (att.uri?.trim()) {
26
- const resolved = await resolveMediaReference(att.uri.trim());
27
- out.push(mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id));
26
+ const uri = att.uri.trim();
27
+ if (uri.startsWith("media://")) {
28
+ const resolved = await resolveMediaReference(uri);
29
+ out.push(mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id));
30
+ } else {
31
+ const resolved = await opts?.resolveUri?.(uri, att);
32
+ if (!resolved) throw new Error(`Inbound attachment "${att.name ?? "file"}" has unsupported uri`);
33
+ const saved = await saveMediaBuffer(resolved.buffer, {
34
+ contentType: resolved.mimeType,
35
+ bucket: "inbound",
36
+ maxBytes,
37
+ originalFilename: resolved.name
38
+ });
39
+ out.push(toMediaRef({
40
+ ...att,
41
+ mimeType: resolved.mimeType,
42
+ name: resolved.name,
43
+ size: resolved.size
44
+ }, saved));
45
+ }
28
46
  continue;
29
47
  }
30
48
  if (!att.data || att.data.length === 0) throw new Error(`Inbound attachment "${att.name ?? "file"}" is missing data and uri`);
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-persist.js","names":[],"sources":["../../../../src/channels/attachments/inbound-persist.ts"],"sourcesContent":["/**\n * Persist inbound uploads to the global media store (`{stateDir}/media/inbound/`).\n */\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n MEDIA_MAX_BYTES,\n parseMediaUri,\n readMediaReference,\n resolveMediaReference,\n saveMediaBuffer,\n} from '../../media/index.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nimport { mediaRefFromUri, toMediaRef } from './inbound-types.js';\n\nconst log = createLogger('InboundPersist');\n\nexport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nexport { isImageInboundAttachment, isVoiceInboundAttachment } from './attachment-classifiers.js';\nexport { toMediaRef, mediaRefFromUri } from './inbound-types.js';\n\n/** Decode base64 or data-URL payload (composer wire + voice STT). */\nexport function decodeInboundAttachmentBase64(data: string): Buffer {\n const trimmed = data.trim();\n const b64 = trimmed.startsWith('data:') ? (trimmed.split(/base64,/)[1] ?? trimmed) : trimmed;\n return Buffer.from(b64.replace(/\\s/g, ''), 'base64');\n}\n\n/**\n * Write attachments with binary `data` to the media store; returns metadata-only refs.\n */\nexport async function persistInboundAttachments(\n attachments: InboundAttachmentInput[] | undefined,\n opts?: { maxBytes?: number },\n): Promise<MediaRef[] | undefined> {\n if (!attachments?.length) return undefined;\n\n const maxBytes = opts?.maxBytes ?? MEDIA_MAX_BYTES;\n const out: MediaRef[] = [];\n\n for (const att of attachments) {\n if (att.uri?.trim()) {\n const resolved = await resolveMediaReference(att.uri.trim());\n out.push(\n mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id),\n );\n continue;\n }\n\n if (!att.data || att.data.length === 0) {\n throw new Error(`Inbound attachment \"${att.name ?? 'file'}\" is missing data and uri`);\n }\n\n const buf = decodeInboundAttachmentBase64(att.data);\n const saved = await saveMediaBuffer(buf, {\n contentType: att.mimeType,\n bucket: 'inbound',\n maxBytes,\n originalFilename: att.name,\n });\n\n log.debug({ uri: saved.uri, bytes: saved.size, name: att.name }, 'Inbound attachment persisted');\n\n out.push(toMediaRef(att, saved));\n }\n\n return out.length ? out : undefined;\n}\n\n/** Read attachment bytes by media URI (voice STT, tools). */\nexport async function readInboundAttachmentBuffer(uri: string): Promise<Buffer> {\n const { buffer } = await readMediaReference(uri);\n return buffer;\n}\n\nexport function assertMediaUri(uri: string): void {\n parseMediaUri(uri);\n}\n"],"mappings":";;;;;;;;aAIqD;AAWrD,MAAM,MAAM,aAAa,iBAAiB;;AAO1C,SAAgB,8BAA8B,MAAsB;CAClE,MAAM,UAAU,KAAK,MAAM;CAC3B,MAAM,MAAM,QAAQ,WAAW,QAAQ,GAAI,QAAQ,MAAM,UAAU,CAAC,MAAM,UAAW;AACrF,QAAO,OAAO,KAAK,IAAI,QAAQ,OAAO,GAAG,EAAE,SAAS;;;;;AAMtD,eAAsB,0BACpB,aACA,MACiC;AACjC,KAAI,CAAC,aAAa,OAAQ,QAAO,KAAA;CAEjC,MAAM,WAAW,MAAM,YAAA;CACvB,MAAM,MAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,aAAa;AAC7B,MAAI,IAAI,KAAK,MAAM,EAAE;GACnB,MAAM,WAAW,MAAM,sBAAsB,IAAI,IAAI,MAAM,CAAC;AAC5D,OAAI,KACF,gBAAgB,KAAK,SAAS,KAAK,SAAS,MAAM,SAAS,QAAQ,SAAS,GAAG,CAChF;AACD;;AAGF,MAAI,CAAC,IAAI,QAAQ,IAAI,KAAK,WAAW,EACnC,OAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ,OAAO,2BAA2B;EAIvF,MAAM,QAAQ,MAAM,gBADR,8BAA8B,IAAI,KACP,EAAE;GACvC,aAAa,IAAI;GACjB,QAAQ;GACR;GACA,kBAAkB,IAAI;GACvB,CAAC;AAEF,MAAI,MAAM;GAAE,KAAK,MAAM;GAAK,OAAO,MAAM;GAAM,MAAM,IAAI;GAAM,EAAE,+BAA+B;AAEhG,MAAI,KAAK,WAAW,KAAK,MAAM,CAAC;;AAGlC,QAAO,IAAI,SAAS,MAAM,KAAA;;;AAI5B,eAAsB,4BAA4B,KAA8B;CAC9E,MAAM,EAAE,WAAW,MAAM,mBAAmB,IAAI;AAChD,QAAO;;AAGT,SAAgB,eAAe,KAAmB;AAChD,eAAc,IAAI"}
1
+ {"version":3,"file":"inbound-persist.js","names":[],"sources":["../../../../src/channels/attachments/inbound-persist.ts"],"sourcesContent":["/**\n * Persist inbound uploads to the global media store (`{stateDir}/media/inbound/`).\n */\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n MEDIA_MAX_BYTES,\n parseMediaUri,\n readMediaReference,\n resolveMediaReference,\n saveMediaBuffer,\n} from '../../media/index.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nimport { mediaRefFromUri, toMediaRef } from './inbound-types.js';\n\nconst log = createLogger('InboundPersist');\n\nexport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nexport { isImageInboundAttachment, isVoiceInboundAttachment } from './attachment-classifiers.js';\nexport { toMediaRef, mediaRefFromUri } from './inbound-types.js';\n\n/** Decode base64 or data-URL payload (composer wire + voice STT). */\nexport function decodeInboundAttachmentBase64(data: string): Buffer {\n const trimmed = data.trim();\n const b64 = trimmed.startsWith('data:') ? (trimmed.split(/base64,/)[1] ?? trimmed) : trimmed;\n return Buffer.from(b64.replace(/\\s/g, ''), 'base64');\n}\n\n/**\n * Write attachments with binary `data` to the media store; returns metadata-only refs.\n */\nexport async function persistInboundAttachments(\n attachments: InboundAttachmentInput[] | undefined,\n opts?: {\n maxBytes?: number;\n resolveUri?: (uri: string, attachment: InboundAttachmentInput) => Promise<{\n buffer: Buffer;\n path: string;\n mimeType: string;\n name: string;\n size: number;\n } | null>;\n },\n): Promise<MediaRef[] | undefined> {\n if (!attachments?.length) return undefined;\n\n const maxBytes = opts?.maxBytes ?? MEDIA_MAX_BYTES;\n const out: MediaRef[] = [];\n\n for (const att of attachments) {\n if (att.uri?.trim()) {\n const uri = att.uri.trim();\n if (uri.startsWith('media://')) {\n const resolved = await resolveMediaReference(uri);\n out.push(\n mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id),\n );\n } else {\n const resolved = await opts?.resolveUri?.(uri, att);\n if (!resolved) {\n throw new Error(`Inbound attachment \"${att.name ?? 'file'}\" has unsupported uri`);\n }\n const saved = await saveMediaBuffer(resolved.buffer, {\n contentType: resolved.mimeType,\n bucket: 'inbound',\n maxBytes,\n originalFilename: resolved.name,\n });\n out.push(toMediaRef({ ...att, mimeType: resolved.mimeType, name: resolved.name, size: resolved.size }, saved));\n }\n continue;\n }\n\n if (!att.data || att.data.length === 0) {\n throw new Error(`Inbound attachment \"${att.name ?? 'file'}\" is missing data and uri`);\n }\n\n const buf = decodeInboundAttachmentBase64(att.data);\n const saved = await saveMediaBuffer(buf, {\n contentType: att.mimeType,\n bucket: 'inbound',\n maxBytes,\n originalFilename: att.name,\n });\n\n log.debug({ uri: saved.uri, bytes: saved.size, name: att.name }, 'Inbound attachment persisted');\n\n out.push(toMediaRef(att, saved));\n }\n\n return out.length ? out : undefined;\n}\n\n/** Read attachment bytes by media URI (voice STT, tools). */\nexport async function readInboundAttachmentBuffer(uri: string): Promise<Buffer> {\n const { buffer } = await readMediaReference(uri);\n return buffer;\n}\n\nexport function assertMediaUri(uri: string): void {\n parseMediaUri(uri);\n}\n"],"mappings":";;;;;;;;aAIqD;AAWrD,MAAM,MAAM,aAAa,iBAAiB;;AAO1C,SAAgB,8BAA8B,MAAsB;CAClE,MAAM,UAAU,KAAK,MAAM;CAC3B,MAAM,MAAM,QAAQ,WAAW,QAAQ,GAAI,QAAQ,MAAM,UAAU,CAAC,MAAM,UAAW;AACrF,QAAO,OAAO,KAAK,IAAI,QAAQ,OAAO,GAAG,EAAE,SAAS;;;;;AAMtD,eAAsB,0BACpB,aACA,MAUiC;AACjC,KAAI,CAAC,aAAa,OAAQ,QAAO,KAAA;CAEjC,MAAM,WAAW,MAAM,YAAA;CACvB,MAAM,MAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,aAAa;AAC7B,MAAI,IAAI,KAAK,MAAM,EAAE;GACnB,MAAM,MAAM,IAAI,IAAI,MAAM;AAC1B,OAAI,IAAI,WAAW,WAAW,EAAE;IAC9B,MAAM,WAAW,MAAM,sBAAsB,IAAI;AACjD,QAAI,KACF,gBAAgB,KAAK,SAAS,KAAK,SAAS,MAAM,SAAS,QAAQ,SAAS,GAAG,CAChF;UACI;IACL,MAAM,WAAW,MAAM,MAAM,aAAa,KAAK,IAAI;AACnD,QAAI,CAAC,SACH,OAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ,OAAO,uBAAuB;IAEnF,MAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ;KACnD,aAAa,SAAS;KACtB,QAAQ;KACR;KACA,kBAAkB,SAAS;KAC5B,CAAC;AACF,QAAI,KAAK,WAAW;KAAE,GAAG;KAAK,UAAU,SAAS;KAAU,MAAM,SAAS;KAAM,MAAM,SAAS;KAAM,EAAE,MAAM,CAAC;;AAEhH;;AAGF,MAAI,CAAC,IAAI,QAAQ,IAAI,KAAK,WAAW,EACnC,OAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ,OAAO,2BAA2B;EAIvF,MAAM,QAAQ,MAAM,gBADR,8BAA8B,IAAI,KACP,EAAE;GACvC,aAAa,IAAI;GACjB,QAAQ;GACR;GACA,kBAAkB,IAAI;GACvB,CAAC;AAEF,MAAI,MAAM;GAAE,KAAK,MAAM;GAAK,OAAO,MAAM;GAAM,MAAM,IAAI;GAAM,EAAE,+BAA+B;AAEhG,MAAI,KAAK,WAAW,KAAK,MAAM,CAAC;;AAGlC,QAAO,IAAI,SAAS,MAAM,KAAA;;;AAI5B,eAAsB,4BAA4B,KAA8B;CAC9E,MAAM,EAAE,WAAW,MAAM,mBAAmB,IAAI;AAChD,QAAO;;AAGT,SAAgB,eAAe,KAAmB;AAChD,eAAc,IAAI"}
@@ -1,4 +1,4 @@
1
1
  /**
2
- * Persistent `/goal` (Hermes-style Ralph loop) on any channel with session metadata.
2
+ * `/goal` — first-class goal control from any command surface.
3
3
  */
4
4
  export declare function registerGoalCommand(): void;
@@ -1,33 +1,29 @@
1
+ import { getDefaultAgentId, init_resolve_route } from "../../routing/resolve-route.js";
2
+ import { GoalService } from "../../goals/goal-service.js";
3
+ import "../../goals/index.js";
1
4
  import { commandRegistry } from "../registry.js";
2
- import { PERSISTENT_GOAL_CUSTOM_KEY, defaultMaxTurns, mergeCustomDataPatch, readPersistentGoal, serializePersistentGoal } from "../../agent/goals/state.js";
3
- import { applyPersistentGoalUserAction } from "../../agent/goals/patch-from-user-action.js";
4
5
  //#region src/chat-commands/builtins/goal.ts
5
- function statusLine(state) {
6
- const turns = `${state.turnsUsed}/${state.maxTurns} turns`;
7
- if (state.status === "active") return `⊙ Goal (active, ${turns}): ${state.goal}`;
8
- if (state.status === "paused") return `⏸ Goal (paused, ${turns}${state.pausedReason ? ` — ${state.pausedReason}` : ""}): ${state.goal}`;
9
- if (state.status === "done") return `✓ Goal done (${turns}): ${state.goal}`;
10
- return `Goal (${state.status}, ${turns}): ${state.goal}`;
6
+ init_resolve_route();
7
+ const goals = new GoalService();
8
+ function statusLine(goal) {
9
+ const turns = `${goal.turnsUsed}/${goal.maxTurns} turns`;
10
+ const progress = goal.checklist.length ? ` · ${goal.checklist.filter((it) => it.status === "completed" || it.status === "impossible").length}/${goal.checklist.length} criteria` : "";
11
+ if (goal.status === "active") return `⊙ Goal (active, ${turns}${progress}): ${goal.title}`;
12
+ if (goal.status === "paused") return `⏸ Goal (paused, ${turns}${progress}): ${goal.title}`;
13
+ if (goal.status === "blocked" || goal.status === "needs_input") {
14
+ const reason = goal.blockedReason ? ` — ${goal.blockedReason}` : "";
15
+ return `⏸ Goal (${goal.status}, ${turns}${progress}${reason}): ${goal.title}`;
16
+ }
17
+ if (goal.status === "done") return `✓ Goal done (${turns}${progress}): ${goal.title}`;
18
+ return `Goal (${goal.status}, ${turns}${progress}): ${goal.title}`;
11
19
  }
12
- async function patchGoalMetadata(ctx, patchCustom) {
13
- const apis = ctx.persistentGoalApis;
14
- if (!apis) return {
15
- ok: false,
16
- error: "Goals unavailable on this surface."
17
- };
18
- const meta = await apis.getSessionMetadata(ctx.sessionKey);
19
- if (!meta) return {
20
- ok: false,
21
- error: "Session not found"
22
- };
23
- const customData = patchCustom({ ...meta.customData });
24
- await apis.updateSessionMetadata(ctx.sessionKey, { customData });
25
- return { ok: true };
20
+ function activeGoal(ctx) {
21
+ return goals.getActiveForSession(ctx.sessionKey);
26
22
  }
27
23
  const goalCommand = {
28
24
  id: "system.goal",
29
25
  name: "goal",
30
- description: "Set or manage a standing goal (Hermes-style): checklist + judge loop; /subgoal edits criteria; pause, resume, clear",
26
+ description: "Create and manage a durable goal: checklist + judge loop; /subgoal edits criteria",
31
27
  category: "system",
32
28
  scope: [
33
29
  "global",
@@ -43,111 +39,73 @@ const goalCommand = {
43
39
  "/goal clear"
44
40
  ],
45
41
  handler: async (ctx, args) => {
46
- const apis = ctx.persistentGoalApis;
47
- if (!apis) return {
48
- content: "Goals unavailable on this surface.",
49
- success: false
50
- };
51
42
  const t = args.trim();
52
43
  const lower = t.toLowerCase();
53
44
  if (!t || lower === "status") {
54
- const s = readPersistentGoal((await apis.getSessionMetadata(ctx.sessionKey))?.customData);
55
- if (!s || s.status === "cleared") return {
56
- content: "No active goal. Set one with /goal <text>.",
57
- success: true
58
- };
45
+ const goal = activeGoal(ctx);
59
46
  return {
60
- content: statusLine(s),
47
+ content: goal ? statusLine(goal) : "No active goal. Set one with /goal <text>.",
61
48
  success: true
62
49
  };
63
50
  }
64
51
  if (lower === "pause") {
65
- const applied = applyPersistentGoalUserAction((await apis.getSessionMetadata(ctx.sessionKey))?.customData, "pause");
66
- if (applied.kind === "error") return {
67
- content: applied.error,
68
- success: false
69
- };
70
- if (applied.kind === "noop") return {
71
- content: applied.message,
52
+ const goal = activeGoal(ctx);
53
+ if (!goal) return {
54
+ content: "No active goal.",
72
55
  success: true
73
56
  };
74
- const r = await patchGoalMetadata(ctx, () => applied.customData);
75
- if (!r.ok) return {
76
- content: "error" in r ? r.error : "Failed",
77
- success: false
78
- };
79
57
  return {
80
- content: `⏸ Goal paused: ${readPersistentGoal(applied.customData)?.goal ?? ""}`,
58
+ content: `⏸ Goal paused: ${goals.pause(goal.id)?.title ?? goal.title}`,
81
59
  success: true
82
60
  };
83
61
  }
84
62
  if (lower === "resume") {
85
- const applied = applyPersistentGoalUserAction((await apis.getSessionMetadata(ctx.sessionKey))?.customData, "resume");
86
- if (applied.kind === "error") return {
87
- content: applied.error,
88
- success: false
89
- };
90
- if (applied.kind === "noop") return {
91
- content: applied.message,
63
+ const goal = activeGoal(ctx);
64
+ if (!goal) return {
65
+ content: "No goal to resume.",
92
66
  success: true
93
67
  };
94
- const r = await patchGoalMetadata(ctx, () => applied.customData);
95
- if (!r.ok) return {
96
- content: "error" in r ? r.error : "Failed",
97
- success: false
98
- };
68
+ const next = goals.resume(goal.id);
69
+ ctx.persistentGoalApis?.scheduleContinuation(ctx.sessionKey, next?.nextAction || next?.title || goal.title);
99
70
  return {
100
- content: `▶ Goal resumed: ${readPersistentGoal(applied.customData)?.goal ?? ""}\nSend any message to continue, or wait — the next assistant turn will pick up from here.`,
71
+ content: `▶ Goal resumed: ${next?.title ?? goal.title}`,
101
72
  success: true
102
73
  };
103
74
  }
104
- if (lower === "clear" || lower === "stop" || lower === "done") {
105
- const meta = await apis.getSessionMetadata(ctx.sessionKey);
106
- const had = !!readPersistentGoal(meta?.customData);
107
- const applied = applyPersistentGoalUserAction(meta?.customData, "clear");
108
- if (applied.kind === "error") return {
109
- content: applied.error,
110
- success: false
111
- };
112
- if (applied.kind === "noop") return {
113
- content: applied.message,
75
+ if (lower === "clear" || lower === "stop" || lower === "archive") {
76
+ const goal = activeGoal(ctx);
77
+ if (!goal) return {
78
+ content: "No active goal.",
114
79
  success: true
115
80
  };
116
- const r = await patchGoalMetadata(ctx, () => applied.customData);
117
- if (!r.ok) return {
118
- content: "error" in r ? r.error : "Failed",
119
- success: false
120
- };
81
+ goals.archive(goal.id);
82
+ try {
83
+ await ctx.abortCurrentTurn?.();
84
+ } catch {
85
+ return {
86
+ content: "✓ Goal archived. Current turn could not be stopped automatically.",
87
+ success: true
88
+ };
89
+ }
121
90
  return {
122
- content: had ? "✓ Goal cleared." : "No active goal.",
91
+ content: "✓ Goal archived.",
123
92
  success: true
124
93
  };
125
94
  }
126
- if (apis.inboundConcurrentDepth(ctx.sessionKey) > 1) return {
95
+ if ((ctx.persistentGoalApis?.inboundConcurrentDepth(ctx.sessionKey) ?? 0) > 1) return {
127
96
  content: "Agent is running — use /goal status / pause / clear mid-run, or wait for the current turn to finish before setting a new goal.",
128
97
  success: false
129
98
  };
130
- const maxTurns = defaultMaxTurns(ctx.config.goals);
131
- const judgeModelRef = typeof ctx.config.goals?.judgeModelRef === "string" ? ctx.config.goals.judgeModelRef.trim() : void 0;
132
- const next = {
133
- goal: t,
134
- status: "active",
135
- turnsUsed: 0,
136
- maxTurns,
137
- createdAt: Date.now(),
138
- lastTurnAt: 0,
139
- decomposed: false,
140
- consecutiveParseFailures: 0,
141
- ...judgeModelRef ? { judgeModelRef } : {}
142
- };
143
- const r = await patchGoalMetadata(ctx, (base) => mergeCustomDataPatch(base, { [PERSISTENT_GOAL_CUSTOM_KEY]: serializePersistentGoal(next) }));
144
- if (!r.ok) return {
145
- content: "error" in r ? r.error : "Failed",
146
- success: false
147
- };
148
- apis.scheduleContinuation(ctx.sessionKey, next.goal);
99
+ const goal = goals.create({
100
+ title: t,
101
+ sessionKey: ctx.sessionKey,
102
+ agentId: getDefaultAgentId(ctx.config),
103
+ config: ctx.config,
104
+ source: ctx.source === "cli" ? "cli" : ctx.source === "api" ? "api" : "channel"
105
+ });
106
+ ctx.persistentGoalApis?.scheduleContinuation(ctx.sessionKey, goal.title);
149
107
  return {
150
- content: `⊙ Goal set (${next.maxTurns}-turn budget): ${next.goal}\nI'll keep working until the goal is done, you pause/clear it, or the budget is exhausted.
108
+ content: `⊙ Goal set (${goal.maxTurns}-turn budget): ${goal.title}\nI'll keep working until the goal is done, you pause/archive it, or the budget is exhausted.
151
109
  Controls: /goal status · /goal pause · /goal resume · /goal clear · /subgoal`,
152
110
  success: true
153
111
  };
@@ -1 +1 @@
1
- {"version":3,"file":"goal.js","names":[],"sources":["../../../../src/chat-commands/builtins/goal.ts"],"sourcesContent":["/**\n * Persistent `/goal` (Hermes-style Ralph loop) on any channel with session metadata.\n */\n\nimport type { CommandContext, CommandDefinition } from '../types.js';\nimport { commandRegistry } from '../registry.js';\n\nimport {\n defaultMaxTurns,\n mergeCustomDataPatch,\n readPersistentGoal,\n serializePersistentGoal,\n PERSISTENT_GOAL_CUSTOM_KEY,\n type PersistentGoalState,\n} from '../../agent/goals/state.js';\nimport { applyPersistentGoalUserAction } from '../../agent/goals/patch-from-user-action.js';\n\nfunction statusLine(state: NonNullable<ReturnType<typeof readPersistentGoal>>): string {\n const turns = `${state.turnsUsed}/${state.maxTurns} turns`;\n if (state.status === 'active') {\n return `⊙ Goal (active, ${turns}): ${state.goal}`;\n }\n if (state.status === 'paused') {\n const extra = state.pausedReason ? ` — ${state.pausedReason}` : '';\n return `⏸ Goal (paused, ${turns}${extra}): ${state.goal}`;\n }\n if (state.status === 'done') {\n return `✓ Goal done (${turns}): ${state.goal}`;\n }\n return `Goal (${state.status}, ${turns}): ${state.goal}`;\n}\n\nasync function patchGoalMetadata(\n ctx: CommandContext,\n patchCustom: (base: Record<string, unknown>) => Record<string, unknown>,\n): Promise<{ ok: true } | { ok: false; error: string }> {\n const apis = ctx.persistentGoalApis;\n if (!apis) return { ok: false, error: 'Goals unavailable on this surface.' };\n const meta = await apis.getSessionMetadata(ctx.sessionKey);\n if (!meta) return { ok: false, error: 'Session not found' };\n const base = { ...(meta.customData as Record<string, unknown> | undefined) };\n const customData = patchCustom(base);\n await apis.updateSessionMetadata(ctx.sessionKey, { customData });\n return { ok: true };\n}\n\nconst goalCommand: CommandDefinition = {\n id: 'system.goal',\n name: 'goal',\n description:\n 'Set or manage a standing goal (Hermes-style): checklist + judge loop; /subgoal edits criteria; pause, resume, clear',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/goal Ship the fix', '/goal status', '/goal pause', '/goal resume', '/goal clear'],\n handler: async (ctx: CommandContext, args: string) => {\n const apis = ctx.persistentGoalApis;\n if (!apis) {\n return {\n content: 'Goals unavailable on this surface.',\n success: false,\n };\n }\n\n const t = args.trim();\n const lower = t.toLowerCase();\n\n if (!t || lower === 'status') {\n const meta = await apis.getSessionMetadata(ctx.sessionKey);\n const s = readPersistentGoal(meta?.customData as Record<string, unknown> | undefined);\n if (!s || s.status === 'cleared') {\n return {\n content: 'No active goal. Set one with /goal <text>.',\n success: true,\n };\n }\n return { content: statusLine(s), success: true };\n }\n\n if (lower === 'pause') {\n const meta = await apis.getSessionMetadata(ctx.sessionKey);\n const applied = applyPersistentGoalUserAction(\n meta?.customData as Record<string, unknown> | undefined,\n 'pause',\n );\n if (applied.kind === 'error') {\n return { content: applied.error, success: false };\n }\n if (applied.kind === 'noop') {\n return { content: applied.message, success: true };\n }\n const r = await patchGoalMetadata(ctx, () => applied.customData);\n if (!r.ok) return { content: 'error' in r ? r.error : 'Failed', success: false };\n const s = readPersistentGoal(applied.customData);\n return { content: `⏸ Goal paused: ${s?.goal ?? ''}`, success: true };\n }\n\n if (lower === 'resume') {\n const meta = await apis.getSessionMetadata(ctx.sessionKey);\n const applied = applyPersistentGoalUserAction(\n meta?.customData as Record<string, unknown> | undefined,\n 'resume',\n );\n if (applied.kind === 'error') {\n return { content: applied.error, success: false };\n }\n if (applied.kind === 'noop') {\n return { content: applied.message, success: true };\n }\n const r = await patchGoalMetadata(ctx, () => applied.customData);\n if (!r.ok) return { content: 'error' in r ? r.error : 'Failed', success: false };\n const s = readPersistentGoal(applied.customData);\n return {\n content:\n `▶ Goal resumed: ${s?.goal ?? ''}\\n` +\n 'Send any message to continue, or wait — the next assistant turn will pick up from here.',\n success: true,\n };\n }\n\n if (lower === 'clear' || lower === 'stop' || lower === 'done') {\n const meta = await apis.getSessionMetadata(ctx.sessionKey);\n const had = !!readPersistentGoal(meta?.customData as Record<string, unknown> | undefined);\n const applied = applyPersistentGoalUserAction(\n meta?.customData as Record<string, unknown> | undefined,\n 'clear',\n );\n if (applied.kind === 'error') {\n return { content: applied.error, success: false };\n }\n if (applied.kind === 'noop') {\n return { content: applied.message, success: true };\n }\n const r = await patchGoalMetadata(ctx, () => applied.customData);\n if (!r.ok) return { content: 'error' in r ? r.error : 'Failed', success: false };\n return { content: had ? '✓ Goal cleared.' : 'No active goal.', success: true };\n }\n\n // New goal text — Hermes rejects when another inbound turn is in flight for this session.\n const depth = apis.inboundConcurrentDepth(ctx.sessionKey);\n if (depth > 1) {\n return {\n content:\n 'Agent is running — use /goal status / pause / clear mid-run, or wait for the current turn to finish before setting a new goal.',\n success: false,\n };\n }\n\n const maxTurns = defaultMaxTurns(ctx.config.goals);\n const judgeModelRef =\n typeof ctx.config.goals?.judgeModelRef === 'string' ? ctx.config.goals.judgeModelRef.trim() : undefined;\n\n const next: PersistentGoalState = {\n goal: t,\n status: 'active',\n turnsUsed: 0,\n maxTurns,\n createdAt: Date.now(),\n lastTurnAt: 0,\n decomposed: false,\n consecutiveParseFailures: 0,\n ...(judgeModelRef ? { judgeModelRef } : {}),\n };\n\n const r = await patchGoalMetadata(ctx, (base) =>\n mergeCustomDataPatch(base, { [PERSISTENT_GOAL_CUSTOM_KEY]: serializePersistentGoal(next) }),\n );\n if (!r.ok) return { content: 'error' in r ? r.error : 'Failed', success: false };\n\n apis.scheduleContinuation(ctx.sessionKey, next.goal);\n\n return {\n content:\n `⊙ Goal set (${next.maxTurns}-turn budget): ${next.goal}\\n` +\n \"I'll keep working until the goal is done, you pause/clear it, or the budget is exhausted.\\n\" +\n 'Controls: /goal status · /goal pause · /goal resume · /goal clear · /subgoal',\n success: true,\n };\n },\n};\n\nexport function registerGoalCommand(): void {\n commandRegistry.register(goalCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"goal.js","names":[],"sources":["../../../../src/chat-commands/builtins/goal.ts"],"sourcesContent":["/**\n * `/goal` — first-class goal control from any command surface.\n */\n\nimport { getDefaultAgentId } from '../../routing/resolve-route.js';\nimport { GoalService, type GoalWithDetails } from '../../goals/index.js';\nimport type { CommandContext, CommandDefinition } from '../types.js';\nimport { commandRegistry } from '../registry.js';\n\nconst goals = new GoalService();\n\nfunction statusLine(goal: GoalWithDetails): string {\n const turns = `${goal.turnsUsed}/${goal.maxTurns} turns`;\n const progress = goal.checklist.length\n ? ` · ${goal.checklist.filter((it) => it.status === 'completed' || it.status === 'impossible').length}/${goal.checklist.length} criteria`\n : '';\n if (goal.status === 'active') return `⊙ Goal (active, ${turns}${progress}): ${goal.title}`;\n if (goal.status === 'paused') return `⏸ Goal (paused, ${turns}${progress}): ${goal.title}`;\n if (goal.status === 'blocked' || goal.status === 'needs_input') {\n const reason = goal.blockedReason ? ` — ${goal.blockedReason}` : '';\n return `⏸ Goal (${goal.status}, ${turns}${progress}${reason}): ${goal.title}`;\n }\n if (goal.status === 'done') return `✓ Goal done (${turns}${progress}): ${goal.title}`;\n return `Goal (${goal.status}, ${turns}${progress}): ${goal.title}`;\n}\n\nfunction activeGoal(ctx: CommandContext): GoalWithDetails | null {\n return goals.getActiveForSession(ctx.sessionKey);\n}\n\nconst goalCommand: CommandDefinition = {\n id: 'system.goal',\n name: 'goal',\n description: 'Create and manage a durable goal: checklist + judge loop; /subgoal edits criteria',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/goal Ship the fix', '/goal status', '/goal pause', '/goal resume', '/goal clear'],\n handler: async (ctx: CommandContext, args: string) => {\n const t = args.trim();\n const lower = t.toLowerCase();\n\n if (!t || lower === 'status') {\n const goal = activeGoal(ctx);\n return {\n content: goal ? statusLine(goal) : 'No active goal. Set one with /goal <text>.',\n success: true,\n };\n }\n\n if (lower === 'pause') {\n const goal = activeGoal(ctx);\n if (!goal) return { content: 'No active goal.', success: true };\n const next = goals.pause(goal.id);\n return { content: `⏸ Goal paused: ${next?.title ?? goal.title}`, success: true };\n }\n\n if (lower === 'resume') {\n const goal = activeGoal(ctx);\n if (!goal) return { content: 'No goal to resume.', success: true };\n const next = goals.resume(goal.id);\n ctx.persistentGoalApis?.scheduleContinuation(ctx.sessionKey, next?.nextAction || next?.title || goal.title);\n return { content: `▶ Goal resumed: ${next?.title ?? goal.title}`, success: true };\n }\n\n if (lower === 'clear' || lower === 'stop' || lower === 'archive') {\n const goal = activeGoal(ctx);\n if (!goal) return { content: 'No active goal.', success: true };\n goals.archive(goal.id);\n try {\n await ctx.abortCurrentTurn?.();\n } catch {\n return { content: '✓ Goal archived. Current turn could not be stopped automatically.', success: true };\n }\n return { content: '✓ Goal archived.', success: true };\n }\n\n const depth = ctx.persistentGoalApis?.inboundConcurrentDepth(ctx.sessionKey) ?? 0;\n if (depth > 1) {\n return {\n content:\n 'Agent is running — use /goal status / pause / clear mid-run, or wait for the current turn to finish before setting a new goal.',\n success: false,\n };\n }\n\n const goal = goals.create({\n title: t,\n sessionKey: ctx.sessionKey,\n agentId: getDefaultAgentId(ctx.config),\n config: ctx.config,\n source: ctx.source === 'cli' ? 'cli' : ctx.source === 'api' ? 'api' : 'channel',\n });\n\n ctx.persistentGoalApis?.scheduleContinuation(ctx.sessionKey, goal.title);\n\n return {\n content:\n `⊙ Goal set (${goal.maxTurns}-turn budget): ${goal.title}\\n` +\n \"I'll keep working until the goal is done, you pause/archive it, or the budget is exhausted.\\n\" +\n 'Controls: /goal status · /goal pause · /goal resume · /goal clear · /subgoal',\n success: true,\n };\n },\n};\n\nexport function registerGoalCommand(): void {\n commandRegistry.register(goalCommand);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  /**
2
- * `/subgoal` — view or edit the standing goal checklist (Hermes parity).
2
+ * `/subgoal` — view or edit checklist criteria for the active first-class goal.
3
3
  */
4
4
  export declare function registerSubgoalCommand(): void;
@@ -1,27 +1,28 @@
1
+ import { GoalService } from "../../goals/goal-service.js";
2
+ import "../../goals/index.js";
1
3
  import { commandRegistry } from "../registry.js";
2
- import { checklistCounts } from "../../agent/goals/checklist-types.js";
3
- import { readPersistentGoal, renderChecklistNumbered } from "../../agent/goals/state.js";
4
- import { applyChecklistUserMutation } from "../../agent/goals/checklist-user.js";
5
4
  //#region src/chat-commands/builtins/subgoal.ts
6
- async function patchMetadata(ctx, patchCustom) {
7
- const apis = ctx.persistentGoalApis;
8
- if (!apis) return {
9
- ok: false,
10
- error: "Goals unavailable on this surface."
11
- };
12
- const meta = await apis.getSessionMetadata(ctx.sessionKey);
13
- if (!meta) return {
14
- ok: false,
15
- error: "Session not found"
16
- };
17
- const customData = patchCustom({ ...meta.customData });
18
- await apis.updateSessionMetadata(ctx.sessionKey, { customData });
19
- return { ok: true };
5
+ /**
6
+ * `/subgoal` view or edit checklist criteria for the active first-class goal.
7
+ */
8
+ const goals = new GoalService();
9
+ function activeGoal(ctx) {
10
+ return goals.getActiveForSession(ctx.sessionKey);
11
+ }
12
+ function renderChecklist(goal) {
13
+ if (!goal.checklist.length) return "Checklist is empty. Add criteria with /subgoal add <text>.";
14
+ const done = goal.checklist.filter((it) => it.status === "completed" || it.status === "impossible").length;
15
+ const lines = goal.checklist.map((it, index) => {
16
+ const marker = it.status === "completed" ? "[x]" : it.status === "impossible" ? "[!]" : "[ ]";
17
+ const evidence = it.evidenceSummary ? ` (${it.evidenceSummary})` : "";
18
+ return `${index + 1}. ${marker} ${it.text}${evidence}`;
19
+ });
20
+ return `Checklist (${done}/${goal.checklist.length} terminal):\n${lines.join("\n")}`;
20
21
  }
21
22
  const subgoalCommand = {
22
23
  id: "system.subgoal",
23
24
  name: "subgoal",
24
- description: "View or edit checklist criteria for the active /goal (add, mark, remove, reset)",
25
+ description: "View or edit checklist criteria for the active goal (add, mark, remove, reset)",
25
26
  category: "system",
26
27
  scope: [
27
28
  "global",
@@ -37,73 +38,31 @@ const subgoalCommand = {
37
38
  "/subgoal reset"
38
39
  ],
39
40
  handler: async (ctx, args) => {
40
- const apis = ctx.persistentGoalApis;
41
- if (!apis) return {
42
- content: "Goals unavailable on this surface.",
43
- success: false
41
+ const goal = activeGoal(ctx);
42
+ if (!goal) return {
43
+ content: "No active goal. Set one with /goal <text> first.",
44
+ success: true
44
45
  };
45
46
  const t = args.trim();
46
47
  const lower = t.toLowerCase();
47
- const custom = (await apis.getSessionMetadata(ctx.sessionKey))?.customData;
48
- const s = readPersistentGoal(custom);
49
- if (!t || lower === "list" || lower === "status") {
50
- if (!s || s.status === "cleared") return {
51
- content: "No active goal. Set one with /goal <text> first.",
52
- success: true
53
- };
54
- const items = s.checklist ?? [];
55
- if (!items.length) return {
56
- content: s.decomposed === true ? "Checklist is empty (decomposition produced no items). The judge will use freeform mode." : "Checklist not built yet — it appears after the first assistant turn once decomposition runs.",
57
- success: true
58
- };
59
- const { total, completed, impossible } = checklistCounts(items);
60
- return {
61
- content: `Checklist (${completed + impossible}/${total} terminal):\n` + renderChecklistNumbered(items),
62
- success: true
63
- };
64
- }
48
+ if (!t || lower === "list" || lower === "status") return {
49
+ content: renderChecklist(goal),
50
+ success: true
51
+ };
65
52
  if (lower === "reset" || lower === "clear") {
66
- const applied = applyChecklistUserMutation(custom, { type: "reset" });
67
- if (applied.kind === "error") return {
68
- content: applied.error,
69
- success: false
70
- };
71
- if (applied.kind === "noop") return {
72
- content: applied.message,
73
- success: true
74
- };
75
- const cd = applied.customData;
76
- const r = await patchMetadata(ctx, () => cd);
77
- if (!r.ok) return {
78
- content: "error" in r ? r.error : "Failed",
79
- success: false
80
- };
53
+ goals.updateChecklist(goal.id, { type: "reset" });
81
54
  return {
82
- content: "✓ Checklist cleared. Decomposition will run again on the next goal evaluation turn (or set /goal anew).",
55
+ content: "✓ Checklist cleared.",
83
56
  success: true
84
57
  };
85
58
  }
86
59
  const addM = t.match(/^add\s+([\s\S]+)$/i);
87
60
  if (addM) {
88
61
  const text = addM[1].trim();
89
- const applied = applyChecklistUserMutation(custom, {
62
+ goals.updateChecklist(goal.id, {
90
63
  type: "add",
91
64
  text
92
65
  });
93
- if (applied.kind === "error") return {
94
- content: applied.error,
95
- success: false
96
- };
97
- if (applied.kind === "noop") return {
98
- content: applied.message,
99
- success: true
100
- };
101
- const cd = applied.customData;
102
- const r = await patchMetadata(ctx, () => cd);
103
- if (!r.ok) return {
104
- content: "error" in r ? r.error : "Failed",
105
- success: false
106
- };
107
66
  return {
108
67
  content: `⊙ Added checklist item: ${text}`,
109
68
  success: true
@@ -111,60 +70,42 @@ const subgoalCommand = {
111
70
  }
112
71
  const removeM = t.match(/^remove\s+(\d+)\s*$/i);
113
72
  if (removeM) {
114
- const idx = Number(removeM[1]);
115
- const applied = applyChecklistUserMutation(custom, {
116
- type: "remove",
117
- index1Based: idx
118
- });
119
- if (applied.kind === "error") return {
120
- content: applied.error,
121
- success: false
122
- };
123
- if (applied.kind === "noop") return {
124
- content: applied.message,
125
- success: true
126
- };
127
- const cd = applied.customData;
128
- const r = await patchMetadata(ctx, () => cd);
129
- if (!r.ok) return {
130
- content: "error" in r ? r.error : "Failed",
73
+ const index = Number(removeM[1]);
74
+ const item = goal.checklist[index - 1];
75
+ if (!item) return {
76
+ content: `No checklist item #${index}.`,
131
77
  success: false
132
78
  };
79
+ goals.updateChecklist(goal.id, {
80
+ type: "remove",
81
+ itemId: item.id
82
+ });
133
83
  return {
134
- content: `✓ Removed item #${idx}.`,
84
+ content: `✓ Removed item #${index}.`,
135
85
  success: true
136
86
  };
137
87
  }
138
88
  const markM = t.match(/^mark\s+(\d+)\s+(pending|completed|impossible)\s*$/i);
139
89
  if (markM) {
140
- const idx = Number(markM[1]);
90
+ const index = Number(markM[1]);
141
91
  const status = markM[2].toLowerCase();
142
- const applied = applyChecklistUserMutation(custom, {
92
+ const item = goal.checklist[index - 1];
93
+ if (!item) return {
94
+ content: `No checklist item #${index}.`,
95
+ success: false
96
+ };
97
+ goals.updateChecklist(goal.id, {
143
98
  type: "mark",
144
- index1Based: idx,
99
+ itemId: item.id,
145
100
  status
146
101
  });
147
- if (applied.kind === "error") return {
148
- content: applied.error,
149
- success: false
150
- };
151
- if (applied.kind === "noop") return {
152
- content: applied.message,
153
- success: true
154
- };
155
- const cd = applied.customData;
156
- const r = await patchMetadata(ctx, () => cd);
157
- if (!r.ok) return {
158
- content: "error" in r ? r.error : "Failed",
159
- success: false
160
- };
161
102
  return {
162
- content: `⊙ Item #${idx} ${status}.`,
103
+ content: `⊙ Item #${index} -> ${status}.`,
163
104
  success: true
164
105
  };
165
106
  }
166
107
  return {
167
- content: "Usage:\n• /subgoal — show checklist\n• /subgoal add <text>\n• /subgoal mark <n> pending|completed|impossible\n• /subgoal remove <n>\n• /subgoal reset — wipe checklist (re-decompose on next turn)",
108
+ content: "Usage:\n• /subgoal — show checklist\n• /subgoal add <text>\n• /subgoal mark <n> pending|completed|impossible\n• /subgoal remove <n>\n• /subgoal reset",
168
109
  success: true
169
110
  };
170
111
  }