@xopcai/xopc 0.0.124 → 0.0.125
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/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-B5uSVAyJ.js → agent-avatar-dicebear-BKDKICZV.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BnTB7-c5.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-DI4BqHKW.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-6_ie3NQ4.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-B6bG65SW.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CXUGRp2i.js → archive-plugin-kUHEJ1HK.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Cmiub3Wg.js → attachment-load-Dm-9prpU.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-OTxQTPnC.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-1lVbo0dS.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-0Izz-N3p.js → binary-plugins-DllA_jQ0.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C1Kv0IkJ.js → block-editor-Cm6CWNj1.js} +6 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CzDBlOK0.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-VJ89b6I2.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BaPj8wC2.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-BeA2aVor.js → channel-recipient-api-B5PwBEUx.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-Df4m7p1k.js +1 -0
- package/dist/gateway/static/root/assets/code-editor-gbRgS14t.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BMTphmHS.js +2 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-2rjzUu57.js → directory-picker-path-field-DySMzOF6.js} +1 -1
- 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-settings-D_AjSnV_.js +3 -0
- package/dist/gateway/static/root/assets/extension-debug-page-BaM0FpW7.js +1 -0
- package/dist/gateway/static/root/assets/extension-page-DuZWVOGA.js +1 -0
- package/dist/gateway/static/root/assets/extension-settings-page-DarVtbmJ.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-BU5S0Sn7.js → fetch-NFKfkqrd.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BIPGwrl1.js → gateway-config-api-CFEm9rgy.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-BMk2S5td.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-Cd0Zz30N.js → gateway-startup-retry-Dtwh_yGg.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BKP7ylcG.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-C2cTxf71.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-B95KdGMj.js → goals-config-api-DTWZNA_Y.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-CTmA3hWK.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-Bhct0ZT-.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-C-UxSU4a.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-CQLWDiwE.js → html-workspace-editor-DV90ddkE.js} +1 -1
- package/dist/gateway/static/root/assets/index-BNHDd2j1.css +1 -0
- package/dist/gateway/static/root/assets/index-BPXLPMuY.js +193 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-DCjy2y4d.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-CGc3_CQU.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-editor-AssmZFx4.js → markdown-editor-BFOjBBla.js} +2 -2
- package/dist/gateway/static/root/assets/markdown-split-BaqxP-GQ.js +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-BhIQU5yS.js → media-plugins-DQ_EXloK.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CjuIJcZl.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-Drz4UQMb.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-CY1ylng1.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-X7IaD--t.js +2 -0
- package/dist/gateway/static/root/assets/page-tabs-EIZhLppK.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DGzet1d7.js → preview-open-alternatives-r_uB4Epr.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DE5adRBE.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-CuOb1k6F.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-BOxNrUVA.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-Cqgb-jqa.js → save-bar-controls-B132Uf-2.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DVZcI8Yt.js → schedule-field-B5qshALm.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Bj7sOxC8.js → schema-form-CvLmkdS9.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CY0IQcGW.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4NBNVCB.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BmXn-8N6.js → settings-collapsible-section-pRlvqsAf.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BnqYMWHR.js → settings-form-section-BBYUDLVd.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-DaO7zYZM.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-CdWZU1hd.js → settings-page-layout-D8g9PAek.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Bks0CLLT.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-IB1v8i86.js → share-preview-page-v6-IOp8t.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-CEZsKm9r.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-oTkHbb9W.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DigsbZ6K.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-BxQ384RC.js → system-settings-panel-HUU2FFjM.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BkcpvFYO.js → typed-models-lib-AH160QHh.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-Cd0FaEQ4.js → use-channel-catalog-BXzDmjCQ.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-DQ7NnRSY.js → user-profile-settings-panel-DxwnQvQ3.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-YKhVf7Ka.js → utils-i5POEtqd.js} +1 -1
- package/dist/gateway/static/root/assets/{vendor-codemirror-CNP1wPe7.js → vendor-codemirror-BvNgyE1Q.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CEJklEgb.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-CD6FL_Rs.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-ClVVMgN-.js +3 -0
- package/dist/gateway/static/root/assets/{workflow-start-dialog-BNJVCWoS.js → workflow-start-dialog-Dv4ZA1T0.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-OLQiUpoW.js +18 -0
- package/dist/gateway/static/root/index.html +8 -7
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +6 -2
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/child-agent-factory.d.ts +2 -0
- package/dist/src/agent/child-agent-factory.js +2 -0
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/coding/index.d.ts +1 -0
- package/dist/src/agent/coding/index.js +2 -0
- package/dist/src/agent/coding/turn-diff-tracker.d.ts +67 -0
- package/dist/src/agent/coding/turn-diff-tracker.js +233 -0
- package/dist/src/agent/coding/turn-diff-tracker.js.map +1 -0
- package/dist/src/agent/memory/dreaming/config.d.ts +2 -2
- package/dist/src/agent/memory/dreaming/config.js +3 -3
- package/dist/src/agent/memory/dreaming/config.js.map +1 -1
- package/dist/src/agent/memory/dreaming/constants.d.ts +1 -1
- package/dist/src/agent/memory/dreaming/constants.js +1 -1
- package/dist/src/agent/memory/dreaming/constants.js.map +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +1 -0
- package/dist/src/agent/memory/dreaming/deep-promotion.js +11 -20
- package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -1
- package/dist/src/agent/memory/dreaming/events.d.ts +3 -3
- package/dist/src/agent/memory/dreaming/events.js +6 -6
- package/dist/src/agent/memory/dreaming/events.js.map +1 -1
- package/dist/src/agent/memory/dreaming/last-run.d.ts +2 -2
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js.map +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.d.ts +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +3 -3
- package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
- package/dist/src/agent/memory/dreaming/preview.d.ts +1 -0
- package/dist/src/agent/memory/dreaming/preview.js +2 -2
- package/dist/src/agent/memory/dreaming/preview.js.map +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +13 -13
- package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
- package/dist/src/agent/memory/dreaming/scope.d.ts +14 -0
- package/dist/src/agent/memory/dreaming/scope.js +27 -0
- package/dist/src/agent/memory/dreaming/scope.js.map +1 -0
- package/dist/src/agent/memory/dreaming/short-term-store.d.ts +4 -4
- package/dist/src/agent/memory/dreaming/short-term-store.js +17 -17
- package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.d.ts +2 -0
- package/dist/src/agent/orchestration/agent-event-handler.js +13 -0
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +22 -12
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/sandbox/command-validator.js +12 -0
- package/dist/src/agent/sandbox/command-validator.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -1
- package/dist/src/agent/service.js +18 -6
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/session-inspector.d.ts +5 -3
- package/dist/src/agent/session/session-inspector.js +16 -7
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/source-context/types.d.ts +1 -1
- package/dist/src/agent/source-context/types.js +1 -1
- package/dist/src/agent/source-context/types.js.map +1 -1
- package/dist/src/agent/tools/apply-patch.js +116 -62
- package/dist/src/agent/tools/apply-patch.js.map +1 -1
- package/dist/src/agent/tools/concurrency.d.ts +8 -0
- package/dist/src/agent/tools/concurrency.js +31 -0
- package/dist/src/agent/tools/concurrency.js.map +1 -0
- package/dist/src/agent/tools/dreaming-tool.d.ts +2 -0
- package/dist/src/agent/tools/dreaming-tool.js +13 -10
- package/dist/src/agent/tools/dreaming-tool.js.map +1 -1
- package/dist/src/agent/tools/exec-command.js +4 -0
- package/dist/src/agent/tools/exec-command.js.map +1 -1
- package/dist/src/agent/tools/executor.d.ts +6 -4
- package/dist/src/agent/tools/executor.js +7 -4
- package/dist/src/agent/tools/executor.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -0
- package/dist/src/agent/tools/factory.js +7 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/find.js +2 -0
- package/dist/src/agent/tools/find.js.map +1 -1
- package/dist/src/agent/tools/grep.js +2 -0
- package/dist/src/agent/tools/grep.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +2 -0
- package/dist/src/agent/tools/index.js +3 -1
- package/dist/src/agent/tools/list-dir.js +2 -0
- package/dist/src/agent/tools/list-dir.js.map +1 -1
- package/dist/src/agent/tools/memory-tool.d.ts +1 -0
- package/dist/src/agent/tools/memory-tool.js +2 -2
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/tools/metadata.d.ts +15 -0
- package/dist/src/agent/tools/metadata.js +26 -0
- package/dist/src/agent/tools/metadata.js.map +1 -0
- package/dist/src/agent/tools/read.js +2 -0
- package/dist/src/agent/tools/read.js.map +1 -1
- package/dist/src/agent/tools/write.js +6 -0
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.d.ts +1 -0
- package/dist/src/agent/workflow/subagent-runner.js +5 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/agent/workflow/workflow-child-tools.js +5 -0
- package/dist/src/agent/workflow/workflow-child-tools.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/connectors/health.d.ts +2 -1
- package/dist/src/connectors/health.js +59 -1
- package/dist/src/connectors/health.js.map +1 -1
- package/dist/src/connectors/install.d.ts +1 -0
- package/dist/src/connectors/install.js +28 -1
- package/dist/src/connectors/install.js.map +1 -1
- package/dist/src/connectors/instances.js +7 -0
- package/dist/src/connectors/instances.js.map +1 -1
- package/dist/src/connectors/materialize.js +2 -1
- package/dist/src/connectors/materialize.js.map +1 -1
- package/dist/src/connectors/registries/modelscope.js +38 -13
- package/dist/src/connectors/registries/modelscope.js.map +1 -1
- package/dist/src/connectors/types.d.ts +2 -0
- package/dist/src/gateway/dreaming-automation-reconciler.d.ts +1 -0
- package/dist/src/gateway/dreaming-automation-reconciler.js +62 -42
- package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -1
- package/dist/src/gateway/hono/app.js +7 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +46 -2
- package/dist/src/gateway/hono/routes/connectors.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +111 -66
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +846 -43
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +1 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/service.js +17 -8
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/media/store.js +1 -1
- package/dist/src/media/store.js.map +1 -1
- package/dist/src/media/types.d.ts +1 -1
- package/dist/src/media/uri.js +2 -1
- package/dist/src/media/uri.js.map +1 -1
- package/dist/src/session/session-context-for-llm.d.ts +2 -0
- package/dist/src/session/session-context-for-llm.js +14 -1
- package/dist/src/session/session-context-for-llm.js.map +1 -1
- package/dist/src/session/store.js +2 -2
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/022_work_items.sql +61 -0
- package/dist/src/storage/sqlite/migrations/023_work_item_attachments.sql +15 -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 +78 -0
- package/dist/src/tui/components/assistant-message.js +12 -2
- package/dist/src/tui/components/assistant-message.js.map +1 -1
- package/dist/src/tui/components/tool-execution.d.ts +8 -0
- package/dist/src/tui/components/tool-execution.js +87 -77
- package/dist/src/tui/components/tool-execution.js.map +1 -1
- package/dist/src/tui/components/visual-truncate.d.ts +1 -0
- package/dist/src/tui/components/visual-truncate.js +28 -1
- package/dist/src/tui/components/visual-truncate.js.map +1 -1
- package/dist/src/tui/tool-summary.d.ts +22 -0
- package/dist/src/tui/tool-summary.js +212 -0
- package/dist/src/tui/tool-summary.js.map +1 -0
- package/dist/src/tui/tui-tool-diff.d.ts +15 -0
- package/dist/src/tui/tui-tool-diff.js +178 -7
- package/dist/src/tui/tui-tool-diff.js.map +1 -1
- package/dist/src/work-items/agent-context.d.ts +3 -0
- package/dist/src/work-items/agent-context.js +39 -0
- package/dist/src/work-items/agent-context.js.map +1 -0
- package/dist/src/work-items/index.d.ts +4 -0
- package/dist/src/work-items/index.js +4 -0
- package/dist/src/work-items/types.d.ts +112 -0
- package/dist/src/work-items/types.js +1 -0
- package/dist/src/work-items/work-item-service.d.ts +33 -0
- package/dist/src/work-items/work-item-service.js +180 -0
- package/dist/src/work-items/work-item-service.js.map +1 -0
- package/dist/src/work-items/work-item-store.d.ts +21 -0
- package/dist/src/work-items/work-item-store.js +302 -0
- package/dist/src/work-items/work-item-store.js.map +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +4 -3
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js +3 -0
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-CYCONLIL.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-Clw3ryKg.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +0 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-CU0-4nQ-.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BmQTM9Il.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +0 -1
- package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +0 -2
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
- package/dist/gateway/static/root/assets/dist-wO9a2_ID.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-DnHO686d.js +0 -1
- package/dist/gateway/static/root/assets/extension-debug-page-BGLRuWVN.js +0 -1
- package/dist/gateway/static/root/assets/extension-page-D978ePp9.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-B6Ih4Swx.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-B-KHKeXN.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-BTJzIl28.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-BJyEuLLm.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +0 -1
- package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +0 -1
- package/dist/gateway/static/root/assets/index-c_9TVr_C.js +0 -191
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BT8T4DyC.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +0 -2
- package/dist/gateway/static/root/assets/markdown-split-Dv66bO2b.js +0 -1
- package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CWtPM7RK.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-BYD9BtOq.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +0 -18
|
@@ -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 { readFile } from 'node:fs/promises';\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 {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} 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 { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n} from '../config/agent-profile.js';\nimport { resolveUserProfilePath } from '../config/paths.js';\nimport { getProjectForSession } from '../projects/workspace.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 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 automations 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\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.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 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 getAutomationService: config.getAutomationService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n getSelfVerifyPromptContext: (sessionKey, agentId) =>\n this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId),\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: () => undefined,\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 reloadSkills: () => this.refreshSkillsAfterDiskChange(),\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 resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(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 windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n 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 this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 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: 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.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 getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\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 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 async reconcileDreamingNow(): Promise<void> {\n return Promise.resolve();\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 async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\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, getProjectForSession(sessionKey));\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads the global user profile and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n void sessionKey;\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const userPath = resolveUserProfilePath();\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\n private getContextWindow(): number {\n return 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';\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 { TurnDiffTracker } from './coding/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 {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} 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 { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n} from '../config/agent-profile.js';\nimport { resolveUserProfilePath } from '../config/paths.js';\nimport { getProjectForSession } from '../projects/workspace.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 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 turnDiffTracker: TurnDiffTracker;\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 automations 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\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.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 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 getAutomationService: config.getAutomationService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n getSelfVerifyPromptContext: (sessionKey, agentId) => [\n this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId),\n this.turnDiffTracker.buildFinalGuardContext(sessionKey),\n ].filter(Boolean).join('\\n\\n'),\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 turnDiffTracker: this.turnDiffTracker,\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: () => undefined,\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 reloadSkills: () => this.refreshSkillsAfterDiskChange(),\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: (sessionKey) => this.getContextWindowForSession(sessionKey),\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 resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(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 windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n 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 this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 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 this.turnDiffTracker = new TurnDiffTracker();\n\n this.requestLimiter = new RequestLimiter({\n 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.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 getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\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 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 async reconcileDreamingNow(): Promise<void> {\n return Promise.resolve();\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 async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\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, getProjectForSession(sessionKey));\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads the global user profile and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n void sessionKey;\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const userPath = resolveUserProfilePath();\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.getContextWindowForSession(sessionKey);\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\n private getContextWindowForSession(sessionKey: string): number {\n try {\n return this.modelManager.getResolvedModelForSession(sessionKey).contextWindow ?? 128000;\n } catch (err) {\n log.warn({ err, sessionKey }, 'Failed to resolve session context window; using default');\n return 128000;\n }\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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"}
|
|
@@ -31,10 +31,10 @@ export interface SessionInspectorOptions {
|
|
|
31
31
|
/** Effective config snapshot accessor (honours runtime overrides). */
|
|
32
32
|
getConfig: () => Config | undefined;
|
|
33
33
|
/**
|
|
34
|
-
* Nominal context window the session is budgeted against.
|
|
35
|
-
*
|
|
34
|
+
* Nominal context window the session is budgeted against. Derived from the
|
|
35
|
+
* effective session model metadata, defaulting to 128k.
|
|
36
36
|
*/
|
|
37
|
-
getContextWindow: () => number;
|
|
37
|
+
getContextWindow: (sessionKey: string) => number;
|
|
38
38
|
}
|
|
39
39
|
export interface SessionContextUsage {
|
|
40
40
|
estimatedTokens: number;
|
|
@@ -52,6 +52,8 @@ export interface SessionAgentConfigView {
|
|
|
52
52
|
export declare class SessionInspector {
|
|
53
53
|
private readonly opts;
|
|
54
54
|
constructor(opts: SessionInspectorOptions);
|
|
55
|
+
private ensureEffectiveSessionModel;
|
|
56
|
+
private contextWindowForSession;
|
|
55
57
|
/**
|
|
56
58
|
* Manual compaction triggered by a user / API. Always forces a compaction
|
|
57
59
|
* pass (`force: true` by default) and evicts the in-memory agent so the
|
|
@@ -16,6 +16,18 @@ var SessionInspector = class {
|
|
|
16
16
|
constructor(opts) {
|
|
17
17
|
this.opts = opts;
|
|
18
18
|
}
|
|
19
|
+
async ensureEffectiveSessionModel(sessionKey) {
|
|
20
|
+
await this.opts.sessionHydrator.model(sessionKey);
|
|
21
|
+
const cfg = this.opts.getConfig();
|
|
22
|
+
if (!cfg) return;
|
|
23
|
+
const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);
|
|
24
|
+
const profileModelRef = profile.primaryModelRef?.trim();
|
|
25
|
+
if (profileModelRef) this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);
|
|
26
|
+
}
|
|
27
|
+
async contextWindowForSession(sessionKey) {
|
|
28
|
+
await this.ensureEffectiveSessionModel(sessionKey);
|
|
29
|
+
return this.opts.getContextWindow(sessionKey);
|
|
30
|
+
}
|
|
19
31
|
/**
|
|
20
32
|
* Manual compaction triggered by a user / API. Always forces a compaction
|
|
21
33
|
* pass (`force: true` by default) and evicts the in-memory agent so the
|
|
@@ -23,7 +35,7 @@ var SessionInspector = class {
|
|
|
23
35
|
*/
|
|
24
36
|
async compact(sessionKey, options) {
|
|
25
37
|
const messages = await this.opts.sessionStore.load(sessionKey);
|
|
26
|
-
const contextWindow = this.
|
|
38
|
+
const contextWindow = await this.contextWindowForSession(sessionKey);
|
|
27
39
|
const result = await this.opts.sessionStore.compact(sessionKey, messages, contextWindow, options?.instructions, options?.force ?? true);
|
|
28
40
|
if (result.compacted) this.opts.agentManager.removeAgent(sessionKey);
|
|
29
41
|
log.info({
|
|
@@ -53,7 +65,7 @@ var SessionInspector = class {
|
|
|
53
65
|
/** Rough context usage for TUI footer (estimated tokens vs nominal budget). */
|
|
54
66
|
async contextUsage(sessionKey) {
|
|
55
67
|
const messages = await this.opts.sessionStore.load(sessionKey);
|
|
56
|
-
const contextWindow = this.
|
|
68
|
+
const contextWindow = await this.contextWindowForSession(sessionKey);
|
|
57
69
|
const estimatedTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);
|
|
58
70
|
return {
|
|
59
71
|
estimatedTokens,
|
|
@@ -66,7 +78,7 @@ var SessionInspector = class {
|
|
|
66
78
|
const cfg = this.opts.getConfig();
|
|
67
79
|
if (!cfg) throw new Error("SessionInspector requires a config snapshot to render report");
|
|
68
80
|
const messages = await this.opts.sessionStore.load(sessionKey);
|
|
69
|
-
const cw = this.
|
|
81
|
+
const cw = await this.contextWindowForSession(sessionKey);
|
|
70
82
|
const computed = this.stats(sessionKey, messages);
|
|
71
83
|
const model = this.opts.modelManager.getModelForSession(sessionKey);
|
|
72
84
|
const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, await this.opts.sessionConfigStore.get(sessionKey), getProjectForSession(sessionKey));
|
|
@@ -94,13 +106,10 @@ var SessionInspector = class {
|
|
|
94
106
|
}
|
|
95
107
|
/** Resolved thinking / model / workspace for the Web UI. */
|
|
96
108
|
async agentConfig(sessionKey) {
|
|
97
|
-
await this.
|
|
109
|
+
await this.ensureEffectiveSessionModel(sessionKey);
|
|
98
110
|
const cfg = this.opts.getConfig();
|
|
99
111
|
if (!cfg) throw new Error("SessionInspector requires a config snapshot to resolve agent config");
|
|
100
112
|
const sc = await this.opts.sessionConfigStore.get(sessionKey);
|
|
101
|
-
const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);
|
|
102
|
-
const profileModelRef = profile.primaryModelRef?.trim();
|
|
103
|
-
if (profileModelRef) this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);
|
|
104
113
|
const level = await resolveEffectiveThinkingLevel(this.opts.sessionConfigStore, sessionKey, null, "medium");
|
|
105
114
|
const reasoningLevel = await resolveEffectiveReasoningLevel(this.opts.sessionConfigStore, sessionKey, "stream");
|
|
106
115
|
const verboseLevel = await resolveVerboseLevel(this.opts.sessionConfigStore, sessionKey, "full");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-inspector.js","names":[],"sources":["../../../../src/agent/session/session-inspector.ts"],"sourcesContent":["/**\n * SessionInspector — read-only / introspection-oriented operations on a session.\n *\n * Owns the five methods previously scattered across `AgentService` that all\n * compute a \"summary view\" of one session:\n * - `compact` (manual user-triggered compaction)\n * - `btwQuery` (one-shot LLM answer with transcript as background)\n * - `report` (Markdown / JSON `/context` summary)\n * - `agentConfig` (resolved thinking + model + workspace for the Web UI)\n * - `contextUsage` (rough token budget vs estimated transcript)\n *\n * The shared helper `computeStats` keeps the three callsites that need\n * `getWindowStats` / `getCompactionStats` / `estimateTokenUsage` in lockstep.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport {\n effectiveWorkspacePathForSession,\n projectWorkspacePath,\n resolveEffectiveReasoningLevel,\n resolveEffectiveThinkingLevel,\n resolveVerboseLevel,\n type SessionConfigStore,\n} from '../../session/index.js';\nimport { getProjectForSession } from '../../projects/workspace.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport { runBtwQuery } from '../service/btw-query.js';\nimport { formatSessionContextReport } from '../service/session-context-report.js';\nimport type { ReasoningLevel, VerboseLevel } from '../transcript/thinking-types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SessionHydrator } from './session-hydrator.js';\n\nconst log = createLogger('SessionInspector');\n\nexport interface SessionInspectorOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n sessionHydrator: SessionHydrator;\n /** Effective config snapshot accessor (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /**\n * Nominal context window the session is budgeted against. Currently derived\n * from the active model metadata, defaulting to 128k.\n */\n getContextWindow: () => number;\n}\n\nexport interface SessionContextUsage {\n estimatedTokens: number;\n contextWindow: number;\n usagePercent: number | null;\n}\n\nexport interface SessionAgentConfigView {\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n verboseLevel: VerboseLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n}\n\nexport class SessionInspector {\n private readonly opts: SessionInspectorOptions;\n\n constructor(opts: SessionInspectorOptions) {\n this.opts = opts;\n }\n\n /**\n * Manual compaction triggered by a user / API. Always forces a compaction\n * pass (`force: true` by default) and evicts the in-memory agent so the\n * next turn reloads from the compacted transcript.\n */\n async compact(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = this.opts.getContextWindow();\n const result = await this.opts.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n this.opts.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /** One-shot LLM answer for `/btw`: transcript as background, not persisted. */\n btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n return runBtwQuery({\n sessionKey,\n question,\n sessionStore: this.opts.sessionStore,\n modelForSession: this.opts.modelManager.getModelForSession(sessionKey),\n log,\n });\n }\n\n /** Cheap stats used by both the report and other callers. */\n stats(sessionKey: string, messages: AgentMessage[]): {\n windowStats: ReturnType<SessionStore['getWindowStats']>;\n compactionStats: ReturnType<SessionStore['getCompactionStats']>;\n tokenEstimate: ReturnType<SessionStore['estimateTokenUsage']>;\n } {\n return {\n windowStats: this.opts.sessionStore.getWindowStats(messages),\n compactionStats: this.opts.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.opts.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n /** Rough context usage for TUI footer (estimated tokens vs nominal budget). */\n async contextUsage(sessionKey: string): Promise<SessionContextUsage> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = this.opts.getContextWindow();\n const estimatedTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent =\n contextWindow > 0 ? Math.min(100, Math.round((estimatedTokens / contextWindow) * 100)) : null;\n return { estimatedTokens, contextWindow, usagePercent };\n }\n\n /** Markdown or JSON summary for `/context`. */\n async report(sessionKey: string, mode: 'list' | 'detail' | 'json'): Promise<string> {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to render report');\n }\n const messages = await this.opts.sessionStore.load(sessionKey);\n const cw = this.opts.getContextWindow();\n const computed = this.stats(sessionKey, messages);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n const project = getProjectForSession(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const estTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const deniedTools = [...profile.tools.denied].sort((a, b) => a.localeCompare(b));\n const toolsSummary = deniedTools.length > 0 ? `denied: ${deniedTools.join(', ')}` : '(no denied tools)';\n\n return formatSessionContextReport({\n sessionKey,\n mode,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n thinkingDefault: undefined,\n reasoningDefault: undefined,\n verboseDefault: undefined,\n compaction: undefined,\n toolsFlagsSummary: toolsSummary,\n windowStats: computed.windowStats,\n compactionRunStats: computed.compactionStats,\n });\n }\n\n /** Resolved thinking / model / workspace for the Web UI. */\n async agentConfig(sessionKey: string): Promise<SessionAgentConfigView> {\n await this.opts.sessionHydrator.model(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to resolve agent config');\n }\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n\n // Ensure model display matches the effective agent profile even before an Agent instance exists.\n // Otherwise `ModelManager.getModelForSession()` falls back to the global default until the first turn creates the agent.\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);\n }\n\n const defThink = 'medium';\n const level = await resolveEffectiveThinkingLevel(this.opts.sessionConfigStore, sessionKey, null, defThink);\n const defReason = 'stream' as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.opts.sessionConfigStore, sessionKey, defReason);\n const defVerbose = 'full' as VerboseLevel;\n const verboseLevel = await resolveVerboseLevel(this.opts.sessionConfigStore, sessionKey, defVerbose);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const project = getProjectForSession(sessionKey);\n const projectWorkspace = projectWorkspacePath(project);\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n verboseLevel,\n effectiveWorkspacePath: effectiveWorkspacePathForSession(cfg, sessionKey, sc, project),\n workingDirectoryLocked: Boolean(projectWorkspace || sc?.workingDirectoryOverride?.trim()),\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-inspector.js","names":[],"sources":["../../../../src/agent/session/session-inspector.ts"],"sourcesContent":["/**\n * SessionInspector — read-only / introspection-oriented operations on a session.\n *\n * Owns the five methods previously scattered across `AgentService` that all\n * compute a \"summary view\" of one session:\n * - `compact` (manual user-triggered compaction)\n * - `btwQuery` (one-shot LLM answer with transcript as background)\n * - `report` (Markdown / JSON `/context` summary)\n * - `agentConfig` (resolved thinking + model + workspace for the Web UI)\n * - `contextUsage` (rough token budget vs estimated transcript)\n *\n * The shared helper `computeStats` keeps the three callsites that need\n * `getWindowStats` / `getCompactionStats` / `estimateTokenUsage` in lockstep.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport {\n effectiveWorkspacePathForSession,\n projectWorkspacePath,\n resolveEffectiveReasoningLevel,\n resolveEffectiveThinkingLevel,\n resolveVerboseLevel,\n type SessionConfigStore,\n} from '../../session/index.js';\nimport { getProjectForSession } from '../../projects/workspace.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport { runBtwQuery } from '../service/btw-query.js';\nimport { formatSessionContextReport } from '../service/session-context-report.js';\nimport type { ReasoningLevel, VerboseLevel } from '../transcript/thinking-types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SessionHydrator } from './session-hydrator.js';\n\nconst log = createLogger('SessionInspector');\n\nexport interface SessionInspectorOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n sessionHydrator: SessionHydrator;\n /** Effective config snapshot accessor (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /**\n * Nominal context window the session is budgeted against. Derived from the\n * effective session model metadata, defaulting to 128k.\n */\n getContextWindow: (sessionKey: string) => number;\n}\n\nexport interface SessionContextUsage {\n estimatedTokens: number;\n contextWindow: number;\n usagePercent: number | null;\n}\n\nexport interface SessionAgentConfigView {\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n verboseLevel: VerboseLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n}\n\nexport class SessionInspector {\n private readonly opts: SessionInspectorOptions;\n\n constructor(opts: SessionInspectorOptions) {\n this.opts = opts;\n }\n\n private async ensureEffectiveSessionModel(sessionKey: string): Promise<void> {\n await this.opts.sessionHydrator.model(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) return;\n\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);\n }\n }\n\n private async contextWindowForSession(sessionKey: string): Promise<number> {\n await this.ensureEffectiveSessionModel(sessionKey);\n return this.opts.getContextWindow(sessionKey);\n }\n\n /**\n * Manual compaction triggered by a user / API. Always forces a compaction\n * pass (`force: true` by default) and evicts the in-memory agent so the\n * next turn reloads from the compacted transcript.\n */\n async compact(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const result = await this.opts.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n this.opts.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /** One-shot LLM answer for `/btw`: transcript as background, not persisted. */\n btwQuery(sessionKey: string, question: string): Promise<{ text: string; error?: string }> {\n return runBtwQuery({\n sessionKey,\n question,\n sessionStore: this.opts.sessionStore,\n modelForSession: this.opts.modelManager.getModelForSession(sessionKey),\n log,\n });\n }\n\n /** Cheap stats used by both the report and other callers. */\n stats(sessionKey: string, messages: AgentMessage[]): {\n windowStats: ReturnType<SessionStore['getWindowStats']>;\n compactionStats: ReturnType<SessionStore['getCompactionStats']>;\n tokenEstimate: ReturnType<SessionStore['estimateTokenUsage']>;\n } {\n return {\n windowStats: this.opts.sessionStore.getWindowStats(messages),\n compactionStats: this.opts.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.opts.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n /** Rough context usage for TUI footer (estimated tokens vs nominal budget). */\n async contextUsage(sessionKey: string): Promise<SessionContextUsage> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const estimatedTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent =\n contextWindow > 0 ? Math.min(100, Math.round((estimatedTokens / contextWindow) * 100)) : null;\n return { estimatedTokens, contextWindow, usagePercent };\n }\n\n /** Markdown or JSON summary for `/context`. */\n async report(sessionKey: string, mode: 'list' | 'detail' | 'json'): Promise<string> {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to render report');\n }\n const messages = await this.opts.sessionStore.load(sessionKey);\n const cw = await this.contextWindowForSession(sessionKey);\n const computed = this.stats(sessionKey, messages);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n const project = getProjectForSession(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const estTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const deniedTools = [...profile.tools.denied].sort((a, b) => a.localeCompare(b));\n const toolsSummary = deniedTools.length > 0 ? `denied: ${deniedTools.join(', ')}` : '(no denied tools)';\n\n return formatSessionContextReport({\n sessionKey,\n mode,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n thinkingDefault: undefined,\n reasoningDefault: undefined,\n verboseDefault: undefined,\n compaction: undefined,\n toolsFlagsSummary: toolsSummary,\n windowStats: computed.windowStats,\n compactionRunStats: computed.compactionStats,\n });\n }\n\n /** Resolved thinking / model / workspace for the Web UI. */\n async agentConfig(sessionKey: string): Promise<SessionAgentConfigView> {\n await this.ensureEffectiveSessionModel(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to resolve agent config');\n }\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n\n const defThink = 'medium';\n const level = await resolveEffectiveThinkingLevel(this.opts.sessionConfigStore, sessionKey, null, defThink);\n const defReason = 'stream' as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.opts.sessionConfigStore, sessionKey, defReason);\n const defVerbose = 'full' as VerboseLevel;\n const verboseLevel = await resolveVerboseLevel(this.opts.sessionConfigStore, sessionKey, defVerbose);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const project = getProjectForSession(sessionKey);\n const projectWorkspace = projectWorkspacePath(project);\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n verboseLevel,\n effectiveWorkspacePath: effectiveWorkspacePathForSession(cfg, sessionKey, sc, project),\n workingDirectoryLocked: Boolean(projectWorkspace || sc?.workingDirectoryOverride?.trim()),\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
function isSessionSourceBinding(value) {
|
|
3
3
|
if (!value || typeof value !== "object") return false;
|
|
4
4
|
const row = value;
|
|
5
|
-
return row.kind === "note" && typeof row.sourceId === "string" && row.sourceId.trim().length > 0 && typeof row.version === "string" && typeof row.attachedAt === "number";
|
|
5
|
+
return (row.kind === "note" || row.kind === "work_item") && typeof row.sourceId === "string" && row.sourceId.trim().length > 0 && typeof row.version === "string" && typeof row.attachedAt === "number";
|
|
6
6
|
}
|
|
7
7
|
//#endregion
|
|
8
8
|
export { isSessionSourceBinding };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/agent/source-context/types.ts"],"sourcesContent":["import type { ImageContent } from '@earendil-works/pi-ai';\n\nexport interface SessionSourceBinding {\n kind: 'note';\n sourceId: string;\n version: string;\n attachedAt: number;\n}\n\nexport interface AgentSourceContext {\n kind: SessionSourceBinding['kind'];\n sourceId: string;\n version: string;\n title: string;\n text: string;\n images?: ImageContent[];\n tokenEstimate?: number;\n truncated?: boolean;\n}\n\nexport type AgentSourceContextResolver = (\n binding: SessionSourceBinding,\n sessionKey: string,\n) => Promise<AgentSourceContext | null>;\n\nexport function isSessionSourceBinding(value: unknown): value is SessionSourceBinding {\n if (!value || typeof value !== 'object') return false;\n const row = value as Record<string, unknown>;\n return row.kind === 'note'\n && typeof row.sourceId === 'string'\n && row.sourceId.trim().length > 0\n && typeof row.version === 'string'\n && typeof row.attachedAt === 'number';\n}\n"],"mappings":";AAyBA,SAAgB,uBAAuB,OAA+C;AACpF,KAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;CAChD,MAAM,MAAM;AACZ,
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/agent/source-context/types.ts"],"sourcesContent":["import type { ImageContent } from '@earendil-works/pi-ai';\n\nexport interface SessionSourceBinding {\n kind: 'note' | 'work_item';\n sourceId: string;\n version: string;\n attachedAt: number;\n}\n\nexport interface AgentSourceContext {\n kind: SessionSourceBinding['kind'];\n sourceId: string;\n version: string;\n title: string;\n text: string;\n images?: ImageContent[];\n tokenEstimate?: number;\n truncated?: boolean;\n}\n\nexport type AgentSourceContextResolver = (\n binding: SessionSourceBinding,\n sessionKey: string,\n) => Promise<AgentSourceContext | null>;\n\nexport function isSessionSourceBinding(value: unknown): value is SessionSourceBinding {\n if (!value || typeof value !== 'object') return false;\n const row = value as Record<string, unknown>;\n return (row.kind === 'note' || row.kind === 'work_item')\n && typeof row.sourceId === 'string'\n && row.sourceId.trim().length > 0\n && typeof row.version === 'string'\n && typeof row.attachedAt === 'number';\n}\n"],"mappings":";AAyBA,SAAgB,uBAAuB,OAA+C;AACpF,KAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;CAChD,MAAM,MAAM;AACZ,SAAQ,IAAI,SAAS,UAAU,IAAI,SAAS,gBACvC,OAAO,IAAI,aAAa,YACxB,IAAI,SAAS,MAAM,CAAC,SAAS,KAC7B,OAAO,IAAI,YAAY,YACvB,OAAO,IAAI,eAAe"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { checkFileSafety } from "../prompt/safety.js";
|
|
2
2
|
import { resolvePathUnderWorkspace } from "./tool-paths.js";
|
|
3
3
|
import { evaluateFilePolicy } from "../sandbox/exec-policy.js";
|
|
4
|
-
import { generateDiffString, normalizeToLF } from "./edit-diff.js";
|
|
4
|
+
import { detectLineEnding, generateDiffString, normalizeToLF, restoreLineEndings } from "./edit-diff.js";
|
|
5
5
|
import { dirname, relative } from "node:path";
|
|
6
6
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
@@ -129,7 +129,7 @@ function applyHunks(original, hunks, path) {
|
|
|
129
129
|
return content;
|
|
130
130
|
}
|
|
131
131
|
async function readExisting(path) {
|
|
132
|
-
return
|
|
132
|
+
return readFile(path, "utf-8");
|
|
133
133
|
}
|
|
134
134
|
async function assertWritable(workspace, path) {
|
|
135
135
|
const quick = checkFileSafety("write", path);
|
|
@@ -142,74 +142,128 @@ async function assertWritable(workspace, path) {
|
|
|
142
142
|
if (!policy.allowed) throw new Error(`Sandbox: ${policy.reason}`);
|
|
143
143
|
return resolvePathUnderWorkspace(path, workspace);
|
|
144
144
|
}
|
|
145
|
+
async function pathExists(path) {
|
|
146
|
+
try {
|
|
147
|
+
await stat(path);
|
|
148
|
+
return true;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (e.code === "ENOENT") return false;
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function buildPatchPlan(workspace, parsed) {
|
|
155
|
+
const changes = [];
|
|
156
|
+
const plannedTargets = /* @__PURE__ */ new Set();
|
|
157
|
+
const rememberTarget = (target) => {
|
|
158
|
+
if (plannedTargets.has(target)) throw new Error(`Patch touches the same target multiple times: ${relative(workspace, target)}`);
|
|
159
|
+
plannedTargets.add(target);
|
|
160
|
+
};
|
|
161
|
+
for (const op of parsed) {
|
|
162
|
+
if (op.kind === "add") {
|
|
163
|
+
const target = await assertWritable(workspace, op.path);
|
|
164
|
+
rememberTarget(target);
|
|
165
|
+
if (await pathExists(target)) throw new Error(`Add file target already exists: ${op.path}`);
|
|
166
|
+
const diff = generateDiffString("", op.content, op.path);
|
|
167
|
+
const counts = countDiff(diff);
|
|
168
|
+
changes.push({
|
|
169
|
+
kind: "add",
|
|
170
|
+
path: op.path,
|
|
171
|
+
absolutePath: target,
|
|
172
|
+
newContent: op.content,
|
|
173
|
+
...counts,
|
|
174
|
+
diff
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (op.kind === "delete") {
|
|
179
|
+
const target = await assertWritable(workspace, op.path);
|
|
180
|
+
rememberTarget(target);
|
|
181
|
+
const oldContent = await readExisting(target);
|
|
182
|
+
const diff = generateDiffString(oldContent, "", op.path);
|
|
183
|
+
const counts = countDiff(diff);
|
|
184
|
+
changes.push({
|
|
185
|
+
kind: "delete",
|
|
186
|
+
path: op.path,
|
|
187
|
+
absolutePath: target,
|
|
188
|
+
oldContent,
|
|
189
|
+
...counts,
|
|
190
|
+
diff
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const target = await assertWritable(workspace, op.path);
|
|
195
|
+
rememberTarget(target);
|
|
196
|
+
const oldContent = await readExisting(target);
|
|
197
|
+
const lineEnding = detectLineEnding(oldContent);
|
|
198
|
+
const newContent = restoreLineEndings(applyHunks(normalizeToLF(oldContent), op.hunks, op.path), lineEnding);
|
|
199
|
+
const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;
|
|
200
|
+
if (destination !== target) {
|
|
201
|
+
rememberTarget(destination);
|
|
202
|
+
if (await pathExists(destination)) throw new Error(`Move target already exists: ${op.moveTo}`);
|
|
203
|
+
}
|
|
204
|
+
const diff = generateDiffString(oldContent, newContent, op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path);
|
|
205
|
+
const counts = countDiff(diff);
|
|
206
|
+
changes.push({
|
|
207
|
+
kind: op.moveTo ? "move" : "update",
|
|
208
|
+
path: op.path,
|
|
209
|
+
absolutePath: target,
|
|
210
|
+
moveTo: op.moveTo,
|
|
211
|
+
absoluteMoveTo: op.moveTo ? destination : void 0,
|
|
212
|
+
oldContent,
|
|
213
|
+
newContent,
|
|
214
|
+
...counts,
|
|
215
|
+
diff
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return changes;
|
|
219
|
+
}
|
|
220
|
+
async function commitPatchPlan(changes) {
|
|
221
|
+
const committed = [];
|
|
222
|
+
try {
|
|
223
|
+
for (const change of changes) {
|
|
224
|
+
committed.push(change);
|
|
225
|
+
if (change.kind === "add") {
|
|
226
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
227
|
+
await writeFile(change.absolutePath, change.newContent ?? "", "utf-8");
|
|
228
|
+
} else if (change.kind === "delete") await rm(change.absolutePath);
|
|
229
|
+
else {
|
|
230
|
+
const destination = change.absoluteMoveTo ?? change.absolutePath;
|
|
231
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
232
|
+
await writeFile(destination, change.newContent ?? "", "utf-8");
|
|
233
|
+
if (destination !== change.absolutePath) await rm(change.absolutePath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
for (const change of committed.reverse()) try {
|
|
238
|
+
if (change.kind === "add") await rm(change.absolutePath, { force: true });
|
|
239
|
+
else if (change.kind === "delete") {
|
|
240
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
241
|
+
await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
242
|
+
} else if (change.absoluteMoveTo) {
|
|
243
|
+
await rm(change.absoluteMoveTo, { force: true });
|
|
244
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
245
|
+
await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
246
|
+
} else await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
247
|
+
} catch {}
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
145
251
|
function createApplyPatchTool(workspace, options) {
|
|
146
252
|
return {
|
|
147
253
|
name: "apply_patch",
|
|
148
254
|
label: "Apply Patch",
|
|
149
255
|
description: "Apply a strict multi-file patch. Use this for code edits instead of shell redirection or write_file.",
|
|
150
256
|
parameters: ApplyPatchSchema,
|
|
257
|
+
mutatesWorkspace: true,
|
|
258
|
+
mutationScope: "workspace",
|
|
259
|
+
supportsParallel: false,
|
|
260
|
+
idempotent: false,
|
|
261
|
+
requiresExclusiveWorkspaceLock: true,
|
|
262
|
+
finalGuardRelevant: true,
|
|
151
263
|
async execute(_toolCallId, params) {
|
|
152
264
|
try {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
for (const op of parsed) {
|
|
156
|
-
if (op.kind === "add") {
|
|
157
|
-
const target = await assertWritable(workspace, op.path);
|
|
158
|
-
let exists = false;
|
|
159
|
-
try {
|
|
160
|
-
await stat(target);
|
|
161
|
-
exists = true;
|
|
162
|
-
} catch (e) {
|
|
163
|
-
if (e.code !== "ENOENT") throw e;
|
|
164
|
-
}
|
|
165
|
-
if (exists) throw new Error(`Add file target already exists: ${op.path}`);
|
|
166
|
-
await mkdir(dirname(target), { recursive: true });
|
|
167
|
-
await writeFile(target, op.content, "utf-8");
|
|
168
|
-
const diff = generateDiffString("", op.content, op.path);
|
|
169
|
-
const counts = countDiff(diff);
|
|
170
|
-
changes.push({
|
|
171
|
-
kind: "add",
|
|
172
|
-
path: op.path,
|
|
173
|
-
absolutePath: target,
|
|
174
|
-
...counts,
|
|
175
|
-
diff
|
|
176
|
-
});
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (op.kind === "delete") {
|
|
180
|
-
const target = await assertWritable(workspace, op.path);
|
|
181
|
-
const oldContent = await readExisting(target);
|
|
182
|
-
await rm(target);
|
|
183
|
-
const diff = generateDiffString(oldContent, "", op.path);
|
|
184
|
-
const counts = countDiff(diff);
|
|
185
|
-
changes.push({
|
|
186
|
-
kind: "delete",
|
|
187
|
-
path: op.path,
|
|
188
|
-
absolutePath: target,
|
|
189
|
-
...counts,
|
|
190
|
-
diff
|
|
191
|
-
});
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const target = await assertWritable(workspace, op.path);
|
|
195
|
-
const oldContent = await readExisting(target);
|
|
196
|
-
const newContent = applyHunks(oldContent, op.hunks, op.path);
|
|
197
|
-
const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;
|
|
198
|
-
await mkdir(dirname(destination), { recursive: true });
|
|
199
|
-
await writeFile(destination, newContent, "utf-8");
|
|
200
|
-
if (destination !== target) await rm(target);
|
|
201
|
-
const diff = generateDiffString(oldContent, newContent, op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path);
|
|
202
|
-
const counts = countDiff(diff);
|
|
203
|
-
changes.push({
|
|
204
|
-
kind: op.moveTo ? "move" : "update",
|
|
205
|
-
path: op.path,
|
|
206
|
-
absolutePath: target,
|
|
207
|
-
moveTo: op.moveTo,
|
|
208
|
-
absoluteMoveTo: op.moveTo ? destination : void 0,
|
|
209
|
-
...counts,
|
|
210
|
-
diff
|
|
211
|
-
});
|
|
212
|
-
}
|
|
265
|
+
const changes = await buildPatchPlan(workspace, parsePatch(params.patch ?? ""));
|
|
266
|
+
await commitPatchPlan(changes);
|
|
213
267
|
const diff = changes.map((change) => change.diff).join("\n");
|
|
214
268
|
const added = changes.reduce((sum, change) => sum + change.added, 0);
|
|
215
269
|
const removed = changes.reduce((sum, change) => sum + change.removed, 0);
|