@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport { AgentManager, type AgentSkillAvailabilityPayload, type SkillCatalogEntry } from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { reconcileManagedDreamingCronJobs } from './service/reconcile-dreaming-cron.js';\nimport { parseOutboundSessionKey } from './service/parse-outbound-session-key.js';\n\nimport {\n resolveAgentHomeDir,\n resolveAgentProfileDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n resolveEffectiveAgentProfileForSession,\n} from '../config/agent-profile.js';\nimport { cleanTrailingErrors } from './memory/message-sanitizer.js';\nimport { MemoryFlushService } from './memory/memory-flush.js';\nimport type { MemoryFlushConfig } from './memory/memory-flush.js';\nimport { tryApplySessionTranscriptHygiene } from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and cron jobs do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n private memoryFlushService: MemoryFlushService;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.memoryFlushService = new MemoryFlushService();\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getWorkflowRunService: config.getWorkflowRunService,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: () => this.getContextWindow(),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n parseSessionKey: (sk) => this.parseSessionKey(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const sessionStoreDefaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: sessionStoreDefaults?.maxToolIterations || 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: sessionStoreDefaults?.compaction?.enabled ?? true,\n mode: (sessionStoreDefaults?.compaction?.mode as 'extractive' | 'abstractive' | 'structured') || 'abstractive',\n reserveTokens: sessionStoreDefaults?.compaction?.reserveTokens || 8000,\n triggerThreshold: sessionStoreDefaults?.compaction?.triggerThreshold || 0.8,\n minMessagesBeforeCompact: sessionStoreDefaults?.compaction?.minMessagesBeforeCompact || 10,\n keepRecentMessages: sessionStoreDefaults?.compaction?.keepRecentMessages || 10,\n evictionWindow: sessionStoreDefaults?.compaction?.evictionWindow || 0.2,\n retentionWindow: sessionStoreDefaults?.compaction?.retentionWindow || 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: defaults?.maxToolFailuresPerTurn || 3,\n maxTotalFailures: defaults?.maxToolFailuresPerTurn ? defaults.maxToolFailuresPerTurn + 2 : 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: defaults?.maxRequestsPerTurn || 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n await this.reconcileDreamingCronJob().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Dreaming cron reconcile failed: ${em}`);\n });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Reconcile managed Dreaming cron job against the current effective config.\n * Safe to call after config saves to apply changes without restarting the process.\n */\n async reconcileDreamingNow(): Promise<void> {\n await this.reconcileDreamingCronJob();\n }\n\n private async reconcileDreamingCronJob(): Promise<void> {\n const cron = this.config.getCronService?.();\n if (!cron) {\n return;\n }\n await reconcileManagedDreamingCronJobs(cron, this.effectiveAppConfig());\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private parseSessionKey(sessionKey: string): { channel: string; chatId: string } {\n return parseOutboundSessionKey(sessionKey, this.config.config);\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments);\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads `USER.md` under the agent `profile/` directory and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const { agentId } = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const userPath = join(resolveAgentProfileDir(cfg, agentId), 'USER.md');\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n\n await this.checkAndFlush(sessionKey, messages, result, contextWindow);\n }\n\n private resolveMemoryFlushConfig(): MemoryFlushConfig {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const raw = defaults?.memoryFlush;\n return {\n enabled: raw?.enabled ?? true,\n threshold: raw?.threshold ?? 0.88,\n softThresholdTokens: raw?.softThresholdTokens ?? 4000,\n afterCompactions: raw?.afterCompactions ?? 2,\n maxEntryChars: raw?.maxEntryChars ?? 2000,\n includeToolHistory: raw?.includeToolHistory ?? true,\n };\n }\n\n private async checkAndFlush(\n sessionKey: string,\n messages: AgentMessage[],\n compactionResult: import('./memory/compaction.js').CompactionResult,\n contextWindow: number,\n ): Promise<void> {\n const cfg = this.resolveMemoryFlushConfig();\n if (!cfg.enabled) return;\n\n const metadata = await this.sessionStore.getMetadata(sessionKey);\n const compactedCount = metadata?.compactedCount ?? 0;\n\n const totalTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent = totalTokens / contextWindow;\n const effectiveThreshold = Math.max(0.5, cfg.threshold - cfg.softThresholdTokens / contextWindow);\n const exceedsThreshold = usagePercent >= effectiveThreshold;\n\n const tooManyCompactions = compactedCount >= cfg.afterCompactions;\n\n if (!exceedsThreshold && !tooManyCompactions) return;\n\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const flushResult = await this.memoryFlushService.flush({\n workspaceDir,\n sessionKey,\n compactionResult,\n config: cfg,\n });\n\n if (flushResult.flushed) {\n log.info({ sessionKey, entryPath: flushResult.entryPath, entryLength: flushResult.entryLength }, 'Memory flushed to daily notes');\n const currentMeta = await this.sessionStore.getMetadata(sessionKey);\n await this.sessionStore.updateMetadata(sessionKey, {\n lastFlushedAt: new Date().toISOString(),\n flushCount: (currentMeta?.flushCount ?? 0) + 1,\n });\n }\n }\n\n private getContextWindow(): number {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n return defaults?.maxTokens ? defaults.maxTokens * 4 : 128000;\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport { AgentManager, type AgentSkillAvailabilityPayload, type SkillCatalogEntry } from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { reconcileManagedDreamingCronJobs } from './service/reconcile-dreaming-cron.js';\nimport { parseOutboundSessionKey } from './service/parse-outbound-session-key.js';\nimport { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveAgentProfileDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n resolveEffectiveAgentProfileForSession,\n} from '../config/agent-profile.js';\nimport { cleanTrailingErrors } from './memory/message-sanitizer.js';\nimport { MemoryFlushService } from './memory/memory-flush.js';\nimport type { MemoryFlushConfig } from './memory/memory-flush.js';\nimport { tryApplySessionTranscriptHygiene } from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and cron jobs do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n private memoryFlushService: MemoryFlushService;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.memoryFlushService = new MemoryFlushService();\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getWorkflowRunService: config.getWorkflowRunService,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => this.effectiveAppConfig()?.agents?.defaults?.thinkingDefault,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n getWorkflowRunService: config.getWorkflowRunService,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n onGoalStatusUpdated: config.onGoalStatusUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: () => this.getContextWindow(),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n parseSessionKey: (sk) => this.parseSessionKey(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const sessionStoreDefaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: sessionStoreDefaults?.maxToolIterations || 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: sessionStoreDefaults?.compaction?.enabled ?? true,\n mode: (sessionStoreDefaults?.compaction?.mode as 'extractive' | 'abstractive' | 'structured') || 'abstractive',\n reserveTokens: sessionStoreDefaults?.compaction?.reserveTokens || 8000,\n triggerThreshold: sessionStoreDefaults?.compaction?.triggerThreshold || 0.8,\n minMessagesBeforeCompact: sessionStoreDefaults?.compaction?.minMessagesBeforeCompact || 10,\n keepRecentMessages: sessionStoreDefaults?.compaction?.keepRecentMessages || 10,\n evictionWindow: sessionStoreDefaults?.compaction?.evictionWindow || 0.2,\n retentionWindow: sessionStoreDefaults?.compaction?.retentionWindow || 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: defaults?.maxToolFailuresPerTurn || 3,\n maxTotalFailures: defaults?.maxToolFailuresPerTurn ? defaults.maxToolFailuresPerTurn + 2 : 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: defaults?.maxRequestsPerTurn || 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n await this.reconcileDreamingCronJob().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Dreaming cron reconcile failed: ${em}`);\n });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Reconcile managed Dreaming cron job against the current effective config.\n * Safe to call after config saves to apply changes without restarting the process.\n */\n async reconcileDreamingNow(): Promise<void> {\n await this.reconcileDreamingCronJob();\n }\n\n private async reconcileDreamingCronJob(): Promise<void> {\n const cron = this.config.getCronService?.();\n if (!cron) {\n return;\n }\n await reconcileManagedDreamingCronJobs(cron, this.effectiveAppConfig());\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private parseSessionKey(sessionKey: string): { channel: string; chatId: string } {\n return parseOutboundSessionKey(sessionKey, this.config.config);\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments, {\n resolveUri: async (uri) => {\n const target = parseNoteAttachmentTarget(uri);\n if (!target) return null;\n const notesService = (this as unknown as {\n notesServiceInstance?: {\n getAttachmentPath(noteId: string, attachmentId: string): Promise<{\n filePath: string;\n mimeType?: string;\n fileName?: string;\n } | null>;\n };\n }).notesServiceInstance;\n if (!notesService) return null;\n const located = await notesService.getAttachmentPath(target.noteId, target.attachmentId);\n if (!located) return null;\n const buffer = await readFile(located.filePath);\n return {\n buffer,\n path: located.filePath,\n mimeType: located.mimeType,\n name: located.fileName,\n size: buffer.byteLength,\n };\n },\n });\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads `USER.md` under the agent `profile/` directory and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const { agentId } = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const userPath = join(resolveAgentProfileDir(cfg, agentId), 'USER.md');\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n\n await this.checkAndFlush(sessionKey, messages, result, contextWindow);\n }\n\n private resolveMemoryFlushConfig(): MemoryFlushConfig {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n const raw = defaults?.memoryFlush;\n return {\n enabled: raw?.enabled ?? true,\n threshold: raw?.threshold ?? 0.88,\n softThresholdTokens: raw?.softThresholdTokens ?? 4000,\n afterCompactions: raw?.afterCompactions ?? 2,\n maxEntryChars: raw?.maxEntryChars ?? 2000,\n includeToolHistory: raw?.includeToolHistory ?? true,\n };\n }\n\n private async checkAndFlush(\n sessionKey: string,\n messages: AgentMessage[],\n compactionResult: import('./memory/compaction.js').CompactionResult,\n contextWindow: number,\n ): Promise<void> {\n const cfg = this.resolveMemoryFlushConfig();\n if (!cfg.enabled) return;\n\n const metadata = await this.sessionStore.getMetadata(sessionKey);\n const compactedCount = metadata?.compactedCount ?? 0;\n\n const totalTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent = totalTokens / contextWindow;\n const effectiveThreshold = Math.max(0.5, cfg.threshold - cfg.softThresholdTokens / contextWindow);\n const exceedsThreshold = usagePercent >= effectiveThreshold;\n\n const tooManyCompactions = compactedCount >= cfg.afterCompactions;\n\n if (!exceedsThreshold && !tooManyCompactions) return;\n\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const flushResult = await this.memoryFlushService.flush({\n workspaceDir,\n sessionKey,\n compactionResult,\n config: cfg,\n });\n\n if (flushResult.flushed) {\n log.info({ sessionKey, entryPath: flushResult.entryPath, entryLength: flushResult.entryLength }, 'Memory flushed to daily notes');\n const currentMeta = await this.sessionStore.getMetadata(sessionKey);\n await this.sessionStore.updateMetadata(sessionKey, {\n lastFlushedAt: new Date().toISOString(),\n flushCount: (currentMeta?.flushCount ?? 0) + 1,\n });\n }\n }\n\n private getContextWindow(): number {\n const defaults = this.config.agentDefaults || this.config.config?.agents?.defaults;\n return defaults?.maxTokens ? defaults.maxTokens * 4 : 128000;\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -39,6 +39,14 @@ export interface AgentServiceConfig {
|
|
|
39
39
|
}) => void;
|
|
40
40
|
/** Gateway: transcript JSONL append (goal verdict, slash receipt, background turns). */
|
|
41
41
|
onSessionTranscriptUpdated?: (sessionKey: string) => void;
|
|
42
|
+
/** Gateway: durable goal status changed after post-turn judge. */
|
|
43
|
+
onGoalStatusUpdated?: (payload: {
|
|
44
|
+
goalId: string;
|
|
45
|
+
sessionKey: string;
|
|
46
|
+
previousStatus: string;
|
|
47
|
+
status: string;
|
|
48
|
+
goal: import('../goals/types.js').GoalWithDetails;
|
|
49
|
+
}) => void;
|
|
42
50
|
/** Gateway: persisted workflow runs. */
|
|
43
51
|
getWorkflowRunService?: () => import('../workflows/service/workflow-run-service.types.js').WorkflowRunServiceLike;
|
|
44
52
|
/** Gateway: resolves bound source context (e.g. Note-grounded chat) before each turn. */
|
|
@@ -30,6 +30,8 @@ const CronjobSchema = Type.Object({
|
|
|
30
30
|
workflowDefinitionId: Type.Optional(Type.String({ description: "Workflow definition id for a direct workflowRun job (mutually exclusive with message on create)." })),
|
|
31
31
|
workflowGoal: Type.Optional(Type.String({ description: "Optional goal override when creating a workflowRun job." })),
|
|
32
32
|
workflowInputJson: Type.Optional(Type.String({ description: "JSON object for workflow input payload (workflowRun create/update)." })),
|
|
33
|
+
goalId: Type.Optional(Type.String({ description: "Durable goal id for a goalContinue job (mutually exclusive with message and workflowDefinitionId)." })),
|
|
34
|
+
goalMessage: Type.Optional(Type.String({ description: "Optional custom continuation message for a goalContinue job." })),
|
|
33
35
|
waitForCompletion: Type.Optional(Type.Boolean({ description: "For workflowRun jobs: when true (default), cron waits for terminal status before succeeding." })),
|
|
34
36
|
deliveryChannel: Type.Optional(Type.String({ description: "Delivery channel when workflow completes (e.g. telegram)." })),
|
|
35
37
|
deliveryTo: Type.Optional(Type.String({ description: "Delivery recipient chat id when workflow completes." })),
|
|
@@ -75,7 +77,7 @@ function createCronjobTool(deps) {
|
|
|
75
77
|
return {
|
|
76
78
|
name: "cronjob",
|
|
77
79
|
label: "⏰ Cronjob",
|
|
78
|
-
description: "Manage scheduled tasks (cron jobs) that run automatically.\n\nJobs can run an agent message (agentTurn)
|
|
80
|
+
description: "Manage scheduled tasks (cron jobs) that run automatically.\n\nJobs can run an agent message (agentTurn), a workflow directly (workflowRun), or continue a durable goal (goalContinue).\n\nACTIONS:\n- list: Show all scheduled jobs with status and next run time\n- create: Create a job (schedule + exactly one of message, workflowDefinitionId, or goalId)\n- update: Change schedule, message, workflow fields, name, timezone, sessionTarget, agentId, or workingDirectory (requires jobId)\n- remove: Delete a job (requires jobId)\n- enable / disable: Toggle a job (requires jobId)\n- history: Recent executions for a job (requires jobId)",
|
|
79
81
|
parameters: CronjobSchema,
|
|
80
82
|
async execute(_toolCallId, params, _signal) {
|
|
81
83
|
const cron = deps.getCronService();
|
|
@@ -95,8 +97,14 @@ function createCronjobTool(deps) {
|
|
|
95
97
|
}
|
|
96
98
|
case "create": {
|
|
97
99
|
const workflowId = params.workflowDefinitionId?.trim();
|
|
100
|
+
const goalId = params.goalId?.trim();
|
|
98
101
|
const hasMessage = Boolean(params.message?.trim());
|
|
99
|
-
|
|
102
|
+
const targetCount = [
|
|
103
|
+
workflowId,
|
|
104
|
+
goalId,
|
|
105
|
+
hasMessage ? "message" : ""
|
|
106
|
+
].filter(Boolean).length;
|
|
107
|
+
if (!params.schedule?.trim() || targetCount !== 1) return textResult("Error: create requires schedule and exactly one of message, workflowDefinitionId, or goalId.");
|
|
100
108
|
if (hasMessage) {
|
|
101
109
|
const scanResult = scanCronPrompt(params.message);
|
|
102
110
|
if (scanResult) return textResult(`Error: ${scanResult}`);
|
|
@@ -105,7 +113,14 @@ function createCronjobTool(deps) {
|
|
|
105
113
|
let timeout;
|
|
106
114
|
let sessionTarget = params.sessionTarget ?? "isolated";
|
|
107
115
|
let delivery;
|
|
108
|
-
if (
|
|
116
|
+
if (goalId) {
|
|
117
|
+
payload = {
|
|
118
|
+
kind: "goalContinue",
|
|
119
|
+
goalId,
|
|
120
|
+
...params.goalMessage?.trim() ? { message: params.goalMessage.trim() } : {}
|
|
121
|
+
};
|
|
122
|
+
sessionTarget = "isolated";
|
|
123
|
+
} else if (workflowId) {
|
|
109
124
|
let inputEnvelope;
|
|
110
125
|
if (params.workflowInputJson?.trim()) try {
|
|
111
126
|
inputEnvelope = { payload: JSON.parse(params.workflowInputJson) };
|
|
@@ -142,7 +157,7 @@ function createCronjobTool(deps) {
|
|
|
142
157
|
...params.workingDirectory?.trim() ? { workingDirectory: params.workingDirectory.trim() } : {},
|
|
143
158
|
payload
|
|
144
159
|
});
|
|
145
|
-
return textResult(`Created ${workflowId ? `workflow job (${workflowId})` : "job"}${params.name ? ` "${params.name.trim()}"` : ""} (${result.id})\nSchedule: ${result.schedule}`);
|
|
160
|
+
return textResult(`Created ${goalId ? `goal job (${goalId})` : workflowId ? `workflow job (${workflowId})` : "job"}${params.name ? ` "${params.name.trim()}"` : ""} (${result.id})\nSchedule: ${result.schedule}`);
|
|
146
161
|
}
|
|
147
162
|
case "update": {
|
|
148
163
|
if (!params.jobId?.trim()) return textResult("Error: update requires jobId.");
|
|
@@ -156,6 +171,14 @@ function createCronjobTool(deps) {
|
|
|
156
171
|
message: params.message.trim()
|
|
157
172
|
};
|
|
158
173
|
}
|
|
174
|
+
if (params.goalId?.trim()) {
|
|
175
|
+
updates.payload = {
|
|
176
|
+
kind: "goalContinue",
|
|
177
|
+
goalId: params.goalId.trim(),
|
|
178
|
+
...params.goalMessage?.trim() ? { message: params.goalMessage.trim() } : {}
|
|
179
|
+
};
|
|
180
|
+
updates.sessionTarget = "isolated";
|
|
181
|
+
}
|
|
159
182
|
if (params.workflowDefinitionId?.trim()) {
|
|
160
183
|
let inputEnvelope;
|
|
161
184
|
if (params.workflowInputJson?.trim()) try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cronjob-tool.js","names":[],"sources":["../../../../src/agent/tools/cronjob-tool.ts"],"sourcesContent":["// Agent tool for managing scheduled cron jobs (CronService-backed)\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { CronService } from '../../cron/index.js';\nimport { getCronPayloadText } from '../../cron/job-content.js';\nimport { DEFAULT_WORKFLOW_CRON_WAIT_MS } from '../../cron/workflow-run-completion.js';\nimport type { CronPayload, JobData, JobExecution, JobWithNextRun } from '../../cron/types.js';\n\nconst CRON_THREAT_PATTERNS: Array<[RegExp, string]> = [\n [/ignore\\s+(previous|all|above)\\s+instructions/i, 'prompt_injection'],\n [/do\\s+not\\s+tell\\s+the\\s+user/i, 'deception'],\n [/system\\s+prompt\\s+override/i, 'sys_prompt_override'],\n [/curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD)/i, 'exfil_curl'],\n [/cat\\s+[^\\n]*(\\.env|credentials|\\.netrc)/i, 'read_secrets'],\n [/rm\\s+-rf\\s+\\//i, 'destructive_rm'],\n];\n\nexport function scanCronPrompt(prompt: string): string | null {\n for (const [pattern, id] of CRON_THREAT_PATTERNS) {\n if (pattern.test(prompt)) {\n return (\n `Blocked: prompt matches threat pattern '${id}'. ` +\n 'Cron prompts must not contain injection or exfiltration payloads.'\n );\n }\n }\n return null;\n}\n\nconst CronjobSchema = Type.Object({\n action: Type.Union(\n [\n Type.Literal('list'),\n Type.Literal('create'),\n Type.Literal('update'),\n Type.Literal('remove'),\n Type.Literal('enable'),\n Type.Literal('disable'),\n Type.Literal('history'),\n ],\n { description: 'Action to perform on cron jobs' },\n ),\n\n name: Type.Optional(Type.String({ description: 'Human-readable job name' })),\n schedule: Type.Optional(\n Type.String({\n description:\n 'Cron schedule expression. Examples:\\n' +\n ' \"0 9 * * *\" = every day at 9:00 AM\\n' +\n ' \"*/30 * * * *\" = every 30 minutes\\n' +\n ' \"0 9 * * 1-5\" = weekdays at 9:00 AM\\n' +\n ' \"0 0 1 * *\" = first day of each month',\n }),\n ),\n message: Type.Optional(\n Type.String({\n description:\n 'Instruction for the agent when the job runs (agentTurn payload; typically a fresh session).',\n }),\n ),\n workflowDefinitionId: Type.Optional(\n Type.String({\n description:\n 'Workflow definition id for a direct workflowRun job (mutually exclusive with message on create).',\n }),\n ),\n workflowGoal: Type.Optional(\n Type.String({ description: 'Optional goal override when creating a workflowRun job.' }),\n ),\n workflowInputJson: Type.Optional(\n Type.String({\n description: 'JSON object for workflow input payload (workflowRun create/update).',\n }),\n ),\n waitForCompletion: Type.Optional(\n Type.Boolean({\n description:\n 'For workflowRun jobs: when true (default), cron waits for terminal status before succeeding.',\n }),\n ),\n deliveryChannel: Type.Optional(\n Type.String({ description: 'Delivery channel when workflow completes (e.g. telegram).' }),\n ),\n deliveryTo: Type.Optional(\n Type.String({ description: 'Delivery recipient chat id when workflow completes.' }),\n ),\n timezone: Type.Optional(\n Type.String({\n description:\n 'IANA timezone (e.g. Asia/Shanghai, America/New_York). Must be one supported by the cron store.',\n }),\n ),\n sessionTarget: Type.Optional(\n Type.Union([Type.Literal('main'), Type.Literal('isolated')], {\n description:\n '\"main\" uses the main session context; \"isolated\" (default on create) uses a separate session per run.',\n }),\n ),\n agentId: Type.Optional(\n Type.String({\n description:\n 'Agent profile id for isolated jobs (session key). Omit to use the configured default agent (usually `main`).',\n }),\n ),\n workingDirectory: Type.Optional(\n Type.String({\n description:\n 'Absolute workspace path on the gateway host for isolated jobs. Omit to use the agent default workspace.',\n }),\n ),\n\n jobId: Type.Optional(Type.String({ description: 'Job ID (from list output)' })),\n});\n\nexport type CronjobToolParams = {\n action: 'list' | 'create' | 'update' | 'remove' | 'enable' | 'disable' | 'history';\n name?: string;\n schedule?: string;\n message?: string;\n workflowDefinitionId?: string;\n workflowGoal?: string;\n workflowInputJson?: string;\n waitForCompletion?: boolean;\n deliveryChannel?: string;\n deliveryTo?: string;\n timezone?: string;\n sessionTarget?: 'main' | 'isolated';\n agentId?: string;\n workingDirectory?: string;\n jobId?: string;\n};\n\nexport interface CronjobToolDeps {\n getCronService: () => CronService | undefined;\n}\n\nfunction textResult(text: string): AgentToolResult<{}> {\n return { content: [{ type: 'text', text }], details: {} };\n}\n\nfunction formatJob(job: JobWithNextRun): string {\n const status = job.enabled ? '▶️ active' : '⏸️ disabled';\n const payloadText = getCronPayloadText(job);\n const truncatedPayload =\n payloadText.length > 100 ? `${payloadText.slice(0, 100)}...` : payloadText;\n const payloadLine =\n job.payload.kind === 'workflowRun'\n ? ` Workflow: ${job.payload.definitionId}${job.payload.goal ? ` — ${job.payload.goal}` : ''}`\n : ` Message: ${truncatedPayload}`;\n\n return [\n `${status} ${job.name ?? '(unnamed)'} (${job.id})`,\n ` Schedule: ${job.schedule}${job.timezone ? ` (${job.timezone})` : ''}`,\n ` Type: ${job.payload.kind}`,\n payloadLine,\n ` Next run: ${job.next_run ?? 'N/A'}`,\n ` Session: ${job.sessionTarget ?? 'main'}`,\n ` Agent: ${job.agentId?.trim() || '(default)'}`,\n ` Workspace: ${job.workingDirectory?.trim() || '(agent default)'}`,\n ].join('\\n');\n}\n\nfunction formatExecution(exec: JobExecution): string {\n const dur = exec.duration != null ? `${(exec.duration / 1000).toFixed(1)}s` : 'N/A';\n const lines = [`[${exec.status}] ${exec.startedAt} (${dur})`];\n if (exec.summary) {\n lines.push(` Summary: ${exec.summary.slice(0, 200)}`);\n }\n if (exec.error) {\n lines.push(` Error: ${exec.error.slice(0, 200)}`);\n }\n return lines.join('\\n');\n}\n\nexport function createCronjobTool(deps: CronjobToolDeps): AgentTool {\n return {\n name: 'cronjob',\n label: '⏰ Cronjob',\n description:\n 'Manage scheduled tasks (cron jobs) that run automatically.\\n\\n' +\n 'Jobs can run an agent message (agentTurn) or a workflow directly (workflowRun).\\n\\n' +\n 'ACTIONS:\\n' +\n '- list: Show all scheduled jobs with status and next run time\\n' +\n '- create: Create a job (schedule + message OR workflowDefinitionId; optional goal, workflowInputJson, deliveryChannel/deliveryTo, waitForCompletion)\\n' +\n '- update: Change schedule, message, workflow fields, name, timezone, sessionTarget, agentId, or workingDirectory (requires jobId)\\n' +\n '- remove: Delete a job (requires jobId)\\n' +\n '- enable / disable: Toggle a job (requires jobId)\\n' +\n '- history: Recent executions for a job (requires jobId)',\n parameters: CronjobSchema,\n\n async execute(_toolCallId, params: CronjobToolParams, _signal) {\n const cron = deps.getCronService();\n if (!cron) {\n return textResult('Cron service is not available in this environment.');\n }\n\n try {\n switch (params.action) {\n case 'list': {\n const jobs = await cron.listJobs();\n if (jobs.length === 0) {\n return textResult('No scheduled jobs.');\n }\n const formatted = jobs.map(formatJob).join('\\n\\n');\n return { content: [{ type: 'text', text: formatted }], details: {} };\n }\n\n case 'create': {\n const workflowId = params.workflowDefinitionId?.trim();\n const hasMessage = Boolean(params.message?.trim());\n if (!params.schedule?.trim() || (!workflowId && !hasMessage) || (workflowId && hasMessage)) {\n return textResult(\n 'Error: create requires schedule and exactly one of message or workflowDefinitionId.',\n );\n }\n\n if (hasMessage) {\n const scanResult = scanCronPrompt(params.message!);\n if (scanResult) {\n return textResult(`Error: ${scanResult}`);\n }\n }\n\n let payload: CronPayload;\n let timeout: number | undefined;\n let sessionTarget = params.sessionTarget ?? 'isolated';\n let delivery: JobData['delivery'];\n\n if (workflowId) {\n let inputEnvelope: { payload: unknown } | undefined;\n if (params.workflowInputJson?.trim()) {\n try {\n inputEnvelope = { payload: JSON.parse(params.workflowInputJson) as unknown };\n } catch {\n return textResult('Error: workflowInputJson must be valid JSON.');\n }\n }\n const agentId = params.agentId?.trim() || undefined;\n payload = {\n kind: 'workflowRun',\n definitionId: workflowId,\n ...(params.workflowGoal?.trim() ? { goal: params.workflowGoal.trim() } : {}),\n ...(inputEnvelope ? { inputEnvelope } : {}),\n ...(agentId ? { agentId } : {}),\n ...(params.waitForCompletion === false ? { waitForCompletion: false } : {}),\n };\n sessionTarget = 'isolated';\n timeout = DEFAULT_WORKFLOW_CRON_WAIT_MS;\n if (params.deliveryChannel?.trim() && params.deliveryTo?.trim()) {\n delivery = {\n mode: 'direct',\n channel: params.deliveryChannel.trim(),\n to: params.deliveryTo.trim(),\n };\n }\n } else {\n payload = {\n kind: 'agentTurn',\n message: params.message!.trim(),\n };\n }\n\n const result = await cron.addJob(params.schedule.trim(), {\n name: params.name?.trim() || undefined,\n timezone: params.timezone?.trim() || undefined,\n sessionTarget,\n ...(timeout ? { timeout } : {}),\n ...(delivery ? { delivery } : {}),\n ...(params.agentId?.trim() ? { agentId: params.agentId.trim() } : {}),\n ...(params.workingDirectory?.trim()\n ? { workingDirectory: params.workingDirectory.trim() }\n : {}),\n payload,\n });\n\n const kindLabel = workflowId ? `workflow job (${workflowId})` : 'job';\n return textResult(\n `Created ${kindLabel}${params.name ? ` \"${params.name.trim()}\"` : ''} (${result.id})\\n` +\n `Schedule: ${result.schedule}`,\n );\n }\n\n case 'update': {\n if (!params.jobId?.trim()) {\n return textResult('Error: update requires jobId.');\n }\n\n const updates: Partial<Omit<JobData, 'id' | 'created_at' | 'updated_at'>> = {};\n if (params.schedule?.trim()) {\n updates.schedule = params.schedule.trim();\n }\n if (params.message != null && params.message.trim()) {\n const scanResult = scanCronPrompt(params.message);\n if (scanResult) {\n return textResult(`Error: ${scanResult}`);\n }\n updates.payload = { kind: 'agentTurn', message: params.message.trim() };\n }\n if (params.workflowDefinitionId?.trim()) {\n let inputEnvelope: { payload: unknown } | undefined;\n if (params.workflowInputJson?.trim()) {\n try {\n inputEnvelope = { payload: JSON.parse(params.workflowInputJson) as unknown };\n } catch {\n return textResult('Error: workflowInputJson must be valid JSON.');\n }\n }\n const agentId = params.agentId?.trim() || undefined;\n updates.payload = {\n kind: 'workflowRun',\n definitionId: params.workflowDefinitionId.trim(),\n ...(params.workflowGoal?.trim() ? { goal: params.workflowGoal.trim() } : {}),\n ...(inputEnvelope ? { inputEnvelope } : {}),\n ...(agentId ? { agentId } : {}),\n ...(params.waitForCompletion === false ? { waitForCompletion: false } : {}),\n };\n updates.sessionTarget = 'isolated';\n updates.timeout = DEFAULT_WORKFLOW_CRON_WAIT_MS;\n }\n if (params.deliveryChannel?.trim() && params.deliveryTo?.trim()) {\n updates.delivery = {\n mode: 'direct',\n channel: params.deliveryChannel.trim(),\n to: params.deliveryTo.trim(),\n };\n }\n if (params.timezone?.trim()) {\n updates.timezone = params.timezone.trim();\n }\n if (params.name !== undefined) {\n updates.name = params.name.trim() || undefined;\n }\n if (params.sessionTarget !== undefined) {\n updates.sessionTarget = params.sessionTarget;\n }\n if (params.agentId !== undefined) {\n const t = params.agentId.trim();\n updates.agentId = t || null;\n }\n if (params.workingDirectory !== undefined) {\n const t = params.workingDirectory.trim();\n updates.workingDirectory = t || null;\n }\n\n if (Object.keys(updates).length === 0) {\n return textResult(\n 'Error: update requires at least one of schedule, message, workflowDefinitionId, name, timezone, sessionTarget, agentId, workingDirectory, deliveryChannel/deliveryTo.',\n );\n }\n\n const success = await cron.updateJob(params.jobId.trim(), updates);\n return textResult(\n success ? `Updated job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'remove': {\n if (!params.jobId?.trim()) {\n return textResult('Error: remove requires jobId.');\n }\n const removed = await cron.removeJob(params.jobId.trim());\n return textResult(\n removed ? `Removed job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'disable': {\n if (!params.jobId?.trim()) {\n return textResult('Error: disable requires jobId.');\n }\n const toggled = await cron.toggleJob(params.jobId.trim(), false);\n return textResult(\n toggled ? `Disabled job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'enable': {\n if (!params.jobId?.trim()) {\n return textResult('Error: enable requires jobId.');\n }\n const toggled = await cron.toggleJob(params.jobId.trim(), true);\n return textResult(\n toggled ? `Enabled job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'history': {\n if (!params.jobId?.trim()) {\n return textResult('Error: history requires jobId.');\n }\n const history = await cron.getJobHistory(params.jobId.trim(), 5);\n if (history.length === 0) {\n return textResult(`No execution history for job ${params.jobId.trim()}.`);\n }\n const formatted = history.map(formatExecution).join('\\n\\n');\n return textResult(formatted);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return textResult(`Error: ${message}`);\n }\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cronjob-tool.js","names":[],"sources":["../../../../src/agent/tools/cronjob-tool.ts"],"sourcesContent":["// Agent tool for managing scheduled cron jobs (CronService-backed)\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { CronService } from '../../cron/index.js';\nimport { getCronPayloadText } from '../../cron/job-content.js';\nimport { DEFAULT_WORKFLOW_CRON_WAIT_MS } from '../../cron/workflow-run-completion.js';\nimport type { CronPayload, JobData, JobExecution, JobWithNextRun } from '../../cron/types.js';\n\nconst CRON_THREAT_PATTERNS: Array<[RegExp, string]> = [\n [/ignore\\s+(previous|all|above)\\s+instructions/i, 'prompt_injection'],\n [/do\\s+not\\s+tell\\s+the\\s+user/i, 'deception'],\n [/system\\s+prompt\\s+override/i, 'sys_prompt_override'],\n [/curl\\s+[^\\n]*\\$\\{?\\w*(KEY|TOKEN|SECRET|PASSWORD)/i, 'exfil_curl'],\n [/cat\\s+[^\\n]*(\\.env|credentials|\\.netrc)/i, 'read_secrets'],\n [/rm\\s+-rf\\s+\\//i, 'destructive_rm'],\n];\n\nexport function scanCronPrompt(prompt: string): string | null {\n for (const [pattern, id] of CRON_THREAT_PATTERNS) {\n if (pattern.test(prompt)) {\n return (\n `Blocked: prompt matches threat pattern '${id}'. ` +\n 'Cron prompts must not contain injection or exfiltration payloads.'\n );\n }\n }\n return null;\n}\n\nconst CronjobSchema = Type.Object({\n action: Type.Union(\n [\n Type.Literal('list'),\n Type.Literal('create'),\n Type.Literal('update'),\n Type.Literal('remove'),\n Type.Literal('enable'),\n Type.Literal('disable'),\n Type.Literal('history'),\n ],\n { description: 'Action to perform on cron jobs' },\n ),\n\n name: Type.Optional(Type.String({ description: 'Human-readable job name' })),\n schedule: Type.Optional(\n Type.String({\n description:\n 'Cron schedule expression. Examples:\\n' +\n ' \"0 9 * * *\" = every day at 9:00 AM\\n' +\n ' \"*/30 * * * *\" = every 30 minutes\\n' +\n ' \"0 9 * * 1-5\" = weekdays at 9:00 AM\\n' +\n ' \"0 0 1 * *\" = first day of each month',\n }),\n ),\n message: Type.Optional(\n Type.String({\n description:\n 'Instruction for the agent when the job runs (agentTurn payload; typically a fresh session).',\n }),\n ),\n workflowDefinitionId: Type.Optional(\n Type.String({\n description:\n 'Workflow definition id for a direct workflowRun job (mutually exclusive with message on create).',\n }),\n ),\n workflowGoal: Type.Optional(\n Type.String({ description: 'Optional goal override when creating a workflowRun job.' }),\n ),\n workflowInputJson: Type.Optional(\n Type.String({\n description: 'JSON object for workflow input payload (workflowRun create/update).',\n }),\n ),\n goalId: Type.Optional(\n Type.String({\n description:\n 'Durable goal id for a goalContinue job (mutually exclusive with message and workflowDefinitionId).',\n }),\n ),\n goalMessage: Type.Optional(\n Type.String({\n description: 'Optional custom continuation message for a goalContinue job.',\n }),\n ),\n waitForCompletion: Type.Optional(\n Type.Boolean({\n description:\n 'For workflowRun jobs: when true (default), cron waits for terminal status before succeeding.',\n }),\n ),\n deliveryChannel: Type.Optional(\n Type.String({ description: 'Delivery channel when workflow completes (e.g. telegram).' }),\n ),\n deliveryTo: Type.Optional(\n Type.String({ description: 'Delivery recipient chat id when workflow completes.' }),\n ),\n timezone: Type.Optional(\n Type.String({\n description:\n 'IANA timezone (e.g. Asia/Shanghai, America/New_York). Must be one supported by the cron store.',\n }),\n ),\n sessionTarget: Type.Optional(\n Type.Union([Type.Literal('main'), Type.Literal('isolated')], {\n description:\n '\"main\" uses the main session context; \"isolated\" (default on create) uses a separate session per run.',\n }),\n ),\n agentId: Type.Optional(\n Type.String({\n description:\n 'Agent profile id for isolated jobs (session key). Omit to use the configured default agent (usually `main`).',\n }),\n ),\n workingDirectory: Type.Optional(\n Type.String({\n description:\n 'Absolute workspace path on the gateway host for isolated jobs. Omit to use the agent default workspace.',\n }),\n ),\n\n jobId: Type.Optional(Type.String({ description: 'Job ID (from list output)' })),\n});\n\nexport type CronjobToolParams = {\n action: 'list' | 'create' | 'update' | 'remove' | 'enable' | 'disable' | 'history';\n name?: string;\n schedule?: string;\n message?: string;\n workflowDefinitionId?: string;\n workflowGoal?: string;\n workflowInputJson?: string;\n goalId?: string;\n goalMessage?: string;\n waitForCompletion?: boolean;\n deliveryChannel?: string;\n deliveryTo?: string;\n timezone?: string;\n sessionTarget?: 'main' | 'isolated';\n agentId?: string;\n workingDirectory?: string;\n jobId?: string;\n};\n\nexport interface CronjobToolDeps {\n getCronService: () => CronService | undefined;\n}\n\nfunction textResult(text: string): AgentToolResult<{}> {\n return { content: [{ type: 'text', text }], details: {} };\n}\n\nfunction formatJob(job: JobWithNextRun): string {\n const status = job.enabled ? '▶️ active' : '⏸️ disabled';\n const payloadText = getCronPayloadText(job);\n const truncatedPayload =\n payloadText.length > 100 ? `${payloadText.slice(0, 100)}...` : payloadText;\n const payloadLine =\n job.payload.kind === 'workflowRun'\n ? ` Workflow: ${job.payload.definitionId}${job.payload.goal ? ` — ${job.payload.goal}` : ''}`\n : ` Message: ${truncatedPayload}`;\n\n return [\n `${status} ${job.name ?? '(unnamed)'} (${job.id})`,\n ` Schedule: ${job.schedule}${job.timezone ? ` (${job.timezone})` : ''}`,\n ` Type: ${job.payload.kind}`,\n payloadLine,\n ` Next run: ${job.next_run ?? 'N/A'}`,\n ` Session: ${job.sessionTarget ?? 'main'}`,\n ` Agent: ${job.agentId?.trim() || '(default)'}`,\n ` Workspace: ${job.workingDirectory?.trim() || '(agent default)'}`,\n ].join('\\n');\n}\n\nfunction formatExecution(exec: JobExecution): string {\n const dur = exec.duration != null ? `${(exec.duration / 1000).toFixed(1)}s` : 'N/A';\n const lines = [`[${exec.status}] ${exec.startedAt} (${dur})`];\n if (exec.summary) {\n lines.push(` Summary: ${exec.summary.slice(0, 200)}`);\n }\n if (exec.error) {\n lines.push(` Error: ${exec.error.slice(0, 200)}`);\n }\n return lines.join('\\n');\n}\n\nexport function createCronjobTool(deps: CronjobToolDeps): AgentTool {\n return {\n name: 'cronjob',\n label: '⏰ Cronjob',\n description:\n 'Manage scheduled tasks (cron jobs) that run automatically.\\n\\n' +\n 'Jobs can run an agent message (agentTurn), a workflow directly (workflowRun), or continue a durable goal (goalContinue).\\n\\n' +\n 'ACTIONS:\\n' +\n '- list: Show all scheduled jobs with status and next run time\\n' +\n '- create: Create a job (schedule + exactly one of message, workflowDefinitionId, or goalId)\\n' +\n '- update: Change schedule, message, workflow fields, name, timezone, sessionTarget, agentId, or workingDirectory (requires jobId)\\n' +\n '- remove: Delete a job (requires jobId)\\n' +\n '- enable / disable: Toggle a job (requires jobId)\\n' +\n '- history: Recent executions for a job (requires jobId)',\n parameters: CronjobSchema,\n\n async execute(_toolCallId, params: CronjobToolParams, _signal) {\n const cron = deps.getCronService();\n if (!cron) {\n return textResult('Cron service is not available in this environment.');\n }\n\n try {\n switch (params.action) {\n case 'list': {\n const jobs = await cron.listJobs();\n if (jobs.length === 0) {\n return textResult('No scheduled jobs.');\n }\n const formatted = jobs.map(formatJob).join('\\n\\n');\n return { content: [{ type: 'text', text: formatted }], details: {} };\n }\n\n case 'create': {\n const workflowId = params.workflowDefinitionId?.trim();\n const goalId = params.goalId?.trim();\n const hasMessage = Boolean(params.message?.trim());\n const targetCount = [workflowId, goalId, hasMessage ? 'message' : ''].filter(Boolean).length;\n if (!params.schedule?.trim() || targetCount !== 1) {\n return textResult(\n 'Error: create requires schedule and exactly one of message, workflowDefinitionId, or goalId.',\n );\n }\n\n if (hasMessage) {\n const scanResult = scanCronPrompt(params.message!);\n if (scanResult) {\n return textResult(`Error: ${scanResult}`);\n }\n }\n\n let payload: CronPayload;\n let timeout: number | undefined;\n let sessionTarget = params.sessionTarget ?? 'isolated';\n let delivery: JobData['delivery'];\n\n if (goalId) {\n payload = {\n kind: 'goalContinue',\n goalId,\n ...(params.goalMessage?.trim() ? { message: params.goalMessage.trim() } : {}),\n };\n sessionTarget = 'isolated';\n } else if (workflowId) {\n let inputEnvelope: { payload: unknown } | undefined;\n if (params.workflowInputJson?.trim()) {\n try {\n inputEnvelope = { payload: JSON.parse(params.workflowInputJson) as unknown };\n } catch {\n return textResult('Error: workflowInputJson must be valid JSON.');\n }\n }\n const agentId = params.agentId?.trim() || undefined;\n payload = {\n kind: 'workflowRun',\n definitionId: workflowId,\n ...(params.workflowGoal?.trim() ? { goal: params.workflowGoal.trim() } : {}),\n ...(inputEnvelope ? { inputEnvelope } : {}),\n ...(agentId ? { agentId } : {}),\n ...(params.waitForCompletion === false ? { waitForCompletion: false } : {}),\n };\n sessionTarget = 'isolated';\n timeout = DEFAULT_WORKFLOW_CRON_WAIT_MS;\n if (params.deliveryChannel?.trim() && params.deliveryTo?.trim()) {\n delivery = {\n mode: 'direct',\n channel: params.deliveryChannel.trim(),\n to: params.deliveryTo.trim(),\n };\n }\n } else {\n payload = {\n kind: 'agentTurn',\n message: params.message!.trim(),\n };\n }\n\n const result = await cron.addJob(params.schedule.trim(), {\n name: params.name?.trim() || undefined,\n timezone: params.timezone?.trim() || undefined,\n sessionTarget,\n ...(timeout ? { timeout } : {}),\n ...(delivery ? { delivery } : {}),\n ...(params.agentId?.trim() ? { agentId: params.agentId.trim() } : {}),\n ...(params.workingDirectory?.trim()\n ? { workingDirectory: params.workingDirectory.trim() }\n : {}),\n payload,\n });\n\n const kindLabel = goalId ? `goal job (${goalId})` : workflowId ? `workflow job (${workflowId})` : 'job';\n return textResult(\n `Created ${kindLabel}${params.name ? ` \"${params.name.trim()}\"` : ''} (${result.id})\\n` +\n `Schedule: ${result.schedule}`,\n );\n }\n\n case 'update': {\n if (!params.jobId?.trim()) {\n return textResult('Error: update requires jobId.');\n }\n\n const updates: Partial<Omit<JobData, 'id' | 'created_at' | 'updated_at'>> = {};\n if (params.schedule?.trim()) {\n updates.schedule = params.schedule.trim();\n }\n if (params.message != null && params.message.trim()) {\n const scanResult = scanCronPrompt(params.message);\n if (scanResult) {\n return textResult(`Error: ${scanResult}`);\n }\n updates.payload = { kind: 'agentTurn', message: params.message.trim() };\n }\n if (params.goalId?.trim()) {\n updates.payload = {\n kind: 'goalContinue',\n goalId: params.goalId.trim(),\n ...(params.goalMessage?.trim() ? { message: params.goalMessage.trim() } : {}),\n };\n updates.sessionTarget = 'isolated';\n }\n if (params.workflowDefinitionId?.trim()) {\n let inputEnvelope: { payload: unknown } | undefined;\n if (params.workflowInputJson?.trim()) {\n try {\n inputEnvelope = { payload: JSON.parse(params.workflowInputJson) as unknown };\n } catch {\n return textResult('Error: workflowInputJson must be valid JSON.');\n }\n }\n const agentId = params.agentId?.trim() || undefined;\n updates.payload = {\n kind: 'workflowRun',\n definitionId: params.workflowDefinitionId.trim(),\n ...(params.workflowGoal?.trim() ? { goal: params.workflowGoal.trim() } : {}),\n ...(inputEnvelope ? { inputEnvelope } : {}),\n ...(agentId ? { agentId } : {}),\n ...(params.waitForCompletion === false ? { waitForCompletion: false } : {}),\n };\n updates.sessionTarget = 'isolated';\n updates.timeout = DEFAULT_WORKFLOW_CRON_WAIT_MS;\n }\n if (params.deliveryChannel?.trim() && params.deliveryTo?.trim()) {\n updates.delivery = {\n mode: 'direct',\n channel: params.deliveryChannel.trim(),\n to: params.deliveryTo.trim(),\n };\n }\n if (params.timezone?.trim()) {\n updates.timezone = params.timezone.trim();\n }\n if (params.name !== undefined) {\n updates.name = params.name.trim() || undefined;\n }\n if (params.sessionTarget !== undefined) {\n updates.sessionTarget = params.sessionTarget;\n }\n if (params.agentId !== undefined) {\n const t = params.agentId.trim();\n updates.agentId = t || null;\n }\n if (params.workingDirectory !== undefined) {\n const t = params.workingDirectory.trim();\n updates.workingDirectory = t || null;\n }\n\n if (Object.keys(updates).length === 0) {\n return textResult(\n 'Error: update requires at least one of schedule, message, workflowDefinitionId, name, timezone, sessionTarget, agentId, workingDirectory, deliveryChannel/deliveryTo.',\n );\n }\n\n const success = await cron.updateJob(params.jobId.trim(), updates);\n return textResult(\n success ? `Updated job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'remove': {\n if (!params.jobId?.trim()) {\n return textResult('Error: remove requires jobId.');\n }\n const removed = await cron.removeJob(params.jobId.trim());\n return textResult(\n removed ? `Removed job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'disable': {\n if (!params.jobId?.trim()) {\n return textResult('Error: disable requires jobId.');\n }\n const toggled = await cron.toggleJob(params.jobId.trim(), false);\n return textResult(\n toggled ? `Disabled job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'enable': {\n if (!params.jobId?.trim()) {\n return textResult('Error: enable requires jobId.');\n }\n const toggled = await cron.toggleJob(params.jobId.trim(), true);\n return textResult(\n toggled ? `Enabled job ${params.jobId.trim()}.` : `Job ${params.jobId.trim()} not found.`,\n );\n }\n\n case 'history': {\n if (!params.jobId?.trim()) {\n return textResult('Error: history requires jobId.');\n }\n const history = await cron.getJobHistory(params.jobId.trim(), 5);\n if (history.length === 0) {\n return textResult(`No execution history for job ${params.jobId.trim()}.`);\n }\n const formatted = history.map(formatExecution).join('\\n\\n');\n return textResult(formatted);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return textResult(`Error: ${message}`);\n }\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from '@earendil-works/pi-agent-core';
|
|
2
|
+
import type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';
|
|
2
3
|
export interface EditToolDetails {
|
|
3
4
|
diff?: string;
|
|
4
5
|
firstChangedLine?: number;
|
|
@@ -7,6 +8,7 @@ export interface EditToolDetails {
|
|
|
7
8
|
export interface CreateEditFileToolOptions {
|
|
8
9
|
/** When set and the path is a bare profile filename (e.g. SOUL.md), edit under this root. */
|
|
9
10
|
profileMarkdownRoot?: string;
|
|
11
|
+
recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;
|
|
10
12
|
}
|
|
11
13
|
export declare function createEditFileTool(workspace: string, options?: CreateEditFileToolOptions): AgentTool;
|
|
12
14
|
export declare const editFileTool: AgentTool;
|
|
@@ -86,6 +86,16 @@ function createEditFileTool(workspace, options) {
|
|
|
86
86
|
};
|
|
87
87
|
const diffResult = generateDiffString(originalWithReplacement, finalContent, normalized);
|
|
88
88
|
await writeFile(normalized, finalContent, "utf-8");
|
|
89
|
+
await options?.recordGoalEvidence?.({
|
|
90
|
+
kind: "diff",
|
|
91
|
+
title: `File edited: ${normalized}`,
|
|
92
|
+
summary: diffResult.slice(0, 4e3),
|
|
93
|
+
uri: normalized,
|
|
94
|
+
data: {
|
|
95
|
+
path: normalized,
|
|
96
|
+
fuzzyMatchUsed: matchResult.usedFuzzyMatch
|
|
97
|
+
}
|
|
98
|
+
});
|
|
89
99
|
return {
|
|
90
100
|
content: [{
|
|
91
101
|
type: "text",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edit.js","names":[],"sources":["../../../../src/agent/tools/edit.ts"],"sourcesContent":["// Edit file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { readFile, writeFile, stat } from 'fs/promises';\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 { normalizeToLF, restoreLineEndings, normalizeForFuzzyMatch, fuzzyFindText, stripBom, generateDiffString } from './edit-diff.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst EditFileSchema = Type.Object({\n path: Type.String({ description: 'File path to edit' }),\n oldText: Type.String({ description: 'Text to replace' }),\n newText: Type.String({ description: 'Replacement text' }),\n});\n\nexport interface EditToolDetails {\n diff?: string;\n firstChangedLine?: number;\n fuzzyMatchUsed?: boolean;\n}\n\ntype EditFileParams = { path: string; oldText: string; newText: string };\n\nexport interface CreateEditFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), edit under this root. */\n profileMarkdownRoot?: string;\n}\n\nexport function createEditFileTool(\n workspace: string,\n options?: CreateEditFileToolOptions,\n): AgentTool {\n return {\n name: 'edit_file',\n description:\n 'Edit file by replacing text. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is edited automatically when given by filename.',\n parameters: EditFileSchema,\n label: '✏️ Edit',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<EditToolDetails>> {\n try {\n const p = params as EditFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n\n const editsProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = editsProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'edit',\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 normalized = editsProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n const stats = await stat(normalized);\n if (stats.size > MAX_FILE_SIZE) return { content: [{ type: 'text', text: `🚫 File too large` }], details: {} };\n\n const rawContent = await readFile(normalized, 'utf-8');\n const content = stripBom(rawContent);\n const lineEnding = detectLineEnding(rawContent);\n\n const normalizedContent = normalizeToLF(content);\n const normalizedOldText = normalizeToLF(p.oldText);\n const normalizedNewText = normalizeToLF(p.newText);\n\n const matchResult = fuzzyFindText(normalizedContent, normalizedOldText);\n if (!matchResult.found) return { content: [{ type: 'text', text: `Error: oldText not found` }], details: {} };\n\n const fuzzyContent = normalizeForFuzzyMatch(normalizedContent);\n const fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);\n const occurrences = fuzzyContent.split(fuzzyOldText).length - 1;\n if (occurrences > 1)\n return { content: [{ type: 'text', text: `Error: ${occurrences} occurrences found, text must be unique` }], details: {} };\n\n const baseContent = matchResult.contentForReplacement;\n const newContent =\n baseContent.substring(0, matchResult.index) +\n normalizedNewText +\n baseContent.substring(matchResult.index + matchResult.matchLength);\n const finalContent = restoreLineEndings(newContent, lineEnding);\n const originalWithReplacement = restoreLineEndings(baseContent, lineEnding);\n\n if (originalWithReplacement === finalContent)\n return { content: [{ type: 'text', text: `Error: No changes` }], details: {} };\n\n const diffResult = generateDiffString(originalWithReplacement, finalContent, normalized);\n await writeFile(normalized, finalContent, 'utf-8');\n\n return {\n content: [{ type: 'text', text: `File edited: ${normalized}` }],\n details: { diff: diffResult, fuzzyMatchUsed: matchResult.usedFuzzyMatch },\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],\n details: {},\n };\n }\n },\n } as any;\n}\n\nexport const editFileTool: AgentTool = createEditFileTool(process.cwd());\n\nfunction detectLineEnding(content: string): '\\r\\n' | '\\n' {\n const crlfIdx = content.indexOf('\\r\\n');\n const lfIdx = content.indexOf('\\n');\n if (lfIdx === -1) return '\\n';\n if (crlfIdx === -1) return '\\n';\n return crlfIdx < lfIdx ? '\\r\\n' : '\\n';\n}\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"edit.js","names":[],"sources":["../../../../src/agent/tools/edit.ts"],"sourcesContent":["// Edit file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { readFile, writeFile, stat } from 'fs/promises';\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 { normalizeToLF, restoreLineEndings, normalizeForFuzzyMatch, fuzzyFindText, stripBom, generateDiffString } from './edit-diff.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst EditFileSchema = Type.Object({\n path: Type.String({ description: 'File path to edit' }),\n oldText: Type.String({ description: 'Text to replace' }),\n newText: Type.String({ description: 'Replacement text' }),\n});\n\nexport interface EditToolDetails {\n diff?: string;\n firstChangedLine?: number;\n fuzzyMatchUsed?: boolean;\n}\n\ntype EditFileParams = { path: string; oldText: string; newText: string };\n\nexport interface CreateEditFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), edit under this root. */\n profileMarkdownRoot?: string;\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\nexport function createEditFileTool(\n workspace: string,\n options?: CreateEditFileToolOptions,\n): AgentTool {\n return {\n name: 'edit_file',\n description:\n 'Edit file by replacing text. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is edited automatically when given by filename.',\n parameters: EditFileSchema,\n label: '✏️ Edit',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<EditToolDetails>> {\n try {\n const p = params as EditFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n\n const editsProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = editsProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'edit',\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 normalized = editsProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n const stats = await stat(normalized);\n if (stats.size > MAX_FILE_SIZE) return { content: [{ type: 'text', text: `🚫 File too large` }], details: {} };\n\n const rawContent = await readFile(normalized, 'utf-8');\n const content = stripBom(rawContent);\n const lineEnding = detectLineEnding(rawContent);\n\n const normalizedContent = normalizeToLF(content);\n const normalizedOldText = normalizeToLF(p.oldText);\n const normalizedNewText = normalizeToLF(p.newText);\n\n const matchResult = fuzzyFindText(normalizedContent, normalizedOldText);\n if (!matchResult.found) return { content: [{ type: 'text', text: `Error: oldText not found` }], details: {} };\n\n const fuzzyContent = normalizeForFuzzyMatch(normalizedContent);\n const fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);\n const occurrences = fuzzyContent.split(fuzzyOldText).length - 1;\n if (occurrences > 1)\n return { content: [{ type: 'text', text: `Error: ${occurrences} occurrences found, text must be unique` }], details: {} };\n\n const baseContent = matchResult.contentForReplacement;\n const newContent =\n baseContent.substring(0, matchResult.index) +\n normalizedNewText +\n baseContent.substring(matchResult.index + matchResult.matchLength);\n const finalContent = restoreLineEndings(newContent, lineEnding);\n const originalWithReplacement = restoreLineEndings(baseContent, lineEnding);\n\n if (originalWithReplacement === finalContent)\n return { content: [{ type: 'text', text: `Error: No changes` }], details: {} };\n\n const diffResult = generateDiffString(originalWithReplacement, finalContent, normalized);\n await writeFile(normalized, finalContent, 'utf-8');\n await options?.recordGoalEvidence?.({\n kind: 'diff',\n title: `File edited: ${normalized}`,\n summary: diffResult.slice(0, 4000),\n uri: normalized,\n data: {\n path: normalized,\n fuzzyMatchUsed: matchResult.usedFuzzyMatch,\n },\n });\n\n return {\n content: [{ type: 'text', text: `File edited: ${normalized}` }],\n details: { diff: diffResult, fuzzyMatchUsed: matchResult.usedFuzzyMatch },\n };\n } catch (error) {\n return {\n content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],\n details: {},\n };\n }\n },\n } as any;\n}\n\nexport const editFileTool: AgentTool = createEditFileTool(process.cwd());\n\nfunction detectLineEnding(content: string): '\\r\\n' | '\\n' {\n const crlfIdx = content.indexOf('\\r\\n');\n const lfIdx = content.indexOf('\\n');\n if (lfIdx === -1) return '\\n';\n if (crlfIdx === -1) return '\\n';\n return crlfIdx < lfIdx ? '\\r\\n' : '\\n';\n}\n"],"mappings":"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"}
|