@xopcai/xopc 0.0.86 → 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-mS3_HpRI.js → agents-BEAbXpuP.js} +6 -6
- package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-Dg8R-Szf.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-BG6b9KrW.js → channels-settings-yohw9YSu.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-BSHqqCF1.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-0h_QT8U3.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-BkfKFfFk.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-Cmjp2APP.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-CFa9z_1N.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BI8eaTPq.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-B-W4x2xP.js → extension-settings-page-x4BB7q1X.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-BiNHBo2Y.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
- package/dist/gateway/static/root/assets/{index-BmVYculr.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-sTsVWz0X.js → logs-page-BFZ8GgCv.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-FaG_Vlkb.js → sessions-page-CD7AfB-2.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-DiqqVs6m.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Bet1OerL.js → settings-page-BBOjEQW3.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-n1Gprylk.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-DhUO235y.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-BY7bU1DT.js → utils-CkWBfxs4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CGEydndO.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 +7 -1
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
- 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 +4 -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 +3 -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/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 +2 -2
- 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/heartbeat/index.js +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/index-ew_2L2We.css +0 -1
- 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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { SessionConfigSchema, init_schema } from "../config/schema.js";
|
|
2
|
+
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
3
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
4
|
+
import { init_logger } from "../utils/logger.js";
|
|
5
|
+
import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset-policy.js";
|
|
6
|
+
import { resolveChannelResetConfig, resolveSessionResetType } from "./reset-type.js";
|
|
7
|
+
import { resolveSessionLifecycleTimestamps } from "./lifecycle-timestamps.js";
|
|
8
|
+
import { resolveSessionKeyForRequest } from "./resolve-session.js";
|
|
9
|
+
import { bareResetAckMessage, matchResetTriggers, resolveResetTriggers } from "./reset-triggers.js";
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
//#region src/session/init-session-turn.ts
|
|
12
|
+
init_schema();
|
|
13
|
+
init_session_key();
|
|
14
|
+
init_logger();
|
|
15
|
+
const log = createLogger("InitSessionTurn");
|
|
16
|
+
/**
|
|
17
|
+
* Turn-start session init: match `resetTriggers`, evaluate freshness, archive +
|
|
18
|
+
* assign new `sessionId` when stale or explicitly reset. OpenClaw `initSessionState`
|
|
19
|
+
* equivalent for xopc direct + channel paths.
|
|
20
|
+
*/
|
|
21
|
+
async function initSessionTurn(opts) {
|
|
22
|
+
const sessionCfg = opts.cfg.session ?? SessionConfigSchema.parse({});
|
|
23
|
+
const triggers = resolveResetTriggers(sessionCfg.resetTriggers);
|
|
24
|
+
const rawBody = opts.body ?? "";
|
|
25
|
+
const triggerMatch = matchResetTriggers(rawBody, triggers);
|
|
26
|
+
const { sessionKey, sessionStore } = await resolveSessionKeyForRequest({
|
|
27
|
+
cfg: opts.cfg,
|
|
28
|
+
sessionKey: opts.sessionKey
|
|
29
|
+
});
|
|
30
|
+
const key = sessionKey?.trim() ?? opts.sessionKey.trim();
|
|
31
|
+
const sessionEntry = key ? sessionStore[key] : void 0;
|
|
32
|
+
const parsed = key ? parseSessionKey(key) : null;
|
|
33
|
+
const peerKind = parsed?.peerKind;
|
|
34
|
+
const resetType = resolveSessionResetType({
|
|
35
|
+
sessionKey: key,
|
|
36
|
+
isGroup: peerKind === "group" || peerKind === "channel",
|
|
37
|
+
isThread: Boolean(parsed?.threadId)
|
|
38
|
+
});
|
|
39
|
+
const meta = sessionEntry?.pluginExtensions?.xopc?.metadata;
|
|
40
|
+
const resetPolicy = resolveSessionResetPolicy({
|
|
41
|
+
sessionCfg,
|
|
42
|
+
resetType,
|
|
43
|
+
resetOverride: resolveChannelResetConfig({
|
|
44
|
+
sessionCfg,
|
|
45
|
+
channel: parsed?.source ?? meta?.sourceChannel
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
const lifecycle = resolveSessionLifecycleTimestamps({ entry: sessionEntry });
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const freshness = sessionEntry ? evaluateSessionFreshness({
|
|
51
|
+
updatedAt: sessionEntry.updatedAt,
|
|
52
|
+
...lifecycle,
|
|
53
|
+
now,
|
|
54
|
+
policy: resetPolicy
|
|
55
|
+
}) : { fresh: false };
|
|
56
|
+
const skipImplicit = opts.skipImplicitExpiry ?? false;
|
|
57
|
+
const staleRollover = Boolean(sessionEntry && !skipImplicit && !freshness.fresh);
|
|
58
|
+
const needsRollover = triggerMatch.resetTriggered || staleRollover;
|
|
59
|
+
let sessionId = sessionEntry?.sessionId;
|
|
60
|
+
let previousSessionId;
|
|
61
|
+
let isNewSession = false;
|
|
62
|
+
if (needsRollover && sessionEntry?.sessionId) {
|
|
63
|
+
const outcome = await opts.resetSession(key);
|
|
64
|
+
if (outcome) {
|
|
65
|
+
previousSessionId = outcome.previousSessionId;
|
|
66
|
+
sessionId = outcome.sessionId;
|
|
67
|
+
isNewSession = true;
|
|
68
|
+
log.info({
|
|
69
|
+
sessionKey: key,
|
|
70
|
+
sessionId: outcome.sessionId,
|
|
71
|
+
previousSessionId: outcome.previousSessionId,
|
|
72
|
+
resetTriggered: triggerMatch.resetTriggered,
|
|
73
|
+
staleRollover,
|
|
74
|
+
resetType
|
|
75
|
+
}, triggerMatch.resetTriggered ? "Session reset via reset trigger" : "Session rolled over (stale freshness)");
|
|
76
|
+
} else {
|
|
77
|
+
log.warn({ sessionKey: key }, "Session rollover requested but resetSession returned null");
|
|
78
|
+
sessionId = randomUUID();
|
|
79
|
+
isNewSession = true;
|
|
80
|
+
}
|
|
81
|
+
} else if (!sessionEntry) {
|
|
82
|
+
isNewSession = true;
|
|
83
|
+
sessionId = randomUUID();
|
|
84
|
+
}
|
|
85
|
+
const bareReset = triggerMatch.resetTriggered && triggerMatch.bareReset;
|
|
86
|
+
const ackMessage = bareReset ? bareResetAckMessage(triggerMatch.matchedTrigger) : void 0;
|
|
87
|
+
return {
|
|
88
|
+
sessionKey: key,
|
|
89
|
+
sessionId,
|
|
90
|
+
previousSessionId,
|
|
91
|
+
isNewSession,
|
|
92
|
+
resetTriggered: triggerMatch.resetTriggered,
|
|
93
|
+
staleRollover,
|
|
94
|
+
bodyStripped: triggerMatch.resetTriggered ? triggerMatch.bodyStripped : rawBody,
|
|
95
|
+
bareReset,
|
|
96
|
+
ackMessage
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { initSessionTurn };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=init-session-turn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-session-turn.js","names":[],"sources":["../../../src/session/init-session-turn.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { SessionConfigSchema, type Config } from '../config/schema.js';\nimport { parseSessionKey } from '../routing/session-key.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { resolveSessionLifecycleTimestamps } from './lifecycle-timestamps.js';\nimport {\n evaluateSessionFreshness,\n resolveSessionResetPolicy,\n} from './reset-policy.js';\nimport {\n bareResetAckMessage,\n matchResetTriggers,\n resolveResetTriggers,\n} from './reset-triggers.js';\nimport { resolveChannelResetConfig, resolveSessionResetType } from './reset-type.js';\nimport { resolveSessionKeyForRequest } from './resolve-session.js';\n\nconst log = createLogger('InitSessionTurn');\n\nexport type SessionResetFn = (\n sessionKey: string,\n) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n\nexport type InitSessionTurnResult = {\n sessionKey: string;\n sessionId?: string;\n previousSessionId?: string;\n isNewSession: boolean;\n resetTriggered: boolean;\n staleRollover: boolean;\n bodyStripped: string;\n bareReset: boolean;\n ackMessage?: string;\n};\n\nexport type InitSessionTurnOptions = {\n cfg: Config;\n sessionKey: string;\n body?: string;\n resetSession: SessionResetFn;\n /** When true, skip idle/daily implicit rollover (provider-owned CLI sessions). */\n skipImplicitExpiry?: boolean;\n};\n\n/**\n * Turn-start session init: match `resetTriggers`, evaluate freshness, archive +\n * assign new `sessionId` when stale or explicitly reset. OpenClaw `initSessionState`\n * equivalent for xopc direct + channel paths.\n */\nexport async function initSessionTurn(\n opts: InitSessionTurnOptions,\n): Promise<InitSessionTurnResult> {\n const sessionCfg = opts.cfg.session ?? SessionConfigSchema.parse({});\n const triggers = resolveResetTriggers(sessionCfg.resetTriggers);\n const rawBody = opts.body ?? '';\n const triggerMatch = matchResetTriggers(rawBody, triggers);\n\n const { sessionKey, sessionStore } = await resolveSessionKeyForRequest({\n cfg: opts.cfg,\n sessionKey: opts.sessionKey,\n });\n const key = sessionKey?.trim() ?? opts.sessionKey.trim();\n const sessionEntry = key ? sessionStore[key] : undefined;\n\n const parsed = key ? parseSessionKey(key) : null;\n const peerKind = parsed?.peerKind;\n const resetType = resolveSessionResetType({\n sessionKey: key,\n isGroup: peerKind === 'group' || peerKind === 'channel',\n isThread: Boolean(parsed?.threadId),\n });\n const meta = sessionEntry?.pluginExtensions?.xopc?.metadata;\n const channelReset = resolveChannelResetConfig({\n sessionCfg,\n channel: parsed?.source ?? meta?.sourceChannel,\n });\n const resetPolicy = resolveSessionResetPolicy({\n sessionCfg,\n resetType,\n resetOverride: channelReset,\n });\n const lifecycle = resolveSessionLifecycleTimestamps({ entry: sessionEntry });\n const now = Date.now();\n const freshness = sessionEntry\n ? evaluateSessionFreshness({\n updatedAt: sessionEntry.updatedAt,\n ...lifecycle,\n now,\n policy: resetPolicy,\n })\n : { fresh: false };\n\n const skipImplicit = opts.skipImplicitExpiry ?? false;\n const staleRollover = Boolean(sessionEntry && !skipImplicit && !freshness.fresh);\n const needsRollover = triggerMatch.resetTriggered || staleRollover;\n\n let sessionId = sessionEntry?.sessionId;\n let previousSessionId: string | undefined;\n let isNewSession = false;\n\n if (needsRollover && sessionEntry?.sessionId) {\n const outcome = await opts.resetSession(key);\n if (outcome) {\n previousSessionId = outcome.previousSessionId;\n sessionId = outcome.sessionId;\n isNewSession = true;\n log.info(\n {\n sessionKey: key,\n sessionId: outcome.sessionId,\n previousSessionId: outcome.previousSessionId,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n resetType,\n },\n triggerMatch.resetTriggered\n ? 'Session reset via reset trigger'\n : 'Session rolled over (stale freshness)',\n );\n } else {\n log.warn({ sessionKey: key }, 'Session rollover requested but resetSession returned null');\n sessionId = randomUUID();\n isNewSession = true;\n }\n } else if (!sessionEntry) {\n isNewSession = true;\n sessionId = randomUUID();\n }\n\n const bareReset = triggerMatch.resetTriggered && triggerMatch.bareReset;\n const ackMessage = bareReset ? bareResetAckMessage(triggerMatch.matchedTrigger) : undefined;\n\n return {\n sessionKey: key,\n sessionId,\n previousSessionId,\n isNewSession,\n resetTriggered: triggerMatch.resetTriggered,\n staleRollover,\n bodyStripped: triggerMatch.resetTriggered ? triggerMatch.bodyStripped : rawBody,\n bareReset,\n ackMessage,\n };\n}\n"],"mappings":";;;;;;;;;;;aAEuE;kBACX;aACV;AAelD,MAAM,MAAM,aAAa,kBAAkB;;;;;;AAgC3C,eAAsB,gBACpB,MACgC;CAChC,MAAM,aAAa,KAAK,IAAI,WAAW,oBAAoB,MAAM,EAAE,CAAC;CACpE,MAAM,WAAW,qBAAqB,WAAW,cAAc;CAC/D,MAAM,UAAU,KAAK,QAAQ;CAC7B,MAAM,eAAe,mBAAmB,SAAS,SAAS;CAE1D,MAAM,EAAE,YAAY,iBAAiB,MAAM,4BAA4B;EACrE,KAAK,KAAK;EACV,YAAY,KAAK;EAClB,CAAC;CACF,MAAM,MAAM,YAAY,MAAM,IAAI,KAAK,WAAW,MAAM;CACxD,MAAM,eAAe,MAAM,aAAa,OAAO,KAAA;CAE/C,MAAM,SAAS,MAAM,gBAAgB,IAAI,GAAG;CAC5C,MAAM,WAAW,QAAQ;CACzB,MAAM,YAAY,wBAAwB;EACxC,YAAY;EACZ,SAAS,aAAa,WAAW,aAAa;EAC9C,UAAU,QAAQ,QAAQ,SAAS;EACpC,CAAC;CACF,MAAM,OAAO,cAAc,kBAAkB,MAAM;CAKnD,MAAM,cAAc,0BAA0B;EAC5C;EACA;EACA,eAPmB,0BAA0B;GAC7C;GACA,SAAS,QAAQ,UAAU,MAAM;GAClC,CAI4B;EAC5B,CAAC;CACF,MAAM,YAAY,kCAAkC,EAAE,OAAO,cAAc,CAAC;CAC5E,MAAM,MAAM,KAAK,KAAK;CACtB,MAAM,YAAY,eACd,yBAAyB;EACvB,WAAW,aAAa;EACxB,GAAG;EACH;EACA,QAAQ;EACT,CAAC,GACF,EAAE,OAAO,OAAO;CAEpB,MAAM,eAAe,KAAK,sBAAsB;CAChD,MAAM,gBAAgB,QAAQ,gBAAgB,CAAC,gBAAgB,CAAC,UAAU,MAAM;CAChF,MAAM,gBAAgB,aAAa,kBAAkB;CAErD,IAAI,YAAY,cAAc;CAC9B,IAAI;CACJ,IAAI,eAAe;AAEnB,KAAI,iBAAiB,cAAc,WAAW;EAC5C,MAAM,UAAU,MAAM,KAAK,aAAa,IAAI;AAC5C,MAAI,SAAS;AACX,uBAAoB,QAAQ;AAC5B,eAAY,QAAQ;AACpB,kBAAe;AACf,OAAI,KACF;IACE,YAAY;IACZ,WAAW,QAAQ;IACnB,mBAAmB,QAAQ;IAC3B,gBAAgB,aAAa;IAC7B;IACA;IACD,EACD,aAAa,iBACT,oCACA,wCACL;SACI;AACL,OAAI,KAAK,EAAE,YAAY,KAAK,EAAE,4DAA4D;AAC1F,eAAY,YAAY;AACxB,kBAAe;;YAER,CAAC,cAAc;AACxB,iBAAe;AACf,cAAY,YAAY;;CAG1B,MAAM,YAAY,aAAa,kBAAkB,aAAa;CAC9D,MAAM,aAAa,YAAY,oBAAoB,aAAa,eAAe,GAAG,KAAA;AAElF,QAAO;EACL,YAAY;EACZ;EACA;EACA;EACA,gBAAgB,aAAa;EAC7B;EACA,cAAc,aAAa,iBAAiB,aAAa,eAAe;EACxE;EACA;EACD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { XopcSessionDiskEntry } from './parity/xopc-session-disk-entry.js';
|
|
2
|
+
export type SessionLifecycleEntry = Pick<XopcSessionDiskEntry, 'sessionStartedAt' | 'lastInteractionAt' | 'updatedAt'>;
|
|
3
|
+
export declare function resolveSessionLifecycleTimestamps(params: {
|
|
4
|
+
entry: SessionLifecycleEntry | undefined;
|
|
5
|
+
}): {
|
|
6
|
+
sessionStartedAt?: number;
|
|
7
|
+
lastInteractionAt?: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/session/lifecycle-timestamps.ts
|
|
2
|
+
function resolveTimestamp(value) {
|
|
3
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
4
|
+
}
|
|
5
|
+
function resolveSessionLifecycleTimestamps(params) {
|
|
6
|
+
const entry = params.entry;
|
|
7
|
+
if (!entry) return {};
|
|
8
|
+
return {
|
|
9
|
+
sessionStartedAt: resolveTimestamp(entry.sessionStartedAt),
|
|
10
|
+
lastInteractionAt: resolveTimestamp(entry.lastInteractionAt)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { resolveSessionLifecycleTimestamps };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=lifecycle-timestamps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-timestamps.js","names":[],"sources":["../../../src/session/lifecycle-timestamps.ts"],"sourcesContent":["import type { XopcSessionDiskEntry } from './parity/xopc-session-disk-entry.js';\n\nexport type SessionLifecycleEntry = Pick<\n XopcSessionDiskEntry,\n 'sessionStartedAt' | 'lastInteractionAt' | 'updatedAt'\n>;\n\nfunction resolveTimestamp(value: number | undefined): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;\n}\n\nexport function resolveSessionLifecycleTimestamps(params: {\n entry: SessionLifecycleEntry | undefined;\n}): { sessionStartedAt?: number; lastInteractionAt?: number } {\n const entry = params.entry;\n if (!entry) {\n return {};\n }\n return {\n sessionStartedAt: resolveTimestamp(entry.sessionStartedAt),\n lastInteractionAt: resolveTimestamp(entry.lastInteractionAt),\n };\n}\n"],"mappings":";AAOA,SAAS,iBAAiB,OAA+C;AACvE,QAAO,OAAO,UAAU,YAAY,OAAO,SAAS,MAAM,IAAI,SAAS,IAAI,QAAQ,KAAA;;AAGrF,SAAgB,kCAAkC,QAEY;CAC5D,MAAM,QAAQ,OAAO;AACrB,KAAI,CAAC,MACH,QAAO,EAAE;AAEX,QAAO;EACL,kBAAkB,iBAAiB,MAAM,iBAAiB;EAC1D,mBAAmB,iBAAiB,MAAM,kBAAkB;EAC7D"}
|
|
@@ -112,7 +112,8 @@ export declare class SessionIndex extends EventEmitter {
|
|
|
112
112
|
/** Wrapped transcript document (stable id, compaction history); null if missing or not a valid envelope. */
|
|
113
113
|
loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null>;
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* Runtime turns must use PiTranscriptManager.appendMessage; this entry point
|
|
116
|
+
* is reserved for compaction, tests, and admin tools.
|
|
116
117
|
*/
|
|
117
118
|
saveMessages(key: string, messages: any[]): Promise<void>;
|
|
118
119
|
/**
|
|
@@ -121,6 +122,11 @@ export declare class SessionIndex extends EventEmitter {
|
|
|
121
122
|
appendTranscriptContextEntry(key: string, entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>): Promise<void>;
|
|
122
123
|
/** Delete session data */
|
|
123
124
|
delete(key: string): Promise<void>;
|
|
125
|
+
/** Archive transcript and start a new session id for the same key. */
|
|
126
|
+
resetSession(key: string): Promise<{
|
|
127
|
+
sessionId: string;
|
|
128
|
+
previousSessionId: string;
|
|
129
|
+
} | null>;
|
|
124
130
|
/** Token/window stats for a message list */
|
|
125
131
|
getWindowStats(messages: any[]): {
|
|
126
132
|
total: number;
|
|
@@ -204,7 +204,8 @@ var SessionIndex = class extends EventEmitter$1 {
|
|
|
204
204
|
return this.store.loadTranscriptDocument(key);
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
|
-
*
|
|
207
|
+
* Runtime turns must use PiTranscriptManager.appendMessage; this entry point
|
|
208
|
+
* is reserved for compaction, tests, and admin tools.
|
|
208
209
|
*/
|
|
209
210
|
async saveMessages(key, messages) {
|
|
210
211
|
return this.store.saveMessages(key, messages);
|
|
@@ -220,6 +221,12 @@ var SessionIndex = class extends EventEmitter$1 {
|
|
|
220
221
|
async delete(key) {
|
|
221
222
|
await this.store.delete(key);
|
|
222
223
|
}
|
|
224
|
+
/** Archive transcript and start a new session id for the same key. */
|
|
225
|
+
async resetSession(key) {
|
|
226
|
+
const result = await this.store.reset(key);
|
|
227
|
+
if (result) this.emit("sessionUpdated", { key });
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
223
230
|
/** Token/window stats for a message list */
|
|
224
231
|
getWindowStats(messages) {
|
|
225
232
|
return this.store.getWindowStats(messages);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","names":["EventEmitter"],"sources":["../../../src/session/manager.ts"],"sourcesContent":["// Session manager - high-level session management service\n\nimport EventEmitter from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionStore } from './store.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionStatus,\n} from './types.js';\nimport type { Message } from './types.js';\nimport type { CompactionConfig, CompactionResult } from '../agent/memory/compaction.js';\nimport type { XopcSessionTranscriptV1 } from './transcript-format.js';\nimport type { XopcTranscriptContextEntry } from './session-context-for-llm.js';\nimport { applySessionPatchToMetadata, type SessionPatchBody } from './patch-metadata.js';\nimport type { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionIndex');\n\nexport interface SessionIndexConfig {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionIndex extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionIndexConfig) {\n super();\n this.store = new SessionStore(\n {\n config: config.config,\n agentId: config.agentId,\n sessionsDir: config.sessionsDir,\n },\n config.windowConfig,\n config.compactionConfig\n );\n }\n\n async initialize(): Promise<void> {\n await this.store.initialize();\n this.emit('ready');\n }\n\n /** Low-level store (e.g. cron resolving weixin delivery from session index). */\n getStore(): SessionStore {\n return this.store;\n }\n\n // ========== CRUD Operations ==========\n\n async listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list(query);\n }\n\n /**\n * List all subagent sessions.\n * Subagent sessions have keys starting with 'subagent:'.\n */\n async listSubagents(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n // Filter for subagent sessions only\n const subagentQuery: SessionListQuery = {\n ...query,\n search: query.search ? `subagent:${query.search}` : 'subagent:',\n };\n \n const result = await this.store.list(subagentQuery);\n \n // Additional filtering to ensure only subagent sessions\n const subagentSessions = result.items.filter((s) => s.key.startsWith('subagent:'));\n \n return {\n ...result,\n items: subagentSessions,\n total: subagentSessions.length,\n hasMore: false, // Simplified for now\n };\n }\n\n async getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const session = await this.store.get(key, options);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\n }\n\n async getSessionMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n },\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const result = await this.store.getMessagePage(key, options);\n if (result) {\n this.emit('sessionAccessed', { key });\n }\n return result;\n }\n\n /**\n * OpenClaw-style `sessions.patch`: partial metadata (name, tags, customData shallow merge).\n */\n async patchSession(\n key: string,\n patch: SessionPatchBody,\n ): Promise<{ ok: true } | { ok: false; error: string }> {\n const meta = await this.store.getMetadata(key);\n if (!meta) {\n return { ok: false, error: 'Session not found' };\n }\n const updates = applySessionPatchToMetadata(meta, patch);\n if (Object.keys(updates).length === 0) {\n return { ok: true };\n }\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n return { ok: true };\n }\n\n async getSessionMetadata(key: string): Promise<SessionMetadata | null> {\n return this.store.getMetadata(key);\n }\n\n async deleteSession(key: string): Promise<boolean> {\n const result = await this.store.delete(key);\n if (result) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const result = await this.store.deleteMany(keys);\n for (const key of result.success) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n // ========== Metadata Updates ==========\n\n async renameSession(key: string, name: string): Promise<void> {\n await this.store.updateMetadata(key, { name });\n this.emit('sessionUpdated', { key, name });\n }\n\n /** Partial metadata update (caller merges nested fields like `customData` when needed). */\n async updateSessionMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n }\n\n async tagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n // Merge tags, remove duplicates\n const mergedTags = [...new Set([...existing.tags, ...tags])];\n await this.store.updateMetadata(key, { tags: mergedTags });\n this.emit('sessionUpdated', { key, tags: mergedTags });\n }\n\n async untagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n const filteredTags = existing.tags.filter((t) => !tags.includes(t));\n await this.store.updateMetadata(key, { tags: filteredTags });\n this.emit('sessionUpdated', { key, tags: filteredTags });\n }\n\n async setSessionTags(key: string, tags: string[]): Promise<void> {\n await this.store.updateMetadata(key, { tags: [...new Set(tags)] });\n this.emit('sessionUpdated', { key, tags });\n }\n\n // ========== Status Management ==========\n\n async archiveSession(key: string): Promise<void> {\n await this.store.archive(key);\n this.emit('sessionArchived', { key });\n }\n\n async unarchiveSession(key: string): Promise<void> {\n await this.store.unarchive(key);\n this.emit('sessionRestored', { key });\n }\n\n async pinSession(key: string): Promise<void> {\n await this.store.pin(key);\n this.emit('sessionPinned', { key });\n }\n\n async unpinSession(key: string): Promise<void> {\n await this.store.unpin(key);\n this.emit('sessionUnpinned', { key });\n }\n\n async setSessionStatus(key: string, status: SessionStatus): Promise<void> {\n await this.store.setStatus(key, status);\n this.emit('sessionStatusChanged', { key, status });\n }\n\n // ========== Search ==========\n\n async searchSessions(query: string): Promise<SessionMetadata[]> {\n const result = await this.store.list({ search: query, limit: 100 });\n return result.items;\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n return this.store.searchInSession(key, keyword);\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n return this.store.exportSession(key, format);\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n return this.store.getStats();\n }\n\n // ========== Maintenance ==========\n\n async archiveOldSessions(olderThanDays: number): Promise<number> {\n const count = await this.store.archiveOld(olderThanDays);\n log.info({ count, olderThanDays }, 'Archived old sessions');\n return count;\n }\n\n // ========== Event Helpers ==========\n\n onSessionCreated(callback: (metadata: SessionMetadata) => void): void {\n this.on('sessionCreated', callback);\n }\n\n onSessionUpdated(callback: (data: { key: string; name?: string; tags?: string[] }) => void): void {\n this.on('sessionUpdated', callback);\n }\n\n onSessionDeleted(callback: (data: { key: string }) => void): void {\n this.on('sessionDeleted', callback);\n }\n\n onSessionArchived(callback: (data: { key: string }) => void): void {\n this.on('sessionArchived', callback);\n }\n\n onSessionRestored(callback: (data: { key: string }) => void): void {\n this.on('sessionRestored', callback);\n }\n\n onSessionPinned(callback: (data: { key: string }) => void): void {\n this.on('sessionPinned', callback);\n }\n\n onSessionUnpinned(callback: (data: { key: string }) => void): void {\n this.on('sessionUnpinned', callback);\n }\n\n onSessionStatusChanged(callback: (data: { key: string; status: SessionStatus }) => void): void {\n this.on('sessionStatusChanged', callback);\n }\n\n onSessionAccessed(callback: (data: { key: string }) => void): void {\n this.on('sessionAccessed', callback);\n }\n\n // ========== Store delegation (messages, compaction) ==========\n\n /** Load messages for a session key */\n async loadMessages(key: string) {\n return this.store.loadMessages(key);\n }\n\n /** Wrapped transcript document (stable id, compaction history); null if missing or not a valid envelope. */\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n return this.store.loadTranscriptDocument(key);\n }\n\n /**\n * @deprecated Runtime turns must use PiTranscriptManager.appendMessage. Compaction/admin only.\n */\n async saveMessages(key: string, messages: any[]) {\n return this.store.saveMessages(key, messages);\n }\n\n /**\n * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).\n */\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n await this.store.appendTranscriptContextEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n /** Delete session data */\n async delete(key: string): Promise<void> {\n await this.store.delete(key);\n }\n\n /** Token/window stats for a message list */\n getWindowStats(messages: any[]) {\n return this.store.getWindowStats(messages);\n }\n\n /** Prepare compaction run */\n prepareCompaction(key: string, messages: any[], contextWindow: number) {\n return this.store.prepareCompaction(key, messages, contextWindow);\n }\n\n /** Compact session messages */\n compact(\n key: string,\n messages: any[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n return this.store.compact(key, messages, contextWindow, instructions, force);\n }\n\n /** Compaction stats for a session */\n async getCompactionStats(key: string) {\n return this.store.getCompactionStats(key);\n }\n\n /** List pre-compaction transcript snapshots (newest first). */\n listCompactionCheckpoints(key: string) {\n return this.store.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpointDetail(key: string, checkpointId: string) {\n return this.store.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n restoreCompactionCheckpoint(key: string, checkpointId: string) {\n return this.store.restoreCompactionCheckpoint(key, checkpointId);\n }\n\n /** Estimate token usage for messages */\n async estimateTokenUsage(key: string, messages: any[]): Promise<number> {\n return this.store.estimateTokens(messages);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"manager.js","names":["EventEmitter"],"sources":["../../../src/session/manager.ts"],"sourcesContent":["// Session manager - high-level session management service\n\nimport EventEmitter from 'events';\nimport { createLogger } from '../utils/logger.js';\nimport { SessionStore } from './store.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionStatus,\n} from './types.js';\nimport type { Message } from './types.js';\nimport type { CompactionConfig, CompactionResult } from '../agent/memory/compaction.js';\nimport type { XopcSessionTranscriptV1 } from './transcript-format.js';\nimport type { XopcTranscriptContextEntry } from './session-context-for-llm.js';\nimport { applySessionPatchToMetadata, type SessionPatchBody } from './patch-metadata.js';\nimport type { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionIndex');\n\nexport interface SessionIndexConfig {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionIndex extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionIndexConfig) {\n super();\n this.store = new SessionStore(\n {\n config: config.config,\n agentId: config.agentId,\n sessionsDir: config.sessionsDir,\n },\n config.windowConfig,\n config.compactionConfig\n );\n }\n\n async initialize(): Promise<void> {\n await this.store.initialize();\n this.emit('ready');\n }\n\n /** Low-level store (e.g. cron resolving weixin delivery from session index). */\n getStore(): SessionStore {\n return this.store;\n }\n\n // ========== CRUD Operations ==========\n\n async listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n return this.store.list(query);\n }\n\n /**\n * List all subagent sessions.\n * Subagent sessions have keys starting with 'subagent:'.\n */\n async listSubagents(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n // Filter for subagent sessions only\n const subagentQuery: SessionListQuery = {\n ...query,\n search: query.search ? `subagent:${query.search}` : 'subagent:',\n };\n \n const result = await this.store.list(subagentQuery);\n \n // Additional filtering to ensure only subagent sessions\n const subagentSessions = result.items.filter((s) => s.key.startsWith('subagent:'));\n \n return {\n ...result,\n items: subagentSessions,\n total: subagentSessions.length,\n hasMore: false, // Simplified for now\n };\n }\n\n async getSession(\n key: string,\n options?: { includeTranscriptSummary?: boolean; includeTranscriptRows?: boolean },\n ): Promise<SessionDetail | null> {\n const session = await this.store.get(key, options);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\n }\n\n async getSessionMessagePage(\n key: string,\n options?: {\n offset?: number;\n limit?: number;\n before?: string;\n includeTranscriptSummary?: boolean;\n includeTranscriptRows?: boolean;\n },\n ): Promise<{\n session: SessionDetail;\n pagination: {\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n before?: string;\n nextBeforeCursor?: string;\n };\n } | null> {\n const result = await this.store.getMessagePage(key, options);\n if (result) {\n this.emit('sessionAccessed', { key });\n }\n return result;\n }\n\n /**\n * OpenClaw-style `sessions.patch`: partial metadata (name, tags, customData shallow merge).\n */\n async patchSession(\n key: string,\n patch: SessionPatchBody,\n ): Promise<{ ok: true } | { ok: false; error: string }> {\n const meta = await this.store.getMetadata(key);\n if (!meta) {\n return { ok: false, error: 'Session not found' };\n }\n const updates = applySessionPatchToMetadata(meta, patch);\n if (Object.keys(updates).length === 0) {\n return { ok: true };\n }\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n return { ok: true };\n }\n\n async getSessionMetadata(key: string): Promise<SessionMetadata | null> {\n return this.store.getMetadata(key);\n }\n\n async deleteSession(key: string): Promise<boolean> {\n const result = await this.store.delete(key);\n if (result) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n async deleteSessions(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const result = await this.store.deleteMany(keys);\n for (const key of result.success) {\n this.emit('sessionDeleted', { key });\n }\n return result;\n }\n\n // ========== Metadata Updates ==========\n\n async renameSession(key: string, name: string): Promise<void> {\n await this.store.updateMetadata(key, { name });\n this.emit('sessionUpdated', { key, name });\n }\n\n /** Partial metadata update (caller merges nested fields like `customData` when needed). */\n async updateSessionMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n await this.store.updateMetadata(key, updates);\n this.emit('sessionUpdated', { key });\n }\n\n async tagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n // Merge tags, remove duplicates\n const mergedTags = [...new Set([...existing.tags, ...tags])];\n await this.store.updateMetadata(key, { tags: mergedTags });\n this.emit('sessionUpdated', { key, tags: mergedTags });\n }\n\n async untagSession(key: string, tags: string[]): Promise<void> {\n const existing = await this.store.getMetadata(key);\n if (!existing) {\n throw new Error(`Session not found: ${key}`);\n }\n\n const filteredTags = existing.tags.filter((t) => !tags.includes(t));\n await this.store.updateMetadata(key, { tags: filteredTags });\n this.emit('sessionUpdated', { key, tags: filteredTags });\n }\n\n async setSessionTags(key: string, tags: string[]): Promise<void> {\n await this.store.updateMetadata(key, { tags: [...new Set(tags)] });\n this.emit('sessionUpdated', { key, tags });\n }\n\n // ========== Status Management ==========\n\n async archiveSession(key: string): Promise<void> {\n await this.store.archive(key);\n this.emit('sessionArchived', { key });\n }\n\n async unarchiveSession(key: string): Promise<void> {\n await this.store.unarchive(key);\n this.emit('sessionRestored', { key });\n }\n\n async pinSession(key: string): Promise<void> {\n await this.store.pin(key);\n this.emit('sessionPinned', { key });\n }\n\n async unpinSession(key: string): Promise<void> {\n await this.store.unpin(key);\n this.emit('sessionUnpinned', { key });\n }\n\n async setSessionStatus(key: string, status: SessionStatus): Promise<void> {\n await this.store.setStatus(key, status);\n this.emit('sessionStatusChanged', { key, status });\n }\n\n // ========== Search ==========\n\n async searchSessions(query: string): Promise<SessionMetadata[]> {\n const result = await this.store.list({ search: query, limit: 100 });\n return result.items;\n }\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n return this.store.searchInSession(key, keyword);\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n return this.store.exportSession(key, format);\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n return this.store.getStats();\n }\n\n // ========== Maintenance ==========\n\n async archiveOldSessions(olderThanDays: number): Promise<number> {\n const count = await this.store.archiveOld(olderThanDays);\n log.info({ count, olderThanDays }, 'Archived old sessions');\n return count;\n }\n\n // ========== Event Helpers ==========\n\n onSessionCreated(callback: (metadata: SessionMetadata) => void): void {\n this.on('sessionCreated', callback);\n }\n\n onSessionUpdated(callback: (data: { key: string; name?: string; tags?: string[] }) => void): void {\n this.on('sessionUpdated', callback);\n }\n\n onSessionDeleted(callback: (data: { key: string }) => void): void {\n this.on('sessionDeleted', callback);\n }\n\n onSessionArchived(callback: (data: { key: string }) => void): void {\n this.on('sessionArchived', callback);\n }\n\n onSessionRestored(callback: (data: { key: string }) => void): void {\n this.on('sessionRestored', callback);\n }\n\n onSessionPinned(callback: (data: { key: string }) => void): void {\n this.on('sessionPinned', callback);\n }\n\n onSessionUnpinned(callback: (data: { key: string }) => void): void {\n this.on('sessionUnpinned', callback);\n }\n\n onSessionStatusChanged(callback: (data: { key: string; status: SessionStatus }) => void): void {\n this.on('sessionStatusChanged', callback);\n }\n\n onSessionAccessed(callback: (data: { key: string }) => void): void {\n this.on('sessionAccessed', callback);\n }\n\n // ========== Store delegation (messages, compaction) ==========\n\n /** Load messages for a session key */\n async loadMessages(key: string) {\n return this.store.loadMessages(key);\n }\n\n /** Wrapped transcript document (stable id, compaction history); null if missing or not a valid envelope. */\n async loadTranscriptDocument(key: string): Promise<XopcSessionTranscriptV1 | null> {\n return this.store.loadTranscriptDocument(key);\n }\n\n /**\n * Runtime turns must use PiTranscriptManager.appendMessage; this entry point\n * is reserved for compaction, tests, and admin tools.\n */\n async saveMessages(key: string, messages: any[]) {\n return this.store.saveMessages(key, messages);\n }\n\n /**\n * Append `kind: 'context'` transcript row (persisted, excluded from {@link loadMessages} / LLM).\n */\n async appendTranscriptContextEntry(\n key: string,\n entry: Omit<XopcTranscriptContextEntry, 'kind'> & Partial<Pick<XopcTranscriptContextEntry, 'kind'>>,\n ): Promise<void> {\n await this.store.appendTranscriptContextEntry(key, entry);\n this.emit('sessionUpdated', { key });\n }\n\n /** Delete session data */\n async delete(key: string): Promise<void> {\n await this.store.delete(key);\n }\n\n /** Archive transcript and start a new session id for the same key. */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const result = await this.store.reset(key);\n if (result) {\n this.emit('sessionUpdated', { key });\n }\n return result;\n }\n\n /** Token/window stats for a message list */\n getWindowStats(messages: any[]) {\n return this.store.getWindowStats(messages);\n }\n\n /** Prepare compaction run */\n prepareCompaction(key: string, messages: any[], contextWindow: number) {\n return this.store.prepareCompaction(key, messages, contextWindow);\n }\n\n /** Compact session messages */\n compact(\n key: string,\n messages: any[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n return this.store.compact(key, messages, contextWindow, instructions, force);\n }\n\n /** Compaction stats for a session */\n async getCompactionStats(key: string) {\n return this.store.getCompactionStats(key);\n }\n\n /** List pre-compaction transcript snapshots (newest first). */\n listCompactionCheckpoints(key: string) {\n return this.store.listCompactionCheckpoints(key);\n }\n\n getCompactionCheckpointDetail(key: string, checkpointId: string) {\n return this.store.getCompactionCheckpointDetail(key, checkpointId);\n }\n\n restoreCompactionCheckpoint(key: string, checkpointId: string) {\n return this.store.restoreCompactionCheckpoint(key, checkpointId);\n }\n\n /** Estimate token usage for messages */\n async estimateTokenUsage(key: string, messages: any[]): Promise<number> {\n return this.store.estimateTokens(messages);\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
|
2
2
|
import { formatSessionArchiveTimestamp, init_artifacts } from "./artifacts.js";
|
|
3
3
|
import { init_session_id, validateSessionId } from "./session-id.js";
|
|
4
4
|
import { dirname, relative, resolve } from "node:path";
|
|
5
|
-
import
|
|
5
|
+
import fs from "node:fs";
|
|
6
6
|
//#region src/session/parity/transcript-paths.ts
|
|
7
7
|
function resolveSessionsDir(opts) {
|
|
8
8
|
const sessionsDir = opts?.sessionsDir?.trim();
|
|
@@ -50,7 +50,7 @@ function resolveSessionTranscriptCandidates(sessionId, storePath, sessionFile, _
|
|
|
50
50
|
}
|
|
51
51
|
function archiveFileOnDisk(filePath, reason) {
|
|
52
52
|
const archived = `${filePath}.${reason}.${formatSessionArchiveTimestamp()}`;
|
|
53
|
-
|
|
53
|
+
fs.renameSync(filePath, archived);
|
|
54
54
|
return archived;
|
|
55
55
|
}
|
|
56
56
|
var init_transcript_paths = __esmMin((() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript-paths.js","names":[],"sources":["../../../../src/session/parity/transcript-paths.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { dirname, relative, resolve } from 'node:path';\n\nimport { formatSessionArchiveTimestamp } from './artifacts.js';\nimport { validateSessionId } from './session-id.js';\n\nexport type SessionFilePathOptions = {\n agentId?: string;\n sessionsDir?: string;\n};\n\nfunction resolveSessionsDir(opts?: SessionFilePathOptions): string {\n const sessionsDir = opts?.sessionsDir?.trim();\n if (!sessionsDir) {\n throw new Error('sessionsDir is required for session transcript path resolution');\n }\n return resolve(sessionsDir);\n}\n\nfunction resolvePathWithinSessionsDir(sessionsDir: string, candidate: string): string {\n const trimmed = candidate.trim();\n if (!trimmed) {\n throw new Error('Session file path must not be empty');\n }\n const resolvedBase = resolve(sessionsDir);\n const normalized = resolve(resolvedBase, trimmed);\n const rel = relative(resolvedBase, normalized);\n if (rel.startsWith('..') || rel === '') {\n throw new Error('Session file path must be within sessions directory');\n }\n return normalized;\n}\n\nexport function resolveSessionTranscriptPathInDir(\n sessionId: string,\n sessionsDir: string,\n topicId?: string | number,\n): string {\n const safeSessionId = validateSessionId(sessionId);\n const safeTopicId =\n typeof topicId === 'string'\n ? encodeURIComponent(topicId)\n : typeof topicId === 'number'\n ? String(topicId)\n : undefined;\n const fileName =\n safeTopicId !== undefined\n ? `${safeSessionId}-topic-${safeTopicId}.jsonl`\n : `${safeSessionId}.jsonl`;\n return resolvePathWithinSessionsDir(sessionsDir, fileName);\n}\n\nexport function resolveSessionFilePath(\n sessionId: string,\n entry?: { sessionFile?: string },\n opts?: SessionFilePathOptions,\n): string {\n const sessionsDir = resolveSessionsDir(opts);\n const candidate = entry?.sessionFile?.trim();\n if (candidate) {\n try {\n return resolvePathWithinSessionsDir(sessionsDir, candidate);\n } catch {\n /* fall through */\n }\n }\n return resolveSessionTranscriptPathInDir(sessionId, sessionsDir);\n}\n\n/**\n * Candidate transcript paths for a session (xopc state only — no external legacy dirs).\n */\nexport function resolveSessionTranscriptCandidates(\n sessionId: string,\n storePath: string | undefined,\n sessionFile?: string,\n _agentId?: string,\n): string[] {\n const candidates: string[] = [];\n const push = (p: string): void => {\n if (!candidates.includes(p)) {\n candidates.push(p);\n }\n };\n\n if (storePath) {\n const sessionsDir = dirname(storePath);\n if (sessionFile?.trim()) {\n try {\n push(resolveSessionFilePath(sessionId, { sessionFile }, { sessionsDir }));\n } catch {\n /* ignore */\n }\n }\n push(resolveSessionTranscriptPathInDir(sessionId, sessionsDir));\n } else if (sessionFile?.trim()) {\n push(resolve(sessionFile.trim()));\n }\n\n return candidates;\n}\n\nexport function archiveFileOnDisk(filePath: string, reason: 'reset' | 'deleted' | 'bak'): string {\n const ts = formatSessionArchiveTimestamp();\n const archived = `${filePath}.${reason}.${ts}`;\n fs.renameSync(filePath, archived);\n return archived;\n}\n"],"mappings":";;;;;;AAWA,SAAS,mBAAmB,MAAuC;CACjE,MAAM,cAAc,MAAM,aAAa,MAAM;AAC7C,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,iEAAiE;AAEnF,QAAO,QAAQ,YAAY;;AAG7B,SAAS,6BAA6B,aAAqB,WAA2B;CACpF,MAAM,UAAU,UAAU,MAAM;AAChC,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,sCAAsC;CAExD,MAAM,eAAe,QAAQ,YAAY;CACzC,MAAM,aAAa,QAAQ,cAAc,QAAQ;CACjD,MAAM,MAAM,SAAS,cAAc,WAAW;AAC9C,KAAI,IAAI,WAAW,KAAK,IAAI,QAAQ,GAClC,OAAM,IAAI,MAAM,sDAAsD;AAExE,QAAO;;AAGT,SAAgB,kCACd,WACA,aACA,SACQ;CACR,MAAM,gBAAgB,kBAAkB,UAAU;CAClD,MAAM,cACJ,OAAO,YAAY,WACf,mBAAmB,QAAQ,GAC3B,OAAO,YAAY,WACjB,OAAO,QAAQ,GACf,KAAA;AAKR,QAAO,6BAA6B,aAHlC,gBAAgB,KAAA,IACZ,GAAG,cAAc,SAAS,YAAY,UACtC,GAAG,cAAc,QACmC;;AAG5D,SAAgB,uBACd,WACA,OACA,MACQ;CACR,MAAM,cAAc,mBAAmB,KAAK;CAC5C,MAAM,YAAY,OAAO,aAAa,MAAM;AAC5C,KAAI,UACF,KAAI;AACF,SAAO,6BAA6B,aAAa,UAAU;SACrD;AAIV,QAAO,kCAAkC,WAAW,YAAY;;;;;AAMlE,SAAgB,mCACd,WACA,WACA,aACA,UACU;CACV,MAAM,aAAuB,EAAE;CAC/B,MAAM,QAAQ,MAAoB;AAChC,MAAI,CAAC,WAAW,SAAS,EAAE,CACzB,YAAW,KAAK,EAAE;;AAItB,KAAI,WAAW;EACb,MAAM,cAAc,QAAQ,UAAU;AACtC,MAAI,aAAa,MAAM,CACrB,KAAI;AACF,QAAK,uBAAuB,WAAW,EAAE,aAAa,EAAE,EAAE,aAAa,CAAC,CAAC;UACnE;AAIV,OAAK,kCAAkC,WAAW,YAAY,CAAC;YACtD,aAAa,MAAM,CAC5B,MAAK,QAAQ,YAAY,MAAM,CAAC,CAAC;AAGnC,QAAO;;AAGT,SAAgB,kBAAkB,UAAkB,QAA6C;CAE/F,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,GAD5B,+BACiC;AAC5C,
|
|
1
|
+
{"version":3,"file":"transcript-paths.js","names":[],"sources":["../../../../src/session/parity/transcript-paths.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { dirname, relative, resolve } from 'node:path';\n\nimport { formatSessionArchiveTimestamp } from './artifacts.js';\nimport { validateSessionId } from './session-id.js';\n\nexport type SessionFilePathOptions = {\n agentId?: string;\n sessionsDir?: string;\n};\n\nfunction resolveSessionsDir(opts?: SessionFilePathOptions): string {\n const sessionsDir = opts?.sessionsDir?.trim();\n if (!sessionsDir) {\n throw new Error('sessionsDir is required for session transcript path resolution');\n }\n return resolve(sessionsDir);\n}\n\nfunction resolvePathWithinSessionsDir(sessionsDir: string, candidate: string): string {\n const trimmed = candidate.trim();\n if (!trimmed) {\n throw new Error('Session file path must not be empty');\n }\n const resolvedBase = resolve(sessionsDir);\n const normalized = resolve(resolvedBase, trimmed);\n const rel = relative(resolvedBase, normalized);\n if (rel.startsWith('..') || rel === '') {\n throw new Error('Session file path must be within sessions directory');\n }\n return normalized;\n}\n\nexport function resolveSessionTranscriptPathInDir(\n sessionId: string,\n sessionsDir: string,\n topicId?: string | number,\n): string {\n const safeSessionId = validateSessionId(sessionId);\n const safeTopicId =\n typeof topicId === 'string'\n ? encodeURIComponent(topicId)\n : typeof topicId === 'number'\n ? String(topicId)\n : undefined;\n const fileName =\n safeTopicId !== undefined\n ? `${safeSessionId}-topic-${safeTopicId}.jsonl`\n : `${safeSessionId}.jsonl`;\n return resolvePathWithinSessionsDir(sessionsDir, fileName);\n}\n\nexport function resolveSessionFilePath(\n sessionId: string,\n entry?: { sessionFile?: string },\n opts?: SessionFilePathOptions,\n): string {\n const sessionsDir = resolveSessionsDir(opts);\n const candidate = entry?.sessionFile?.trim();\n if (candidate) {\n try {\n return resolvePathWithinSessionsDir(sessionsDir, candidate);\n } catch {\n /* fall through */\n }\n }\n return resolveSessionTranscriptPathInDir(sessionId, sessionsDir);\n}\n\n/**\n * Candidate transcript paths for a session (xopc state only — no external legacy dirs).\n */\nexport function resolveSessionTranscriptCandidates(\n sessionId: string,\n storePath: string | undefined,\n sessionFile?: string,\n _agentId?: string,\n): string[] {\n const candidates: string[] = [];\n const push = (p: string): void => {\n if (!candidates.includes(p)) {\n candidates.push(p);\n }\n };\n\n if (storePath) {\n const sessionsDir = dirname(storePath);\n if (sessionFile?.trim()) {\n try {\n push(resolveSessionFilePath(sessionId, { sessionFile }, { sessionsDir }));\n } catch {\n /* ignore */\n }\n }\n push(resolveSessionTranscriptPathInDir(sessionId, sessionsDir));\n } else if (sessionFile?.trim()) {\n push(resolve(sessionFile.trim()));\n }\n\n return candidates;\n}\n\nexport function archiveFileOnDisk(filePath: string, reason: 'reset' | 'deleted' | 'bak'): string {\n const ts = formatSessionArchiveTimestamp();\n const archived = `${filePath}.${reason}.${ts}`;\n fs.renameSync(filePath, archived);\n return archived;\n}\n"],"mappings":";;;;;;AAWA,SAAS,mBAAmB,MAAuC;CACjE,MAAM,cAAc,MAAM,aAAa,MAAM;AAC7C,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,iEAAiE;AAEnF,QAAO,QAAQ,YAAY;;AAG7B,SAAS,6BAA6B,aAAqB,WAA2B;CACpF,MAAM,UAAU,UAAU,MAAM;AAChC,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,sCAAsC;CAExD,MAAM,eAAe,QAAQ,YAAY;CACzC,MAAM,aAAa,QAAQ,cAAc,QAAQ;CACjD,MAAM,MAAM,SAAS,cAAc,WAAW;AAC9C,KAAI,IAAI,WAAW,KAAK,IAAI,QAAQ,GAClC,OAAM,IAAI,MAAM,sDAAsD;AAExE,QAAO;;AAGT,SAAgB,kCACd,WACA,aACA,SACQ;CACR,MAAM,gBAAgB,kBAAkB,UAAU;CAClD,MAAM,cACJ,OAAO,YAAY,WACf,mBAAmB,QAAQ,GAC3B,OAAO,YAAY,WACjB,OAAO,QAAQ,GACf,KAAA;AAKR,QAAO,6BAA6B,aAHlC,gBAAgB,KAAA,IACZ,GAAG,cAAc,SAAS,YAAY,UACtC,GAAG,cAAc,QACmC;;AAG5D,SAAgB,uBACd,WACA,OACA,MACQ;CACR,MAAM,cAAc,mBAAmB,KAAK;CAC5C,MAAM,YAAY,OAAO,aAAa,MAAM;AAC5C,KAAI,UACF,KAAI;AACF,SAAO,6BAA6B,aAAa,UAAU;SACrD;AAIV,QAAO,kCAAkC,WAAW,YAAY;;;;;AAMlE,SAAgB,mCACd,WACA,WACA,aACA,UACU;CACV,MAAM,aAAuB,EAAE;CAC/B,MAAM,QAAQ,MAAoB;AAChC,MAAI,CAAC,WAAW,SAAS,EAAE,CACzB,YAAW,KAAK,EAAE;;AAItB,KAAI,WAAW;EACb,MAAM,cAAc,QAAQ,UAAU;AACtC,MAAI,aAAa,MAAM,CACrB,KAAI;AACF,QAAK,uBAAuB,WAAW,EAAE,aAAa,EAAE,EAAE,aAAa,CAAC,CAAC;UACnE;AAIV,OAAK,kCAAkC,WAAW,YAAY,CAAC;YACtD,aAAa,MAAM,CAC5B,MAAK,QAAQ,YAAY,MAAM,CAAC,CAAC;AAGnC,QAAO;;AAGT,SAAgB,kBAAkB,UAAkB,QAA6C;CAE/F,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,GAD5B,+BACiC;AAC5C,IAAG,WAAW,UAAU,SAAS;AACjC,QAAO;;;iBAvGsD;kBACX"}
|
|
@@ -6,6 +6,12 @@ export interface XopcSessionDiskEntry extends Record<string, unknown> {
|
|
|
6
6
|
sessionId: string;
|
|
7
7
|
updatedAt: number;
|
|
8
8
|
sessionStartedAt?: number;
|
|
9
|
+
/** Last user/channel interaction (ms) — extends idle reset lifetime. */
|
|
10
|
+
lastInteractionAt?: number;
|
|
11
|
+
/** Persisted thinking level (OpenClaw SessionEntry parity). */
|
|
12
|
+
thinkingLevel?: string;
|
|
13
|
+
/** Persisted verbose level (OpenClaw SessionEntry parity). */
|
|
14
|
+
verboseLevel?: string;
|
|
9
15
|
sessionFile?: string;
|
|
10
16
|
pluginExtensions?: {
|
|
11
17
|
xopc?: {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
export type SessionResetMode = 'daily' | 'idle';
|
|
3
|
+
export type SessionResetType = 'direct' | 'group' | 'thread';
|
|
4
|
+
export type SessionResetConfig = NonNullable<Config['session']>['reset'];
|
|
5
|
+
export type SessionResetPolicy = {
|
|
6
|
+
mode: SessionResetMode;
|
|
7
|
+
atHour: number;
|
|
8
|
+
idleMinutes?: number;
|
|
9
|
+
configured?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type SessionFreshness = {
|
|
12
|
+
fresh: boolean;
|
|
13
|
+
dailyResetAt?: number;
|
|
14
|
+
idleExpiresAt?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const DEFAULT_RESET_MODE: SessionResetMode;
|
|
17
|
+
export declare const DEFAULT_RESET_AT_HOUR = 4;
|
|
18
|
+
/** Default idle window when mode is `idle` and no explicit minutes are set. */
|
|
19
|
+
export declare const DEFAULT_IDLE_MINUTES = 0;
|
|
20
|
+
export declare function resolveDailyResetAtMs(now: number, atHour: number): number;
|
|
21
|
+
export declare function resolveSessionResetPolicy(params: {
|
|
22
|
+
sessionCfg?: Config['session'];
|
|
23
|
+
resetType: SessionResetType;
|
|
24
|
+
resetOverride?: SessionResetConfig;
|
|
25
|
+
}): SessionResetPolicy;
|
|
26
|
+
export declare function evaluateSessionFreshness(params: {
|
|
27
|
+
updatedAt: number;
|
|
28
|
+
sessionStartedAt?: number;
|
|
29
|
+
lastInteractionAt?: number;
|
|
30
|
+
now: number;
|
|
31
|
+
policy: SessionResetPolicy;
|
|
32
|
+
}): SessionFreshness;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/session/reset-policy.ts
|
|
2
|
+
const DEFAULT_RESET_MODE = "daily";
|
|
3
|
+
const DEFAULT_RESET_AT_HOUR = 4;
|
|
4
|
+
/** Default idle window when mode is `idle` and no explicit minutes are set. */
|
|
5
|
+
const DEFAULT_IDLE_MINUTES = 0;
|
|
6
|
+
function resolveDailyResetAtMs(now, atHour) {
|
|
7
|
+
const normalizedAtHour = normalizeResetAtHour(atHour);
|
|
8
|
+
const resetAt = new Date(now);
|
|
9
|
+
resetAt.setHours(normalizedAtHour, 0, 0, 0);
|
|
10
|
+
if (now < resetAt.getTime()) resetAt.setDate(resetAt.getDate() - 1);
|
|
11
|
+
return resetAt.getTime();
|
|
12
|
+
}
|
|
13
|
+
function resolveSessionResetPolicy(params) {
|
|
14
|
+
const sessionCfg = params.sessionCfg;
|
|
15
|
+
const baseReset = params.resetOverride ?? sessionCfg?.reset;
|
|
16
|
+
const typeReset = params.resetOverride ? void 0 : sessionCfg?.resetByType?.[params.resetType];
|
|
17
|
+
const hasExplicitReset = Boolean(baseReset || sessionCfg?.resetByType);
|
|
18
|
+
const legacyIdleMinutes = params.resetOverride ? void 0 : sessionCfg?.idleMinutes;
|
|
19
|
+
const configured = Boolean(baseReset || typeReset || legacyIdleMinutes != null);
|
|
20
|
+
const mode = typeReset?.mode ?? baseReset?.mode ?? (!hasExplicitReset && legacyIdleMinutes != null ? "idle" : "daily");
|
|
21
|
+
const atHour = normalizeResetAtHour(typeReset?.atHour ?? baseReset?.atHour ?? 4);
|
|
22
|
+
const idleMinutesRaw = typeReset?.idleMinutes ?? baseReset?.idleMinutes ?? legacyIdleMinutes;
|
|
23
|
+
let idleMinutes;
|
|
24
|
+
if (idleMinutesRaw != null) {
|
|
25
|
+
const normalized = Math.floor(idleMinutesRaw);
|
|
26
|
+
if (Number.isFinite(normalized)) idleMinutes = Math.max(normalized, 0);
|
|
27
|
+
} else if (mode === "idle") idleMinutes = 0;
|
|
28
|
+
return {
|
|
29
|
+
mode,
|
|
30
|
+
atHour,
|
|
31
|
+
idleMinutes,
|
|
32
|
+
configured
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function evaluateSessionFreshness(params) {
|
|
36
|
+
const updatedAt = resolveTimestamp(params.updatedAt, params.now) ?? 0;
|
|
37
|
+
const sessionStartedAt = resolveTimestamp(params.sessionStartedAt, params.now) ?? updatedAt;
|
|
38
|
+
const lastInteractionAt = resolveTimestamp(params.lastInteractionAt, params.now) ?? sessionStartedAt;
|
|
39
|
+
const dailyResetAt = params.policy.mode === "daily" ? resolveDailyResetAtMs(params.now, params.policy.atHour) : void 0;
|
|
40
|
+
const idleExpiresAt = params.policy.idleMinutes != null && params.policy.idleMinutes > 0 ? lastInteractionAt + params.policy.idleMinutes * 6e4 : void 0;
|
|
41
|
+
const staleDaily = dailyResetAt != null && sessionStartedAt < dailyResetAt;
|
|
42
|
+
const staleIdle = idleExpiresAt != null && params.now > idleExpiresAt;
|
|
43
|
+
return {
|
|
44
|
+
fresh: !(staleDaily || staleIdle),
|
|
45
|
+
dailyResetAt,
|
|
46
|
+
idleExpiresAt
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function resolveTimestamp(value, now) {
|
|
50
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return;
|
|
51
|
+
if (typeof now === "number" && Number.isFinite(now) && value > now) return;
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function normalizeResetAtHour(value) {
|
|
55
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 4;
|
|
56
|
+
const normalized = Math.floor(value);
|
|
57
|
+
if (!Number.isFinite(normalized)) return 4;
|
|
58
|
+
if (normalized < 0) return 0;
|
|
59
|
+
if (normalized > 23) return 23;
|
|
60
|
+
return normalized;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, evaluateSessionFreshness, resolveDailyResetAtMs, resolveSessionResetPolicy };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=reset-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-policy.js","names":[],"sources":["../../../src/session/reset-policy.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\n\nexport type SessionResetMode = 'daily' | 'idle';\nexport type SessionResetType = 'direct' | 'group' | 'thread';\n\nexport type SessionResetConfig = NonNullable<Config['session']>['reset'];\nexport type SessionResetPolicy = {\n mode: SessionResetMode;\n atHour: number;\n idleMinutes?: number;\n configured?: boolean;\n};\n\nexport type SessionFreshness = {\n fresh: boolean;\n dailyResetAt?: number;\n idleExpiresAt?: number;\n};\n\nexport const DEFAULT_RESET_MODE: SessionResetMode = 'daily';\nexport const DEFAULT_RESET_AT_HOUR = 4;\n/** Default idle window when mode is `idle` and no explicit minutes are set. */\nexport const DEFAULT_IDLE_MINUTES = 0;\n\nexport function resolveDailyResetAtMs(now: number, atHour: number): number {\n const normalizedAtHour = normalizeResetAtHour(atHour);\n const resetAt = new Date(now);\n resetAt.setHours(normalizedAtHour, 0, 0, 0);\n if (now < resetAt.getTime()) {\n resetAt.setDate(resetAt.getDate() - 1);\n }\n return resetAt.getTime();\n}\n\nexport function resolveSessionResetPolicy(params: {\n sessionCfg?: Config['session'];\n resetType: SessionResetType;\n resetOverride?: SessionResetConfig;\n}): SessionResetPolicy {\n const sessionCfg = params.sessionCfg;\n const baseReset = params.resetOverride ?? sessionCfg?.reset;\n const typeReset = params.resetOverride ? undefined : sessionCfg?.resetByType?.[params.resetType];\n const hasExplicitReset = Boolean(baseReset || sessionCfg?.resetByType);\n const legacyIdleMinutes = params.resetOverride ? undefined : sessionCfg?.idleMinutes;\n const configured = Boolean(baseReset || typeReset || legacyIdleMinutes != null);\n const mode =\n typeReset?.mode ??\n baseReset?.mode ??\n (!hasExplicitReset && legacyIdleMinutes != null ? 'idle' : DEFAULT_RESET_MODE);\n const atHour = normalizeResetAtHour(typeReset?.atHour ?? baseReset?.atHour ?? DEFAULT_RESET_AT_HOUR);\n const idleMinutesRaw = typeReset?.idleMinutes ?? baseReset?.idleMinutes ?? legacyIdleMinutes;\n\n let idleMinutes: number | undefined;\n if (idleMinutesRaw != null) {\n const normalized = Math.floor(idleMinutesRaw);\n if (Number.isFinite(normalized)) {\n idleMinutes = Math.max(normalized, 0);\n }\n } else if (mode === 'idle') {\n idleMinutes = DEFAULT_IDLE_MINUTES;\n }\n\n return { mode, atHour, idleMinutes, configured };\n}\n\nexport function evaluateSessionFreshness(params: {\n updatedAt: number;\n sessionStartedAt?: number;\n lastInteractionAt?: number;\n now: number;\n policy: SessionResetPolicy;\n}): SessionFreshness {\n const updatedAt = resolveTimestamp(params.updatedAt, params.now) ?? 0;\n const sessionStartedAt = resolveTimestamp(params.sessionStartedAt, params.now) ?? updatedAt;\n const lastInteractionAt =\n resolveTimestamp(params.lastInteractionAt, params.now) ?? sessionStartedAt;\n const dailyResetAt =\n params.policy.mode === 'daily'\n ? resolveDailyResetAtMs(params.now, params.policy.atHour)\n : undefined;\n const idleExpiresAt =\n params.policy.idleMinutes != null && params.policy.idleMinutes > 0\n ? lastInteractionAt + params.policy.idleMinutes * 60_000\n : undefined;\n const staleDaily = dailyResetAt != null && sessionStartedAt < dailyResetAt;\n const staleIdle = idleExpiresAt != null && params.now > idleExpiresAt;\n return {\n fresh: !(staleDaily || staleIdle),\n dailyResetAt,\n idleExpiresAt,\n };\n}\n\nfunction resolveTimestamp(value: number | undefined, now?: number): number | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n return undefined;\n }\n if (typeof now === 'number' && Number.isFinite(now) && value > now) {\n return undefined;\n }\n return value;\n}\n\nfunction normalizeResetAtHour(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return DEFAULT_RESET_AT_HOUR;\n }\n const normalized = Math.floor(value);\n if (!Number.isFinite(normalized)) {\n return DEFAULT_RESET_AT_HOUR;\n }\n if (normalized < 0) {\n return 0;\n }\n if (normalized > 23) {\n return 23;\n }\n return normalized;\n}\n"],"mappings":";AAmBA,MAAa,qBAAuC;AACpD,MAAa,wBAAwB;;AAErC,MAAa,uBAAuB;AAEpC,SAAgB,sBAAsB,KAAa,QAAwB;CACzE,MAAM,mBAAmB,qBAAqB,OAAO;CACrD,MAAM,UAAU,IAAI,KAAK,IAAI;AAC7B,SAAQ,SAAS,kBAAkB,GAAG,GAAG,EAAE;AAC3C,KAAI,MAAM,QAAQ,SAAS,CACzB,SAAQ,QAAQ,QAAQ,SAAS,GAAG,EAAE;AAExC,QAAO,QAAQ,SAAS;;AAG1B,SAAgB,0BAA0B,QAInB;CACrB,MAAM,aAAa,OAAO;CAC1B,MAAM,YAAY,OAAO,iBAAiB,YAAY;CACtD,MAAM,YAAY,OAAO,gBAAgB,KAAA,IAAY,YAAY,cAAc,OAAO;CACtF,MAAM,mBAAmB,QAAQ,aAAa,YAAY,YAAY;CACtE,MAAM,oBAAoB,OAAO,gBAAgB,KAAA,IAAY,YAAY;CACzE,MAAM,aAAa,QAAQ,aAAa,aAAa,qBAAqB,KAAK;CAC/E,MAAM,OACJ,WAAW,QACX,WAAW,SACV,CAAC,oBAAoB,qBAAqB,OAAO,SAAA;CACpD,MAAM,SAAS,qBAAqB,WAAW,UAAU,WAAW,UAAA,EAAgC;CACpG,MAAM,iBAAiB,WAAW,eAAe,WAAW,eAAe;CAE3E,IAAI;AACJ,KAAI,kBAAkB,MAAM;EAC1B,MAAM,aAAa,KAAK,MAAM,eAAe;AAC7C,MAAI,OAAO,SAAS,WAAW,CAC7B,eAAc,KAAK,IAAI,YAAY,EAAE;YAE9B,SAAS,OAClB,eAAA;AAGF,QAAO;EAAE;EAAM;EAAQ;EAAa;EAAY;;AAGlD,SAAgB,yBAAyB,QAMpB;CACnB,MAAM,YAAY,iBAAiB,OAAO,WAAW,OAAO,IAAI,IAAI;CACpE,MAAM,mBAAmB,iBAAiB,OAAO,kBAAkB,OAAO,IAAI,IAAI;CAClF,MAAM,oBACJ,iBAAiB,OAAO,mBAAmB,OAAO,IAAI,IAAI;CAC5D,MAAM,eACJ,OAAO,OAAO,SAAS,UACnB,sBAAsB,OAAO,KAAK,OAAO,OAAO,OAAO,GACvD,KAAA;CACN,MAAM,gBACJ,OAAO,OAAO,eAAe,QAAQ,OAAO,OAAO,cAAc,IAC7D,oBAAoB,OAAO,OAAO,cAAc,MAChD,KAAA;CACN,MAAM,aAAa,gBAAgB,QAAQ,mBAAmB;CAC9D,MAAM,YAAY,iBAAiB,QAAQ,OAAO,MAAM;AACxD,QAAO;EACL,OAAO,EAAE,cAAc;EACvB;EACA;EACD;;AAGH,SAAS,iBAAiB,OAA2B,KAAkC;AACrF,KAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,MAAM,IAAI,QAAQ,EAClE;AAEF,KAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,IAAI,IAAI,QAAQ,IAC7D;AAEF,QAAO;;AAGT,SAAS,qBAAqB,OAAmC;AAC/D,KAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,MAAM,CACtD,QAAA;CAEF,MAAM,aAAa,KAAK,MAAM,MAAM;AACpC,KAAI,CAAC,OAAO,SAAS,WAAW,CAC9B,QAAA;AAEF,KAAI,aAAa,EACf,QAAO;AAET,KAAI,aAAa,GACf,QAAO;AAET,QAAO"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Default reset triggers (OpenClaw-aligned). */
|
|
2
|
+
export declare const DEFAULT_RESET_TRIGGERS: readonly ["/new", "/reset"];
|
|
3
|
+
export type ResetTriggerMatch = {
|
|
4
|
+
resetTriggered: boolean;
|
|
5
|
+
bodyStripped: string;
|
|
6
|
+
matchedTrigger?: string;
|
|
7
|
+
bareReset: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** Strip gateway/channel envelope prefix e.g. `[Jun 4 17:35] `. */
|
|
10
|
+
export declare function stripLeadingEnvelopeTimestamp(body: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Match configured reset triggers against inbound body (case-insensitive trigger,
|
|
13
|
+
* preserve tail casing). Mirrors OpenClaw `initSessionState` reset-trigger loop.
|
|
14
|
+
*/
|
|
15
|
+
export declare function matchResetTriggers(body: string, triggers: readonly string[]): ResetTriggerMatch;
|
|
16
|
+
export declare function resolveResetTriggers(configured?: string[]): readonly string[];
|
|
17
|
+
export declare function bareResetAckMessage(matchedTrigger?: string): string;
|
|
18
|
+
/** Slash command names that overlap reset triggers — skip when init already reset. */
|
|
19
|
+
export declare const RESET_OVERLAP_COMMANDS: Set<string>;
|
|
20
|
+
export declare function shouldSkipResetOverlapCommand(command: string | undefined, resetTriggered: boolean): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/session/reset-triggers.ts
|
|
2
|
+
/** Default reset triggers (OpenClaw-aligned). */
|
|
3
|
+
const DEFAULT_RESET_TRIGGERS = ["/new", "/reset"];
|
|
4
|
+
/** Strip gateway/channel envelope prefix e.g. `[Jun 4 17:35] `. */
|
|
5
|
+
function stripLeadingEnvelopeTimestamp(body) {
|
|
6
|
+
return body.replace(/^\[[^\]]+\]\s*/, "");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Match configured reset triggers against inbound body (case-insensitive trigger,
|
|
10
|
+
* preserve tail casing). Mirrors OpenClaw `initSessionState` reset-trigger loop.
|
|
11
|
+
*/
|
|
12
|
+
function matchResetTriggers(body, triggers) {
|
|
13
|
+
const normalized = stripLeadingEnvelopeTimestamp(body.trim());
|
|
14
|
+
const lower = normalized.toLowerCase();
|
|
15
|
+
for (const trigger of triggers) {
|
|
16
|
+
const trimmedTrigger = trigger.trim();
|
|
17
|
+
if (!trimmedTrigger) continue;
|
|
18
|
+
const triggerLower = trimmedTrigger.toLowerCase();
|
|
19
|
+
if (lower === triggerLower) return {
|
|
20
|
+
resetTriggered: true,
|
|
21
|
+
bodyStripped: "",
|
|
22
|
+
matchedTrigger: trimmedTrigger,
|
|
23
|
+
bareReset: true
|
|
24
|
+
};
|
|
25
|
+
const prefix = `${triggerLower} `;
|
|
26
|
+
if (lower.startsWith(prefix)) {
|
|
27
|
+
const rest = normalized.slice(trimmedTrigger.length).trimStart();
|
|
28
|
+
return {
|
|
29
|
+
resetTriggered: true,
|
|
30
|
+
bodyStripped: rest,
|
|
31
|
+
matchedTrigger: trimmedTrigger,
|
|
32
|
+
bareReset: rest.length === 0
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
resetTriggered: false,
|
|
38
|
+
bodyStripped: body,
|
|
39
|
+
bareReset: false
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function resolveResetTriggers(configured) {
|
|
43
|
+
if (configured?.length) return configured;
|
|
44
|
+
return DEFAULT_RESET_TRIGGERS;
|
|
45
|
+
}
|
|
46
|
+
function bareResetAckMessage(matchedTrigger) {
|
|
47
|
+
if ((matchedTrigger ?? "").toLowerCase().includes("reset")) return "✅ Session reset.";
|
|
48
|
+
return "✅ New session started.";
|
|
49
|
+
}
|
|
50
|
+
/** Slash command names that overlap reset triggers — skip when init already reset. */
|
|
51
|
+
const RESET_OVERLAP_COMMANDS = new Set([
|
|
52
|
+
"new",
|
|
53
|
+
"reset",
|
|
54
|
+
"restart"
|
|
55
|
+
]);
|
|
56
|
+
function shouldSkipResetOverlapCommand(command, resetTriggered) {
|
|
57
|
+
if (!resetTriggered || !command) return false;
|
|
58
|
+
return RESET_OVERLAP_COMMANDS.has(command.toLowerCase());
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { DEFAULT_RESET_TRIGGERS, RESET_OVERLAP_COMMANDS, bareResetAckMessage, matchResetTriggers, resolveResetTriggers, shouldSkipResetOverlapCommand, stripLeadingEnvelopeTimestamp };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=reset-triggers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-triggers.js","names":[],"sources":["../../../src/session/reset-triggers.ts"],"sourcesContent":["/** Default reset triggers (OpenClaw-aligned). */\nexport const DEFAULT_RESET_TRIGGERS = ['/new', '/reset'] as const;\n\nexport type ResetTriggerMatch = {\n resetTriggered: boolean;\n bodyStripped: string;\n matchedTrigger?: string;\n bareReset: boolean;\n};\n\n/** Strip gateway/channel envelope prefix e.g. `[Jun 4 17:35] `. */\nexport function stripLeadingEnvelopeTimestamp(body: string): string {\n return body.replace(/^\\[[^\\]]+\\]\\s*/, '');\n}\n\n/**\n * Match configured reset triggers against inbound body (case-insensitive trigger,\n * preserve tail casing). Mirrors OpenClaw `initSessionState` reset-trigger loop.\n */\nexport function matchResetTriggers(body: string, triggers: readonly string[]): ResetTriggerMatch {\n const normalized = stripLeadingEnvelopeTimestamp(body.trim());\n const lower = normalized.toLowerCase();\n\n for (const trigger of triggers) {\n const trimmedTrigger = trigger.trim();\n if (!trimmedTrigger) {\n continue;\n }\n const triggerLower = trimmedTrigger.toLowerCase();\n\n if (lower === triggerLower) {\n return {\n resetTriggered: true,\n bodyStripped: '',\n matchedTrigger: trimmedTrigger,\n bareReset: true,\n };\n }\n\n const prefix = `${triggerLower} `;\n if (lower.startsWith(prefix)) {\n const rest = normalized.slice(trimmedTrigger.length).trimStart();\n return {\n resetTriggered: true,\n bodyStripped: rest,\n matchedTrigger: trimmedTrigger,\n bareReset: rest.length === 0,\n };\n }\n }\n\n return {\n resetTriggered: false,\n bodyStripped: body,\n bareReset: false,\n };\n}\n\nexport function resolveResetTriggers(\n configured?: string[],\n): readonly string[] {\n if (configured?.length) {\n return configured;\n }\n return DEFAULT_RESET_TRIGGERS;\n}\n\nexport function bareResetAckMessage(matchedTrigger?: string): string {\n const lower = (matchedTrigger ?? '').toLowerCase();\n if (lower.includes('reset')) {\n return '✅ Session reset.';\n }\n return '✅ New session started.';\n}\n\n/** Slash command names that overlap reset triggers — skip when init already reset. */\nexport const RESET_OVERLAP_COMMANDS = new Set(['new', 'reset', 'restart']);\n\nexport function shouldSkipResetOverlapCommand(\n command: string | undefined,\n resetTriggered: boolean,\n): boolean {\n if (!resetTriggered || !command) {\n return false;\n }\n return RESET_OVERLAP_COMMANDS.has(command.toLowerCase());\n}\n"],"mappings":";;AACA,MAAa,yBAAyB,CAAC,QAAQ,SAAS;;AAUxD,SAAgB,8BAA8B,MAAsB;AAClE,QAAO,KAAK,QAAQ,kBAAkB,GAAG;;;;;;AAO3C,SAAgB,mBAAmB,MAAc,UAAgD;CAC/F,MAAM,aAAa,8BAA8B,KAAK,MAAM,CAAC;CAC7D,MAAM,QAAQ,WAAW,aAAa;AAEtC,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,iBAAiB,QAAQ,MAAM;AACrC,MAAI,CAAC,eACH;EAEF,MAAM,eAAe,eAAe,aAAa;AAEjD,MAAI,UAAU,aACZ,QAAO;GACL,gBAAgB;GAChB,cAAc;GACd,gBAAgB;GAChB,WAAW;GACZ;EAGH,MAAM,SAAS,GAAG,aAAa;AAC/B,MAAI,MAAM,WAAW,OAAO,EAAE;GAC5B,MAAM,OAAO,WAAW,MAAM,eAAe,OAAO,CAAC,WAAW;AAChE,UAAO;IACL,gBAAgB;IAChB,cAAc;IACd,gBAAgB;IAChB,WAAW,KAAK,WAAW;IAC5B;;;AAIL,QAAO;EACL,gBAAgB;EAChB,cAAc;EACd,WAAW;EACZ;;AAGH,SAAgB,qBACd,YACmB;AACnB,KAAI,YAAY,OACd,QAAO;AAET,QAAO;;AAGT,SAAgB,oBAAoB,gBAAiC;AAEnE,MADe,kBAAkB,IAAI,aAC5B,CAAC,SAAS,QAAQ,CACzB,QAAO;AAET,QAAO;;;AAIT,MAAa,yBAAyB,IAAI,IAAI;CAAC;CAAO;CAAS;CAAU,CAAC;AAE1E,SAAgB,8BACd,SACA,gBACS;AACT,KAAI,CAAC,kBAAkB,CAAC,QACtB,QAAO;AAET,QAAO,uBAAuB,IAAI,QAAQ,aAAa,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
import type { SessionResetConfig, SessionResetType } from './reset-policy.js';
|
|
3
|
+
export declare function isThreadSessionKey(sessionKey?: string | null): boolean;
|
|
4
|
+
export declare function resolveSessionResetType(params: {
|
|
5
|
+
sessionKey?: string | null;
|
|
6
|
+
isGroup?: boolean;
|
|
7
|
+
isThread?: boolean;
|
|
8
|
+
}): SessionResetType;
|
|
9
|
+
export declare function resolveChannelResetConfig(params: {
|
|
10
|
+
sessionCfg?: Config['session'];
|
|
11
|
+
channel?: string | null;
|
|
12
|
+
}): SessionResetConfig | undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { init_string_coerce, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "../utils/string-coerce.js";
|
|
2
|
+
//#region src/session/reset-type.ts
|
|
3
|
+
init_string_coerce();
|
|
4
|
+
const GROUP_SESSION_MARKERS = [":group:", ":channel:"];
|
|
5
|
+
function isThreadSessionKey(sessionKey) {
|
|
6
|
+
return (sessionKey ?? "").trim().includes(":thread:");
|
|
7
|
+
}
|
|
8
|
+
function resolveSessionResetType(params) {
|
|
9
|
+
if (params.isThread || isThreadSessionKey(params.sessionKey)) return "thread";
|
|
10
|
+
if (params.isGroup) return "group";
|
|
11
|
+
const normalized = normalizeLowercaseStringOrEmpty(params.sessionKey);
|
|
12
|
+
if (GROUP_SESSION_MARKERS.some((marker) => normalized.includes(marker))) return "group";
|
|
13
|
+
return "direct";
|
|
14
|
+
}
|
|
15
|
+
function resolveChannelResetConfig(params) {
|
|
16
|
+
const resetByChannel = params.sessionCfg?.resetByChannel;
|
|
17
|
+
if (!resetByChannel) return;
|
|
18
|
+
const key = normalizeOptionalLowercaseString(params.channel);
|
|
19
|
+
if (!key) return;
|
|
20
|
+
return resetByChannel[key];
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { isThreadSessionKey, resolveChannelResetConfig, resolveSessionResetType };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=reset-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-type.js","names":[],"sources":["../../../src/session/reset-type.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from '../utils/string-coerce.js';\n\nimport type { SessionResetConfig, SessionResetType } from './reset-policy.js';\n\nconst GROUP_SESSION_MARKERS = [':group:', ':channel:'];\n\nexport function isThreadSessionKey(sessionKey?: string | null): boolean {\n const raw = (sessionKey ?? '').trim();\n return raw.includes(':thread:');\n}\n\nexport function resolveSessionResetType(params: {\n sessionKey?: string | null;\n isGroup?: boolean;\n isThread?: boolean;\n}): SessionResetType {\n if (params.isThread || isThreadSessionKey(params.sessionKey)) {\n return 'thread';\n }\n if (params.isGroup) {\n return 'group';\n }\n const normalized = normalizeLowercaseStringOrEmpty(params.sessionKey);\n if (GROUP_SESSION_MARKERS.some((marker) => normalized.includes(marker))) {\n return 'group';\n }\n return 'direct';\n}\n\nexport function resolveChannelResetConfig(params: {\n sessionCfg?: Config['session'];\n channel?: string | null;\n}): SessionResetConfig | undefined {\n const resetByChannel = params.sessionCfg?.resetByChannel;\n if (!resetByChannel) {\n return undefined;\n }\n const key = normalizeOptionalLowercaseString(params.channel);\n if (!key) {\n return undefined;\n }\n return resetByChannel[key];\n}\n"],"mappings":";;oBAC8G;AAI9G,MAAM,wBAAwB,CAAC,WAAW,YAAY;AAEtD,SAAgB,mBAAmB,YAAqC;AAEtE,SADa,cAAc,IAAI,MACrB,CAAC,SAAS,WAAW;;AAGjC,SAAgB,wBAAwB,QAInB;AACnB,KAAI,OAAO,YAAY,mBAAmB,OAAO,WAAW,CAC1D,QAAO;AAET,KAAI,OAAO,QACT,QAAO;CAET,MAAM,aAAa,gCAAgC,OAAO,WAAW;AACrE,KAAI,sBAAsB,MAAM,WAAW,WAAW,SAAS,OAAO,CAAC,CACrE,QAAO;AAET,QAAO;;AAGT,SAAgB,0BAA0B,QAGP;CACjC,MAAM,iBAAiB,OAAO,YAAY;AAC1C,KAAI,CAAC,eACH;CAEF,MAAM,MAAM,iCAAiC,OAAO,QAAQ;AAC5D,KAAI,CAAC,IACH;AAEF,QAAO,eAAe"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Config } from '../config/schema.js';
|
|
2
|
+
import { type ThinkLevel, type VerboseLevel } from '../agent/transcript/thinking-types.js';
|
|
3
|
+
import type { XopcSessionDiskEntry } from './parity/xopc-session-disk-entry.js';
|
|
4
|
+
export type SessionResolution = {
|
|
5
|
+
sessionId: string;
|
|
6
|
+
sessionKey?: string;
|
|
7
|
+
sessionEntry?: XopcSessionDiskEntry;
|
|
8
|
+
sessionStore: Record<string, XopcSessionDiskEntry>;
|
|
9
|
+
storePath: string;
|
|
10
|
+
isNewSession: boolean;
|
|
11
|
+
persistedThinking?: ThinkLevel;
|
|
12
|
+
persistedVerbose?: VerboseLevel;
|
|
13
|
+
};
|
|
14
|
+
export type SessionKeyResolution = {
|
|
15
|
+
sessionKey?: string;
|
|
16
|
+
sessionStore: Record<string, XopcSessionDiskEntry>;
|
|
17
|
+
storePath: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function resolveSessionKeyForRequest(opts: {
|
|
20
|
+
cfg: Config;
|
|
21
|
+
sessionKey?: string;
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
agentId?: string;
|
|
24
|
+
}): Promise<SessionKeyResolution>;
|
|
25
|
+
export declare function resolveSession(opts: {
|
|
26
|
+
cfg: Config;
|
|
27
|
+
sessionKey?: string;
|
|
28
|
+
sessionId?: string;
|
|
29
|
+
agentId?: string;
|
|
30
|
+
}): Promise<SessionResolution>;
|