@xopcai/xopc 0.0.85 → 0.0.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
- package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +1 -0
- package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/api/api.js.map +1 -1
- package/dist/extensions/weixin/src/auth/accounts.js +12 -12
- package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
- package/dist/extensions/weixin/src/delivery-to.js +2 -2
- package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
- package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
- package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
- package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
- package/dist/gateway/static/root/assets/agents-BEAbXpuP.js +222 -0
- package/dist/gateway/static/root/assets/{apps-page-D7v7649T.js → apps-page-Dg8R-Szf.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-nCaMb0a7.js → channels-settings-yohw9YSu.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-C1gZBcJV.js → channels-status-swr-BSHqqCF1.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-CoYK0hlm.js → cron-api-0h_QT8U3.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-DeGo-Vjc.js → cron-page-BkfKFfFk.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-DaK4dsss.js → dist-Cmjp2APP.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BZngZWbO.js → extension-debug-page-CFa9z_1N.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-D6JSyV27.js → extension-page-BI8eaTPq.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-x4BB7q1X.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-Zzl22MvN.js → field-primitives-BiNHBo2Y.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-BtIcpG0O.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
- package/dist/gateway/static/root/assets/{index-D4vM3-P7.js → index-Cu7bKuUi.js} +96 -94
- package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
- package/dist/gateway/static/root/assets/{logs-page-_d4UJ-qQ.js → logs-page-BFZ8GgCv.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-5N4aF2Wk.js → sessions-page-CD7AfB-2.js} +1 -1
- package/dist/gateway/static/root/assets/settings-form-section-DiqqVs6m.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-BBOjEQW3.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-D4EG_vM1.js → share-preview-page-n1Gprylk.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-sPAXhh8w.js → skills-page-CcN_gj--.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
- package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
- package/dist/gateway/static/root/assets/{utils-CYO9eTCM.js → utils-CkWBfxs4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-Ds51havm.js → voice-api-key-field-O6awz9hi.js} +1 -1
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-scope.d.ts +4 -0
- package/dist/src/agent/agent-scope.js +53 -10
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
- package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
- package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +2 -2
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
- package/dist/src/agent/goals/persistent-goal-service.js +0 -1
- package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
- package/dist/src/agent/image/generation/normalization.js +2 -12
- package/dist/src/agent/image/generation/normalization.js.map +1 -1
- package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
- package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
- package/dist/src/agent/image/generation/runtime.d.ts +2 -2
- package/dist/src/agent/image/generation/runtime.js.map +1 -1
- package/dist/src/agent/image/generation/types.d.ts +0 -18
- package/dist/src/agent/image/image-helpers.js +6 -1
- package/dist/src/agent/image/image-helpers.js.map +1 -1
- package/dist/src/agent/image/index.d.ts +1 -1
- package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
- package/dist/src/agent/inbound/inbound-loop.js +41 -10
- package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +2 -1
- package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
- package/dist/src/agent/media-generation/runtime-shared.js +2 -9
- package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +6 -0
- package/dist/src/agent/messaging/command-handler.js +5 -0
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/prompt/safety.d.ts +0 -7
- package/dist/src/agent/prompt/safety.js +1 -20
- package/dist/src/agent/prompt/safety.js.map +1 -1
- package/dist/src/agent/service/build-direct-message-content.js +1 -1
- package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
- package/dist/src/agent/service/direct-turn-helpers.js +6 -1
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
- package/dist/src/agent/service/process-direct-one-shot.js +15 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
- package/dist/src/agent/service/process-direct-streaming.js +34 -4
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/webchat-tts.js +1 -1
- package/dist/src/agent/service/webchat-tts.js.map +1 -1
- package/dist/src/agent/service.d.ts +8 -0
- package/dist/src/agent/service.js +21 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/create-share-tool.js +27 -20
- package/dist/src/agent/tools/create-share-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/index.d.ts +0 -1
- package/dist/src/agent/tools/index.js +4 -5
- package/dist/src/agent/tools/shell.js +0 -13
- package/dist/src/agent/tools/shell.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.js +10 -4
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/workflow/builtins/audit-repo.d.ts +5 -1
- package/dist/src/agent/workflow/builtins/audit-repo.js +52 -11
- package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +13 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js +155 -0
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -0
- package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
- package/dist/src/agent/workflow/builtins/index.js +11 -1
- package/dist/src/agent/workflow/builtins/index.js.map +1 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +6 -1
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js +66 -30
- package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +12 -0
- package/dist/src/agent/workflow/builtins/pr-review.js +156 -0
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -0
- package/dist/src/agent/workflow/builtins/research.d.ts +5 -1
- package/dist/src/agent/workflow/builtins/research.js +37 -6
- package/dist/src/agent/workflow/builtins/research.js.map +1 -1
- package/dist/src/agent/workflow/catalog.d.ts +5 -0
- package/dist/src/agent/workflow/catalog.js +6 -2
- package/dist/src/agent/workflow/catalog.js.map +1 -1
- package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
- package/dist/src/agent/workflow/index.d.ts +1 -1
- package/dist/src/agent/workflow/lint.d.ts +38 -0
- package/dist/src/agent/workflow/lint.js +74 -0
- package/dist/src/agent/workflow/lint.js.map +1 -0
- package/dist/src/agent/workflow/parser.js +13 -1
- package/dist/src/agent/workflow/parser.js.map +1 -1
- package/dist/src/agent/workflow/runtime.d.ts +3 -0
- package/dist/src/agent/workflow/runtime.js +76 -3
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +11 -1
- package/dist/src/browser/index.js +4 -4
- package/dist/src/browser/manager.d.ts +1 -3
- package/dist/src/browser/manager.js +0 -6
- package/dist/src/browser/manager.js.map +1 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
- package/dist/src/browser/providers/browser-ext-install.js +38 -85
- package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
- package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
- package/dist/src/browser/providers/cloakbrowser.js +2 -55
- package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
- package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
- package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +9 -9
- package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
- package/dist/src/channels/pairing/pairing-store.js +6 -6
- package/dist/src/channels/pairing/pairing-store.js.map +1 -1
- package/dist/src/chat-commands/builtins/session.js +1 -1
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/tts.js +2 -2
- package/dist/src/chat-commands/builtins/tts.js.map +1 -1
- package/dist/src/chat-commands/builtins/workflow.js +7 -2
- package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +21 -3
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/session-key.d.ts +4 -37
- package/dist/src/chat-commands/session-key.js +49 -85
- package/dist/src/chat-commands/session-key.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +2 -0
- package/dist/src/cli/commands/agent/interactive.js +2 -2
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent/sessions.js +2 -2
- package/dist/src/cli/commands/agent/sessions.js.map +1 -1
- package/dist/src/cli/commands/agent.js +4 -5
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/channels.js +1 -5
- package/dist/src/cli/commands/channels.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
- package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
- package/dist/src/cli/commands/gateway/logs.js +50 -17
- package/dist/src/cli/commands/gateway/logs.js.map +1 -1
- package/dist/src/cli/commands/image.js +22 -21
- package/dist/src/cli/commands/image.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js +2 -2
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.js +26 -46
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/utils/session.d.ts +0 -5
- package/dist/src/cli/utils/session.js +1 -6
- package/dist/src/cli/utils/session.js.map +1 -1
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +5 -27
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/model-input.js +2 -5
- package/dist/src/config/model-input.js.map +1 -1
- package/dist/src/config/schema.d.ts +201 -217
- package/dist/src/config/schema.js +54 -39
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/workspace-path-helpers.d.ts +1 -2
- package/dist/src/config/workspace-path-helpers.js.map +1 -1
- package/dist/src/daemon/install-plan.js +25 -1
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.d.ts +8 -0
- package/dist/src/daemon/launchd.js +5 -12
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/schtasks.d.ts +25 -0
- package/dist/src/daemon/schtasks.js +166 -46
- package/dist/src/daemon/schtasks.js.map +1 -1
- package/dist/src/daemon/service.js +5 -4
- package/dist/src/daemon/service.js.map +1 -1
- package/dist/src/daemon/systemd.d.ts +6 -0
- package/dist/src/daemon/systemd.js +18 -3
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/activation-context.js +0 -1
- package/dist/src/extensions/activation-context.js.map +1 -1
- package/dist/src/extensions/normalize-manifest.js +0 -1
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/types/manifest.d.ts +0 -2
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +10 -2
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/heartbeat/service.js.map +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
- package/dist/src/gateway/hono/lib/agent-model.js +24 -35
- package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
- package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
- package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/goals.js +1 -1
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +28 -7
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +14 -12
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js +1 -1
- package/dist/src/gateway/hono/routes/update.js +4 -2
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +16 -33
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/lock.js +10 -10
- package/dist/src/gateway/lock.js.map +1 -1
- package/dist/src/gateway/ports.js +6 -6
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
- package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
- package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +27 -11
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +3 -0
- package/dist/src/gateway/service/sessions-api.js +8 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.d.ts +0 -2
- package/dist/src/gateway/service.js +2 -7
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/session-reset-service.d.ts +20 -0
- package/dist/src/gateway/session-reset-service.js +54 -0
- package/dist/src/gateway/session-reset-service.js.map +1 -0
- package/dist/src/gateway/startup-readiness.d.ts +1 -1
- package/dist/src/gateway/startup-readiness.js +1 -0
- package/dist/src/gateway/startup-readiness.js.map +1 -1
- package/dist/src/infra/gateway-processes.js +2 -2
- package/dist/src/infra/gateway-processes.js.map +1 -1
- package/dist/src/infra/run-command.d.ts +16 -0
- package/dist/src/infra/run-command.js +67 -0
- package/dist/src/infra/run-command.js.map +1 -0
- package/dist/src/infra/update-global.d.ts +45 -0
- package/dist/src/infra/update-global.js +224 -0
- package/dist/src/infra/update-global.js.map +1 -0
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mcp/channel-shared.js +2 -1
- package/dist/src/mcp/channel-shared.js.map +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
- package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
- package/dist/src/providers/auth-runtime/types.d.ts +6 -12
- package/dist/src/routing/agent-session-key.d.ts +58 -0
- package/dist/src/routing/agent-session-key.js +164 -0
- package/dist/src/routing/agent-session-key.js.map +1 -0
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +4 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.d.ts +15 -0
- package/dist/src/routing/resolve-route.js +41 -20
- package/dist/src/routing/resolve-route.js.map +1 -1
- package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
- package/dist/src/routing/resolve-tui-session-key.js +54 -0
- package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
- package/dist/src/routing/session-key-utils.d.ts +24 -0
- package/dist/src/routing/session-key-utils.js +92 -0
- package/dist/src/routing/session-key-utils.js.map +1 -0
- package/dist/src/routing/session-key.d.ts +19 -49
- package/dist/src/routing/session-key.js +143 -116
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/index.d.ts +6 -0
- package/dist/src/session/index.js +7 -1
- package/dist/src/session/init-session-turn.d.ts +30 -0
- package/dist/src/session/init-session-turn.js +102 -0
- package/dist/src/session/init-session-turn.js.map +1 -0
- package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
- package/dist/src/session/lifecycle-timestamps.js +16 -0
- package/dist/src/session/lifecycle-timestamps.js.map +1 -0
- package/dist/src/session/manager.d.ts +7 -1
- package/dist/src/session/manager.js +8 -1
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/parity/transcript-paths.js +2 -2
- package/dist/src/session/parity/transcript-paths.js.map +1 -1
- package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
- package/dist/src/session/reset-policy.d.ts +32 -0
- package/dist/src/session/reset-policy.js +65 -0
- package/dist/src/session/reset-policy.js.map +1 -0
- package/dist/src/session/reset-triggers.d.ts +20 -0
- package/dist/src/session/reset-triggers.js +63 -0
- package/dist/src/session/reset-triggers.js.map +1 -0
- package/dist/src/session/reset-type.d.ts +12 -0
- package/dist/src/session/reset-type.js +25 -0
- package/dist/src/session/reset-type.js.map +1 -0
- package/dist/src/session/resolve-session.d.ts +30 -0
- package/dist/src/session/resolve-session.js +93 -0
- package/dist/src/session/resolve-session.js.map +1 -0
- package/dist/src/session/session-title.js +3 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.d.ts +11 -4
- package/dist/src/session/store.js +57 -6
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/transcript-events.js +2 -1
- package/dist/src/session/transcript-events.js.map +1 -1
- package/dist/src/share/share-url.d.ts +33 -0
- package/dist/src/share/share-url.js +56 -14
- package/dist/src/share/share-url.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +4 -9
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/chat-log.js +3 -3
- package/dist/src/tui/components/chat-log.js.map +1 -1
- package/dist/src/tui/theme.d.ts +0 -2
- package/dist/src/tui/theme.js +1 -3
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/tui-commands.d.ts +3 -0
- package/dist/src/tui/tui-commands.js +45 -10
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -21
- package/dist/src/tui/tui-keybindings-file.js.map +1 -1
- package/dist/src/tui/tui-session-actions.d.ts +28 -0
- package/dist/src/tui/tui-session-actions.js +88 -0
- package/dist/src/tui/tui-session-actions.js.map +1 -0
- package/dist/src/tui/tui.js +52 -47
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/utils/string-coerce.d.ts +2 -0
- package/dist/src/utils/string-coerce.js +10 -1
- package/dist/src/utils/string-coerce.js.map +1 -1
- package/dist/src/voice/stt/config-slice.d.ts +2 -5
- package/dist/src/voice/stt/config-slice.js +5 -26
- package/dist/src/voice/stt/config-slice.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +1 -18
- package/dist/src/voice/stt/types.js +4 -2
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/config-slice.d.ts +3 -7
- package/dist/src/voice/tts/config-slice.js +7 -38
- package/dist/src/voice/tts/config-slice.js.map +1 -1
- package/dist/src/voice/tts/merge-config.js +2 -48
- package/dist/src/voice/tts/merge-config.js.map +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
- package/dist/src/voice/tts/types.d.ts +1 -29
- package/dist/src/voice/tts/types.js +19 -17
- package/dist/src/voice/tts/types.js.map +1 -1
- package/package.json +1 -4
- package/dist/gateway/static/root/assets/agents-D3_-kNlZ.js +0 -222
- package/dist/gateway/static/root/assets/extension-settings-page-8PZcmWI7.js +0 -1
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-D_tgb8r2.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-C18xBt4X.js +0 -3
- package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
- package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
- package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
- package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pairing-store.js","names":["fs"],"sources":["../../../../src/channels/pairing/pairing-store.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { appendAllowFromIdSync } from './allow-from-file.js';\nimport { PAIRING_PENDING_MAX } from './pairing-constants.js';\n\nconst PAIRING_CODE_LENGTH = 8;\nconst PAIRING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\nexport const PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;\n\nexport type PairingRequest = {\n id: string;\n code: string;\n createdAt: string;\n lastSeenAt: string;\n meta?: Record<string, string>;\n};\n\ntype PairingStoreFile = {\n version: 1;\n requests: PairingRequest[];\n};\n\nfunction randomCode(): string {\n let out = '';\n for (let i = 0; i < PAIRING_CODE_LENGTH; i++) {\n const idx = crypto.randomInt(0, PAIRING_CODE_ALPHABET.length);\n out += PAIRING_CODE_ALPHABET[idx]!;\n }\n return out;\n}\n\nfunction parseTs(iso: string | undefined): number | null {\n if (!iso) return null;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction isExpired(entry: PairingRequest, nowMs: number): boolean {\n const t = parseTs(entry.createdAt);\n if (t == null) return true;\n return nowMs - t > PAIRING_PENDING_TTL_MS;\n}\n\nfunction pruneExpired(reqs: PairingRequest[], nowMs: number): PairingRequest[] {\n return reqs.filter((r) => !isExpired(r, nowMs));\n}\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction readStore(filePath: string): PairingStoreFile {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, requests: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as PairingStoreFile;\n if (parsed && Array.isArray(parsed.requests)) {\n return { version: 1, requests: parsed.requests };\n }\n } catch {\n /* ignore */\n }\n return { version: 1, requests: [] };\n}\n\nfunction writeStore(filePath: string, store: PairingStoreFile): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Create or refresh a pending pairing request for `id` (e.g. Telegram user id).\n * Returns `created: true` when a new code was minted (caller should notify the user).\n */\nexport function upsertPairingRequestSync(params: {\n pairingFilePath: string;\n id: string;\n accountId: string;\n meta?: Record<string, string | undefined | null>;\n}): { code: string; created: boolean } {\n const now = new Date().toISOString();\n const nowMs = Date.now();\n const id = String(params.id).trim();\n if (!id) return { code: '', created: false };\n\n const normalizedAccountId = normalizeAccountId(params.accountId);\n const metaClean =\n params.meta && typeof params.meta === 'object'\n ? Object.fromEntries(\n Object.entries(params.meta)\n .map(([k, v]) => [k, typeof v === 'string' ? v.trim() : ''] as const)\n .filter(([, v]) => Boolean(v)),\n )\n : {};\n const meta: Record<string, string> = { ...metaClean, accountId: normalizedAccountId };\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, nowMs);\n\n const idx = reqs.findIndex((r) => r.id === id && requestAccountId(r) === normalizedAccountId);\n const existingCodes = new Set(reqs.map((r) => r.code.toUpperCase()));\n\n if (idx >= 0) {\n const existing = reqs[idx]!;\n const code = existing.code || randomCode();\n reqs[idx] = {\n id,\n code,\n createdAt: existing.createdAt || now,\n lastSeenAt: now,\n meta: { ...existing.meta, ...meta },\n };\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: false };\n }\n\n const forAccount = reqs.filter((r) => requestAccountId(r) === normalizedAccountId);\n if (forAccount.length >= PAIRING_PENDING_MAX) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code: '', created: false };\n }\n\n let code = randomCode();\n for (let attempt = 0; attempt < 50 && existingCodes.has(code.toUpperCase()); attempt++) {\n code = randomCode();\n }\n existingCodes.add(code.toUpperCase());\n\n reqs = [...reqs, { id, code, createdAt: now, lastSeenAt: now, meta }];\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: true };\n}\n\n/**\n * Approve a pairing code: remove pending request and append sender id to allowFrom file.\n */\n/** Non-expired pending requests; prunes expired entries from disk. */\nexport function listPendingPairingRequestsSync(pairingFilePath: string): PairingRequest[] {\n const nowMs = Date.now();\n let store = readStore(pairingFilePath);\n const reqs = pruneExpired(store.requests, nowMs);\n if (reqs.length !== store.requests.length) {\n writeStore(pairingFilePath, { version: 1, requests: reqs });\n }\n return reqs;\n}\n\nexport function approvePairingCodeSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n code: string;\n /** When set, only requests tagged with this account id match. */\n accountId?: string;\n}): { senderId: string } | null {\n const want = (params.code ?? '').trim().toUpperCase();\n if (!want) return null;\n const normalizedAccount = normalizeAccountId(params.accountId);\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.code.toUpperCase() === want && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Approve a pending request by sender id (gateway-authenticated quick approve). */\nexport function approvePairingBySenderIdSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Remove a pending request without approving (admin dismiss). */\nexport function dismissPairingBySenderIdSync(params: {\n pairingFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { senderId: entry.id };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pairing-store.js","names":[],"sources":["../../../../src/channels/pairing/pairing-store.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { appendAllowFromIdSync } from './allow-from-file.js';\nimport { PAIRING_PENDING_MAX } from './pairing-constants.js';\n\nconst PAIRING_CODE_LENGTH = 8;\nconst PAIRING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\nexport const PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;\n\nexport type PairingRequest = {\n id: string;\n code: string;\n createdAt: string;\n lastSeenAt: string;\n meta?: Record<string, string>;\n};\n\ntype PairingStoreFile = {\n version: 1;\n requests: PairingRequest[];\n};\n\nfunction randomCode(): string {\n let out = '';\n for (let i = 0; i < PAIRING_CODE_LENGTH; i++) {\n const idx = crypto.randomInt(0, PAIRING_CODE_ALPHABET.length);\n out += PAIRING_CODE_ALPHABET[idx]!;\n }\n return out;\n}\n\nfunction parseTs(iso: string | undefined): number | null {\n if (!iso) return null;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction isExpired(entry: PairingRequest, nowMs: number): boolean {\n const t = parseTs(entry.createdAt);\n if (t == null) return true;\n return nowMs - t > PAIRING_PENDING_TTL_MS;\n}\n\nfunction pruneExpired(reqs: PairingRequest[], nowMs: number): PairingRequest[] {\n return reqs.filter((r) => !isExpired(r, nowMs));\n}\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction readStore(filePath: string): PairingStoreFile {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, requests: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as PairingStoreFile;\n if (parsed && Array.isArray(parsed.requests)) {\n return { version: 1, requests: parsed.requests };\n }\n } catch {\n /* ignore */\n }\n return { version: 1, requests: [] };\n}\n\nfunction writeStore(filePath: string, store: PairingStoreFile): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Create or refresh a pending pairing request for `id` (e.g. Telegram user id).\n * Returns `created: true` when a new code was minted (caller should notify the user).\n */\nexport function upsertPairingRequestSync(params: {\n pairingFilePath: string;\n id: string;\n accountId: string;\n meta?: Record<string, string | undefined | null>;\n}): { code: string; created: boolean } {\n const now = new Date().toISOString();\n const nowMs = Date.now();\n const id = String(params.id).trim();\n if (!id) return { code: '', created: false };\n\n const normalizedAccountId = normalizeAccountId(params.accountId);\n const metaClean =\n params.meta && typeof params.meta === 'object'\n ? Object.fromEntries(\n Object.entries(params.meta)\n .map(([k, v]) => [k, typeof v === 'string' ? v.trim() : ''] as const)\n .filter(([, v]) => Boolean(v)),\n )\n : {};\n const meta: Record<string, string> = { ...metaClean, accountId: normalizedAccountId };\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, nowMs);\n\n const idx = reqs.findIndex((r) => r.id === id && requestAccountId(r) === normalizedAccountId);\n const existingCodes = new Set(reqs.map((r) => r.code.toUpperCase()));\n\n if (idx >= 0) {\n const existing = reqs[idx]!;\n const code = existing.code || randomCode();\n reqs[idx] = {\n id,\n code,\n createdAt: existing.createdAt || now,\n lastSeenAt: now,\n meta: { ...existing.meta, ...meta },\n };\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: false };\n }\n\n const forAccount = reqs.filter((r) => requestAccountId(r) === normalizedAccountId);\n if (forAccount.length >= PAIRING_PENDING_MAX) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code: '', created: false };\n }\n\n let code = randomCode();\n for (let attempt = 0; attempt < 50 && existingCodes.has(code.toUpperCase()); attempt++) {\n code = randomCode();\n }\n existingCodes.add(code.toUpperCase());\n\n reqs = [...reqs, { id, code, createdAt: now, lastSeenAt: now, meta }];\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: true };\n}\n\n/**\n * Approve a pairing code: remove pending request and append sender id to allowFrom file.\n */\n/** Non-expired pending requests; prunes expired entries from disk. */\nexport function listPendingPairingRequestsSync(pairingFilePath: string): PairingRequest[] {\n const nowMs = Date.now();\n let store = readStore(pairingFilePath);\n const reqs = pruneExpired(store.requests, nowMs);\n if (reqs.length !== store.requests.length) {\n writeStore(pairingFilePath, { version: 1, requests: reqs });\n }\n return reqs;\n}\n\nexport function approvePairingCodeSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n code: string;\n /** When set, only requests tagged with this account id match. */\n accountId?: string;\n}): { senderId: string } | null {\n const want = (params.code ?? '').trim().toUpperCase();\n if (!want) return null;\n const normalizedAccount = normalizeAccountId(params.accountId);\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.code.toUpperCase() === want && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Approve a pending request by sender id (gateway-authenticated quick approve). */\nexport function approvePairingBySenderIdSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Remove a pending request without approving (admin dismiss). */\nexport function dismissPairingBySenderIdSync(params: {\n pairingFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { senderId: entry.id };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":[],"sources":["../../../../src/chat-commands/builtins/session.ts"],"sourcesContent":["/**\n * Session Commands\n * \n * Built-in commands for session management:\n * - /new - Start a new session\n * - /list - List all sessions\n * - /switch - Switch to a different session\n * - /clear - Clear current session without archiving\n * - /abort - Cancel in-flight assistant reply (generation / streaming)\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { getSessionDisplayName } from '../session-key.js';\n\nconst newCommand: CommandDefinition = {\n id: 'session.new',\n name: 'new',\n aliases: ['reset', 'restart'],\n description: 'Start a new session (archive current)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.clearSession();\n \n // Note: clearSession already sends confirmation message\n return {\n content: '',\n success: true,\n };\n },\n};\n\nconst listCommand: CommandDefinition = {\n id: 'session.list',\n name: 'list',\n aliases: ['sessions'],\n description: 'List all your sessions',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n const sessions = await ctx.listSessions();\n \n if (sessions.length === 0) {\n return {\n content: '📋 No sessions found.',\n success: true,\n };\n }\n \n // Sort by updatedAt desc\n sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n \n // Build text response\n const lines = sessions.slice(0, 10).map(s => {\n const indicator = s.isActive ? '▶️' : ' ';\n const name = getSessionDisplayName(s.key);\n const date = s.updatedAt.toLocaleDateString();\n return `${indicator} ${name}\\n ${s.messageCount} messages · ${date}`;\n });\n \n const content = '📋 Your Sessions:\\n\\n' + lines.join('\\n\\n');\n \n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'session-list',\n sessions: sessions.slice(0, 5).map(s => ({\n ...s,\n name: getSessionDisplayName(s.key),\n })),\n currentSession: ctx.sessionKey,\n };\n \n return {\n content,\n success: true,\n components: [component],\n };\n }\n \n return {\n content,\n success: true,\n };\n },\n};\n\nconst clearCommand: CommandDefinition = {\n id: 'session.clear',\n name: 'clear',\n description: 'Clear current session without archiving',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n // Just delete without archiving\n const messages = await ctx.getSession();\n await ctx.clearSession();\n \n return {\n content: `🗑️ Session cleared. ${messages.length} messages deleted.`,\n success: true,\n };\n },\n};\n\nconst abortCommand: CommandDefinition = {\n id: 'session.abort',\n name: 'abort',\n aliases: ['stop', 'cancel'],\n description: 'Stop the current assistant reply (in-flight generation or stream)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n if (!ctx.abortCurrentTurn) {\n return {\n content: 'Abort is not available in this environment.',\n success: false,\n };\n }\n await ctx.abortCurrentTurn();\n return {\n content: '⏹️ Current reply cancelled.',\n success: true,\n };\n },\n};\n\nconst compactCommand: CommandDefinition = {\n id: 'session.compact',\n name: 'compact',\n description: 'Compact session history (LLM summary + keep recent turns) to save context',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/compact', '/compact focus on API design'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const instructions = args.trim() || undefined;\n const result = await ctx.compactSession?.({ instructions, force: true });\n if (result === null || result === undefined) {\n return {\n content: '⚠️ Session compaction is not available in this environment.',\n success: false,\n };\n }\n if (!result.compacted) {\n return {\n content:\n 'ℹ️ Nothing to compact yet. Need at least two messages, or the session is already small.\\n' +\n 'Tip: add optional focus text, e.g. `/compact emphasize decisions about auth`.',\n success: true,\n };\n }\n const preview =\n result.summary && result.summary.length > 600\n ? `${result.summary.slice(0, 600)}…`\n : result.summary || '';\n return {\n content:\n `🗜️ *Session compacted*\\n\\n` +\n `Tokens (approx): ${result.tokensBefore} → ${result.tokensAfter}\\n\\n` +\n (preview ? `*Summary:*\\n${preview}` : ''),\n success: true,\n };\n },\n};\n\nconst btwCommand: CommandDefinition = {\n id: 'session.btw',\n name: 'btw',\n aliases: ['aside'],\n description: 'Ask a side question without adding to the session transcript',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/btw What does that error code mean?', '/aside Summarize the last topic in one line'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const q = args.trim();\n if (!q) {\n return {\n content:\n '💬 *Side question*\\n\\n' +\n 'Usage: `/btw <question>`\\n' +\n 'Answers use your current chat as background only; the reply is not saved to the session.',\n success: true,\n };\n }\n const out = await ctx.btwQuery?.(q);\n if (!out) {\n return { content: '⚠️ /btw is not available here.', success: false };\n }\n if (out.error) {\n return { content: `⚠️ ${out.error}`, success: false };\n }\n return { content: `💬 *BTW*\\n\\n${out.text}`, success: true };\n },\n};\n\nconst exportSessionCommand: CommandDefinition = {\n id: 'session.export',\n name: 'export-session',\n aliases: ['export'],\n description: 'Export this session to workspace exports/ (markdown, html, or json)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/export-session', '/export-session html', '/export json'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const raw = args.trim().toLowerCase();\n const fmt =\n raw === 'json' || raw === 'html' || raw === 'markdown'\n ? (raw as 'json' | 'html' | 'markdown')\n : 'markdown';\n if (!ctx.exportSessionToWorkspace) {\n return { content: '⚠️ Export is not available in this environment.', success: false };\n }\n try {\n const { path } = await ctx.exportSessionToWorkspace(fmt);\n return {\n content: `📄 Exported (${fmt}) to:\\n\\`${path}\\``,\n success: true,\n };\n } catch (e) {\n const em = e instanceof Error ? e.message : String(e);\n return { content: `⚠️ Export failed: ${em}`, success: false };\n }\n },\n};\n\nconst archiveCommand: CommandDefinition = {\n id: 'session.archive',\n name: 'archive',\n description: 'Archive current session',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.archiveSession();\n \n return {\n content: '📦 Current session has been archived.',\n success: true,\n };\n },\n};\n\n// Register all session commands\nexport function registerSessionCommands(): void {\n commandRegistry.register(newCommand);\n commandRegistry.register(listCommand);\n commandRegistry.register(clearCommand);\n commandRegistry.register(abortCommand);\n commandRegistry.register(compactCommand);\n commandRegistry.register(btwCommand);\n commandRegistry.register(exportSessionCommand);\n commandRegistry.register(archiveCommand);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session.js","names":[],"sources":["../../../../src/chat-commands/builtins/session.ts"],"sourcesContent":["/**\n * Session Commands\n * \n * Built-in commands for session management:\n * - /new - Start a new session\n * - /list - List all sessions\n * - /switch - Switch to a different session\n * - /clear - Clear current session without archiving\n * - /abort - Cancel in-flight assistant reply (generation / streaming)\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { getSessionDisplayName } from '../session-key.js';\n\nconst newCommand: CommandDefinition = {\n id: 'session.new',\n name: 'new',\n aliases: ['reset', 'restart'],\n description: 'Start a new session (archive current)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.resetSession();\n \n // Note: resetSession already sends confirmation message\n return {\n content: '',\n success: true,\n };\n },\n};\n\nconst listCommand: CommandDefinition = {\n id: 'session.list',\n name: 'list',\n aliases: ['sessions'],\n description: 'List all your sessions',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n const sessions = await ctx.listSessions();\n \n if (sessions.length === 0) {\n return {\n content: '📋 No sessions found.',\n success: true,\n };\n }\n \n // Sort by updatedAt desc\n sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n \n // Build text response\n const lines = sessions.slice(0, 10).map(s => {\n const indicator = s.isActive ? '▶️' : ' ';\n const name = getSessionDisplayName(s.key);\n const date = s.updatedAt.toLocaleDateString();\n return `${indicator} ${name}\\n ${s.messageCount} messages · ${date}`;\n });\n \n const content = '📋 Your Sessions:\\n\\n' + lines.join('\\n\\n');\n \n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'session-list',\n sessions: sessions.slice(0, 5).map(s => ({\n ...s,\n name: getSessionDisplayName(s.key),\n })),\n currentSession: ctx.sessionKey,\n };\n \n return {\n content,\n success: true,\n components: [component],\n };\n }\n \n return {\n content,\n success: true,\n };\n },\n};\n\nconst clearCommand: CommandDefinition = {\n id: 'session.clear',\n name: 'clear',\n description: 'Clear current session without archiving',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n // Just delete without archiving\n const messages = await ctx.getSession();\n await ctx.clearSession();\n \n return {\n content: `🗑️ Session cleared. ${messages.length} messages deleted.`,\n success: true,\n };\n },\n};\n\nconst abortCommand: CommandDefinition = {\n id: 'session.abort',\n name: 'abort',\n aliases: ['stop', 'cancel'],\n description: 'Stop the current assistant reply (in-flight generation or stream)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n if (!ctx.abortCurrentTurn) {\n return {\n content: 'Abort is not available in this environment.',\n success: false,\n };\n }\n await ctx.abortCurrentTurn();\n return {\n content: '⏹️ Current reply cancelled.',\n success: true,\n };\n },\n};\n\nconst compactCommand: CommandDefinition = {\n id: 'session.compact',\n name: 'compact',\n description: 'Compact session history (LLM summary + keep recent turns) to save context',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/compact', '/compact focus on API design'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const instructions = args.trim() || undefined;\n const result = await ctx.compactSession?.({ instructions, force: true });\n if (result === null || result === undefined) {\n return {\n content: '⚠️ Session compaction is not available in this environment.',\n success: false,\n };\n }\n if (!result.compacted) {\n return {\n content:\n 'ℹ️ Nothing to compact yet. Need at least two messages, or the session is already small.\\n' +\n 'Tip: add optional focus text, e.g. `/compact emphasize decisions about auth`.',\n success: true,\n };\n }\n const preview =\n result.summary && result.summary.length > 600\n ? `${result.summary.slice(0, 600)}…`\n : result.summary || '';\n return {\n content:\n `🗜️ *Session compacted*\\n\\n` +\n `Tokens (approx): ${result.tokensBefore} → ${result.tokensAfter}\\n\\n` +\n (preview ? `*Summary:*\\n${preview}` : ''),\n success: true,\n };\n },\n};\n\nconst btwCommand: CommandDefinition = {\n id: 'session.btw',\n name: 'btw',\n aliases: ['aside'],\n description: 'Ask a side question without adding to the session transcript',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/btw What does that error code mean?', '/aside Summarize the last topic in one line'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const q = args.trim();\n if (!q) {\n return {\n content:\n '💬 *Side question*\\n\\n' +\n 'Usage: `/btw <question>`\\n' +\n 'Answers use your current chat as background only; the reply is not saved to the session.',\n success: true,\n };\n }\n const out = await ctx.btwQuery?.(q);\n if (!out) {\n return { content: '⚠️ /btw is not available here.', success: false };\n }\n if (out.error) {\n return { content: `⚠️ ${out.error}`, success: false };\n }\n return { content: `💬 *BTW*\\n\\n${out.text}`, success: true };\n },\n};\n\nconst exportSessionCommand: CommandDefinition = {\n id: 'session.export',\n name: 'export-session',\n aliases: ['export'],\n description: 'Export this session to workspace exports/ (markdown, html, or json)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/export-session', '/export-session html', '/export json'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const raw = args.trim().toLowerCase();\n const fmt =\n raw === 'json' || raw === 'html' || raw === 'markdown'\n ? (raw as 'json' | 'html' | 'markdown')\n : 'markdown';\n if (!ctx.exportSessionToWorkspace) {\n return { content: '⚠️ Export is not available in this environment.', success: false };\n }\n try {\n const { path } = await ctx.exportSessionToWorkspace(fmt);\n return {\n content: `📄 Exported (${fmt}) to:\\n\\`${path}\\``,\n success: true,\n };\n } catch (e) {\n const em = e instanceof Error ? e.message : String(e);\n return { content: `⚠️ Export failed: ${em}`, success: false };\n }\n },\n};\n\nconst archiveCommand: CommandDefinition = {\n id: 'session.archive',\n name: 'archive',\n description: 'Archive current session',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.archiveSession();\n \n return {\n content: '📦 Current session has been archived.',\n success: true,\n };\n },\n};\n\n// Register all session commands\nexport function registerSessionCommands(): void {\n commandRegistry.register(newCommand);\n commandRegistry.register(listCommand);\n commandRegistry.register(clearCommand);\n commandRegistry.register(abortCommand);\n commandRegistry.register(compactCommand);\n commandRegistry.register(btwCommand);\n commandRegistry.register(exportSessionCommand);\n commandRegistry.register(archiveCommand);\n}\n"],"mappings":"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"}
|
|
@@ -41,8 +41,8 @@ const ttsCommand = {
|
|
|
41
41
|
const isEnabled = ttsConfig?.enabled ?? false;
|
|
42
42
|
const currentTrigger = ttsConfig?.trigger ?? "off";
|
|
43
43
|
const currentProvider = ttsConfig?.provider ?? "openai";
|
|
44
|
-
const
|
|
45
|
-
const currentVoice =
|
|
44
|
+
const providerSlice = ttsConfig?.providers?.[currentProvider];
|
|
45
|
+
const currentVoice = typeof providerSlice?.voice === "string" && providerSlice.voice.trim() ? providerSlice.voice : defaultTtsVoiceForProvider(currentProvider);
|
|
46
46
|
const ttsRuntimeOk = isTTSAvailable(mergeTtsConfigFromAppConfig(ttsConfig));
|
|
47
47
|
const arg = args.trim().toLowerCase();
|
|
48
48
|
const formatTimeAgo = (timestamp) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.js","names":[],"sources":["../../../../src/chat-commands/builtins/tts.ts"],"sourcesContent":["/**\n * TTS Commands\n *\n * Built-in commands for TTS management:\n * - /tts - Show TTS status\n * - /tts on - Enable TTS\n * - /tts off - Disable TTS\n * - /tts always - Set trigger mode to always\n * - /tts inbound - Set trigger mode to inbound\n * - /tts tagged - Set trigger mode to tagged\n * - /tts never - Set trigger mode to off\n * - /tts provider <provider> - Set TTS provider\n * - /tts voice <voice> - Set TTS voice\n */\n\nimport type { CommandDefinition, CommandContext } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport type { TTSAutoMode, TTSProvider } from '../../voice/tts/types.js';\nimport {\n appendTtsReadinessNote,\n formatTtsSetupHint,\n isTTSAvailable,\n mergeTtsConfigFromAppConfig,\n} from '../../voice/tts/index.js';\nimport { ttsStatusTracker } from '../../voice/tts/status-tracker.js';\n\nfunction defaultTtsVoiceForProvider(provider: string): string {\n switch (provider) {\n case 'openai':\n return 'alloy';\n case 'alibaba':\n return 'Cherry';\n case 'edge':\n return 'en-US-MichelleNeural';\n case 'minimax':\n return 'male-qn-qingse';\n default:\n return 'alloy';\n }\n}\n\nconst ttsCommand: CommandDefinition = {\n id: 'tts.manage',\n name: 'tts',\n description: 'Manage TTS (Text-to-Speech) settings',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: [\n '/tts',\n '/tts on',\n '/tts off',\n '/tts always',\n '/tts inbound',\n '/tts tagged',\n '/tts provider openai',\n '/tts provider minimax',\n '/tts voice alloy',\n '/tts status',\n ],\n handler: async (ctx: CommandContext, args: string) => {\n const config = ctx.getConfig?.();\n const ttsConfig = config?.messages?.tts;\n\n // Get current TTS status\n const isEnabled = ttsConfig?.enabled ?? false;\n const currentTrigger = ttsConfig?.trigger ?? 'off';\n const currentProvider = ttsConfig?.provider ?? 'openai';\n const voicePack = ttsConfig as\n | {\n openai?: { voice?: string };\n alibaba?: { voice?: string };\n edge?: { voice?: string };\n minimax?: { voice?: string };\n }\n | undefined;\n const currentVoice =\n (currentProvider === 'openai'\n ? voicePack?.openai?.voice\n : currentProvider === 'alibaba'\n ? voicePack?.alibaba?.voice\n : currentProvider === 'edge'\n ? voicePack?.edge?.voice\n : currentProvider === 'minimax'\n ? voicePack?.minimax?.voice\n : undefined) ?? defaultTtsVoiceForProvider(currentProvider);\n\n const effectiveTts = mergeTtsConfigFromAppConfig(ttsConfig);\n const ttsRuntimeOk = isTTSAvailable(effectiveTts);\n\n // Parse arguments\n const arg = args.trim().toLowerCase();\n\n const formatTimeAgo = (timestamp: number): string => {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;\n if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;\n return `${Math.floor(seconds / 86400)}d ago`;\n };\n\n const formatBytes = (bytes: number): string => {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n };\n\n if (!arg) {\n // Show current status\n const triggerLabels: Record<string, string> = {\n off: 'Off',\n always: 'Always',\n inbound: 'Inbound',\n tagged: 'Tagged',\n };\n\n const status = isEnabled ? '✅ Enabled' : '❌ Disabled';\n const trigger = triggerLabels[currentTrigger] ?? currentTrigger;\n\n const runtimeLine =\n !isEnabled\n ? ''\n : ttsRuntimeOk\n ? `Runtime: ✅ *Ready* (audio can be generated)\n`\n : `Runtime: ⚠️ *Not ready* — no provider can run with current config\n`;\n\n const setupHint =\n isEnabled && !ttsRuntimeOk ? `\\n${formatTtsSetupHint()}\\n` : ''\n\n return {\n content:\n `🔊 *TTS Settings*\n\n` +\n `Status: ${status}\n` +\n `Trigger Mode: *${trigger}*\n` +\n `Provider: *${currentProvider}*\n` +\n `Voice: *${currentVoice}*\n` +\n (runtimeLine ? `\\n${runtimeLine}` : '') +\n setupHint +\n `*Commands:*\n` +\n `/tts on - Enable TTS\n` +\n `/tts off - Disable TTS\n` +\n `/tts always - Always use TTS\n` +\n `/tts inbound - Only reply to voice with voice\n` +\n `/tts tagged - Only use TTS with [[tts]] directive\n` +\n `/tts status - Runtime TTS diagnostics (last call + stats)\n` +\n `/tts provider <openai|alibaba|minimax|edge> - Set provider\n` +\n `/tts voice <voice-id> - Set voice`,\n success: true,\n };\n }\n\n // Handle subcommands\n switch (arg) {\n case 'on':\n case 'enable': {\n const success = await ctx.updateConfig?.('tts.enabled', true);\n const base = success\n ? '✅ TTS enabled. Use `/tts always` or `/tts inbound` to set trigger mode.'\n : '❌ Failed to enable TTS.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'off':\n case 'disable': {\n const success = await ctx.updateConfig?.('tts.enabled', false);\n return {\n content: success\n ? '✅ TTS disabled.'\n : '❌ Failed to disable TTS.',\n success: !!success,\n };\n }\n\n case 'always':\n case 'inbound':\n case 'tagged': {\n const mode = arg as TTSAutoMode;\n const success = await ctx.updateConfig?.('tts.trigger', mode);\n if (success && !isEnabled) {\n // Also enable TTS if setting a trigger mode\n await ctx.updateConfig?.('tts.enabled', true);\n }\n const base = success\n ? `✅ TTS trigger mode set to *${mode}*${!isEnabled ? ' and TTS enabled' : ''}.`\n : `❌ Failed to set TTS trigger mode.`;\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'never': {\n const success = await ctx.updateConfig?.('tts.trigger', 'off');\n return {\n content: success\n ? '✅ TTS trigger mode set to *off*.'\n : '❌ Failed to set TTS trigger mode.',\n success: !!success,\n };\n }\n\n case 'status': {\n const status = ttsStatusTracker.getStatus();\n const lines: string[] = ['📊 *TTS Status*', ''];\n\n if (status.lastAttempt) {\n const last = status.lastAttempt;\n const timeAgo = formatTimeAgo(last.timestamp);\n const statusIcon = last.success ? '✅' : '❌';\n\n lines.push(`*Last attempt*: ${statusIcon} ${timeAgo}`);\n\n if (last.success) {\n lines.push(` Provider: ${last.provider ?? '—'}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n lines.push(\n ` Text: ${last.textLength ?? '—'} chars → Audio: ${formatBytes(last.audioSize || 0)}`,\n );\n if (last.usedFallback) lines.push(` ⚠️ Used fallback provider`);\n if (last.wasSummarized) lines.push(` 📝 Text was summarized`);\n } else {\n lines.push(` Error: ${last.error ?? '—'}`);\n if (last.provider) lines.push(` Provider: ${last.provider}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n }\n } else {\n lines.push('No TTS calls recorded yet.');\n }\n\n lines.push('');\n lines.push(\n `*Statistics*: ${status.totalCalls} calls, ${status.totalSuccesses} success, ${status.totalFailures} failed`,\n );\n\n if (status.recentSuccessRate !== undefined && status.totalCalls > 0) {\n const rate = (status.recentSuccessRate * 100).toFixed(0);\n const window = Math.min(status.totalCalls, 20);\n lines.push(`*Recent success rate*: ${rate}% (last ${window} calls)`);\n }\n\n return {\n content: lines.join('\\n'),\n success: true,\n };\n }\n\n default: {\n // Check for provider or voice subcommand with args\n const parts = arg.split(/\\s+/);\n const subcommand = parts[0];\n const subarg = parts[1];\n\n if (subcommand === 'provider' && subarg) {\n const provider = subarg as TTSProvider;\n if (!['openai', 'alibaba', 'minimax', 'edge'].includes(provider)) {\n return {\n content: `❌ Invalid provider: ${provider}\\nValid providers: openai, alibaba, minimax, edge`,\n success: false,\n };\n }\n const success = await ctx.updateConfig?.('tts.provider', provider);\n const base = success\n ? `✅ TTS provider set to *${provider}*.`\n : '❌ Failed to set TTS provider.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n if (subcommand === 'voice' && subarg) {\n const voice = subarg;\n const provider = currentProvider;\n const success = await ctx.updateConfig?.(`tts.${provider}.voice`, voice);\n return {\n content: success\n ? `✅ TTS voice set to *${voice}* for ${provider}.`\n : '❌ Failed to set TTS voice.',\n success: !!success,\n };\n }\n\n return {\n content: `❌ Unknown TTS command: ${arg}\\n\\nUse /tts to see available commands.`,\n success: false,\n };\n }\n }\n },\n};\n\n// Register TTS commands\nexport function registerTTSCommands(): void {\n commandRegistry.register(ttsCommand);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tts.js","names":[],"sources":["../../../../src/chat-commands/builtins/tts.ts"],"sourcesContent":["/**\n * TTS Commands\n *\n * Built-in commands for TTS management:\n * - /tts - Show TTS status\n * - /tts on - Enable TTS\n * - /tts off - Disable TTS\n * - /tts always - Set trigger mode to always\n * - /tts inbound - Set trigger mode to inbound\n * - /tts tagged - Set trigger mode to tagged\n * - /tts never - Set trigger mode to off\n * - /tts provider <provider> - Set TTS provider\n * - /tts voice <voice> - Set TTS voice\n */\n\nimport type { CommandDefinition, CommandContext } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport type { TTSAutoMode, TTSProvider } from '../../voice/tts/types.js';\nimport {\n appendTtsReadinessNote,\n formatTtsSetupHint,\n isTTSAvailable,\n mergeTtsConfigFromAppConfig,\n} from '../../voice/tts/index.js';\nimport { ttsStatusTracker } from '../../voice/tts/status-tracker.js';\n\nfunction defaultTtsVoiceForProvider(provider: string): string {\n switch (provider) {\n case 'openai':\n return 'alloy';\n case 'alibaba':\n return 'Cherry';\n case 'edge':\n return 'en-US-MichelleNeural';\n case 'minimax':\n return 'male-qn-qingse';\n default:\n return 'alloy';\n }\n}\n\nconst ttsCommand: CommandDefinition = {\n id: 'tts.manage',\n name: 'tts',\n description: 'Manage TTS (Text-to-Speech) settings',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: [\n '/tts',\n '/tts on',\n '/tts off',\n '/tts always',\n '/tts inbound',\n '/tts tagged',\n '/tts provider openai',\n '/tts provider minimax',\n '/tts voice alloy',\n '/tts status',\n ],\n handler: async (ctx: CommandContext, args: string) => {\n const config = ctx.getConfig?.();\n const ttsConfig = config?.messages?.tts;\n\n // Get current TTS status\n const isEnabled = ttsConfig?.enabled ?? false;\n const currentTrigger = ttsConfig?.trigger ?? 'off';\n const currentProvider = ttsConfig?.provider ?? 'openai';\n const providerSlice = ttsConfig?.providers?.[currentProvider] as\n | { voice?: string }\n | undefined;\n const currentVoice =\n typeof providerSlice?.voice === 'string' && providerSlice.voice.trim()\n ? providerSlice.voice\n : defaultTtsVoiceForProvider(currentProvider);\n\n const effectiveTts = mergeTtsConfigFromAppConfig(ttsConfig);\n const ttsRuntimeOk = isTTSAvailable(effectiveTts);\n\n // Parse arguments\n const arg = args.trim().toLowerCase();\n\n const formatTimeAgo = (timestamp: number): string => {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;\n if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;\n return `${Math.floor(seconds / 86400)}d ago`;\n };\n\n const formatBytes = (bytes: number): string => {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n };\n\n if (!arg) {\n // Show current status\n const triggerLabels: Record<string, string> = {\n off: 'Off',\n always: 'Always',\n inbound: 'Inbound',\n tagged: 'Tagged',\n };\n\n const status = isEnabled ? '✅ Enabled' : '❌ Disabled';\n const trigger = triggerLabels[currentTrigger] ?? currentTrigger;\n\n const runtimeLine =\n !isEnabled\n ? ''\n : ttsRuntimeOk\n ? `Runtime: ✅ *Ready* (audio can be generated)\n`\n : `Runtime: ⚠️ *Not ready* — no provider can run with current config\n`;\n\n const setupHint =\n isEnabled && !ttsRuntimeOk ? `\\n${formatTtsSetupHint()}\\n` : ''\n\n return {\n content:\n `🔊 *TTS Settings*\n\n` +\n `Status: ${status}\n` +\n `Trigger Mode: *${trigger}*\n` +\n `Provider: *${currentProvider}*\n` +\n `Voice: *${currentVoice}*\n` +\n (runtimeLine ? `\\n${runtimeLine}` : '') +\n setupHint +\n `*Commands:*\n` +\n `/tts on - Enable TTS\n` +\n `/tts off - Disable TTS\n` +\n `/tts always - Always use TTS\n` +\n `/tts inbound - Only reply to voice with voice\n` +\n `/tts tagged - Only use TTS with [[tts]] directive\n` +\n `/tts status - Runtime TTS diagnostics (last call + stats)\n` +\n `/tts provider <openai|alibaba|minimax|edge> - Set provider\n` +\n `/tts voice <voice-id> - Set voice`,\n success: true,\n };\n }\n\n // Handle subcommands\n switch (arg) {\n case 'on':\n case 'enable': {\n const success = await ctx.updateConfig?.('tts.enabled', true);\n const base = success\n ? '✅ TTS enabled. Use `/tts always` or `/tts inbound` to set trigger mode.'\n : '❌ Failed to enable TTS.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'off':\n case 'disable': {\n const success = await ctx.updateConfig?.('tts.enabled', false);\n return {\n content: success\n ? '✅ TTS disabled.'\n : '❌ Failed to disable TTS.',\n success: !!success,\n };\n }\n\n case 'always':\n case 'inbound':\n case 'tagged': {\n const mode = arg as TTSAutoMode;\n const success = await ctx.updateConfig?.('tts.trigger', mode);\n if (success && !isEnabled) {\n // Also enable TTS if setting a trigger mode\n await ctx.updateConfig?.('tts.enabled', true);\n }\n const base = success\n ? `✅ TTS trigger mode set to *${mode}*${!isEnabled ? ' and TTS enabled' : ''}.`\n : `❌ Failed to set TTS trigger mode.`;\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'never': {\n const success = await ctx.updateConfig?.('tts.trigger', 'off');\n return {\n content: success\n ? '✅ TTS trigger mode set to *off*.'\n : '❌ Failed to set TTS trigger mode.',\n success: !!success,\n };\n }\n\n case 'status': {\n const status = ttsStatusTracker.getStatus();\n const lines: string[] = ['📊 *TTS Status*', ''];\n\n if (status.lastAttempt) {\n const last = status.lastAttempt;\n const timeAgo = formatTimeAgo(last.timestamp);\n const statusIcon = last.success ? '✅' : '❌';\n\n lines.push(`*Last attempt*: ${statusIcon} ${timeAgo}`);\n\n if (last.success) {\n lines.push(` Provider: ${last.provider ?? '—'}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n lines.push(\n ` Text: ${last.textLength ?? '—'} chars → Audio: ${formatBytes(last.audioSize || 0)}`,\n );\n if (last.usedFallback) lines.push(` ⚠️ Used fallback provider`);\n if (last.wasSummarized) lines.push(` 📝 Text was summarized`);\n } else {\n lines.push(` Error: ${last.error ?? '—'}`);\n if (last.provider) lines.push(` Provider: ${last.provider}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n }\n } else {\n lines.push('No TTS calls recorded yet.');\n }\n\n lines.push('');\n lines.push(\n `*Statistics*: ${status.totalCalls} calls, ${status.totalSuccesses} success, ${status.totalFailures} failed`,\n );\n\n if (status.recentSuccessRate !== undefined && status.totalCalls > 0) {\n const rate = (status.recentSuccessRate * 100).toFixed(0);\n const window = Math.min(status.totalCalls, 20);\n lines.push(`*Recent success rate*: ${rate}% (last ${window} calls)`);\n }\n\n return {\n content: lines.join('\\n'),\n success: true,\n };\n }\n\n default: {\n // Check for provider or voice subcommand with args\n const parts = arg.split(/\\s+/);\n const subcommand = parts[0];\n const subarg = parts[1];\n\n if (subcommand === 'provider' && subarg) {\n const provider = subarg as TTSProvider;\n if (!['openai', 'alibaba', 'minimax', 'edge'].includes(provider)) {\n return {\n content: `❌ Invalid provider: ${provider}\\nValid providers: openai, alibaba, minimax, edge`,\n success: false,\n };\n }\n const success = await ctx.updateConfig?.('tts.provider', provider);\n const base = success\n ? `✅ TTS provider set to *${provider}*.`\n : '❌ Failed to set TTS provider.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n if (subcommand === 'voice' && subarg) {\n const voice = subarg;\n const provider = currentProvider;\n const success = await ctx.updateConfig?.(`tts.${provider}.voice`, voice);\n return {\n content: success\n ? `✅ TTS voice set to *${voice}* for ${provider}.`\n : '❌ Failed to set TTS voice.',\n success: !!success,\n };\n }\n\n return {\n content: `❌ Unknown TTS command: ${arg}\\n\\nUse /tts to see available commands.`,\n success: false,\n };\n }\n }\n },\n};\n\n// Register TTS commands\nexport function registerTTSCommands(): void {\n commandRegistry.register(ttsCommand);\n}\n"],"mappings":"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"}
|
|
@@ -19,6 +19,11 @@ import { bulletList, code, joinBlocks, section } from "../format-output.js";
|
|
|
19
19
|
* piece.
|
|
20
20
|
*/
|
|
21
21
|
const VIEW_MAX_LINES = 200;
|
|
22
|
+
function formatEntryDetail(entry) {
|
|
23
|
+
const tags = entry.tags?.length ? `[${entry.tags.join(", ")}] ` : "";
|
|
24
|
+
const agents = entry.estimatedAgents ? ` (~${entry.estimatedAgents.min}–${entry.estimatedAgents.max} agents)` : "";
|
|
25
|
+
return `${tags}${entry.description}${agents}`;
|
|
26
|
+
}
|
|
22
27
|
function formatWorkflowListContent(entries, userDir) {
|
|
23
28
|
const grouped = {
|
|
24
29
|
builtin: entries.filter((e) => e.source === "builtin"),
|
|
@@ -28,11 +33,11 @@ function formatWorkflowListContent(entries, userDir) {
|
|
|
28
33
|
const blocks = [];
|
|
29
34
|
if (grouped.user.length > 0) blocks.push(joinBlocks(section("User workflows"), bulletList(grouped.user.map((e) => ({
|
|
30
35
|
label: e.name,
|
|
31
|
-
detail: e
|
|
36
|
+
detail: formatEntryDetail(e)
|
|
32
37
|
})))));
|
|
33
38
|
if (grouped.builtin.length > 0) blocks.push(joinBlocks(section("Built-in workflows"), bulletList(grouped.builtin.map((e) => ({
|
|
34
39
|
label: e.name,
|
|
35
|
-
detail: e
|
|
40
|
+
detail: formatEntryDetail(e)
|
|
36
41
|
})))));
|
|
37
42
|
blocks.push(joinBlocks(section("How to run"), bulletList([
|
|
38
43
|
`Plain language: "run the ${exampleName} workflow"`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.js","names":[],"sources":["../../../../src/chat-commands/builtins/workflow.ts"],"sourcesContent":["/**\n * `/workflows` and `/workflow <subcommand>` — read-only commands for browsing\n * saved workflows.\n *\n * Why only read commands here? Slash command results are sent to the user; they\n * are NOT injected back into the agent's turn. Triggering a workflow run from\n * the slash layer would require a non-trivial cross-cutting \"inject this as the\n * next user message\" hook that doesn't exist yet. Instead, the workflow tool\n * understands `name`, and its description tells the model to prefer that route\n * whenever a user mentions a saved workflow by name. Plain text — \"run the\n * audit_repo workflow\" — works end-to-end.\n *\n * `/workflows` and `/workflow view` make discovery cheap, which is the missing\n * piece.\n */\n\nimport { commandRegistry } from '../registry.js';\nimport type { CommandContext, CommandDefinition } from '../types.js';\nimport { bulletList, code, joinBlocks, section } from '../format-output.js';\n\nimport { createWorkflowCatalog } from '../../agent/workflow/catalog.js';\nimport { getLastWorkflowMemory } from '../../agent/workflow/last-run-memory.js';\nimport type { CatalogEntry } from '../../agent/workflow/catalog.js';\n\nconst VIEW_MAX_LINES = 200;\n\nfunction formatWorkflowListContent(entries: CatalogEntry[], userDir: string): string {\n const grouped = {\n builtin: entries.filter((e) => e.source === 'builtin'),\n user: entries.filter((e) => e.source === 'user'),\n };\n const exampleName = entries[0]?.name ?? 'audit_repo';\n const blocks: string[] = [];\n\n if (grouped.user.length > 0) {\n blocks.push(\n joinBlocks(\n section('User workflows'),\n bulletList(grouped.user.map((e) => ({ label: e.name, detail: e.description }))),\n ),\n );\n }\n if (grouped.builtin.length > 0) {\n blocks.push(\n joinBlocks(\n section('Built-in workflows'),\n bulletList(grouped.builtin.map((e) => ({ label: e.name, detail: e.description }))),\n ),\n );\n }\n\n blocks.push(\n joinBlocks(\n section('How to run'),\n bulletList([\n `Plain language: \"run the ${exampleName} workflow\"`,\n `Inspect source: ${code(`/workflow view ${exampleName}`)}`,\n `Add your own: drop a ${code('.js')} at ${code(userDir)}`,\n ]),\n ),\n );\n\n return joinBlocks(...blocks);\n}\n\nconst workflowsCommand: CommandDefinition = {\n id: 'system.workflows',\n name: 'workflows',\n description: 'List saved workflows (built-in + ~/.xopc/workflows/)',\n category: 'system',\n scope: ['global', 'private', 'group'],\n handler: async (_ctx: CommandContext) => {\n const catalog = createWorkflowCatalog();\n const entries = catalog.list();\n if (entries.length === 0) {\n return {\n content: `No workflows found. Drop a script at ${code(`${catalog.userDir}/<name>.js`)} to add one.`,\n success: true,\n };\n }\n return { content: formatWorkflowListContent(entries, catalog.userDir), success: true };\n },\n};\n\nexport const workflowCommand: CommandDefinition = {\n id: 'system.workflow',\n name: 'workflow',\n description: 'Inspect or manage saved workflows. Subcommands: list, view <name>',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/workflow list', '/workflow view audit_repo'],\n handler: async (ctx: CommandContext, args: string) => {\n const trimmed = args.trim();\n if (!trimmed || trimmed.toLowerCase() === 'list') {\n return workflowsCommand.handler(ctx, '');\n }\n const [sub, ...rest] = trimmed.split(/\\s+/);\n const subLower = sub.toLowerCase();\n const target = rest.join(' ').trim();\n\n if (subLower === 'view' || subLower === 'show' || subLower === 'cat') {\n if (!target) {\n return { content: `usage: ${code('/workflow view <name>')}`, success: false };\n }\n const catalog = createWorkflowCatalog();\n try {\n const loaded = catalog.load(target);\n const lines = loaded.script.split('\\n');\n const visible =\n lines.length > VIEW_MAX_LINES\n ? [...lines.slice(0, VIEW_MAX_LINES), `… (truncated; ${lines.length - VIEW_MAX_LINES} more lines)`]\n : lines;\n const source = loaded.source === 'user' ? loaded.path ?? 'user' : 'built-in';\n return {\n content: joinBlocks(\n `**${loaded.name}** (${source}) — ${loaded.meta.description}`,\n '```js\\n' + visible.join('\\n') + '\\n```',\n ),\n success: true,\n };\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return { content: `error: ${message}`, success: false };\n }\n }\n\n if (subLower === 'run' || subLower === 'start') {\n const name = target || '<name>';\n return {\n content: joinBlocks(\n `To run a workflow, ask in plain language: \"run the ${name} workflow\".`,\n `The assistant will call the workflow tool with ${code(`name=\"${name}\"`)} and stream progress inline.`,\n ),\n success: true,\n };\n }\n\n if (subLower === 'save') {\n if (!target) {\n return { content: `usage: ${code('/workflow save <name>')}`, success: false };\n }\n const last = getLastWorkflowMemory().get(ctx.sessionKey);\n if (!last) {\n return {\n content:\n 'No workflow has run successfully in this session yet. Run one first (e.g. ask \"run the audit_repo workflow\"), then `/workflow save <name>`.',\n success: false,\n };\n }\n const catalog = createWorkflowCatalog();\n try {\n // Allow the user to rename: if target differs from meta.name, rewrite it\n // before saving so the file is addressable as `target`.\n const script =\n last.metaName === target ? last.script : rewriteMetaName(last.script, target);\n const { path } = catalog.save(target, script);\n return {\n content: joinBlocks(\n `✓ Saved workflow **${target}** → ${code(path)}`,\n bulletList([\n `Trigger with ${code(`/${target}`)} or \"run the ${target} workflow\"`,\n `Inspect with ${code(`/workflow view ${target}`)}`,\n ]),\n ),\n success: true,\n };\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return { content: `error: ${message}`, success: false };\n }\n }\n\n return {\n content: joinBlocks(\n `Unknown subcommand \"${sub}\". Available: list, view ${code('<name>')}, save ${code('<name>')}.`,\n 'To run a workflow, ask in plain language (\"run the audit_repo workflow\") — the assistant uses the workflow tool with `name=\"...\"`.',\n ),\n success: false,\n };\n },\n};\n\n/**\n * Replace the `name` field inside the FIRST `export const meta = { ... }` literal.\n *\n * Why text-level (not AST re-emit)? The parser already accepted the script\n * once (the runtime ran it), so the surrounding code is unchanged. A targeted\n * regex on the `name: '...'` slot inside the first object literal keeps the\n * user's formatting / comments / quote style intact, which an AST round-trip\n * would smash. The match anchors to the first `name:` after `export const meta`\n * and only touches that single value.\n */\nfunction rewriteMetaName(script: string, newName: string): string {\n const re = /(export\\s+const\\s+meta\\s*=\\s*\\{[^}]*?\\bname\\s*:\\s*)(['\"`])([^'\"`]*)\\2/;\n if (!re.test(script)) {\n throw new Error('could not locate meta.name in the recorded script to rewrite');\n }\n return script.replace(re, (_m, prefix, quote) => `${prefix}${quote}${newName}${quote}`);\n}\n\nexport function registerWorkflowCommands(): void {\n commandRegistry.register(workflowsCommand);\n commandRegistry.register(workflowCommand);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workflow.js","names":[],"sources":["../../../../src/chat-commands/builtins/workflow.ts"],"sourcesContent":["/**\n * `/workflows` and `/workflow <subcommand>` — read-only commands for browsing\n * saved workflows.\n *\n * Why only read commands here? Slash command results are sent to the user; they\n * are NOT injected back into the agent's turn. Triggering a workflow run from\n * the slash layer would require a non-trivial cross-cutting \"inject this as the\n * next user message\" hook that doesn't exist yet. Instead, the workflow tool\n * understands `name`, and its description tells the model to prefer that route\n * whenever a user mentions a saved workflow by name. Plain text — \"run the\n * audit_repo workflow\" — works end-to-end.\n *\n * `/workflows` and `/workflow view` make discovery cheap, which is the missing\n * piece.\n */\n\nimport { commandRegistry } from '../registry.js';\nimport type { CommandContext, CommandDefinition } from '../types.js';\nimport { bulletList, code, joinBlocks, section } from '../format-output.js';\n\nimport { createWorkflowCatalog } from '../../agent/workflow/catalog.js';\nimport { getLastWorkflowMemory } from '../../agent/workflow/last-run-memory.js';\nimport type { CatalogEntry } from '../../agent/workflow/catalog.js';\n\nconst VIEW_MAX_LINES = 200;\n\nfunction formatEntryDetail(entry: CatalogEntry): string {\n const tags = entry.tags?.length ? `[${entry.tags.join(', ')}] ` : '';\n const agents = entry.estimatedAgents\n ? ` (~${entry.estimatedAgents.min}–${entry.estimatedAgents.max} agents)`\n : '';\n return `${tags}${entry.description}${agents}`;\n}\n\nfunction formatWorkflowListContent(entries: CatalogEntry[], userDir: string): string {\n const grouped = {\n builtin: entries.filter((e) => e.source === 'builtin'),\n user: entries.filter((e) => e.source === 'user'),\n };\n const exampleName = entries[0]?.name ?? 'audit_repo';\n const blocks: string[] = [];\n\n if (grouped.user.length > 0) {\n blocks.push(\n joinBlocks(\n section('User workflows'),\n bulletList(grouped.user.map((e) => ({ label: e.name, detail: formatEntryDetail(e) }))),\n ),\n );\n }\n if (grouped.builtin.length > 0) {\n blocks.push(\n joinBlocks(\n section('Built-in workflows'),\n bulletList(grouped.builtin.map((e) => ({ label: e.name, detail: formatEntryDetail(e) }))),\n ),\n );\n }\n\n blocks.push(\n joinBlocks(\n section('How to run'),\n bulletList([\n `Plain language: \"run the ${exampleName} workflow\"`,\n `Inspect source: ${code(`/workflow view ${exampleName}`)}`,\n `Add your own: drop a ${code('.js')} at ${code(userDir)}`,\n ]),\n ),\n );\n\n return joinBlocks(...blocks);\n}\n\nconst workflowsCommand: CommandDefinition = {\n id: 'system.workflows',\n name: 'workflows',\n description: 'List saved workflows (built-in + ~/.xopc/workflows/)',\n category: 'system',\n scope: ['global', 'private', 'group'],\n handler: async (_ctx: CommandContext) => {\n const catalog = createWorkflowCatalog();\n const entries = catalog.list();\n if (entries.length === 0) {\n return {\n content: `No workflows found. Drop a script at ${code(`${catalog.userDir}/<name>.js`)} to add one.`,\n success: true,\n };\n }\n return { content: formatWorkflowListContent(entries, catalog.userDir), success: true };\n },\n};\n\nexport const workflowCommand: CommandDefinition = {\n id: 'system.workflow',\n name: 'workflow',\n description: 'Inspect or manage saved workflows. Subcommands: list, view <name>',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/workflow list', '/workflow view audit_repo'],\n handler: async (ctx: CommandContext, args: string) => {\n const trimmed = args.trim();\n if (!trimmed || trimmed.toLowerCase() === 'list') {\n return workflowsCommand.handler(ctx, '');\n }\n const [sub, ...rest] = trimmed.split(/\\s+/);\n const subLower = sub.toLowerCase();\n const target = rest.join(' ').trim();\n\n if (subLower === 'view' || subLower === 'show' || subLower === 'cat') {\n if (!target) {\n return { content: `usage: ${code('/workflow view <name>')}`, success: false };\n }\n const catalog = createWorkflowCatalog();\n try {\n const loaded = catalog.load(target);\n const lines = loaded.script.split('\\n');\n const visible =\n lines.length > VIEW_MAX_LINES\n ? [...lines.slice(0, VIEW_MAX_LINES), `… (truncated; ${lines.length - VIEW_MAX_LINES} more lines)`]\n : lines;\n const source = loaded.source === 'user' ? loaded.path ?? 'user' : 'built-in';\n return {\n content: joinBlocks(\n `**${loaded.name}** (${source}) — ${loaded.meta.description}`,\n '```js\\n' + visible.join('\\n') + '\\n```',\n ),\n success: true,\n };\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return { content: `error: ${message}`, success: false };\n }\n }\n\n if (subLower === 'run' || subLower === 'start') {\n const name = target || '<name>';\n return {\n content: joinBlocks(\n `To run a workflow, ask in plain language: \"run the ${name} workflow\".`,\n `The assistant will call the workflow tool with ${code(`name=\"${name}\"`)} and stream progress inline.`,\n ),\n success: true,\n };\n }\n\n if (subLower === 'save') {\n if (!target) {\n return { content: `usage: ${code('/workflow save <name>')}`, success: false };\n }\n const last = getLastWorkflowMemory().get(ctx.sessionKey);\n if (!last) {\n return {\n content:\n 'No workflow has run successfully in this session yet. Run one first (e.g. ask \"run the audit_repo workflow\"), then `/workflow save <name>`.',\n success: false,\n };\n }\n const catalog = createWorkflowCatalog();\n try {\n // Allow the user to rename: if target differs from meta.name, rewrite it\n // before saving so the file is addressable as `target`.\n const script =\n last.metaName === target ? last.script : rewriteMetaName(last.script, target);\n const { path } = catalog.save(target, script);\n return {\n content: joinBlocks(\n `✓ Saved workflow **${target}** → ${code(path)}`,\n bulletList([\n `Trigger with ${code(`/${target}`)} or \"run the ${target} workflow\"`,\n `Inspect with ${code(`/workflow view ${target}`)}`,\n ]),\n ),\n success: true,\n };\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return { content: `error: ${message}`, success: false };\n }\n }\n\n return {\n content: joinBlocks(\n `Unknown subcommand \"${sub}\". Available: list, view ${code('<name>')}, save ${code('<name>')}.`,\n 'To run a workflow, ask in plain language (\"run the audit_repo workflow\") — the assistant uses the workflow tool with `name=\"...\"`.',\n ),\n success: false,\n };\n },\n};\n\n/**\n * Replace the `name` field inside the FIRST `export const meta = { ... }` literal.\n *\n * Why text-level (not AST re-emit)? The parser already accepted the script\n * once (the runtime ran it), so the surrounding code is unchanged. A targeted\n * regex on the `name: '...'` slot inside the first object literal keeps the\n * user's formatting / comments / quote style intact, which an AST round-trip\n * would smash. The match anchors to the first `name:` after `export const meta`\n * and only touches that single value.\n */\nfunction rewriteMetaName(script: string, newName: string): string {\n const re = /(export\\s+const\\s+meta\\s*=\\s*\\{[^}]*?\\bname\\s*:\\s*)(['\"`])([^'\"`]*)\\2/;\n if (!re.test(script)) {\n throw new Error('could not locate meta.name in the recorded script to rewrite');\n }\n return script.replace(re, (_m, prefix, quote) => `${prefix}${quote}${newName}${quote}`);\n}\n\nexport function registerWorkflowCommands(): void {\n commandRegistry.register(workflowsCommand);\n commandRegistry.register(workflowCommand);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,iBAAiB;AAEvB,SAAS,kBAAkB,OAA6B;CACtD,MAAM,OAAO,MAAM,MAAM,SAAS,IAAI,MAAM,KAAK,KAAK,KAAK,CAAC,MAAM;CAClE,MAAM,SAAS,MAAM,kBACjB,MAAM,MAAM,gBAAgB,IAAI,GAAG,MAAM,gBAAgB,IAAI,YAC7D;AACJ,QAAO,GAAG,OAAO,MAAM,cAAc;;AAGvC,SAAS,0BAA0B,SAAyB,SAAyB;CACnF,MAAM,UAAU;EACd,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU;EACtD,MAAM,QAAQ,QAAQ,MAAM,EAAE,WAAW,OAAO;EACjD;CACD,MAAM,cAAc,QAAQ,IAAI,QAAQ;CACxC,MAAM,SAAmB,EAAE;AAE3B,KAAI,QAAQ,KAAK,SAAS,EACxB,QAAO,KACL,WACE,QAAQ,iBAAiB,EACzB,WAAW,QAAQ,KAAK,KAAK,OAAO;EAAE,OAAO,EAAE;EAAM,QAAQ,kBAAkB,EAAE;EAAE,EAAE,CAAC,CACvF,CACF;AAEH,KAAI,QAAQ,QAAQ,SAAS,EAC3B,QAAO,KACL,WACE,QAAQ,qBAAqB,EAC7B,WAAW,QAAQ,QAAQ,KAAK,OAAO;EAAE,OAAO,EAAE;EAAM,QAAQ,kBAAkB,EAAE;EAAE,EAAE,CAAC,CAC1F,CACF;AAGH,QAAO,KACL,WACE,QAAQ,aAAa,EACrB,WAAW;EACT,4BAA4B,YAAY;EACxC,mBAAmB,KAAK,kBAAkB,cAAc;EACxD,wBAAwB,KAAK,MAAM,CAAC,MAAM,KAAK,QAAQ;EACxD,CAAC,CACH,CACF;AAED,QAAO,WAAW,GAAG,OAAO;;AAG9B,MAAM,mBAAsC;CAC1C,IAAI;CACJ,MAAM;CACN,aAAa;CACb,UAAU;CACV,OAAO;EAAC;EAAU;EAAW;EAAQ;CACrC,SAAS,OAAO,SAAyB;EACvC,MAAM,UAAU,uBAAuB;EACvC,MAAM,UAAU,QAAQ,MAAM;AAC9B,MAAI,QAAQ,WAAW,EACrB,QAAO;GACL,SAAS,wCAAwC,KAAK,GAAG,QAAQ,QAAQ,YAAY,CAAC;GACtF,SAAS;GACV;AAEH,SAAO;GAAE,SAAS,0BAA0B,SAAS,QAAQ,QAAQ;GAAE,SAAS;GAAM;;CAEzF;AAED,MAAa,kBAAqC;CAChD,IAAI;CACJ,MAAM;CACN,aAAa;CACb,UAAU;CACV,OAAO;EAAC;EAAU;EAAW;EAAQ;CACrC,aAAa;CACb,UAAU,CAAC,kBAAkB,4BAA4B;CACzD,SAAS,OAAO,KAAqB,SAAiB;EACpD,MAAM,UAAU,KAAK,MAAM;AAC3B,MAAI,CAAC,WAAW,QAAQ,aAAa,KAAK,OACxC,QAAO,iBAAiB,QAAQ,KAAK,GAAG;EAE1C,MAAM,CAAC,KAAK,GAAG,QAAQ,QAAQ,MAAM,MAAM;EAC3C,MAAM,WAAW,IAAI,aAAa;EAClC,MAAM,SAAS,KAAK,KAAK,IAAI,CAAC,MAAM;AAEpC,MAAI,aAAa,UAAU,aAAa,UAAU,aAAa,OAAO;AACpE,OAAI,CAAC,OACH,QAAO;IAAE,SAAS,UAAU,KAAK,wBAAwB;IAAI,SAAS;IAAO;GAE/E,MAAM,UAAU,uBAAuB;AACvC,OAAI;IACF,MAAM,SAAS,QAAQ,KAAK,OAAO;IACnC,MAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;IACvC,MAAM,UACJ,MAAM,SAAS,iBACX,CAAC,GAAG,MAAM,MAAM,GAAG,eAAe,EAAE,iBAAiB,MAAM,SAAS,eAAe,cAAc,GACjG;IACN,MAAM,SAAS,OAAO,WAAW,SAAS,OAAO,QAAQ,SAAS;AAClE,WAAO;KACL,SAAS,WACP,KAAK,OAAO,KAAK,MAAM,OAAO,MAAM,OAAO,KAAK,eAChD,YAAY,QAAQ,KAAK,KAAK,GAAG,QAClC;KACD,SAAS;KACV;YACM,GAAG;AAEV,WAAO;KAAE,SAAS,UADF,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;KACnB,SAAS;KAAO;;;AAI3D,MAAI,aAAa,SAAS,aAAa,SAAS;GAC9C,MAAM,OAAO,UAAU;AACvB,UAAO;IACL,SAAS,WACP,sDAAsD,KAAK,cAC3D,kDAAkD,KAAK,SAAS,KAAK,GAAG,CAAC,8BAC1E;IACD,SAAS;IACV;;AAGH,MAAI,aAAa,QAAQ;AACvB,OAAI,CAAC,OACH,QAAO;IAAE,SAAS,UAAU,KAAK,wBAAwB;IAAI,SAAS;IAAO;GAE/E,MAAM,OAAO,uBAAuB,CAAC,IAAI,IAAI,WAAW;AACxD,OAAI,CAAC,KACH,QAAO;IACL,SACE;IACF,SAAS;IACV;GAEH,MAAM,UAAU,uBAAuB;AACvC,OAAI;IAGF,MAAM,SACJ,KAAK,aAAa,SAAS,KAAK,SAAS,gBAAgB,KAAK,QAAQ,OAAO;IAC/E,MAAM,EAAE,SAAS,QAAQ,KAAK,QAAQ,OAAO;AAC7C,WAAO;KACL,SAAS,WACP,sBAAsB,OAAO,OAAO,KAAK,KAAK,IAC9C,WAAW,CACT,gBAAgB,KAAK,IAAI,SAAS,CAAC,eAAe,OAAO,aACzD,gBAAgB,KAAK,kBAAkB,SAAS,GACjD,CAAC,CACH;KACD,SAAS;KACV;YACM,GAAG;AAEV,WAAO;KAAE,SAAS,UADF,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;KACnB,SAAS;KAAO;;;AAI3D,SAAO;GACL,SAAS,WACP,uBAAuB,IAAI,2BAA2B,KAAK,SAAS,CAAC,SAAS,KAAK,SAAS,CAAC,IAC7F,yIACD;GACD,SAAS;GACV;;CAEJ;;;;;;;;;;;AAYD,SAAS,gBAAgB,QAAgB,SAAyB;CAChE,MAAM,KAAK;AACX,KAAI,CAAC,GAAG,KAAK,OAAO,CAClB,OAAM,IAAI,MAAM,+DAA+D;AAEjF,QAAO,OAAO,QAAQ,KAAK,IAAI,QAAQ,UAAU,GAAG,SAAS,QAAQ,UAAU,QAAQ;;AAGzF,SAAgB,2BAAiC;AAC/C,iBAAgB,SAAS,iBAAiB;AAC1C,iBAAgB,SAAS,gBAAgB"}
|
|
@@ -31,6 +31,8 @@ export interface CommandContextDeps {
|
|
|
31
31
|
supportedFeatures: PlatformFeature[];
|
|
32
32
|
/** Called after session files are removed so in-memory agents match disk */
|
|
33
33
|
invalidateAgentSession?: (sessionKey: string) => void;
|
|
34
|
+
/** Reset session in place (archive + new session id); optional — falls back to clearSession */
|
|
35
|
+
resetSession?: (sessionKey: string) => Promise<void>;
|
|
34
36
|
getCurrentModel?: () => string;
|
|
35
37
|
switchModel?: (modelId: string) => Promise<boolean>;
|
|
36
38
|
listModels?: () => Promise<ModelInfo[]>;
|
|
@@ -64,6 +66,7 @@ export declare class CommandContextImpl implements CommandContext {
|
|
|
64
66
|
replyComponent(component: UIComponent): Promise<void>;
|
|
65
67
|
setTyping(typing: boolean): Promise<void>;
|
|
66
68
|
getSession(): Promise<AgentMessage[]>;
|
|
69
|
+
resetSession(): Promise<void>;
|
|
67
70
|
clearSession(): Promise<void>;
|
|
68
71
|
archiveSession(): Promise<void>;
|
|
69
72
|
listSessions(): Promise<SessionInfo[]>;
|
|
@@ -51,6 +51,25 @@ var CommandContextImpl = class {
|
|
|
51
51
|
async getSession() {
|
|
52
52
|
return this.deps.sessionStore.load(this.sessionKey);
|
|
53
53
|
}
|
|
54
|
+
async resetSession() {
|
|
55
|
+
if (this.deps.resetSession) await this.deps.resetSession(this.sessionKey);
|
|
56
|
+
else if (typeof this.deps.sessionStore.reset === "function") {
|
|
57
|
+
if (!await this.deps.sessionStore.reset(this.sessionKey)) throw new Error("Session not found");
|
|
58
|
+
this.deps.invalidateAgentSession?.(this.sessionKey);
|
|
59
|
+
} else {
|
|
60
|
+
await this.clearSession();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const routing = getRoutingInfo(this.sessionKey);
|
|
64
|
+
await this.deps.bus.publishOutbound({
|
|
65
|
+
channel: routing.channel,
|
|
66
|
+
chat_id: routing.chatId,
|
|
67
|
+
content: "✅ New session started. Previous transcript archived; model and session overrides kept.",
|
|
68
|
+
type: "message",
|
|
69
|
+
metadata: { threadId: routing.threadId }
|
|
70
|
+
});
|
|
71
|
+
log.info({ sessionKey: this.sessionKey }, "Session reset");
|
|
72
|
+
}
|
|
54
73
|
async clearSession() {
|
|
55
74
|
const messages = await this.getSession();
|
|
56
75
|
if (messages.length > 0) {
|
|
@@ -66,7 +85,7 @@ var CommandContextImpl = class {
|
|
|
66
85
|
await this.deps.bus.publishOutbound({
|
|
67
86
|
channel: routing.channel,
|
|
68
87
|
chat_id: routing.chatId,
|
|
69
|
-
content: "✅
|
|
88
|
+
content: "✅ Session cleared.",
|
|
70
89
|
type: "message",
|
|
71
90
|
metadata: { threadId: routing.threadId }
|
|
72
91
|
});
|
|
@@ -95,8 +114,7 @@ var CommandContextImpl = class {
|
|
|
95
114
|
}
|
|
96
115
|
getCurrentModel() {
|
|
97
116
|
if (this.deps.getCurrentModel) return this.deps.getCurrentModel();
|
|
98
|
-
|
|
99
|
-
return typeof modelConfig === "string" ? modelConfig : modelConfig?.primary || "minimax/minimax-m2.1";
|
|
117
|
+
return this.config.agents?.defaults?.model?.primary || "minimax/minimax-m2.1";
|
|
100
118
|
}
|
|
101
119
|
async listModels() {
|
|
102
120
|
if (this.deps.listModels) return this.deps.listModels();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":[],"sources":["../../../src/chat-commands/context.ts"],"sourcesContent":["/**\n * Command Context Implementation\n * \n * Provides the concrete implementation of CommandContext interface,\n * bridging commands to core services (AgentService, SessionStore, etc.)\n */\n\nimport type {\n CommandContext,\n ReplyOptions,\n UIComponent,\n SessionInfo,\n ModelInfo,\n UsageStats,\n PlatformFeature,\n MessageSource,\n CompactSessionResult,\n} from './types.js';\nimport { getSessionDisplayName } from './session-key.js';\nimport type { Config } from '../config/schema.js';\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { SessionStore, SessionConfigStore } from '../session/index.js';\nimport { createLogger } from '../utils/logger.js';\nimport { getRoutingInfo } from './session-key.js';\nimport { saveConfig } from '../config/loader.js';\nimport type { ThinkLevel, ReasoningLevel, VerboseLevel } from '../agent/transcript/thinking-types.js';\nimport { mkdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { effectiveWorkspacePathForSession } from '../session/session-workspace.js';\nimport { wrapMarkdownExportAsHtml } from '../session/chat-export.js';\nimport type { CompactionResult } from '../agent/memory/compaction.js';\nimport type { PersistentGoalApis } from '../agent/goals/persistent-goal-apis.js';\n\nconst log = createLogger('CommandContext');\n\nexport interface CommandContextDeps {\n sessionKey: string;\n source: MessageSource;\n channelId: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After persisting session thinking, sync pi-agent in-memory state */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n // Callbacks for platform-specific operations\n replyHandler: (text: string, options?: ReplyOptions) => Promise<void>;\n componentHandler?: (component: UIComponent) => Promise<void>;\n typingHandler?: (typing: boolean) => Promise<void>;\n supportedFeatures: PlatformFeature[];\n /** Called after session files are removed so in-memory agents match disk */\n invalidateAgentSession?: (sessionKey: string) => void;\n // Model management (optional, will be injected)\n getCurrentModel?: () => string;\n switchModel?: (modelId: string) => Promise<boolean>;\n listModels?: () => Promise<ModelInfo[]>;\n getUsage?: () => Promise<UsageStats>;\n /** Stop current LLM turn and clear channel preview stream (Telegram draft, etc.) */\n abortCurrentTurn?: () => Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n\n persistentGoalApis?: PersistentGoalApis;\n}\n\nexport class CommandContextImpl implements CommandContext {\n readonly sessionKey: string;\n readonly source: MessageSource;\n readonly channelId: string;\n readonly chatId: string;\n readonly senderId: string;\n readonly isGroup: boolean;\n readonly config: Config;\n readonly abortCurrentTurn?: () => Promise<void>;\n readonly persistentGoalApis?: PersistentGoalApis;\n\n private deps: CommandContextDeps;\n\n constructor(deps: CommandContextDeps) {\n this.sessionKey = deps.sessionKey;\n this.source = deps.source;\n this.channelId = deps.channelId;\n this.chatId = deps.chatId;\n this.senderId = deps.senderId;\n this.isGroup = deps.isGroup;\n this.config = deps.config;\n this.deps = deps;\n\n if (deps.abortCurrentTurn) {\n const run = deps.abortCurrentTurn;\n this.abortCurrentTurn = async () => {\n await run();\n };\n }\n\n this.persistentGoalApis = deps.persistentGoalApis;\n }\n\n // === Reply API ===\n\n async reply(text: string, options?: ReplyOptions): Promise<void> {\n await this.deps.replyHandler(text, options);\n }\n\n async replyComponent(component: UIComponent): Promise<void> {\n if (this.deps.componentHandler) {\n await this.deps.componentHandler(component);\n } else {\n // Fallback to text representation\n await this.reply(this.renderComponentAsText(component));\n }\n }\n\n async setTyping(typing: boolean): Promise<void> {\n if (this.deps.typingHandler) {\n await this.deps.typingHandler(typing);\n }\n }\n\n // === Session Management ===\n\n async getSession(): Promise<AgentMessage[]> {\n return this.deps.sessionStore.load(this.sessionKey);\n }\n\n async clearSession(): Promise<void> {\n // Archive first if has messages\n const messages = await this.getSession();\n if (messages.length > 0) {\n await this.deps.sessionStore.archive(this.sessionKey);\n log.info({ sessionKey: this.sessionKey, messageCount: messages.length }, 'Session archived');\n }\n\n // Delete session\n await this.deps.sessionStore.deleteSession(this.sessionKey);\n this.deps.invalidateAgentSession?.(this.sessionKey);\n\n // Publish outbound message to confirm\n const routing = getRoutingInfo(this.sessionKey);\n await this.deps.bus.publishOutbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n content: '✅ New session started. Previous session has been archived.',\n type: 'message',\n metadata: {\n threadId: routing.threadId,\n },\n });\n\n log.info({ sessionKey: this.sessionKey }, 'Session cleared');\n }\n\n async archiveSession(): Promise<void> {\n await this.deps.sessionStore.archive(this.sessionKey);\n log.info({ sessionKey: this.sessionKey }, 'Session archived');\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n // TODO: Implement listSessions in SessionStore\n // For now, return current session only\n const messages = await this.getSession();\n return [{\n key: this.sessionKey,\n name: getSessionDisplayName(this.sessionKey),\n messageCount: messages.length,\n createdAt: new Date(),\n updatedAt: new Date(),\n isActive: true,\n }];\n }\n\n async switchSession(sessionKey: string): Promise<void> {\n // This is mainly for CLI/Web UI where you can switch between sessions\n // For Telegram, each chat has its own session\n log.info({ from: this.sessionKey, to: sessionKey }, 'Session switch requested');\n \n // Note: In the current architecture, switching session means\n // the next message will use a different sessionKey\n // The actual switch happens at the adapter level\n }\n\n // === Model Management ===\n\n getCurrentModel(): string {\n if (this.deps.getCurrentModel) {\n return this.deps.getCurrentModel();\n }\n \n // Fallback to config default\n const modelConfig = this.config.agents?.defaults?.model;\n return typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary || 'minimax/minimax-m2.1';\n }\n\n async listModels(): Promise<ModelInfo[]> {\n if (this.deps.listModels) {\n return this.deps.listModels();\n }\n \n // Fallback to empty list\n return [];\n }\n\n async switchModel(modelId: string): Promise<boolean> {\n if (this.deps.switchModel) {\n return this.deps.switchModel(modelId);\n }\n \n // No model manager available\n await this.reply('❌ Model switching not available in this context.');\n return false;\n }\n\n async getUsage(): Promise<UsageStats> {\n if (this.deps.getUsage) {\n return this.deps.getUsage();\n }\n \n // Fallback: calculate from session\n const messages = await this.getSession();\n let promptTokens = 0;\n let completionTokens = 0;\n \n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n promptTokens += msg.usage.input || 0;\n completionTokens += msg.usage.output || 0;\n }\n }\n \n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n }\n\n // === Platform Features ===\n\n supports(feature: PlatformFeature): boolean {\n return this.deps.supportedFeatures.includes(feature);\n }\n\n // === Configuration ===\n\n getConfig(): Config {\n return this.config;\n }\n\n async updateConfig(path: string, value: unknown): Promise<boolean> {\n try {\n // Update config object using path\n const keys = path.split('.');\n let target: Record<string, unknown> = this.config as Record<string, unknown>;\n \n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (!(key in target) || typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n target = target[key] as Record<string, unknown>;\n }\n \n target[keys[keys.length - 1]] = value;\n \n // Save to disk\n await saveConfig(this.config);\n \n log.info({ path, value }, 'Config updated via command');\n return true;\n } catch (error) {\n log.error({ err: error, path, value }, 'Failed to update config');\n return false;\n }\n }\n\n // === Private Helpers ===\n\n // === Thinking Configuration ===\n\n /**\n * Get the session config store (if available)\n */\n getSessionConfigStore(): SessionConfigStore | undefined {\n return this.deps.sessionConfigStore;\n }\n\n /**\n * Get current thinking level (session override or default)\n */\n async getThinkingLevel(): Promise<ThinkLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.thinkingLevel) {\n return sessionConfig.thinkingLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.thinkingDefault;\n }\n\n /**\n * Set thinking level for this session\n */\n async setThinkingLevel(level: ThinkLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { thinkingLevel: level });\n }\n this.deps.applySessionThinkingLevel?.(this.sessionKey, level);\n }\n\n syncAgentThinkingLevel(level: ThinkLevel): void {\n this.deps.applySessionThinkingLevel?.(this.sessionKey, level);\n }\n\n async compactSession(options?: { instructions?: string; force?: boolean }): Promise<CompactSessionResult | null> {\n if (!this.deps.compactSession) {\n return null;\n }\n const r = await this.deps.compactSession(this.sessionKey, options);\n return {\n compacted: r.compacted,\n tokensBefore: r.tokensBefore,\n tokensAfter: r.tokensAfter,\n summary: r.summary,\n };\n }\n\n async btwQuery(question: string): Promise<{ text: string; error?: string }> {\n if (!this.deps.btwQuery) {\n return { text: '', error: 'Side questions are not available in this environment.' };\n }\n return this.deps.btwQuery(this.sessionKey, question);\n }\n\n async exportSessionToWorkspace(format: 'markdown' | 'html' | 'json'): Promise<{ path: string }> {\n const exportFmt = format === 'json' ? 'json' : 'markdown';\n let body = await this.deps.sessionStore.exportSession(this.sessionKey, exportFmt);\n if (format === 'html') {\n body = wrapMarkdownExportAsHtml(`Session ${this.sessionKey}`, body);\n }\n const sc = this.deps.sessionConfigStore\n ? await this.deps.sessionConfigStore.get(this.sessionKey)\n : null;\n const root = effectiveWorkspacePathForSession(this.config, this.sessionKey, sc);\n const dir = join(root, 'exports');\n await mkdir(dir, { recursive: true });\n const safe = this.sessionKey.replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 96);\n const ext = format === 'json' ? 'json' : format === 'html' ? 'html' : 'md';\n const name = `session-${safe}-${Date.now()}.${ext}`;\n const outPath = join(dir, name);\n await writeFile(outPath, body, 'utf-8');\n return { path: outPath };\n }\n\n async agentContextReport(mode: 'list' | 'detail' | 'json' = 'list'): Promise<string> {\n if (!this.deps.getSessionContextReport) {\n return 'Context report is not available in this environment.';\n }\n return this.deps.getSessionContextReport(this.sessionKey, mode);\n }\n\n /**\n * Get current reasoning level (session override or default)\n */\n async getReasoningLevel(): Promise<ReasoningLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.reasoningLevel) {\n return sessionConfig.reasoningLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.reasoningDefault;\n }\n\n /**\n * Set reasoning level for this session\n */\n async setReasoningLevel(level: ReasoningLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { reasoningLevel: level });\n }\n }\n\n /**\n * Get current verbose level (session override or default)\n */\n async getVerboseLevel(): Promise<VerboseLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.verboseLevel) {\n return sessionConfig.verboseLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.verboseDefault;\n }\n\n /**\n * Set verbose level for this session\n */\n async setVerboseLevel(level: VerboseLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { verboseLevel: level });\n }\n }\n\n private renderComponentAsText(component: UIComponent): string {\n switch (component.type) {\n case 'buttons':\n return component.buttons.map(b => `[${b.text}]`).join(' ');\n \n case 'select':\n return component.options.map(o => `- ${o.label}`).join('\\n');\n \n case 'model-picker':\n return component.providers.map(p => \n `**${p.name}**\\n${p.models.map(m => ` - ${m.name}`).join('\\n')}`\n ).join('\\n\\n');\n \n case 'usage-display':\n return `📊 Usage Stats:\\n` +\n `📥 Prompt: ${component.stats.promptTokens.toLocaleString()} tokens\\n` +\n `📤 Completion: ${component.stats.completionTokens.toLocaleString()} tokens\\n` +\n `📊 Total: ${component.stats.totalTokens.toLocaleString()} tokens`;\n \n case 'session-list':\n return component.sessions.map(s => \n `${s.isActive ? '▶️' : ' '} ${s.key} (${s.messageCount} messages)`\n ).join('\\n');\n \n case 'text-input':\n return component.placeholder || 'Enter text...';\n \n default:\n return '[UI Component]';\n }\n }\n}\n\n/**\n * Create a command context from dependencies\n */\nexport function createCommandContext(deps: CommandContextDeps): CommandContext {\n return new CommandContextImpl(deps);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../../../src/chat-commands/context.ts"],"sourcesContent":["/**\n * Command Context Implementation\n * \n * Provides the concrete implementation of CommandContext interface,\n * bridging commands to core services (AgentService, SessionStore, etc.)\n */\n\nimport type {\n CommandContext,\n ReplyOptions,\n UIComponent,\n SessionInfo,\n ModelInfo,\n UsageStats,\n PlatformFeature,\n MessageSource,\n CompactSessionResult,\n} from './types.js';\nimport { getSessionDisplayName } from './session-key.js';\nimport type { Config } from '../config/schema.js';\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport type { SessionStore, SessionConfigStore } from '../session/index.js';\nimport { createLogger } from '../utils/logger.js';\nimport { getRoutingInfo } from './session-key.js';\nimport { saveConfig } from '../config/loader.js';\nimport type { ThinkLevel, ReasoningLevel, VerboseLevel } from '../agent/transcript/thinking-types.js';\nimport { mkdir, writeFile } from 'fs/promises';\nimport { join } from 'path';\nimport { effectiveWorkspacePathForSession } from '../session/session-workspace.js';\nimport { wrapMarkdownExportAsHtml } from '../session/chat-export.js';\nimport type { CompactionResult } from '../agent/memory/compaction.js';\nimport type { PersistentGoalApis } from '../agent/goals/persistent-goal-apis.js';\n\nconst log = createLogger('CommandContext');\n\nexport interface CommandContextDeps {\n sessionKey: string;\n source: MessageSource;\n channelId: string;\n chatId: string;\n senderId: string;\n isGroup: boolean;\n config: Config;\n bus: MessageBus;\n sessionStore: SessionStore;\n sessionConfigStore?: SessionConfigStore;\n /** After persisting session thinking, sync pi-agent in-memory state */\n applySessionThinkingLevel?: (sessionKey: string, level: ThinkLevel) => void;\n // Callbacks for platform-specific operations\n replyHandler: (text: string, options?: ReplyOptions) => Promise<void>;\n componentHandler?: (component: UIComponent) => Promise<void>;\n typingHandler?: (typing: boolean) => Promise<void>;\n supportedFeatures: PlatformFeature[];\n /** Called after session files are removed so in-memory agents match disk */\n invalidateAgentSession?: (sessionKey: string) => void;\n /** Reset session in place (archive + new session id); optional — falls back to clearSession */\n resetSession?: (sessionKey: string) => Promise<void>;\n // Model management (optional, will be injected)\n getCurrentModel?: () => string;\n switchModel?: (modelId: string) => Promise<boolean>;\n listModels?: () => Promise<ModelInfo[]>;\n getUsage?: () => Promise<UsageStats>;\n /** Stop current LLM turn and clear channel preview stream (Telegram draft, etc.) */\n abortCurrentTurn?: () => Promise<void>;\n\n compactSession?: (\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ) => Promise<CompactionResult>;\n\n btwQuery?: (sessionKey: string, question: string) => Promise<{ text: string; error?: string }>;\n\n getSessionContextReport?: (\n sessionKey: string,\n mode: 'list' | 'detail' | 'json',\n ) => Promise<string>;\n\n persistentGoalApis?: PersistentGoalApis;\n}\n\nexport class CommandContextImpl implements CommandContext {\n readonly sessionKey: string;\n readonly source: MessageSource;\n readonly channelId: string;\n readonly chatId: string;\n readonly senderId: string;\n readonly isGroup: boolean;\n readonly config: Config;\n readonly abortCurrentTurn?: () => Promise<void>;\n readonly persistentGoalApis?: PersistentGoalApis;\n\n private deps: CommandContextDeps;\n\n constructor(deps: CommandContextDeps) {\n this.sessionKey = deps.sessionKey;\n this.source = deps.source;\n this.channelId = deps.channelId;\n this.chatId = deps.chatId;\n this.senderId = deps.senderId;\n this.isGroup = deps.isGroup;\n this.config = deps.config;\n this.deps = deps;\n\n if (deps.abortCurrentTurn) {\n const run = deps.abortCurrentTurn;\n this.abortCurrentTurn = async () => {\n await run();\n };\n }\n\n this.persistentGoalApis = deps.persistentGoalApis;\n }\n\n // === Reply API ===\n\n async reply(text: string, options?: ReplyOptions): Promise<void> {\n await this.deps.replyHandler(text, options);\n }\n\n async replyComponent(component: UIComponent): Promise<void> {\n if (this.deps.componentHandler) {\n await this.deps.componentHandler(component);\n } else {\n // Fallback to text representation\n await this.reply(this.renderComponentAsText(component));\n }\n }\n\n async setTyping(typing: boolean): Promise<void> {\n if (this.deps.typingHandler) {\n await this.deps.typingHandler(typing);\n }\n }\n\n // === Session Management ===\n\n async getSession(): Promise<AgentMessage[]> {\n return this.deps.sessionStore.load(this.sessionKey);\n }\n\n async resetSession(): Promise<void> {\n if (this.deps.resetSession) {\n await this.deps.resetSession(this.sessionKey);\n } else if (typeof this.deps.sessionStore.reset === 'function') {\n const outcome = await this.deps.sessionStore.reset(this.sessionKey);\n if (!outcome) {\n throw new Error('Session not found');\n }\n this.deps.invalidateAgentSession?.(this.sessionKey);\n } else {\n await this.clearSession();\n return;\n }\n\n const routing = getRoutingInfo(this.sessionKey);\n await this.deps.bus.publishOutbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n content: '✅ New session started. Previous transcript archived; model and session overrides kept.',\n type: 'message',\n metadata: {\n threadId: routing.threadId,\n },\n });\n\n log.info({ sessionKey: this.sessionKey }, 'Session reset');\n }\n\n async clearSession(): Promise<void> {\n // Archive first if has messages\n const messages = await this.getSession();\n if (messages.length > 0) {\n await this.deps.sessionStore.archive(this.sessionKey);\n log.info({ sessionKey: this.sessionKey, messageCount: messages.length }, 'Session archived');\n }\n\n // Delete session\n await this.deps.sessionStore.deleteSession(this.sessionKey);\n this.deps.invalidateAgentSession?.(this.sessionKey);\n\n // Publish outbound message to confirm\n const routing = getRoutingInfo(this.sessionKey);\n await this.deps.bus.publishOutbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n content: '✅ Session cleared.',\n type: 'message',\n metadata: {\n threadId: routing.threadId,\n },\n });\n\n log.info({ sessionKey: this.sessionKey }, 'Session cleared');\n }\n\n async archiveSession(): Promise<void> {\n await this.deps.sessionStore.archive(this.sessionKey);\n log.info({ sessionKey: this.sessionKey }, 'Session archived');\n }\n\n async listSessions(): Promise<SessionInfo[]> {\n // TODO: Implement listSessions in SessionStore\n // For now, return current session only\n const messages = await this.getSession();\n return [{\n key: this.sessionKey,\n name: getSessionDisplayName(this.sessionKey),\n messageCount: messages.length,\n createdAt: new Date(),\n updatedAt: new Date(),\n isActive: true,\n }];\n }\n\n async switchSession(sessionKey: string): Promise<void> {\n // This is mainly for CLI/Web UI where you can switch between sessions\n // For Telegram, each chat has its own session\n log.info({ from: this.sessionKey, to: sessionKey }, 'Session switch requested');\n \n // Note: In the current architecture, switching session means\n // the next message will use a different sessionKey\n // The actual switch happens at the adapter level\n }\n\n // === Model Management ===\n\n getCurrentModel(): string {\n if (this.deps.getCurrentModel) {\n return this.deps.getCurrentModel();\n }\n \n // Fallback to config default\n return this.config.agents?.defaults?.model?.primary || 'minimax/minimax-m2.1';\n }\n\n async listModels(): Promise<ModelInfo[]> {\n if (this.deps.listModels) {\n return this.deps.listModels();\n }\n \n // Fallback to empty list\n return [];\n }\n\n async switchModel(modelId: string): Promise<boolean> {\n if (this.deps.switchModel) {\n return this.deps.switchModel(modelId);\n }\n \n // No model manager available\n await this.reply('❌ Model switching not available in this context.');\n return false;\n }\n\n async getUsage(): Promise<UsageStats> {\n if (this.deps.getUsage) {\n return this.deps.getUsage();\n }\n \n // Fallback: calculate from session\n const messages = await this.getSession();\n let promptTokens = 0;\n let completionTokens = 0;\n \n for (const msg of messages) {\n if ('usage' in msg && msg.usage) {\n promptTokens += msg.usage.input || 0;\n completionTokens += msg.usage.output || 0;\n }\n }\n \n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n messageCount: messages.length,\n };\n }\n\n // === Platform Features ===\n\n supports(feature: PlatformFeature): boolean {\n return this.deps.supportedFeatures.includes(feature);\n }\n\n // === Configuration ===\n\n getConfig(): Config {\n return this.config;\n }\n\n async updateConfig(path: string, value: unknown): Promise<boolean> {\n try {\n // Update config object using path\n const keys = path.split('.');\n let target: Record<string, unknown> = this.config as Record<string, unknown>;\n \n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (!(key in target) || typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {};\n }\n target = target[key] as Record<string, unknown>;\n }\n \n target[keys[keys.length - 1]] = value;\n \n // Save to disk\n await saveConfig(this.config);\n \n log.info({ path, value }, 'Config updated via command');\n return true;\n } catch (error) {\n log.error({ err: error, path, value }, 'Failed to update config');\n return false;\n }\n }\n\n // === Private Helpers ===\n\n // === Thinking Configuration ===\n\n /**\n * Get the session config store (if available)\n */\n getSessionConfigStore(): SessionConfigStore | undefined {\n return this.deps.sessionConfigStore;\n }\n\n /**\n * Get current thinking level (session override or default)\n */\n async getThinkingLevel(): Promise<ThinkLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.thinkingLevel) {\n return sessionConfig.thinkingLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.thinkingDefault;\n }\n\n /**\n * Set thinking level for this session\n */\n async setThinkingLevel(level: ThinkLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { thinkingLevel: level });\n }\n this.deps.applySessionThinkingLevel?.(this.sessionKey, level);\n }\n\n syncAgentThinkingLevel(level: ThinkLevel): void {\n this.deps.applySessionThinkingLevel?.(this.sessionKey, level);\n }\n\n async compactSession(options?: { instructions?: string; force?: boolean }): Promise<CompactSessionResult | null> {\n if (!this.deps.compactSession) {\n return null;\n }\n const r = await this.deps.compactSession(this.sessionKey, options);\n return {\n compacted: r.compacted,\n tokensBefore: r.tokensBefore,\n tokensAfter: r.tokensAfter,\n summary: r.summary,\n };\n }\n\n async btwQuery(question: string): Promise<{ text: string; error?: string }> {\n if (!this.deps.btwQuery) {\n return { text: '', error: 'Side questions are not available in this environment.' };\n }\n return this.deps.btwQuery(this.sessionKey, question);\n }\n\n async exportSessionToWorkspace(format: 'markdown' | 'html' | 'json'): Promise<{ path: string }> {\n const exportFmt = format === 'json' ? 'json' : 'markdown';\n let body = await this.deps.sessionStore.exportSession(this.sessionKey, exportFmt);\n if (format === 'html') {\n body = wrapMarkdownExportAsHtml(`Session ${this.sessionKey}`, body);\n }\n const sc = this.deps.sessionConfigStore\n ? await this.deps.sessionConfigStore.get(this.sessionKey)\n : null;\n const root = effectiveWorkspacePathForSession(this.config, this.sessionKey, sc);\n const dir = join(root, 'exports');\n await mkdir(dir, { recursive: true });\n const safe = this.sessionKey.replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 96);\n const ext = format === 'json' ? 'json' : format === 'html' ? 'html' : 'md';\n const name = `session-${safe}-${Date.now()}.${ext}`;\n const outPath = join(dir, name);\n await writeFile(outPath, body, 'utf-8');\n return { path: outPath };\n }\n\n async agentContextReport(mode: 'list' | 'detail' | 'json' = 'list'): Promise<string> {\n if (!this.deps.getSessionContextReport) {\n return 'Context report is not available in this environment.';\n }\n return this.deps.getSessionContextReport(this.sessionKey, mode);\n }\n\n /**\n * Get current reasoning level (session override or default)\n */\n async getReasoningLevel(): Promise<ReasoningLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.reasoningLevel) {\n return sessionConfig.reasoningLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.reasoningDefault;\n }\n\n /**\n * Set reasoning level for this session\n */\n async setReasoningLevel(level: ReasoningLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { reasoningLevel: level });\n }\n }\n\n /**\n * Get current verbose level (session override or default)\n */\n async getVerboseLevel(): Promise<VerboseLevel | undefined> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n const sessionConfig = await configStore.get(this.sessionKey);\n if (sessionConfig?.verboseLevel) {\n return sessionConfig.verboseLevel;\n }\n }\n // Fallback to agent default\n return this.config.agents?.defaults?.verboseDefault;\n }\n\n /**\n * Set verbose level for this session\n */\n async setVerboseLevel(level: VerboseLevel): Promise<void> {\n const configStore = this.deps.sessionConfigStore;\n if (configStore) {\n await configStore.update(this.sessionKey, { verboseLevel: level });\n }\n }\n\n private renderComponentAsText(component: UIComponent): string {\n switch (component.type) {\n case 'buttons':\n return component.buttons.map(b => `[${b.text}]`).join(' ');\n \n case 'select':\n return component.options.map(o => `- ${o.label}`).join('\\n');\n \n case 'model-picker':\n return component.providers.map(p => \n `**${p.name}**\\n${p.models.map(m => ` - ${m.name}`).join('\\n')}`\n ).join('\\n\\n');\n \n case 'usage-display':\n return `📊 Usage Stats:\\n` +\n `📥 Prompt: ${component.stats.promptTokens.toLocaleString()} tokens\\n` +\n `📤 Completion: ${component.stats.completionTokens.toLocaleString()} tokens\\n` +\n `📊 Total: ${component.stats.totalTokens.toLocaleString()} tokens`;\n \n case 'session-list':\n return component.sessions.map(s => \n `${s.isActive ? '▶️' : ' '} ${s.key} (${s.messageCount} messages)`\n ).join('\\n');\n \n case 'text-input':\n return component.placeholder || 'Enter text...';\n \n default:\n return '[UI Component]';\n }\n }\n}\n\n/**\n * Create a command context from dependencies\n */\nexport function createCommandContext(deps: CommandContextDeps): CommandContext {\n return new CommandContextImpl(deps);\n}\n"],"mappings":";;;;;;;;;aAuBkD;aAED;AASjD,MAAM,MAAM,aAAa,iBAAiB;AA+C1C,IAAa,qBAAb,MAA0D;CACxD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA,YAAY,MAA0B;AACpC,OAAK,aAAa,KAAK;AACvB,OAAK,SAAS,KAAK;AACnB,OAAK,YAAY,KAAK;AACtB,OAAK,SAAS,KAAK;AACnB,OAAK,WAAW,KAAK;AACrB,OAAK,UAAU,KAAK;AACpB,OAAK,SAAS,KAAK;AACnB,OAAK,OAAO;AAEZ,MAAI,KAAK,kBAAkB;GACzB,MAAM,MAAM,KAAK;AACjB,QAAK,mBAAmB,YAAY;AAClC,UAAM,KAAK;;;AAIf,OAAK,qBAAqB,KAAK;;CAKjC,MAAM,MAAM,MAAc,SAAuC;AAC/D,QAAM,KAAK,KAAK,aAAa,MAAM,QAAQ;;CAG7C,MAAM,eAAe,WAAuC;AAC1D,MAAI,KAAK,KAAK,iBACZ,OAAM,KAAK,KAAK,iBAAiB,UAAU;MAG3C,OAAM,KAAK,MAAM,KAAK,sBAAsB,UAAU,CAAC;;CAI3D,MAAM,UAAU,QAAgC;AAC9C,MAAI,KAAK,KAAK,cACZ,OAAM,KAAK,KAAK,cAAc,OAAO;;CAMzC,MAAM,aAAsC;AAC1C,SAAO,KAAK,KAAK,aAAa,KAAK,KAAK,WAAW;;CAGrD,MAAM,eAA8B;AAClC,MAAI,KAAK,KAAK,aACZ,OAAM,KAAK,KAAK,aAAa,KAAK,WAAW;WACpC,OAAO,KAAK,KAAK,aAAa,UAAU,YAAY;AAE7D,OAAI,CAAC,MADiB,KAAK,KAAK,aAAa,MAAM,KAAK,WAAW,CAEjE,OAAM,IAAI,MAAM,oBAAoB;AAEtC,QAAK,KAAK,yBAAyB,KAAK,WAAW;SAC9C;AACL,SAAM,KAAK,cAAc;AACzB;;EAGF,MAAM,UAAU,eAAe,KAAK,WAAW;AAC/C,QAAM,KAAK,KAAK,IAAI,gBAAgB;GAClC,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,SAAS;GACT,MAAM;GACN,UAAU,EACR,UAAU,QAAQ,UACnB;GACF,CAAC;AAEF,MAAI,KAAK,EAAE,YAAY,KAAK,YAAY,EAAE,gBAAgB;;CAG5D,MAAM,eAA8B;EAElC,MAAM,WAAW,MAAM,KAAK,YAAY;AACxC,MAAI,SAAS,SAAS,GAAG;AACvB,SAAM,KAAK,KAAK,aAAa,QAAQ,KAAK,WAAW;AACrD,OAAI,KAAK;IAAE,YAAY,KAAK;IAAY,cAAc,SAAS;IAAQ,EAAE,mBAAmB;;AAI9F,QAAM,KAAK,KAAK,aAAa,cAAc,KAAK,WAAW;AAC3D,OAAK,KAAK,yBAAyB,KAAK,WAAW;EAGnD,MAAM,UAAU,eAAe,KAAK,WAAW;AAC/C,QAAM,KAAK,KAAK,IAAI,gBAAgB;GAClC,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,SAAS;GACT,MAAM;GACN,UAAU,EACR,UAAU,QAAQ,UACnB;GACF,CAAC;AAEF,MAAI,KAAK,EAAE,YAAY,KAAK,YAAY,EAAE,kBAAkB;;CAG9D,MAAM,iBAAgC;AACpC,QAAM,KAAK,KAAK,aAAa,QAAQ,KAAK,WAAW;AACrD,MAAI,KAAK,EAAE,YAAY,KAAK,YAAY,EAAE,mBAAmB;;CAG/D,MAAM,eAAuC;EAG3C,MAAM,WAAW,MAAM,KAAK,YAAY;AACxC,SAAO,CAAC;GACN,KAAK,KAAK;GACV,MAAM,sBAAsB,KAAK,WAAW;GAC5C,cAAc,SAAS;GACvB,2BAAW,IAAI,MAAM;GACrB,2BAAW,IAAI,MAAM;GACrB,UAAU;GACX,CAAC;;CAGJ,MAAM,cAAc,YAAmC;AAGrD,MAAI,KAAK;GAAE,MAAM,KAAK;GAAY,IAAI;GAAY,EAAE,2BAA2B;;CASjF,kBAA0B;AACxB,MAAI,KAAK,KAAK,gBACZ,QAAO,KAAK,KAAK,iBAAiB;AAIpC,SAAO,KAAK,OAAO,QAAQ,UAAU,OAAO,WAAW;;CAGzD,MAAM,aAAmC;AACvC,MAAI,KAAK,KAAK,WACZ,QAAO,KAAK,KAAK,YAAY;AAI/B,SAAO,EAAE;;CAGX,MAAM,YAAY,SAAmC;AACnD,MAAI,KAAK,KAAK,YACZ,QAAO,KAAK,KAAK,YAAY,QAAQ;AAIvC,QAAM,KAAK,MAAM,mDAAmD;AACpE,SAAO;;CAGT,MAAM,WAAgC;AACpC,MAAI,KAAK,KAAK,SACZ,QAAO,KAAK,KAAK,UAAU;EAI7B,MAAM,WAAW,MAAM,KAAK,YAAY;EACxC,IAAI,eAAe;EACnB,IAAI,mBAAmB;AAEvB,OAAK,MAAM,OAAO,SAChB,KAAI,WAAW,OAAO,IAAI,OAAO;AAC/B,mBAAgB,IAAI,MAAM,SAAS;AACnC,uBAAoB,IAAI,MAAM,UAAU;;AAI5C,SAAO;GACL;GACA;GACA,aAAa,eAAe;GAC5B,cAAc,SAAS;GACxB;;CAKH,SAAS,SAAmC;AAC1C,SAAO,KAAK,KAAK,kBAAkB,SAAS,QAAQ;;CAKtD,YAAoB;AAClB,SAAO,KAAK;;CAGd,MAAM,aAAa,MAAc,OAAkC;AACjE,MAAI;GAEF,MAAM,OAAO,KAAK,MAAM,IAAI;GAC5B,IAAI,SAAkC,KAAK;AAE3C,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;IACxC,MAAM,MAAM,KAAK;AACjB,QAAI,EAAE,OAAO,WAAW,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,KACzE,QAAO,OAAO,EAAE;AAElB,aAAS,OAAO;;AAGlB,UAAO,KAAK,KAAK,SAAS,MAAM;AAGhC,SAAM,WAAW,KAAK,OAAO;AAE7B,OAAI,KAAK;IAAE;IAAM;IAAO,EAAE,6BAA6B;AACvD,UAAO;WACA,OAAO;AACd,OAAI,MAAM;IAAE,KAAK;IAAO;IAAM;IAAO,EAAE,0BAA0B;AACjE,UAAO;;;;;;CAWX,wBAAwD;AACtD,SAAO,KAAK,KAAK;;;;;CAMnB,MAAM,mBAAoD;EACxD,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,aAAa;GACf,MAAM,gBAAgB,MAAM,YAAY,IAAI,KAAK,WAAW;AAC5D,OAAI,eAAe,cACjB,QAAO,cAAc;;AAIzB,SAAO,KAAK,OAAO,QAAQ,UAAU;;;;;CAMvC,MAAM,iBAAiB,OAAkC;EACvD,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,YACF,OAAM,YAAY,OAAO,KAAK,YAAY,EAAE,eAAe,OAAO,CAAC;AAErE,OAAK,KAAK,4BAA4B,KAAK,YAAY,MAAM;;CAG/D,uBAAuB,OAAyB;AAC9C,OAAK,KAAK,4BAA4B,KAAK,YAAY,MAAM;;CAG/D,MAAM,eAAe,SAA4F;AAC/G,MAAI,CAAC,KAAK,KAAK,eACb,QAAO;EAET,MAAM,IAAI,MAAM,KAAK,KAAK,eAAe,KAAK,YAAY,QAAQ;AAClE,SAAO;GACL,WAAW,EAAE;GACb,cAAc,EAAE;GAChB,aAAa,EAAE;GACf,SAAS,EAAE;GACZ;;CAGH,MAAM,SAAS,UAA6D;AAC1E,MAAI,CAAC,KAAK,KAAK,SACb,QAAO;GAAE,MAAM;GAAI,OAAO;GAAyD;AAErF,SAAO,KAAK,KAAK,SAAS,KAAK,YAAY,SAAS;;CAGtD,MAAM,yBAAyB,QAAiE;EAC9F,MAAM,YAAY,WAAW,SAAS,SAAS;EAC/C,IAAI,OAAO,MAAM,KAAK,KAAK,aAAa,cAAc,KAAK,YAAY,UAAU;AACjF,MAAI,WAAW,OACb,QAAO,yBAAyB,WAAW,KAAK,cAAc,KAAK;EAErE,MAAM,KAAK,KAAK,KAAK,qBACjB,MAAM,KAAK,KAAK,mBAAmB,IAAI,KAAK,WAAW,GACvD;EAEJ,MAAM,MAAM,KADC,iCAAiC,KAAK,QAAQ,KAAK,YAAY,GACvD,EAAE,UAAU;AACjC,QAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;EACrC,MAAM,OAAO,KAAK,WAAW,QAAQ,qBAAqB,IAAI,CAAC,MAAM,GAAG,GAAG;EAC3E,MAAM,MAAM,WAAW,SAAS,SAAS,WAAW,SAAS,SAAS;EAEtE,MAAM,UAAU,KAAK,KAAK,WADF,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG,MACf;AAC/B,QAAM,UAAU,SAAS,MAAM,QAAQ;AACvC,SAAO,EAAE,MAAM,SAAS;;CAG1B,MAAM,mBAAmB,OAAmC,QAAyB;AACnF,MAAI,CAAC,KAAK,KAAK,wBACb,QAAO;AAET,SAAO,KAAK,KAAK,wBAAwB,KAAK,YAAY,KAAK;;;;;CAMjE,MAAM,oBAAyD;EAC7D,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,aAAa;GACf,MAAM,gBAAgB,MAAM,YAAY,IAAI,KAAK,WAAW;AAC5D,OAAI,eAAe,eACjB,QAAO,cAAc;;AAIzB,SAAO,KAAK,OAAO,QAAQ,UAAU;;;;;CAMvC,MAAM,kBAAkB,OAAsC;EAC5D,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,YACF,OAAM,YAAY,OAAO,KAAK,YAAY,EAAE,gBAAgB,OAAO,CAAC;;;;;CAOxE,MAAM,kBAAqD;EACzD,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,aAAa;GACf,MAAM,gBAAgB,MAAM,YAAY,IAAI,KAAK,WAAW;AAC5D,OAAI,eAAe,aACjB,QAAO,cAAc;;AAIzB,SAAO,KAAK,OAAO,QAAQ,UAAU;;;;;CAMvC,MAAM,gBAAgB,OAAoC;EACxD,MAAM,cAAc,KAAK,KAAK;AAC9B,MAAI,YACF,OAAM,YAAY,OAAO,KAAK,YAAY,EAAE,cAAc,OAAO,CAAC;;CAItE,sBAA8B,WAAgC;AAC5D,UAAQ,UAAU,MAAlB;GACE,KAAK,UACH,QAAO,UAAU,QAAQ,KAAI,MAAK,IAAI,EAAE,KAAK,GAAG,CAAC,KAAK,IAAI;GAE5D,KAAK,SACH,QAAO,UAAU,QAAQ,KAAI,MAAK,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK;GAE9D,KAAK,eACH,QAAO,UAAU,UAAU,KAAI,MAC7B,KAAK,EAAE,KAAK,MAAM,EAAE,OAAO,KAAI,MAAK,OAAO,EAAE,OAAO,CAAC,KAAK,KAAK,GAChE,CAAC,KAAK,OAAO;GAEhB,KAAK,gBACH,QAAO,+BACS,UAAU,MAAM,aAAa,gBAAgB,CAAC,0BAC1C,UAAU,MAAM,iBAAiB,gBAAgB,CAAC,qBACvD,UAAU,MAAM,YAAY,gBAAgB,CAAC;GAE9D,KAAK,eACH,QAAO,UAAU,SAAS,KAAI,MAC5B,GAAG,EAAE,WAAW,OAAO,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,aAAa,YACzD,CAAC,KAAK,KAAK;GAEd,KAAK,aACH,QAAO,UAAU,eAAe;GAElC,QACE,QAAO;;;;;;;AAQf,SAAgB,qBAAqB,MAA0C;AAC7E,QAAO,IAAI,mBAAmB,KAAK"}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session Key Generator
|
|
3
|
-
*
|
|
4
|
-
* Uses the new routing system session key format:
|
|
5
|
-
* {agentId}:{source}:{accountId}:{peerKind}:{peerId}[:thread:{threadId}][:scope:{scopeId}]
|
|
6
|
-
*
|
|
7
|
-
* Examples:
|
|
8
|
-
* - main:telegram:default:dm:123456
|
|
9
|
-
* - main:telegram:default:group:-100123456
|
|
10
|
-
* - main:gateway:default:direct:chat_abc123
|
|
11
|
-
* - main:cli:default:direct:cli
|
|
2
|
+
* Session Key Generator — OpenClaw `agent:{agentId}:{rest}` format.
|
|
12
3
|
*/
|
|
13
4
|
import type { MessageSource } from './types.js';
|
|
14
5
|
export interface SessionKeyContext {
|
|
@@ -18,28 +9,13 @@ export interface SessionKeyContext {
|
|
|
18
9
|
senderId: string;
|
|
19
10
|
isGroup: boolean;
|
|
20
11
|
threadId?: string;
|
|
21
|
-
/** Agent ID (defaults to 'main') */
|
|
22
12
|
agentId?: string;
|
|
23
|
-
/** Account ID (defaults to 'default') */
|
|
24
13
|
accountId?: string;
|
|
14
|
+
mainKey?: string;
|
|
15
|
+
dmScope?: 'main' | 'per-peer' | 'per-channel-peer' | 'per-account-channel-peer';
|
|
16
|
+
identityLinks?: Record<string, string[]>;
|
|
25
17
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Generate a unified session key using the new routing format
|
|
28
|
-
*
|
|
29
|
-
* Format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}[:thread:{threadId}]
|
|
30
|
-
*
|
|
31
|
-
* Examples:
|
|
32
|
-
* - telegram DM: main:telegram:default:dm:123456
|
|
33
|
-
* - telegram Group: main:telegram:default:group:-100123456
|
|
34
|
-
* - gateway: main:gateway:default:direct:chat_abc123
|
|
35
|
-
* - cli: main:cli:default:direct:cli
|
|
36
|
-
*/
|
|
37
18
|
export declare function generateSessionKey(ctx: SessionKeyContext): string;
|
|
38
|
-
/**
|
|
39
|
-
* Parse a session key into its components
|
|
40
|
-
*
|
|
41
|
-
* Returns a UI-oriented shape (`type`, `chatId`, etc.) derived from the routing key.
|
|
42
|
-
*/
|
|
43
19
|
export declare function parseSessionKey(sessionKey: string): {
|
|
44
20
|
source: MessageSource;
|
|
45
21
|
type: 'dm' | 'group' | 'thread' | 'direct' | 'other';
|
|
@@ -48,17 +24,8 @@ export declare function parseSessionKey(sessionKey: string): {
|
|
|
48
24
|
agentId?: string;
|
|
49
25
|
accountId?: string;
|
|
50
26
|
};
|
|
51
|
-
/**
|
|
52
|
-
* Check if a session key is valid
|
|
53
|
-
*/
|
|
54
27
|
export declare function isValidSessionKey(sessionKey: string): boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Get display name for a session key
|
|
57
|
-
*/
|
|
58
28
|
export declare function getSessionDisplayName(sessionKey: string): string;
|
|
59
|
-
/**
|
|
60
|
-
* Extract channel info from session key for reply routing
|
|
61
|
-
*/
|
|
62
29
|
export declare function getRoutingInfo(sessionKey: string): {
|
|
63
30
|
channel: string;
|
|
64
31
|
chatId: string;
|