@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":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { AgentService } from '../../agent/index.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { messagesToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { getAllProviders, getModelsByProvider } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\n\nconst log = createLogger('TUI:Embedded');\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: { extensionRegistry?: ExtensionRegistryImpl }) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = loadConfig();\n const workspace = getWorkspacePath(config);\n const modelConfig = config.agents?.defaults?.model;\n const modelId = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n\n this.agent = new AgentService(this.bus, {\n workspace: workspace ?? process.cwd(),\n model: modelId,\n config,\n extensionRegistry: this.opts?.extensionRegistry,\n });\n\n this.agent.start().catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n\n // Process outbound messages in background\n this.processOutbound();\n\n // Signal ready\n queueMicrotask(() => this.onConnected?.());\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n if (!this.agent) throw new Error('Agent not started');\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = this.agent!.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n undefined,\n opts.thinking,\n { signal },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n this.onEvent?.({ event: event.type, data: event });\n }\n\n if (!signal.aborted) {\n this.onEvent?.({\n event: 'result',\n data: { ok: true },\n });\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const detail = await this.agent.sessionStore.get(opts.sessionKey);\n if (!detail) {\n return { messages: [] };\n }\n return {\n messages: messagesToClientHistory(detail.messages, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = loadConfig();\n const modelConfig = config.agents?.defaults?.model;\n const model = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = loadConfig();\n const modelConfig = config.agents?.defaults?.model;\n const model = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const choices: TuiModelChoice[] = [];\n for (const provider of getAllProviders()) {\n for (const model of getModelsByProvider(provider)) {\n choices.push({\n id: model.id,\n name: model.name ?? model.id,\n provider,\n });\n }\n }\n return choices;\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.clearSessionMessages(sessionKey);\n }\n\n async patchSession(\n _sessionKey: string,\n _patch: Record<string, unknown>,\n ): Promise<void> {\n // Not supported in embedded mode\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean },\n ): Promise<{ compacted: boolean; summary?: string }> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, { force: options?.force ?? true });\n const summary = result.compacted\n ? `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`\n : 'Nothing to compact';\n return { compacted: result.compacted, summary };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import type { ExtensionRegistryImpl } from '../../extensions/loader.js';\nimport { AgentService } from '../../agent/index.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { messagesToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { getAllProviders, getModelsByProvider } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\nimport { sessionMetadataToTuiItem } from '../tui-session-format.js';\n\nconst log = createLogger('TUI:Embedded');\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor(private readonly opts?: { extensionRegistry?: ExtensionRegistryImpl }) {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = loadConfig();\n const workspace = getWorkspacePath(config);\n const modelId = config.agents?.defaults?.model?.primary;\n\n this.agent = new AgentService(this.bus, {\n workspace: workspace ?? process.cwd(),\n model: modelId,\n config,\n extensionRegistry: this.opts?.extensionRegistry,\n });\n\n this.agent.start().catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n\n // Process outbound messages in background\n this.processOutbound();\n\n // Signal ready\n queueMicrotask(() => this.onConnected?.());\n }\n\n stop(): void {\n this.running = false;\n this.chatAbort?.abort();\n this.chatAbort = null;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n if (!this.agent) throw new Error('Agent not started');\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = this.agent!.turnDispatcher.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n undefined,\n opts.thinking,\n { signal },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n this.onEvent?.({ event: event.type, data: event });\n }\n\n if (!signal.aborted) {\n this.onEvent?.({\n event: 'result',\n data: { ok: true },\n });\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n const ok = await this.agent.turnDispatcher.steerWebchatSession(opts.sessionKey, opts.message);\n return { ok };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const detail = await this.agent.sessionStore.get(opts.sessionKey);\n if (!detail) {\n return { messages: [] };\n }\n return {\n messages: messagesToClientHistory(detail.messages, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n if (!this.agent) return [];\n try {\n const result = await this.agent.sessionStore.list({\n limit: 200,\n sortBy: 'updatedAt',\n sortOrder: 'desc',\n });\n return result.items.map(sessionMetadataToTuiItem);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded listSessions failed: ${errorMessage}`);\n return [];\n }\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n await this.agent.sessionStore.updateMetadata(sessionKey, { name: name.trim() });\n return { ok: true };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded renameSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n if (!this.agent) return { ok: false };\n try {\n const ok = await this.agent.sessionStore.deleteSession(sessionKey);\n return { ok };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, sessionKey, errorMessage }, `Embedded deleteSession failed: ${errorMessage}`);\n return { ok: false };\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n if (!this.agent) {\n const config = loadConfig();\n const model = config.agents?.defaults?.model?.primary;\n return { model: model ?? undefined };\n }\n try {\n const cfg = await this.agent.sessionInspector.agentConfig(sessionKey);\n const parsed = parseModelRef(cfg.model);\n const usage = await this.agent.sessionInspector.contextUsage(sessionKey);\n return {\n model: parsed?.model ?? cfg.model,\n modelProvider: parsed?.provider,\n thinkingLevel: cfg.thinkingLevel,\n totalTokens: usage.estimatedTokens,\n contextWindow: usage.contextWindow,\n contextUsagePercent: usage.usagePercent,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey, errorMessage }, `getSessionInfo failed: ${errorMessage}`);\n const config = loadConfig();\n const model = config.agents?.defaults?.model?.primary;\n return { model: model ?? undefined };\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const choices: TuiModelChoice[] = [];\n for (const provider of getAllProviders()) {\n for (const model of getModelsByProvider(provider)) {\n choices.push({\n id: model.id,\n name: model.name ?? model.id,\n provider,\n });\n }\n }\n return choices;\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n if (!this.agent) return;\n await this.agent.resetSession(sessionKey);\n }\n\n async patchSession(\n _sessionKey: string,\n _patch: Record<string, unknown>,\n ): Promise<void> {\n // Not supported in embedded mode\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean },\n ): Promise<{ compacted: boolean; summary?: string }> {\n if (!this.agent) return { compacted: false, summary: 'Agent not started' };\n try {\n const result = await this.agent.sessionInspector.compact(sessionKey, { force: options?.force ?? true });\n const summary = result.compacted\n ? `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`\n : 'Nothing to compact';\n return { compacted: result.compacted, summary };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
|
|
@@ -232,7 +232,7 @@ var GatewaySseBackend = class {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
async resetSession(sessionKey) {
|
|
235
|
-
await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}`, this.token, { method: "
|
|
235
|
+
await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}/reset`, this.token, { method: "POST" }).catch(() => {});
|
|
236
236
|
}
|
|
237
237
|
async compactSession(sessionKey, options) {
|
|
238
238
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n // Match EmbeddedBackend: set activeRunId before any token/tool events so TUI state stays on one\n // runId (avoids assistant under \"default\" and tools under the real uuid).\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: { content: body.error?.message ?? `Gateway error: ${res.status}` },\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n this.onEvent?.({\n event: 'token',\n data: { content: json.payload.content },\n });\n this.onEvent?.({ event: 'result', data: { ok: true } });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/steer', this.token, {\n method: 'POST',\n body: JSON.stringify({ chatId: opts.sessionKey, message: opts.message }),\n });\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok === true };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ''}`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as { ok?: boolean; payload?: { messages?: HistoryMessage[] } };\n return { messages: json.payload?.messages ?? [] };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n messageCount?: number;\n customData?: Record<string, unknown>;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n messageCount: typeof s.messageCount === 'number' ? s.messageCount : undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n }));\n } catch {\n return [];\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n const out: SessionInfo = {};\n try {\n const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;\n const [sessionRes, agentCfgRes] = await Promise.all([\n gatewayFetch(this.baseUrl, sessionPath, this.token),\n gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token),\n ]);\n\n type SessionRow = {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n let session: SessionRow | undefined;\n\n if (sessionRes.ok) {\n const json = (await sessionRes.json()) as { session?: SessionRow };\n session = json.session;\n if (session) {\n if (session.name) out.displayName = session.name;\n if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;\n }\n }\n\n if (agentCfgRes.ok) {\n const json = (await agentCfgRes.json()) as {\n ok?: boolean;\n payload?: { model?: string; thinkingLevel?: string };\n };\n const p = json.payload;\n if (p?.model && typeof p.model === 'string') {\n const parsed = parseModelRef(p.model);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = p.model;\n }\n }\n if (p?.thinkingLevel && typeof p.thinkingLevel === 'string') {\n out.thinkingLevel = p.thinkingLevel;\n }\n }\n\n if (!out.model && session?.customData) {\n const cd = session.customData;\n const ref =\n typeof cd.model === 'string'\n ? cd.model\n : typeof cd.modelRef === 'string'\n ? cd.modelRef\n : undefined;\n if (ref) {\n const parsed = parseModelRef(ref);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = ref;\n }\n }\n if (!out.modelProvider && typeof cd.modelProvider === 'string') {\n out.modelProvider = cd.modelProvider;\n }\n }\n\n if (out.totalTokens != null) {\n const models = await this.listModels();\n const match = models.find(\n (m) =>\n m.id === out.model &&\n (!out.modelProvider || m.provider === out.modelProvider),\n );\n const contextWindow = match?.contextWindow ?? 128_000;\n out.contextWindow = contextWindow;\n out.contextUsagePercent =\n contextWindow > 0\n ? Math.min(100, Math.round((out.totalTokens / contextWindow) * 100))\n : null;\n }\n\n return out;\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return json.payload?.models ?? [];\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n ).catch(() => {});\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean },\n ): Promise<{ compacted: boolean; summary?: string }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/compaction/run`,\n this.token,\n {\n method: 'POST',\n body: JSON.stringify({ force: options?.force ?? true }),\n },\n );\n if (!res.ok) {\n return { compacted: false, summary: `Compaction failed (${res.status})` };\n }\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { result?: { compacted?: boolean; tokensBefore?: number; tokensAfter?: number } };\n };\n const result = json.payload?.result;\n if (!result?.compacted) {\n return { compacted: false, summary: 'Nothing to compact' };\n }\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n ).catch(() => {});\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/rename`,\n this.token,\n { method: 'POST', body: JSON.stringify({ name }) },\n );\n return { ok: res.ok };\n } catch {\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n );\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { deleted?: boolean };\n return { ok: json.deleted !== false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { parseModelRef } from '../../agent/models/selection.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n // Match EmbeddedBackend: set activeRunId before any token/tool events so TUI state stays on one\n // runId (avoids assistant under \"default\" and tools under the real uuid).\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: { content: body.error?.message ?? `Gateway error: ${res.status}` },\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n this.onEvent?.({\n event: 'token',\n data: { content: json.payload.content },\n });\n this.onEvent?.({ event: 'result', data: { ok: true } });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n async steerChat(opts: { sessionKey: string; message: string }): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/steer', this.token, {\n method: 'POST',\n body: JSON.stringify({ chatId: opts.sessionKey, message: opts.message }),\n });\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok === true };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ''}`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as { ok?: boolean; payload?: { messages?: HistoryMessage[] } };\n return { messages: json.payload?.messages ?? [] };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n messageCount?: number;\n customData?: Record<string, unknown>;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n messageCount: typeof s.messageCount === 'number' ? s.messageCount : undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n }));\n } catch {\n return [];\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n const out: SessionInfo = {};\n try {\n const sessionPath = `/api/sessions/${encodeURIComponent(sessionKey)}`;\n const [sessionRes, agentCfgRes] = await Promise.all([\n gatewayFetch(this.baseUrl, sessionPath, this.token),\n gatewayFetch(this.baseUrl, `${sessionPath}/agent-config`, this.token),\n ]);\n\n type SessionRow = {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n let session: SessionRow | undefined;\n\n if (sessionRes.ok) {\n const json = (await sessionRes.json()) as { session?: SessionRow };\n session = json.session;\n if (session) {\n if (session.name) out.displayName = session.name;\n if (session.estimatedTokens != null) out.totalTokens = session.estimatedTokens;\n }\n }\n\n if (agentCfgRes.ok) {\n const json = (await agentCfgRes.json()) as {\n ok?: boolean;\n payload?: { model?: string; thinkingLevel?: string };\n };\n const p = json.payload;\n if (p?.model && typeof p.model === 'string') {\n const parsed = parseModelRef(p.model);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = p.model;\n }\n }\n if (p?.thinkingLevel && typeof p.thinkingLevel === 'string') {\n out.thinkingLevel = p.thinkingLevel;\n }\n }\n\n if (!out.model && session?.customData) {\n const cd = session.customData;\n const ref =\n typeof cd.model === 'string'\n ? cd.model\n : typeof cd.modelRef === 'string'\n ? cd.modelRef\n : undefined;\n if (ref) {\n const parsed = parseModelRef(ref);\n if (parsed) {\n out.model = parsed.model;\n out.modelProvider = parsed.provider;\n } else {\n out.model = ref;\n }\n }\n if (!out.modelProvider && typeof cd.modelProvider === 'string') {\n out.modelProvider = cd.modelProvider;\n }\n }\n\n if (out.totalTokens != null) {\n const models = await this.listModels();\n const match = models.find(\n (m) =>\n m.id === out.model &&\n (!out.modelProvider || m.provider === out.modelProvider),\n );\n const contextWindow = match?.contextWindow ?? 128_000;\n out.contextWindow = contextWindow;\n out.contextUsagePercent =\n contextWindow > 0\n ? Math.min(100, Math.round((out.totalTokens / contextWindow) * 100))\n : null;\n }\n\n return out;\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return json.payload?.models ?? [];\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/reset`,\n this.token,\n { method: 'POST' },\n ).catch(() => {});\n }\n\n async compactSession(\n sessionKey: string,\n options?: { force?: boolean },\n ): Promise<{ compacted: boolean; summary?: string }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/compaction/run`,\n this.token,\n {\n method: 'POST',\n body: JSON.stringify({ force: options?.force ?? true }),\n },\n );\n if (!res.ok) {\n return { compacted: false, summary: `Compaction failed (${res.status})` };\n }\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { result?: { compacted?: boolean; tokensBefore?: number; tokensAfter?: number } };\n };\n const result = json.payload?.result;\n if (!result?.compacted) {\n return { compacted: false, summary: 'Nothing to compact' };\n }\n return {\n compacted: true,\n summary: `Compacted (${result.tokensBefore ?? '?'} → ${result.tokensAfter ?? '?'} tokens)`,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n return { compacted: false, summary: errorMessage };\n }\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n ).catch(() => {});\n }\n\n async renameSession(sessionKey: string, name: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}/rename`,\n this.token,\n { method: 'POST', body: JSON.stringify({ name }) },\n );\n return { ok: res.ok };\n } catch {\n return { ok: false };\n }\n }\n\n async deleteSession(sessionKey: string): Promise<{ ok: boolean }> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n );\n if (!res.ok) return { ok: false };\n const json = (await res.json()) as { deleted?: boolean };\n return { ok: json.deleted !== false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
|
|
@@ -82,9 +82,9 @@ var ChatLog = class extends Container {
|
|
|
82
82
|
this.assistantAnchorByRunId.set(runId, existing);
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
const
|
|
86
|
-
this.append(
|
|
87
|
-
if (text.trim()) this.assistantAnchorByRunId.set(runId,
|
|
85
|
+
const finalMessage = new AssistantMessageComponent(text);
|
|
86
|
+
this.append(finalMessage);
|
|
87
|
+
if (text.trim()) this.assistantAnchorByRunId.set(runId, finalMessage);
|
|
88
88
|
}
|
|
89
89
|
dropAssistant(runId) {
|
|
90
90
|
const existing = this.streamingRuns.get(runId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-log.js","names":[],"sources":["../../../../src/tui/components/chat-log.ts"],"sourcesContent":["import type { Component } from '@earendil-works/pi-tui';\nimport { Container, Spacer, Text } from '@earendil-works/pi-tui';\n\nimport { BashExecutionComponent } from './bash-execution.js';\nimport { theme } from '../theme.js';\nimport { AssistantMessageComponent } from './assistant-message.js';\nimport { ToolExecutionComponent } from './tool-execution.js';\nimport { UserMessageComponent } from './user-message.js';\n\nconst MAX_COMPONENTS = 180;\n\nexport class ChatLog extends Container {\n private toolById = new Map<string, ToolExecutionComponent>();\n private bashBlocks: BashExecutionComponent[] = [];\n private streamingRuns = new Map<string, AssistantMessageComponent>();\n /** After finalizeAssistant, late tool_start can still arrive; keep the bubble to insert tools above. */\n private assistantAnchorByRunId = new Map<string, AssistantMessageComponent>();\n private toolsExpanded = false;\n\n private pruneOverflow(): void {\n while (this.children.length > MAX_COMPONENTS) {\n const oldest = this.children[0];\n if (!oldest) return;\n this.removeChild(oldest);\n this.dropReferences(oldest);\n }\n }\n\n private dropReferences(component: Component): void {\n for (const [id, tool] of this.toolById.entries()) {\n if (tool === component) this.toolById.delete(id);\n }\n for (const [runId, msg] of this.streamingRuns.entries()) {\n if (msg === component) this.streamingRuns.delete(runId);\n }\n for (const [runId, msg] of this.assistantAnchorByRunId.entries()) {\n if (msg === component) this.assistantAnchorByRunId.delete(runId);\n }\n }\n\n private append(component: Component): void {\n this.addChild(component);\n this.pruneOverflow();\n }\n\n clearAll(): void {\n this.clear();\n this.toolById.clear();\n this.bashBlocks = [];\n this.streamingRuns.clear();\n this.assistantAnchorByRunId.clear();\n }\n\n addSystem(text: string): void {\n const entry = new Container();\n entry.addChild(new Spacer(1));\n entry.addChild(new Text(theme.system(text), 1, 0));\n this.append(entry);\n }\n\n addUser(text: string): void {\n this.assistantAnchorByRunId.clear();\n this.append(new UserMessageComponent(text));\n }\n\n /** Stream local `!command` output in a bordered block. */\n addBashExecution(\n command: string,\n ui: import('@earendil-works/pi-tui').TUI,\n excludeFromContext: boolean,\n ): BashExecutionComponent {\n this.assistantAnchorByRunId.clear();\n const component = new BashExecutionComponent(command, ui, excludeFromContext);\n component.setExpanded(this.toolsExpanded);\n this.bashBlocks.push(component);\n this.append(component);\n return component;\n }\n\n startAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (existing) {\n existing.setText(text);\n return;\n }\n const component = new AssistantMessageComponent(text);\n this.streamingRuns.set(runId, component);\n this.append(component);\n }\n\n updateAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (!existing) {\n this.startAssistant(text, runId);\n return;\n }\n existing.setText(text);\n }\n\n finalizeAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (existing) {\n existing.setText(text);\n this.streamingRuns.delete(runId);\n this.assistantAnchorByRunId.set(runId, existing);\n return;\n }\n const
|
|
1
|
+
{"version":3,"file":"chat-log.js","names":[],"sources":["../../../../src/tui/components/chat-log.ts"],"sourcesContent":["import type { Component } from '@earendil-works/pi-tui';\nimport { Container, Spacer, Text } from '@earendil-works/pi-tui';\n\nimport { BashExecutionComponent } from './bash-execution.js';\nimport { theme } from '../theme.js';\nimport { AssistantMessageComponent } from './assistant-message.js';\nimport { ToolExecutionComponent } from './tool-execution.js';\nimport { UserMessageComponent } from './user-message.js';\n\nconst MAX_COMPONENTS = 180;\n\nexport class ChatLog extends Container {\n private toolById = new Map<string, ToolExecutionComponent>();\n private bashBlocks: BashExecutionComponent[] = [];\n private streamingRuns = new Map<string, AssistantMessageComponent>();\n /** After finalizeAssistant, late tool_start can still arrive; keep the bubble to insert tools above. */\n private assistantAnchorByRunId = new Map<string, AssistantMessageComponent>();\n private toolsExpanded = false;\n\n private pruneOverflow(): void {\n while (this.children.length > MAX_COMPONENTS) {\n const oldest = this.children[0];\n if (!oldest) return;\n this.removeChild(oldest);\n this.dropReferences(oldest);\n }\n }\n\n private dropReferences(component: Component): void {\n for (const [id, tool] of this.toolById.entries()) {\n if (tool === component) this.toolById.delete(id);\n }\n for (const [runId, msg] of this.streamingRuns.entries()) {\n if (msg === component) this.streamingRuns.delete(runId);\n }\n for (const [runId, msg] of this.assistantAnchorByRunId.entries()) {\n if (msg === component) this.assistantAnchorByRunId.delete(runId);\n }\n }\n\n private append(component: Component): void {\n this.addChild(component);\n this.pruneOverflow();\n }\n\n clearAll(): void {\n this.clear();\n this.toolById.clear();\n this.bashBlocks = [];\n this.streamingRuns.clear();\n this.assistantAnchorByRunId.clear();\n }\n\n addSystem(text: string): void {\n const entry = new Container();\n entry.addChild(new Spacer(1));\n entry.addChild(new Text(theme.system(text), 1, 0));\n this.append(entry);\n }\n\n addUser(text: string): void {\n this.assistantAnchorByRunId.clear();\n this.append(new UserMessageComponent(text));\n }\n\n /** Stream local `!command` output in a bordered block. */\n addBashExecution(\n command: string,\n ui: import('@earendil-works/pi-tui').TUI,\n excludeFromContext: boolean,\n ): BashExecutionComponent {\n this.assistantAnchorByRunId.clear();\n const component = new BashExecutionComponent(command, ui, excludeFromContext);\n component.setExpanded(this.toolsExpanded);\n this.bashBlocks.push(component);\n this.append(component);\n return component;\n }\n\n startAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (existing) {\n existing.setText(text);\n return;\n }\n const component = new AssistantMessageComponent(text);\n this.streamingRuns.set(runId, component);\n this.append(component);\n }\n\n updateAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (!existing) {\n this.startAssistant(text, runId);\n return;\n }\n existing.setText(text);\n }\n\n finalizeAssistant(text: string, runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (existing) {\n existing.setText(text);\n this.streamingRuns.delete(runId);\n this.assistantAnchorByRunId.set(runId, existing);\n return;\n }\n const finalMessage = new AssistantMessageComponent(text);\n this.append(finalMessage);\n if (text.trim()) {\n this.assistantAnchorByRunId.set(runId, finalMessage);\n }\n }\n\n dropAssistant(runId: string): void {\n const existing = this.streamingRuns.get(runId);\n if (!existing) return;\n this.removeChild(existing);\n this.streamingRuns.delete(runId);\n }\n\n startTool(toolCallId: string, toolName: string, args: unknown, runId: string): void {\n const existing = this.toolById.get(toolCallId);\n if (existing) {\n existing.setArgs(args);\n return;\n }\n const component = new ToolExecutionComponent(toolName, args);\n component.setExpanded(this.toolsExpanded);\n this.toolById.set(toolCallId, component);\n\n const assistant =\n this.streamingRuns.get(runId) ?? this.assistantAnchorByRunId.get(runId);\n if (assistant) {\n // Streamed assistant text is updated in place from the start of the turn; tools\n // arrive later from SSE but should appear above the conversational reply (like the web UI).\n this.removeChild(assistant);\n this.addChild(component);\n this.addChild(assistant);\n } else {\n this.addChild(component);\n }\n this.pruneOverflow();\n }\n\n updateToolResult(toolCallId: string, result: string, isError: boolean): void {\n const existing = this.toolById.get(toolCallId);\n if (!existing) return;\n existing.setResult(result, isError);\n }\n\n setToolsExpanded(expanded: boolean): void {\n this.toolsExpanded = expanded;\n for (const tool of this.toolById.values()) {\n tool.setExpanded(expanded);\n }\n for (const bash of this.bashBlocks) {\n bash.setExpanded(expanded);\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/src/tui/theme.d.ts
CHANGED
|
@@ -24,8 +24,6 @@ export declare const theme: {
|
|
|
24
24
|
italic: (text: string) => string;
|
|
25
25
|
};
|
|
26
26
|
export declare const palette: import("./theme-manager.js").ThemePalette;
|
|
27
|
-
/** @deprecated Use `getLightMode()` — kept for existing imports. */
|
|
28
|
-
export declare const lightMode: boolean;
|
|
29
27
|
export declare const markdownTheme: import("@earendil-works/pi-tui").MarkdownTheme;
|
|
30
28
|
export declare const selectListTheme: import("@earendil-works/pi-tui").SelectListTheme;
|
|
31
29
|
export declare const searchableSelectListTheme: import("./components/searchable-select-list.js").SearchableSelectListTheme;
|
package/dist/src/tui/theme.js
CHANGED
|
@@ -11,8 +11,6 @@ const theme = new Proxy({}, { get(_target, prop) {
|
|
|
11
11
|
const palette = new Proxy({}, { get(_target, prop) {
|
|
12
12
|
return getThemeExports().palette[prop];
|
|
13
13
|
} });
|
|
14
|
-
/** @deprecated Use `getLightMode()` — kept for existing imports. */
|
|
15
|
-
const lightMode = getLightMode();
|
|
16
14
|
const markdownTheme = new Proxy({}, { get(_target, prop) {
|
|
17
15
|
const exports = getThemeExports().markdownTheme;
|
|
18
16
|
const value = exports[prop];
|
|
@@ -34,6 +32,6 @@ const editorTheme = new Proxy({}, { get(_target, prop) {
|
|
|
34
32
|
return typeof value === "function" ? value.bind(exports) : value;
|
|
35
33
|
} });
|
|
36
34
|
//#endregion
|
|
37
|
-
export { applyThemeById, editorTheme, getActiveThemeId, getBashExcludeBorderColor, getBashModeBorderColor, getCustomThemesDir, getDefaultEditorBorderColor, getLightMode, getThemeExports, getThinkingBorderColor, initTuiTheme,
|
|
35
|
+
export { applyThemeById, editorTheme, getActiveThemeId, getBashExcludeBorderColor, getBashModeBorderColor, getCustomThemesDir, getDefaultEditorBorderColor, getLightMode, getThemeExports, getThinkingBorderColor, initTuiTheme, listAvailableThemeIds, markdownTheme, palette, resolveThemePalette, searchableSelectListTheme, selectListTheme, theme };
|
|
38
36
|
|
|
39
37
|
//# sourceMappingURL=theme.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/tui/theme.ts"],"sourcesContent":["export {\n applyThemeById,\n getActiveThemeId,\n getBashExcludeBorderColor,\n getBashModeBorderColor,\n getCustomThemesDir,\n getDefaultEditorBorderColor,\n getThemeExports,\n getThinkingBorderColor,\n initTuiTheme,\n listAvailableThemeIds,\n resolveThemePalette,\n type ThemeExports,\n type ThemePalette,\n} from './theme-manager.js';\n\nimport { getThemeExports } from './theme-manager.js';\n\nexport function getLightMode(): boolean {\n return getThemeExports().lightMode;\n}\n\n/** Mutable theme accessors — updated when `applyThemeById` runs. */\nexport const theme = new Proxy({} as ReturnType<typeof getThemeExports>['theme'], {\n get(_target, prop) {\n const value = getThemeExports().theme[prop as keyof ReturnType<typeof getThemeExports>['theme']];\n return typeof value === 'function' ? value.bind(getThemeExports().theme) : value;\n },\n});\n\nexport const palette = new Proxy({} as ReturnType<typeof getThemeExports>['palette'], {\n get(_target, prop) {\n return getThemeExports().palette[prop as keyof ReturnType<typeof getThemeExports>['palette']];\n },\n});\n\
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../../../src/tui/theme.ts"],"sourcesContent":["export {\n applyThemeById,\n getActiveThemeId,\n getBashExcludeBorderColor,\n getBashModeBorderColor,\n getCustomThemesDir,\n getDefaultEditorBorderColor,\n getThemeExports,\n getThinkingBorderColor,\n initTuiTheme,\n listAvailableThemeIds,\n resolveThemePalette,\n type ThemeExports,\n type ThemePalette,\n} from './theme-manager.js';\n\nimport { getThemeExports } from './theme-manager.js';\n\nexport function getLightMode(): boolean {\n return getThemeExports().lightMode;\n}\n\n/** Mutable theme accessors — updated when `applyThemeById` runs. */\nexport const theme = new Proxy({} as ReturnType<typeof getThemeExports>['theme'], {\n get(_target, prop) {\n const value = getThemeExports().theme[prop as keyof ReturnType<typeof getThemeExports>['theme']];\n return typeof value === 'function' ? value.bind(getThemeExports().theme) : value;\n },\n});\n\nexport const palette = new Proxy({} as ReturnType<typeof getThemeExports>['palette'], {\n get(_target, prop) {\n return getThemeExports().palette[prop as keyof ReturnType<typeof getThemeExports>['palette']];\n },\n});\n\nexport const markdownTheme = new Proxy({} as ReturnType<typeof getThemeExports>['markdownTheme'], {\n get(_target, prop) {\n const exports = getThemeExports().markdownTheme;\n const value = exports[prop as keyof typeof exports];\n return typeof value === 'function' ? value.bind(exports) : value;\n },\n});\n\nexport const selectListTheme = new Proxy({} as ReturnType<typeof getThemeExports>['selectListTheme'], {\n get(_target, prop) {\n const exports = getThemeExports().selectListTheme;\n const value = exports[prop as keyof typeof exports];\n return typeof value === 'function' ? value.bind(exports) : value;\n },\n});\n\nexport const searchableSelectListTheme = new Proxy(\n {} as ReturnType<typeof getThemeExports>['searchableSelectListTheme'],\n {\n get(_target, prop) {\n const exports = getThemeExports().searchableSelectListTheme;\n const value = exports[prop as keyof typeof exports];\n return typeof value === 'function' ? value.bind(exports) : value;\n },\n },\n);\n\nexport const editorTheme = new Proxy({} as ReturnType<typeof getThemeExports>['editorTheme'], {\n get(_target, prop) {\n const exports = getThemeExports().editorTheme;\n const value = exports[prop as keyof typeof exports];\n return typeof value === 'function' ? value.bind(exports) : value;\n },\n});\n"],"mappings":";;AAkBA,SAAgB,eAAwB;AACtC,QAAO,iBAAiB,CAAC;;;AAI3B,MAAa,QAAQ,IAAI,MAAM,EAAE,EAAiD,EAChF,IAAI,SAAS,MAAM;CACjB,MAAM,QAAQ,iBAAiB,CAAC,MAAM;AACtC,QAAO,OAAO,UAAU,aAAa,MAAM,KAAK,iBAAiB,CAAC,MAAM,GAAG;GAE9E,CAAC;AAEF,MAAa,UAAU,IAAI,MAAM,EAAE,EAAmD,EACpF,IAAI,SAAS,MAAM;AACjB,QAAO,iBAAiB,CAAC,QAAQ;GAEpC,CAAC;AAEF,MAAa,gBAAgB,IAAI,MAAM,EAAE,EAAyD,EAChG,IAAI,SAAS,MAAM;CACjB,MAAM,UAAU,iBAAiB,CAAC;CAClC,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,GAAG;GAE9D,CAAC;AAEF,MAAa,kBAAkB,IAAI,MAAM,EAAE,EAA2D,EACpG,IAAI,SAAS,MAAM;CACjB,MAAM,UAAU,iBAAiB,CAAC;CAClC,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,GAAG;GAE9D,CAAC;AAEF,MAAa,4BAA4B,IAAI,MAC3C,EAAE,EACF,EACE,IAAI,SAAS,MAAM;CACjB,MAAM,UAAU,iBAAiB,CAAC;CAClC,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,GAAG;GAE9D,CACF;AAED,MAAa,cAAc,IAAI,MAAM,EAAE,EAAuD,EAC5F,IAAI,SAAS,MAAM;CACjB,MAAM,UAAU,iBAAiB,CAAC;CAClC,MAAM,QAAQ,QAAQ;AACtB,QAAO,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,GAAG;GAE9D,CAAC"}
|
|
@@ -30,6 +30,9 @@ export type CommandHandlerDeps = {
|
|
|
30
30
|
};
|
|
31
31
|
runCompaction?: () => void | Promise<void>;
|
|
32
32
|
extensionSlashCommands?: TuiExtensionSlashCommandEntry[];
|
|
33
|
+
currentAgentId?: string;
|
|
34
|
+
setSession?: (rawKey: string) => Promise<void>;
|
|
35
|
+
resetSession?: () => Promise<void>;
|
|
33
36
|
};
|
|
34
37
|
export declare function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void;
|
|
35
38
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getTuiKeybindingsPath } from "./tui-keybindings-file.js";
|
|
2
2
|
import { formatXopcTuiHotkeys } from "./format-tui-hotkeys.js";
|
|
3
3
|
import { rewriteUnknownSlashAsWorkflow } from "./tui-workflow-slash.js";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
4
5
|
//#region src/tui/tui-commands.ts
|
|
5
6
|
function getSlashCommands(_isLocal) {
|
|
6
7
|
return [
|
|
@@ -38,11 +39,15 @@ function getSlashCommands(_isLocal) {
|
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
name: "new",
|
|
41
|
-
description: "Start a new session"
|
|
42
|
+
description: "Start a new isolated TUI session (tui-{uuid})"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "reset",
|
|
46
|
+
description: "Reset current session transcript and reload history"
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
name: "clear",
|
|
45
|
-
description: "
|
|
50
|
+
description: "Alias for /reset"
|
|
46
51
|
},
|
|
47
52
|
{
|
|
48
53
|
name: "list",
|
|
@@ -146,7 +151,20 @@ function formatTuiHelpText(isLocal) {
|
|
|
146
151
|
return lines.join("\n");
|
|
147
152
|
}
|
|
148
153
|
function createTuiCommandHandler(deps) {
|
|
149
|
-
const { state, chatLog, tui, assembler, isLocalMode, abortActive, sendMessage, requestExit, updateFooter, keybindings, uiOverlays, extensionSlashCommands = [] } = deps;
|
|
154
|
+
const { state, chatLog, tui, assembler, isLocalMode, abortActive, sendMessage, requestExit, updateFooter, keybindings, uiOverlays, extensionSlashCommands = [], setSession, resetSession } = deps;
|
|
155
|
+
const runReset = async () => {
|
|
156
|
+
await abortActive();
|
|
157
|
+
if (resetSession) {
|
|
158
|
+
await resetSession();
|
|
159
|
+
chatLog.addSystem(`session ${state.currentSessionKey} reset`);
|
|
160
|
+
} else {
|
|
161
|
+
assembler.clear();
|
|
162
|
+
chatLog.clearAll();
|
|
163
|
+
state.messageFollowUpQueue.length = 0;
|
|
164
|
+
chatLog.addSystem("Session cleared (reset not available in this mode).");
|
|
165
|
+
}
|
|
166
|
+
tui.requestRender();
|
|
167
|
+
};
|
|
150
168
|
return (input) => {
|
|
151
169
|
const [commandName, ...restParts] = input.replace(/^\//, "").trim().split(/\s+/);
|
|
152
170
|
const normalizedCommand = (commandName ?? "").toLowerCase();
|
|
@@ -216,16 +234,33 @@ function createTuiCommandHandler(deps) {
|
|
|
216
234
|
}
|
|
217
235
|
switch (normalizedCommand) {
|
|
218
236
|
case "new":
|
|
237
|
+
(async () => {
|
|
238
|
+
try {
|
|
239
|
+
await abortActive();
|
|
240
|
+
const uniqueKey = `tui-${randomUUID()}`;
|
|
241
|
+
if (setSession) {
|
|
242
|
+
await setSession(uniqueKey);
|
|
243
|
+
chatLog.addSystem(`new session: ${state.currentSessionKey}`);
|
|
244
|
+
} else {
|
|
245
|
+
assembler.clear();
|
|
246
|
+
chatLog.clearAll();
|
|
247
|
+
state.messageFollowUpQueue.length = 0;
|
|
248
|
+
chatLog.addSystem("New session requires gateway or local session support.");
|
|
249
|
+
}
|
|
250
|
+
tui.requestRender();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
253
|
+
chatLog.addSystem(`new session failed: ${errorMessage}`);
|
|
254
|
+
tui.requestRender();
|
|
255
|
+
}
|
|
256
|
+
})();
|
|
257
|
+
return;
|
|
219
258
|
case "reset":
|
|
220
259
|
case "restart":
|
|
260
|
+
runReset();
|
|
261
|
+
return;
|
|
221
262
|
case "clear":
|
|
222
|
-
|
|
223
|
-
assembler.clear();
|
|
224
|
-
chatLog.clearAll();
|
|
225
|
-
state.messageFollowUpQueue.length = 0;
|
|
226
|
-
tui.requestRender();
|
|
227
|
-
sendMessage(input);
|
|
228
|
-
});
|
|
263
|
+
runReset();
|
|
229
264
|
return;
|
|
230
265
|
default: break;
|
|
231
266
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-commands.js","names":[],"sources":["../../../src/tui/tui-commands.ts"],"sourcesContent":["import type { KeybindingsManager, TUI } from '@earendil-works/pi-tui';\n\nimport type { ChatLog } from './components/chat-log.js';\nimport { formatXopcTuiHotkeys } from './format-tui-hotkeys.js';\nimport { getTuiKeybindingsPath } from './tui-keybindings-file.js';\nimport type { StreamAssembler } from './stream-assembler.js';\nimport type { TuiState } from './tui-types.js';\n\nimport { rewriteUnknownSlashAsWorkflow } from './tui-workflow-slash.js';\n\ninterface SlashCommandDef {\n name: string;\n description: string;\n}\n\nexport type TuiExtensionSlashCommandEntry = SlashCommandDef & {\n handler: (args: string) => void | Promise<void>;\n};\n\nexport function getSlashCommands(_isLocal: boolean): SlashCommandDef[] {\n return [\n { name: 'help', description: 'Show available commands' },\n { name: 'abort', description: 'Abort active run (or press Escape)' },\n { name: 'tools', description: 'Toggle tool output expanded/collapsed (or Ctrl+O)' },\n { name: 'thinking', description: 'Toggle thinking display (or Ctrl+T)' },\n { name: 'exit', description: 'Exit the TUI' },\n { name: 'models', description: 'List available models' },\n { name: 'switch', description: 'Switch model — copy `provider/model` from /models' },\n { name: 'usage', description: 'Show token usage statistics' },\n { name: 'new', description: 'Start a new session' },\n { name: 'clear', description: 'Clear current session' },\n { name: 'list', description: 'List sessions' },\n { name: 'resume', description: 'Open session picker (or Ctrl+Shift+P)' },\n { name: 'scoped-models', description: 'Choose models for Ctrl+P cycling' },\n { name: 'compact', description: 'Compact session history (local API)' },\n { name: 'think', description: 'Set thinking level (e.g. /think high)' },\n { name: 'reasoning', description: 'Set reasoning visibility (e.g. /reasoning stream)' },\n { name: 'verbose', description: 'Toggle verbose mode' },\n { name: 'status', description: 'Show agent status' },\n { name: 'config', description: 'Show or update configuration' },\n { name: 'context', description: 'Show context budget' },\n { name: 'btw', description: 'Side question without saving to session' },\n { name: 'export', description: 'Export session (markdown/html/json)' },\n { name: 'settings', description: 'Open TUI settings overlay' },\n { name: 'reload-keybindings', description: 'Reload ~/.xopc/keybindings.json' },\n { name: 'start', description: 'Show welcome message' },\n { name: 'hotkeys', description: 'Show resolved keyboard shortcuts (pi-style)' },\n { name: 'workflows', description: 'List saved workflows (built-in + ~/.xopc/workflows/)' },\n { name: 'workflow', description: 'Workflow subcommands: list, view <name>, save <name>' },\n ];\n}\n\nexport function formatTuiHelpText(isLocal: boolean): string {\n const commands = getSlashCommands(isLocal);\n const lines = ['Available commands:'];\n for (const c of commands) {\n lines.push(` /${c.name} — ${c.description}`);\n }\n lines.push('', 'Keyboard shortcuts (defaults align with pi coding-agent where noted):');\n lines.push(' Escape — Abort active run');\n lines.push(' Shift+Tab — Cycle /think level');\n lines.push(' Ctrl+P / Shift+Ctrl+P — Next / previous model (/switch)');\n lines.push(' Ctrl+L — Model picker');\n lines.push(' Ctrl+Shift+P — Session picker (rename/delete)');\n lines.push(' /scoped-models — Limit Ctrl+P model cycle set');\n lines.push(' Ctrl+O — Toggle tool output');\n lines.push(' Ctrl+T — Toggle thinking block display');\n lines.push(' Ctrl+G — Edit draft in $EDITOR');\n lines.push(' Ctrl+Z — Suspend to shell (Unix)');\n lines.push(' Alt+Enter — Queue follow-up while busy (sends when this reply finishes)');\n lines.push(' Alt+Up — Restore queued follow-up messages to editor');\n lines.push(' Enter (while busy) — Steer: inject at next tool boundary');\n lines.push(' Ctrl+V (mac/Linux) / Alt+V (Win) — Paste image from clipboard');\n lines.push(' /settings — Theme, thinking display, terminal progress, …');\n lines.push(' ~/.xopc/keybindings.json — Custom shortcuts (use /reload-keybindings)');\n lines.push(' Ctrl+C — Clear input; repeat within ~0.5s to exit when empty');\n lines.push(' Ctrl+D — Exit when input empty');\n lines.push(' !cmd — Local shell (gated; runs on this machine)');\n lines.push('', 'Use /hotkeys for the resolved binding list from the active keymap.');\n return lines.join('\\n');\n}\n\nexport type CommandHandlerDeps = {\n state: TuiState;\n chatLog: ChatLog;\n tui: TUI;\n assembler: StreamAssembler;\n isLocalMode: boolean;\n abortActive: () => Promise<void>;\n sendMessage: (text: string) => void;\n requestExit: () => void;\n updateFooter: () => void;\n keybindings: KeybindingsManager;\n uiOverlays?: {\n openSessionPicker: () => void;\n openScopedModels: () => void;\n openSettings: () => void;\n reloadKeybindings: () => void;\n };\n runCompaction?: () => void | Promise<void>;\n extensionSlashCommands?: TuiExtensionSlashCommandEntry[];\n};\n\nexport function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void {\n const {\n state,\n chatLog,\n tui,\n assembler,\n isLocalMode,\n abortActive,\n sendMessage,\n requestExit,\n updateFooter,\n keybindings,\n uiOverlays,\n extensionSlashCommands = [],\n } = deps;\n\n return (input: string) => {\n const trimmed = input.replace(/^\\//, '').trim();\n const [commandName, ...restParts] = trimmed.split(/\\s+/);\n const normalizedCommand = (commandName ?? '').toLowerCase();\n const commandArgs = restParts.join(' ');\n\n const extensionCmd = extensionSlashCommands.find((c) => c.name === normalizedCommand);\n if (extensionCmd) {\n void Promise.resolve(extensionCmd.handler(commandArgs)).then(() => {\n tui.requestRender();\n });\n return;\n }\n\n switch (normalizedCommand) {\n case 'help':\n chatLog.addSystem(formatTuiHelpText(isLocalMode));\n tui.requestRender();\n return;\n case 'hotkeys':\n case 'keys':\n chatLog.addSystem(formatXopcTuiHotkeys(keybindings));\n tui.requestRender();\n return;\n case 'exit':\n case 'quit':\n requestExit();\n return;\n case 'abort':\n case 'stop':\n case 'cancel':\n void abortActive().then(() => {\n chatLog.addSystem('Aborted.');\n tui.requestRender();\n });\n return;\n case 'tools':\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n chatLog.addSystem(`Tools: ${state.toolsExpanded ? 'expanded' : 'collapsed'}`);\n tui.requestRender();\n return;\n case 'thinking':\n state.showThinking = !state.showThinking;\n chatLog.addSystem(`Thinking display: ${state.showThinking ? 'on' : 'off'}`);\n updateFooter();\n tui.requestRender();\n return;\n case 'resume':\n case 'sessions':\n uiOverlays?.openSessionPicker();\n return;\n case 'scoped-models':\n case 'scopedmodels':\n uiOverlays?.openScopedModels();\n return;\n case 'settings':\n uiOverlays?.openSettings();\n return;\n case 'reload-keybindings':\n case 'reload-keybind':\n uiOverlays?.reloadKeybindings();\n chatLog.addSystem(`Keybindings reloaded from ${getTuiKeybindingsPath()}`);\n tui.requestRender();\n return;\n case 'compact':\n void deps.runCompaction?.();\n return;\n default:\n break;\n }\n\n switch (normalizedCommand) {\n case 'new':\n case 'reset':\n case 'restart':\n case 'clear': {\n void abortActive().then(() => {\n assembler.clear();\n chatLog.clearAll();\n state.messageFollowUpQueue.length = 0;\n tui.requestRender();\n sendMessage(input);\n });\n return;\n }\n default:\n break;\n }\n\n // Unknown slash that names a known workflow → rewrite into a natural prompt\n // so the assistant deterministically calls workflow({name}) instead of\n // depending on the model to puzzle out \"/audit_repo\".\n if (input.trimStart().startsWith('/')) {\n const rewritten = rewriteUnknownSlashAsWorkflow(normalizedCommand, commandArgs);\n if (rewritten) {\n chatLog.addSystem(`▶ Running workflow: ${normalizedCommand}`);\n tui.requestRender();\n sendMessage(rewritten);\n return;\n }\n }\n\n sendMessage(input);\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tui-commands.js","names":[],"sources":["../../../src/tui/tui-commands.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { KeybindingsManager, TUI } from '@earendil-works/pi-tui';\n\nimport type { ChatLog } from './components/chat-log.js';\nimport { formatXopcTuiHotkeys } from './format-tui-hotkeys.js';\nimport { getTuiKeybindingsPath } from './tui-keybindings-file.js';\nimport type { StreamAssembler } from './stream-assembler.js';\nimport type { TuiState } from './tui-types.js';\n\nimport { rewriteUnknownSlashAsWorkflow } from './tui-workflow-slash.js';\n\ninterface SlashCommandDef {\n name: string;\n description: string;\n}\n\nexport type TuiExtensionSlashCommandEntry = SlashCommandDef & {\n handler: (args: string) => void | Promise<void>;\n};\n\nexport function getSlashCommands(_isLocal: boolean): SlashCommandDef[] {\n return [\n { name: 'help', description: 'Show available commands' },\n { name: 'abort', description: 'Abort active run (or press Escape)' },\n { name: 'tools', description: 'Toggle tool output expanded/collapsed (or Ctrl+O)' },\n { name: 'thinking', description: 'Toggle thinking display (or Ctrl+T)' },\n { name: 'exit', description: 'Exit the TUI' },\n { name: 'models', description: 'List available models' },\n { name: 'switch', description: 'Switch model — copy `provider/model` from /models' },\n { name: 'usage', description: 'Show token usage statistics' },\n { name: 'new', description: 'Start a new isolated TUI session (tui-{uuid})' },\n { name: 'reset', description: 'Reset current session transcript and reload history' },\n { name: 'clear', description: 'Alias for /reset' },\n { name: 'list', description: 'List sessions' },\n { name: 'resume', description: 'Open session picker (or Ctrl+Shift+P)' },\n { name: 'scoped-models', description: 'Choose models for Ctrl+P cycling' },\n { name: 'compact', description: 'Compact session history (local API)' },\n { name: 'think', description: 'Set thinking level (e.g. /think high)' },\n { name: 'reasoning', description: 'Set reasoning visibility (e.g. /reasoning stream)' },\n { name: 'verbose', description: 'Toggle verbose mode' },\n { name: 'status', description: 'Show agent status' },\n { name: 'config', description: 'Show or update configuration' },\n { name: 'context', description: 'Show context budget' },\n { name: 'btw', description: 'Side question without saving to session' },\n { name: 'export', description: 'Export session (markdown/html/json)' },\n { name: 'settings', description: 'Open TUI settings overlay' },\n { name: 'reload-keybindings', description: 'Reload ~/.xopc/keybindings.json' },\n { name: 'start', description: 'Show welcome message' },\n { name: 'hotkeys', description: 'Show resolved keyboard shortcuts (pi-style)' },\n { name: 'workflows', description: 'List saved workflows (built-in + ~/.xopc/workflows/)' },\n { name: 'workflow', description: 'Workflow subcommands: list, view <name>, save <name>' },\n ];\n}\n\nexport function formatTuiHelpText(isLocal: boolean): string {\n const commands = getSlashCommands(isLocal);\n const lines = ['Available commands:'];\n for (const c of commands) {\n lines.push(` /${c.name} — ${c.description}`);\n }\n lines.push('', 'Keyboard shortcuts (defaults align with pi coding-agent where noted):');\n lines.push(' Escape — Abort active run');\n lines.push(' Shift+Tab — Cycle /think level');\n lines.push(' Ctrl+P / Shift+Ctrl+P — Next / previous model (/switch)');\n lines.push(' Ctrl+L — Model picker');\n lines.push(' Ctrl+Shift+P — Session picker (rename/delete)');\n lines.push(' /scoped-models — Limit Ctrl+P model cycle set');\n lines.push(' Ctrl+O — Toggle tool output');\n lines.push(' Ctrl+T — Toggle thinking block display');\n lines.push(' Ctrl+G — Edit draft in $EDITOR');\n lines.push(' Ctrl+Z — Suspend to shell (Unix)');\n lines.push(' Alt+Enter — Queue follow-up while busy (sends when this reply finishes)');\n lines.push(' Alt+Up — Restore queued follow-up messages to editor');\n lines.push(' Enter (while busy) — Steer: inject at next tool boundary');\n lines.push(' Ctrl+V (mac/Linux) / Alt+V (Win) — Paste image from clipboard');\n lines.push(' /settings — Theme, thinking display, terminal progress, …');\n lines.push(' ~/.xopc/keybindings.json — Custom shortcuts (use /reload-keybindings)');\n lines.push(' Ctrl+C — Clear input; repeat within ~0.5s to exit when empty');\n lines.push(' Ctrl+D — Exit when input empty');\n lines.push(' !cmd — Local shell (gated; runs on this machine)');\n lines.push('', 'Use /hotkeys for the resolved binding list from the active keymap.');\n return lines.join('\\n');\n}\n\nexport type CommandHandlerDeps = {\n state: TuiState;\n chatLog: ChatLog;\n tui: TUI;\n assembler: StreamAssembler;\n isLocalMode: boolean;\n abortActive: () => Promise<void>;\n sendMessage: (text: string) => void;\n requestExit: () => void;\n updateFooter: () => void;\n keybindings: KeybindingsManager;\n uiOverlays?: {\n openSessionPicker: () => void;\n openScopedModels: () => void;\n openSettings: () => void;\n reloadKeybindings: () => void;\n };\n runCompaction?: () => void | Promise<void>;\n extensionSlashCommands?: TuiExtensionSlashCommandEntry[];\n currentAgentId?: string;\n setSession?: (rawKey: string) => Promise<void>;\n resetSession?: () => Promise<void>;\n};\n\nexport function createTuiCommandHandler(deps: CommandHandlerDeps): (input: string) => void {\n const {\n state,\n chatLog,\n tui,\n assembler,\n isLocalMode,\n abortActive,\n sendMessage,\n requestExit,\n updateFooter,\n keybindings,\n uiOverlays,\n extensionSlashCommands = [],\n setSession,\n resetSession,\n } = deps;\n\n const runReset = async () => {\n await abortActive();\n if (resetSession) {\n await resetSession();\n chatLog.addSystem(`session ${state.currentSessionKey} reset`);\n } else {\n assembler.clear();\n chatLog.clearAll();\n state.messageFollowUpQueue.length = 0;\n chatLog.addSystem('Session cleared (reset not available in this mode).');\n }\n tui.requestRender();\n };\n\n return (input: string) => {\n const trimmed = input.replace(/^\\//, '').trim();\n const [commandName, ...restParts] = trimmed.split(/\\s+/);\n const normalizedCommand = (commandName ?? '').toLowerCase();\n const commandArgs = restParts.join(' ');\n\n const extensionCmd = extensionSlashCommands.find((c) => c.name === normalizedCommand);\n if (extensionCmd) {\n void Promise.resolve(extensionCmd.handler(commandArgs)).then(() => {\n tui.requestRender();\n });\n return;\n }\n\n switch (normalizedCommand) {\n case 'help':\n chatLog.addSystem(formatTuiHelpText(isLocalMode));\n tui.requestRender();\n return;\n case 'hotkeys':\n case 'keys':\n chatLog.addSystem(formatXopcTuiHotkeys(keybindings));\n tui.requestRender();\n return;\n case 'exit':\n case 'quit':\n requestExit();\n return;\n case 'abort':\n case 'stop':\n case 'cancel':\n void abortActive().then(() => {\n chatLog.addSystem('Aborted.');\n tui.requestRender();\n });\n return;\n case 'tools':\n state.toolsExpanded = !state.toolsExpanded;\n chatLog.setToolsExpanded(state.toolsExpanded);\n chatLog.addSystem(`Tools: ${state.toolsExpanded ? 'expanded' : 'collapsed'}`);\n tui.requestRender();\n return;\n case 'thinking':\n state.showThinking = !state.showThinking;\n chatLog.addSystem(`Thinking display: ${state.showThinking ? 'on' : 'off'}`);\n updateFooter();\n tui.requestRender();\n return;\n case 'resume':\n case 'sessions':\n uiOverlays?.openSessionPicker();\n return;\n case 'scoped-models':\n case 'scopedmodels':\n uiOverlays?.openScopedModels();\n return;\n case 'settings':\n uiOverlays?.openSettings();\n return;\n case 'reload-keybindings':\n case 'reload-keybind':\n uiOverlays?.reloadKeybindings();\n chatLog.addSystem(`Keybindings reloaded from ${getTuiKeybindingsPath()}`);\n tui.requestRender();\n return;\n case 'compact':\n void deps.runCompaction?.();\n return;\n default:\n break;\n }\n\n switch (normalizedCommand) {\n case 'new': {\n void (async () => {\n try {\n await abortActive();\n const uniqueKey = `tui-${randomUUID()}`;\n if (setSession) {\n await setSession(uniqueKey);\n chatLog.addSystem(`new session: ${state.currentSessionKey}`);\n } else {\n assembler.clear();\n chatLog.clearAll();\n state.messageFollowUpQueue.length = 0;\n chatLog.addSystem('New session requires gateway or local session support.');\n }\n tui.requestRender();\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n chatLog.addSystem(`new session failed: ${errorMessage}`);\n tui.requestRender();\n }\n })();\n return;\n }\n case 'reset':\n case 'restart':\n void runReset();\n return;\n case 'clear':\n void runReset();\n return;\n default:\n break;\n }\n\n // Unknown slash that names a known workflow → rewrite into a natural prompt\n // so the assistant deterministically calls workflow({name}) instead of\n // depending on the model to puzzle out \"/audit_repo\".\n if (input.trimStart().startsWith('/')) {\n const rewritten = rewriteUnknownSlashAsWorkflow(normalizedCommand, commandArgs);\n if (rewritten) {\n chatLog.addSystem(`▶ Running workflow: ${normalizedCommand}`);\n tui.requestRender();\n sendMessage(rewritten);\n return;\n }\n }\n\n sendMessage(input);\n };\n}\n"],"mappings":"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"}
|
|
@@ -6,15 +6,6 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
6
6
|
import { KeybindingsManager } from "@earendil-works/pi-tui";
|
|
7
7
|
//#region src/tui/tui-keybindings-file.ts
|
|
8
8
|
init_paths();
|
|
9
|
-
const LEGACY_KEYBINDING_MIGRATIONS = {
|
|
10
|
-
interrupt: "app.interrupt",
|
|
11
|
-
clear: "app.clear",
|
|
12
|
-
exit: "app.exit",
|
|
13
|
-
suspend: "app.suspend",
|
|
14
|
-
pasteImage: "app.clipboard.pasteImage",
|
|
15
|
-
followUp: "app.message.followUp",
|
|
16
|
-
dequeue: "app.message.dequeue"
|
|
17
|
-
};
|
|
18
9
|
function isRecord(value) {
|
|
19
10
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
11
|
}
|
|
@@ -30,15 +21,6 @@ function toKeybindingsConfig(value) {
|
|
|
30
21
|
}
|
|
31
22
|
return config;
|
|
32
23
|
}
|
|
33
|
-
function migrateKeybindingsConfig(rawConfig) {
|
|
34
|
-
const config = {};
|
|
35
|
-
for (const [key, value] of Object.entries(rawConfig)) {
|
|
36
|
-
const nextKey = LEGACY_KEYBINDING_MIGRATIONS[key] ?? key;
|
|
37
|
-
if (Object.hasOwn(rawConfig, nextKey) && nextKey !== key) continue;
|
|
38
|
-
config[nextKey] = value;
|
|
39
|
-
}
|
|
40
|
-
return config;
|
|
41
|
-
}
|
|
42
24
|
function loadRawConfig(path) {
|
|
43
25
|
if (!existsSync(path)) return void 0;
|
|
44
26
|
try {
|
|
@@ -52,9 +34,7 @@ function getTuiKeybindingsPath() {
|
|
|
52
34
|
return join(resolveStateDir(), "keybindings.json");
|
|
53
35
|
}
|
|
54
36
|
function loadTuiKeybindingsConfig(path = getTuiKeybindingsPath()) {
|
|
55
|
-
|
|
56
|
-
if (!raw) return {};
|
|
57
|
-
return toKeybindingsConfig(migrateKeybindingsConfig(raw));
|
|
37
|
+
return toKeybindingsConfig(loadRawConfig(path));
|
|
58
38
|
}
|
|
59
39
|
var XopcKeybindingsManager = class extends KeybindingsManager {
|
|
60
40
|
constructor(userBindings = {}, configPath = getTuiKeybindingsPath()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-keybindings-file.js","names":[],"sources":["../../../src/tui/tui-keybindings-file.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n KeybindingsManager,\n type KeybindingsConfig,\n type KeyId,\n} from '@earendil-works/pi-tui';\n\nimport { resolveStateDir } from '../config/paths.js';\nimport { XOPC_TUI_KEYBINDINGS } from './xopc-tui-keybindings.js';\n\
|
|
1
|
+
{"version":3,"file":"tui-keybindings-file.js","names":[],"sources":["../../../src/tui/tui-keybindings-file.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport {\n KeybindingsManager,\n type KeybindingsConfig,\n type KeyId,\n} from '@earendil-works/pi-tui';\n\nimport { resolveStateDir } from '../config/paths.js';\nimport { XOPC_TUI_KEYBINDINGS } from './xopc-tui-keybindings.js';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction toKeybindingsConfig(value: unknown): KeybindingsConfig {\n if (!isRecord(value)) return {};\n const config: KeybindingsConfig = {};\n for (const [key, binding] of Object.entries(value)) {\n if (typeof binding === 'string') {\n config[key] = binding as KeyId;\n continue;\n }\n if (Array.isArray(binding) && binding.every((entry) => typeof entry === 'string')) {\n config[key] = binding as KeyId[];\n }\n }\n return config;\n}\n\nfunction loadRawConfig(path: string): Record<string, unknown> | undefined {\n if (!existsSync(path)) return undefined;\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function getTuiKeybindingsPath(): string {\n return join(resolveStateDir(), 'keybindings.json');\n}\n\nexport function loadTuiKeybindingsConfig(path = getTuiKeybindingsPath()): KeybindingsConfig {\n return toKeybindingsConfig(loadRawConfig(path));\n}\n\nexport class XopcKeybindingsManager extends KeybindingsManager {\n constructor(\n userBindings: KeybindingsConfig = {},\n private readonly configPath = getTuiKeybindingsPath(),\n ) {\n super(XOPC_TUI_KEYBINDINGS, userBindings);\n }\n\n reload(): void {\n this.setUserBindings(loadTuiKeybindingsConfig(this.configPath));\n }\n\n getConfigPath(): string {\n return this.configPath;\n }\n}\n\nexport function createXopcTuiKeybindingsManager(): XopcKeybindingsManager {\n const configPath = getTuiKeybindingsPath();\n return new XopcKeybindingsManager(loadTuiKeybindingsConfig(configPath), configPath);\n}\n"],"mappings":";;;;;;;YASqD;AAGrD,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,oBAAoB,OAAmC;AAC9D,KAAI,CAAC,SAAS,MAAM,CAAE,QAAO,EAAE;CAC/B,MAAM,SAA4B,EAAE;AACpC,MAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,MAAM,EAAE;AAClD,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAO,OAAO;AACd;;AAEF,MAAI,MAAM,QAAQ,QAAQ,IAAI,QAAQ,OAAO,UAAU,OAAO,UAAU,SAAS,CAC/E,QAAO,OAAO;;AAGlB,QAAO;;AAGT,SAAS,cAAc,MAAmD;AACxE,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,KAAA;AAC9B,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AACrD,SAAO,SAAS,OAAO,GAAG,SAAS,KAAA;SAC7B;AACN;;;AAIJ,SAAgB,wBAAgC;AAC9C,QAAO,KAAK,iBAAiB,EAAE,mBAAmB;;AAGpD,SAAgB,yBAAyB,OAAO,uBAAuB,EAAqB;AAC1F,QAAO,oBAAoB,cAAc,KAAK,CAAC;;AAGjD,IAAa,yBAAb,cAA4C,mBAAmB;CAC7D,YACE,eAAkC,EAAE,EACpC,aAA8B,uBAAuB,EACrD;AACA,QAAM,sBAAsB,aAAa;AAFxB,OAAA,aAAA;;CAKnB,SAAe;AACb,OAAK,gBAAgB,yBAAyB,KAAK,WAAW,CAAC;;CAGjE,gBAAwB;AACtB,SAAO,KAAK;;;AAIhB,SAAgB,kCAA0D;CACxE,MAAM,aAAa,uBAAuB;AAC1C,QAAO,IAAI,uBAAuB,yBAAyB,WAAW,EAAE,WAAW"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TUI } from '@earendil-works/pi-tui';
|
|
2
|
+
import type { ChatLog } from './components/chat-log.js';
|
|
3
|
+
import type { TuiBackend } from './tui-backend.js';
|
|
4
|
+
import type { StreamAssembler } from './stream-assembler.js';
|
|
5
|
+
import type { TuiState } from './tui-types.js';
|
|
6
|
+
export type SessionActionsContext = {
|
|
7
|
+
client: TuiBackend;
|
|
8
|
+
chatLog: ChatLog;
|
|
9
|
+
tui: TUI;
|
|
10
|
+
state: TuiState;
|
|
11
|
+
assembler: StreamAssembler;
|
|
12
|
+
resolveSessionKey: (raw?: string) => string;
|
|
13
|
+
updateHeader: () => void;
|
|
14
|
+
updateFooter: () => void;
|
|
15
|
+
setActivityStatus: (status: string) => void;
|
|
16
|
+
historyLimit?: number;
|
|
17
|
+
onAgentIdChange?: (agentId: string) => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function createSessionActions(context: SessionActionsContext): {
|
|
20
|
+
refreshSessionInfo: () => Promise<void>;
|
|
21
|
+
loadHistory: () => Promise<void>;
|
|
22
|
+
setSession: (rawKey: string) => Promise<void>;
|
|
23
|
+
abortActive: (opts?: {
|
|
24
|
+
clearUi?: boolean;
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
resetCurrentSession: () => Promise<void>;
|
|
27
|
+
clearChatForSessionSwitch: () => void;
|
|
28
|
+
};
|