@xopcai/xopc 0.0.6 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BdC4Y-HX.js} +2 -2
- package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-C-oaSHkm.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-C-oaSHkm.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-BqEUppPO.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-BqEUppPO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-BhqjQ7iL.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-Cli49RKR.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-Cli49RKR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Dkj-Ldpf.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
- package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js +2 -0
- package/dist/gateway/static/root/assets/electron-env-BDtJw9AY.js.map +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-BMcZlaxF.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-D5HEF0KR.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-CXdCSSPl.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-CXdCSSPl.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-DZCZgQE2.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-CX6STpx3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Cph02QZn.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js +16 -0
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-B9O5l3I8.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-B9O5l3I8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-BLiY_O25.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-BLiY_O25.js.map} +1 -1
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
- package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
- package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-BFpnFTed.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-DEhQXWJg.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-DEhQXWJg.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-DKOtWs3-.js} +3 -3
- package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-BYlWP1ep.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js +2 -0
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-DWrn8Az0.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-DWrn8Az0.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-C59WQpM1.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-C59WQpM1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CywXkKml.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CywXkKml.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
- package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-CACj0DBJ.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
- package/dist/gateway/static/root/index.html +16 -16
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +17 -9
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +2 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- 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/context/expand-at-file-mentions.d.ts +4 -0
- package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
- package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/image/index.d.ts +0 -1
- package/dist/src/agent/image/index.js +1 -2
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +38 -11
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +13 -0
- package/dist/src/agent/messaging/command-handler.js +14 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/service.d.ts +16 -1
- package/dist/src/agent/service.js +175 -17
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
- package/dist/src/agent/skills/hub-hash.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/tools/browser/tools.js +2 -2
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/image-tool.js +2 -2
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +1 -1
- package/dist/src/agent/tools/index.js +2 -2
- package/dist/src/agent/tools/read.d.ts +0 -2
- package/dist/src/agent/tools/read.js +1 -3
- package/dist/src/agent/tools/read.js.map +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/index.d.ts +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/pipeline.d.ts +8 -1
- package/dist/src/channels/pipeline.js +49 -4
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/channels/plugin-types.d.ts +14 -0
- package/dist/src/chat-commands/builtins/config.d.ts +4 -0
- package/dist/src/chat-commands/builtins/config.js +197 -0
- package/dist/src/chat-commands/builtins/config.js.map +1 -0
- package/dist/src/chat-commands/builtins/context.d.ts +4 -0
- package/dist/src/chat-commands/builtins/context.js +44 -0
- package/dist/src/chat-commands/builtins/context.js.map +1 -0
- package/dist/src/chat-commands/builtins/session.js +111 -0
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +49 -21
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.d.ts +10 -0
- package/dist/src/chat-commands/config-paths.js +45 -0
- package/dist/src/chat-commands/config-paths.js.map +1 -0
- package/dist/src/chat-commands/config-value.d.ts +12 -0
- package/dist/src/chat-commands/config-value.js +53 -0
- package/dist/src/chat-commands/config-value.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +24 -1
- package/dist/src/chat-commands/context.js +41 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +2 -0
- package/dist/src/chat-commands/index.js +5 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +33 -1
- package/dist/src/cli/commands/agent/interactive.js +1 -1
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent.js +21 -9
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
- package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
- package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
- package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
- package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
- package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
- package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
- package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
- package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
- package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
- package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
- package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
- package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
- package/dist/src/cli/commands/doctor/flow.js +51 -0
- package/dist/src/cli/commands/doctor/flow.js.map +1 -0
- package/dist/src/cli/commands/doctor/format.d.ts +6 -0
- package/dist/src/cli/commands/doctor/format.js +61 -0
- package/dist/src/cli/commands/doctor/format.js.map +1 -0
- package/dist/src/cli/commands/doctor/index.js +44 -0
- package/dist/src/cli/commands/doctor/index.js.map +1 -0
- package/dist/src/cli/commands/doctor/types.d.ts +20 -0
- package/dist/src/cli/commands/doctor/types.js +1 -0
- package/dist/src/cli/commands/extension.js +10 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/init.js +1 -2
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts +1 -0
- package/dist/src/cli/commands/update.js +171 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -2
- package/dist/src/cli/index.js +4 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/init-workspace.js +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +4 -3
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.d.ts +15 -15
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +1 -1
- package/dist/src/config/runtime-overrides.d.ts +8 -0
- package/dist/src/config/runtime-overrides.js +40 -0
- package/dist/src/config/runtime-overrides.js.map +1 -0
- package/dist/src/config/schema.d.ts +34 -104
- package/dist/src/config/schema.js +18 -39
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +1 -1
- package/dist/src/extensions/loader.js +5 -8
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +6 -1
- package/dist/src/extensions/sdk/index.js.map +1 -0
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
- package/dist/src/gateway/hono/routes/doctor.js +35 -0
- package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
- package/dist/src/gateway/hono/routes/index.js +4 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +64 -11
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.d.ts +3 -0
- package/dist/src/gateway/hono/routes/update.js +141 -0
- package/dist/src/gateway/hono/routes/update.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +82 -2
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +98 -3
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/service.d.ts +1 -4
- package/dist/src/gateway/service.js +13 -20
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
- package/dist/src/gateway/workspace-fs-file-list.js +56 -0
- package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
- package/dist/src/gateway/workspace-ripgrep.js +88 -4
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/infra/update-channels.d.ts +14 -0
- package/dist/src/infra/update-channels.js +30 -0
- package/dist/src/infra/update-channels.js.map +1 -0
- package/dist/src/infra/update-check.d.ts +53 -0
- package/dist/src/infra/update-check.js +155 -0
- package/dist/src/infra/update-check.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +18 -0
- package/dist/src/infra/update-runner.js +112 -0
- package/dist/src/infra/update-runner.js.map +1 -0
- package/dist/src/infra/update-startup.d.ts +20 -0
- package/dist/src/infra/update-startup.js +246 -0
- package/dist/src/infra/update-startup.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
- package/dist/src/providers/extension-stream-bridge.js +239 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -0
- package/dist/src/providers/index.d.ts +7 -2
- package/dist/src/providers/index.js +77 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/plugin-registry.js +92 -87
- package/dist/src/providers/plugin-registry.js.map +1 -1
- package/dist/src/routing/bindings.js +1 -1
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +2 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/session-key.d.ts +0 -5
- package/dist/src/routing/session-key.js +1 -27
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/chat-export.d.ts +5 -0
- package/dist/src/session/chat-export.js +35 -0
- package/dist/src/session/chat-export.js.map +1 -0
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/store.d.ts +1 -1
- package/dist/src/session/store.js +3 -7
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/types.d.ts +0 -10
- package/dist/src/session/types.js.map +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +2 -2
- package/dist/gateway/static/root/assets/agents-B6s2BvpH.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
- package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
- package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
- package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
- package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
- package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
- package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
- package/dist/src/acp/commands.d.ts +0 -11
- package/dist/src/acp/commands.js +0 -17
- package/dist/src/acp/commands.js.map +0 -1
- package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
- package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
- package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
- package/dist/src/acp/control-plane/index.d.ts +0 -10
- package/dist/src/acp/control-plane/index.js +0 -6
- package/dist/src/acp/control-plane/manager.d.ts +0 -86
- package/dist/src/acp/control-plane/manager.js +0 -502
- package/dist/src/acp/control-plane/manager.js.map +0 -1
- package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
- package/dist/src/acp/control-plane/manager.types.js +0 -14
- package/dist/src/acp/control-plane/manager.types.js.map +0 -1
- package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
- package/dist/src/acp/control-plane/manager.utils.js +0 -46
- package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
- package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
- package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
- package/dist/src/acp/control-plane/runtime-cache.js +0 -58
- package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
- package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
- package/dist/src/acp/control-plane/runtime-options.js +0 -92
- package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
- package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
- package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
- package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
- package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
- package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
- package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
- package/dist/src/acp/control-plane/session-store.d.ts +0 -39
- package/dist/src/acp/control-plane/session-store.js +0 -149
- package/dist/src/acp/control-plane/session-store.js.map +0 -1
- package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
- package/dist/src/acp/control-plane/turn-manager.js +0 -134
- package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
- package/dist/src/acp/event-mapper.d.ts +0 -48
- package/dist/src/acp/event-mapper.js +0 -94
- package/dist/src/acp/event-mapper.js.map +0 -1
- package/dist/src/acp/index.d.ts +0 -10
- package/dist/src/acp/index.js +0 -5
- package/dist/src/acp/meta.d.ts +0 -15
- package/dist/src/acp/meta.js +0 -36
- package/dist/src/acp/meta.js.map +0 -1
- package/dist/src/acp/routing-integration.d.ts +0 -37
- package/dist/src/acp/routing-integration.js +0 -58
- package/dist/src/acp/routing-integration.js.map +0 -1
- package/dist/src/acp/runtime/backends/index.d.ts +0 -4
- package/dist/src/acp/runtime/backends/index.js +0 -2
- package/dist/src/acp/runtime/backends/local.d.ts +0 -136
- package/dist/src/acp/runtime/backends/local.js +0 -603
- package/dist/src/acp/runtime/backends/local.js.map +0 -1
- package/dist/src/acp/runtime/error-text.d.ts +0 -16
- package/dist/src/acp/runtime/error-text.js +0 -40
- package/dist/src/acp/runtime/error-text.js.map +0 -1
- package/dist/src/acp/runtime/errors.d.ts +0 -31
- package/dist/src/acp/runtime/errors.js +0 -47
- package/dist/src/acp/runtime/errors.js.map +0 -1
- package/dist/src/acp/runtime/index.d.ts +0 -7
- package/dist/src/acp/runtime/index.js +0 -4
- package/dist/src/acp/runtime/registry.d.ts +0 -35
- package/dist/src/acp/runtime/registry.js +0 -85
- package/dist/src/acp/runtime/registry.js.map +0 -1
- package/dist/src/acp/runtime/session-identity.d.ts +0 -35
- package/dist/src/acp/runtime/session-identity.js +0 -134
- package/dist/src/acp/runtime/session-identity.js.map +0 -1
- package/dist/src/acp/runtime/types.d.ts +0 -214
- package/dist/src/acp/secret-file.d.ts +0 -7
- package/dist/src/acp/secret-file.js +0 -19
- package/dist/src/acp/secret-file.js.map +0 -1
- package/dist/src/acp/server.d.ts +0 -48
- package/dist/src/acp/server.js +0 -300
- package/dist/src/acp/server.js.map +0 -1
- package/dist/src/acp/session.d.ts +0 -29
- package/dist/src/acp/session.js +0 -30
- package/dist/src/acp/session.js.map +0 -1
- package/dist/src/acp/types.d.ts +0 -39
- package/dist/src/acp/types.js +0 -13
- package/dist/src/acp/types.js.map +0 -1
- package/dist/src/agent/image/describe-images.d.ts +0 -18
- package/dist/src/agent/image/describe-images.js +0 -19
- package/dist/src/agent/image/describe-images.js.map +0 -1
- package/dist/src/cli/commands/acp.d.ts +0 -4
- package/dist/src/cli/commands/acp.js +0 -200
- package/dist/src/cli/commands/acp.js.map +0 -1
- /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","names":[],"sources":["../../../../../src/agent/tools/browser/tools.ts"],"sourcesContent":["import type { Static } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport type { Locator, Page } from 'playwright-core';\n\nimport type { Config } from '../../../config/schema.js';\nimport { describeImagesWithPiAi } from '../../image/describe-images.js';\nimport { buildImageToolTextResult } from '../../image/image-helpers.js';\nimport { runWithImageModelFallback } from '../../image/image-model-fallback.js';\nimport { resolveImageModelConfigForTool } from '../image-tool.js';\nimport type { BrowserManager } from './manager.js';\nimport {\n BrowserClickSchema,\n BrowserNavigateSchema,\n BrowserScreenshotSchema,\n BrowserScrollSchema,\n BrowserSnapshotSchema,\n BrowserTypeSchema,\n} from './schemas.js';\nimport { assertBrowserUrlAllowed } from './url-policy.js';\n\nconst DEFAULT_SNAPSHOT_MAX = 30_000;\nconst NAV_TIMEOUT_MS = 30_000;\nconst MAX_SCREENSHOT_BYTES = 6 * 1024 * 1024;\n\nexport interface CreateBrowserToolsDeps {\n getManager: () => BrowserManager;\n getTaskId: () => string;\n getConfig: () => Config | undefined;\n}\n\nfunction resolveClickLocator(page: Page, params: Static<typeof BrowserClickSchema>): Locator {\n const hasSel = Boolean(params.selector?.trim());\n const hasText = Boolean(params.text?.trim());\n const hasRole = Boolean(params.role?.trim());\n const n = (hasSel ? 1 : 0) + (hasText ? 1 : 0) + (hasRole ? 1 : 0);\n if (n !== 1) {\n throw new Error('Provide exactly one of: selector, text, role');\n }\n if (hasSel) {\n return page.locator(params.selector!.trim()).first();\n }\n if (hasText) {\n return page.getByText(params.text!.trim(), { exact: false }).first();\n }\n const raw = params.role!.trim();\n const idx = raw.indexOf(':');\n const role = (idx >= 0 ? raw.slice(0, idx) : raw).trim();\n const name = idx >= 0 ? raw.slice(idx + 1).trim() : '';\n if (!role) {\n throw new Error('Invalid role: empty ARIA role');\n }\n // Playwright typings list roles as a union; the model may pass any valid ARIA role string.\n return page.getByRole(role as never, name ? { name } : undefined).first();\n}\n\nfunction resolveTypeLocator(page: Page, params: Static<typeof BrowserTypeSchema>): Locator {\n const hasSel = Boolean(params.selector?.trim());\n const hasLab = Boolean(params.label?.trim());\n if (hasSel === hasLab) {\n throw new Error('Provide exactly one of: selector, label');\n }\n if (hasSel) {\n return page.locator(params.selector!.trim()).first();\n }\n return page.getByLabel(params.label!.trim()).first();\n}\n\nasync function ariaSnapshotFor(\n page: Page,\n selector: string | undefined,\n maxLength: number,\n): Promise<string> {\n const loc = selector?.trim() ? page.locator(selector.trim()).first() : page.locator('body');\n await loc.waitFor({ state: 'attached', timeout: 15_000 });\n let text = await loc.ariaSnapshot({ mode: 'ai', timeout: 15_000 });\n if (!text || !text.trim()) {\n text = '(empty snapshot)';\n }\n if (text.length > maxLength) {\n text = `${text.slice(0, maxLength)}\\n... (truncated)`;\n }\n return text;\n}\n\nexport function createBrowserTools(deps: CreateBrowserToolsDeps): AgentTool<any, any>[] {\n const pageFor = () => deps.getManager().getPage(deps.getTaskId());\n\n const navigate: AgentTool<typeof BrowserNavigateSchema, { url: string; title: string }> = {\n name: 'browser_navigate',\n label: '🌐 Browser Navigate',\n description:\n 'Navigate the headless browser to a URL. The page persists for this chat session.\\n' +\n 'Call `browser_snapshot` after navigation to inspect the UI. Only http(s) public URLs; private IPs and localhost are blocked.',\n parameters: BrowserNavigateSchema,\n\n async execute(_id, params, signal) {\n assertBrowserUrlAllowed(params.url);\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const waitUntil = params.waitFor ?? 'domcontentloaded';\n await page.goto(params.url, {\n waitUntil,\n timeout: NAV_TIMEOUT_MS,\n });\n const title = await page.title();\n const url = page.url();\n return {\n content: [{ type: 'text', text: `Navigated to: ${title}\\nURL: ${url}` }],\n details: { url, title },\n };\n },\n };\n\n const snapshot: AgentTool<typeof BrowserSnapshotSchema, { length: number }> = {\n name: 'browser_snapshot',\n label: '📸 Browser Snapshot',\n description:\n 'Capture an AI-oriented ARIA snapshot of the current page (YAML-like tree with element refs).\\n' +\n 'Use after `browser_navigate` to see interactive elements before `browser_click` / `browser_type`.',\n parameters: BrowserSnapshotSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const maxLength = params.maxLength ?? DEFAULT_SNAPSHOT_MAX;\n try {\n const text = await ariaSnapshotFor(page, params.selector, maxLength);\n return {\n content: [{ type: 'text', text }],\n details: { length: text.length },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Snapshot failed: ${msg}` }],\n details: { length: 0 },\n };\n }\n },\n };\n\n const click: AgentTool<typeof BrowserClickSchema, { ok: boolean }> = {\n name: 'browser_click',\n label: '🖱️ Browser Click',\n description:\n 'Click an element. Provide exactly one targeting mode: `selector` (CSS), `text` (visible text), or `role` (e.g. `button:Submit`).',\n parameters: BrowserClickSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n try {\n const loc = resolveClickLocator(page, params);\n await loc.click({ timeout: 15_000 });\n return {\n content: [{ type: 'text', text: 'Click succeeded.' }],\n details: { ok: true },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Click failed: ${msg}` }],\n details: { ok: false },\n };\n }\n },\n };\n\n const typeTool: AgentTool<typeof BrowserTypeSchema, { ok: boolean }> = {\n name: 'browser_type',\n label: '⌨️ Browser Type',\n description:\n 'Type into an input. Provide exactly one of `selector` or `label` (associated label text). Optional `pressEnter` to submit.',\n parameters: BrowserTypeSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n try {\n const loc = resolveTypeLocator(page, params);\n await loc.clear({ timeout: 5000 }).catch(() => {});\n await loc.fill(params.text, { timeout: 15_000 });\n if (params.pressEnter) {\n await page.keyboard.press('Enter');\n }\n return {\n content: [{ type: 'text', text: 'Typed into field.' }],\n details: { ok: true },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Type failed: ${msg}` }],\n details: { ok: false },\n };\n }\n },\n };\n\n const scroll: AgentTool<typeof BrowserScrollSchema, { ok: boolean }> = {\n name: 'browser_scroll',\n label: '📜 Browser Scroll',\n description: 'Scroll the page up or down by a pixel amount (default 500).',\n parameters: BrowserScrollSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const amount = params.amount ?? 500;\n const dy = params.direction === 'down' ? amount : -amount;\n await page.evaluate(\n ({ deltaY }) => {\n (globalThis as unknown as { scrollBy: (x: number, y: number) => void }).scrollBy(0, deltaY);\n },\n { deltaY: dy },\n );\n return {\n content: [{ type: 'text', text: `Scrolled ${params.direction} by ${amount}px.` }],\n details: { ok: true },\n };\n },\n };\n\n const screenshot: AgentTool<typeof BrowserScreenshotSchema, Record<string, unknown>> = {\n name: 'browser_screenshot',\n label: '🖼️ Browser Screenshot',\n description:\n 'Take a PNG screenshot of the viewport or a CSS selector. When `agents.defaults.imageModel` is configured, runs vision on the image using `description` as the prompt (default: short UI summary).',\n parameters: BrowserScreenshotSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const cfg = deps.getConfig();\n let buf: Buffer;\n try {\n if (params.selector?.trim()) {\n const loc = page.locator(params.selector.trim()).first();\n await loc.waitFor({ state: 'visible', timeout: 15_000 });\n buf = await loc.screenshot({ type: 'png', timeout: 15_000 });\n } else {\n buf = await page.screenshot({ type: 'png', fullPage: false, timeout: 15_000 });\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Screenshot failed: ${msg}` }],\n details: { error: msg },\n };\n }\n\n if (buf.length > MAX_SCREENSHOT_BYTES) {\n return {\n content: [\n {\n type: 'text',\n text: `Screenshot too large (${buf.length} bytes, max ${MAX_SCREENSHOT_BYTES}). Try a narrower selector.`,\n },\n ],\n details: { error: 'too_large', bytes: buf.length },\n };\n }\n\n const imageModelConfig = resolveImageModelConfigForTool({ cfg });\n const prompt =\n params.description?.trim() ||\n 'Describe this browser screenshot briefly. Focus on visible text, controls, and actionable UI state.';\n\n if (!imageModelConfig) {\n return {\n content: [\n {\n type: 'text',\n text:\n `Captured PNG screenshot (${buf.length} bytes). Configure agents.defaults.imageModel for automatic visual description.`,\n },\n ],\n details: { bytes: buf.length, vision: false },\n };\n }\n\n try {\n const runResult = await runWithImageModelFallback({\n toolConfig: imageModelConfig,\n modelOverride: undefined,\n run: async (modelRef) => {\n const { text, provider, model } = await describeImagesWithPiAi({\n modelRef,\n prompt,\n images: [{ buffer: buf, mimeType: 'image/png' }],\n timeoutMs: 60_000,\n signal,\n });\n return { text, provider, model };\n },\n });\n const { result: inner, attempts } = runResult;\n return buildImageToolTextResult(\n { text: inner.text, provider: inner.provider, model: inner.model, attempts },\n { bytes: buf.length, vision: true },\n );\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [\n {\n type: 'text',\n text: `Screenshot captured (${buf.length} bytes) but vision failed: ${msg}`,\n },\n ],\n details: { bytes: buf.length, visionError: msg },\n };\n }\n },\n };\n\n return [navigate, snapshot, click, typeTool, scroll, screenshot];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tools.js","names":[],"sources":["../../../../../src/agent/tools/browser/tools.ts"],"sourcesContent":["import type { Static } from '@sinclair/typebox';\nimport type { AgentTool } from '@mariozechner/pi-agent-core';\nimport type { Locator, Page } from 'playwright-core';\n\nimport type { Config } from '../../../config/schema.js';\nimport { describeImages } from '../../image/understanding/runtime.js';\nimport { buildImageToolTextResult } from '../../image/image-helpers.js';\nimport { runWithImageModelFallback } from '../../image/image-model-fallback.js';\nimport { resolveImageModelConfigForTool } from '../image-tool.js';\nimport type { BrowserManager } from './manager.js';\nimport {\n BrowserClickSchema,\n BrowserNavigateSchema,\n BrowserScreenshotSchema,\n BrowserScrollSchema,\n BrowserSnapshotSchema,\n BrowserTypeSchema,\n} from './schemas.js';\nimport { assertBrowserUrlAllowed } from './url-policy.js';\n\nconst DEFAULT_SNAPSHOT_MAX = 30_000;\nconst NAV_TIMEOUT_MS = 30_000;\nconst MAX_SCREENSHOT_BYTES = 6 * 1024 * 1024;\n\nexport interface CreateBrowserToolsDeps {\n getManager: () => BrowserManager;\n getTaskId: () => string;\n getConfig: () => Config | undefined;\n}\n\nfunction resolveClickLocator(page: Page, params: Static<typeof BrowserClickSchema>): Locator {\n const hasSel = Boolean(params.selector?.trim());\n const hasText = Boolean(params.text?.trim());\n const hasRole = Boolean(params.role?.trim());\n const n = (hasSel ? 1 : 0) + (hasText ? 1 : 0) + (hasRole ? 1 : 0);\n if (n !== 1) {\n throw new Error('Provide exactly one of: selector, text, role');\n }\n if (hasSel) {\n return page.locator(params.selector!.trim()).first();\n }\n if (hasText) {\n return page.getByText(params.text!.trim(), { exact: false }).first();\n }\n const raw = params.role!.trim();\n const idx = raw.indexOf(':');\n const role = (idx >= 0 ? raw.slice(0, idx) : raw).trim();\n const name = idx >= 0 ? raw.slice(idx + 1).trim() : '';\n if (!role) {\n throw new Error('Invalid role: empty ARIA role');\n }\n // Playwright typings list roles as a union; the model may pass any valid ARIA role string.\n return page.getByRole(role as never, name ? { name } : undefined).first();\n}\n\nfunction resolveTypeLocator(page: Page, params: Static<typeof BrowserTypeSchema>): Locator {\n const hasSel = Boolean(params.selector?.trim());\n const hasLab = Boolean(params.label?.trim());\n if (hasSel === hasLab) {\n throw new Error('Provide exactly one of: selector, label');\n }\n if (hasSel) {\n return page.locator(params.selector!.trim()).first();\n }\n return page.getByLabel(params.label!.trim()).first();\n}\n\nasync function ariaSnapshotFor(\n page: Page,\n selector: string | undefined,\n maxLength: number,\n): Promise<string> {\n const loc = selector?.trim() ? page.locator(selector.trim()).first() : page.locator('body');\n await loc.waitFor({ state: 'attached', timeout: 15_000 });\n let text = await loc.ariaSnapshot({ mode: 'ai', timeout: 15_000 });\n if (!text || !text.trim()) {\n text = '(empty snapshot)';\n }\n if (text.length > maxLength) {\n text = `${text.slice(0, maxLength)}\\n... (truncated)`;\n }\n return text;\n}\n\nexport function createBrowserTools(deps: CreateBrowserToolsDeps): AgentTool<any, any>[] {\n const pageFor = () => deps.getManager().getPage(deps.getTaskId());\n\n const navigate: AgentTool<typeof BrowserNavigateSchema, { url: string; title: string }> = {\n name: 'browser_navigate',\n label: '🌐 Browser Navigate',\n description:\n 'Navigate the headless browser to a URL. The page persists for this chat session.\\n' +\n 'Call `browser_snapshot` after navigation to inspect the UI. Only http(s) public URLs; private IPs and localhost are blocked.',\n parameters: BrowserNavigateSchema,\n\n async execute(_id, params, signal) {\n assertBrowserUrlAllowed(params.url);\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const waitUntil = params.waitFor ?? 'domcontentloaded';\n await page.goto(params.url, {\n waitUntil,\n timeout: NAV_TIMEOUT_MS,\n });\n const title = await page.title();\n const url = page.url();\n return {\n content: [{ type: 'text', text: `Navigated to: ${title}\\nURL: ${url}` }],\n details: { url, title },\n };\n },\n };\n\n const snapshot: AgentTool<typeof BrowserSnapshotSchema, { length: number }> = {\n name: 'browser_snapshot',\n label: '📸 Browser Snapshot',\n description:\n 'Capture an AI-oriented ARIA snapshot of the current page (YAML-like tree with element refs).\\n' +\n 'Use after `browser_navigate` to see interactive elements before `browser_click` / `browser_type`.',\n parameters: BrowserSnapshotSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const maxLength = params.maxLength ?? DEFAULT_SNAPSHOT_MAX;\n try {\n const text = await ariaSnapshotFor(page, params.selector, maxLength);\n return {\n content: [{ type: 'text', text }],\n details: { length: text.length },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Snapshot failed: ${msg}` }],\n details: { length: 0 },\n };\n }\n },\n };\n\n const click: AgentTool<typeof BrowserClickSchema, { ok: boolean }> = {\n name: 'browser_click',\n label: '🖱️ Browser Click',\n description:\n 'Click an element. Provide exactly one targeting mode: `selector` (CSS), `text` (visible text), or `role` (e.g. `button:Submit`).',\n parameters: BrowserClickSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n try {\n const loc = resolveClickLocator(page, params);\n await loc.click({ timeout: 15_000 });\n return {\n content: [{ type: 'text', text: 'Click succeeded.' }],\n details: { ok: true },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Click failed: ${msg}` }],\n details: { ok: false },\n };\n }\n },\n };\n\n const typeTool: AgentTool<typeof BrowserTypeSchema, { ok: boolean }> = {\n name: 'browser_type',\n label: '⌨️ Browser Type',\n description:\n 'Type into an input. Provide exactly one of `selector` or `label` (associated label text). Optional `pressEnter` to submit.',\n parameters: BrowserTypeSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n try {\n const loc = resolveTypeLocator(page, params);\n await loc.clear({ timeout: 5000 }).catch(() => {});\n await loc.fill(params.text, { timeout: 15_000 });\n if (params.pressEnter) {\n await page.keyboard.press('Enter');\n }\n return {\n content: [{ type: 'text', text: 'Typed into field.' }],\n details: { ok: true },\n };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Type failed: ${msg}` }],\n details: { ok: false },\n };\n }\n },\n };\n\n const scroll: AgentTool<typeof BrowserScrollSchema, { ok: boolean }> = {\n name: 'browser_scroll',\n label: '📜 Browser Scroll',\n description: 'Scroll the page up or down by a pixel amount (default 500).',\n parameters: BrowserScrollSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const amount = params.amount ?? 500;\n const dy = params.direction === 'down' ? amount : -amount;\n await page.evaluate(\n ({ deltaY }) => {\n (globalThis as unknown as { scrollBy: (x: number, y: number) => void }).scrollBy(0, deltaY);\n },\n { deltaY: dy },\n );\n return {\n content: [{ type: 'text', text: `Scrolled ${params.direction} by ${amount}px.` }],\n details: { ok: true },\n };\n },\n };\n\n const screenshot: AgentTool<typeof BrowserScreenshotSchema, Record<string, unknown>> = {\n name: 'browser_screenshot',\n label: '🖼️ Browser Screenshot',\n description:\n 'Take a PNG screenshot of the viewport or a CSS selector. When `agents.defaults.imageModel` is configured, runs vision on the image using `description` as the prompt (default: short UI summary).',\n parameters: BrowserScreenshotSchema,\n\n async execute(_id, params, signal) {\n if (signal?.aborted) {\n throw new Error('aborted');\n }\n const page = await pageFor();\n const cfg = deps.getConfig();\n let buf: Buffer;\n try {\n if (params.selector?.trim()) {\n const loc = page.locator(params.selector.trim()).first();\n await loc.waitFor({ state: 'visible', timeout: 15_000 });\n buf = await loc.screenshot({ type: 'png', timeout: 15_000 });\n } else {\n buf = await page.screenshot({ type: 'png', fullPage: false, timeout: 15_000 });\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [{ type: 'text', text: `Screenshot failed: ${msg}` }],\n details: { error: msg },\n };\n }\n\n if (buf.length > MAX_SCREENSHOT_BYTES) {\n return {\n content: [\n {\n type: 'text',\n text: `Screenshot too large (${buf.length} bytes, max ${MAX_SCREENSHOT_BYTES}). Try a narrower selector.`,\n },\n ],\n details: { error: 'too_large', bytes: buf.length },\n };\n }\n\n const imageModelConfig = resolveImageModelConfigForTool({ cfg });\n const prompt =\n params.description?.trim() ||\n 'Describe this browser screenshot briefly. Focus on visible text, controls, and actionable UI state.';\n\n if (!imageModelConfig) {\n return {\n content: [\n {\n type: 'text',\n text:\n `Captured PNG screenshot (${buf.length} bytes). Configure agents.defaults.imageModel for automatic visual description.`,\n },\n ],\n details: { bytes: buf.length, vision: false },\n };\n }\n\n try {\n const runResult = await runWithImageModelFallback({\n toolConfig: imageModelConfig,\n modelOverride: undefined,\n run: async (modelRef) => {\n const { text, provider, model } = await describeImages({\n modelRef,\n prompt,\n images: [{ buffer: buf, mimeType: 'image/png' }],\n timeoutMs: 60_000,\n signal,\n });\n return { text, provider, model };\n },\n });\n const { result: inner, attempts } = runResult;\n return buildImageToolTextResult(\n { text: inner.text, provider: inner.provider, model: inner.model, attempts },\n { bytes: buf.length, vision: true },\n );\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return {\n content: [\n {\n type: 'text',\n text: `Screenshot captured (${buf.length} bytes) but vision failed: ${msg}`,\n },\n ],\n details: { bytes: buf.length, visionError: msg },\n };\n }\n },\n };\n\n return [navigate, snapshot, click, typeTool, scroll, screenshot];\n}\n"],"mappings":"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"}
|
|
@@ -3,9 +3,9 @@ import { applyImageGenerationModelConfigDefaults } from "../image/image-helpers.
|
|
|
3
3
|
import { DASHSCOPE_DEFAULT_IMAGE_MODEL, OPENAI_DEFAULT_IMAGE_MODEL } from "../image/generation/constants.js";
|
|
4
4
|
import { generateImage, listImageGenerationProvidersSummary } from "../image/generation/runtime.js";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { randomBytes } from "node:crypto";
|
|
7
6
|
import { Type } from "@sinclair/typebox";
|
|
8
7
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
9
|
//#region src/agent/tools/image-generate-tool.ts
|
|
10
10
|
const DEFAULT_COUNT = 1;
|
|
11
11
|
const MAX_COUNT = 4;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveImageModelConfigForTool } from "../image/tool-model-config.js";
|
|
2
2
|
import { buildImageToolTextResult, resolvePromptAndModelOverride } from "../image/image-helpers.js";
|
|
3
|
-
import {
|
|
3
|
+
import { describeImages } from "../image/understanding/runtime.js";
|
|
4
4
|
import { runWithImageModelFallback } from "../image/image-model-fallback.js";
|
|
5
5
|
import { loadImageForToolInput } from "../image/load-image-media.js";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
@@ -121,7 +121,7 @@ function createImageTool(options) {
|
|
|
121
121
|
toolConfig: imageModelConfig,
|
|
122
122
|
modelOverride,
|
|
123
123
|
run: async (modelRef) => {
|
|
124
|
-
const { text, provider, model } = await
|
|
124
|
+
const { text, provider, model } = await describeImages({
|
|
125
125
|
modelRef,
|
|
126
126
|
prompt: promptRaw,
|
|
127
127
|
images: loadedImages.map((img) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-tool.js","names":[],"sources":["../../../../src/agent/tools/image-tool.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport type { Config } from '../../config/schema.js';\nimport { describeImagesWithPiAi } from '../image/describe-images.js';\nimport { buildImageToolTextResult, resolvePromptAndModelOverride } from '../image/image-helpers.js';\nimport { runWithImageModelFallback } from '../image/image-model-fallback.js';\nimport { loadImageForToolInput } from '../image/load-image-media.js';\nimport {\n resolveImageModelConfigForTool,\n type ToolModelConfig,\n} from '../image/tool-model-config.js';\n\nconst DEFAULT_PROMPT = 'Describe the image.';\nconst DEFAULT_MAX_IMAGES = 20;\n\nexport { resolveImageModelConfigForTool } from '../image/tool-model-config.js';\n\nfunction pickMaxBytes(cfg?: Config, maxBytesMb?: number): number {\n if (typeof maxBytesMb === 'number' && Number.isFinite(maxBytesMb) && maxBytesMb > 0) {\n return Math.floor(maxBytesMb * 1024 * 1024);\n }\n const configured = cfg?.agents?.defaults?.mediaMaxMb;\n if (typeof configured === 'number' && Number.isFinite(configured) && configured > 0) {\n return Math.floor(configured * 1024 * 1024);\n }\n return 20 * 1024 * 1024;\n}\n\nexport function createImageTool(options: {\n config?: Config;\n workspace: string;\n /** When true, session model already receives images in the user message. */\n modelHasVision?: boolean;\n}): AgentTool<any, Record<string, unknown>> | null {\n const imageModelConfig = resolveImageModelConfigForTool({ cfg: options.config });\n if (!imageModelConfig) {\n return null;\n }\n\n const description = options.modelHasVision\n ? 'Analyze one or more images with a vision model. Use `image` for a single path/URL, or `images` for multiple (up to 20). Only use when images were NOT already in the user message.'\n : 'Analyze one or more images using the configured image model (agents.defaults.imageModel). Use `image` or `images` for paths/URLs; optional `prompt` for what to extract.';\n\n const localRoots = [options.workspace];\n\n return {\n name: 'image',\n label: 'Image',\n description,\n parameters: Type.Object({\n prompt: Type.Optional(Type.String()),\n image: Type.Optional(Type.String({ description: 'Single image path or URL.' })),\n images: Type.Optional(\n Type.Array(Type.String(), {\n description: 'Multiple image paths or URLs (up to maxImages, default 20).',\n }),\n ),\n model: Type.Optional(Type.String({ description: 'Optional provider/model override.' })),\n maxBytesMb: Type.Optional(Type.Number()),\n maxImages: Type.Optional(Type.Number()),\n }),\n async execute(\n _toolCallId: string,\n args: Record<string, unknown>,\n ): Promise<AgentToolResult<Record<string, unknown>>> {\n const record = args && typeof args === 'object' ? args : {};\n\n const imageCandidates: string[] = [];\n if (typeof record.image === 'string') {\n imageCandidates.push(record.image);\n }\n if (Array.isArray(record.images)) {\n imageCandidates.push(...record.images.filter((v): v is string => typeof v === 'string'));\n }\n\n const seenImages = new Set<string>();\n const imageInputs: string[] = [];\n for (const candidate of imageCandidates) {\n const trimmedCandidate = candidate.trim();\n const normalizedForDedupe = trimmedCandidate.startsWith('@')\n ? trimmedCandidate.slice(1).trim()\n : trimmedCandidate;\n if (!normalizedForDedupe || seenImages.has(normalizedForDedupe)) {\n continue;\n }\n seenImages.add(normalizedForDedupe);\n imageInputs.push(trimmedCandidate);\n }\n\n if (imageInputs.length === 0) {\n return {\n content: [{ type: 'text', text: 'Error: provide `image` or `images`.' }],\n details: { error: 'missing_image' },\n };\n }\n\n const maxImagesRaw = typeof record.maxImages === 'number' ? record.maxImages : undefined;\n const maxImages =\n typeof maxImagesRaw === 'number' && Number.isFinite(maxImagesRaw) && maxImagesRaw > 0\n ? Math.floor(maxImagesRaw)\n : DEFAULT_MAX_IMAGES;\n if (imageInputs.length > maxImages) {\n return {\n content: [\n {\n type: 'text',\n text: `Too many images: ${imageInputs.length} (max ${maxImages}).`,\n },\n ],\n details: { error: 'too_many_images', count: imageInputs.length, max: maxImages },\n };\n }\n\n const { prompt: promptRaw, modelOverride } = resolvePromptAndModelOverride(\n record,\n DEFAULT_PROMPT,\n );\n const maxBytesMb = typeof record.maxBytesMb === 'number' ? record.maxBytesMb : undefined;\n const maxBytes = pickMaxBytes(options.config, maxBytesMb);\n\n const loadedImages: Array<{ buffer: Buffer; mimeType: string; resolvedImage: string }> = [];\n\n for (const imageRawInput of imageInputs) {\n const trimmed = imageRawInput.trim();\n const imageRaw = trimmed.startsWith('@') ? trimmed.slice(1).trim() : trimmed;\n if (!imageRaw) {\n return {\n content: [{ type: 'text', text: 'Error: empty image entry.' }],\n details: { error: 'empty_image' },\n };\n }\n\n const looksLikeWindowsDrivePath = /^[a-zA-Z]:[\\\\/]/.test(imageRaw);\n const hasScheme = /^[a-z][a-z0-9+.-]*:/i.test(imageRaw);\n const isFileUrl = /^file:/i.test(imageRaw);\n const isHttpUrl = /^https?:\\/\\//i.test(imageRaw);\n const isDataUrl = /^data:/i.test(imageRaw);\n if (hasScheme && !looksLikeWindowsDrivePath && !isFileUrl && !isHttpUrl && !isDataUrl) {\n return {\n content: [\n {\n type: 'text',\n text: `Unsupported image reference: ${imageRawInput}. Use a path, file://, data:, or http(s) URL.`,\n },\n ],\n details: { error: 'unsupported_image_reference', image: imageRawInput },\n };\n }\n\n try {\n const media = await loadImageForToolInput(imageRaw, {\n maxBytes,\n workspace: options.workspace,\n localRoots,\n });\n loadedImages.push({\n buffer: media.buffer,\n mimeType: media.mimeType,\n resolvedImage: imageRaw,\n });\n } catch (e) {\n return {\n content: [\n {\n type: 'text',\n text: `Failed to load image (${imageRawInput}): ${e instanceof Error ? e.message : String(e)}`,\n },\n ],\n details: { error: 'load_failed', image: imageRawInput },\n };\n }\n }\n\n const runResult = await runWithImageModelFallback({\n toolConfig: imageModelConfig,\n modelOverride,\n run: async (modelRef) => {\n const { text, provider, model } = await describeImagesWithPiAi({\n modelRef,\n prompt: promptRaw,\n images: loadedImages.map((img) => ({ buffer: img.buffer, mimeType: img.mimeType })),\n timeoutMs: 60_000,\n });\n return { text, provider, model };\n },\n });\n\n const { result: inner, attempts } = runResult;\n const result = {\n text: inner.text,\n provider: inner.provider,\n model: inner.model,\n attempts,\n };\n\n const imageDetails =\n loadedImages.length === 1\n ? { image: loadedImages[0].resolvedImage }\n : {\n images: loadedImages.map((img) => ({ image: img.resolvedImage })),\n };\n\n return buildImageToolTextResult(result, imageDetails);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"image-tool.js","names":[],"sources":["../../../../src/agent/tools/image-tool.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport type { Config } from '../../config/schema.js';\nimport { describeImages } from '../image/understanding/runtime.js';\nimport { buildImageToolTextResult, resolvePromptAndModelOverride } from '../image/image-helpers.js';\nimport { runWithImageModelFallback } from '../image/image-model-fallback.js';\nimport { loadImageForToolInput } from '../image/load-image-media.js';\nimport { resolveImageModelConfigForTool } from '../image/tool-model-config.js';\n\nconst DEFAULT_PROMPT = 'Describe the image.';\nconst DEFAULT_MAX_IMAGES = 20;\n\nexport { resolveImageModelConfigForTool } from '../image/tool-model-config.js';\n\nfunction pickMaxBytes(cfg?: Config, maxBytesMb?: number): number {\n if (typeof maxBytesMb === 'number' && Number.isFinite(maxBytesMb) && maxBytesMb > 0) {\n return Math.floor(maxBytesMb * 1024 * 1024);\n }\n const configured = cfg?.agents?.defaults?.mediaMaxMb;\n if (typeof configured === 'number' && Number.isFinite(configured) && configured > 0) {\n return Math.floor(configured * 1024 * 1024);\n }\n return 20 * 1024 * 1024;\n}\n\nexport function createImageTool(options: {\n config?: Config;\n workspace: string;\n /** When true, session model already receives images in the user message. */\n modelHasVision?: boolean;\n}): AgentTool<any, Record<string, unknown>> | null {\n const imageModelConfig = resolveImageModelConfigForTool({ cfg: options.config });\n if (!imageModelConfig) {\n return null;\n }\n\n const description = options.modelHasVision\n ? 'Analyze one or more images with a vision model. Use `image` for a single path/URL, or `images` for multiple (up to 20). Only use when images were NOT already in the user message.'\n : 'Analyze one or more images using the configured image model (agents.defaults.imageModel). Use `image` or `images` for paths/URLs; optional `prompt` for what to extract.';\n\n const localRoots = [options.workspace];\n\n return {\n name: 'image',\n label: 'Image',\n description,\n parameters: Type.Object({\n prompt: Type.Optional(Type.String()),\n image: Type.Optional(Type.String({ description: 'Single image path or URL.' })),\n images: Type.Optional(\n Type.Array(Type.String(), {\n description: 'Multiple image paths or URLs (up to maxImages, default 20).',\n }),\n ),\n model: Type.Optional(Type.String({ description: 'Optional provider/model override.' })),\n maxBytesMb: Type.Optional(Type.Number()),\n maxImages: Type.Optional(Type.Number()),\n }),\n async execute(\n _toolCallId: string,\n args: Record<string, unknown>,\n ): Promise<AgentToolResult<Record<string, unknown>>> {\n const record = args && typeof args === 'object' ? args : {};\n\n const imageCandidates: string[] = [];\n if (typeof record.image === 'string') {\n imageCandidates.push(record.image);\n }\n if (Array.isArray(record.images)) {\n imageCandidates.push(...record.images.filter((v): v is string => typeof v === 'string'));\n }\n\n const seenImages = new Set<string>();\n const imageInputs: string[] = [];\n for (const candidate of imageCandidates) {\n const trimmedCandidate = candidate.trim();\n const normalizedForDedupe = trimmedCandidate.startsWith('@')\n ? trimmedCandidate.slice(1).trim()\n : trimmedCandidate;\n if (!normalizedForDedupe || seenImages.has(normalizedForDedupe)) {\n continue;\n }\n seenImages.add(normalizedForDedupe);\n imageInputs.push(trimmedCandidate);\n }\n\n if (imageInputs.length === 0) {\n return {\n content: [{ type: 'text', text: 'Error: provide `image` or `images`.' }],\n details: { error: 'missing_image' },\n };\n }\n\n const maxImagesRaw = typeof record.maxImages === 'number' ? record.maxImages : undefined;\n const maxImages =\n typeof maxImagesRaw === 'number' && Number.isFinite(maxImagesRaw) && maxImagesRaw > 0\n ? Math.floor(maxImagesRaw)\n : DEFAULT_MAX_IMAGES;\n if (imageInputs.length > maxImages) {\n return {\n content: [\n {\n type: 'text',\n text: `Too many images: ${imageInputs.length} (max ${maxImages}).`,\n },\n ],\n details: { error: 'too_many_images', count: imageInputs.length, max: maxImages },\n };\n }\n\n const { prompt: promptRaw, modelOverride } = resolvePromptAndModelOverride(\n record,\n DEFAULT_PROMPT,\n );\n const maxBytesMb = typeof record.maxBytesMb === 'number' ? record.maxBytesMb : undefined;\n const maxBytes = pickMaxBytes(options.config, maxBytesMb);\n\n const loadedImages: Array<{ buffer: Buffer; mimeType: string; resolvedImage: string }> = [];\n\n for (const imageRawInput of imageInputs) {\n const trimmed = imageRawInput.trim();\n const imageRaw = trimmed.startsWith('@') ? trimmed.slice(1).trim() : trimmed;\n if (!imageRaw) {\n return {\n content: [{ type: 'text', text: 'Error: empty image entry.' }],\n details: { error: 'empty_image' },\n };\n }\n\n const looksLikeWindowsDrivePath = /^[a-zA-Z]:[\\\\/]/.test(imageRaw);\n const hasScheme = /^[a-z][a-z0-9+.-]*:/i.test(imageRaw);\n const isFileUrl = /^file:/i.test(imageRaw);\n const isHttpUrl = /^https?:\\/\\//i.test(imageRaw);\n const isDataUrl = /^data:/i.test(imageRaw);\n if (hasScheme && !looksLikeWindowsDrivePath && !isFileUrl && !isHttpUrl && !isDataUrl) {\n return {\n content: [\n {\n type: 'text',\n text: `Unsupported image reference: ${imageRawInput}. Use a path, file://, data:, or http(s) URL.`,\n },\n ],\n details: { error: 'unsupported_image_reference', image: imageRawInput },\n };\n }\n\n try {\n const media = await loadImageForToolInput(imageRaw, {\n maxBytes,\n workspace: options.workspace,\n localRoots,\n });\n loadedImages.push({\n buffer: media.buffer,\n mimeType: media.mimeType,\n resolvedImage: imageRaw,\n });\n } catch (e) {\n return {\n content: [\n {\n type: 'text',\n text: `Failed to load image (${imageRawInput}): ${e instanceof Error ? e.message : String(e)}`,\n },\n ],\n details: { error: 'load_failed', image: imageRawInput },\n };\n }\n }\n\n const runResult = await runWithImageModelFallback({\n toolConfig: imageModelConfig,\n modelOverride,\n run: async (modelRef) => {\n const { text, provider, model } = await describeImages({\n modelRef,\n prompt: promptRaw,\n images: loadedImages.map((img) => ({ buffer: img.buffer, mimeType: img.mimeType })),\n timeoutMs: 60_000,\n });\n return { text, provider, model };\n },\n });\n\n const { result: inner, attempts } = runResult;\n const result = {\n text: inner.text,\n provider: inner.provider,\n model: inner.model,\n attempts,\n };\n\n const imageDetails =\n loadedImages.length === 1\n ? { image: loadedImages[0].resolvedImage }\n : {\n images: loadedImages.map((img) => ({ image: img.resolvedImage })),\n };\n\n return buildImageToolTextResult(result, imageDetails);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createReadFileTool,
|
|
1
|
+
export { createReadFileTool, type CreateReadFileToolOptions } from './read.js';
|
|
2
2
|
export { createWriteFileTool, writeFileTool } from './write.js';
|
|
3
3
|
export { createEditFileTool, editFileTool, type EditToolDetails } from './edit.js';
|
|
4
4
|
export { createListDirTool, listDirTool } from './list-dir.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, GREP_MAX_LINE_LENGTH, formatSize, truncateHead, truncateLine, truncateTail } from "./truncate.js";
|
|
2
|
-
import { createReadFileTool
|
|
2
|
+
import { createReadFileTool } from "./read.js";
|
|
3
3
|
import { createWriteFileTool, writeFileTool } from "./write.js";
|
|
4
4
|
import { fuzzyFindText, generateDiffString, normalizeForFuzzyMatch, normalizeToLF, restoreLineEndings, stripBom } from "./edit-diff.js";
|
|
5
5
|
import { createEditFileTool, editFileTool } from "./edit.js";
|
|
@@ -29,4 +29,4 @@ import { createCronjobTool, scanCronPrompt } from "./cronjob-tool.js";
|
|
|
29
29
|
import { createSkillViewTool, createSkillsListTool } from "./skills-tools.js";
|
|
30
30
|
import { createSkillManageTool } from "./skill-manage-tool.js";
|
|
31
31
|
import { createImageGenerateTool, resolveImageGenerationModelConfigForTool } from "./image-generate-tool.js";
|
|
32
|
-
export { BrowserManager, DEFAULT_DELEGATE_TOOLS, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, DEFAULT_WEB_EXTRACT_MAX_LENGTH, DELEGATE_BLOCKED_TOOLS, GREP_MAX_LINE_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, SANDBOX_ALLOWED_TOOLS, TodoStore, assertBrowserUrlAllowed, buildSandboxToolMap, createBrowserTools, createClarifyTool, createCronjobTool, createCuratedMemoryTool, createDelegateTool, createEditFileTool, createExecuteCodeTool, createFindTool, createGrepTool, createImageGenerateTool, createImageTool, createListDirTool, createMemoryGetTool, createMemorySearchTool, createMessageTool, createReadFileTool, createSendMediaTool, createSessionSearchTool, createShellTool, createSkillManageTool, createSkillViewTool, createSkillsListTool, createTodoTool, createWebExtractTool, createWebSearchTool, createWriteFileTool, editFileTool, findTool, formatSize, fuzzyFindText, generateDiffString, grepTool, invalidateSessionSearchIndexCache, listDirTool, normalizeForFuzzyMatch, normalizeToLF,
|
|
32
|
+
export { BrowserManager, DEFAULT_DELEGATE_TOOLS, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, DEFAULT_WEB_EXTRACT_MAX_LENGTH, DELEGATE_BLOCKED_TOOLS, GREP_MAX_LINE_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, SANDBOX_ALLOWED_TOOLS, TodoStore, assertBrowserUrlAllowed, buildSandboxToolMap, createBrowserTools, createClarifyTool, createCronjobTool, createCuratedMemoryTool, createDelegateTool, createEditFileTool, createExecuteCodeTool, createFindTool, createGrepTool, createImageGenerateTool, createImageTool, createListDirTool, createMemoryGetTool, createMemorySearchTool, createMessageTool, createReadFileTool, createSendMediaTool, createSessionSearchTool, createShellTool, createSkillManageTool, createSkillViewTool, createSkillsListTool, createTodoTool, createWebExtractTool, createWebSearchTool, createWriteFileTool, editFileTool, findTool, formatSize, fuzzyFindText, generateDiffString, grepTool, invalidateSessionSearchIndexCache, listDirTool, normalizeForFuzzyMatch, normalizeToLF, resolveImageGenerationModelConfigForTool, resolveImageModelConfigForTool, restoreLineEndings, scanCronPrompt, stripBom, stripHtmlBoilerplate, truncateHead, truncateLine, truncateTail, webFetchTool, writeFileTool };
|
|
@@ -8,6 +8,4 @@ export interface CreateReadFileToolOptions {
|
|
|
8
8
|
bootstrapDir?: string;
|
|
9
9
|
}
|
|
10
10
|
export declare function createReadFileTool(workspace: string, options?: CreateReadFileToolOptions): AgentTool<typeof ReadFileSchema, {}>;
|
|
11
|
-
/** @deprecated Use {@link createReadFileTool}(process.cwd()) — default cwd; prefer factory with workspace. */
|
|
12
|
-
export declare const readFileTool: AgentTool<typeof ReadFileSchema, {}>;
|
|
13
11
|
export {};
|
|
@@ -77,9 +77,7 @@ async function executeReadFile(workspace, bootstrapDir, params) {
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
/** @deprecated Use {@link createReadFileTool}(process.cwd()) — default cwd; prefer factory with workspace. */
|
|
81
|
-
const readFileTool = createReadFileTool(process.cwd());
|
|
82
80
|
//#endregion
|
|
83
|
-
export { createReadFileTool
|
|
81
|
+
export { createReadFileTool };
|
|
84
82
|
|
|
85
83
|
//# sourceMappingURL=read.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","names":[],"sources":["../../../../src/agent/tools/read.ts"],"sourcesContent":["// Read file tool\nimport { Type, type Static } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { readFile, stat } from 'fs/promises';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { truncateHead, formatSize, DEFAULT_MAX_BYTES } from './truncate.js';\nimport {\n isBareBootstrapFileName,\n resolveBootstrapPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst DEFAULT_MAX_LINES = 500;\n\nconst ReadFileSchema = Type.Object({\n path: Type.String({ description: 'File path to read' }),\n limit: Type.Optional(Type.Number({ description: 'Max lines (default: 500)' })),\n});\n\nexport interface CreateReadFileToolOptions {\n /** When set and the path is a bare bootstrap name (e.g. SOUL.md), try this dir if not in workspace. */\n bootstrapDir?: string;\n}\n\nexport function createReadFileTool(\n workspace: string,\n options?: CreateReadFileToolOptions,\n): AgentTool<typeof ReadFileSchema, {}> {\n return {\n name: 'read_file',\n description:\n 'Read file contents. Relative paths are from the current agent workspace; persona files (SOUL.md, etc.) may live in agent bootstrap and are found automatically when given by filename.',\n parameters: ReadFileSchema,\n label: '📄 Read',\n\n async execute(\n _toolCallId: string,\n params: Static<typeof ReadFileSchema>,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n return executeReadFile(workspace, options?.bootstrapDir, params);\n },\n };\n}\n\nasync function executeReadFile(\n workspace: string,\n bootstrapDir: string | undefined,\n params: Static<typeof ReadFileSchema>,\n): Promise<AgentToolResult<{}>> {\n try {\n const safety = checkFileSafety('read', params.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n let normalized = resolvePathUnderWorkspace(params.path, workspace);\n let stats;\n try {\n stats = await stat(normalized);\n } catch (e) {\n const code = (e as NodeJS.ErrnoException)?.code;\n if (\n code === 'ENOENT' &&\n bootstrapDir &&\n isBareBootstrapFileName(params.path)\n ) {\n const alt = resolveBootstrapPathIfBareName(params.path, bootstrapDir);\n try {\n stats = await stat(alt);\n normalized = alt;\n } catch {\n throw e;\n }\n } else {\n throw e;\n }\n }\n\n if (stats.size > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${formatSize(stats.size)}` }], details: {} };\n }\n\n const content = await readFile(normalized, 'utf-8');\n const truncation = truncateHead(content, { maxLines: params.limit || DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });\n\n let outputText = truncation.content;\n if (truncation.truncated) {\n if (truncation.firstLineExceedsLimit) {\n outputText = `(Line exceeds ${formatSize(DEFAULT_MAX_BYTES)})`;\n } else {\n outputText += `\\n\\n[${truncation.outputLines}/${truncation.totalLines} lines]`;\n }\n }\n\n return { content: [{ type: 'text', text: outputText }], details: {} };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n}\n
|
|
1
|
+
{"version":3,"file":"read.js","names":[],"sources":["../../../../src/agent/tools/read.ts"],"sourcesContent":["// Read file tool\nimport { Type, type Static } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';\nimport { readFile, stat } from 'fs/promises';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport { truncateHead, formatSize, DEFAULT_MAX_BYTES } from './truncate.js';\nimport {\n isBareBootstrapFileName,\n resolveBootstrapPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\nconst DEFAULT_MAX_LINES = 500;\n\nconst ReadFileSchema = Type.Object({\n path: Type.String({ description: 'File path to read' }),\n limit: Type.Optional(Type.Number({ description: 'Max lines (default: 500)' })),\n});\n\nexport interface CreateReadFileToolOptions {\n /** When set and the path is a bare bootstrap name (e.g. SOUL.md), try this dir if not in workspace. */\n bootstrapDir?: string;\n}\n\nexport function createReadFileTool(\n workspace: string,\n options?: CreateReadFileToolOptions,\n): AgentTool<typeof ReadFileSchema, {}> {\n return {\n name: 'read_file',\n description:\n 'Read file contents. Relative paths are from the current agent workspace; persona files (SOUL.md, etc.) may live in agent bootstrap and are found automatically when given by filename.',\n parameters: ReadFileSchema,\n label: '📄 Read',\n\n async execute(\n _toolCallId: string,\n params: Static<typeof ReadFileSchema>,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n return executeReadFile(workspace, options?.bootstrapDir, params);\n },\n };\n}\n\nasync function executeReadFile(\n workspace: string,\n bootstrapDir: string | undefined,\n params: Static<typeof ReadFileSchema>,\n): Promise<AgentToolResult<{}>> {\n try {\n const safety = checkFileSafety('read', params.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n let normalized = resolvePathUnderWorkspace(params.path, workspace);\n let stats;\n try {\n stats = await stat(normalized);\n } catch (e) {\n const code = (e as NodeJS.ErrnoException)?.code;\n if (\n code === 'ENOENT' &&\n bootstrapDir &&\n isBareBootstrapFileName(params.path)\n ) {\n const alt = resolveBootstrapPathIfBareName(params.path, bootstrapDir);\n try {\n stats = await stat(alt);\n normalized = alt;\n } catch {\n throw e;\n }\n } else {\n throw e;\n }\n }\n\n if (stats.size > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${formatSize(stats.size)}` }], details: {} };\n }\n\n const content = await readFile(normalized, 'utf-8');\n const truncation = truncateHead(content, { maxLines: params.limit || DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });\n\n let outputText = truncation.content;\n if (truncation.truncated) {\n if (truncation.firstLineExceedsLimit) {\n outputText = `(Line exceeds ${formatSize(DEFAULT_MAX_BYTES)})`;\n } else {\n outputText += `\\n\\n[${truncation.outputLines}/${truncation.totalLines} lines]`;\n }\n }\n\n return { content: [{ type: 'text', text: outputText }], details: {} };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n}\n"],"mappings":";;;;;;AAYA,MAAM,gBAAgB,KAAK,OAAO;AAClC,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,KAAK,OAAO;CACjC,MAAM,KAAK,OAAO,EAAE,aAAa,qBAAqB,CAAC;CACvD,OAAO,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC,CAAC;CAC/E,CAAC;AAOF,SAAgB,mBACd,WACA,SACsC;AACtC,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,UAAO,gBAAgB,WAAW,SAAS,cAAc,OAAO;;EAEnE;;AAGH,eAAe,gBACb,WACA,cACA,QAC8B;AAC9B,KAAI;EACF,MAAM,SAAS,gBAAgB,QAAQ,OAAO,KAAK;AACnD,MAAI,CAAC,OAAO,QACV,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,MAAM,OAAO;IAAW,CAAC;GAAE,SAAS,EAAE;GAAE;EAGnF,IAAI,aAAa,0BAA0B,OAAO,MAAM,UAAU;EAClE,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,KAAK,WAAW;WACvB,GAAG;AAEV,OADc,GAA6B,SAEhC,YACT,gBACA,wBAAwB,OAAO,KAAK,EACpC;IACA,MAAM,MAAM,+BAA+B,OAAO,MAAM,aAAa;AACrE,QAAI;AACF,aAAQ,MAAM,KAAK,IAAI;AACvB,kBAAa;YACP;AACN,WAAM;;SAGR,OAAM;;AAIV,MAAI,MAAM,OAAO,cACf,QAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,sBAAsB,WAAW,MAAM,KAAK;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE;EAI3G,MAAM,aAAa,aADH,MAAM,SAAS,YAAY,QAAQ,EACV;GAAE,UAAU,OAAO,SAAS;GAAmB,UAAU;GAAmB,CAAC;EAEtH,IAAI,aAAa,WAAW;AAC5B,MAAI,WAAW,UACb,KAAI,WAAW,sBACb,cAAa,iBAAiB,WAAW,kBAAkB,CAAC;MAE5D,eAAc,QAAQ,WAAW,YAAY,GAAG,WAAW,WAAW;AAI1E,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAY,CAAC;GAAE,SAAS,EAAE;GAAE;UAC9D,OAAO;AACd,SAAO;GAAE,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAAI,CAAC;GAAE,SAAS,EAAE;GAAE"}
|
|
@@ -2,9 +2,9 @@ import { resolveStateDir } from "../../config/paths-state.js";
|
|
|
2
2
|
import { init_paths } from "../../config/paths.js";
|
|
3
3
|
import { createSkillConfigManager } from "../skills/config.js";
|
|
4
4
|
import { applyPatchToContent, atomicWriteUtf8, effectiveAgentWritePolicy, ensureCategorySegment, isPathInsideDir, maxSkillMdChars, maxSupportFileBytes, mutatableSkillOrNull, resolveCreateSkillDir, scanSkillDirOrError, validateSkillMdContent, validateSkillNameSegment, validateSupportingRelativePath } from "../skills/skill-manage-ops.js";
|
|
5
|
+
import { readFile, rm } from "fs/promises";
|
|
5
6
|
import { join } from "path";
|
|
6
7
|
import { existsSync, rmSync } from "fs";
|
|
7
|
-
import { readFile, rm } from "fs/promises";
|
|
8
8
|
import { Type } from "@sinclair/typebox";
|
|
9
9
|
//#region src/agent/tools/skill-manage-tool.ts
|
|
10
10
|
init_paths();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { checkFileSafety } from "../prompt/safety.js";
|
|
2
2
|
import { resolvePathUnderWorkspace } from "./tool-paths.js";
|
|
3
|
-
import { dirname } from "path";
|
|
4
3
|
import { mkdir, writeFile } from "fs/promises";
|
|
4
|
+
import { dirname } from "path";
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
//#region src/agent/tools/write.ts
|
|
7
7
|
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
-
import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveCredentialsDir, resolveOAuthPath } from "../config/paths.js";
|
|
5
4
|
import { getApiKeyFromEnv, init_env_keys } from "../providers/env-keys.js";
|
|
6
|
-
import {
|
|
5
|
+
import { init_paths, resolveAgentAuthProfilesPath, resolveAuthProfilesPath, resolveCredentialsDir, resolveOAuthPath } from "../config/paths.js";
|
|
7
6
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
8
|
//#region src/auth/credentials.ts
|
|
9
9
|
function getCredentialResolver(options) {
|
|
10
10
|
return new CredentialResolver(options);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { join, resolve } from "path";
|
|
4
3
|
import { mkdir, writeFile } from "fs/promises";
|
|
4
|
+
import { join, resolve } from "path";
|
|
5
5
|
import { randomBytes } from "crypto";
|
|
6
6
|
//#region src/channels/attachments/inbound-persist.ts
|
|
7
7
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { join, resolve } from "path";
|
|
4
3
|
import { mkdir, writeFile } from "fs/promises";
|
|
4
|
+
import { join, resolve } from "path";
|
|
5
5
|
import { randomBytes } from "crypto";
|
|
6
6
|
//#region src/channels/attachments/outbound-tts-persist.ts
|
|
7
7
|
/**
|
|
@@ -8,7 +8,7 @@ export { CHAT_CHANNEL_ORDER, getChatChannelMeta, isChatChannelId, listChatChanne
|
|
|
8
8
|
export { getChannelDock, getDockForBuiltinChannel, type ChannelDock } from './dock.js';
|
|
9
9
|
export type { ChannelPlugin, ChannelPluginDefaults, ChannelPluginInitOptions, ChannelPluginSessionModelHooks, ChannelPluginReloadMeta, ChannelPluginStartOptions, ChannelOutboundContext, ChannelOutboundPayloadContext, OutboundDeliveryResult, ChannelStreamHandle, ChannelStatusAdapter, ChannelSecurityAdapter, ChannelConfigAdapter, ChannelStreamingAdapter, ChannelCapabilities, ChannelMeta, ChannelAccountSnapshot, StreamMode, ChannelOutboundMediaType, } from './plugin-types.js';
|
|
10
10
|
export { compileAllowlist, resolveAllowlistMatch, resolveAllowlistMatchSimple, evaluateAccess, resolveDmPolicy, resolveGroupPolicy, hasBotMention, removeBotMention, } from './security.js';
|
|
11
|
-
export { MessagePipeline, createPipeline, createFilterSelfHandler, createFilterEmptyHandler, createFilterCommandsHandler, standardPreflightHandlers, standardProcessHandlers, type PipelineMessageContext, type PipelineMediaRef, type PreflightHandler, type ProcessHandler, type DeliveryHandler, type AgentResponse, } from './pipeline.js';
|
|
11
|
+
export { MessagePipeline, createPipeline, createEnvelopeTimestampHandler, createFilterSelfHandler, createFilterEmptyHandler, createFilterCommandsHandler, standardPreflightHandlers, standardProcessHandlers, type PipelineMessageContext, type PipelineMediaRef, type PreflightHandler, type ProcessHandler, type DeliveryHandler, type AgentResponse, } from './pipeline.js';
|
|
12
12
|
export { ChannelManager, createChannelManager, type OutboundChannelHooks } from './manager.js';
|
|
13
13
|
export { collectSetupWizardChannels } from './setup-wizard-discovery.js';
|
|
14
14
|
export { listChannelPlugins, getChannelPlugin, getChannelRegistryVersion, syncChannelPluginsFromManager, } from './plugins/registry.js';
|
|
@@ -11,8 +11,8 @@ import { bundledChannelPlugins } from "../generated/bundled-channel-plugins.js";
|
|
|
11
11
|
import "./plugins/bundled.js";
|
|
12
12
|
import { getChannelDock, getDockForBuiltinChannel } from "./dock.js";
|
|
13
13
|
import "./format.js";
|
|
14
|
-
import { MessagePipeline, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createPipeline, standardPreflightHandlers, standardProcessHandlers } from "./pipeline.js";
|
|
14
|
+
import { MessagePipeline, createEnvelopeTimestampHandler, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createPipeline, standardPreflightHandlers, standardProcessHandlers } from "./pipeline.js";
|
|
15
15
|
import { getChannelPlugin, getChannelRegistryVersion, listChannelPlugins, syncChannelPluginsFromManager } from "./plugins/registry.js";
|
|
16
16
|
import { ChannelManager, createChannelManager } from "./manager.js";
|
|
17
17
|
import { collectSetupWizardChannels } from "./setup-wizard-discovery.js";
|
|
18
|
-
export { CHAT_CHANNEL_ORDER, ChannelManager, MessagePipeline, buildCodeSpanIndex, bundledChannelPlugins, chunkMarkdownIR, chunkText, collectSetupWizardChannels, compileAllowlist, convertMarkdownTables, createChannelManager, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createInlineCodeState, createPipeline, evaluateAccess, findFenceSpanAt, getChannelDock, getChannelPlugin, getChannelRegistryVersion, getChatChannelMeta, getDockForBuiltinChannel, hasBotMention, isChatChannelId, isSafeFenceBreak, listChannelPlugins, listChatChannelMeta, markdownToIR, markdownToIRWithMeta, parseFenceSpans, parseFrontmatterBlock, removeBotMention, renderMarkdownWithMarkers, resolveAllowlistMatch, resolveAllowlistMatchSimple, resolveDmPolicy, resolveGroupPolicy, standardPreflightHandlers, standardProcessHandlers, syncChannelPluginsFromManager };
|
|
18
|
+
export { CHAT_CHANNEL_ORDER, ChannelManager, MessagePipeline, buildCodeSpanIndex, bundledChannelPlugins, chunkMarkdownIR, chunkText, collectSetupWizardChannels, compileAllowlist, convertMarkdownTables, createChannelManager, createEnvelopeTimestampHandler, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createInlineCodeState, createPipeline, evaluateAccess, findFenceSpanAt, getChannelDock, getChannelPlugin, getChannelRegistryVersion, getChatChannelMeta, getDockForBuiltinChannel, hasBotMention, isChatChannelId, isSafeFenceBreak, listChannelPlugins, listChatChannelMeta, markdownToIR, markdownToIRWithMeta, parseFenceSpans, parseFrontmatterBlock, removeBotMention, renderMarkdownWithMarkers, resolveAllowlistMatch, resolveAllowlistMatchSimple, resolveDmPolicy, resolveGroupPolicy, standardPreflightHandlers, standardProcessHandlers, syncChannelPluginsFromManager };
|
|
@@ -101,15 +101,22 @@ export declare function createFilterCommandsHandler(commands: string[]): Preflig
|
|
|
101
101
|
* Create standard preflight handlers
|
|
102
102
|
*/
|
|
103
103
|
export declare function standardPreflightHandlers(botId: string): PreflightHandler[];
|
|
104
|
+
/**
|
|
105
|
+
* Prepends a per-turn `[YYYY-MM-DD HH:MM TZ]` prefix to inbound text so the model has
|
|
106
|
+
* a stable "now" without changing the system prompt (prompt-cache friendly).
|
|
107
|
+
*/
|
|
108
|
+
export declare function createEnvelopeTimestampHandler(timezone?: string): ProcessHandler;
|
|
104
109
|
/**
|
|
105
110
|
* Create standard process handlers
|
|
106
111
|
*/
|
|
107
|
-
export declare function standardProcessHandlers(): ProcessHandler[];
|
|
112
|
+
export declare function standardProcessHandlers(timezone?: string): ProcessHandler[];
|
|
108
113
|
export interface CreatePipelineParams {
|
|
109
114
|
channel: string;
|
|
110
115
|
botId: string;
|
|
111
116
|
agentInvoke: PipelineOptions['agentInvoke'];
|
|
112
117
|
onError?: PipelineOptions['onError'];
|
|
118
|
+
/** IANA timezone — matches userTimezone from agent config / USER.md */
|
|
119
|
+
timezone?: string;
|
|
113
120
|
}
|
|
114
121
|
/**
|
|
115
122
|
* Create message processing pipeline
|
|
@@ -196,11 +196,56 @@ function standardPreflightHandlers(botId) {
|
|
|
196
196
|
])
|
|
197
197
|
];
|
|
198
198
|
}
|
|
199
|
+
function formatEnvelopeTimestamp(timezone) {
|
|
200
|
+
const now = /* @__PURE__ */ new Date();
|
|
201
|
+
try {
|
|
202
|
+
const resolvedTimezone = timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
203
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
204
|
+
timeZone: resolvedTimezone,
|
|
205
|
+
year: "numeric",
|
|
206
|
+
month: "2-digit",
|
|
207
|
+
day: "2-digit",
|
|
208
|
+
hour: "2-digit",
|
|
209
|
+
minute: "2-digit",
|
|
210
|
+
hour12: false,
|
|
211
|
+
timeZoneName: "short"
|
|
212
|
+
}).formatToParts(now);
|
|
213
|
+
const map = {};
|
|
214
|
+
for (const part of parts) if (part.type !== "literal") map[part.type] = part.value;
|
|
215
|
+
const year = map.year ?? "";
|
|
216
|
+
const month = map.month ?? "";
|
|
217
|
+
const day = map.day ?? "";
|
|
218
|
+
const hour = map.hour ?? "";
|
|
219
|
+
const minute = map.minute ?? "";
|
|
220
|
+
const tzName = map.timeZoneName ?? "";
|
|
221
|
+
if (!year || !month || !day || !hour || !minute) return `${now.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
222
|
+
return `${year}-${month}-${day} ${hour}:${minute}${tzName ? ` ${tzName}` : ""}`;
|
|
223
|
+
} catch {
|
|
224
|
+
return `${now.toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Prepends a per-turn `[YYYY-MM-DD HH:MM TZ]` prefix to inbound text so the model has
|
|
229
|
+
* a stable "now" without changing the system prompt (prompt-cache friendly).
|
|
230
|
+
*/
|
|
231
|
+
function createEnvelopeTimestampHandler(timezone) {
|
|
232
|
+
return {
|
|
233
|
+
name: "envelopeTimestamp",
|
|
234
|
+
process: async (ctx) => {
|
|
235
|
+
if (!ctx.content?.trim()) return ctx;
|
|
236
|
+
const timestamp = formatEnvelopeTimestamp(timezone);
|
|
237
|
+
return {
|
|
238
|
+
...ctx,
|
|
239
|
+
content: `[${timestamp}] ${ctx.content}`
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
199
244
|
/**
|
|
200
245
|
* Create standard process handlers
|
|
201
246
|
*/
|
|
202
|
-
function standardProcessHandlers() {
|
|
203
|
-
return [];
|
|
247
|
+
function standardProcessHandlers(timezone) {
|
|
248
|
+
return [createEnvelopeTimestampHandler(timezone)];
|
|
204
249
|
}
|
|
205
250
|
/**
|
|
206
251
|
* Create message processing pipeline
|
|
@@ -209,12 +254,12 @@ function createPipeline(params) {
|
|
|
209
254
|
return new MessagePipeline({
|
|
210
255
|
channel: params.channel,
|
|
211
256
|
preflightHandlers: standardPreflightHandlers(params.botId),
|
|
212
|
-
processHandlers: standardProcessHandlers(),
|
|
257
|
+
processHandlers: standardProcessHandlers(params.timezone),
|
|
213
258
|
agentInvoke: params.agentInvoke,
|
|
214
259
|
onError: params.onError
|
|
215
260
|
});
|
|
216
261
|
}
|
|
217
262
|
//#endregion
|
|
218
|
-
export { MessagePipeline, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createPipeline, standardPreflightHandlers, standardProcessHandlers };
|
|
263
|
+
export { MessagePipeline, createEnvelopeTimestampHandler, createFilterCommandsHandler, createFilterEmptyHandler, createFilterSelfHandler, createPipeline, standardPreflightHandlers, standardProcessHandlers };
|
|
219
264
|
|
|
220
265
|
//# sourceMappingURL=pipeline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","names":[],"sources":["../../../src/channels/pipeline.ts"],"sourcesContent":["/**\n * Message Processing Pipeline\n * \n * Three-stage pipeline:\n * - Preflight: Filter empty messages, self-messages, detect commands\n * - Process: Transform format, extract metadata\n * - Delivery: Send to Agent\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport type { AgentResponse } from './plugin-types.js';\n\n// Re-export for convenience\nexport type { AgentResponse } from './plugin-types.js';\n\nconst log = createLogger('Pipeline');\n\nfunction pipelineLogRequestId(ctx: PipelineMessageContext): string {\n const raw = ctx.metadata?.requestId;\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return raw.trim();\n }\n return randomUUID();\n}\n\nfunction pipelineLogSessionId(ctx: PipelineMessageContext): string {\n const sk = ctx.metadata?.sessionKey;\n if (typeof sk === 'string' && sk.trim().length > 0) {\n return sk.trim();\n }\n return `${ctx.channel}:${ctx.chatId}`;\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface PipelineMessageContext {\n /** Channel identifier */\n channel: string;\n /** Account ID */\n accountId: string;\n /** Chat ID */\n chatId: string;\n /** Sender ID */\n senderId: string;\n /** Message content */\n content: string;\n /** Original message metadata */\n metadata: Record<string, unknown>;\n /** Is group chat */\n isGroup: boolean;\n /** Is direct message */\n isDm: boolean;\n /** Thread ID (optional) */\n threadId?: string;\n /** Message ID (optional) */\n messageId?: string;\n}\n\nexport interface PipelineMediaRef {\n type: 'photo' | 'video' | 'audio' | 'document' | 'voice';\n fileId: string;\n mimeType?: string;\n fileName?: string;\n url?: string;\n}\n\n// ============================================\n// Handler Interfaces\n// ============================================\n\nexport interface PreflightHandler {\n /** Handler name */\n name: string;\n /** Preflight - return null to skip message */\n preflight?(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null>;\n}\n\nexport interface ProcessHandler {\n /** Handler name */\n name: string;\n /** Process message - transform and extract */\n process(ctx: PipelineMessageContext): Promise<PipelineMessageContext>;\n}\n\nexport interface DeliveryHandler {\n /** Handler name */\n name: string;\n /** Deliver message to Agent */\n deliver(ctx: PipelineMessageContext, response: AgentResponse): Promise<void>;\n}\n\n// ============================================\n// Pipeline Options\n// ============================================\n\nexport interface PipelineOptions {\n /** Channel name */\n channel: string;\n /** Preflight handlers */\n preflightHandlers?: PreflightHandler[];\n /** Process handlers */\n processHandlers?: ProcessHandler[];\n /** Delivery handlers */\n deliveryHandlers?: DeliveryHandler[];\n /** Agent callback */\n agentInvoke?: (ctx: PipelineMessageContext) => Promise<AgentResponse>;\n /** Error handler */\n onError?: (err: unknown, ctx: PipelineMessageContext) => void;\n}\n\n// ============================================\n// Pipeline Implementation\n// ============================================\n\nexport class MessagePipeline {\n private channel: string;\n private preflightHandlers: PreflightHandler[];\n private processHandlers: ProcessHandler[];\n private deliveryHandlers: DeliveryHandler[];\n private agentInvoke?: PipelineOptions['agentInvoke'];\n private onError?: PipelineOptions['onError'];\n\n constructor(options: PipelineOptions) {\n this.channel = options.channel;\n this.preflightHandlers = options.preflightHandlers ?? [];\n this.processHandlers = options.processHandlers ?? [];\n this.deliveryHandlers = options.deliveryHandlers ?? [];\n this.agentInvoke = options.agentInvoke;\n this.onError = options.onError;\n }\n\n /**\n * Handle inbound message\n */\n async handleMessage(ctx: PipelineMessageContext): Promise<void> {\n const channel = this.channel;\n const requestId = pipelineLogRequestId(ctx);\n\n await runWithLogContext(\n {\n requestId,\n sessionId: pipelineLogSessionId(ctx),\n },\n async () => {\n // 1. Preflight stage\n let processedCtx = await this.runPreflight(ctx);\n if (!processedCtx) {\n log.debug({ channel, chatId: ctx.chatId }, 'Message filtered in preflight');\n return;\n }\n\n // 2. Process stage\n try {\n processedCtx = await this.runProcess(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Process handler error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n const resolvedSk = processedCtx.metadata?.sessionKey;\n if (typeof resolvedSk === 'string' && resolvedSk.trim().length > 0) {\n updateAsyncLogContext({ sessionId: resolvedSk.trim() });\n }\n\n // 3. Deliver to Agent\n if (!this.agentInvoke) {\n log.warn({ channel }, 'No agentInvoke configured');\n return;\n }\n\n let response: AgentResponse;\n try {\n response = await this.agentInvoke(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Agent invocation error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n // 4. Delivery stage\n try {\n await this.runDelivery(processedCtx, response);\n } catch (err) {\n log.error({ channel, err }, 'Delivery handler error');\n this.onError?.(err, processedCtx);\n }\n },\n );\n }\n\n private async runPreflight(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null> {\n for (const handler of this.preflightHandlers) {\n try {\n const result = await handler.preflight?.(ctx);\n if (!result) {\n log.debug({ channel: this.channel, handler: handler.name }, 'Preflight filtered message');\n return null;\n }\n ctx = result;\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Preflight handler error');\n return null;\n }\n }\n return ctx;\n }\n\n private async runProcess(ctx: PipelineMessageContext): Promise<PipelineMessageContext> {\n for (const handler of this.processHandlers) {\n try {\n ctx = await handler.process(ctx);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Process handler error');\n throw err;\n }\n }\n return ctx;\n }\n\n private async runDelivery(ctx: PipelineMessageContext, response: AgentResponse): Promise<void> {\n for (const handler of this.deliveryHandlers) {\n try {\n await handler.deliver(ctx, response);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Delivery handler error');\n throw err;\n }\n }\n }\n}\n\n// ============================================\n// Standard Handlers\n// ============================================\n\n/**\n * Create filter-self handler\n */\nexport function createFilterSelfHandler(currentBotId: string): PreflightHandler {\n return {\n name: 'filterSelf',\n preflight: async (ctx) => {\n if (ctx.senderId === currentBotId) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-empty handler\n */\nexport function createFilterEmptyHandler(): PreflightHandler {\n return {\n name: 'filterEmpty',\n preflight: async (ctx) => {\n const content = ctx.content?.trim() ?? '';\n if (!content && (!ctx.metadata.media || (ctx.metadata.media as PipelineMediaRef[]).length === 0)) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-commands handler\n */\nexport function createFilterCommandsHandler(commands: string[]): PreflightHandler {\n const commandSet = new Set(commands.map(c => c.toLowerCase()));\n return {\n name: 'filterCommands',\n preflight: async (ctx) => {\n const firstWord = ctx.content?.split(/\\s/)[0]?.toLowerCase() ?? '';\n if (firstWord.startsWith('/') && commandSet.has(firstWord.slice(1))) {\n ctx.metadata.isCommand = true;\n ctx.metadata.command = firstWord.slice(1);\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create standard preflight handlers\n */\nexport function standardPreflightHandlers(botId: string): PreflightHandler[] {\n return [\n createFilterSelfHandler(botId),\n createFilterEmptyHandler(),\n createFilterCommandsHandler(['start', 'help', 'status', 'stop']),\n ];\n}\n\n/**\n * Create standard process handlers\n */\nexport function standardProcessHandlers(): ProcessHandler[] {\n return [];\n}\n\n// ============================================\n// Factory\n// ============================================\n\nexport interface CreatePipelineParams {\n channel: string;\n botId: string;\n agentInvoke: PipelineOptions['agentInvoke'];\n onError?: PipelineOptions['onError'];\n}\n\n/**\n * Create message processing pipeline\n */\nexport function createPipeline(params: CreatePipelineParams): MessagePipeline {\n return new MessagePipeline({\n channel: params.channel,\n preflightHandlers: standardPreflightHandlers(params.botId),\n processHandlers: standardProcessHandlers(),\n agentInvoke: params.agentInvoke,\n onError: params.onError,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipeline.js","names":[],"sources":["../../../src/channels/pipeline.ts"],"sourcesContent":["/**\n * Message Processing Pipeline\n * \n * Three-stage pipeline:\n * - Preflight: Filter empty messages, self-messages, detect commands\n * - Process: Transform format, extract metadata\n * - Delivery: Send to Agent\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { createLogger, runWithLogContext, updateAsyncLogContext } from '../utils/logger.js';\nimport type { AgentResponse } from './plugin-types.js';\n\n// Re-export for convenience\nexport type { AgentResponse } from './plugin-types.js';\n\nconst log = createLogger('Pipeline');\n\nfunction pipelineLogRequestId(ctx: PipelineMessageContext): string {\n const raw = ctx.metadata?.requestId;\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return raw.trim();\n }\n return randomUUID();\n}\n\nfunction pipelineLogSessionId(ctx: PipelineMessageContext): string {\n const sk = ctx.metadata?.sessionKey;\n if (typeof sk === 'string' && sk.trim().length > 0) {\n return sk.trim();\n }\n return `${ctx.channel}:${ctx.chatId}`;\n}\n\n// ============================================\n// Types\n// ============================================\n\nexport interface PipelineMessageContext {\n /** Channel identifier */\n channel: string;\n /** Account ID */\n accountId: string;\n /** Chat ID */\n chatId: string;\n /** Sender ID */\n senderId: string;\n /** Message content */\n content: string;\n /** Original message metadata */\n metadata: Record<string, unknown>;\n /** Is group chat */\n isGroup: boolean;\n /** Is direct message */\n isDm: boolean;\n /** Thread ID (optional) */\n threadId?: string;\n /** Message ID (optional) */\n messageId?: string;\n}\n\nexport interface PipelineMediaRef {\n type: 'photo' | 'video' | 'audio' | 'document' | 'voice';\n fileId: string;\n mimeType?: string;\n fileName?: string;\n url?: string;\n}\n\n// ============================================\n// Handler Interfaces\n// ============================================\n\nexport interface PreflightHandler {\n /** Handler name */\n name: string;\n /** Preflight - return null to skip message */\n preflight?(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null>;\n}\n\nexport interface ProcessHandler {\n /** Handler name */\n name: string;\n /** Process message - transform and extract */\n process(ctx: PipelineMessageContext): Promise<PipelineMessageContext>;\n}\n\nexport interface DeliveryHandler {\n /** Handler name */\n name: string;\n /** Deliver message to Agent */\n deliver(ctx: PipelineMessageContext, response: AgentResponse): Promise<void>;\n}\n\n// ============================================\n// Pipeline Options\n// ============================================\n\nexport interface PipelineOptions {\n /** Channel name */\n channel: string;\n /** Preflight handlers */\n preflightHandlers?: PreflightHandler[];\n /** Process handlers */\n processHandlers?: ProcessHandler[];\n /** Delivery handlers */\n deliveryHandlers?: DeliveryHandler[];\n /** Agent callback */\n agentInvoke?: (ctx: PipelineMessageContext) => Promise<AgentResponse>;\n /** Error handler */\n onError?: (err: unknown, ctx: PipelineMessageContext) => void;\n}\n\n// ============================================\n// Pipeline Implementation\n// ============================================\n\nexport class MessagePipeline {\n private channel: string;\n private preflightHandlers: PreflightHandler[];\n private processHandlers: ProcessHandler[];\n private deliveryHandlers: DeliveryHandler[];\n private agentInvoke?: PipelineOptions['agentInvoke'];\n private onError?: PipelineOptions['onError'];\n\n constructor(options: PipelineOptions) {\n this.channel = options.channel;\n this.preflightHandlers = options.preflightHandlers ?? [];\n this.processHandlers = options.processHandlers ?? [];\n this.deliveryHandlers = options.deliveryHandlers ?? [];\n this.agentInvoke = options.agentInvoke;\n this.onError = options.onError;\n }\n\n /**\n * Handle inbound message\n */\n async handleMessage(ctx: PipelineMessageContext): Promise<void> {\n const channel = this.channel;\n const requestId = pipelineLogRequestId(ctx);\n\n await runWithLogContext(\n {\n requestId,\n sessionId: pipelineLogSessionId(ctx),\n },\n async () => {\n // 1. Preflight stage\n let processedCtx = await this.runPreflight(ctx);\n if (!processedCtx) {\n log.debug({ channel, chatId: ctx.chatId }, 'Message filtered in preflight');\n return;\n }\n\n // 2. Process stage\n try {\n processedCtx = await this.runProcess(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Process handler error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n const resolvedSk = processedCtx.metadata?.sessionKey;\n if (typeof resolvedSk === 'string' && resolvedSk.trim().length > 0) {\n updateAsyncLogContext({ sessionId: resolvedSk.trim() });\n }\n\n // 3. Deliver to Agent\n if (!this.agentInvoke) {\n log.warn({ channel }, 'No agentInvoke configured');\n return;\n }\n\n let response: AgentResponse;\n try {\n response = await this.agentInvoke(processedCtx);\n } catch (err) {\n log.error({ channel, err }, 'Agent invocation error');\n this.onError?.(err, processedCtx);\n return;\n }\n\n // 4. Delivery stage\n try {\n await this.runDelivery(processedCtx, response);\n } catch (err) {\n log.error({ channel, err }, 'Delivery handler error');\n this.onError?.(err, processedCtx);\n }\n },\n );\n }\n\n private async runPreflight(ctx: PipelineMessageContext): Promise<PipelineMessageContext | null> {\n for (const handler of this.preflightHandlers) {\n try {\n const result = await handler.preflight?.(ctx);\n if (!result) {\n log.debug({ channel: this.channel, handler: handler.name }, 'Preflight filtered message');\n return null;\n }\n ctx = result;\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Preflight handler error');\n return null;\n }\n }\n return ctx;\n }\n\n private async runProcess(ctx: PipelineMessageContext): Promise<PipelineMessageContext> {\n for (const handler of this.processHandlers) {\n try {\n ctx = await handler.process(ctx);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Process handler error');\n throw err;\n }\n }\n return ctx;\n }\n\n private async runDelivery(ctx: PipelineMessageContext, response: AgentResponse): Promise<void> {\n for (const handler of this.deliveryHandlers) {\n try {\n await handler.deliver(ctx, response);\n } catch (err) {\n log.error({ channel: this.channel, handler: handler.name, err }, 'Delivery handler error');\n throw err;\n }\n }\n }\n}\n\n// ============================================\n// Standard Handlers\n// ============================================\n\n/**\n * Create filter-self handler\n */\nexport function createFilterSelfHandler(currentBotId: string): PreflightHandler {\n return {\n name: 'filterSelf',\n preflight: async (ctx) => {\n if (ctx.senderId === currentBotId) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-empty handler\n */\nexport function createFilterEmptyHandler(): PreflightHandler {\n return {\n name: 'filterEmpty',\n preflight: async (ctx) => {\n const content = ctx.content?.trim() ?? '';\n if (!content && (!ctx.metadata.media || (ctx.metadata.media as PipelineMediaRef[]).length === 0)) {\n return null;\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create filter-commands handler\n */\nexport function createFilterCommandsHandler(commands: string[]): PreflightHandler {\n const commandSet = new Set(commands.map(c => c.toLowerCase()));\n return {\n name: 'filterCommands',\n preflight: async (ctx) => {\n const firstWord = ctx.content?.split(/\\s/)[0]?.toLowerCase() ?? '';\n if (firstWord.startsWith('/') && commandSet.has(firstWord.slice(1))) {\n ctx.metadata.isCommand = true;\n ctx.metadata.command = firstWord.slice(1);\n }\n return ctx;\n },\n };\n}\n\n/**\n * Create standard preflight handlers\n */\nexport function standardPreflightHandlers(botId: string): PreflightHandler[] {\n return [\n createFilterSelfHandler(botId),\n createFilterEmptyHandler(),\n createFilterCommandsHandler(['start', 'help', 'status', 'stop']),\n ];\n}\n\nfunction formatEnvelopeTimestamp(timezone?: string): string {\n const now = new Date();\n try {\n const resolvedTimezone =\n timezone?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: resolvedTimezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n }).formatToParts(now);\n\n const map: Record<string, string> = {};\n for (const part of parts) {\n if (part.type !== 'literal') {\n map[part.type] = part.value;\n }\n }\n\n const year = map.year ?? '';\n const month = map.month ?? '';\n const day = map.day ?? '';\n const hour = map.hour ?? '';\n const minute = map.minute ?? '';\n const tzName = map.timeZoneName ?? '';\n\n if (!year || !month || !day || !hour || !minute) {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n\n return `${year}-${month}-${day} ${hour}:${minute}${tzName ? ` ${tzName}` : ''}`;\n } catch {\n return `${now.toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n }\n}\n\n/**\n * Prepends a per-turn `[YYYY-MM-DD HH:MM TZ]` prefix to inbound text so the model has\n * a stable \"now\" without changing the system prompt (prompt-cache friendly).\n */\nexport function createEnvelopeTimestampHandler(timezone?: string): ProcessHandler {\n return {\n name: 'envelopeTimestamp',\n process: async (ctx) => {\n const text = ctx.content?.trim();\n if (!text) {\n return ctx;\n }\n const timestamp = formatEnvelopeTimestamp(timezone);\n return { ...ctx, content: `[${timestamp}] ${ctx.content}` };\n },\n };\n}\n\n/**\n * Create standard process handlers\n */\nexport function standardProcessHandlers(timezone?: string): ProcessHandler[] {\n return [createEnvelopeTimestampHandler(timezone)];\n}\n\n// ============================================\n// Factory\n// ============================================\n\nexport interface CreatePipelineParams {\n channel: string;\n botId: string;\n agentInvoke: PipelineOptions['agentInvoke'];\n onError?: PipelineOptions['onError'];\n /** IANA timezone — matches userTimezone from agent config / USER.md */\n timezone?: string;\n}\n\n/**\n * Create message processing pipeline\n */\nexport function createPipeline(params: CreatePipelineParams): MessagePipeline {\n return new MessagePipeline({\n channel: params.channel,\n preflightHandlers: standardPreflightHandlers(params.botId),\n processHandlers: standardProcessHandlers(params.timezone),\n agentInvoke: params.agentInvoke,\n onError: params.onError,\n });\n}\n"],"mappings":"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"}
|
|
@@ -69,6 +69,18 @@ export interface ChannelPluginReloadMeta {
|
|
|
69
69
|
/** Config path prefixes that hot-reload this plugin (for docs and tooling). */
|
|
70
70
|
configPrefixes: string[];
|
|
71
71
|
}
|
|
72
|
+
export interface ChannelDoctorCheckResult {
|
|
73
|
+
id: string;
|
|
74
|
+
label: string;
|
|
75
|
+
status: 'pass' | 'warn' | 'fail' | 'skip';
|
|
76
|
+
message: string;
|
|
77
|
+
hints: string[];
|
|
78
|
+
}
|
|
79
|
+
export interface ChannelDoctorAdapter {
|
|
80
|
+
check(params: {
|
|
81
|
+
cfg: Config;
|
|
82
|
+
}): Promise<ChannelDoctorCheckResult[]>;
|
|
83
|
+
}
|
|
72
84
|
export interface ChannelPlugin<ResolvedAccount = any> {
|
|
73
85
|
id: ChannelId;
|
|
74
86
|
meta: ChannelMeta;
|
|
@@ -117,6 +129,8 @@ export interface ChannelPlugin<ResolvedAccount = any> {
|
|
|
117
129
|
heartbeat?: ChannelHeartbeatAdapter;
|
|
118
130
|
agentPrompt?: ChannelAgentPromptAdapter;
|
|
119
131
|
agentTools?: ChannelAgentTool[];
|
|
132
|
+
/** Optional doctor health-check contributed by this channel plugin. */
|
|
133
|
+
doctor?: ChannelDoctorAdapter;
|
|
120
134
|
}
|
|
121
135
|
export interface ChannelConfigAdapter<ResolvedAccount> {
|
|
122
136
|
/** List all account IDs */
|