@xopcai/xopc 0.0.123 → 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/README.md +28 -11
- package/README.zh-CN.md +28 -11
- 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-Bicr3ZBg.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-Bpy64GjU.js → archive-plugin-kUHEJ1HK.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DMkp4xSb.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-DA9C5mCw.js → binary-plugins-DllA_jQ0.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CiSXk_YC.js → block-editor-Cm6CWNj1.js} +5 -5
- 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-CDhrhfhN.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-7jKfbH0J.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-Dtpbbe0s.js → fetch-NFKfkqrd.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BN6pyX2c.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-BIET2x3y.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-CQm2r1Ty.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-DS44pJzr.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-Dej6zHnr.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-Zu1FzH6z.js → save-bar-controls-B132Uf-2.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BW78mKc0.js → schedule-field-B5qshALm.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Cc-IOydE.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-DIpIYht_.js → settings-collapsible-section-pRlvqsAf.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BFxBNxOG.js → settings-form-section-BBYUDLVd.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-C4fivemT.js → settings-page-DaO7zYZM.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CLx4o2PN.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-ZswWCrDu.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-3ZNcGRhS.js → system-settings-panel-HUU2FFjM.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-bpzZjiVF.js → typed-models-lib-AH160QHh.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BCjZLZ3O.js → use-channel-catalog-BXzDmjCQ.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-CIXafITi.js → user-profile-settings-panel-DxwnQvQ3.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-DXMtz5j_.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-B4sZpTDQ.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.d.ts +6 -1
- package/dist/src/agent/agent-manager.js +34 -14
- 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/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
- package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
- package/dist/src/agent/lifecycle/progress.js +3 -1
- package/dist/src/agent/lifecycle/progress.js.map +1 -1
- package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
- package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
- package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
- 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/memory/summary-generator.d.ts +1 -1
- package/dist/src/agent/memory/summary-generator.js +1 -2
- package/dist/src/agent/memory/summary-generator.js.map +1 -1
- package/dist/src/agent/middleware/index.d.ts +1 -1
- package/dist/src/agent/middleware/self-verify.d.ts +44 -14
- package/dist/src/agent/middleware/self-verify.js +214 -44
- package/dist/src/agent/middleware/self-verify.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 +69 -14
- 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/prompt/sections/tooling.js +18 -13
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/prompt/system-reminder.js +2 -2
- package/dist/src/agent/prompt/system-reminder.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/sandbox/sanitize-env-vars.d.ts +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
- package/dist/src/agent/sandbox/types.d.ts +2 -2
- package/dist/src/agent/sandbox/types.js.map +1 -1
- package/dist/src/agent/service/btw-query.js +1 -1
- package/dist/src/agent/service/btw-query.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.js +6 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +12 -7
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -1
- package/dist/src/agent/service.js +19 -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/skills/skill-tool-gating.js +1 -1
- package/dist/src/agent/skills/skill-tool-gating.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/starter-agents.js +9 -9
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/apply-patch.d.ts +25 -0
- package/dist/src/agent/tools/apply-patch.js +310 -0
- package/dist/src/agent/tools/apply-patch.js.map +1 -0
- 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/delegate-tool.d.ts +1 -1
- package/dist/src/agent/tools/delegate-tool.js +3 -3
- package/dist/src/agent/tools/delegate-tool.js.map +1 -1
- 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.d.ts +30 -0
- package/dist/src/agent/tools/exec-command.js +228 -0
- package/dist/src/agent/tools/exec-command.js.map +1 -0
- package/dist/src/agent/tools/execute-code-tool.js +7 -4
- package/dist/src/agent/tools/execute-code-tool.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 +6 -3
- package/dist/src/agent/tools/factory.js +14 -9
- 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 +5 -2
- package/dist/src/agent/tools/index.js +7 -4
- 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/skills-tools.d.ts +1 -1
- package/dist/src/agent/tools/skills-tools.js.map +1 -1
- package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
- package/dist/src/agent/tools/update-plan-tool.js +72 -0
- package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
- package/dist/src/agent/tools/write.js +6 -0
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
- package/dist/src/agent/workflow/step-labels.js +7 -3
- package/dist/src/agent/workflow/step-labels.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/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/review.d.ts +1 -0
- package/dist/src/chat-commands/builtins/review.js +167 -0
- package/dist/src/chat-commands/builtins/review.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.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/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +3 -2
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
- package/dist/src/gateway/chat-stream/mapper.js +149 -6
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
- 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/commands-skills.js +21 -0
- package/dist/src/gateway/hono/routes/commands-skills.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/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +886 -45
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/search.d.ts +3 -0
- package/dist/src/gateway/hono/routes/search.js +47 -0
- package/dist/src/gateway/hono/routes/search.js.map +1 -0
- 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/security/dangerous-tools.js +1 -1
- package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
- package/dist/src/gateway/service.js +17 -8
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/mcp/channel-bridge.js +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/projects/index.d.ts +2 -0
- package/dist/src/projects/index.js +3 -1
- package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
- package/dist/src/projects/project-agent-suggestion.js +19 -0
- package/dist/src/projects/project-agent-suggestion.js.map +1 -0
- package/dist/src/projects/project-kind.d.ts +13 -0
- package/dist/src/projects/project-kind.js +147 -0
- package/dist/src/projects/project-kind.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +1 -0
- package/dist/src/projects/project-service.js +1 -1
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.js +32 -8
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +1 -0
- package/dist/src/review/review-git.d.ts +48 -0
- package/dist/src/review/review-git.js +333 -0
- package/dist/src/review/review-git.js.map +1 -0
- package/dist/src/review/review-types.d.ts +22 -0
- package/dist/src/review/review-types.js +29 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/session/session-context-for-llm.d.ts +2 -0
- package/dist/src/session/session-context-for-llm.js +165 -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/021_projects_fts.sql +21 -0
- 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/backends/embedded-backend.d.ts +1 -0
- package/dist/src/tui/backends/embedded-backend.js +20 -0
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +2 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +7 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/assistant-message.js +47 -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-agent-events.js +25 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +4 -0
- package/dist/src/tui/tui-commands.d.ts +1 -0
- package/dist/src/tui/tui-commands.js +110 -4
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
- package/dist/src/tui/tui-picker-overlay.js +153 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- 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/tui/tui.js +16 -3
- package/dist/src/tui/tui.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-CxDSFHGx.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-CbEy9sR1.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-CTHja6n9.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CyZdgqvm.js +0 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-KnYaP7NT.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-8VLKM4au.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-5tPg0DcP.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-CEJ44WCU.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BJW3aVST.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CorUCTrs.js +0 -1
- package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-VKstC3sP.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-m1-wmz4o.js +0 -1
- package/dist/gateway/static/root/assets/extension-debug-page-C9qQQrBS.js +0 -1
- package/dist/gateway/static/root/assets/extension-page-DcBBMEGl.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-BQ-jMMP9.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-hQtgVmSR.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-BuIMDx6C.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-FEypCq74.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C4-2_MLG.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-DDrdRMxB.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-BJVok3Sl.js +0 -1
- package/dist/gateway/static/root/assets/index-CBDr5xjB.css +0 -1
- package/dist/gateway/static/root/assets/index-T0SaNd5a.js +0 -191
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-C2TuQppb.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-DwkwPgid.js +0 -2
- package/dist/gateway/static/root/assets/markdown-split-DhONQ81k.js +0 -1
- package/dist/gateway/static/root/assets/messages-avfvyZuQ.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-huNY4A1G.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-DdfGuEZ1.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-B0bT9Hqa.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-CjxhPorG.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-C4Z5OJMF.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CVCOPUVd.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B4qExXHC.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ce8yLTBi.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Kyc4eUf0.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Bu76CMiC.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-D-CFg86j.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CUU4f6Ss.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-C1MrQD8R.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-DqizIcY5.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-B3sVNPFk.js +0 -18
- package/dist/src/agent/tools/edit.d.ts +0 -14
- package/dist/src/agent/tools/edit.js +0 -132
- package/dist/src/agent/tools/edit.js.map +0 -1
- package/dist/src/agent/tools/shell.d.ts +0 -15
- package/dist/src/agent/tools/shell.js +0 -158
- package/dist/src/agent/tools/shell.js.map +0 -1
|
@@ -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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Filters process environment before passing it to child processes (e.g.
|
|
2
|
+
* Filters process environment before passing it to child processes (e.g. exec_command)
|
|
3
3
|
* to avoid leaking API keys and other credentials.
|
|
4
4
|
*/
|
|
5
5
|
export interface SanitizedEnvVars {
|
|
@@ -12,7 +12,7 @@ export declare function sanitizeEnvVars(env: Record<string, string | undefined>,
|
|
|
12
12
|
customBlocked?: RegExp[];
|
|
13
13
|
}): SanitizedEnvVars;
|
|
14
14
|
/**
|
|
15
|
-
* Environment for agent
|
|
15
|
+
* Environment for agent command execution: strips secrets, keeps normal tooling vars.
|
|
16
16
|
*/
|
|
17
17
|
export declare function prepareSafeToolEnv(baseEnv: NodeJS.ProcessEnv | Record<string, string | undefined>, options?: {
|
|
18
18
|
allowedVars?: string[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/agent/sandbox/sanitize-env-vars.ts
|
|
2
2
|
/**
|
|
3
|
-
* Filters process environment before passing it to child processes (e.g.
|
|
3
|
+
* Filters process environment before passing it to child processes (e.g. exec_command)
|
|
4
4
|
* to avoid leaking API keys and other credentials.
|
|
5
5
|
*/
|
|
6
6
|
const BLOCKED_ENV_VAR_PATTERNS = [
|
|
@@ -57,7 +57,7 @@ function sanitizeEnvVars(env, options) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Environment for agent
|
|
60
|
+
* Environment for agent command execution: strips secrets, keeps normal tooling vars.
|
|
61
61
|
*/
|
|
62
62
|
function prepareSafeToolEnv(baseEnv, options) {
|
|
63
63
|
const { safe } = sanitizeEnvVars(baseEnv, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize-env-vars.js","names":[],"sources":["../../../../src/agent/sandbox/sanitize-env-vars.ts"],"sourcesContent":["/**\n * Filters process environment before passing it to child processes (e.g.
|
|
1
|
+
{"version":3,"file":"sanitize-env-vars.js","names":[],"sources":["../../../../src/agent/sandbox/sanitize-env-vars.ts"],"sourcesContent":["/**\n * Filters process environment before passing it to child processes (e.g. exec_command)\n * to avoid leaking API keys and other credentials.\n */\n\nconst BLOCKED_ENV_VAR_PATTERNS: RegExp[] = [\n /^ANTHROPIC_API_KEY$/i,\n /^OPENAI_API_KEY$/i,\n /^GEMINI_API_KEY$/i,\n /^GOOGLE_API_KEY$/i,\n /^TELEGRAM_BOT_TOKEN$/i,\n /^DISCORD_BOT_TOKEN$/i,\n /^XOPC_GATEWAY_TOKEN$/i,\n /^XOPC_CONFIG$/i,\n /^AWS_(SECRET_ACCESS_KEY|SECRET_KEY|SESSION_TOKEN)$/i,\n /^AZURE_(CLIENT_SECRET|KEY)$/i,\n /_?(API_KEY|TOKEN|PASSWORD|PASSWD|PRIVATE_KEY|SECRET)$/i,\n];\n\nconst MAX_ENV_VAR_LENGTH = 32768;\n\nexport interface SanitizedEnvVars {\n safe: Record<string, string>;\n blocked: string[];\n warnings: string[];\n}\n\nfunction isDangerousEnvVar(key: string): boolean {\n const upperKey = key.toUpperCase();\n return BLOCKED_ENV_VAR_PATTERNS.some((pattern) => pattern.test(upperKey));\n}\n\nfunction validateEnvVarValue(key: string, value: string): string | null {\n if (value.includes('\\0')) {\n return `Environment variable ${key} contains null bytes`;\n }\n if (value.length > MAX_ENV_VAR_LENGTH) {\n return `Environment variable ${key} exceeds maximum length (${MAX_ENV_VAR_LENGTH})`;\n }\n if (/^[A-Za-z0-9+/=]{80,}$/.test(value)) {\n return `Environment variable ${key} looks like base64-encoded credential data`;\n }\n return null;\n}\n\nexport function sanitizeEnvVars(\n env: Record<string, string | undefined>,\n options?: {\n allowedVars?: string[];\n customBlocked?: RegExp[];\n },\n): SanitizedEnvVars {\n const safe: Record<string, string> = {};\n const blocked: string[] = [];\n const warnings: string[] = [];\n\n const allowedVars = new Set(options?.allowedVars ?? []);\n const customBlocked = options?.customBlocked ?? [];\n\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n\n if (allowedVars.has(key)) {\n safe[key] = value;\n continue;\n }\n\n const isBlocked =\n isDangerousEnvVar(key) || customBlocked.some((pattern) => pattern.test(key));\n\n if (isBlocked) {\n blocked.push(key);\n continue;\n }\n\n const warning = validateEnvVarValue(key, value);\n if (warning) {\n warnings.push(warning);\n continue;\n }\n\n safe[key] = value;\n }\n\n return { safe, blocked, warnings };\n}\n\n/**\n * Environment for agent command execution: strips secrets, keeps normal tooling vars.\n */\nexport function prepareSafeToolEnv(\n baseEnv: NodeJS.ProcessEnv | Record<string, string | undefined>,\n options?: { allowedVars?: string[] },\n): Record<string, string> {\n const { safe } = sanitizeEnvVars(baseEnv as Record<string, string | undefined>, options);\n return {\n ...safe,\n HOME: typeof baseEnv.HOME === 'string' && baseEnv.HOME ? baseEnv.HOME : safe.HOME || '/tmp',\n };\n}\n"],"mappings":";;;;;AAKA,MAAM,2BAAqC;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,qBAAqB;AAQ3B,SAAS,kBAAkB,KAAsB;CAC/C,MAAM,WAAW,IAAI,aAAa;AAClC,QAAO,yBAAyB,MAAM,YAAY,QAAQ,KAAK,SAAS,CAAC;;AAG3E,SAAS,oBAAoB,KAAa,OAA8B;AACtE,KAAI,MAAM,SAAS,KAAK,CACtB,QAAO,wBAAwB,IAAI;AAErC,KAAI,MAAM,SAAS,mBACjB,QAAO,wBAAwB,IAAI,2BAA2B,mBAAmB;AAEnF,KAAI,wBAAwB,KAAK,MAAM,CACrC,QAAO,wBAAwB,IAAI;AAErC,QAAO;;AAGT,SAAgB,gBACd,KACA,SAIkB;CAClB,MAAM,OAA+B,EAAE;CACvC,MAAM,UAAoB,EAAE;CAC5B,MAAM,WAAqB,EAAE;CAE7B,MAAM,cAAc,IAAI,IAAI,SAAS,eAAe,EAAE,CAAC;CACvD,MAAM,gBAAgB,SAAS,iBAAiB,EAAE;AAElD,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,EAAE;AAC9C,MAAI,UAAU,KAAA,EAAW;AAEzB,MAAI,YAAY,IAAI,IAAI,EAAE;AACxB,QAAK,OAAO;AACZ;;AAMF,MAFE,kBAAkB,IAAI,IAAI,cAAc,MAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,EAE/D;AACb,WAAQ,KAAK,IAAI;AACjB;;EAGF,MAAM,UAAU,oBAAoB,KAAK,MAAM;AAC/C,MAAI,SAAS;AACX,YAAS,KAAK,QAAQ;AACtB;;AAGF,OAAK,OAAO;;AAGd,QAAO;EAAE;EAAM;EAAS;EAAU;;;;;AAMpC,SAAgB,mBACd,SACA,SACwB;CACxB,MAAM,EAAE,SAAS,gBAAgB,SAA+C,QAAQ;AACxF,QAAO;EACL,GAAG;EACH,MAAM,OAAO,QAAQ,SAAS,YAAY,QAAQ,OAAO,QAAQ,OAAO,KAAK,QAAQ;EACtF"}
|
|
@@ -30,9 +30,9 @@ export type SandboxConfig = {
|
|
|
30
30
|
mode: SandboxMode;
|
|
31
31
|
/** Directories where file operations and command execution are allowed. */
|
|
32
32
|
allowedRoots: string[];
|
|
33
|
-
/** Maximum execution time for
|
|
33
|
+
/** Maximum execution time for commands (ms). */
|
|
34
34
|
maxExecutionTimeMs: number;
|
|
35
|
-
/** Maximum output size from
|
|
35
|
+
/** Maximum output size from commands (bytes). */
|
|
36
36
|
maxOutputBytes: number;
|
|
37
37
|
/** Paths that are always blocked regardless of allowedRoots. */
|
|
38
38
|
blockedPaths?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/agent/sandbox/types.ts"],"sourcesContent":["/**\n * Sandbox type definitions for Phase 1 process-level isolation.\n */\n\n/** Controls how strictly the sandbox enforces isolation. */\nexport type SandboxMode = 'off' | 'basic' | 'container';\n\n/** Result of path validation against the sandbox policy. */\nexport type PathValidationResult = {\n allowed: boolean;\n /** Canonical (symlink-resolved) path after validation. */\n canonicalPath?: string;\n reason?: string;\n};\n\n/** Result of command validation against injection patterns. */\nexport type CommandValidationResult = {\n allowed: boolean;\n reason?: string;\n /** Risk severity when blocked. */\n severity?: 'critical' | 'high' | 'medium';\n};\n\n/** Aggregated result from the exec policy layer. */\nexport type ExecPolicyResult = {\n allowed: boolean;\n reason?: string;\n sanitizedEnv: Record<string, string>;\n effectiveCwd: string;\n timeoutMs: number;\n};\n\n/** Sandbox configuration for an agent or workspace. */\nexport type SandboxConfig = {\n mode: SandboxMode;\n /** Directories where file operations and command execution are allowed. */\n allowedRoots: string[];\n /** Maximum execution time for
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/agent/sandbox/types.ts"],"sourcesContent":["/**\n * Sandbox type definitions for Phase 1 process-level isolation.\n */\n\n/** Controls how strictly the sandbox enforces isolation. */\nexport type SandboxMode = 'off' | 'basic' | 'container';\n\n/** Result of path validation against the sandbox policy. */\nexport type PathValidationResult = {\n allowed: boolean;\n /** Canonical (symlink-resolved) path after validation. */\n canonicalPath?: string;\n reason?: string;\n};\n\n/** Result of command validation against injection patterns. */\nexport type CommandValidationResult = {\n allowed: boolean;\n reason?: string;\n /** Risk severity when blocked. */\n severity?: 'critical' | 'high' | 'medium';\n};\n\n/** Aggregated result from the exec policy layer. */\nexport type ExecPolicyResult = {\n allowed: boolean;\n reason?: string;\n sanitizedEnv: Record<string, string>;\n effectiveCwd: string;\n timeoutMs: number;\n};\n\n/** Sandbox configuration for an agent or workspace. */\nexport type SandboxConfig = {\n mode: SandboxMode;\n /** Directories where file operations and command execution are allowed. */\n allowedRoots: string[];\n /** Maximum execution time for commands (ms). */\n maxExecutionTimeMs: number;\n /** Maximum output size from commands (bytes). */\n maxOutputBytes: number;\n /** Paths that are always blocked regardless of allowedRoots. */\n blockedPaths?: string[];\n /** Env var names to pass through even if they match secret heuristics. */\n allowedEnvVars?: string[];\n};\n\n/** Default sandbox configuration. */\nexport const DEFAULT_SANDBOX_CONFIG: SandboxConfig = {\n mode: 'basic',\n allowedRoots: [],\n maxExecutionTimeMs: 300_000,\n maxOutputBytes: 50 * 1024,\n};\n"],"mappings":";;AAgDA,MAAa,yBAAwC;CACnD,MAAM;CACN,cAAc,EAAE;CAChB,oBAAoB;CACpB,gBAAgB,KAAK;CACtB"}
|
|
@@ -51,7 +51,7 @@ async function runBtwQuery(opts) {
|
|
|
51
51
|
"You are answering an ephemeral /btw side question about the current conversation.",
|
|
52
52
|
"Use the conversation only as background context.",
|
|
53
53
|
"Answer only the side question. Do not continue or complete any unfinished task from the conversation.",
|
|
54
|
-
"Do not use tools,
|
|
54
|
+
"Do not use tools, commands, or file writes unless the question explicitly requires a tiny code snippet.",
|
|
55
55
|
"If the question can be answered briefly, answer briefly."
|
|
56
56
|
].join("\n"),
|
|
57
57
|
"",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport { resolveModel } from '../../providers/index.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction textFromCompleteContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n return content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c?.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('');\n}\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools,
|
|
1
|
+
{"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport { resolveModel } from '../../providers/index.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction textFromCompleteContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n return content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c?.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('');\n}\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, commands, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: 2048,\n temperature: 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = textFromCompleteContent((out as { content?: unknown }).content).trim();\n return text ? { text } : { text: '', error: 'No text returned from model.' };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, sessionKey: opts.sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;;gBAKwD;AAIxD,SAAS,wBAAwB,SAA0B;AACzD,KAAI,OAAO,YAAY,SACrB,QAAO;AAET,KAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,QAAO;AAET,QAAO,QACJ,QACE,MACC,GAAG,SAAS,UAAU,OAAQ,EAAyB,SAAS,SACnE,CACA,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;;AAGb,SAAS,qBAAqB,UAAkC;AAC9D,QAAO,SACJ,KAAK,MAAM;EACV,MAAM,OAAO,EAAE;EACf,IAAI,OAAO;EACX,MAAM,MAAM,wBAAwB,EAAE;AACtC,MAAI,OAAO,QAAQ,SACjB,QAAO;WACE,MAAM,QAAQ,IAAI,CAC3B,QAAO,IACJ,QAAQ,MAA2C,EAAE,SAAS,OAAO,CACrE,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,KAAK;EAEf,MAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,KAAK,SAAS,MAAO,GAAG,KAAK,MAAM,GAAG,IAAK,CAAC,KAAK;GACxD,CACD,KAAK,OAAO;;;AAIjB,eAAsB,YAAY,MAMY;CAC5C,MAAM,IAAI,KAAK,SAAS,MAAM;AAC9B,KAAI,CAAC,EACH,QAAO;EAAE,MAAM;EAAI,OAAO;EAAmB;CAE/C,MAAM,WAAW,MAAM,KAAK,aAAa,KAAK,KAAK,WAAW;CAC9D,MAAM,WAAW,KAAK;CACtB,IAAI;AACJ,KAAI;AACF,UAAQ,aAAa,SAAS;UACvB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK;GAAU,cAAc;GAAI,EAAE,iCAAiC;AACpF,SAAO;GAAE,MAAM;GAAI,OAAO,4BAA4B;GAAY;;CAGpE,MAAM,aAAa,qBAAqB,SAAS,MAAM,IAAI,CAAC;CAqB5D,MAAM,cAA2B;EAAE,MAAM;EAAQ,SAZ9B;GARC;IAClB;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAGM;GACX;GACA;GACA;GACA,cAAc;GACd;GACA;GACA;GACA;GACD,CAAC,KAAK,KAE6D;EAAE,WAAW,KAAK,KAAK;EAAE;CAC7F,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAO;AAC9D,KAAI;EAMF,MAAM,OAAO,yBAAyB,MALpB,SAAS,OAAO,EAAE,UAAU,CAAC,YAAY,EAAE,EAAE;GAC7D,WAAW;GACX,aAAa;GACb,QAAQ,WAAW;GACpB,CAAC,EACkE,QAAQ,CAAC,MAAM;AACnF,SAAO,OAAO,EAAE,MAAM,GAAG;GAAE,MAAM;GAAI,OAAO;GAAgC;UACrE,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK,YAAY,KAAK;GAAY,cAAc;GAAI,EAAE,kBAAkB;AACxF,SAAO;GAAE,MAAM;GAAI,OAAO;GAAI;WACtB;AACR,eAAa,UAAU"}
|
|
@@ -4,6 +4,9 @@ import "../../session/index.js";
|
|
|
4
4
|
import { buildDirectUserMessageContent } from "./build-direct-message-content.js";
|
|
5
5
|
import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
|
|
6
6
|
//#region src/agent/service/process-direct-one-shot.ts
|
|
7
|
+
function isReviewOutput(value) {
|
|
8
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && value.type === "review";
|
|
9
|
+
}
|
|
7
10
|
async function runProcessDirect(deps, input) {
|
|
8
11
|
const { channel, chatId } = await deps.resolveSessionEndpoint(input.sessionKey);
|
|
9
12
|
deps.initSessionContext(input.sessionKey, channel, chatId);
|
|
@@ -30,11 +33,12 @@ async function runProcessDirect(deps, input) {
|
|
|
30
33
|
const trimmed = slash.aggregatedText.trim();
|
|
31
34
|
if (trimmed) await deps.sessionStore.appendTranscriptMessage(input.sessionKey, {
|
|
32
35
|
role: "assistant",
|
|
33
|
-
content: [{
|
|
36
|
+
content: isReviewOutput(slash.metadata?.review) ? [slash.metadata.review] : [{
|
|
34
37
|
type: "text",
|
|
35
38
|
text: trimmed
|
|
36
39
|
}],
|
|
37
|
-
timestamp: Date.now()
|
|
40
|
+
timestamp: Date.now(),
|
|
41
|
+
...slash.metadata && Object.keys(slash.metadata).length > 0 ? { metadata: slash.metadata } : {}
|
|
38
42
|
});
|
|
39
43
|
return slash.aggregatedText ?? "";
|
|
40
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-direct-one-shot.js","names":[],"sources":["../../../../src/agent/service/process-direct-one-shot.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { initSessionTurn } from '../../session/index.js';\nimport {\n buildDirectUserMessageContent,\n} from './build-direct-message-content.js';\nimport type { ProcessDirectStreamLog } from './process-direct-streaming.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type RunProcessDirectDeps = {\n log: ProcessDirectStreamLog;\n config: Config;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => void;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentManager: AgentInstanceGateway;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n};\n\nexport async function runProcessDirect(\n deps: RunProcessDirectDeps,\n input: {\n content: string;\n sessionKey: string;\n attachments?: InboundAttachmentInput[];\n thinking?: string;\n },\n): Promise<string> {\n const { channel, chatId } = await deps.resolveSessionEndpoint(input.sessionKey);\n deps.initSessionContext(input.sessionKey, channel, chatId);\n\n try {\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n const turn = await initSessionTurn({\n cfg: deps.config,\n sessionKey: input.sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n return turn.ackMessage;\n }\n turnBody = turn.bodyStripped;\n\n await hydratePerTurnState(deps, input.sessionKey, input.thinking);\n const prepared = await deps.prepareInboundAttachments(input.sessionKey, input.attachments);\n\n const slash = await tryRunSlashCommand(\n deps,\n { sessionKey: input.sessionKey, channel, chatId },\n turnBody,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n const trimmed = slash.aggregatedText.trim();\n if (trimmed) {\n await deps.sessionStore.appendTranscriptMessage(input.sessionKey, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n }\n return slash.aggregatedText ?? '';\n }\n\n const textForDirect = turnBody.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(turnBody)\n : turnBody;\n const userMessage = await buildDirectUserMessageContent({\n content: textForDirect,\n attachments: prepared,\n sessionKey: input.sessionKey,\n config: deps.config,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n });\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n\n const result = await (async () => {\n try {\n return await runDirectAgentTurn(\n { ...deps, config: deps.config },\n {\n sessionKey: input.sessionKey,\n userMessage,\n },\n );\n } finally {\n clearPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n }\n })();\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(input.sessionKey, result.lastAssistantText);\n }\n\n return result.lastAssistantText ?? '';\n } finally {\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"process-direct-one-shot.js","names":[],"sources":["../../../../src/agent/service/process-direct-one-shot.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { initSessionTurn } from '../../session/index.js';\nimport {\n buildDirectUserMessageContent,\n} from './build-direct-message-content.js';\nimport type { ProcessDirectStreamLog } from './process-direct-streaming.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport type { ReviewOutput } from '../../review/review-types.js';\n\nexport type RunProcessDirectDeps = {\n log: ProcessDirectStreamLog;\n config: Config;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => void;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentManager: AgentInstanceGateway;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n};\n\nfunction isReviewOutput(value: unknown): value is ReviewOutput {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n && (value as { type?: unknown }).type === 'review';\n}\n\nexport async function runProcessDirect(\n deps: RunProcessDirectDeps,\n input: {\n content: string;\n sessionKey: string;\n attachments?: InboundAttachmentInput[];\n thinking?: string;\n },\n): Promise<string> {\n const { channel, chatId } = await deps.resolveSessionEndpoint(input.sessionKey);\n deps.initSessionContext(input.sessionKey, channel, chatId);\n\n try {\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n const turn = await initSessionTurn({\n cfg: deps.config,\n sessionKey: input.sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n return turn.ackMessage;\n }\n turnBody = turn.bodyStripped;\n\n await hydratePerTurnState(deps, input.sessionKey, input.thinking);\n const prepared = await deps.prepareInboundAttachments(input.sessionKey, input.attachments);\n\n const slash = await tryRunSlashCommand(\n deps,\n { sessionKey: input.sessionKey, channel, chatId },\n turnBody,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n const trimmed = slash.aggregatedText.trim();\n if (trimmed) {\n await deps.sessionStore.appendTranscriptMessage(input.sessionKey, {\n role: 'assistant',\n content: isReviewOutput(slash.metadata?.review)\n ? [slash.metadata.review]\n : [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n ...(slash.metadata && Object.keys(slash.metadata).length > 0\n ? { metadata: slash.metadata }\n : {}),\n } as AgentMessage);\n }\n return slash.aggregatedText ?? '';\n }\n\n const textForDirect = turnBody.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(turnBody)\n : turnBody;\n const userMessage = await buildDirectUserMessageContent({\n content: textForDirect,\n attachments: prepared,\n sessionKey: input.sessionKey,\n config: deps.config,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n });\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n\n const result = await (async () => {\n try {\n return await runDirectAgentTurn(\n { ...deps, config: deps.config },\n {\n sessionKey: input.sessionKey,\n userMessage,\n },\n );\n } finally {\n clearPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n }\n })();\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(input.sessionKey, result.lastAssistantText);\n }\n\n return result.lastAssistantText ?? '';\n } finally {\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":";;;;;;AAiDA,SAAS,eAAe,OAAuC;AAC7D,QAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,IAC9D,MAA6B,SAAS;;AAG9C,eAAsB,iBACpB,MACA,OAMiB;CACjB,MAAM,EAAE,SAAS,WAAW,MAAM,KAAK,uBAAuB,MAAM,WAAW;AAC/E,MAAK,mBAAmB,MAAM,YAAY,SAAS,OAAO;AAE1D,KAAI;EACF,IAAI,WAAW,MAAM;EACrB,IAAI,uBAAuB;EAC3B,MAAM,OAAO,MAAM,gBAAgB;GACjC,KAAK,KAAK;GACV,YAAY,MAAM;GAClB,MAAM,MAAM;GACZ,cAAc,KAAK;GACpB,CAAC;AACF,yBAAuB,KAAK;AAC5B,MAAI,KAAK,aAAa,KAAK,WACzB,QAAO,KAAK;AAEd,aAAW,KAAK;AAEhB,QAAM,oBAAoB,MAAM,MAAM,YAAY,MAAM,SAAS;EACjE,MAAM,WAAW,MAAM,KAAK,0BAA0B,MAAM,YAAY,MAAM,YAAY;EAE1F,MAAM,QAAQ,MAAM,mBAClB,MACA;GAAE,YAAY,MAAM;GAAY;GAAS;GAAQ,EACjD,UACA,EAAE,mBAAmB,sBAAsB,CAC5C;AACD,MAAI,MAAM,SAAS;GACjB,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,OAAI,QACF,OAAM,KAAK,aAAa,wBAAwB,MAAM,YAAY;IAChE,MAAM;IACN,SAAS,eAAe,MAAM,UAAU,OAAO,GAC3C,CAAC,MAAM,SAAS,OAAO,GACvB,CAAC;KAAE,MAAM;KAAQ,MAAM;KAAS,CAAC;IACrC,WAAW,KAAK,KAAK;IACrB,GAAI,MAAM,YAAY,OAAO,KAAK,MAAM,SAAS,CAAC,SAAS,IACvD,EAAE,UAAU,MAAM,UAAU,GAC5B,EAAE;IACP,CAAiB;AAEpB,UAAO,MAAM,kBAAkB;;EAMjC,MAAM,cAAc,MAAM,8BAA8B;GACtD,SAJoB,SAAS,WAAW,CAAC,WAAW,UAAU,GAC5D,KAAK,aAAa,oBAAoB,SAAS,GAC/C;GAGF,aAAa;GACb,YAAY,MAAM;GAClB,QAAQ,KAAK;GACb,cAAc,KAAK;GACnB,cAAc,KAAK;GACpB,CAAC;EAEF,MAAM,qBAAqB;AAC3B,kCAAgC,MAAM,YAAY,mBAAmB;EAErE,MAAM,SAAS,OAAO,YAAY;AAChC,OAAI;AACF,WAAO,MAAM,mBACX;KAAE,GAAG;KAAM,QAAQ,KAAK;KAAQ,EAChC;KACE,YAAY,MAAM;KAClB;KACD,CACF;aACO;AACR,sCAAkC,MAAM,YAAY,mBAAmB;;MAEvE;AAEJ,MAAI,OAAO,kBACT,MAAK,iBAAiB,MAAM,YAAY,OAAO,kBAAkB;AAGnE,SAAO,OAAO,qBAAqB;WAC3B;AACR,OAAK,yBAAyB"}
|
|
@@ -12,18 +12,23 @@ import { isSessionSourceBinding } from "../source-context/types.js";
|
|
|
12
12
|
import { AsyncQueue } from "./async-queue.js";
|
|
13
13
|
import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
|
|
14
14
|
//#region src/agent/service/process-direct-streaming.ts
|
|
15
|
-
function
|
|
15
|
+
function isReviewOutput(value) {
|
|
16
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && value.type === "review";
|
|
17
|
+
}
|
|
18
|
+
function makeAssistantReceiptMessage(text, metadata) {
|
|
19
|
+
const review = isReviewOutput(metadata?.review) ? metadata.review : void 0;
|
|
16
20
|
return {
|
|
17
21
|
role: "assistant",
|
|
18
|
-
content: [{
|
|
22
|
+
content: review ? [review] : [{
|
|
19
23
|
type: "text",
|
|
20
24
|
text
|
|
21
25
|
}],
|
|
22
|
-
timestamp: Date.now()
|
|
26
|
+
timestamp: Date.now(),
|
|
27
|
+
...metadata && Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
23
28
|
};
|
|
24
29
|
}
|
|
25
|
-
function pushAssistantReceipt(queue, text, runId) {
|
|
26
|
-
const message = makeAssistantReceiptMessage(text);
|
|
30
|
+
function pushAssistantReceipt(queue, text, runId, metadata) {
|
|
31
|
+
const message = makeAssistantReceiptMessage(text, metadata);
|
|
27
32
|
queue.push({
|
|
28
33
|
type: "message_start",
|
|
29
34
|
runId,
|
|
@@ -147,7 +152,7 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
147
152
|
const text = slash.aggregatedText.trim();
|
|
148
153
|
if (text) {
|
|
149
154
|
webchatSlashReceipt = text;
|
|
150
|
-
pushAssistantReceipt(queue, text, input.runId);
|
|
155
|
+
pushAssistantReceipt(queue, text, input.runId, slashCommandMetadata);
|
|
151
156
|
} else if (channel === "webchat") {
|
|
152
157
|
webchatSlashReceipt = "Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.";
|
|
153
158
|
pushAssistantReceipt(queue, webchatSlashReceipt, input.runId);
|
|
@@ -244,7 +249,7 @@ async function* runProcessDirectStreaming(deps, input) {
|
|
|
244
249
|
if (webchatSlashReceipt?.trim()) {
|
|
245
250
|
const assistantMsg = {
|
|
246
251
|
role: "assistant",
|
|
247
|
-
content: [{
|
|
252
|
+
content: isReviewOutput(slashCommandMetadata?.review) ? [slashCommandMetadata.review] : [{
|
|
248
253
|
type: "text",
|
|
249
254
|
text: webchatSlashReceipt.trim()
|
|
250
255
|
}],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-direct-streaming.js","names":[],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ImageContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../channels/attachments/inbound-persist.js';\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport {\n isVoiceLikeAttachment,\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n initSessionTurn,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { formatAgentRunErrorForClient } from '../client-error-format.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport { injectSourceContextIntoUserMessage } from '../source-context/injector.js';\nimport { isSessionSourceBinding, type AgentSourceContextResolver } from '../source-context/types.js';\n\nimport { AsyncQueue } from './async-queue.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type DirectStreamInboundAttachment = InboundAttachmentInput;\n\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentInstanceGateway;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<MediaRef[] | undefined>;\n buildTranscriptUserMessage: (\n content: string,\n prepared: MediaRef[] | undefined,\n sessionKey: string,\n ) => Promise<TranscriptUserMessage>;\n recordPersistentGoalStreamOutcome?: (\n sessionKey: string,\n outcome: { skipPersistentGoalPostTurn: boolean },\n ) => void;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n /** Disk-only transcript sync (slash receipt already streamed as tokens). */\n reloadWebchatTranscript?: (sessionKey: string) => void;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; uri: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n runId?: string;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nfunction makeAssistantReceiptMessage(text: string): AgentMessage {\n return {\n role: 'assistant',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n } as AgentMessage;\n}\n\nfunction pushAssistantReceipt(\n queue: AsyncQueue<ProcessDirectStreamingSseEvent>,\n text: string,\n runId?: string,\n): void {\n const message = makeAssistantReceiptMessage(text);\n queue.push({ type: 'message_start', runId, message });\n queue.push({ type: 'message_update', runId, message });\n queue.push({ type: 'message_end', runId, message });\n}\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const sessionKey = input.sessionKey ?? 'agent:main:main';\n const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);\n const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);\n\n const queue = new AsyncQueue<ProcessDirectStreamingSseEvent>();\n let reasoningLevel: ReasoningLevel = 'stream';\n\n const pushVisible = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n queue.push(visible);\n }\n };\n\n const formatStreamError = (raw: string): string => {\n let provider: string | undefined;\n let modelRef: string | undefined;\n try {\n const resolved = deps.modelManager.getResolvedModelForSession(sessionKey);\n provider = resolved.provider;\n modelRef = deps.modelManager.getModelForSession(sessionKey);\n } catch {\n /* ignore — format without provider context */\n }\n return formatAgentRunErrorForClient(raw, { provider, modelRef });\n };\n\n if (channel === 'webchat') {\n deps.registerWebchatSsePublisher(sessionKey, pushVisible);\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n let inboundVoice = false;\n let ranSlashCommand = false;\n let mergedUserText = input.content;\n let webchatSlashReceipt: string | undefined;\n let slashCommandMetadata: Record<string, unknown> | undefined;\n\n const taskPromise = (async () => {\n try {\n const cfg = deps.getConfig();\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n if (cfg) {\n const turn = await initSessionTurn({\n cfg,\n sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n ranSlashCommand = true;\n webchatSlashReceipt = turn.ackMessage;\n pushAssistantReceipt(queue, turn.ackMessage, input.runId);\n return;\n }\n turnBody = turn.bodyStripped;\n if (turn.isNewSession) {\n deps.log.debug(\n {\n sessionKey,\n sessionId: turn.sessionId,\n previousSessionId: turn.previousSessionId,\n resetTriggered: turn.resetTriggered,\n staleRollover: turn.staleRollover,\n },\n 'Session reset boundary at direct turn start',\n );\n }\n }\n\n await hydratePerTurnState(deps, sessionKey, input.thinking);\n {\n const defReason = 'stream' as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(deps.sessionConfigStore, sessionKey, defReason);\n }\n\n const prepared = await deps.prepareInboundAttachments(sessionKey, input.attachments);\n\n const sttCfg = mergeSttConfigFromAppConfig(deps.getConfig()?.tools?.media?.audio, deps.getConfig()?.tools?.media);\n const voiceMerge = await mergeVoiceTranscriptsIntoUserText(prepared, turnBody, sttCfg);\n mergedUserText = voiceMerge.text;\n inboundVoice = voiceMerge.inboundVoice;\n\n if (inboundVoice) {\n const transcriptParts = [\n voiceMerge.voiceTranscripts.filter(Boolean).join('\\n'),\n turnBody.trim(),\n ].filter(Boolean);\n const voiceMedia = (prepared ?? []).filter(isVoiceLikeAttachment).map((att) => ({\n uri: att.uri,\n mimeType: att.mimeType,\n name: att.name,\n }));\n pushVisible({\n type: 'user_transcript',\n text: transcriptParts.join('\\n\\n'),\n media: voiceMedia,\n });\n }\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n void abortEmbeddedRun(sessionKey);\n queue.close();\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n return;\n }\n signal.addEventListener('abort', armAbort, { once: true });\n }\n\n const slash = await tryRunSlashCommand(\n deps,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n inboundMetadata: context.metadata,\n },\n mergedUserText,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n ranSlashCommand = true;\n slashCommandMetadata = slash.metadata;\n const text = slash.aggregatedText.trim();\n if (text) {\n webchatSlashReceipt = text;\n pushAssistantReceipt(queue, text, input.runId);\n } else if (channel === 'webchat') {\n webchatSlashReceipt =\n 'Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.';\n pushAssistantReceipt(queue, webchatSlashReceipt, input.runId);\n }\n const workflowRun = slash.metadata?.workflowRun;\n if (workflowRun && typeof workflowRun === 'object') {\n queue.push({\n type: 'workflow_run_started',\n runId: input.runId,\n payload: { workflowRun },\n });\n }\n return;\n }\n\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n const userMessage = await deps.buildTranscriptUserMessage(textForAgent, prepared, sessionKey);\n let sourceEnrichedUserMessage: AgentMessage = userMessage;\n let sourceImages: ImageContent[] | undefined;\n if (deps.sourceContextResolver) {\n const metadata = await deps.sessionStore.getMetadata(sessionKey).catch(() => null);\n const sourceBinding = metadata?.customData && typeof metadata.customData === 'object'\n ? (metadata.customData as Record<string, unknown>).sourceBinding\n : undefined;\n if (isSessionSourceBinding(sourceBinding)) {\n const sourceContext = await deps.sourceContextResolver(sourceBinding, sessionKey);\n sourceEnrichedUserMessage = injectSourceContextIntoUserMessage(userMessage, sourceContext);\n sourceImages = sourceContext?.images;\n }\n }\n\n if (channel === 'webchat') {\n pushVisible({\n type: 'user_message',\n timestamp: userMessage.timestamp ?? Date.now(),\n content: readAgentMessageContent(userMessage),\n media: userMessage.media,\n });\n if (textForAgent.trim()) {\n deps.enqueueProvisionalSessionTitle?.(sessionKey, textForAgent);\n }\n }\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n\n try {\n const result = await runDirectAgentTurn(\n {\n sessionStore: deps.sessionStore,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n config: deps.getConfig(),\n },\n {\n sessionKey,\n userMessage: sourceEnrichedUserMessage,\n abortSignal: signal,\n sourceImages,\n runId: input.runId,\n onEvent: (embeddedEvent) => {\n const event = { ...embeddedEvent };\n if (event.type === 'error' && typeof event.content === 'string') {\n event.content = formatStreamError(event.content);\n }\n pushVisible(event);\n },\n },\n );\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(sessionKey, result.lastAssistantText);\n }\n if (!result.ok && result.errorMessage && !abortHandled) {\n pushVisible({ type: 'error', content: formatStreamError(result.errorMessage) });\n }\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n }\n } catch (err) {\n if (!abortHandled) {\n const em = err instanceof Error ? err.message : String(err);\n pushVisible({ type: 'error', content: formatStreamError(em) });\n }\n } finally {\n if (!userAborted && channel === 'webchat') {\n try {\n const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n queue.push(ttsAudioEvent);\n }\n } catch (ttsErr) {\n deps.log.warn({ err: ttsErr, sessionKey }, 'Failed to emit TTS audio before stream close');\n }\n }\n queue.close();\n }\n })();\n\n try {\n for await (const event of queue) {\n yield event;\n }\n await taskPromise;\n\n if (channel === 'webchat' && ranSlashCommand) {\n try {\n const userMsg = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: mergedUserText }],\n timestamp: Date.now(),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, userMsg);\n if (webchatSlashReceipt?.trim()) {\n const assistantMsg = {\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text: webchatSlashReceipt.trim() }],\n timestamp: Date.now(),\n ...(slashCommandMetadata && Object.keys(slashCommandMetadata).length > 0\n ? { metadata: slashCommandMetadata }\n : {}),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, assistantMsg);\n }\n deps.reloadWebchatTranscript?.(sessionKey);\n } catch (err) {\n deps.log.warn({ err, sessionKey }, 'Failed to persist webchat slash command receipt');\n }\n }\n\n deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });\n } finally {\n if (channel === 'webchat') {\n deps.unregisterWebchatSsePublisher(sessionKey);\n }\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"process-direct-streaming.js","names":[],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ImageContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../channels/attachments/inbound-persist.js';\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport {\n isVoiceLikeAttachment,\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n initSessionTurn,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { formatAgentRunErrorForClient } from '../client-error-format.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport { injectSourceContextIntoUserMessage } from '../source-context/injector.js';\nimport { isSessionSourceBinding, type AgentSourceContextResolver } from '../source-context/types.js';\nimport type { ReviewOutput } from '../../review/review-types.js';\n\nimport { AsyncQueue } from './async-queue.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type DirectStreamInboundAttachment = InboundAttachmentInput;\n\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentInstanceGateway;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<MediaRef[] | undefined>;\n buildTranscriptUserMessage: (\n content: string,\n prepared: MediaRef[] | undefined,\n sessionKey: string,\n ) => Promise<TranscriptUserMessage>;\n recordPersistentGoalStreamOutcome?: (\n sessionKey: string,\n outcome: { skipPersistentGoalPostTurn: boolean },\n ) => void;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n /** Disk-only transcript sync (slash receipt already streamed as tokens). */\n reloadWebchatTranscript?: (sessionKey: string) => void;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; uri: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n runId?: string;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nfunction isReviewOutput(value: unknown): value is ReviewOutput {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n && (value as { type?: unknown }).type === 'review';\n}\n\nfunction makeAssistantReceiptMessage(text: string, metadata?: Record<string, unknown>): AgentMessage {\n const review = isReviewOutput(metadata?.review) ? metadata.review : undefined;\n return {\n role: 'assistant',\n content: review ? [review] : [{ type: 'text', text }],\n timestamp: Date.now(),\n ...(metadata && Object.keys(metadata).length > 0 ? { metadata } : {}),\n } as AgentMessage;\n}\n\nfunction pushAssistantReceipt(\n queue: AsyncQueue<ProcessDirectStreamingSseEvent>,\n text: string,\n runId?: string,\n metadata?: Record<string, unknown>,\n): void {\n const message = makeAssistantReceiptMessage(text, metadata);\n queue.push({ type: 'message_start', runId, message });\n queue.push({ type: 'message_update', runId, message });\n queue.push({ type: 'message_end', runId, message });\n}\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const sessionKey = input.sessionKey ?? 'agent:main:main';\n const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);\n const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);\n\n const queue = new AsyncQueue<ProcessDirectStreamingSseEvent>();\n let reasoningLevel: ReasoningLevel = 'stream';\n\n const pushVisible = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n queue.push(visible);\n }\n };\n\n const formatStreamError = (raw: string): string => {\n let provider: string | undefined;\n let modelRef: string | undefined;\n try {\n const resolved = deps.modelManager.getResolvedModelForSession(sessionKey);\n provider = resolved.provider;\n modelRef = deps.modelManager.getModelForSession(sessionKey);\n } catch {\n /* ignore — format without provider context */\n }\n return formatAgentRunErrorForClient(raw, { provider, modelRef });\n };\n\n if (channel === 'webchat') {\n deps.registerWebchatSsePublisher(sessionKey, pushVisible);\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n let inboundVoice = false;\n let ranSlashCommand = false;\n let mergedUserText = input.content;\n let webchatSlashReceipt: string | undefined;\n let slashCommandMetadata: Record<string, unknown> | undefined;\n\n const taskPromise = (async () => {\n try {\n const cfg = deps.getConfig();\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n if (cfg) {\n const turn = await initSessionTurn({\n cfg,\n sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n ranSlashCommand = true;\n webchatSlashReceipt = turn.ackMessage;\n pushAssistantReceipt(queue, turn.ackMessage, input.runId);\n return;\n }\n turnBody = turn.bodyStripped;\n if (turn.isNewSession) {\n deps.log.debug(\n {\n sessionKey,\n sessionId: turn.sessionId,\n previousSessionId: turn.previousSessionId,\n resetTriggered: turn.resetTriggered,\n staleRollover: turn.staleRollover,\n },\n 'Session reset boundary at direct turn start',\n );\n }\n }\n\n await hydratePerTurnState(deps, sessionKey, input.thinking);\n {\n const defReason = 'stream' as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(deps.sessionConfigStore, sessionKey, defReason);\n }\n\n const prepared = await deps.prepareInboundAttachments(sessionKey, input.attachments);\n\n const sttCfg = mergeSttConfigFromAppConfig(deps.getConfig()?.tools?.media?.audio, deps.getConfig()?.tools?.media);\n const voiceMerge = await mergeVoiceTranscriptsIntoUserText(prepared, turnBody, sttCfg);\n mergedUserText = voiceMerge.text;\n inboundVoice = voiceMerge.inboundVoice;\n\n if (inboundVoice) {\n const transcriptParts = [\n voiceMerge.voiceTranscripts.filter(Boolean).join('\\n'),\n turnBody.trim(),\n ].filter(Boolean);\n const voiceMedia = (prepared ?? []).filter(isVoiceLikeAttachment).map((att) => ({\n uri: att.uri,\n mimeType: att.mimeType,\n name: att.name,\n }));\n pushVisible({\n type: 'user_transcript',\n text: transcriptParts.join('\\n\\n'),\n media: voiceMedia,\n });\n }\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n void abortEmbeddedRun(sessionKey);\n queue.close();\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n return;\n }\n signal.addEventListener('abort', armAbort, { once: true });\n }\n\n const slash = await tryRunSlashCommand(\n deps,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n inboundMetadata: context.metadata,\n },\n mergedUserText,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n ranSlashCommand = true;\n slashCommandMetadata = slash.metadata;\n const text = slash.aggregatedText.trim();\n if (text) {\n webchatSlashReceipt = text;\n pushAssistantReceipt(queue, text, input.runId, slashCommandMetadata);\n } else if (channel === 'webchat') {\n webchatSlashReceipt =\n 'Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.';\n pushAssistantReceipt(queue, webchatSlashReceipt, input.runId);\n }\n const workflowRun = slash.metadata?.workflowRun;\n if (workflowRun && typeof workflowRun === 'object') {\n queue.push({\n type: 'workflow_run_started',\n runId: input.runId,\n payload: { workflowRun },\n });\n }\n return;\n }\n\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n const userMessage = await deps.buildTranscriptUserMessage(textForAgent, prepared, sessionKey);\n let sourceEnrichedUserMessage: AgentMessage = userMessage;\n let sourceImages: ImageContent[] | undefined;\n if (deps.sourceContextResolver) {\n const metadata = await deps.sessionStore.getMetadata(sessionKey).catch(() => null);\n const sourceBinding = metadata?.customData && typeof metadata.customData === 'object'\n ? (metadata.customData as Record<string, unknown>).sourceBinding\n : undefined;\n if (isSessionSourceBinding(sourceBinding)) {\n const sourceContext = await deps.sourceContextResolver(sourceBinding, sessionKey);\n sourceEnrichedUserMessage = injectSourceContextIntoUserMessage(userMessage, sourceContext);\n sourceImages = sourceContext?.images;\n }\n }\n\n if (channel === 'webchat') {\n pushVisible({\n type: 'user_message',\n timestamp: userMessage.timestamp ?? Date.now(),\n content: readAgentMessageContent(userMessage),\n media: userMessage.media,\n });\n if (textForAgent.trim()) {\n deps.enqueueProvisionalSessionTitle?.(sessionKey, textForAgent);\n }\n }\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n\n try {\n const result = await runDirectAgentTurn(\n {\n sessionStore: deps.sessionStore,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n config: deps.getConfig(),\n },\n {\n sessionKey,\n userMessage: sourceEnrichedUserMessage,\n abortSignal: signal,\n sourceImages,\n runId: input.runId,\n onEvent: (embeddedEvent) => {\n const event = { ...embeddedEvent };\n if (event.type === 'error' && typeof event.content === 'string') {\n event.content = formatStreamError(event.content);\n }\n pushVisible(event);\n },\n },\n );\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(sessionKey, result.lastAssistantText);\n }\n if (!result.ok && result.errorMessage && !abortHandled) {\n pushVisible({ type: 'error', content: formatStreamError(result.errorMessage) });\n }\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n }\n } catch (err) {\n if (!abortHandled) {\n const em = err instanceof Error ? err.message : String(err);\n pushVisible({ type: 'error', content: formatStreamError(em) });\n }\n } finally {\n if (!userAborted && channel === 'webchat') {\n try {\n const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n queue.push(ttsAudioEvent);\n }\n } catch (ttsErr) {\n deps.log.warn({ err: ttsErr, sessionKey }, 'Failed to emit TTS audio before stream close');\n }\n }\n queue.close();\n }\n })();\n\n try {\n for await (const event of queue) {\n yield event;\n }\n await taskPromise;\n\n if (channel === 'webchat' && ranSlashCommand) {\n try {\n const userMsg = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: mergedUserText }],\n timestamp: Date.now(),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, userMsg);\n if (webchatSlashReceipt?.trim()) {\n const assistantMsg = {\n role: 'assistant' as const,\n content: isReviewOutput(slashCommandMetadata?.review)\n ? [slashCommandMetadata.review]\n : [{ type: 'text' as const, text: webchatSlashReceipt.trim() }],\n timestamp: Date.now(),\n ...(slashCommandMetadata && Object.keys(slashCommandMetadata).length > 0\n ? { metadata: slashCommandMetadata }\n : {}),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, assistantMsg);\n }\n deps.reloadWebchatTranscript?.(sessionKey);\n } catch (err) {\n deps.log.warn({ err, sessionKey }, 'Failed to persist webchat slash command receipt');\n }\n }\n\n deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });\n } finally {\n if (channel === 'webchat') {\n deps.unregisterWebchatSsePublisher(sessionKey);\n }\n deps.endDirectRequestContext();\n }\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;
|
|
@@ -225,7 +227,8 @@ var AgentService = class {
|
|
|
225
227
|
getAutomationService: config.getAutomationService,
|
|
226
228
|
getWorkflowRunService: config.getWorkflowRunService,
|
|
227
229
|
onSkillsUpdated: config.onSkillsUpdated,
|
|
228
|
-
isWorkspaceTrusted: config.isWorkspaceTrusted
|
|
230
|
+
isWorkspaceTrusted: config.isWorkspaceTrusted,
|
|
231
|
+
getSelfVerifyPromptContext: (sessionKey, agentId) => [this.selfVerifyMiddleware.getPendingVerificationContext(sessionKey, agentId), this.turnDiffTracker.buildFinalGuardContext(sessionKey)].filter(Boolean).join("\n\n")
|
|
229
232
|
});
|
|
230
233
|
this.agentEventHandler = new AgentEventHandler({
|
|
231
234
|
progressManager: this.progressManager,
|
|
@@ -236,7 +239,8 @@ var AgentService = class {
|
|
|
236
239
|
selfVerifyMiddleware: this.selfVerifyMiddleware,
|
|
237
240
|
systemReminder: this.systemReminder,
|
|
238
241
|
toolUsageAnalyzer: this.toolUsageAnalyzer,
|
|
239
|
-
errorPatternMatcher: this.errorPatternMatcher
|
|
242
|
+
errorPatternMatcher: this.errorPatternMatcher,
|
|
243
|
+
turnDiffTracker: this.turnDiffTracker
|
|
240
244
|
});
|
|
241
245
|
this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(this.agentEventHandler);
|
|
242
246
|
this.sessionHydrator = new SessionHydrator({
|
|
@@ -325,7 +329,7 @@ var AgentService = class {
|
|
|
325
329
|
agentManager: this.agentManager,
|
|
326
330
|
sessionHydrator: this.sessionHydrator,
|
|
327
331
|
getConfig: () => this.effectiveAppConfig(),
|
|
328
|
-
getContextWindow: () => this.
|
|
332
|
+
getContextWindow: (sessionKey) => this.getContextWindowForSession(sessionKey)
|
|
329
333
|
});
|
|
330
334
|
this.outboundCoordinator = new OutboundCoordinator({
|
|
331
335
|
bus,
|
|
@@ -445,6 +449,7 @@ var AgentService = class {
|
|
|
445
449
|
minTurnsForVerification: 4,
|
|
446
450
|
resetOnVerification: true
|
|
447
451
|
});
|
|
452
|
+
this.turnDiffTracker = new TurnDiffTracker();
|
|
448
453
|
this.requestLimiter = new RequestLimiter({
|
|
449
454
|
maxRequestsPerTurn: 50,
|
|
450
455
|
warnThreshold: .8,
|
|
@@ -762,7 +767,7 @@ var AgentService = class {
|
|
|
762
767
|
this.agentEventHandler.handle(event, currentContext);
|
|
763
768
|
}
|
|
764
769
|
async checkAndCompact(sessionKey, messages) {
|
|
765
|
-
const contextWindow = this.
|
|
770
|
+
const contextWindow = this.getContextWindowForSession(sessionKey);
|
|
766
771
|
const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);
|
|
767
772
|
if (!prep.needsCompaction) return;
|
|
768
773
|
log.info({
|
|
@@ -782,8 +787,16 @@ var AgentService = class {
|
|
|
782
787
|
tokensAfter: result.tokensAfter
|
|
783
788
|
}, "Session compacted");
|
|
784
789
|
}
|
|
785
|
-
|
|
786
|
-
|
|
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
|
+
}
|
|
787
800
|
}
|
|
788
801
|
dispose() {
|
|
789
802
|
this.sessionTracker.dispose();
|