@xopcai/xopc 0.0.124 → 0.0.126
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 +56 -102
- package/README.zh-CN.md +51 -98
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/schema/config-schema.d.ts +6 -6
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
- package/dist/extensions/telegram/src/config-schema.d.ts +8 -8
- package/dist/extensions/telegram/src/inbound-processor.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/config-schema.d.ts +3 -3
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +41 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B5uSVAyJ.js → agent-avatar-dicebear-C70vgt60.js} +1 -1
- package/dist/gateway/static/root/assets/agents-DRYVahI-.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-D9-HW6cy.js +1 -0
- package/dist/gateway/static/root/assets/appearance-settings-BHlahIDL.js +1 -0
- package/dist/gateway/static/root/assets/apps-page-BdJ1eQg3.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-CXUGRp2i.js → archive-plugin-B-aoIt0w.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Cmiub3Wg.js → attachment-load-BD0G0FD5.js} +1 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-iJRVI7aZ.js +1 -0
- package/dist/gateway/static/root/assets/automations-page-D8hYQbkl.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-0Izz-N3p.js → binary-plugins-B0mNlVSz.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C1Kv0IkJ.js → block-editor-CJY9Lu5G.js} +3 -3
- package/dist/gateway/static/root/assets/browser-settings-page-CiWEO-Iy.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-Bgc8ag55.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-B1nIi0TC.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-BeA2aVor.js → channel-recipient-api-0sauApoC.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-BddPdqVo.js +1 -0
- package/dist/gateway/static/root/assets/code-editor-gbRgS14t.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-Ba2GDhSE.js +2 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-2rjzUu57.js → directory-picker-path-field-CiEc4KmY.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-4bvpk5wr.js +3 -0
- package/dist/gateway/static/root/assets/extension-debug-page-BbyczVZ3.js +1 -0
- package/dist/gateway/static/root/assets/extension-page-DLN1I3DR.js +1 -0
- package/dist/gateway/static/root/assets/extension-settings-page-D9VGHmYJ.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-BU5S0Sn7.js → fetch-5V96dUz3.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BIPGwrl1.js → gateway-config-api-BtIzObwp.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-KFsGGDhg.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-Cd0Zz30N.js → gateway-startup-retry-DEMad2wt.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-4PcF6Sh6.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-B482PSaG.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-B95KdGMj.js → goals-config-api-CVN7-pdM.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-CUdgJuDn.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-IYCH3-Pi.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-Cf_a2Bmz.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-CA2tR2vm.js +256 -0
- package/dist/gateway/static/root/assets/index-Ds3u0LOk.css +1 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-lKTg6St7.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BCporcNH.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-Bv4k6iGJ.js +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-BhIQU5yS.js → media-plugins-Bbi8g3N8.js} +1 -1
- package/dist/gateway/static/root/assets/messages-C22hxE1N.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-D68Pyow7.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-CuUMOiyv.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-Dx2N5zTk.js +2 -0
- package/dist/gateway/static/root/assets/page-tabs-Cp395RYD.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DGzet1d7.js → preview-open-alternatives-DqV5OM4m.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-sB8AzZi3.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-LBnJqWK2.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-Dbj0o6SC.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-Cqgb-jqa.js → save-bar-controls-BItmzAzI.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DVZcI8Yt.js → schedule-field-BQ8IjjCn.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Bj7sOxC8.js → schema-form-BRpD6x3d.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DhDZKspB.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BvXLMNL6.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BmXn-8N6.js → settings-collapsible-section-BKSPQM4R.js} +1 -1
- package/dist/gateway/static/root/assets/settings-form-section-BmyfthNQ.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-Cfm9UxAr.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-layout-Bls5tfbH.js +1 -0
- package/dist/gateway/static/root/assets/setup-status-panel-kQEsTGAs.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-IB1v8i86.js → share-preview-page-DbYZNAPb.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-BtR4B0kn.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-CJNVOWoK.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DcWDHrt3.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-BxQ384RC.js → system-settings-panel-DKDAojWM.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BkcpvFYO.js → typed-models-lib-BCCV4Kkp.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-Cd0FaEQ4.js → use-channel-catalog-NBqg3iFY.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-DQ7NnRSY.js → user-profile-settings-panel-suQ2x-LB.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-YKhVf7Ka.js → utils-DPGVr3tS.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-DaJASKFP.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-BrCZpAWh.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-BTkRVNAh.js +3 -0
- package/dist/gateway/static/root/assets/{workflow-start-dialog-BNJVCWoS.js → workflow-start-dialog-DAImVtdO.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-vyJ8wSXm.js +18 -0
- package/dist/gateway/static/root/index.html +8 -7
- package/dist/package.js +1 -1
- package/dist/src/activity/context.d.ts +11 -0
- package/dist/src/activity/context.js +36 -0
- package/dist/src/activity/context.js.map +1 -0
- package/dist/src/activity/emitter.d.ts +6 -0
- package/dist/src/activity/emitter.js +43 -0
- package/dist/src/activity/emitter.js.map +1 -0
- package/dist/src/activity/index.d.ts +3 -0
- package/dist/src/activity/index.js +3 -0
- package/dist/src/activity/service.d.ts +12 -0
- package/dist/src/activity/service.js +59 -0
- package/dist/src/activity/service.js.map +1 -0
- package/dist/src/activity/types.d.ts +109 -0
- package/dist/src/activity/types.js +1 -0
- package/dist/src/agent/agent-manager.d.ts +7 -0
- package/dist/src/agent/agent-manager.js +12 -5
- 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/project-context.js +2 -2
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +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/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.d.ts +2 -0
- package/dist/src/agent/orchestration/agent-event-handler.js +13 -0
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +22 -12
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/sandbox/command-validator.js +12 -0
- package/dist/src/agent/sandbox/command-validator.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -1
- package/dist/src/agent/service.js +24 -9
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +7 -0
- package/dist/src/agent/session/session-inspector.d.ts +5 -3
- package/dist/src/agent/session/session-inspector.js +17 -8
- package/dist/src/agent/session/session-inspector.js.map +1 -1
- package/dist/src/agent/skills/hub-pull.js +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/tool-manuals/index.js +15 -6
- package/dist/src/agent/tool-manuals/index.js.map +1 -1
- package/dist/src/agent/tool-manuals/xopc-use.d.ts +1 -0
- package/dist/src/agent/tool-manuals/xopc-use.js +204 -0
- package/dist/src/agent/tool-manuals/xopc-use.js.map +1 -0
- package/dist/src/agent/tools/apply-patch.js +116 -62
- package/dist/src/agent/tools/apply-patch.js.map +1 -1
- package/dist/src/agent/tools/concurrency.d.ts +8 -0
- package/dist/src/agent/tools/concurrency.js +31 -0
- package/dist/src/agent/tools/concurrency.js.map +1 -0
- package/dist/src/agent/tools/dreaming-tool.d.ts +2 -0
- package/dist/src/agent/tools/dreaming-tool.js +13 -10
- package/dist/src/agent/tools/dreaming-tool.js.map +1 -1
- package/dist/src/agent/tools/exec-command.js +4 -0
- package/dist/src/agent/tools/exec-command.js.map +1 -1
- package/dist/src/agent/tools/executor.d.ts +6 -4
- package/dist/src/agent/tools/executor.js +7 -4
- package/dist/src/agent/tools/executor.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +10 -0
- package/dist/src/agent/tools/factory.js +16 -1
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/find.js +2 -0
- package/dist/src/agent/tools/find.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.js +2 -2
- 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 +3 -0
- package/dist/src/agent/tools/index.js +4 -1
- package/dist/src/agent/tools/list-dir.js +2 -0
- package/dist/src/agent/tools/list-dir.js.map +1 -1
- package/dist/src/agent/tools/memory-tool.d.ts +1 -0
- package/dist/src/agent/tools/memory-tool.js +2 -2
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/agent/tools/metadata.d.ts +15 -0
- package/dist/src/agent/tools/metadata.js +26 -0
- package/dist/src/agent/tools/metadata.js.map +1 -0
- package/dist/src/agent/tools/read.js +2 -0
- package/dist/src/agent/tools/read.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/write.js +6 -0
- package/dist/src/agent/tools/write.js.map +1 -1
- package/dist/src/agent/tools/xopc-use-tool.d.ts +34 -0
- package/dist/src/agent/tools/xopc-use-tool.js +636 -0
- package/dist/src/agent/tools/xopc-use-tool.js.map +1 -0
- 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/agent-manifest/schema.d.ts +27 -27
- package/dist/src/automations/api/routes.js +1 -1
- package/dist/src/automations/domain/validation.d.ts +55 -55
- package/dist/src/automations/draft/automation-draft-validator.d.ts +25 -25
- package/dist/src/automations/service/action-executor.js +2 -2
- package/dist/src/browser/cdp-local-launcher.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +2 -2
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/builtins/config.js +1 -1
- package/dist/src/chat-commands/builtins/goal.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +2 -2
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/goal.js +3 -3
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/project.js +4 -4
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +14 -14
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/schema.d.ts +60 -60
- 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/daemon/launchd.js +1 -1
- package/dist/src/daemon/systemd.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/loader.js +2 -2
- package/dist/src/gateway/agents-admin.js +1 -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/lib/config-payload.d.ts +14 -14
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/middleware/log-context.js +8 -1
- package/dist/src/gateway/hono/middleware/log-context.js.map +1 -1
- package/dist/src/gateway/hono/routes/activity.d.ts +14 -0
- package/dist/src/gateway/hono/routes/activity.js +72 -0
- package/dist/src/gateway/hono/routes/activity.js.map +1 -0
- 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/goals.js +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/memory.js +2 -2
- package/dist/src/gateway/hono/routes/projects.js +903 -50
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +102 -2
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +2 -2
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +10 -0
- package/dist/src/gateway/service/sessions-api.js +31 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.js +28 -16
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-store.js +1 -1
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/goal-workflow-judge.js +1 -1
- package/dist/src/infra/ports.js +1 -1
- package/dist/src/infra/ssh-tunnel.js +1 -1
- package/dist/src/infra/update-check.js +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/migrations/runner.js +1 -1
- package/dist/src/notes/service.js +65 -6
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/notes/store.js +1 -1
- package/dist/src/projects/project-service.d.ts +2 -1
- package/dist/src/projects/project-service.js +59 -2
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.d.ts +2 -1
- package/dist/src/projects/project-store.js +43 -0
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +10 -0
- package/dist/src/projects/workspace.js +1 -1
- package/dist/src/routing/agent-session-key.js +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/resolve-tui-session-key.js +1 -1
- package/dist/src/session/client-history.d.ts +3 -0
- package/dist/src/session/client-history.js +24 -9
- package/dist/src/session/client-history.js.map +1 -1
- package/dist/src/session/init-session-turn.js +1 -1
- package/dist/src/session/resolve-session.js +1 -1
- package/dist/src/session/session-context-for-llm.d.ts +2 -0
- package/dist/src/session/session-context-for-llm.js +14 -1
- package/dist/src/session/session-context-for-llm.js.map +1 -1
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/store.js +3 -3
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/types.d.ts +4 -0
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/storage/sqlite/activity-repository.d.ts +8 -0
- package/dist/src/storage/sqlite/activity-repository.js +208 -0
- package/dist/src/storage/sqlite/activity-repository.js.map +1 -0
- package/dist/src/storage/sqlite/connection.js +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/migrations/022_work_items.sql +61 -0
- package/dist/src/storage/sqlite/migrations/023_work_item_attachments.sql +15 -0
- package/dist/src/storage/sqlite/migrations/024_activity_events.sql +62 -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/row-mappers.js +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +141 -0
- package/dist/src/storage/sqlite/session-repository.js +11 -0
- package/dist/src/storage/sqlite/session-repository.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +13 -0
- package/dist/src/tui/backends/embedded-backend.js +76 -7
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +13 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +53 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/chat-history.js +16 -3
- package/dist/src/tui/chat-history.js.map +1 -1
- package/dist/src/tui/clipboard-image.js +1 -1
- package/dist/src/tui/components/assistant-message.js +12 -2
- package/dist/src/tui/components/assistant-message.js.map +1 -1
- package/dist/src/tui/components/chat-log.d.ts +23 -3
- package/dist/src/tui/components/chat-log.js +68 -9
- package/dist/src/tui/components/chat-log.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/tui-bottom-bar.js +9 -5
- package/dist/src/tui/components/tui-bottom-bar.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-backend.d.ts +16 -0
- package/dist/src/tui/tui-command-formatters.js +2 -2
- package/dist/src/tui/tui-commands.d.ts +9 -0
- package/dist/src/tui/tui-commands.js +95 -6
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.d.ts +1 -0
- package/dist/src/tui/tui-picker-overlay.js +36 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/tui/tui-picker-services.d.ts +13 -0
- package/dist/src/tui/tui-run-state.js +11 -0
- package/dist/src/tui/tui-run-state.js.map +1 -1
- package/dist/src/tui/tui-run-status-format.d.ts +3 -0
- package/dist/src/tui/tui-run-status-format.js +33 -0
- package/dist/src/tui/tui-run-status-format.js.map +1 -0
- package/dist/src/tui/tui-session-actions.d.ts +5 -0
- package/dist/src/tui/tui-session-actions.js +24 -0
- package/dist/src/tui/tui-session-actions.js.map +1 -1
- package/dist/src/tui/tui-timeline-picker.d.ts +19 -0
- package/dist/src/tui/tui-timeline-picker.js +81 -0
- package/dist/src/tui/tui-timeline-picker.js.map +1 -0
- package/dist/src/tui/tui-timeline.d.ts +15 -0
- package/dist/src/tui/tui-timeline.js +65 -0
- package/dist/src/tui/tui-timeline.js.map +1 -0
- package/dist/src/tui/tui-tool-diff.d.ts +15 -0
- package/dist/src/tui/tui-tool-diff.js +178 -7
- package/dist/src/tui/tui-tool-diff.js.map +1 -1
- package/dist/src/tui/tui-types.d.ts +1 -0
- package/dist/src/tui/tui-types.js +1 -0
- package/dist/src/tui/tui-types.js.map +1 -1
- package/dist/src/tui/tui.js +31 -25
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/logger/index.js +1 -1
- package/dist/src/utils/logger/stats.d.ts +3 -3
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/work-items/agent-context.d.ts +7 -0
- package/dist/src/work-items/agent-context.js +75 -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 +35 -0
- package/dist/src/work-items/work-item-service.js +277 -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/draft/workflow-draft-service.js +1 -1
- 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 +4 -1
- package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
- package/package.json +5 -5
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-CYCONLIL.js +0 -1
- package/dist/gateway/static/root/assets/appearance-settings-Clw3ryKg.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +0 -1
- package/dist/gateway/static/root/assets/automation-suggestion-card-CU0-4nQ-.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BmQTM9Il.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +0 -1
- package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +0 -2
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
- package/dist/gateway/static/root/assets/dist-wO9a2_ID.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-DnHO686d.js +0 -1
- package/dist/gateway/static/root/assets/extension-debug-page-BGLRuWVN.js +0 -1
- package/dist/gateway/static/root/assets/extension-page-D978ePp9.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-B6Ih4Swx.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-B-KHKeXN.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-BTJzIl28.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-BJyEuLLm.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +0 -1
- package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +0 -1
- package/dist/gateway/static/root/assets/index-c_9TVr_C.js +0 -191
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BT8T4DyC.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +0 -2
- package/dist/gateway/static/root/assets/markdown-split-Dv66bO2b.js +0 -1
- package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-BnqYMWHR.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-layout-CdWZU1hd.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CWtPM7RK.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-BYD9BtOq.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +0 -18
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { checkFileSafety } from "../prompt/safety.js";
|
|
2
2
|
import { resolvePathUnderWorkspace } from "./tool-paths.js";
|
|
3
3
|
import { evaluateFilePolicy } from "../sandbox/exec-policy.js";
|
|
4
|
-
import { generateDiffString, normalizeToLF } from "./edit-diff.js";
|
|
4
|
+
import { detectLineEnding, generateDiffString, normalizeToLF, restoreLineEndings } from "./edit-diff.js";
|
|
5
5
|
import { dirname, relative } from "node:path";
|
|
6
6
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
@@ -129,7 +129,7 @@ function applyHunks(original, hunks, path) {
|
|
|
129
129
|
return content;
|
|
130
130
|
}
|
|
131
131
|
async function readExisting(path) {
|
|
132
|
-
return
|
|
132
|
+
return readFile(path, "utf-8");
|
|
133
133
|
}
|
|
134
134
|
async function assertWritable(workspace, path) {
|
|
135
135
|
const quick = checkFileSafety("write", path);
|
|
@@ -142,74 +142,128 @@ async function assertWritable(workspace, path) {
|
|
|
142
142
|
if (!policy.allowed) throw new Error(`Sandbox: ${policy.reason}`);
|
|
143
143
|
return resolvePathUnderWorkspace(path, workspace);
|
|
144
144
|
}
|
|
145
|
+
async function pathExists(path) {
|
|
146
|
+
try {
|
|
147
|
+
await stat(path);
|
|
148
|
+
return true;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (e.code === "ENOENT") return false;
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function buildPatchPlan(workspace, parsed) {
|
|
155
|
+
const changes = [];
|
|
156
|
+
const plannedTargets = /* @__PURE__ */ new Set();
|
|
157
|
+
const rememberTarget = (target) => {
|
|
158
|
+
if (plannedTargets.has(target)) throw new Error(`Patch touches the same target multiple times: ${relative(workspace, target)}`);
|
|
159
|
+
plannedTargets.add(target);
|
|
160
|
+
};
|
|
161
|
+
for (const op of parsed) {
|
|
162
|
+
if (op.kind === "add") {
|
|
163
|
+
const target = await assertWritable(workspace, op.path);
|
|
164
|
+
rememberTarget(target);
|
|
165
|
+
if (await pathExists(target)) throw new Error(`Add file target already exists: ${op.path}`);
|
|
166
|
+
const diff = generateDiffString("", op.content, op.path);
|
|
167
|
+
const counts = countDiff(diff);
|
|
168
|
+
changes.push({
|
|
169
|
+
kind: "add",
|
|
170
|
+
path: op.path,
|
|
171
|
+
absolutePath: target,
|
|
172
|
+
newContent: op.content,
|
|
173
|
+
...counts,
|
|
174
|
+
diff
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (op.kind === "delete") {
|
|
179
|
+
const target = await assertWritable(workspace, op.path);
|
|
180
|
+
rememberTarget(target);
|
|
181
|
+
const oldContent = await readExisting(target);
|
|
182
|
+
const diff = generateDiffString(oldContent, "", op.path);
|
|
183
|
+
const counts = countDiff(diff);
|
|
184
|
+
changes.push({
|
|
185
|
+
kind: "delete",
|
|
186
|
+
path: op.path,
|
|
187
|
+
absolutePath: target,
|
|
188
|
+
oldContent,
|
|
189
|
+
...counts,
|
|
190
|
+
diff
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const target = await assertWritable(workspace, op.path);
|
|
195
|
+
rememberTarget(target);
|
|
196
|
+
const oldContent = await readExisting(target);
|
|
197
|
+
const lineEnding = detectLineEnding(oldContent);
|
|
198
|
+
const newContent = restoreLineEndings(applyHunks(normalizeToLF(oldContent), op.hunks, op.path), lineEnding);
|
|
199
|
+
const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;
|
|
200
|
+
if (destination !== target) {
|
|
201
|
+
rememberTarget(destination);
|
|
202
|
+
if (await pathExists(destination)) throw new Error(`Move target already exists: ${op.moveTo}`);
|
|
203
|
+
}
|
|
204
|
+
const diff = generateDiffString(oldContent, newContent, op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path);
|
|
205
|
+
const counts = countDiff(diff);
|
|
206
|
+
changes.push({
|
|
207
|
+
kind: op.moveTo ? "move" : "update",
|
|
208
|
+
path: op.path,
|
|
209
|
+
absolutePath: target,
|
|
210
|
+
moveTo: op.moveTo,
|
|
211
|
+
absoluteMoveTo: op.moveTo ? destination : void 0,
|
|
212
|
+
oldContent,
|
|
213
|
+
newContent,
|
|
214
|
+
...counts,
|
|
215
|
+
diff
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return changes;
|
|
219
|
+
}
|
|
220
|
+
async function commitPatchPlan(changes) {
|
|
221
|
+
const committed = [];
|
|
222
|
+
try {
|
|
223
|
+
for (const change of changes) {
|
|
224
|
+
committed.push(change);
|
|
225
|
+
if (change.kind === "add") {
|
|
226
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
227
|
+
await writeFile(change.absolutePath, change.newContent ?? "", "utf-8");
|
|
228
|
+
} else if (change.kind === "delete") await rm(change.absolutePath);
|
|
229
|
+
else {
|
|
230
|
+
const destination = change.absoluteMoveTo ?? change.absolutePath;
|
|
231
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
232
|
+
await writeFile(destination, change.newContent ?? "", "utf-8");
|
|
233
|
+
if (destination !== change.absolutePath) await rm(change.absolutePath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
for (const change of committed.reverse()) try {
|
|
238
|
+
if (change.kind === "add") await rm(change.absolutePath, { force: true });
|
|
239
|
+
else if (change.kind === "delete") {
|
|
240
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
241
|
+
await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
242
|
+
} else if (change.absoluteMoveTo) {
|
|
243
|
+
await rm(change.absoluteMoveTo, { force: true });
|
|
244
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
245
|
+
await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
246
|
+
} else await writeFile(change.absolutePath, change.oldContent ?? "", "utf-8");
|
|
247
|
+
} catch {}
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
145
251
|
function createApplyPatchTool(workspace, options) {
|
|
146
252
|
return {
|
|
147
253
|
name: "apply_patch",
|
|
148
254
|
label: "Apply Patch",
|
|
149
255
|
description: "Apply a strict multi-file patch. Use this for code edits instead of shell redirection or write_file.",
|
|
150
256
|
parameters: ApplyPatchSchema,
|
|
257
|
+
mutatesWorkspace: true,
|
|
258
|
+
mutationScope: "workspace",
|
|
259
|
+
supportsParallel: false,
|
|
260
|
+
idempotent: false,
|
|
261
|
+
requiresExclusiveWorkspaceLock: true,
|
|
262
|
+
finalGuardRelevant: true,
|
|
151
263
|
async execute(_toolCallId, params) {
|
|
152
264
|
try {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
for (const op of parsed) {
|
|
156
|
-
if (op.kind === "add") {
|
|
157
|
-
const target = await assertWritable(workspace, op.path);
|
|
158
|
-
let exists = false;
|
|
159
|
-
try {
|
|
160
|
-
await stat(target);
|
|
161
|
-
exists = true;
|
|
162
|
-
} catch (e) {
|
|
163
|
-
if (e.code !== "ENOENT") throw e;
|
|
164
|
-
}
|
|
165
|
-
if (exists) throw new Error(`Add file target already exists: ${op.path}`);
|
|
166
|
-
await mkdir(dirname(target), { recursive: true });
|
|
167
|
-
await writeFile(target, op.content, "utf-8");
|
|
168
|
-
const diff = generateDiffString("", op.content, op.path);
|
|
169
|
-
const counts = countDiff(diff);
|
|
170
|
-
changes.push({
|
|
171
|
-
kind: "add",
|
|
172
|
-
path: op.path,
|
|
173
|
-
absolutePath: target,
|
|
174
|
-
...counts,
|
|
175
|
-
diff
|
|
176
|
-
});
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (op.kind === "delete") {
|
|
180
|
-
const target = await assertWritable(workspace, op.path);
|
|
181
|
-
const oldContent = await readExisting(target);
|
|
182
|
-
await rm(target);
|
|
183
|
-
const diff = generateDiffString(oldContent, "", op.path);
|
|
184
|
-
const counts = countDiff(diff);
|
|
185
|
-
changes.push({
|
|
186
|
-
kind: "delete",
|
|
187
|
-
path: op.path,
|
|
188
|
-
absolutePath: target,
|
|
189
|
-
...counts,
|
|
190
|
-
diff
|
|
191
|
-
});
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const target = await assertWritable(workspace, op.path);
|
|
195
|
-
const oldContent = await readExisting(target);
|
|
196
|
-
const newContent = applyHunks(oldContent, op.hunks, op.path);
|
|
197
|
-
const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;
|
|
198
|
-
await mkdir(dirname(destination), { recursive: true });
|
|
199
|
-
await writeFile(destination, newContent, "utf-8");
|
|
200
|
-
if (destination !== target) await rm(target);
|
|
201
|
-
const diff = generateDiffString(oldContent, newContent, op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path);
|
|
202
|
-
const counts = countDiff(diff);
|
|
203
|
-
changes.push({
|
|
204
|
-
kind: op.moveTo ? "move" : "update",
|
|
205
|
-
path: op.path,
|
|
206
|
-
absolutePath: target,
|
|
207
|
-
moveTo: op.moveTo,
|
|
208
|
-
absoluteMoveTo: op.moveTo ? destination : void 0,
|
|
209
|
-
...counts,
|
|
210
|
-
diff
|
|
211
|
-
});
|
|
212
|
-
}
|
|
265
|
+
const changes = await buildPatchPlan(workspace, parsePatch(params.patch ?? ""));
|
|
266
|
+
await commitPatchPlan(changes);
|
|
213
267
|
const diff = changes.map((change) => change.diff).join("\n");
|
|
214
268
|
const added = changes.reduce((sum, change) => sum + change.added, 0);
|
|
215
269
|
const removed = changes.reduce((sum, change) => sum + change.removed, 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-patch.js","names":[],"sources":["../../../../src/agent/tools/apply-patch.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\n\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\nimport { generateDiffString, normalizeToLF } from './edit-diff.js';\nimport { resolvePathUnderWorkspace } from './tool-paths.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst ApplyPatchSchema = Type.Object({\n patch: Type.String({\n description: [\n 'Patch text using the grammar:',\n '*** Begin Patch',\n '*** Add File: path / *** Delete File: path / *** Update File: path',\n 'optional *** Move to: path for update hunks',\n '@@ then lines prefixed with space, -, or +',\n '*** End Patch',\n ].join(' '),\n }),\n});\n\nexport type AppliedPatchChangeKind = 'add' | 'delete' | 'update' | 'move';\n\nexport interface AppliedPatchChange {\n kind: AppliedPatchChangeKind;\n path: string;\n absolutePath: string;\n moveTo?: string;\n absoluteMoveTo?: string;\n added: number;\n removed: number;\n diff: string;\n}\n\nexport interface ApplyPatchDetails {\n changes: AppliedPatchChange[];\n files: string[];\n summary: string;\n diff: string;\n added: number;\n removed: number;\n}\n\nexport interface CreateApplyPatchToolOptions {\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\ntype ParsedPatch =\n | { kind: 'add'; path: string; content: string }\n | { kind: 'delete'; path: string }\n | { kind: 'update'; path: string; moveTo?: string; hunks: PatchHunk[] };\n\ntype PatchHunkLine = { op: 'context' | 'add' | 'remove'; text: string };\ntype PatchHunk = { lines: PatchHunkLine[] };\n\nfunction countDiff(diff: string): { added: number; removed: number } {\n let added = 0;\n let removed = 0;\n for (const line of diff.split('\\n')) {\n if (line.startsWith('+') && !line.startsWith('+++')) added += 1;\n if (line.startsWith('-') && !line.startsWith('---')) removed += 1;\n }\n return { added, removed };\n}\n\nfunction ensurePatchEnvelope(lines: string[]): void {\n if (lines[0]?.trim() !== '*** Begin Patch') {\n throw new Error('Patch must start with \"*** Begin Patch\"');\n }\n if (lines[lines.length - 1]?.trim() !== '*** End Patch') {\n throw new Error('Patch must end with \"*** End Patch\"');\n }\n}\n\nfunction parsePatch(rawPatch: string): ParsedPatch[] {\n const lines = normalizeToLF(rawPatch).trimEnd().split('\\n');\n ensurePatchEnvelope(lines);\n const ops: ParsedPatch[] = [];\n let i = 1;\n\n while (i < lines.length - 1) {\n const line = lines[i];\n if (line.startsWith('*** Add File: ')) {\n const path = line.slice('*** Add File: '.length).trim();\n i += 1;\n const body: string[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('*** ')) {\n const current = lines[i];\n if (!current.startsWith('+')) {\n throw new Error(`Add file ${path} contains a non-added line`);\n }\n body.push(current.slice(1));\n i += 1;\n }\n ops.push({ kind: 'add', path, content: `${body.join('\\n')}${body.length > 0 ? '\\n' : ''}` });\n continue;\n }\n\n if (line.startsWith('*** Delete File: ')) {\n ops.push({ kind: 'delete', path: line.slice('*** Delete File: '.length).trim() });\n i += 1;\n continue;\n }\n\n if (line.startsWith('*** Update File: ')) {\n const path = line.slice('*** Update File: '.length).trim();\n i += 1;\n let moveTo: string | undefined;\n if (lines[i]?.startsWith('*** Move to: ')) {\n moveTo = lines[i].slice('*** Move to: '.length).trim();\n i += 1;\n }\n const hunks: PatchHunk[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('*** ')) {\n if (!lines[i].startsWith('@@')) {\n throw new Error(`Update file ${path} expected \"@@\" hunk marker`);\n }\n i += 1;\n const hunkLines: PatchHunkLine[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('@@') && !lines[i].startsWith('*** ')) {\n const current = lines[i];\n const prefix = current[0];\n if (prefix === ' ') hunkLines.push({ op: 'context', text: current.slice(1) });\n else if (prefix === '+') hunkLines.push({ op: 'add', text: current.slice(1) });\n else if (prefix === '-') hunkLines.push({ op: 'remove', text: current.slice(1) });\n else throw new Error(`Update file ${path} has invalid hunk line prefix`);\n i += 1;\n }\n hunks.push({ lines: hunkLines });\n }\n ops.push({ kind: 'update', path, moveTo, hunks });\n continue;\n }\n\n if (!line.trim()) {\n i += 1;\n continue;\n }\n throw new Error(`Unknown patch operation: ${line}`);\n }\n\n if (ops.length === 0) throw new Error('Patch contains no operations');\n return ops;\n}\n\nfunction applyHunks(original: string, hunks: PatchHunk[], path: string): string {\n let content = normalizeToLF(original);\n for (const hunk of hunks) {\n const oldLines = hunk.lines\n .filter((line) => line.op === 'context' || line.op === 'remove')\n .map((line) => line.text);\n const newLines = hunk.lines\n .filter((line) => line.op === 'context' || line.op === 'add')\n .map((line) => line.text);\n const oldText = oldLines.join('\\n');\n const newText = newLines.join('\\n');\n const idx = content.indexOf(oldText);\n if (idx === -1) {\n const preview = oldText.split('\\n').slice(0, 4).join('\\\\n');\n throw new Error(\n `Hunk did not match file: ${path}. Re-read the target lines and regenerate the patch with exact current context.${preview ? ` First unmatched context: ${preview}` : ''}`,\n );\n }\n content = `${content.slice(0, idx)}${newText}${content.slice(idx + oldText.length)}`;\n }\n return content;\n}\n\nasync function readExisting(path: string): Promise<string> {\n return normalizeToLF(await readFile(path, 'utf-8'));\n}\n\nasync function assertWritable(workspace: string, path: string): Promise<string> {\n const quick = checkFileSafety('write', path);\n if (!quick.allowed) throw new Error(quick.message ?? `Cannot write ${path}`);\n const policy = evaluateFilePolicy({ operation: 'write', path, workspaceRoot: workspace });\n if (!policy.allowed) throw new Error(`Sandbox: ${policy.reason}`);\n return resolvePathUnderWorkspace(path, workspace);\n}\n\nexport function createApplyPatchTool(\n workspace: string,\n options?: CreateApplyPatchToolOptions,\n): AgentTool {\n return {\n name: 'apply_patch',\n label: 'Apply Patch',\n description: 'Apply a strict multi-file patch. Use this for code edits instead of shell redirection or write_file.',\n parameters: ApplyPatchSchema,\n\n async execute(\n _toolCallId: string,\n params: { patch?: string },\n ): Promise<AgentToolResult<ApplyPatchDetails>> {\n try {\n const patch = params.patch ?? '';\n const parsed = parsePatch(patch);\n const changes: AppliedPatchChange[] = [];\n\n for (const op of parsed) {\n if (op.kind === 'add') {\n const target = await assertWritable(workspace, op.path);\n let exists = false;\n try {\n await stat(target);\n exists = true;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e;\n }\n if (exists) throw new Error(`Add file target already exists: ${op.path}`);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, op.content, 'utf-8');\n const diff = generateDiffString('', op.content, op.path);\n const counts = countDiff(diff);\n changes.push({\n kind: 'add',\n path: op.path,\n absolutePath: target,\n ...counts,\n diff,\n });\n continue;\n }\n\n if (op.kind === 'delete') {\n const target = await assertWritable(workspace, op.path);\n const oldContent = await readExisting(target);\n await rm(target);\n const diff = generateDiffString(oldContent, '', op.path);\n const counts = countDiff(diff);\n changes.push({\n kind: 'delete',\n path: op.path,\n absolutePath: target,\n ...counts,\n diff,\n });\n continue;\n }\n\n const target = await assertWritable(workspace, op.path);\n const oldContent = await readExisting(target);\n const newContent = applyHunks(oldContent, op.hunks, op.path);\n const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;\n await mkdir(dirname(destination), { recursive: true });\n await writeFile(destination, newContent, 'utf-8');\n if (destination !== target) {\n await rm(target);\n }\n const displayPath = op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path;\n const diff = generateDiffString(oldContent, newContent, displayPath);\n const counts = countDiff(diff);\n changes.push({\n kind: op.moveTo ? 'move' : 'update',\n path: op.path,\n absolutePath: target,\n moveTo: op.moveTo,\n absoluteMoveTo: op.moveTo ? destination : undefined,\n ...counts,\n diff,\n });\n }\n\n const diff = changes.map((change) => change.diff).join('\\n');\n const added = changes.reduce((sum, change) => sum + change.added, 0);\n const removed = changes.reduce((sum, change) => sum + change.removed, 0);\n const files = changes.map((change) => relative(workspace, change.absoluteMoveTo ?? change.absolutePath));\n const summary = changes\n .map((change) => {\n const display = change.moveTo ? `${change.path} -> ${change.moveTo}` : change.path;\n return `${change.kind}: ${display} (+${change.added}/-${change.removed})`;\n })\n .join('\\n');\n\n await options?.recordGoalEvidence?.({\n kind: 'diff',\n title: `Patch applied: ${changes.length} file${changes.length === 1 ? '' : 's'}`,\n summary: diff.slice(0, 4000),\n uri: changes.length === 1 ? changes[0].absoluteMoveTo ?? changes[0].absolutePath : undefined,\n data: {\n files,\n added,\n removed,\n },\n });\n\n return {\n content: [{ type: 'text', text: summary || 'Patch applied.' }],\n details: { changes, files, summary, diff, added, removed },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`apply_patch failed: ${message}`);\n }\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apply-patch.js","names":[],"sources":["../../../../src/agent/tools/apply-patch.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname, relative } from 'node:path';\n\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\nimport { detectLineEnding, generateDiffString, normalizeToLF, restoreLineEndings } from './edit-diff.js';\nimport { resolvePathUnderWorkspace } from './tool-paths.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst ApplyPatchSchema = Type.Object({\n patch: Type.String({\n description: [\n 'Patch text using the grammar:',\n '*** Begin Patch',\n '*** Add File: path / *** Delete File: path / *** Update File: path',\n 'optional *** Move to: path for update hunks',\n '@@ then lines prefixed with space, -, or +',\n '*** End Patch',\n ].join(' '),\n }),\n});\n\nexport type AppliedPatchChangeKind = 'add' | 'delete' | 'update' | 'move';\n\nexport interface AppliedPatchChange {\n kind: AppliedPatchChangeKind;\n path: string;\n absolutePath: string;\n moveTo?: string;\n absoluteMoveTo?: string;\n added: number;\n removed: number;\n diff: string;\n}\n\nexport interface ApplyPatchDetails {\n changes: AppliedPatchChange[];\n files: string[];\n summary: string;\n diff: string;\n added: number;\n removed: number;\n}\n\nexport interface CreateApplyPatchToolOptions {\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\ntype ParsedPatch =\n | { kind: 'add'; path: string; content: string }\n | { kind: 'delete'; path: string }\n | { kind: 'update'; path: string; moveTo?: string; hunks: PatchHunk[] };\n\ntype PlannedPatchChange = {\n kind: AppliedPatchChangeKind;\n path: string;\n absolutePath: string;\n moveTo?: string;\n absoluteMoveTo?: string;\n oldContent?: string;\n newContent?: string;\n diff: string;\n added: number;\n removed: number;\n};\n\ntype PatchHunkLine = { op: 'context' | 'add' | 'remove'; text: string };\ntype PatchHunk = { lines: PatchHunkLine[] };\n\nfunction countDiff(diff: string): { added: number; removed: number } {\n let added = 0;\n let removed = 0;\n for (const line of diff.split('\\n')) {\n if (line.startsWith('+') && !line.startsWith('+++')) added += 1;\n if (line.startsWith('-') && !line.startsWith('---')) removed += 1;\n }\n return { added, removed };\n}\n\nfunction ensurePatchEnvelope(lines: string[]): void {\n if (lines[0]?.trim() !== '*** Begin Patch') {\n throw new Error('Patch must start with \"*** Begin Patch\"');\n }\n if (lines[lines.length - 1]?.trim() !== '*** End Patch') {\n throw new Error('Patch must end with \"*** End Patch\"');\n }\n}\n\nfunction parsePatch(rawPatch: string): ParsedPatch[] {\n const lines = normalizeToLF(rawPatch).trimEnd().split('\\n');\n ensurePatchEnvelope(lines);\n const ops: ParsedPatch[] = [];\n let i = 1;\n\n while (i < lines.length - 1) {\n const line = lines[i];\n if (line.startsWith('*** Add File: ')) {\n const path = line.slice('*** Add File: '.length).trim();\n i += 1;\n const body: string[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('*** ')) {\n const current = lines[i];\n if (!current.startsWith('+')) {\n throw new Error(`Add file ${path} contains a non-added line`);\n }\n body.push(current.slice(1));\n i += 1;\n }\n ops.push({ kind: 'add', path, content: `${body.join('\\n')}${body.length > 0 ? '\\n' : ''}` });\n continue;\n }\n\n if (line.startsWith('*** Delete File: ')) {\n ops.push({ kind: 'delete', path: line.slice('*** Delete File: '.length).trim() });\n i += 1;\n continue;\n }\n\n if (line.startsWith('*** Update File: ')) {\n const path = line.slice('*** Update File: '.length).trim();\n i += 1;\n let moveTo: string | undefined;\n if (lines[i]?.startsWith('*** Move to: ')) {\n moveTo = lines[i].slice('*** Move to: '.length).trim();\n i += 1;\n }\n const hunks: PatchHunk[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('*** ')) {\n if (!lines[i].startsWith('@@')) {\n throw new Error(`Update file ${path} expected \"@@\" hunk marker`);\n }\n i += 1;\n const hunkLines: PatchHunkLine[] = [];\n while (i < lines.length - 1 && !lines[i].startsWith('@@') && !lines[i].startsWith('*** ')) {\n const current = lines[i];\n const prefix = current[0];\n if (prefix === ' ') hunkLines.push({ op: 'context', text: current.slice(1) });\n else if (prefix === '+') hunkLines.push({ op: 'add', text: current.slice(1) });\n else if (prefix === '-') hunkLines.push({ op: 'remove', text: current.slice(1) });\n else throw new Error(`Update file ${path} has invalid hunk line prefix`);\n i += 1;\n }\n hunks.push({ lines: hunkLines });\n }\n ops.push({ kind: 'update', path, moveTo, hunks });\n continue;\n }\n\n if (!line.trim()) {\n i += 1;\n continue;\n }\n throw new Error(`Unknown patch operation: ${line}`);\n }\n\n if (ops.length === 0) throw new Error('Patch contains no operations');\n return ops;\n}\n\nfunction applyHunks(original: string, hunks: PatchHunk[], path: string): string {\n let content = normalizeToLF(original);\n for (const hunk of hunks) {\n const oldLines = hunk.lines\n .filter((line) => line.op === 'context' || line.op === 'remove')\n .map((line) => line.text);\n const newLines = hunk.lines\n .filter((line) => line.op === 'context' || line.op === 'add')\n .map((line) => line.text);\n const oldText = oldLines.join('\\n');\n const newText = newLines.join('\\n');\n const idx = content.indexOf(oldText);\n if (idx === -1) {\n const preview = oldText.split('\\n').slice(0, 4).join('\\\\n');\n throw new Error(\n `Hunk did not match file: ${path}. Re-read the target lines and regenerate the patch with exact current context.${preview ? ` First unmatched context: ${preview}` : ''}`,\n );\n }\n content = `${content.slice(0, idx)}${newText}${content.slice(idx + oldText.length)}`;\n }\n return content;\n}\n\nasync function readExisting(path: string): Promise<string> {\n return readFile(path, 'utf-8');\n}\n\nasync function assertWritable(workspace: string, path: string): Promise<string> {\n const quick = checkFileSafety('write', path);\n if (!quick.allowed) throw new Error(quick.message ?? `Cannot write ${path}`);\n const policy = evaluateFilePolicy({ operation: 'write', path, workspaceRoot: workspace });\n if (!policy.allowed) throw new Error(`Sandbox: ${policy.reason}`);\n return resolvePathUnderWorkspace(path, workspace);\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw e;\n }\n}\n\nasync function buildPatchPlan(\n workspace: string,\n parsed: ParsedPatch[],\n): Promise<PlannedPatchChange[]> {\n const changes: PlannedPatchChange[] = [];\n const plannedTargets = new Set<string>();\n\n const rememberTarget = (target: string) => {\n if (plannedTargets.has(target)) {\n throw new Error(`Patch touches the same target multiple times: ${relative(workspace, target)}`);\n }\n plannedTargets.add(target);\n };\n\n for (const op of parsed) {\n if (op.kind === 'add') {\n const target = await assertWritable(workspace, op.path);\n rememberTarget(target);\n if (await pathExists(target)) throw new Error(`Add file target already exists: ${op.path}`);\n const diff = generateDiffString('', op.content, op.path);\n const counts = countDiff(diff);\n changes.push({\n kind: 'add',\n path: op.path,\n absolutePath: target,\n newContent: op.content,\n ...counts,\n diff,\n });\n continue;\n }\n\n if (op.kind === 'delete') {\n const target = await assertWritable(workspace, op.path);\n rememberTarget(target);\n const oldContent = await readExisting(target);\n const diff = generateDiffString(oldContent, '', op.path);\n const counts = countDiff(diff);\n changes.push({\n kind: 'delete',\n path: op.path,\n absolutePath: target,\n oldContent,\n ...counts,\n diff,\n });\n continue;\n }\n\n const target = await assertWritable(workspace, op.path);\n rememberTarget(target);\n const oldContent = await readExisting(target);\n const lineEnding = detectLineEnding(oldContent);\n const newLfContent = applyHunks(normalizeToLF(oldContent), op.hunks, op.path);\n const newContent = restoreLineEndings(newLfContent, lineEnding);\n const destination = op.moveTo ? await assertWritable(workspace, op.moveTo) : target;\n if (destination !== target) {\n rememberTarget(destination);\n if (await pathExists(destination)) {\n throw new Error(`Move target already exists: ${op.moveTo}`);\n }\n }\n const displayPath = op.moveTo ? `${op.path} -> ${op.moveTo}` : op.path;\n const diff = generateDiffString(oldContent, newContent, displayPath);\n const counts = countDiff(diff);\n changes.push({\n kind: op.moveTo ? 'move' : 'update',\n path: op.path,\n absolutePath: target,\n moveTo: op.moveTo,\n absoluteMoveTo: op.moveTo ? destination : undefined,\n oldContent,\n newContent,\n ...counts,\n diff,\n });\n }\n\n return changes;\n}\n\nasync function commitPatchPlan(changes: PlannedPatchChange[]): Promise<void> {\n const committed: PlannedPatchChange[] = [];\n try {\n for (const change of changes) {\n committed.push(change);\n if (change.kind === 'add') {\n await mkdir(dirname(change.absolutePath), { recursive: true });\n await writeFile(change.absolutePath, change.newContent ?? '', 'utf-8');\n } else if (change.kind === 'delete') {\n await rm(change.absolutePath);\n } else {\n const destination = change.absoluteMoveTo ?? change.absolutePath;\n await mkdir(dirname(destination), { recursive: true });\n await writeFile(destination, change.newContent ?? '', 'utf-8');\n if (destination !== change.absolutePath) {\n await rm(change.absolutePath);\n }\n }\n }\n } catch (error) {\n for (const change of committed.reverse()) {\n try {\n if (change.kind === 'add') {\n await rm(change.absolutePath, { force: true });\n } else if (change.kind === 'delete') {\n await mkdir(dirname(change.absolutePath), { recursive: true });\n await writeFile(change.absolutePath, change.oldContent ?? '', 'utf-8');\n } else if (change.absoluteMoveTo) {\n await rm(change.absoluteMoveTo, { force: true });\n await mkdir(dirname(change.absolutePath), { recursive: true });\n await writeFile(change.absolutePath, change.oldContent ?? '', 'utf-8');\n } else {\n await writeFile(change.absolutePath, change.oldContent ?? '', 'utf-8');\n }\n } catch {\n // Best-effort rollback; the original write error is more useful to the caller.\n }\n }\n throw error;\n }\n}\n\nexport function createApplyPatchTool(\n workspace: string,\n options?: CreateApplyPatchToolOptions,\n): AgentTool {\n return {\n name: 'apply_patch',\n label: 'Apply Patch',\n description: 'Apply a strict multi-file patch. Use this for code edits instead of shell redirection or write_file.',\n parameters: ApplyPatchSchema,\n mutatesWorkspace: true,\n mutationScope: 'workspace',\n supportsParallel: false,\n idempotent: false,\n requiresExclusiveWorkspaceLock: true,\n finalGuardRelevant: true,\n\n async execute(\n _toolCallId: string,\n params: { patch?: string },\n ): Promise<AgentToolResult<ApplyPatchDetails>> {\n try {\n const patch = params.patch ?? '';\n const parsed = parsePatch(patch);\n const changes = await buildPatchPlan(workspace, parsed);\n await commitPatchPlan(changes);\n\n const diff = changes.map((change) => change.diff).join('\\n');\n const added = changes.reduce((sum, change) => sum + change.added, 0);\n const removed = changes.reduce((sum, change) => sum + change.removed, 0);\n const files = changes.map((change) => relative(workspace, change.absoluteMoveTo ?? change.absolutePath));\n const summary = changes\n .map((change) => {\n const display = change.moveTo ? `${change.path} -> ${change.moveTo}` : change.path;\n return `${change.kind}: ${display} (+${change.added}/-${change.removed})`;\n })\n .join('\\n');\n\n await options?.recordGoalEvidence?.({\n kind: 'diff',\n title: `Patch applied: ${changes.length} file${changes.length === 1 ? '' : 's'}`,\n summary: diff.slice(0, 4000),\n uri: changes.length === 1 ? changes[0].absoluteMoveTo ?? changes[0].absolutePath : undefined,\n data: {\n files,\n added,\n removed,\n },\n });\n\n return {\n content: [{ type: 'text', text: summary || 'Patch applied.' }],\n details: { changes, files, summary, diff, added, removed },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`apply_patch failed: ${message}`);\n }\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentTool } from '@earendil-works/pi-agent-core';
|
|
2
|
+
export type ToolLockMode = 'none' | 'exclusive';
|
|
3
|
+
export declare function resolveToolLockMode(tool: AgentTool<any, any>): ToolLockMode;
|
|
4
|
+
export declare class ToolConcurrencyController {
|
|
5
|
+
private tail;
|
|
6
|
+
run<T>(mode: ToolLockMode, fn: () => Promise<T>): Promise<T>;
|
|
7
|
+
private runExclusive;
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getToolMetadata } from "./metadata.js";
|
|
2
|
+
//#region src/agent/tools/concurrency.ts
|
|
3
|
+
function resolveToolLockMode(tool) {
|
|
4
|
+
const meta = getToolMetadata(tool);
|
|
5
|
+
if (meta.supportsParallel && !meta.mutatesWorkspace && meta.mutationScope === "none") return "none";
|
|
6
|
+
return "exclusive";
|
|
7
|
+
}
|
|
8
|
+
var ToolConcurrencyController = class {
|
|
9
|
+
tail = Promise.resolve();
|
|
10
|
+
run(mode, fn) {
|
|
11
|
+
if (mode === "none") return fn();
|
|
12
|
+
return this.runExclusive(fn);
|
|
13
|
+
}
|
|
14
|
+
async runExclusive(fn) {
|
|
15
|
+
const previous = this.tail;
|
|
16
|
+
let release;
|
|
17
|
+
this.tail = new Promise((resolve) => {
|
|
18
|
+
release = resolve;
|
|
19
|
+
});
|
|
20
|
+
await previous;
|
|
21
|
+
try {
|
|
22
|
+
return await fn();
|
|
23
|
+
} finally {
|
|
24
|
+
release();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ToolConcurrencyController, resolveToolLockMode };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","names":[],"sources":["../../../../src/agent/tools/concurrency.ts"],"sourcesContent":["import type { AgentTool } from '@earendil-works/pi-agent-core';\n\nimport { getToolMetadata } from './metadata.js';\n\nexport type ToolLockMode = 'none' | 'exclusive';\n\nexport function resolveToolLockMode(tool: AgentTool<any, any>): ToolLockMode {\n const meta = getToolMetadata(tool);\n if (meta.supportsParallel && !meta.mutatesWorkspace && meta.mutationScope === 'none') {\n return 'none';\n }\n return 'exclusive';\n}\n\nexport class ToolConcurrencyController {\n private tail: Promise<void> = Promise.resolve();\n\n run<T>(mode: ToolLockMode, fn: () => Promise<T>): Promise<T> {\n if (mode === 'none') {\n return fn();\n }\n return this.runExclusive(fn);\n }\n\n private async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const previous = this.tail;\n let release!: () => void;\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous;\n try {\n return await fn();\n } finally {\n release();\n }\n }\n}\n"],"mappings":";;AAMA,SAAgB,oBAAoB,MAAyC;CAC3E,MAAM,OAAO,gBAAgB,KAAK;AAClC,KAAI,KAAK,oBAAoB,CAAC,KAAK,oBAAoB,KAAK,kBAAkB,OAC5E,QAAO;AAET,QAAO;;AAGT,IAAa,4BAAb,MAAuC;CACrC,OAA8B,QAAQ,SAAS;CAE/C,IAAO,MAAoB,IAAkC;AAC3D,MAAI,SAAS,OACX,QAAO,IAAI;AAEb,SAAO,KAAK,aAAa,GAAG;;CAG9B,MAAc,aAAgB,IAAkC;EAC9D,MAAM,WAAW,KAAK;EACtB,IAAI;AACJ,OAAK,OAAO,IAAI,SAAe,YAAY;AACzC,aAAU;IACV;AAEF,QAAM;AACN,MAAI;AACF,UAAO,MAAM,IAAI;YACT;AACR,YAAS"}
|
|
@@ -2,6 +2,8 @@ import type { AgentTool } from '@earendil-works/pi-agent-core';
|
|
|
2
2
|
import type { Config } from '../../config/schema.js';
|
|
3
3
|
export interface DreamingToolDeps {
|
|
4
4
|
getWorkspace: () => string;
|
|
5
|
+
getDreamingRoot: () => string;
|
|
5
6
|
getConfig: () => Config | undefined;
|
|
7
|
+
getAgentId?: () => string | undefined;
|
|
6
8
|
}
|
|
7
9
|
export declare function createDreamingTool(deps: DreamingToolDeps): AgentTool;
|
|
@@ -35,16 +35,18 @@ function createDreamingTool(deps) {
|
|
|
35
35
|
return {
|
|
36
36
|
name: "dreaming",
|
|
37
37
|
label: "💤 Dreaming",
|
|
38
|
-
description: "Inspect and maintain the dreaming promotion state.\n\nActions:\n- status: show config gates and short-term store stats\n- reset_store: clear short-term recall store (
|
|
38
|
+
description: "Inspect and maintain the dreaming promotion state.\n\nActions:\n- status: show config gates and short-term store stats\n- reset_store: clear this agent short-term recall store (.dreams/short-term-recall.json)\n- clear_lock: remove this agent stale promotion lock file (.dreams/short-term-promotion.lock)",
|
|
39
39
|
parameters: DreamingSchema,
|
|
40
40
|
async execute(_toolCallId, params) {
|
|
41
41
|
const action = params.action;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
42
|
+
const dreamingRoot = deps.getDreamingRoot();
|
|
43
|
+
const cfg = deps.getConfig();
|
|
44
|
+
const agentId = deps.getAgentId?.();
|
|
45
|
+
const resolved = resolveDreamingConfig(cfg, agentId);
|
|
46
|
+
const storePath = path.join(dreamingRoot, SHORT_TERM_RECALL_STORE_RELATIVE);
|
|
47
|
+
const lockPath = path.join(dreamingRoot, SHORT_TERM_PROMOTION_LOCK_RELATIVE);
|
|
46
48
|
if (action === "status") {
|
|
47
|
-
const { store } = await loadDreamingStore({
|
|
49
|
+
const { store } = await loadDreamingStore({ dreamingRoot });
|
|
48
50
|
const entries = Object.values(store.entries ?? {});
|
|
49
51
|
const total = entries.length;
|
|
50
52
|
const promoted = entries.filter((e) => Boolean(e.promotedAt)).length;
|
|
@@ -64,7 +66,8 @@ function createDreamingTool(deps) {
|
|
|
64
66
|
`deep.minRecallCount: ${resolved.deep.minRecallCount}`,
|
|
65
67
|
`deep.limit: ${resolved.deep.limit}`,
|
|
66
68
|
"",
|
|
67
|
-
`
|
|
69
|
+
`agentId: ${agentId ?? "(unknown)"}`,
|
|
70
|
+
`storePath: ${storePath}`,
|
|
68
71
|
`entryCount: ${total}`,
|
|
69
72
|
`promotedCount: ${promoted}`,
|
|
70
73
|
`promotedToday: ${promotedToday}`,
|
|
@@ -76,17 +79,17 @@ function createDreamingTool(deps) {
|
|
|
76
79
|
].join("\n"));
|
|
77
80
|
}
|
|
78
81
|
if (action === "reset_store") {
|
|
79
|
-
const { store } = await loadDreamingStore({
|
|
82
|
+
const { store } = await loadDreamingStore({ dreamingRoot });
|
|
80
83
|
const before = Object.keys(store.entries ?? {}).length;
|
|
81
84
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
82
85
|
store.entries = {};
|
|
83
86
|
store.updatedAt = nowIso;
|
|
84
87
|
await saveDreamingStore({
|
|
85
|
-
|
|
88
|
+
dreamingRoot,
|
|
86
89
|
store
|
|
87
90
|
});
|
|
88
91
|
log.info({
|
|
89
|
-
|
|
92
|
+
dreamingRoot,
|
|
90
93
|
before
|
|
91
94
|
}, "Dreaming store reset");
|
|
92
95
|
return textResult(`Reset short-term store. Removed ${before} entr${before === 1 ? "y" : "ies"}. Path: ${storePath}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dreaming-tool.js","names":[],"sources":["../../../../src/agent/tools/dreaming-tool.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from '../memory/dreaming/constants.js';\nimport { loadDreamingStore, saveDreamingStore } from '../memory/dreaming/short-term-store.js';\n\nconst log = createLogger('DreamingTool');\n\nconst DreamingSchema = Type.Object({\n action: Type.Union([Type.Literal('status'), Type.Literal('reset_store'), Type.Literal('clear_lock')]),\n});\n\ntype DreamingParams = {\n action: 'status' | 'reset_store' | 'clear_lock';\n};\n\nexport interface DreamingToolDeps {\n getWorkspace: () => string;\n getConfig: () => Config | undefined;\n}\n\nfunction textResult(text: string): AgentToolResult<{}> {\n return { content: [{ type: 'text', text }], details: {} };\n}\n\nfunction safeStatMs(ms: number | undefined): number | null {\n if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) return null;\n return Math.floor(ms);\n}\n\nfunction isoDay(isoLike: string | undefined): string | null {\n if (!isoLike) return null;\n const m = isoLike.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n return m?.[1] ?? null;\n}\n\nexport function createDreamingTool(deps: DreamingToolDeps): AgentTool {\n return {\n name: 'dreaming',\n label: '💤 Dreaming',\n description:\n 'Inspect and maintain the dreaming promotion state.\\n\\n' +\n 'Actions:\\n' +\n '- status: show config gates and short-term store stats\\n' +\n '- reset_store: clear short-term recall store (
|
|
1
|
+
{"version":3,"file":"dreaming-tool.js","names":[],"sources":["../../../../src/agent/tools/dreaming-tool.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from '../memory/dreaming/constants.js';\nimport { loadDreamingStore, saveDreamingStore } from '../memory/dreaming/short-term-store.js';\n\nconst log = createLogger('DreamingTool');\n\nconst DreamingSchema = Type.Object({\n action: Type.Union([Type.Literal('status'), Type.Literal('reset_store'), Type.Literal('clear_lock')]),\n});\n\ntype DreamingParams = {\n action: 'status' | 'reset_store' | 'clear_lock';\n};\n\nexport interface DreamingToolDeps {\n getWorkspace: () => string;\n getDreamingRoot: () => string;\n getConfig: () => Config | undefined;\n getAgentId?: () => string | undefined;\n}\n\nfunction textResult(text: string): AgentToolResult<{}> {\n return { content: [{ type: 'text', text }], details: {} };\n}\n\nfunction safeStatMs(ms: number | undefined): number | null {\n if (typeof ms !== 'number' || !Number.isFinite(ms) || ms <= 0) return null;\n return Math.floor(ms);\n}\n\nfunction isoDay(isoLike: string | undefined): string | null {\n if (!isoLike) return null;\n const m = isoLike.match(/^(\\d{4}-\\d{2}-\\d{2})/);\n return m?.[1] ?? null;\n}\n\nexport function createDreamingTool(deps: DreamingToolDeps): AgentTool {\n return {\n name: 'dreaming',\n label: '💤 Dreaming',\n description:\n 'Inspect and maintain the dreaming promotion state.\\n\\n' +\n 'Actions:\\n' +\n '- status: show config gates and short-term store stats\\n' +\n '- reset_store: clear this agent short-term recall store (.dreams/short-term-recall.json)\\n' +\n '- clear_lock: remove this agent stale promotion lock file (.dreams/short-term-promotion.lock)',\n parameters: DreamingSchema,\n async execute(_toolCallId, params: any): Promise<AgentToolResult<{}>> {\n const action = (params as DreamingParams).action;\n const dreamingRoot = deps.getDreamingRoot();\n const cfg = deps.getConfig();\n const agentId = deps.getAgentId?.();\n const resolved = resolveDreamingConfig(cfg, agentId);\n\n const storePath = path.join(dreamingRoot, SHORT_TERM_RECALL_STORE_RELATIVE);\n const lockPath = path.join(dreamingRoot, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n\n if (action === 'status') {\n const { store } = await loadDreamingStore({ dreamingRoot });\n const entries = Object.values(store.entries ?? {});\n const total = entries.length;\n const promoted = entries.filter((e) => Boolean(e.promotedAt)).length;\n const today = new Date().toISOString().slice(0, 10);\n const promotedToday = entries.filter((e) => isoDay(e.promotedAt) === today).length;\n const lastPromotedAt = entries\n .map((e) => e.promotedAt)\n .filter((x): x is string => typeof x === 'string' && x.length > 0)\n .sort()\n .at(-1);\n\n const lockStat = await fs.stat(lockPath).catch(() => null);\n const lockAgeMs =\n lockStat && safeStatMs(lockStat.mtimeMs) ? Date.now() - Math.floor(lockStat.mtimeMs) : null;\n\n const lines = [\n 'Dreaming status',\n '',\n `enabled: ${resolved.enabled ? 'true' : 'false'}`,\n `deep.enabled: ${resolved.deep.enabled ? 'true' : 'false'}`,\n `cron.frequency: ${resolved.frequency}`,\n `cron.timezone: ${resolved.timezone ?? '(default)'}`,\n `deep.minScore: ${resolved.deep.minScore}`,\n `deep.minRecallCount: ${resolved.deep.minRecallCount}`,\n `deep.limit: ${resolved.deep.limit}`,\n '',\n `agentId: ${agentId ?? '(unknown)'}`,\n `storePath: ${storePath}`,\n `entryCount: ${total}`,\n `promotedCount: ${promoted}`,\n `promotedToday: ${promotedToday}`,\n `lastPromotedAt: ${lastPromotedAt ?? '(none)'}`,\n '',\n `lockPath: ${SHORT_TERM_PROMOTION_LOCK_RELATIVE}`,\n `lockPresent: ${lockStat ? 'true' : 'false'}`,\n `lockAgeMs: ${lockAgeMs ?? '(n/a)'}`,\n ];\n return textResult(lines.join('\\n'));\n }\n\n if (action === 'reset_store') {\n const { store } = await loadDreamingStore({ dreamingRoot });\n const before = Object.keys(store.entries ?? {}).length;\n const nowIso = new Date().toISOString();\n store.entries = {};\n store.updatedAt = nowIso;\n await saveDreamingStore({ dreamingRoot, store });\n log.info({ dreamingRoot, before }, 'Dreaming store reset');\n return textResult(\n `Reset short-term store. Removed ${before} entr${before === 1 ? 'y' : 'ies'}. Path: ${storePath}`,\n );\n }\n\n await fs.unlink(lockPath).catch(() => {});\n return textResult(`Cleared lock (if present). Path: ${lockPath}`);\n },\n } as any;\n}\n"],"mappings":";;;;;;;;;aAOqD;AAQrD,MAAM,MAAM,aAAa,eAAe;AAExC,MAAM,iBAAiB,KAAK,OAAO,EACjC,QAAQ,KAAK,MAAM;CAAC,KAAK,QAAQ,SAAS;CAAE,KAAK,QAAQ,cAAc;CAAE,KAAK,QAAQ,aAAa;CAAC,CAAC,EACtG,CAAC;AAaF,SAAS,WAAW,MAAmC;AACrD,QAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ;GAAM,CAAC;EAAE,SAAS,EAAE;EAAE;;AAG3D,SAAS,WAAW,IAAuC;AACzD,KAAI,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,GAAG,IAAI,MAAM,EAAG,QAAO;AACtE,QAAO,KAAK,MAAM,GAAG;;AAGvB,SAAS,OAAO,SAA4C;AAC1D,KAAI,CAAC,QAAS,QAAO;AAErB,QADU,QAAQ,MAAM,uBAChB,GAAG,MAAM;;AAGnB,SAAgB,mBAAmB,MAAmC;AACpE,QAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EAKF,YAAY;EACZ,MAAM,QAAQ,aAAa,QAA2C;GACpE,MAAM,SAAU,OAA0B;GAC1C,MAAM,eAAe,KAAK,iBAAiB;GAC3C,MAAM,MAAM,KAAK,WAAW;GAC5B,MAAM,UAAU,KAAK,cAAc;GACnC,MAAM,WAAW,sBAAsB,KAAK,QAAQ;GAEpD,MAAM,YAAY,KAAK,KAAK,cAAc,iCAAiC;GAC3E,MAAM,WAAW,KAAK,KAAK,cAAc,mCAAmC;AAE5E,OAAI,WAAW,UAAU;IACvB,MAAM,EAAE,UAAU,MAAM,kBAAkB,EAAE,cAAc,CAAC;IAC3D,MAAM,UAAU,OAAO,OAAO,MAAM,WAAW,EAAE,CAAC;IAClD,MAAM,QAAQ,QAAQ;IACtB,MAAM,WAAW,QAAQ,QAAQ,MAAM,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC9D,MAAM,yBAAQ,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;IACnD,MAAM,gBAAgB,QAAQ,QAAQ,MAAM,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC;IAC5E,MAAM,iBAAiB,QACpB,KAAK,MAAM,EAAE,WAAW,CACxB,QAAQ,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS,EAAE,CACjE,MAAM,CACN,GAAG,GAAG;IAET,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS,CAAC,YAAY,KAAK;IAC1D,MAAM,YACJ,YAAY,WAAW,SAAS,QAAQ,GAAG,KAAK,KAAK,GAAG,KAAK,MAAM,SAAS,QAAQ,GAAG;AAwBzF,WAAO,WAAW;KArBhB;KACA;KACA,YAAY,SAAS,UAAU,SAAS;KACxC,iBAAiB,SAAS,KAAK,UAAU,SAAS;KAClD,mBAAmB,SAAS;KAC5B,kBAAkB,SAAS,YAAY;KACvC,kBAAkB,SAAS,KAAK;KAChC,wBAAwB,SAAS,KAAK;KACtC,eAAe,SAAS,KAAK;KAC7B;KACA,YAAY,WAAW;KACvB,cAAc;KACd,eAAe;KACf,kBAAkB;KAClB,kBAAkB;KAClB,mBAAmB,kBAAkB;KACrC;KACA,aAAa;KACb,gBAAgB,WAAW,SAAS;KACpC,cAAc,aAAa;KAEN,CAAC,KAAK,KAAK,CAAC;;AAGrC,OAAI,WAAW,eAAe;IAC5B,MAAM,EAAE,UAAU,MAAM,kBAAkB,EAAE,cAAc,CAAC;IAC3D,MAAM,SAAS,OAAO,KAAK,MAAM,WAAW,EAAE,CAAC,CAAC;IAChD,MAAM,0BAAS,IAAI,MAAM,EAAC,aAAa;AACvC,UAAM,UAAU,EAAE;AAClB,UAAM,YAAY;AAClB,UAAM,kBAAkB;KAAE;KAAc;KAAO,CAAC;AAChD,QAAI,KAAK;KAAE;KAAc;KAAQ,EAAE,uBAAuB;AAC1D,WAAO,WACL,mCAAmC,OAAO,OAAO,WAAW,IAAI,MAAM,MAAM,UAAU,YACvF;;AAGH,SAAM,GAAG,OAAO,SAAS,CAAC,YAAY,GAAG;AACzC,UAAO,WAAW,oCAAoC,WAAW;;EAEpE"}
|
|
@@ -61,6 +61,10 @@ function createExecCommandTool(workspaceCwd, options) {
|
|
|
61
61
|
].join(" "),
|
|
62
62
|
parameters: ExecCommandSchema,
|
|
63
63
|
label: "Run Command",
|
|
64
|
+
mutationScope: "unknown",
|
|
65
|
+
supportsParallel: false,
|
|
66
|
+
idempotent: false,
|
|
67
|
+
finalGuardRelevant: true,
|
|
64
68
|
async execute(_toolCallId, params, signal, onUpdate) {
|
|
65
69
|
const command = (params.cmd ?? params.command ?? "").trim();
|
|
66
70
|
if (!command) return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec-command.js","names":[],"sources":["../../../../src/agent/tools/exec-command.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type {\n AgentTool,\n AgentToolResult,\n AgentToolUpdateCallback,\n} from '@earendil-works/pi-agent-core';\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nimport { evaluateExecPolicy } from '../sandbox/exec-policy.js';\nimport { formatSize, truncateTail } from './truncate.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_COMMAND_TIMEOUT_MS = 300_000;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 50_000;\nconst STREAM_DELTA_MAX_CHARS = 16_000;\n\nconst ExecCommandSchema = Type.Object({\n cmd: Type.String({ description: 'Shell command to execute.' }),\n cwd: Type.Optional(\n Type.String({\n description: 'Working directory. Relative paths are resolved under the agent workspace.',\n }),\n ),\n timeoutMs: Type.Optional(\n Type.Number({\n description: 'Command timeout in milliseconds. Defaults to 120000 and is capped at 300000.',\n }),\n ),\n maxOutputChars: Type.Optional(\n Type.Number({\n description: 'Maximum characters returned to the model. UI streaming still receives output deltas.',\n }),\n ),\n});\n\nexport interface ExecCommandDetails {\n command: string;\n cwd: string;\n status: 'success' | 'failed' | 'timed_out' | 'blocked';\n exitCode: number | null;\n durationMs: number;\n timedOut: boolean;\n truncated: boolean;\n truncatedBy?: 'lines' | 'bytes' | null;\n outputBytes: number;\n stdout: string;\n stderr: string;\n aggregatedOutput: string;\n failureHint?: string;\n}\n\nexport interface ExecCommandUpdateDetails {\n kind: 'command_output_delta';\n command: string;\n cwd: string;\n stream: 'stdout' | 'stderr';\n delta: string;\n}\n\nexport interface CreateExecCommandToolOptions {\n /** Env var names allowed through prepareSafeToolEnv even if they match secret heuristics. */\n getSkillPassthroughEnvVarNames?: () => string[];\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\ntype ExecCommandParams = {\n cmd?: string;\n command?: string;\n cwd?: string;\n timeoutMs?: number;\n maxOutputChars?: number;\n};\n\nfunction clampTimeoutMs(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_TIMEOUT_MS;\n return Math.min(MAX_COMMAND_TIMEOUT_MS, Math.max(1_000, Math.floor(n)));\n}\n\nfunction clampMaxOutputChars(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_MAX_OUTPUT_CHARS;\n return Math.max(1_000, Math.min(500_000, Math.floor(n)));\n}\n\nfunction isLikelyTestCommand(command: string): boolean {\n return /\\b(test|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|type-check|lint)\\b/i.test(command);\n}\n\nfunction commandStatus(exitCode: number | null, timedOut: boolean): ExecCommandDetails['status'] {\n if (timedOut) return 'timed_out';\n if (exitCode === 0) return 'success';\n return 'failed';\n}\n\nfunction commandFailureHint(details: Pick<ExecCommandDetails, 'status' | 'exitCode' | 'timedOut' | 'stderr' | 'stdout'>): string | undefined {\n if (details.status === 'success') return undefined;\n if (details.status === 'timed_out') {\n return 'The command timed out. Re-run a narrower command, increase timeoutMs if the long run is expected, or inspect partial output before retrying.';\n }\n if (details.exitCode !== 0 && details.stderr.trim()) {\n return 'The command failed. Inspect stderr first, fix the underlying issue, then re-run the narrowest relevant verification command.';\n }\n if (details.exitCode !== 0 && details.stdout.trim()) {\n return 'The command failed. Inspect stdout for the failure location, then re-run the narrowest relevant verification command.';\n }\n return 'The command failed without output. Verify the command, cwd, and required dependencies before retrying.';\n}\n\nfunction formatOutputForModel(\n details: ExecCommandDetails,\n maxOutputChars: number,\n): { text: string; truncated: boolean; truncatedBy?: 'lines' | 'bytes' | null; outputBytes: number } {\n let prefix = '';\n if (details.timedOut) {\n prefix += `Command timed out after ${Math.round(details.durationMs / 1000)}s\\n`;\n }\n if (details.exitCode !== 0 && details.exitCode != null) {\n prefix += `Command exited with code ${details.exitCode}\\n`;\n }\n\n const truncation = truncateTail(details.aggregatedOutput, {\n maxBytes: Math.min(maxOutputChars, DEFAULT_MAX_OUTPUT_CHARS),\n });\n let text = `${prefix}${truncation.content}`.trimEnd();\n if (!text) text = '(no output)';\n if (truncation.truncated) {\n text += `\\n\\n[Output truncated: ${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}]`;\n }\n if (details.failureHint) {\n text += `\\n\\nNext action: ${details.failureHint}`;\n }\n return {\n text,\n truncated: truncation.truncated,\n truncatedBy: truncation.truncatedBy,\n outputBytes: truncation.outputBytes,\n };\n}\n\nexport function createExecCommandTool(\n workspaceCwd: string,\n options?: CreateExecCommandToolOptions,\n): AgentTool {\n return {\n name: 'exec_command',\n description: [\n 'Run shell commands for code inspection, builds, tests, type checks, and verification.',\n 'Use apply_patch for file edits; do not edit files by shell redirection unless explicitly necessary.',\n 'Relative cwd values resolve under the current agent workspace.',\n ].join(' '),\n parameters: ExecCommandSchema,\n label: 'Run Command',\n\n async execute(\n _toolCallId: string,\n params: ExecCommandParams,\n signal?: AbortSignal,\n onUpdate?: AgentToolUpdateCallback<ExecCommandUpdateDetails>,\n ): Promise<AgentToolResult<ExecCommandDetails>> {\n const command = (params.cmd ?? params.command ?? '').trim();\n if (!command) {\n return {\n content: [{ type: 'text', text: 'Error: cmd is required' }],\n details: {\n command,\n cwd: workspaceCwd,\n status: 'failed',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: 0,\n stdout: '',\n stderr: 'cmd is required',\n aggregatedOutput: 'cmd is required',\n failureHint: 'Provide a non-empty cmd value.',\n },\n };\n }\n\n const requestedCwd = params.cwd?.trim()\n ? resolve(workspaceCwd, params.cwd.trim())\n : workspaceCwd;\n const passthroughNames = options?.getSkillPassthroughEnvVarNames?.() ?? [];\n const policy = evaluateExecPolicy({\n command,\n cwd: requestedCwd,\n allowedEnvVars: passthroughNames,\n });\n if (!policy.allowed) {\n const text = `Sandbox blocked command: ${policy.reason}`;\n return {\n content: [{ type: 'text', text }],\n details: {\n command,\n cwd: requestedCwd,\n status: 'blocked',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: Buffer.byteLength(text),\n stdout: '',\n stderr: text,\n aggregatedOutput: text,\n failureHint: 'Use a narrower workspace-safe command or inspect files with read/search tools instead.',\n },\n };\n }\n\n const timeoutMs = Math.min(clampTimeoutMs(params.timeoutMs), policy.timeoutMs);\n const maxOutputChars = clampMaxOutputChars(params.maxOutputChars);\n const startTime = Date.now();\n let stdout = '';\n let stderr = '';\n let aggregatedOutput = '';\n let timedOut = false;\n let settled = false;\n\n return new Promise((resolveTool) => {\n const proc = spawn(command, [], {\n shell: true,\n cwd: policy.effectiveCwd,\n env: {\n ...policy.sanitizedEnv,\n COLUMNS: '200',\n },\n });\n\n const publishDelta = (stream: 'stdout' | 'stderr', raw: Buffer | string) => {\n const text = raw.toString();\n if (!text) return;\n if (stream === 'stdout') stdout += text;\n else stderr += text;\n aggregatedOutput += text;\n\n const delta = text.length > STREAM_DELTA_MAX_CHARS\n ? `${text.slice(0, STREAM_DELTA_MAX_CHARS)}\\n[stream delta truncated]`\n : text;\n onUpdate?.({\n content: [],\n details: {\n kind: 'command_output_delta',\n command,\n cwd: policy.effectiveCwd,\n stream,\n delta,\n },\n });\n };\n\n const finish = (\n exitCode: number | null,\n errorText?: string,\n ) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n signal?.removeEventListener('abort', abort);\n if (errorText) {\n stderr += errorText;\n aggregatedOutput += errorText;\n }\n\n const durationMs = Date.now() - startTime;\n const baseDetails: ExecCommandDetails = {\n command,\n cwd: policy.effectiveCwd,\n status: commandStatus(exitCode, timedOut),\n exitCode,\n durationMs,\n timedOut,\n truncated: false,\n outputBytes: Buffer.byteLength(aggregatedOutput),\n stdout,\n stderr,\n aggregatedOutput,\n };\n baseDetails.failureHint = commandFailureHint(baseDetails);\n const formatted = formatOutputForModel(baseDetails, maxOutputChars);\n const details: ExecCommandDetails = {\n ...baseDetails,\n truncated: formatted.truncated,\n truncatedBy: formatted.truncatedBy,\n outputBytes: formatted.outputBytes,\n };\n\n const result: AgentToolResult<ExecCommandDetails> = {\n content: [{ type: 'text', text: formatted.text }],\n details,\n };\n\n void Promise.resolve(options?.recordGoalEvidence?.({\n kind: timedOut || exitCode !== 0 ? 'command' : isLikelyTestCommand(command) ? 'test' : 'command',\n title: `Command: ${command.slice(0, 120)}`,\n summary: formatted.text.slice(0, 2000),\n data: {\n command,\n cwd: policy.effectiveCwd,\n exitCode,\n durationMs,\n timedOut,\n truncated: details.truncated,\n outputBytes: details.outputBytes,\n },\n })).finally(() => resolveTool(result));\n };\n\n const abort = () => {\n timedOut = true;\n proc.kill('SIGKILL');\n };\n\n const timeout = setTimeout(() => {\n timedOut = true;\n proc.kill('SIGKILL');\n }, timeoutMs);\n\n if (signal?.aborted) {\n abort();\n } else {\n signal?.addEventListener('abort', abort, { once: true });\n }\n\n proc.stdout?.on('data', (data) => publishDelta('stdout', data));\n proc.stderr?.on('data', (data) => publishDelta('stderr', data));\n proc.on('error', (err) => finish(null, `Error: ${err.message}`));\n proc.on('close', (code) => finish(code));\n });\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"exec-command.js","names":[],"sources":["../../../../src/agent/tools/exec-command.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type {\n AgentTool,\n AgentToolResult,\n AgentToolUpdateCallback,\n} from '@earendil-works/pi-agent-core';\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nimport { evaluateExecPolicy } from '../sandbox/exec-policy.js';\nimport { formatSize, truncateTail } from './truncate.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_COMMAND_TIMEOUT_MS = 300_000;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst DEFAULT_MAX_OUTPUT_CHARS = 50_000;\nconst STREAM_DELTA_MAX_CHARS = 16_000;\n\nconst ExecCommandSchema = Type.Object({\n cmd: Type.String({ description: 'Shell command to execute.' }),\n cwd: Type.Optional(\n Type.String({\n description: 'Working directory. Relative paths are resolved under the agent workspace.',\n }),\n ),\n timeoutMs: Type.Optional(\n Type.Number({\n description: 'Command timeout in milliseconds. Defaults to 120000 and is capped at 300000.',\n }),\n ),\n maxOutputChars: Type.Optional(\n Type.Number({\n description: 'Maximum characters returned to the model. UI streaming still receives output deltas.',\n }),\n ),\n});\n\nexport interface ExecCommandDetails {\n command: string;\n cwd: string;\n status: 'success' | 'failed' | 'timed_out' | 'blocked';\n exitCode: number | null;\n durationMs: number;\n timedOut: boolean;\n truncated: boolean;\n truncatedBy?: 'lines' | 'bytes' | null;\n outputBytes: number;\n stdout: string;\n stderr: string;\n aggregatedOutput: string;\n failureHint?: string;\n}\n\nexport interface ExecCommandUpdateDetails {\n kind: 'command_output_delta';\n command: string;\n cwd: string;\n stream: 'stdout' | 'stderr';\n delta: string;\n}\n\nexport interface CreateExecCommandToolOptions {\n /** Env var names allowed through prepareSafeToolEnv even if they match secret heuristics. */\n getSkillPassthroughEnvVarNames?: () => string[];\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\ntype ExecCommandParams = {\n cmd?: string;\n command?: string;\n cwd?: string;\n timeoutMs?: number;\n maxOutputChars?: number;\n};\n\nfunction clampTimeoutMs(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_TIMEOUT_MS;\n return Math.min(MAX_COMMAND_TIMEOUT_MS, Math.max(1_000, Math.floor(n)));\n}\n\nfunction clampMaxOutputChars(raw: unknown): number {\n const n = typeof raw === 'number' && Number.isFinite(raw) ? raw : DEFAULT_MAX_OUTPUT_CHARS;\n return Math.max(1_000, Math.min(500_000, Math.floor(n)));\n}\n\nfunction isLikelyTestCommand(command: string): boolean {\n return /\\b(test|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|type-check|lint)\\b/i.test(command);\n}\n\nfunction commandStatus(exitCode: number | null, timedOut: boolean): ExecCommandDetails['status'] {\n if (timedOut) return 'timed_out';\n if (exitCode === 0) return 'success';\n return 'failed';\n}\n\nfunction commandFailureHint(details: Pick<ExecCommandDetails, 'status' | 'exitCode' | 'timedOut' | 'stderr' | 'stdout'>): string | undefined {\n if (details.status === 'success') return undefined;\n if (details.status === 'timed_out') {\n return 'The command timed out. Re-run a narrower command, increase timeoutMs if the long run is expected, or inspect partial output before retrying.';\n }\n if (details.exitCode !== 0 && details.stderr.trim()) {\n return 'The command failed. Inspect stderr first, fix the underlying issue, then re-run the narrowest relevant verification command.';\n }\n if (details.exitCode !== 0 && details.stdout.trim()) {\n return 'The command failed. Inspect stdout for the failure location, then re-run the narrowest relevant verification command.';\n }\n return 'The command failed without output. Verify the command, cwd, and required dependencies before retrying.';\n}\n\nfunction formatOutputForModel(\n details: ExecCommandDetails,\n maxOutputChars: number,\n): { text: string; truncated: boolean; truncatedBy?: 'lines' | 'bytes' | null; outputBytes: number } {\n let prefix = '';\n if (details.timedOut) {\n prefix += `Command timed out after ${Math.round(details.durationMs / 1000)}s\\n`;\n }\n if (details.exitCode !== 0 && details.exitCode != null) {\n prefix += `Command exited with code ${details.exitCode}\\n`;\n }\n\n const truncation = truncateTail(details.aggregatedOutput, {\n maxBytes: Math.min(maxOutputChars, DEFAULT_MAX_OUTPUT_CHARS),\n });\n let text = `${prefix}${truncation.content}`.trimEnd();\n if (!text) text = '(no output)';\n if (truncation.truncated) {\n text += `\\n\\n[Output truncated: ${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}]`;\n }\n if (details.failureHint) {\n text += `\\n\\nNext action: ${details.failureHint}`;\n }\n return {\n text,\n truncated: truncation.truncated,\n truncatedBy: truncation.truncatedBy,\n outputBytes: truncation.outputBytes,\n };\n}\n\nexport function createExecCommandTool(\n workspaceCwd: string,\n options?: CreateExecCommandToolOptions,\n): AgentTool {\n return {\n name: 'exec_command',\n description: [\n 'Run shell commands for code inspection, builds, tests, type checks, and verification.',\n 'Use apply_patch for file edits; do not edit files by shell redirection unless explicitly necessary.',\n 'Relative cwd values resolve under the current agent workspace.',\n ].join(' '),\n parameters: ExecCommandSchema,\n label: 'Run Command',\n mutationScope: 'unknown',\n supportsParallel: false,\n idempotent: false,\n finalGuardRelevant: true,\n\n async execute(\n _toolCallId: string,\n params: ExecCommandParams,\n signal?: AbortSignal,\n onUpdate?: AgentToolUpdateCallback<ExecCommandUpdateDetails>,\n ): Promise<AgentToolResult<ExecCommandDetails>> {\n const command = (params.cmd ?? params.command ?? '').trim();\n if (!command) {\n return {\n content: [{ type: 'text', text: 'Error: cmd is required' }],\n details: {\n command,\n cwd: workspaceCwd,\n status: 'failed',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: 0,\n stdout: '',\n stderr: 'cmd is required',\n aggregatedOutput: 'cmd is required',\n failureHint: 'Provide a non-empty cmd value.',\n },\n };\n }\n\n const requestedCwd = params.cwd?.trim()\n ? resolve(workspaceCwd, params.cwd.trim())\n : workspaceCwd;\n const passthroughNames = options?.getSkillPassthroughEnvVarNames?.() ?? [];\n const policy = evaluateExecPolicy({\n command,\n cwd: requestedCwd,\n allowedEnvVars: passthroughNames,\n });\n if (!policy.allowed) {\n const text = `Sandbox blocked command: ${policy.reason}`;\n return {\n content: [{ type: 'text', text }],\n details: {\n command,\n cwd: requestedCwd,\n status: 'blocked',\n exitCode: null,\n durationMs: 0,\n timedOut: false,\n truncated: false,\n outputBytes: Buffer.byteLength(text),\n stdout: '',\n stderr: text,\n aggregatedOutput: text,\n failureHint: 'Use a narrower workspace-safe command or inspect files with read/search tools instead.',\n },\n };\n }\n\n const timeoutMs = Math.min(clampTimeoutMs(params.timeoutMs), policy.timeoutMs);\n const maxOutputChars = clampMaxOutputChars(params.maxOutputChars);\n const startTime = Date.now();\n let stdout = '';\n let stderr = '';\n let aggregatedOutput = '';\n let timedOut = false;\n let settled = false;\n\n return new Promise((resolveTool) => {\n const proc = spawn(command, [], {\n shell: true,\n cwd: policy.effectiveCwd,\n env: {\n ...policy.sanitizedEnv,\n COLUMNS: '200',\n },\n });\n\n const publishDelta = (stream: 'stdout' | 'stderr', raw: Buffer | string) => {\n const text = raw.toString();\n if (!text) return;\n if (stream === 'stdout') stdout += text;\n else stderr += text;\n aggregatedOutput += text;\n\n const delta = text.length > STREAM_DELTA_MAX_CHARS\n ? `${text.slice(0, STREAM_DELTA_MAX_CHARS)}\\n[stream delta truncated]`\n : text;\n onUpdate?.({\n content: [],\n details: {\n kind: 'command_output_delta',\n command,\n cwd: policy.effectiveCwd,\n stream,\n delta,\n },\n });\n };\n\n const finish = (\n exitCode: number | null,\n errorText?: string,\n ) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n signal?.removeEventListener('abort', abort);\n if (errorText) {\n stderr += errorText;\n aggregatedOutput += errorText;\n }\n\n const durationMs = Date.now() - startTime;\n const baseDetails: ExecCommandDetails = {\n command,\n cwd: policy.effectiveCwd,\n status: commandStatus(exitCode, timedOut),\n exitCode,\n durationMs,\n timedOut,\n truncated: false,\n outputBytes: Buffer.byteLength(aggregatedOutput),\n stdout,\n stderr,\n aggregatedOutput,\n };\n baseDetails.failureHint = commandFailureHint(baseDetails);\n const formatted = formatOutputForModel(baseDetails, maxOutputChars);\n const details: ExecCommandDetails = {\n ...baseDetails,\n truncated: formatted.truncated,\n truncatedBy: formatted.truncatedBy,\n outputBytes: formatted.outputBytes,\n };\n\n const result: AgentToolResult<ExecCommandDetails> = {\n content: [{ type: 'text', text: formatted.text }],\n details,\n };\n\n void Promise.resolve(options?.recordGoalEvidence?.({\n kind: timedOut || exitCode !== 0 ? 'command' : isLikelyTestCommand(command) ? 'test' : 'command',\n title: `Command: ${command.slice(0, 120)}`,\n summary: formatted.text.slice(0, 2000),\n data: {\n command,\n cwd: policy.effectiveCwd,\n exitCode,\n durationMs,\n timedOut,\n truncated: details.truncated,\n outputBytes: details.outputBytes,\n },\n })).finally(() => resolveTool(result));\n };\n\n const abort = () => {\n timedOut = true;\n proc.kill('SIGKILL');\n };\n\n const timeout = setTimeout(() => {\n timedOut = true;\n proc.kill('SIGKILL');\n }, timeoutMs);\n\n if (signal?.aborted) {\n abort();\n } else {\n signal?.addEventListener('abort', abort, { once: true });\n }\n\n proc.stdout?.on('data', (data) => publishDelta('stdout', data));\n proc.stderr?.on('data', (data) => publishDelta('stderr', data));\n proc.on('error', (err) => finish(null, `Error: ${err.message}`));\n proc.on('close', (code) => finish(code));\n });\n },\n } as any;\n}\n"],"mappings":"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"}
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
* instead of synthesising fake-success results.
|
|
13
13
|
*/
|
|
14
14
|
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from '@earendil-works/pi-agent-core';
|
|
15
|
+
import type { XopcToolMetadata } from './metadata.js';
|
|
16
|
+
import { ToolConcurrencyController } from './concurrency.js';
|
|
17
|
+
import type { ToolConcurrencyController as ToolConcurrencyControllerType } from './concurrency.js';
|
|
15
18
|
export interface ToolExecutorConfig {
|
|
16
19
|
/** Default per-tool timeout when the tool does not declare its own `timeoutMs`. */
|
|
17
20
|
defaultTimeoutMs: number;
|
|
@@ -32,9 +35,8 @@ declare const DEFAULT_CONFIG: ToolExecutorConfig;
|
|
|
32
35
|
* - `idempotent`: marks a tool as safe to retry. The wrapper retries only
|
|
33
36
|
* tools that opt in — write/edit-like tools must leave this `false`.
|
|
34
37
|
*/
|
|
35
|
-
export interface XopcToolHints {
|
|
38
|
+
export interface XopcToolHints extends XopcToolMetadata {
|
|
36
39
|
timeoutMs?: number;
|
|
37
|
-
idempotent?: boolean;
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
42
|
* Execute tool with timeout (always) and retry (only for idempotent tools).
|
|
@@ -45,9 +47,9 @@ export declare function executeToolWithProtection<TDetails>(tool: AgentTool<any,
|
|
|
45
47
|
* Wrap a single tool with the protection pipeline. Preserves the original
|
|
46
48
|
* `execute` signature so streaming `signal` / `onUpdate` reach the tool.
|
|
47
49
|
*/
|
|
48
|
-
export declare function wrapToolWithProtection<TDetails>(tool: AgentTool<any, TDetails>, config?: Partial<ToolExecutorConfig
|
|
50
|
+
export declare function wrapToolWithProtection<TDetails>(tool: AgentTool<any, TDetails>, config?: Partial<ToolExecutorConfig>, concurrency?: ToolConcurrencyControllerType): AgentTool<any, TDetails>;
|
|
49
51
|
/**
|
|
50
52
|
* Wrap a batch of tools with protection.
|
|
51
53
|
*/
|
|
52
|
-
export declare function wrapToolsWithProtection(tools: AgentTool<any, any>[], config?: Partial<ToolExecutorConfig
|
|
54
|
+
export declare function wrapToolsWithProtection(tools: AgentTool<any, any>[], config?: Partial<ToolExecutorConfig>, concurrency?: ToolConcurrencyController): AgentTool<any, any>[];
|
|
53
55
|
export { DEFAULT_CONFIG as DEFAULT_TOOL_EXECUTOR_CONFIG };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { ToolConcurrencyController, resolveToolLockMode } from "./concurrency.js";
|
|
3
4
|
import { TimeoutError, executeWithTimeout } from "../lifecycle/timeout-wrapper.js";
|
|
4
5
|
import { withRetry } from "../../infra/retry.js";
|
|
5
6
|
//#region src/agent/tools/executor.ts
|
|
@@ -91,19 +92,21 @@ async function executeToolWithProtection(tool, toolCallId, params, signal, onUpd
|
|
|
91
92
|
* Wrap a single tool with the protection pipeline. Preserves the original
|
|
92
93
|
* `execute` signature so streaming `signal` / `onUpdate` reach the tool.
|
|
93
94
|
*/
|
|
94
|
-
function wrapToolWithProtection(tool, config) {
|
|
95
|
+
function wrapToolWithProtection(tool, config, concurrency) {
|
|
96
|
+
const lockMode = resolveToolLockMode(tool);
|
|
95
97
|
return {
|
|
96
98
|
...tool,
|
|
97
99
|
async execute(toolCallId, params, signal, onUpdate) {
|
|
98
|
-
|
|
100
|
+
const run = () => executeToolWithProtection(tool, toolCallId, params, signal, onUpdate, config);
|
|
101
|
+
return concurrency ? concurrency.run(lockMode, run) : run();
|
|
99
102
|
}
|
|
100
103
|
};
|
|
101
104
|
}
|
|
102
105
|
/**
|
|
103
106
|
* Wrap a batch of tools with protection.
|
|
104
107
|
*/
|
|
105
|
-
function wrapToolsWithProtection(tools, config) {
|
|
106
|
-
return tools.map((tool) => wrapToolWithProtection(tool, config));
|
|
108
|
+
function wrapToolsWithProtection(tools, config, concurrency = new ToolConcurrencyController()) {
|
|
109
|
+
return tools.map((tool) => wrapToolWithProtection(tool, config, concurrency));
|
|
107
110
|
}
|
|
108
111
|
//#endregion
|
|
109
112
|
export { DEFAULT_CONFIG as DEFAULT_TOOL_EXECUTOR_CONFIG, executeToolWithProtection, wrapToolWithProtection, wrapToolsWithProtection };
|