@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.
- package/README.md +46 -4
- package/README.zh-CN.md +46 -4
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +41 -0
- package/dist/gateway/static/root/assets/agent-avatar-dicebear-zlx9l5Yp.js +1 -0
- package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +6 -0
- package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-Bz7JWjRM.js +1 -0
- package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +71 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-DaQwdeCq.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-D2zv6r3P.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-BIppe_jj.js +1 -0
- package/dist/gateway/static/root/assets/archive-plugin-DEmu8eH7.js +1 -0
- package/dist/gateway/static/root/assets/attachment-load-B98lJoN5.js +2 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CybMSIoS.js → attachment-preview-renderer-DonnL2dL.js} +1 -1
- package/dist/gateway/static/root/assets/binary-plugins-CL9QKmyo.js +2 -0
- package/dist/gateway/static/root/assets/block-editor-BOAhMAhT.js +180 -0
- package/dist/gateway/static/root/assets/channels-settings-Br8iRnXp.js +1 -0
- package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
- package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +1 -0
- package/dist/gateway/static/root/assets/config-api-0o1WYdz2.js +3 -0
- package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +2 -0
- package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +1 -0
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +1 -0
- package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +1 -0
- package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +1 -0
- package/dist/gateway/static/root/assets/dist-CkobR7mk.js +1 -0
- package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-scene-BHMRVEUS.js +4585 -0
- package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +1 -0
- package/dist/gateway/static/root/assets/extension-debug-page-Cjx6pZVz.js +1 -0
- package/dist/gateway/static/root/assets/{extension-page-W1wiG38F.js → extension-page-BSPNL90V.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +1 -0
- package/dist/gateway/static/root/assets/fetch-Bs7in_4F.js +1 -0
- package/dist/gateway/static/root/assets/format-interval-label-CftJ5uXu.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-Clfck-hW.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +1 -0
- package/dist/gateway/static/root/assets/gateway-startup-retry-BkjVJc38.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-kqJue0sE.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-CJnzNSDo.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-config-api-Cp7CPuIn.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-ifSI1Wbh.js +1 -0
- package/dist/gateway/static/root/assets/html-workspace-editor-CQLWDiwE.js +1 -0
- package/dist/gateway/static/root/assets/index-COkF54xv.js +121 -0
- package/dist/gateway/static/root/assets/index-DIaSNGEO.css +1 -0
- package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +2 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings--MI1rYl7.js +1 -0
- package/dist/gateway/static/root/assets/locale-store-DjbfSsfA.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BwrVNlyW.js +2 -0
- package/dist/gateway/static/root/assets/markdown-editor-AssmZFx4.js +2 -0
- package/dist/gateway/static/root/assets/markdown-split-B_oc9lJe.js +1 -0
- package/dist/gateway/static/root/assets/media-plugins-DLPvB5H3.js +1 -0
- package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +134 -0
- package/dist/gateway/static/root/assets/messages-Csef1aW7.js +3 -0
- package/dist/gateway/static/root/assets/middleware-Pd0e8Igk.js +1 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +2 -0
- package/dist/gateway/static/root/assets/note-image-lightbox-rSqAgOi9.js +1 -0
- package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +1 -0
- package/dist/gateway/static/root/assets/notes-page-C9ACGEty.js +1 -0
- package/dist/gateway/static/root/assets/preview-open-alternatives-Bw_N2oZi.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-BhcWIKuM.js +1 -0
- package/dist/gateway/static/root/assets/save-bar-controls-CNP2jmw0.js +1 -0
- package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +1 -0
- package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +1 -0
- package/dist/gateway/static/root/assets/schema-form-CDPzua9q.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-DzkpSDZr.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +1 -0
- package/dist/gateway/static/root/assets/settings-collapsible-section-C9bff85B.js +1 -0
- package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-DWK4Zo7s.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-DnutoY_S.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-6oRgJMpn.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +2 -0
- package/dist/gateway/static/root/assets/system-settings-panel-qP1JDtDd.js +1 -0
- package/dist/gateway/static/root/assets/theme-store-CD2wtgZV.js +1 -0
- package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +1 -0
- package/dist/gateway/static/root/assets/use-blob-object-url-D4BFuzZB.js +1 -0
- package/dist/gateway/static/root/assets/use-channel-catalog-C4mg1hjD.js +1 -0
- package/dist/gateway/static/root/assets/use-settings-tab-guard-F6C0mpJt.js +1 -0
- package/dist/gateway/static/root/assets/vendor-codemirror-CNP1wPe7.js +45 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-BnDDb6qO.js +1 -0
- package/dist/gateway/static/root/assets/voice-player-store-B2EwIhtY.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-EYg_xBQS.js → workflow-page.utils-CRB7WX9l.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-2Fptr0je.js +18 -0
- package/dist/gateway/static/root/index.html +12 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/goals/checklist-judge.d.ts +5 -0
- package/dist/src/agent/goals/checklist-judge.js +26 -2
- package/dist/src/agent/goals/checklist-judge.js.map +1 -1
- package/dist/src/agent/goals/evaluate-turn.d.ts +5 -1
- package/dist/src/agent/goals/evaluate-turn.js +29 -3
- package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
- package/dist/src/agent/goals/goal-locale.d.ts +0 -2
- package/dist/src/agent/goals/goal-locale.js +2 -15
- package/dist/src/agent/goals/goal-locale.js.map +1 -1
- package/dist/src/agent/goals/persistent-goal-service.d.ts +9 -0
- package/dist/src/agent/goals/persistent-goal-service.js +2 -1
- package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
- package/dist/src/agent/goals/post-turn.d.ts +7 -0
- package/dist/src/agent/goals/post-turn.js +68 -23
- package/dist/src/agent/goals/post-turn.js.map +1 -1
- package/dist/src/agent/goals/state.d.ts +0 -5
- package/dist/src/agent/goals/state.js +1 -96
- package/dist/src/agent/goals/state.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +4 -0
- package/dist/src/agent/messaging/command-handler.js +6 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
- package/dist/src/agent/service/direct-turn-helpers.js +3 -2
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +10 -1
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.js +20 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +8 -0
- package/dist/src/agent/tools/cronjob-tool.d.ts +2 -0
- package/dist/src/agent/tools/cronjob-tool.js +27 -4
- package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
- package/dist/src/agent/tools/edit.d.ts +2 -0
- package/dist/src/agent/tools/edit.js +10 -0
- package/dist/src/agent/tools/edit.js.map +1 -1
- package/dist/src/agent/tools/factory.js +16 -3
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-evidence-recorder.d.ts +6 -0
- package/dist/src/agent/tools/goal-evidence-recorder.js +34 -0
- package/dist/src/agent/tools/goal-evidence-recorder.js.map +1 -0
- package/dist/src/agent/tools/goal-tool.d.ts +19 -0
- package/dist/src/agent/tools/goal-tool.js +101 -0
- package/dist/src/agent/tools/goal-tool.js.map +1 -0
- package/dist/src/agent/tools/index.d.ts +1 -0
- package/dist/src/agent/tools/index.js +2 -1
- package/dist/src/agent/tools/shell.d.ts +2 -0
- package/dist/src/agent/tools/shell.js +17 -2
- package/dist/src/agent/tools/shell.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.d.ts +1 -0
- package/dist/src/agent/tools/workflow-tool.js +10 -2
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/tools/write.d.ts +2 -0
- package/dist/src/agent/tools/write.js +11 -0
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/channels/attachments/inbound-persist.d.ts +7 -0
- package/dist/src/channels/attachments/inbound-persist.js +20 -2
- package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
- package/dist/src/chat-commands/builtins/goal.d.ts +1 -1
- package/dist/src/chat-commands/builtins/goal.js +55 -97
- package/dist/src/chat-commands/builtins/goal.js.map +1 -1
- package/dist/src/chat-commands/builtins/subgoal.d.ts +1 -1
- package/dist/src/chat-commands/builtins/subgoal.js +49 -108
- package/dist/src/chat-commands/builtins/subgoal.js.map +1 -1
- package/dist/src/chat-commands/builtins/workflow.js +132 -8
- package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +5 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +5 -0
- package/dist/src/cli/command-catalog.js +43 -0
- package/dist/src/cli/command-catalog.js.map +1 -1
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/goal.d.ts +4 -0
- package/dist/src/cli/commands/goal.js +213 -0
- package/dist/src/cli/commands/goal.js.map +1 -0
- package/dist/src/cli/commands/onboard.js +11 -5
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +115 -0
- package/dist/src/config/schema.js +63 -4
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/web-patch.d.ts +14 -0
- package/dist/src/config/web-patch.js +45 -6
- package/dist/src/config/web-patch.js.map +1 -1
- package/dist/src/cron/executor.d.ts +2 -0
- package/dist/src/cron/executor.js +25 -0
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/job-content.js +1 -0
- package/dist/src/cron/job-content.js.map +1 -1
- package/dist/src/cron/types.d.ts +19 -1
- package/dist/src/cron/validation.d.ts +20 -0
- package/dist/src/cron/validation.js +7 -0
- package/dist/src/cron/validation.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +14 -0
- package/dist/src/gateway/hono/routes/goals.d.ts +1 -1
- package/dist/src/gateway/hono/routes/goals.js +670 -173
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/home.d.ts +5 -0
- package/dist/src/gateway/hono/routes/home.js +88 -3
- package/dist/src/gateway/hono/routes/home.js.map +1 -1
- package/dist/src/gateway/hono/routes/notes.js +20 -14
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +6 -2
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/service/agent-runner.d.ts +2 -0
- package/dist/src/gateway/service/agent-runner.js +6 -2
- package/dist/src/gateway/service/agent-runner.js.map +1 -1
- package/dist/src/gateway/service/run-gateway-agent.d.ts +3 -0
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +69 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-notifications.d.ts +29 -0
- package/dist/src/goals/goal-notifications.js +154 -0
- package/dist/src/goals/goal-notifications.js.map +1 -0
- package/dist/src/goals/goal-queue-store.d.ts +16 -0
- package/dist/src/goals/goal-queue-store.js +131 -0
- package/dist/src/goals/goal-queue-store.js.map +1 -0
- package/dist/src/goals/goal-queue-types.d.ts +31 -0
- package/dist/src/goals/goal-queue-types.js +1 -0
- package/dist/src/goals/goal-runner.d.ts +17 -0
- package/dist/src/goals/goal-runner.js +127 -0
- package/dist/src/goals/goal-runner.js.map +1 -0
- package/dist/src/goals/goal-service.d.ts +77 -0
- package/dist/src/goals/goal-service.js +261 -0
- package/dist/src/goals/goal-service.js.map +1 -0
- package/dist/src/goals/goal-store.d.ts +38 -0
- package/dist/src/goals/goal-store.js +404 -0
- package/dist/src/goals/goal-store.js.map +1 -0
- package/dist/src/goals/goal-workflow-judge.d.ts +13 -0
- package/dist/src/goals/goal-workflow-judge.js +147 -0
- package/dist/src/goals/goal-workflow-judge.js.map +1 -0
- package/dist/src/goals/index.d.ts +10 -0
- package/dist/src/goals/index.js +7 -0
- package/dist/src/goals/types.d.ts +119 -0
- package/dist/src/goals/types.js +1 -0
- package/dist/src/storage/sqlite/migrations/006_add_goals.sql +108 -0
- package/dist/src/storage/sqlite/migrations/007_goal_run_judge_schema.sql +4 -0
- package/dist/src/storage/sqlite/migrations/008_goal_queue_workflow_runs.sql +53 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +163 -0
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/workflows/domain/run.d.ts +1 -0
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +5 -1
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +7 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/dist/src/workflows/store/run-index-store.d.ts +8 -0
- package/dist/src/workflows/store/run-index-store.js +58 -0
- package/dist/src/workflows/store/run-index-store.js.map +1 -0
- package/dist/src/workflows/store/run-store.d.ts +2 -0
- package/dist/src/workflows/store/run-store.js +14 -21
- package/dist/src/workflows/store/run-store.js.map +1 -1
- package/package.json +9 -3
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-CFFioJCI.js +0 -78
- package/dist/gateway/static/root/assets/apps-page-BYDmkbzG.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CsbDaMvd.js +0 -1
- package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +0 -1
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-Apk-7p4p.js +0 -1
- package/dist/gateway/static/root/assets/dist-DmGDgpy2.js +0 -1
- package/dist/gateway/static/root/assets/extension-debug-page-pxFC-tUa.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-COXSv6xP.js +0 -1
- package/dist/gateway/static/root/assets/fetch-CFdKBnE6.js +0 -1
- package/dist/gateway/static/root/assets/gateway-startup-retry-DdngdObI.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-DcmAMYmN.js +0 -1
- package/dist/gateway/static/root/assets/index-C_2yKRJX.js +0 -4845
- package/dist/gateway/static/root/assets/index-DI5_GsZt.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-DvTWUOzd.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-CZbdugWZ.js +0 -180
- package/dist/gateway/static/root/assets/note-time-RdyBZxj3.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-mEAwy3e1.js +0 -1
- package/dist/gateway/static/root/assets/schema-form-DEvdni_r.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BsgffzV4.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-t6v8Dwb8.js +0 -2
- package/dist/gateway/static/root/assets/settings-form-section-BoYYZmJG.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-ClIthYF8.js +0 -3
- package/dist/gateway/static/root/assets/share-preview-page-Dd9et7KD.js +0 -2
- package/dist/gateway/static/root/assets/skills-page-DEXtxyfQ.js +0 -2
- package/dist/gateway/static/root/assets/theme-store-BZozwAeK.js +0 -1
- package/dist/gateway/static/root/assets/url-DQRqHuL-.js +0 -3
- package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +0 -45
- package/dist/gateway/static/root/assets/voice-api-key-field-CNrKzQrf.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-EMZkzJNN.js +0 -18
- package/dist/src/agent/goals/checklist-user.d.ts +0 -24
- package/dist/src/agent/goals/checklist-user.js +0 -92
- package/dist/src/agent/goals/checklist-user.js.map +0 -1
- package/dist/src/agent/goals/goal-run-store.d.ts +0 -19
- package/dist/src/agent/goals/goal-run-store.js +0 -138
- package/dist/src/agent/goals/goal-run-store.js.map +0 -1
- package/dist/src/agent/goals/goal-run-types.d.ts +0 -28
- package/dist/src/agent/goals/goal-run-types.js +0 -6
- package/dist/src/agent/goals/goal-run-types.js.map +0 -1
- package/dist/src/agent/goals/patch-from-user-action.d.ts +0 -20
- package/dist/src/agent/goals/patch-from-user-action.js +0 -103
- package/dist/src/agent/goals/patch-from-user-action.js.map +0 -1
- /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":";;;;;;;
|
|
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
|
|
27
|
-
|
|
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?: {
|
|
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,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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return
|
|
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
|
-
|
|
13
|
-
|
|
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: "
|
|
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
|
|
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(
|
|
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
|
|
66
|
-
if (
|
|
67
|
-
content:
|
|
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: ${
|
|
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
|
|
86
|
-
if (
|
|
87
|
-
content:
|
|
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
|
|
95
|
-
|
|
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: ${
|
|
71
|
+
content: `▶ Goal resumed: ${next?.title ?? goal.title}`,
|
|
101
72
|
success: true
|
|
102
73
|
};
|
|
103
74
|
}
|
|
104
|
-
if (lower === "clear" || lower === "stop" || lower === "
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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:
|
|
91
|
+
content: "✓ Goal archived.",
|
|
123
92
|
success: true
|
|
124
93
|
};
|
|
125
94
|
}
|
|
126
|
-
if (
|
|
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
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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 (${
|
|
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,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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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
|
|
41
|
-
if (!
|
|
42
|
-
content: "
|
|
43
|
-
success:
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
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 #${
|
|
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
|
|
90
|
+
const index = Number(markM[1]);
|
|
141
91
|
const status = markM[2].toLowerCase();
|
|
142
|
-
const
|
|
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
|
-
|
|
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 #${
|
|
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
|
|
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
|
}
|