@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":"short-term-store.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/short-term-store.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash, randomUUID } from 'node:crypto';\n\nimport type { MemorySearchOptions } from '../../prompt/memory/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from './constants.js';\nimport { buildEntryKey, clamp01, isoDay, normalizeMemoryPath } from './utils.js';\n\nconst log = createLogger('Dreaming:Store');\n\nexport type DreamingStoreEntry = {\n key: string;\n path: string;\n startLine: number;\n endLine: number;\n snippet: string;\n\n // ── Signal dimensions ────────────────────────────────────────────────\n /** Number of times this snippet was returned by a memory recall query. */\n recallCount: number;\n /** Number of times recorded from source-side sweep phases. */\n sourceCount: number;\n /** Number of times replayed from grounded context (agent-initiated). */\n groundedCount: number;\n /** Number of times the light phase touched this entry. */\n lightHits: number;\n /** Number of times the REM phase touched this entry. */\n remHits: number;\n /** Cross-phase hit count (light + deep + rem combined touches). */\n phaseHitCount: number;\n /** Weighted aggregate of all signal dimensions. */\n totalSignalCount: number;\n\n // ── Score tracking ───────────────────────────────────────────────────\n totalScore: number;\n maxScore: number;\n queryHashes: string[];\n recallDays: string[];\n\n // ── Timestamps ───────────────────────────────────────────────────────\n firstRecalledAt: string;\n lastRecalledAt: string;\n promotedAt?: string;\n};\n\nexport type DreamingStore = {\n version: 1;\n updatedAt: string;\n entries: Record<string, DreamingStoreEntry>;\n};\n\ntype MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];\n\nfunction isRecordableMemoryPath(rel: string): boolean {\n const p = normalizeMemoryPath(rel);\n return p.length > 0 && !p.startsWith('../') && !path.isAbsolute(p);\n}\n\nfunction hashQuery(query: string): string {\n return createHash('sha1').update(query.trim().toLowerCase()).digest('hex').slice(0, 12);\n}\n\nfunction mergeRecentDistinct(existing: string[], nextValue: string, limit: number): string[] {\n const seen = new Set<string>();\n const normalized = existing\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.trim())\n .filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));\n if (nextValue && !normalized.includes(nextValue)) {\n normalized.push(nextValue);\n }\n return normalized.length <= limit ? normalized : normalized.slice(-limit);\n}\n\nfunction mergeQueryHashes(existing: string[], nextHash: string): string[] {\n const out = mergeRecentDistinct(existing, nextHash, 32);\n return out;\n}\n\nfunction emptyStore(nowIso: string): DreamingStore {\n return { version: 1, updatedAt: nowIso, entries: {} };\n}\n\nasync function ensureDreamDir(workspaceDir: string): Promise<string> {\n const dir = path.join(workspaceDir, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\nasync function readStore(workspaceDir: string, nowIso: string): Promise<DreamingStore> {\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n try {\n const raw = await fs.readFile(storePath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return emptyStore(nowIso);\n const rec = parsed as Partial<DreamingStore>;\n if (rec.version !== 1 || !rec.entries || typeof rec.entries !== 'object') {\n return emptyStore(nowIso);\n }\n return {\n version: 1,\n updatedAt: typeof rec.updatedAt === 'string' ? rec.updatedAt : nowIso,\n entries: rec.entries as Record<string, DreamingStoreEntry>,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return emptyStore(nowIso);\n log.warn({ err, workspaceDir }, 'Failed to read dreaming store; resetting');\n return emptyStore(nowIso);\n }\n}\n\nasync function writeStore(workspaceDir: string, store: DreamingStore): Promise<void> {\n await ensureDreamDir(workspaceDir);\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, storePath);\n}\n\nexport async function recordDreamingRecalls(params: {\n workspaceDir: string;\n query: string;\n matches: MemoryMatch[];\n options?: MemorySearchOptions;\n now?: Date;\n}): Promise<{ recorded: number; skipped: number; storePath: string }> {\n const workspaceDir = params.workspaceDir.trim();\n const query = params.query.trim();\n if (!workspaceDir || !query) {\n return { recorded: 0, skipped: params.matches.length, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n }\n const now = params.now ?? new Date();\n const nowIso = now.toISOString();\n const dayBucket = isoDay(now);\n const qHash = hashQuery(query);\n\n const store = await readStore(workspaceDir, nowIso);\n\n let recorded = 0;\n let skipped = 0;\n for (const match of params.matches) {\n const file = typeof match?.file === \"string\" ? match.file : \"\";\n if (!file || !isRecordableMemoryPath(file)) {\n skipped += 1;\n continue;\n }\n const lines = typeof match?.lines === \"string\" ? match.lines.trim() : \"\";\n const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];\n const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {\n skipped += 1;\n continue;\n }\n const score = clamp01(Number(match.score));\n const key = buildEntryKey({ path: file, startLine, endLine });\n const existing = store.entries[key];\n const snippet = lines.length > 0 ? lines.slice(0, 360) : file;\n\n const next: DreamingStoreEntry = existing\n ? {\n ...existing,\n snippet,\n recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),\n sourceCount: existing.sourceCount ?? 0,\n groundedCount: existing.groundedCount ?? 0,\n lightHits: existing.lightHits ?? 0,\n remHits: existing.remHits ?? 0,\n phaseHitCount: existing.phaseHitCount ?? 0,\n totalSignalCount: Math.max(0, (existing.totalSignalCount ?? existing.recallCount ?? 0) + 1),\n totalScore: Math.max(0, existing.totalScore + score),\n maxScore: Math.max(existing.maxScore, score),\n queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),\n recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),\n lastRecalledAt: nowIso,\n }\n : {\n key,\n path: normalizeMemoryPath(file),\n startLine,\n endLine,\n snippet,\n recallCount: 1,\n sourceCount: 0,\n groundedCount: 0,\n lightHits: 0,\n remHits: 0,\n phaseHitCount: 0,\n totalSignalCount: 1,\n totalScore: score,\n maxScore: score,\n queryHashes: [qHash],\n recallDays: [dayBucket],\n firstRecalledAt: nowIso,\n lastRecalledAt: nowIso,\n };\n\n store.entries[key] = next;\n recorded += 1;\n }\n\n if (recorded > 0) {\n store.updatedAt = nowIso;\n await writeStore(workspaceDir, store);\n }\n\n return {\n recorded,\n skipped,\n storePath: SHORT_TERM_RECALL_STORE_RELATIVE,\n };\n}\n\nexport async function withDreamingPromotionLock<T>(\n workspaceDir: string,\n task: () => Promise<T>,\n): Promise<T> {\n const lockPath = path.join(workspaceDir, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n await ensureDreamDir(workspaceDir);\n\n const startedAt = Date.now();\n const timeoutMs = 10_000;\n const retryDelayMs = 50;\n\n while (true) {\n try {\n const handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}\\n`, 'utf-8').catch(() => undefined);\n try {\n return await task();\n } finally {\n await handle.close().catch(() => undefined);\n await fs.unlink(lockPath).catch(() => undefined);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);\n }\n await new Promise((r) => setTimeout(r, retryDelayMs));\n }\n }\n}\n\nexport async function loadDreamingStore(params: {\n workspaceDir: string;\n}): Promise<{ store: DreamingStore; storePath: string }> {\n const nowIso = new Date().toISOString();\n const store = await readStore(params.workspaceDir, nowIso);\n return { store, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n}\n\nexport async function saveDreamingStore(params: {\n workspaceDir: string;\n store: DreamingStore;\n}): Promise<void> {\n await writeStore(params.workspaceDir, params.store);\n}\n\n// ── Phase-level signal helpers ─────────────────────────────────────────\n\ntype PhaseSignalField = 'sourceCount' | 'groundedCount' | 'lightHits' | 'remHits';\n\n/**\n * Increment a phase-specific signal counter on an existing store entry.\n * Also bumps `phaseHitCount` and `totalSignalCount`.\n * Returns `true` if the entry existed and was updated.\n */\nexport function bumpEntryPhaseSignal(\n entry: DreamingStoreEntry,\n field: PhaseSignalField,\n increment = 1,\n): void {\n entry[field] = (entry[field] ?? 0) + increment;\n entry.phaseHitCount = (entry.phaseHitCount ?? 0) + increment;\n entry.totalSignalCount = (entry.totalSignalCount ?? 0) + increment;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"short-term-store.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/short-term-store.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash, randomUUID } from 'node:crypto';\n\nimport type { MemorySearchOptions } from '../../prompt/memory/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from './constants.js';\nimport { buildEntryKey, clamp01, isoDay, normalizeMemoryPath } from './utils.js';\n\nconst log = createLogger('Dreaming:Store');\n\nexport type DreamingStoreEntry = {\n key: string;\n path: string;\n startLine: number;\n endLine: number;\n snippet: string;\n\n // ── Signal dimensions ────────────────────────────────────────────────\n /** Number of times this snippet was returned by a memory recall query. */\n recallCount: number;\n /** Number of times recorded from source-side sweep phases. */\n sourceCount: number;\n /** Number of times replayed from grounded context (agent-initiated). */\n groundedCount: number;\n /** Number of times the light phase touched this entry. */\n lightHits: number;\n /** Number of times the REM phase touched this entry. */\n remHits: number;\n /** Cross-phase hit count (light + deep + rem combined touches). */\n phaseHitCount: number;\n /** Weighted aggregate of all signal dimensions. */\n totalSignalCount: number;\n\n // ── Score tracking ───────────────────────────────────────────────────\n totalScore: number;\n maxScore: number;\n queryHashes: string[];\n recallDays: string[];\n\n // ── Timestamps ───────────────────────────────────────────────────────\n firstRecalledAt: string;\n lastRecalledAt: string;\n promotedAt?: string;\n};\n\nexport type DreamingStore = {\n version: 1;\n updatedAt: string;\n entries: Record<string, DreamingStoreEntry>;\n};\n\ntype MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];\n\nfunction isRecordableMemoryPath(rel: string): boolean {\n const p = normalizeMemoryPath(rel);\n return p.length > 0 && !p.startsWith('../') && !path.isAbsolute(p);\n}\n\nfunction hashQuery(query: string): string {\n return createHash('sha1').update(query.trim().toLowerCase()).digest('hex').slice(0, 12);\n}\n\nfunction mergeRecentDistinct(existing: string[], nextValue: string, limit: number): string[] {\n const seen = new Set<string>();\n const normalized = existing\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.trim())\n .filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));\n if (nextValue && !normalized.includes(nextValue)) {\n normalized.push(nextValue);\n }\n return normalized.length <= limit ? normalized : normalized.slice(-limit);\n}\n\nfunction mergeQueryHashes(existing: string[], nextHash: string): string[] {\n const out = mergeRecentDistinct(existing, nextHash, 32);\n return out;\n}\n\nfunction emptyStore(nowIso: string): DreamingStore {\n return { version: 1, updatedAt: nowIso, entries: {} };\n}\n\nasync function ensureDreamDir(dreamingRoot: string): Promise<string> {\n const dir = path.join(dreamingRoot, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\nasync function readStore(dreamingRoot: string, nowIso: string): Promise<DreamingStore> {\n const storePath = path.join(dreamingRoot, SHORT_TERM_RECALL_STORE_RELATIVE);\n try {\n const raw = await fs.readFile(storePath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return emptyStore(nowIso);\n const rec = parsed as Partial<DreamingStore>;\n if (rec.version !== 1 || !rec.entries || typeof rec.entries !== 'object') {\n return emptyStore(nowIso);\n }\n return {\n version: 1,\n updatedAt: typeof rec.updatedAt === 'string' ? rec.updatedAt : nowIso,\n entries: rec.entries as Record<string, DreamingStoreEntry>,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return emptyStore(nowIso);\n log.warn({ err, dreamingRoot }, 'Failed to read dreaming store; resetting');\n return emptyStore(nowIso);\n }\n}\n\nasync function writeStore(dreamingRoot: string, store: DreamingStore): Promise<void> {\n await ensureDreamDir(dreamingRoot);\n const storePath = path.join(dreamingRoot, SHORT_TERM_RECALL_STORE_RELATIVE);\n const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, storePath);\n}\n\nexport async function recordDreamingRecalls(params: {\n dreamingRoot: string;\n query: string;\n matches: MemoryMatch[];\n options?: MemorySearchOptions;\n now?: Date;\n}): Promise<{ recorded: number; skipped: number; storePath: string }> {\n const dreamingRoot = params.dreamingRoot.trim();\n const query = params.query.trim();\n if (!dreamingRoot || !query) {\n return { recorded: 0, skipped: params.matches.length, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n }\n const now = params.now ?? new Date();\n const nowIso = now.toISOString();\n const dayBucket = isoDay(now);\n const qHash = hashQuery(query);\n\n const store = await readStore(dreamingRoot, nowIso);\n\n let recorded = 0;\n let skipped = 0;\n for (const match of params.matches) {\n const file = typeof match?.file === \"string\" ? match.file : \"\";\n if (!file || !isRecordableMemoryPath(file)) {\n skipped += 1;\n continue;\n }\n const lines = typeof match?.lines === \"string\" ? match.lines.trim() : \"\";\n const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];\n const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {\n skipped += 1;\n continue;\n }\n const score = clamp01(Number(match.score));\n const key = buildEntryKey({ path: file, startLine, endLine });\n const existing = store.entries[key];\n const snippet = lines.length > 0 ? lines.slice(0, 360) : file;\n\n const next: DreamingStoreEntry = existing\n ? {\n ...existing,\n snippet,\n recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),\n sourceCount: existing.sourceCount ?? 0,\n groundedCount: existing.groundedCount ?? 0,\n lightHits: existing.lightHits ?? 0,\n remHits: existing.remHits ?? 0,\n phaseHitCount: existing.phaseHitCount ?? 0,\n totalSignalCount: Math.max(0, (existing.totalSignalCount ?? existing.recallCount ?? 0) + 1),\n totalScore: Math.max(0, existing.totalScore + score),\n maxScore: Math.max(existing.maxScore, score),\n queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),\n recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),\n lastRecalledAt: nowIso,\n }\n : {\n key,\n path: normalizeMemoryPath(file),\n startLine,\n endLine,\n snippet,\n recallCount: 1,\n sourceCount: 0,\n groundedCount: 0,\n lightHits: 0,\n remHits: 0,\n phaseHitCount: 0,\n totalSignalCount: 1,\n totalScore: score,\n maxScore: score,\n queryHashes: [qHash],\n recallDays: [dayBucket],\n firstRecalledAt: nowIso,\n lastRecalledAt: nowIso,\n };\n\n store.entries[key] = next;\n recorded += 1;\n }\n\n if (recorded > 0) {\n store.updatedAt = nowIso;\n await writeStore(dreamingRoot, store);\n }\n\n return {\n recorded,\n skipped,\n storePath: SHORT_TERM_RECALL_STORE_RELATIVE,\n };\n}\n\nexport async function withDreamingPromotionLock<T>(\n dreamingRoot: string,\n task: () => Promise<T>,\n): Promise<T> {\n const lockPath = path.join(dreamingRoot, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n await ensureDreamDir(dreamingRoot);\n\n const startedAt = Date.now();\n const timeoutMs = 10_000;\n const retryDelayMs = 50;\n\n while (true) {\n try {\n const handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}\\n`, 'utf-8').catch(() => undefined);\n try {\n return await task();\n } finally {\n await handle.close().catch(() => undefined);\n await fs.unlink(lockPath).catch(() => undefined);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);\n }\n await new Promise((r) => setTimeout(r, retryDelayMs));\n }\n }\n}\n\nexport async function loadDreamingStore(params: {\n dreamingRoot: string;\n}): Promise<{ store: DreamingStore; storePath: string }> {\n const nowIso = new Date().toISOString();\n const store = await readStore(params.dreamingRoot, nowIso);\n return { store, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n}\n\nexport async function saveDreamingStore(params: {\n dreamingRoot: string;\n store: DreamingStore;\n}): Promise<void> {\n await writeStore(params.dreamingRoot, params.store);\n}\n\n// ── Phase-level signal helpers ─────────────────────────────────────────\n\ntype PhaseSignalField = 'sourceCount' | 'groundedCount' | 'lightHits' | 'remHits';\n\n/**\n * Increment a phase-specific signal counter on an existing store entry.\n * Also bumps `phaseHitCount` and `totalSignalCount`.\n * Returns `true` if the entry existed and was updated.\n */\nexport function bumpEntryPhaseSignal(\n entry: DreamingStoreEntry,\n field: PhaseSignalField,\n increment = 1,\n): void {\n entry[field] = (entry[field] ?? 0) + increment;\n entry.phaseHitCount = (entry.phaseHitCount ?? 0) + increment;\n entry.totalSignalCount = (entry.totalSignalCount ?? 0) + increment;\n}\n"],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@ import type { SelfVerifyMiddleware } from '../middleware/index.js';
|
|
|
23
23
|
import type { SystemReminder } from '../prompt/system-reminder.js';
|
|
24
24
|
import type { ToolUsageAnalyzer } from '../tools/usage-analyzer.js';
|
|
25
25
|
import type { ErrorPatternMatcher } from '../tools/error-pattern-matcher.js';
|
|
26
|
+
import type { TurnDiffTracker } from '../coding/index.js';
|
|
26
27
|
export type SessionEventListener = (event: AgentEvent, context: SessionContext) => void;
|
|
27
28
|
export type SessionEventTypeFilter = AgentEvent['type'] | 'all';
|
|
28
29
|
/**
|
|
@@ -45,6 +46,7 @@ export interface AgentEventHandlerConfig {
|
|
|
45
46
|
systemReminder: SystemReminder;
|
|
46
47
|
toolUsageAnalyzer: ToolUsageAnalyzer;
|
|
47
48
|
errorPatternMatcher: ErrorPatternMatcher;
|
|
49
|
+
turnDiffTracker?: TurnDiffTracker;
|
|
48
50
|
}
|
|
49
51
|
/**
|
|
50
52
|
* Thin façade over {@link SessionEventBus} that pre-registers all built-in
|
|
@@ -294,6 +294,18 @@ function installSelfVerifyListener(bus, selfVerifyMiddleware) {
|
|
|
294
294
|
selfVerifyMiddleware.onTurnStart(context.sessionKey);
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
+
function installTurnDiffTrackerListener(bus, turnDiffTracker) {
|
|
298
|
+
bus.on("tool_execution_end", (event, context) => {
|
|
299
|
+
const e = event;
|
|
300
|
+
turnDiffTracker.recordToolResult({
|
|
301
|
+
sessionKey: context.sessionKey,
|
|
302
|
+
toolName: e.toolName,
|
|
303
|
+
args: e.args,
|
|
304
|
+
result: e.result,
|
|
305
|
+
isError: e.isError
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
297
309
|
/**
|
|
298
310
|
* Thin façade over {@link SessionEventBus} that pre-registers all built-in
|
|
299
311
|
* listeners in their required order. `handle(event, ctx)` is the entry point
|
|
@@ -312,6 +324,7 @@ var AgentEventHandler = class {
|
|
|
312
324
|
});
|
|
313
325
|
installLifecycleHookListener(this.bus, config.lifecycleManager);
|
|
314
326
|
installSystemReminderListener(this.bus, config.systemReminder);
|
|
327
|
+
if (config.turnDiffTracker) installTurnDiffTrackerListener(this.bus, config.turnDiffTracker);
|
|
315
328
|
installSelfVerifyListener(this.bus, config.selfVerifyMiddleware);
|
|
316
329
|
installToolUsageListener(this.bus, config.toolUsageAnalyzer);
|
|
317
330
|
installToolChainListener(this.bus, config.toolChainTracker);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-event-handler.js","names":[],"sources":["../../../../src/agent/orchestration/agent-event-handler.ts"],"sourcesContent":["/**\n * Agent Event Handler — coordinates listeners on pi-agent events.\n *\n * Previously a single god-handler with ten manager fields and one `switch`. Now a\n * thin façade around {@link SessionEventBus}: each concern (progress, lifecycle\n * hooks, tool-chain recording, error tracking, self-verify, etc.) registers its\n * own listener at construction time, and external code can add new listeners via\n * {@link AgentEventHandler.registerListener} without modifying this file (OCP).\n *\n * Ordering note: a few listeners are order-sensitive (notably the\n * `tool_execution_end` chain — `SystemReminder` mutates `event.result` in place\n * and downstream listeners read the mutated value). The `installX` calls below\n * preserve the exact order from the previous implementation.\n */\n\nimport type { AgentEvent } from '@earendil-works/pi-agent-core';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { ProgressFeedbackManager } from '../lifecycle/progress.js';\nimport type { ToolErrorTracker } from '../tools/error-tracker.js';\nimport type { RequestLimiter } from '../models/request-limiter.js';\nimport type { LifecycleManager } from '../lifecycle/index.js';\nimport type { ToolChainTracker } from '../tools/chain-tracker.js';\nimport type { SelfVerifyMiddleware } from '../middleware/index.js';\nimport type { SystemReminder } from '../prompt/system-reminder.js';\nimport type { ToolUsageAnalyzer } from '../tools/usage-analyzer.js';\nimport type { ErrorPatternMatcher } from '../tools/error-pattern-matcher.js';\nimport { extractTextContent } from '../context/workspace.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('AgentEventHandler');\n\n// ── Event bus ──────────────────────────────────────────────────────────────\n\nexport type SessionEventListener = (event: AgentEvent, context: SessionContext) => void;\nexport type SessionEventTypeFilter = AgentEvent['type'] | 'all';\n\n/**\n * Typed pub/sub for agent events. Listeners run in registration order; the bus\n * itself is synchronous — listeners that need async work must dispatch their own\n * promises (typically by awaiting and logging on error, like the lifecycle hooks).\n */\nexport class SessionEventBus {\n private readonly listeners = new Map<SessionEventTypeFilter, SessionEventListener[]>();\n\n on(type: SessionEventTypeFilter, listener: SessionEventListener): () => void {\n const bucket = this.listeners.get(type) ?? [];\n bucket.push(listener);\n this.listeners.set(type, bucket);\n return () => {\n const current = this.listeners.get(type);\n if (!current) return;\n const idx = current.indexOf(listener);\n if (idx >= 0) current.splice(idx, 1);\n };\n }\n\n dispatch(event: AgentEvent, context: SessionContext | null): void {\n if (!context) {\n if (event.type !== 'message_update') {\n log.warn(\n { eventType: event.type },\n `Agent event ignored (no SessionContext): ${event.type}`,\n );\n }\n return;\n }\n const exact = this.listeners.get(event.type);\n if (exact) {\n for (const listener of exact) {\n listener(event, context);\n }\n }\n const all = this.listeners.get('all');\n if (all) {\n for (const listener of all) {\n listener(event, context);\n }\n }\n }\n}\n\n// ── Built-in listener installers ───────────────────────────────────────────\n\nfunction installProgressListener(bus: SessionEventBus, progressManager: ProgressFeedbackManager): void {\n bus.on('agent_start', () => {\n log.debug('Agent turn started');\n progressManager.startTask();\n });\n bus.on('turn_start', () => {\n log.debug('Turn started');\n progressManager.onTurnStart();\n });\n bus.on('tool_execution_start', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n log.debug({ tool: e.toolName, args: e.args }, 'Tool execution started');\n progressManager.onToolStart(e.toolName, e.args || {});\n });\n bus.on('tool_execution_update', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_update' }>;\n progressManager.onToolUpdate(e.toolName, e.partialResult);\n });\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n log.debug({ tool: e.toolName, isError: e.isError }, 'Tool execution complete');\n progressManager.onToolEnd(e.toolName, e.result, e.isError);\n });\n bus.on('agent_end', () => {\n progressManager.endTask();\n });\n}\n\nfunction installRequestLimitListener(\n bus: SessionEventBus,\n deps: {\n requestLimiter: RequestLimiter;\n progressManager: ProgressFeedbackManager;\n lifecycleManager: LifecycleManager;\n },\n): void {\n bus.on('agent_start', (_event, context) => {\n const result = deps.requestLimiter.recordRequest();\n\n deps.lifecycleManager\n .emit(\n 'llm_request',\n context.sessionKey,\n { requestNumber: result.count, maxRequests: result.limit },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n {\n err,\n errorMessage: em,\n sessionKey: context.sessionKey,\n requestNumber: result.count,\n maxRequests: result.limit,\n },\n `Lifecycle emit llm_request failed: ${em}`,\n );\n });\n\n deps.progressManager.onRequestLimitStatus(\n result.count,\n result.limit,\n result.remaining,\n result.isWarning,\n result.shouldStop,\n );\n\n if (result.shouldStop) {\n log.error(\n { count: result.count, limit: result.limit, sessionKey: context.sessionKey },\n `Request limit reached (${result.count}/${result.limit}) for session`,\n );\n }\n });\n\n bus.on('turn_end', () => {\n deps.requestLimiter.reset();\n });\n}\n\nfunction installLifecycleHookListener(bus: SessionEventBus, lifecycleManager: LifecycleManager): void {\n bus.on('message_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'message_end' }>;\n if (e.message?.role !== 'assistant') return;\n const content = e.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n log.debug({ contentLength: text.length }, 'Assistant response complete');\n\n lifecycleManager\n .emit(\n 'llm_response',\n context.sessionKey,\n { response: text, usage: (e.message as { usage?: unknown }).usage },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, responseChars: text.length },\n `Lifecycle emit llm_response failed: ${em}`,\n );\n });\n });\n\n bus.on('tool_execution_start', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n lifecycleManager\n .emit(\n 'tool_call_start',\n context.sessionKey,\n {\n toolName: e.toolName,\n arguments: e.args || {},\n attemptNumber: 1,\n maxAttempts: 3,\n },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, tool: e.toolName },\n `Lifecycle emit tool_call_start failed: ${em}`,\n );\n });\n });\n\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n lifecycleManager\n .emit(\n 'tool_call_end',\n context.sessionKey,\n {\n toolName: e.toolName,\n success: !e.isError,\n result: e.result,\n error: e.isError ? String(e.result) : undefined,\n durationMs,\n },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, tool: e.toolName },\n `Lifecycle hook tool_call_end failed: ${em}`,\n );\n });\n });\n}\n\nfunction installSystemReminderListener(bus: SessionEventBus, systemReminder: SystemReminder): void {\n // MUTATES `event.result` so downstream listeners (tool-chain, error-tracking) see\n // the decorated text. Must run AFTER progress + lifecycle hooks (they consume the\n // original) and BEFORE tool-chain + error-tracking.\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }> & { result: unknown };\n e.result = systemReminder.appendToResult(e.result, e.toolName);\n });\n}\n\nfunction appendTextToToolResult(result: unknown, text: string): unknown {\n if (!text.trim()) {\n return result;\n }\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (Array.isArray(res.content)) {\n res.content = [\n ...res.content,\n { type: 'text', text: `\\n${text}` },\n ];\n return res;\n }\n if (typeof res.content === 'string') {\n res.content = `${res.content}\\n${text}`;\n return res;\n }\n }\n return result;\n}\n\nfunction readRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : null;\n}\n\nfunction readToolResultDetails(result: unknown): Record<string, unknown> | null {\n const rec = readRecord(result);\n if (!rec) return null;\n return readRecord(rec.details);\n}\n\nfunction readStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n}\n\nfunction readChangedPathsFromToolEnd(toolName: string, args: unknown, result: unknown): string[] {\n const name = toolName.toLowerCase();\n const details = readToolResultDetails(result);\n\n if (name === 'apply_patch') {\n const files = readStringArray(details?.files);\n if (files.length > 0) return files;\n\n const changes = Array.isArray(details?.changes) ? details.changes : [];\n return changes.flatMap((change) => {\n const rec = readRecord(change);\n if (!rec) return [];\n return readStringArray([rec.moveTo, rec.path]);\n });\n }\n\n if (name === 'write_file') {\n if (typeof details?.size !== 'number') return [];\n const rec = readRecord(args);\n return typeof rec?.path === 'string' ? [rec.path] : [];\n }\n\n if (name.includes('write') || name.includes('edit')) {\n const rec = readRecord(args);\n return typeof rec?.path === 'string' ? [rec.path] : [];\n }\n\n return [];\n}\n\nfunction installToolChainListener(bus: SessionEventBus, toolChainTracker: ToolChainTracker): void {\n bus.on('turn_start', (_event, context) => {\n // One chain per LLM turn (pi-agent emits turn_start each round; turn_end clears the chain).\n toolChainTracker.startChain(context.sessionKey);\n });\n bus.on('tool_execution_start', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n toolChainTracker.recordCall(context.sessionKey, e.toolName, e.args || {}, 0);\n });\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n const chain = toolChainTracker.getCurrentChain(context.sessionKey);\n if (!chain) return;\n const lastNode = chain.nodes[chain.nodes.length - 1];\n if (lastNode && lastNode.toolName === e.toolName) {\n toolChainTracker.recordResult(\n context.sessionKey,\n lastNode.id,\n e.result,\n e.isError ? 'Tool execution failed' : undefined,\n durationMs,\n );\n }\n });\n bus.on('turn_end', (_event, context) => {\n toolChainTracker.endChain(context.sessionKey);\n });\n}\n\nfunction installToolUsageListener(bus: SessionEventBus, toolUsageAnalyzer: ToolUsageAnalyzer): void {\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n toolUsageAnalyzer.recordUsage(e.toolName, !e.isError, durationMs);\n });\n}\n\nfunction extractErrorText(result: unknown): string {\n if (typeof result === 'string') return result;\n if (result && typeof result === 'object') {\n const obj = result as Record<string, unknown>;\n if (typeof obj.error === 'string') return obj.error;\n if (typeof obj.message === 'string') return obj.message;\n return JSON.stringify(result);\n }\n return String(result);\n}\n\nfunction installErrorTrackingListener(\n bus: SessionEventBus,\n deps: { errorTracker: ToolErrorTracker; errorPatternMatcher: ErrorPatternMatcher },\n): void {\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n if (!e.isError) return;\n const errorText = extractErrorText(e.result);\n deps.errorTracker.recordFailure(e.toolName, errorText);\n\n const errorMatch = deps.errorPatternMatcher.matchError(errorText);\n if (errorMatch.matched && errorMatch.pattern) {\n const preview = errorText.length > 120 ? `${errorText.slice(0, 120)}…` : errorText;\n log.warn(\n { tool: e.toolName, pattern: errorMatch.pattern.name, errorPreview: preview },\n `Tool error matched pattern \"${errorMatch.pattern.name}\" (${e.toolName}): ${preview}`,\n );\n }\n });\n bus.on('turn_end', () => {\n deps.errorTracker.reset();\n });\n}\n\nfunction installSelfVerifyListener(bus: SessionEventBus, selfVerifyMiddleware: SelfVerifyMiddleware): void {\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }> & { result: unknown };\n const name = e.toolName?.toLowerCase() ?? '';\n const args = (e as { args?: unknown }).args;\n\n if (name === 'exec_command') {\n selfVerifyMiddleware.recordVerification(e.toolName, args, {\n isError: e.isError,\n result: e.result,\n }, context.sessionKey);\n return;\n }\n\n if (!e.isError) {\n const changedPaths = readChangedPathsFromToolEnd(e.toolName, args, e.result);\n for (const path of changedPaths) {\n selfVerifyMiddleware.recordEdit(\n path,\n name.includes('write') ? 'write' : 'edit',\n context.sessionKey,\n );\n }\n if (changedPaths.length > 0) {\n e.result = appendTextToToolResult(\n e.result,\n selfVerifyMiddleware.consumePostEditReminder(context.sessionKey),\n );\n return;\n }\n selfVerifyMiddleware.recordVerification(e.toolName, args, {\n isError: e.isError,\n result: e.result,\n }, context.sessionKey);\n }\n });\n bus.on('turn_start', (_event, context) => {\n selfVerifyMiddleware.onTurnStart(context.sessionKey);\n });\n}\n\n// ── Public façade ───────────────────────────────────────────────────────────\n\nexport interface AgentEventHandlerConfig {\n progressManager: ProgressFeedbackManager;\n errorTracker: ToolErrorTracker;\n requestLimiter: RequestLimiter;\n lifecycleManager: LifecycleManager;\n toolChainTracker: ToolChainTracker;\n selfVerifyMiddleware: SelfVerifyMiddleware;\n systemReminder: SystemReminder;\n toolUsageAnalyzer: ToolUsageAnalyzer;\n errorPatternMatcher: ErrorPatternMatcher;\n}\n\n/**\n * Thin façade over {@link SessionEventBus} that pre-registers all built-in\n * listeners in their required order. `handle(event, ctx)` is the entry point\n * used by `AgentService.handleSessionEvent`; extensions can hook in via\n * {@link registerListener} without touching this class.\n */\nexport class AgentEventHandler {\n private readonly bus: SessionEventBus;\n\n constructor(config: AgentEventHandlerConfig) {\n this.bus = new SessionEventBus();\n\n // Order matters for `tool_execution_end`: SystemReminder mutates `event.result`\n // and ToolChain + ErrorTracking depend on the mutated value. Keep these in this\n // exact sequence.\n installProgressListener(this.bus, config.progressManager);\n installRequestLimitListener(this.bus, {\n requestLimiter: config.requestLimiter,\n progressManager: config.progressManager,\n lifecycleManager: config.lifecycleManager,\n });\n installLifecycleHookListener(this.bus, config.lifecycleManager);\n installSystemReminderListener(this.bus, config.systemReminder);\n installSelfVerifyListener(this.bus, config.selfVerifyMiddleware);\n installToolUsageListener(this.bus, config.toolUsageAnalyzer);\n installToolChainListener(this.bus, config.toolChainTracker);\n installErrorTrackingListener(this.bus, {\n errorTracker: config.errorTracker,\n errorPatternMatcher: config.errorPatternMatcher,\n });\n }\n\n /** Dispatch a pi-agent event to all registered listeners. */\n handle(event: AgentEvent, context: SessionContext | null): void {\n this.bus.dispatch(event, context);\n }\n\n /**\n * Register an additional listener (e.g. from an extension). Returns an\n * unsubscribe function. Listeners run after the built-ins in registration\n * order; if you need to run before a built-in, you must create your own\n * {@link SessionEventBus} instance.\n */\n registerListener(type: SessionEventTypeFilter, listener: SessionEventListener): () => void {\n return this.bus.on(type, listener);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-event-handler.js","names":[],"sources":["../../../../src/agent/orchestration/agent-event-handler.ts"],"sourcesContent":["/**\n * Agent Event Handler — coordinates listeners on pi-agent events.\n *\n * Previously a single god-handler with ten manager fields and one `switch`. Now a\n * thin façade around {@link SessionEventBus}: each concern (progress, lifecycle\n * hooks, tool-chain recording, error tracking, self-verify, etc.) registers its\n * own listener at construction time, and external code can add new listeners via\n * {@link AgentEventHandler.registerListener} without modifying this file (OCP).\n *\n * Ordering note: a few listeners are order-sensitive (notably the\n * `tool_execution_end` chain — `SystemReminder` mutates `event.result` in place\n * and downstream listeners read the mutated value). The `installX` calls below\n * preserve the exact order from the previous implementation.\n */\n\nimport type { AgentEvent } from '@earendil-works/pi-agent-core';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { ProgressFeedbackManager } from '../lifecycle/progress.js';\nimport type { ToolErrorTracker } from '../tools/error-tracker.js';\nimport type { RequestLimiter } from '../models/request-limiter.js';\nimport type { LifecycleManager } from '../lifecycle/index.js';\nimport type { ToolChainTracker } from '../tools/chain-tracker.js';\nimport type { SelfVerifyMiddleware } from '../middleware/index.js';\nimport type { SystemReminder } from '../prompt/system-reminder.js';\nimport type { ToolUsageAnalyzer } from '../tools/usage-analyzer.js';\nimport type { ErrorPatternMatcher } from '../tools/error-pattern-matcher.js';\nimport type { TurnDiffTracker } from '../coding/index.js';\nimport { extractTextContent } from '../context/workspace.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('AgentEventHandler');\n\n// ── Event bus ──────────────────────────────────────────────────────────────\n\nexport type SessionEventListener = (event: AgentEvent, context: SessionContext) => void;\nexport type SessionEventTypeFilter = AgentEvent['type'] | 'all';\n\n/**\n * Typed pub/sub for agent events. Listeners run in registration order; the bus\n * itself is synchronous — listeners that need async work must dispatch their own\n * promises (typically by awaiting and logging on error, like the lifecycle hooks).\n */\nexport class SessionEventBus {\n private readonly listeners = new Map<SessionEventTypeFilter, SessionEventListener[]>();\n\n on(type: SessionEventTypeFilter, listener: SessionEventListener): () => void {\n const bucket = this.listeners.get(type) ?? [];\n bucket.push(listener);\n this.listeners.set(type, bucket);\n return () => {\n const current = this.listeners.get(type);\n if (!current) return;\n const idx = current.indexOf(listener);\n if (idx >= 0) current.splice(idx, 1);\n };\n }\n\n dispatch(event: AgentEvent, context: SessionContext | null): void {\n if (!context) {\n if (event.type !== 'message_update') {\n log.warn(\n { eventType: event.type },\n `Agent event ignored (no SessionContext): ${event.type}`,\n );\n }\n return;\n }\n const exact = this.listeners.get(event.type);\n if (exact) {\n for (const listener of exact) {\n listener(event, context);\n }\n }\n const all = this.listeners.get('all');\n if (all) {\n for (const listener of all) {\n listener(event, context);\n }\n }\n }\n}\n\n// ── Built-in listener installers ───────────────────────────────────────────\n\nfunction installProgressListener(bus: SessionEventBus, progressManager: ProgressFeedbackManager): void {\n bus.on('agent_start', () => {\n log.debug('Agent turn started');\n progressManager.startTask();\n });\n bus.on('turn_start', () => {\n log.debug('Turn started');\n progressManager.onTurnStart();\n });\n bus.on('tool_execution_start', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n log.debug({ tool: e.toolName, args: e.args }, 'Tool execution started');\n progressManager.onToolStart(e.toolName, e.args || {});\n });\n bus.on('tool_execution_update', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_update' }>;\n progressManager.onToolUpdate(e.toolName, e.partialResult);\n });\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n log.debug({ tool: e.toolName, isError: e.isError }, 'Tool execution complete');\n progressManager.onToolEnd(e.toolName, e.result, e.isError);\n });\n bus.on('agent_end', () => {\n progressManager.endTask();\n });\n}\n\nfunction installRequestLimitListener(\n bus: SessionEventBus,\n deps: {\n requestLimiter: RequestLimiter;\n progressManager: ProgressFeedbackManager;\n lifecycleManager: LifecycleManager;\n },\n): void {\n bus.on('agent_start', (_event, context) => {\n const result = deps.requestLimiter.recordRequest();\n\n deps.lifecycleManager\n .emit(\n 'llm_request',\n context.sessionKey,\n { requestNumber: result.count, maxRequests: result.limit },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n {\n err,\n errorMessage: em,\n sessionKey: context.sessionKey,\n requestNumber: result.count,\n maxRequests: result.limit,\n },\n `Lifecycle emit llm_request failed: ${em}`,\n );\n });\n\n deps.progressManager.onRequestLimitStatus(\n result.count,\n result.limit,\n result.remaining,\n result.isWarning,\n result.shouldStop,\n );\n\n if (result.shouldStop) {\n log.error(\n { count: result.count, limit: result.limit, sessionKey: context.sessionKey },\n `Request limit reached (${result.count}/${result.limit}) for session`,\n );\n }\n });\n\n bus.on('turn_end', () => {\n deps.requestLimiter.reset();\n });\n}\n\nfunction installLifecycleHookListener(bus: SessionEventBus, lifecycleManager: LifecycleManager): void {\n bus.on('message_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'message_end' }>;\n if (e.message?.role !== 'assistant') return;\n const content = e.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n log.debug({ contentLength: text.length }, 'Assistant response complete');\n\n lifecycleManager\n .emit(\n 'llm_response',\n context.sessionKey,\n { response: text, usage: (e.message as { usage?: unknown }).usage },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, responseChars: text.length },\n `Lifecycle emit llm_response failed: ${em}`,\n );\n });\n });\n\n bus.on('tool_execution_start', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n lifecycleManager\n .emit(\n 'tool_call_start',\n context.sessionKey,\n {\n toolName: e.toolName,\n arguments: e.args || {},\n attemptNumber: 1,\n maxAttempts: 3,\n },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, tool: e.toolName },\n `Lifecycle emit tool_call_start failed: ${em}`,\n );\n });\n });\n\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n lifecycleManager\n .emit(\n 'tool_call_end',\n context.sessionKey,\n {\n toolName: e.toolName,\n success: !e.isError,\n result: e.result,\n error: e.isError ? String(e.result) : undefined,\n durationMs,\n },\n context,\n )\n .catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn(\n { err, errorMessage: em, sessionKey: context.sessionKey, tool: e.toolName },\n `Lifecycle hook tool_call_end failed: ${em}`,\n );\n });\n });\n}\n\nfunction installSystemReminderListener(bus: SessionEventBus, systemReminder: SystemReminder): void {\n // MUTATES `event.result` so downstream listeners (tool-chain, error-tracking) see\n // the decorated text. Must run AFTER progress + lifecycle hooks (they consume the\n // original) and BEFORE tool-chain + error-tracking.\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }> & { result: unknown };\n e.result = systemReminder.appendToResult(e.result, e.toolName);\n });\n}\n\nfunction appendTextToToolResult(result: unknown, text: string): unknown {\n if (!text.trim()) {\n return result;\n }\n if (result && typeof result === 'object') {\n const res = result as Record<string, unknown>;\n if (Array.isArray(res.content)) {\n res.content = [\n ...res.content,\n { type: 'text', text: `\\n${text}` },\n ];\n return res;\n }\n if (typeof res.content === 'string') {\n res.content = `${res.content}\\n${text}`;\n return res;\n }\n }\n return result;\n}\n\nfunction readRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : null;\n}\n\nfunction readToolResultDetails(result: unknown): Record<string, unknown> | null {\n const rec = readRecord(result);\n if (!rec) return null;\n return readRecord(rec.details);\n}\n\nfunction readStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n}\n\nfunction readChangedPathsFromToolEnd(toolName: string, args: unknown, result: unknown): string[] {\n const name = toolName.toLowerCase();\n const details = readToolResultDetails(result);\n\n if (name === 'apply_patch') {\n const files = readStringArray(details?.files);\n if (files.length > 0) return files;\n\n const changes = Array.isArray(details?.changes) ? details.changes : [];\n return changes.flatMap((change) => {\n const rec = readRecord(change);\n if (!rec) return [];\n return readStringArray([rec.moveTo, rec.path]);\n });\n }\n\n if (name === 'write_file') {\n if (typeof details?.size !== 'number') return [];\n const rec = readRecord(args);\n return typeof rec?.path === 'string' ? [rec.path] : [];\n }\n\n if (name.includes('write') || name.includes('edit')) {\n const rec = readRecord(args);\n return typeof rec?.path === 'string' ? [rec.path] : [];\n }\n\n return [];\n}\n\nfunction installToolChainListener(bus: SessionEventBus, toolChainTracker: ToolChainTracker): void {\n bus.on('turn_start', (_event, context) => {\n // One chain per LLM turn (pi-agent emits turn_start each round; turn_end clears the chain).\n toolChainTracker.startChain(context.sessionKey);\n });\n bus.on('tool_execution_start', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_start' }>;\n toolChainTracker.recordCall(context.sessionKey, e.toolName, e.args || {}, 0);\n });\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n const chain = toolChainTracker.getCurrentChain(context.sessionKey);\n if (!chain) return;\n const lastNode = chain.nodes[chain.nodes.length - 1];\n if (lastNode && lastNode.toolName === e.toolName) {\n toolChainTracker.recordResult(\n context.sessionKey,\n lastNode.id,\n e.result,\n e.isError ? 'Tool execution failed' : undefined,\n durationMs,\n );\n }\n });\n bus.on('turn_end', (_event, context) => {\n toolChainTracker.endChain(context.sessionKey);\n });\n}\n\nfunction installToolUsageListener(bus: SessionEventBus, toolUsageAnalyzer: ToolUsageAnalyzer): void {\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n const durationMs = (e as { durationMs?: number }).durationMs || 0;\n toolUsageAnalyzer.recordUsage(e.toolName, !e.isError, durationMs);\n });\n}\n\nfunction extractErrorText(result: unknown): string {\n if (typeof result === 'string') return result;\n if (result && typeof result === 'object') {\n const obj = result as Record<string, unknown>;\n if (typeof obj.error === 'string') return obj.error;\n if (typeof obj.message === 'string') return obj.message;\n return JSON.stringify(result);\n }\n return String(result);\n}\n\nfunction installErrorTrackingListener(\n bus: SessionEventBus,\n deps: { errorTracker: ToolErrorTracker; errorPatternMatcher: ErrorPatternMatcher },\n): void {\n bus.on('tool_execution_end', (event) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }>;\n if (!e.isError) return;\n const errorText = extractErrorText(e.result);\n deps.errorTracker.recordFailure(e.toolName, errorText);\n\n const errorMatch = deps.errorPatternMatcher.matchError(errorText);\n if (errorMatch.matched && errorMatch.pattern) {\n const preview = errorText.length > 120 ? `${errorText.slice(0, 120)}…` : errorText;\n log.warn(\n { tool: e.toolName, pattern: errorMatch.pattern.name, errorPreview: preview },\n `Tool error matched pattern \"${errorMatch.pattern.name}\" (${e.toolName}): ${preview}`,\n );\n }\n });\n bus.on('turn_end', () => {\n deps.errorTracker.reset();\n });\n}\n\nfunction installSelfVerifyListener(bus: SessionEventBus, selfVerifyMiddleware: SelfVerifyMiddleware): void {\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }> & { result: unknown };\n const name = e.toolName?.toLowerCase() ?? '';\n const args = (e as { args?: unknown }).args;\n\n if (name === 'exec_command') {\n selfVerifyMiddleware.recordVerification(e.toolName, args, {\n isError: e.isError,\n result: e.result,\n }, context.sessionKey);\n return;\n }\n\n if (!e.isError) {\n const changedPaths = readChangedPathsFromToolEnd(e.toolName, args, e.result);\n for (const path of changedPaths) {\n selfVerifyMiddleware.recordEdit(\n path,\n name.includes('write') ? 'write' : 'edit',\n context.sessionKey,\n );\n }\n if (changedPaths.length > 0) {\n e.result = appendTextToToolResult(\n e.result,\n selfVerifyMiddleware.consumePostEditReminder(context.sessionKey),\n );\n return;\n }\n selfVerifyMiddleware.recordVerification(e.toolName, args, {\n isError: e.isError,\n result: e.result,\n }, context.sessionKey);\n }\n });\n bus.on('turn_start', (_event, context) => {\n selfVerifyMiddleware.onTurnStart(context.sessionKey);\n });\n}\n\nfunction installTurnDiffTrackerListener(bus: SessionEventBus, turnDiffTracker: TurnDiffTracker): void {\n bus.on('tool_execution_end', (event, context) => {\n const e = event as Extract<AgentEvent, { type: 'tool_execution_end' }> & {\n args?: unknown;\n result: unknown;\n };\n turnDiffTracker.recordToolResult({\n sessionKey: context.sessionKey,\n toolName: e.toolName,\n args: e.args,\n result: e.result,\n isError: e.isError,\n });\n });\n}\n\n// ── Public façade ───────────────────────────────────────────────────────────\n\nexport interface AgentEventHandlerConfig {\n progressManager: ProgressFeedbackManager;\n errorTracker: ToolErrorTracker;\n requestLimiter: RequestLimiter;\n lifecycleManager: LifecycleManager;\n toolChainTracker: ToolChainTracker;\n selfVerifyMiddleware: SelfVerifyMiddleware;\n systemReminder: SystemReminder;\n toolUsageAnalyzer: ToolUsageAnalyzer;\n errorPatternMatcher: ErrorPatternMatcher;\n turnDiffTracker?: TurnDiffTracker;\n}\n\n/**\n * Thin façade over {@link SessionEventBus} that pre-registers all built-in\n * listeners in their required order. `handle(event, ctx)` is the entry point\n * used by `AgentService.handleSessionEvent`; extensions can hook in via\n * {@link registerListener} without touching this class.\n */\nexport class AgentEventHandler {\n private readonly bus: SessionEventBus;\n\n constructor(config: AgentEventHandlerConfig) {\n this.bus = new SessionEventBus();\n\n // Order matters for `tool_execution_end`: SystemReminder mutates `event.result`\n // and ToolChain + ErrorTracking depend on the mutated value. Keep these in this\n // exact sequence.\n installProgressListener(this.bus, config.progressManager);\n installRequestLimitListener(this.bus, {\n requestLimiter: config.requestLimiter,\n progressManager: config.progressManager,\n lifecycleManager: config.lifecycleManager,\n });\n installLifecycleHookListener(this.bus, config.lifecycleManager);\n installSystemReminderListener(this.bus, config.systemReminder);\n if (config.turnDiffTracker) {\n installTurnDiffTrackerListener(this.bus, config.turnDiffTracker);\n }\n installSelfVerifyListener(this.bus, config.selfVerifyMiddleware);\n installToolUsageListener(this.bus, config.toolUsageAnalyzer);\n installToolChainListener(this.bus, config.toolChainTracker);\n installErrorTrackingListener(this.bus, {\n errorTracker: config.errorTracker,\n errorPatternMatcher: config.errorPatternMatcher,\n });\n }\n\n /** Dispatch a pi-agent event to all registered listeners. */\n handle(event: AgentEvent, context: SessionContext | null): void {\n this.bus.dispatch(event, context);\n }\n\n /**\n * Register an additional listener (e.g. from an extension). Returns an\n * unsubscribe function. Listeners run after the built-ins in registration\n * order; if you need to run before a built-in, you must create your own\n * {@link SessionEventBus} instance.\n */\n registerListener(type: SessionEventTypeFilter, listener: SessionEventListener): () => void {\n return this.bus.on(type, listener);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
|
|
1
2
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
3
|
import { init_logger } from "../../utils/logger.js";
|
|
3
4
|
import "../memory/dreaming/constants.js";
|
|
4
|
-
import {
|
|
5
|
+
import { resolveDreamingAgentScope } from "../memory/dreaming/scope.js";
|
|
5
6
|
import { persistInboundAttachments } from "../../channels/attachments/inbound-persist.js";
|
|
6
7
|
import { buildTranscriptUserMessage, clearPendingTranscriptUserMessage, hydrateUserTurnForLlm, setPendingTranscriptUserMessage } from "../inbound/attachment-pipeline.js";
|
|
7
8
|
import { extractAgentUserPlainText } from "../memory/user-message-text.js";
|
|
@@ -52,15 +53,20 @@ var AgentOrchestrator = class {
|
|
|
52
53
|
if (typeof msg.content === "string" && (sessionKey.startsWith("cron:") || sessionKey.includes(":cron:") || sessionKey.includes(":automation:") || context.channel === "cron" || context.channel === "automation")) {
|
|
53
54
|
const content = msg.content;
|
|
54
55
|
if (content.includes("__xopc_memory_dreaming_sweep__") || content.includes("__xopc_memory_dreaming_light_sweep__") || content.includes("__xopc_memory_dreaming_rem_sweep__")) {
|
|
55
|
-
const
|
|
56
|
-
|
|
56
|
+
const cfg = this.getConfig?.();
|
|
57
|
+
if (!cfg) {
|
|
58
|
+
log.warn({ sessionKey }, "Dreaming sweep skipped: config unavailable");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const scope = resolveDreamingAgentScope(cfg, resolveEffectiveAgentProfileForSession(cfg, sessionKey).agentId);
|
|
62
|
+
const resolved = scope.config;
|
|
57
63
|
const t0 = Date.now();
|
|
58
64
|
if (content.includes("__xopc_memory_dreaming_light_sweep__")) {
|
|
59
65
|
const result = await runLightSweep({
|
|
60
|
-
|
|
66
|
+
dreamingRoot: scope.memoriesDir,
|
|
61
67
|
config: resolved.phases.light
|
|
62
68
|
});
|
|
63
|
-
|
|
69
|
+
const event = {
|
|
64
70
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65
71
|
phase: "light",
|
|
66
72
|
ok: result.ok,
|
|
@@ -69,13 +75,14 @@ var AgentOrchestrator = class {
|
|
|
69
75
|
scannedEntries: result.scannedEntries,
|
|
70
76
|
newSignals: result.newSignals,
|
|
71
77
|
deduped: result.deduped
|
|
72
|
-
}
|
|
78
|
+
};
|
|
79
|
+
await appendDreamingEvent(scope.memoriesDir, event);
|
|
73
80
|
} else if (content.includes("__xopc_memory_dreaming_rem_sweep__")) {
|
|
74
81
|
const result = await runRemPatterns({
|
|
75
|
-
|
|
82
|
+
dreamingRoot: scope.memoriesDir,
|
|
76
83
|
config: resolved.phases.rem
|
|
77
84
|
});
|
|
78
|
-
|
|
85
|
+
const event = {
|
|
79
86
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80
87
|
phase: "rem",
|
|
81
88
|
ok: result.ok,
|
|
@@ -83,14 +90,16 @@ var AgentOrchestrator = class {
|
|
|
83
90
|
durationMs: Date.now() - t0,
|
|
84
91
|
patternsDiscovered: result.patternsDiscovered,
|
|
85
92
|
entriesAnalyzed: result.entriesAnalyzed
|
|
86
|
-
}
|
|
93
|
+
};
|
|
94
|
+
await appendDreamingEvent(scope.memoriesDir, event);
|
|
87
95
|
} else {
|
|
88
96
|
const result = await runDreamingDeepPromotion({
|
|
89
|
-
workspaceDir,
|
|
97
|
+
workspaceDir: scope.workspaceDir,
|
|
98
|
+
dreamingRoot: scope.memoriesDir,
|
|
90
99
|
config: resolved.phases.deep,
|
|
91
100
|
memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey)
|
|
92
101
|
});
|
|
93
|
-
|
|
102
|
+
const event = {
|
|
94
103
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
95
104
|
phase: "deep",
|
|
96
105
|
ok: result.ok,
|
|
@@ -98,7 +107,8 @@ var AgentOrchestrator = class {
|
|
|
98
107
|
durationMs: Date.now() - t0,
|
|
99
108
|
candidates: result.candidates,
|
|
100
109
|
applied: result.applied
|
|
101
|
-
}
|
|
110
|
+
};
|
|
111
|
+
await appendDreamingEvent(scope.memoriesDir, event);
|
|
102
112
|
}
|
|
103
113
|
return;
|
|
104
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport { persistInboundAttachments } from '../../channels/attachments/inbound-persist.js';\nimport {\n buildTranscriptUserMessage,\n clearPendingTranscriptUserMessage,\n hydrateUserTurnForLlm,\n setPendingTranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit scheduled maintenance tokens into local runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (\n sessionKey.startsWith('cron:') ||\n sessionKey.includes(':cron:') ||\n sessionKey.includes(':automation:') ||\n context.channel === 'cron' ||\n context.channel === 'automation'\n )\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const resolved = resolveDreamingConfig(this.getConfig?.());\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({ workspaceDir, config: resolved.phases.light });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({ workspaceDir, config: resolved.phases.rem });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else {\n const result = await runDreamingDeepPromotion({\n workspaceDir,\n config: resolved.phases.deep,\n memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey),\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(workspaceDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const channelSystemPrompt =\n typeof context.metadata?.channelSystemPrompt === 'string'\n ? context.metadata.channelSystemPrompt.trim()\n : '';\n if (channelSystemPrompt) {\n this.agentManager.getOrCreateAgent(sessionKey);\n this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);\n }\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n const persistedAttachments = await persistInboundAttachments(msg.attachments);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const userMessage = await buildTranscriptUserMessage({\n text: msg.content,\n prepared: persistedAttachments,\n sessionKey,\n modelRef,\n config: this.getConfig?.(),\n agentManager: this.agentManager,\n });\n setPendingTranscriptUserMessage(sessionKey, userMessage);\n\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n const llmTurn = await hydrateUserTurnForLlm({\n message: userMessage,\n modelRef,\n });\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await (async () => {\n try {\n return await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n llmImages: llmTurn.images,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, userMessage);\n }\n })();\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport { persistInboundAttachments } from '../../channels/attachments/inbound-persist.js';\nimport {\n buildTranscriptUserMessage,\n clearPendingTranscriptUserMessage,\n hydrateUserTurnForLlm,\n setPendingTranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\nimport { resolveDreamingAgentScope } from '../memory/dreaming/scope.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit scheduled maintenance tokens into local runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (\n sessionKey.startsWith('cron:') ||\n sessionKey.includes(':cron:') ||\n sessionKey.includes(':automation:') ||\n context.channel === 'cron' ||\n context.channel === 'automation'\n )\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const cfg = this.getConfig?.();\n if (!cfg) {\n log.warn({ sessionKey }, 'Dreaming sweep skipped: config unavailable');\n return;\n }\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const scope = resolveDreamingAgentScope(cfg, profile.agentId);\n const resolved = scope.config;\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({ dreamingRoot: scope.memoriesDir, config: resolved.phases.light });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({ dreamingRoot: scope.memoriesDir, config: resolved.phases.rem });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else {\n const result = await runDreamingDeepPromotion({\n workspaceDir: scope.workspaceDir,\n dreamingRoot: scope.memoriesDir,\n config: resolved.phases.deep,\n memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey),\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const channelSystemPrompt =\n typeof context.metadata?.channelSystemPrompt === 'string'\n ? context.metadata.channelSystemPrompt.trim()\n : '';\n if (channelSystemPrompt) {\n this.agentManager.getOrCreateAgent(sessionKey);\n this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);\n }\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n const persistedAttachments = await persistInboundAttachments(msg.attachments);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const userMessage = await buildTranscriptUserMessage({\n text: msg.content,\n prepared: persistedAttachments,\n sessionKey,\n modelRef,\n config: this.getConfig?.(),\n agentManager: this.agentManager,\n });\n setPendingTranscriptUserMessage(sessionKey, userMessage);\n\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n const llmTurn = await hydrateUserTurnForLlm({\n message: userMessage,\n modelRef,\n });\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await (async () => {\n try {\n return await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n llmImages: llmTurn.images,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, userMessage);\n }\n })();\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
|
|
@@ -122,6 +122,18 @@ const HIGH_PATTERNS = [
|
|
|
122
122
|
pattern: /\blaunchctl\s+(load|unload|start|stop)\b/,
|
|
123
123
|
reason: "macOS launchctl manipulation"
|
|
124
124
|
},
|
|
125
|
+
{
|
|
126
|
+
pattern: /\bgit\s+(?:-[^\s]+\s+)*(?!push\s+--force\b)(commit|push|checkout|switch|restore|reset|rebase|merge|cherry-pick|clean|apply|am|stash)\b/,
|
|
127
|
+
reason: "Git command may mutate repository state or publish changes"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
pattern: /\bgit\s+(?:-[^\s]+\s+)*branch\s+(-[dD]|--delete|--move|-m|-M|--set-upstream-to)\b/,
|
|
131
|
+
reason: "Git branch mutation"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
pattern: /\bgit\s+(?:-[^\s]+\s+)*tag\s+(-[adfs]|--delete|--annotate|--force|--sign)\b/,
|
|
135
|
+
reason: "Git tag mutation"
|
|
136
|
+
},
|
|
125
137
|
{
|
|
126
138
|
pattern: /\bsudo\s+/,
|
|
127
139
|
reason: "Sudo command execution"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-validator.js","names":[],"sources":["../../../../src/agent/sandbox/command-validator.ts"],"sourcesContent":["/**\n * Command injection detection and dangerous command blocking.\n *\n * Enhances the basic checkShellSafety in prompt/safety.ts with deeper analysis:\n * - Shell metacharacter injection in arguments\n * - Dangerous command prefixes (destructive ops, credential exfiltration)\n * - Pipe-to-interpreter patterns (curl|bash, wget|sh, etc.)\n * - Credential / secret harvesting attempts\n */\n\nimport type { CommandValidationResult } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Critical — always block, no override\n// ---------------------------------------------------------------------------\n\nconst CRITICAL_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Destructive filesystem ops\n { pattern: /\\brm\\s+(-[a-zA-Z]*[rR][a-zA-Z]*\\s+|--recursive\\s+)\\/(\\s|$)/, reason: 'Recursive delete from root' },\n { pattern: /\\brm\\s+(-[a-zA-Z]*[rR][a-zA-Z]*\\s+|--recursive\\s+)~\\//, reason: 'Recursive delete from home directory' },\n { pattern: /\\bmkfs\\b/, reason: 'Filesystem format command' },\n { pattern: /\\bdd\\s+.*\\bof=\\/dev\\//, reason: 'Raw device write via dd' },\n { pattern: />\\s*\\/dev\\/[hs]d/, reason: 'Redirect to block device' },\n\n // Pipe to interpreter — remote code execution\n { pattern: /\\bcurl\\b[^|]*\\|\\s*(ba)?sh\\b/, reason: 'Pipe curl output to shell' },\n { pattern: /\\bwget\\b[^|]*\\|\\s*(ba)?sh\\b/, reason: 'Pipe wget output to shell' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*python/, reason: 'Pipe curl output to python' },\n { pattern: /\\bwget\\b[^|]*\\|\\s*python/, reason: 'Pipe wget output to python' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*node\\b/, reason: 'Pipe curl output to node' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*perl\\b/, reason: 'Pipe curl output to perl' },\n\n // Credential harvesting\n { pattern: /\\bcat\\s+.*\\/\\.ssh\\/id_/, reason: 'Read SSH private key' },\n { pattern: /\\bcat\\s+.*\\/\\.aws\\/credentials/, reason: 'Read AWS credentials' },\n { pattern: /\\bcat\\s+.*\\/\\.gnupg\\//, reason: 'Read GPG keyring' },\n { pattern: /\\bcat\\s+.*\\/\\.netrc/, reason: 'Read netrc credentials' },\n { pattern: /\\bcat\\s+.*\\/etc\\/shadow/, reason: 'Read shadow password file' },\n\n // Docker socket access\n { pattern: /\\/var\\/run\\/docker\\.sock/, reason: 'Docker socket access' },\n { pattern: /\\bdocker\\s+run\\b.*--privileged/, reason: 'Privileged Docker container' },\n];\n\n// ---------------------------------------------------------------------------\n// High — block by default, can be overridden in advanced mode\n// ---------------------------------------------------------------------------\n\nconst HIGH_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Dangerous flags\n { pattern: /\\bchmod\\s+[0-7]*7[0-7]{2}\\b/, reason: 'World-writable chmod' },\n { pattern: /\\bchmod\\s+.*[+]s\\b/, reason: 'Set SUID/SGID bit' },\n { pattern: /\\bchown\\s+root\\b/, reason: 'Change ownership to root' },\n\n // Network exfiltration patterns\n { pattern: /\\bcurl\\b.*--upload-file/, reason: 'File upload via curl' },\n { pattern: /\\bcurl\\b.*-T\\s/, reason: 'File upload via curl -T' },\n { pattern: /\\bnc\\s+-[a-zA-Z]*l/, reason: 'Netcat listener (reverse shell)' },\n { pattern: /\\bsocat\\b.*\\bexec\\b/i, reason: 'Socat exec (reverse shell)' },\n\n // Process manipulation\n { pattern: /\\bkill\\s+-9\\s+1\\b/, reason: 'Kill init process' },\n { pattern: /\\bkillall\\b/, reason: 'Kill all processes by name' },\n\n // Crontab modification\n { pattern: /\\bcrontab\\s+-[erl]/, reason: 'Crontab modification' },\n\n // System service manipulation\n { pattern: /\\bsystemctl\\s+(start|stop|restart|enable|disable)\\b/, reason: 'Systemd service manipulation' },\n { pattern: /\\blaunchctl\\s+(load|unload|start|stop)\\b/, reason: 'macOS launchctl manipulation' },\n\n // Package manager with elevated privileges\n { pattern: /\\bsudo\\s+/, reason: 'Sudo command execution' },\n { pattern: /\\bsu\\s+-\\s/, reason: 'Switch to root user' },\n { pattern: /\\bdoas\\s+/, reason: 'Doas command execution' },\n];\n\n// ---------------------------------------------------------------------------\n// Medium — warn but allow\n// ---------------------------------------------------------------------------\n\nconst MEDIUM_PATTERNS: { pattern: RegExp; reason: string }[] = [\n { pattern: /\\brm\\s+-[a-zA-Z]*[rR]/, reason: 'Recursive delete (non-root)' },\n { pattern: /\\bmv\\s+.*\\s+\\//, reason: 'Move file to root directory' },\n // Exclude common safe patterns like `chmod +x`, only warn on octal or broad ops\n { pattern: /\\bchmod\\s+(?!\\+x\\b)[0-7]{3,4}\\b/, reason: 'Octal permission change' },\n { pattern: /\\bgit\\s+push\\s+--force/, reason: 'Force push to git remote' },\n { pattern: /\\bgit\\s+reset\\s+--hard/, reason: 'Hard git reset' },\n];\n\n// ---------------------------------------------------------------------------\n// Shell metacharacter injection in command arguments\n// ---------------------------------------------------------------------------\n\n/**\n * Detect obvious shell injection patterns that might be embedded in\n * model-generated arguments (e.g. a filename containing \"; rm -rf /\").\n *\n * This is intentionally conservative — we only flag patterns that are\n * extremely unlikely in legitimate commands.\n */\nconst INJECTION_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Backtick containing dangerous commands (not benign subshells like `date` or `mktemp`)\n { pattern: /`[^`]*(curl|wget|nc|rm\\s+-rf|dd\\s+|mkfs)[^`]*`/, reason: 'Dangerous command inside backtick substitution' },\n // $() containing dangerous commands (allow benign like $(date), $(pwd), $(nproc))\n { pattern: /\\$\\([^)]*(curl|wget|nc|rm\\s+-rf|dd\\s+|mkfs)[^)]*\\)/, reason: 'Dangerous command inside $() substitution' },\n // Semicolon-separated second command that is destructive\n { pattern: /;\\s*(rm|dd|mkfs|curl|wget|nc)\\b/, reason: 'Injected destructive command after semicolon' },\n // Newline injection\n { pattern: /\\n\\s*(rm|dd|mkfs|curl|wget|nc)\\b/, reason: 'Newline-injected destructive command' },\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a shell command against injection and dangerous-command patterns.\n *\n * Returns `{ allowed: false }` for critical/high severity issues.\n * Returns `{ allowed: true }` with a reason for medium severity (warning).\n */\nexport function validateCommand(command: string): CommandValidationResult {\n const trimmed = command.trim();\n\n if (!trimmed) {\n return { allowed: false, reason: 'Empty command', severity: 'medium' };\n }\n\n // --- Critical checks ---\n for (const { pattern, reason } of CRITICAL_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Critical: ${reason}`, severity: 'critical' };\n }\n }\n\n // --- Injection checks ---\n for (const { pattern, reason } of INJECTION_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Injection: ${reason}`, severity: 'critical' };\n }\n }\n\n // --- High severity checks ---\n for (const { pattern, reason } of HIGH_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Blocked: ${reason}`, severity: 'high' };\n }\n }\n\n // --- Medium severity checks (allow but flag) ---\n for (const { pattern, reason } of MEDIUM_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: true, reason: `Warning: ${reason}`, severity: 'medium' };\n }\n }\n\n return { allowed: true };\n}\n\n/**\n * Extract a list of all matched warning/block reasons for audit logging.\n * Unlike `validateCommand`, this does not short-circuit on the first match.\n */\nexport function auditCommand(command: string): { severity: 'critical' | 'high' | 'medium'; reason: string }[] {\n const trimmed = command.trim();\n const findings: { severity: 'critical' | 'high' | 'medium'; reason: string }[] = [];\n\n for (const { pattern, reason } of CRITICAL_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'critical', reason });\n }\n for (const { pattern, reason } of INJECTION_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'critical', reason });\n }\n for (const { pattern, reason } of HIGH_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'high', reason });\n }\n for (const { pattern, reason } of MEDIUM_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'medium', reason });\n }\n\n return findings;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command-validator.js","names":[],"sources":["../../../../src/agent/sandbox/command-validator.ts"],"sourcesContent":["/**\n * Command injection detection and dangerous command blocking.\n *\n * Enhances the basic checkShellSafety in prompt/safety.ts with deeper analysis:\n * - Shell metacharacter injection in arguments\n * - Dangerous command prefixes (destructive ops, credential exfiltration)\n * - Pipe-to-interpreter patterns (curl|bash, wget|sh, etc.)\n * - Credential / secret harvesting attempts\n */\n\nimport type { CommandValidationResult } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Critical — always block, no override\n// ---------------------------------------------------------------------------\n\nconst CRITICAL_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Destructive filesystem ops\n { pattern: /\\brm\\s+(-[a-zA-Z]*[rR][a-zA-Z]*\\s+|--recursive\\s+)\\/(\\s|$)/, reason: 'Recursive delete from root' },\n { pattern: /\\brm\\s+(-[a-zA-Z]*[rR][a-zA-Z]*\\s+|--recursive\\s+)~\\//, reason: 'Recursive delete from home directory' },\n { pattern: /\\bmkfs\\b/, reason: 'Filesystem format command' },\n { pattern: /\\bdd\\s+.*\\bof=\\/dev\\//, reason: 'Raw device write via dd' },\n { pattern: />\\s*\\/dev\\/[hs]d/, reason: 'Redirect to block device' },\n\n // Pipe to interpreter — remote code execution\n { pattern: /\\bcurl\\b[^|]*\\|\\s*(ba)?sh\\b/, reason: 'Pipe curl output to shell' },\n { pattern: /\\bwget\\b[^|]*\\|\\s*(ba)?sh\\b/, reason: 'Pipe wget output to shell' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*python/, reason: 'Pipe curl output to python' },\n { pattern: /\\bwget\\b[^|]*\\|\\s*python/, reason: 'Pipe wget output to python' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*node\\b/, reason: 'Pipe curl output to node' },\n { pattern: /\\bcurl\\b[^|]*\\|\\s*perl\\b/, reason: 'Pipe curl output to perl' },\n\n // Credential harvesting\n { pattern: /\\bcat\\s+.*\\/\\.ssh\\/id_/, reason: 'Read SSH private key' },\n { pattern: /\\bcat\\s+.*\\/\\.aws\\/credentials/, reason: 'Read AWS credentials' },\n { pattern: /\\bcat\\s+.*\\/\\.gnupg\\//, reason: 'Read GPG keyring' },\n { pattern: /\\bcat\\s+.*\\/\\.netrc/, reason: 'Read netrc credentials' },\n { pattern: /\\bcat\\s+.*\\/etc\\/shadow/, reason: 'Read shadow password file' },\n\n // Docker socket access\n { pattern: /\\/var\\/run\\/docker\\.sock/, reason: 'Docker socket access' },\n { pattern: /\\bdocker\\s+run\\b.*--privileged/, reason: 'Privileged Docker container' },\n];\n\n// ---------------------------------------------------------------------------\n// High — block by default, can be overridden in advanced mode\n// ---------------------------------------------------------------------------\n\nconst HIGH_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Dangerous flags\n { pattern: /\\bchmod\\s+[0-7]*7[0-7]{2}\\b/, reason: 'World-writable chmod' },\n { pattern: /\\bchmod\\s+.*[+]s\\b/, reason: 'Set SUID/SGID bit' },\n { pattern: /\\bchown\\s+root\\b/, reason: 'Change ownership to root' },\n\n // Network exfiltration patterns\n { pattern: /\\bcurl\\b.*--upload-file/, reason: 'File upload via curl' },\n { pattern: /\\bcurl\\b.*-T\\s/, reason: 'File upload via curl -T' },\n { pattern: /\\bnc\\s+-[a-zA-Z]*l/, reason: 'Netcat listener (reverse shell)' },\n { pattern: /\\bsocat\\b.*\\bexec\\b/i, reason: 'Socat exec (reverse shell)' },\n\n // Process manipulation\n { pattern: /\\bkill\\s+-9\\s+1\\b/, reason: 'Kill init process' },\n { pattern: /\\bkillall\\b/, reason: 'Kill all processes by name' },\n\n // Crontab modification\n { pattern: /\\bcrontab\\s+-[erl]/, reason: 'Crontab modification' },\n\n // System service manipulation\n { pattern: /\\bsystemctl\\s+(start|stop|restart|enable|disable)\\b/, reason: 'Systemd service manipulation' },\n { pattern: /\\blaunchctl\\s+(load|unload|start|stop)\\b/, reason: 'macOS launchctl manipulation' },\n\n // Git operations that mutate repository state or publish externally. Read-only\n // inspection such as status/log/diff/show remains allowed through exec_command.\n { pattern: /\\bgit\\s+(?:-[^\\s]+\\s+)*(?!push\\s+--force\\b)(commit|push|checkout|switch|restore|reset|rebase|merge|cherry-pick|clean|apply|am|stash)\\b/, reason: 'Git command may mutate repository state or publish changes' },\n { pattern: /\\bgit\\s+(?:-[^\\s]+\\s+)*branch\\s+(-[dD]|--delete|--move|-m|-M|--set-upstream-to)\\b/, reason: 'Git branch mutation' },\n { pattern: /\\bgit\\s+(?:-[^\\s]+\\s+)*tag\\s+(-[adfs]|--delete|--annotate|--force|--sign)\\b/, reason: 'Git tag mutation' },\n\n // Package manager with elevated privileges\n { pattern: /\\bsudo\\s+/, reason: 'Sudo command execution' },\n { pattern: /\\bsu\\s+-\\s/, reason: 'Switch to root user' },\n { pattern: /\\bdoas\\s+/, reason: 'Doas command execution' },\n];\n\n// ---------------------------------------------------------------------------\n// Medium — warn but allow\n// ---------------------------------------------------------------------------\n\nconst MEDIUM_PATTERNS: { pattern: RegExp; reason: string }[] = [\n { pattern: /\\brm\\s+-[a-zA-Z]*[rR]/, reason: 'Recursive delete (non-root)' },\n { pattern: /\\bmv\\s+.*\\s+\\//, reason: 'Move file to root directory' },\n // Exclude common safe patterns like `chmod +x`, only warn on octal or broad ops\n { pattern: /\\bchmod\\s+(?!\\+x\\b)[0-7]{3,4}\\b/, reason: 'Octal permission change' },\n { pattern: /\\bgit\\s+push\\s+--force/, reason: 'Force push to git remote' },\n { pattern: /\\bgit\\s+reset\\s+--hard/, reason: 'Hard git reset' },\n];\n\n// ---------------------------------------------------------------------------\n// Shell metacharacter injection in command arguments\n// ---------------------------------------------------------------------------\n\n/**\n * Detect obvious shell injection patterns that might be embedded in\n * model-generated arguments (e.g. a filename containing \"; rm -rf /\").\n *\n * This is intentionally conservative — we only flag patterns that are\n * extremely unlikely in legitimate commands.\n */\nconst INJECTION_PATTERNS: { pattern: RegExp; reason: string }[] = [\n // Backtick containing dangerous commands (not benign subshells like `date` or `mktemp`)\n { pattern: /`[^`]*(curl|wget|nc|rm\\s+-rf|dd\\s+|mkfs)[^`]*`/, reason: 'Dangerous command inside backtick substitution' },\n // $() containing dangerous commands (allow benign like $(date), $(pwd), $(nproc))\n { pattern: /\\$\\([^)]*(curl|wget|nc|rm\\s+-rf|dd\\s+|mkfs)[^)]*\\)/, reason: 'Dangerous command inside $() substitution' },\n // Semicolon-separated second command that is destructive\n { pattern: /;\\s*(rm|dd|mkfs|curl|wget|nc)\\b/, reason: 'Injected destructive command after semicolon' },\n // Newline injection\n { pattern: /\\n\\s*(rm|dd|mkfs|curl|wget|nc)\\b/, reason: 'Newline-injected destructive command' },\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a shell command against injection and dangerous-command patterns.\n *\n * Returns `{ allowed: false }` for critical/high severity issues.\n * Returns `{ allowed: true }` with a reason for medium severity (warning).\n */\nexport function validateCommand(command: string): CommandValidationResult {\n const trimmed = command.trim();\n\n if (!trimmed) {\n return { allowed: false, reason: 'Empty command', severity: 'medium' };\n }\n\n // --- Critical checks ---\n for (const { pattern, reason } of CRITICAL_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Critical: ${reason}`, severity: 'critical' };\n }\n }\n\n // --- Injection checks ---\n for (const { pattern, reason } of INJECTION_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Injection: ${reason}`, severity: 'critical' };\n }\n }\n\n // --- High severity checks ---\n for (const { pattern, reason } of HIGH_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: false, reason: `Blocked: ${reason}`, severity: 'high' };\n }\n }\n\n // --- Medium severity checks (allow but flag) ---\n for (const { pattern, reason } of MEDIUM_PATTERNS) {\n if (pattern.test(trimmed)) {\n return { allowed: true, reason: `Warning: ${reason}`, severity: 'medium' };\n }\n }\n\n return { allowed: true };\n}\n\n/**\n * Extract a list of all matched warning/block reasons for audit logging.\n * Unlike `validateCommand`, this does not short-circuit on the first match.\n */\nexport function auditCommand(command: string): { severity: 'critical' | 'high' | 'medium'; reason: string }[] {\n const trimmed = command.trim();\n const findings: { severity: 'critical' | 'high' | 'medium'; reason: string }[] = [];\n\n for (const { pattern, reason } of CRITICAL_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'critical', reason });\n }\n for (const { pattern, reason } of INJECTION_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'critical', reason });\n }\n for (const { pattern, reason } of HIGH_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'high', reason });\n }\n for (const { pattern, reason } of MEDIUM_PATTERNS) {\n if (pattern.test(trimmed)) findings.push({ severity: 'medium', reason });\n }\n\n return findings;\n}\n"],"mappings":"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"}
|
|
@@ -35,6 +35,7 @@ export declare class AgentService {
|
|
|
35
35
|
private toolChainTracker;
|
|
36
36
|
private errorPatternMatcher;
|
|
37
37
|
private selfVerifyMiddleware;
|
|
38
|
+
private turnDiffTracker;
|
|
38
39
|
private contextMiddleware;
|
|
39
40
|
private messageRouter;
|
|
40
41
|
private commandHandler;
|
|
@@ -185,6 +186,6 @@ export declare class AgentService {
|
|
|
185
186
|
*/
|
|
186
187
|
private handleSessionEvent;
|
|
187
188
|
private checkAndCompact;
|
|
188
|
-
private
|
|
189
|
+
private getContextWindowForSession;
|
|
189
190
|
private dispose;
|
|
190
191
|
}
|
|
@@ -35,6 +35,7 @@ import { ErrorPatternMatcher } from "./tools/error-pattern-matcher.js";
|
|
|
35
35
|
import { SelfVerifyMiddleware } from "./middleware/self-verify.js";
|
|
36
36
|
import { ContextMiddleware } from "./middleware/context.js";
|
|
37
37
|
import "./middleware/index.js";
|
|
38
|
+
import { TurnDiffTracker } from "./coding/turn-diff-tracker.js";
|
|
38
39
|
import { LifecycleManager } from "./lifecycle/manager.js";
|
|
39
40
|
import "./lifecycle/index.js";
|
|
40
41
|
import { CompactionLifecycleHandler } from "./lifecycle/handlers/compaction.js";
|
|
@@ -100,6 +101,7 @@ var AgentService = class {
|
|
|
100
101
|
toolChainTracker;
|
|
101
102
|
errorPatternMatcher;
|
|
102
103
|
selfVerifyMiddleware;
|
|
104
|
+
turnDiffTracker;
|
|
103
105
|
contextMiddleware;
|
|
104
106
|
messageRouter;
|
|
105
107
|
commandHandler;
|
|
@@ -226,7 +228,7 @@ var AgentService = class {
|
|
|
226
228
|
getWorkflowRunService: config.getWorkflowRunService,
|
|
227
229
|
onSkillsUpdated: config.onSkillsUpdated,
|
|
228
230
|
isWorkspaceTrusted: config.isWorkspaceTrusted,
|
|
229
|
-
getSelfVerifyPromptContext: (sessionKey, agentId) => this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId)
|
|
231
|
+
getSelfVerifyPromptContext: (sessionKey, agentId) => [this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId), this.turnDiffTracker.buildFinalGuardContext(sessionKey)].filter(Boolean).join("\n\n")
|
|
230
232
|
});
|
|
231
233
|
this.agentEventHandler = new AgentEventHandler({
|
|
232
234
|
progressManager: this.progressManager,
|
|
@@ -237,7 +239,8 @@ var AgentService = class {
|
|
|
237
239
|
selfVerifyMiddleware: this.selfVerifyMiddleware,
|
|
238
240
|
systemReminder: this.systemReminder,
|
|
239
241
|
toolUsageAnalyzer: this.toolUsageAnalyzer,
|
|
240
|
-
errorPatternMatcher: this.errorPatternMatcher
|
|
242
|
+
errorPatternMatcher: this.errorPatternMatcher,
|
|
243
|
+
turnDiffTracker: this.turnDiffTracker
|
|
241
244
|
});
|
|
242
245
|
this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(this.agentEventHandler);
|
|
243
246
|
this.sessionHydrator = new SessionHydrator({
|
|
@@ -326,7 +329,7 @@ var AgentService = class {
|
|
|
326
329
|
agentManager: this.agentManager,
|
|
327
330
|
sessionHydrator: this.sessionHydrator,
|
|
328
331
|
getConfig: () => this.effectiveAppConfig(),
|
|
329
|
-
getContextWindow: () => this.
|
|
332
|
+
getContextWindow: (sessionKey) => this.getContextWindowForSession(sessionKey)
|
|
330
333
|
});
|
|
331
334
|
this.outboundCoordinator = new OutboundCoordinator({
|
|
332
335
|
bus,
|
|
@@ -446,6 +449,7 @@ var AgentService = class {
|
|
|
446
449
|
minTurnsForVerification: 4,
|
|
447
450
|
resetOnVerification: true
|
|
448
451
|
});
|
|
452
|
+
this.turnDiffTracker = new TurnDiffTracker();
|
|
449
453
|
this.requestLimiter = new RequestLimiter({
|
|
450
454
|
maxRequestsPerTurn: 50,
|
|
451
455
|
warnThreshold: .8,
|
|
@@ -763,7 +767,7 @@ var AgentService = class {
|
|
|
763
767
|
this.agentEventHandler.handle(event, currentContext);
|
|
764
768
|
}
|
|
765
769
|
async checkAndCompact(sessionKey, messages) {
|
|
766
|
-
const contextWindow = this.
|
|
770
|
+
const contextWindow = this.getContextWindowForSession(sessionKey);
|
|
767
771
|
const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);
|
|
768
772
|
if (!prep.needsCompaction) return;
|
|
769
773
|
log.info({
|
|
@@ -783,8 +787,16 @@ var AgentService = class {
|
|
|
783
787
|
tokensAfter: result.tokensAfter
|
|
784
788
|
}, "Session compacted");
|
|
785
789
|
}
|
|
786
|
-
|
|
787
|
-
|
|
790
|
+
getContextWindowForSession(sessionKey) {
|
|
791
|
+
try {
|
|
792
|
+
return this.modelManager.getResolvedModelForSession(sessionKey).contextWindow ?? 128e3;
|
|
793
|
+
} catch (err) {
|
|
794
|
+
log.warn({
|
|
795
|
+
err,
|
|
796
|
+
sessionKey
|
|
797
|
+
}, "Failed to resolve session context window; using default");
|
|
798
|
+
return 128e3;
|
|
799
|
+
}
|
|
788
800
|
}
|
|
789
801
|
dispose() {
|
|
790
802
|
this.sessionTracker.dispose();
|