@xopcai/xopc 0.0.8 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/gateway/static/root/assets/{agents-BSNzJWbQ.js → agents-BdC4Y-HX.js} +2 -2
- package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js → apps-page-C-oaSHkm.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js.map → apps-page-C-oaSHkm.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js → channels-settings-BqEUppPO.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js.map → channels-settings-BqEUppPO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js → chat-agents-api-BhqjQ7iL.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js → cron-page-Cli49RKR.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js.map → cron-page-Cli49RKR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js → cron-utils-Dkj-Ldpf.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js → electron-env-BDtJw9AY.js} +2 -2
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js.map → electron-env-BDtJw9AY.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js → extension-debug-page-BMcZlaxF.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js → extension-iframe-host-D5HEF0KR.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js → extension-page-CXdCSSPl.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js.map → extension-page-CXdCSSPl.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js → extension-provider-DZCZgQE2.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js → extension-settings-page-CX6STpx3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js → gateway-config-swr-Cph02QZn.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
- package/dist/gateway/static/root/assets/{index-BXUJbteW.js → index-iTUyfzNr.js} +10 -10
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js → logs-page-B9O5l3I8.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js.map → logs-page-B9O5l3I8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js → model-selector-BLiY_O25.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js.map → model-selector-BLiY_O25.js.map} +1 -1
- package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-DJHD9Ean.js.map → page-header-store-BFpnFTed.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js → session-api-DEhQXWJg.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js.map → session-api-DEhQXWJg.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js → session-working-directory-control-DKOtWs3-.js} +2 -2
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js → sessions-page-BYlWP1ep.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B3QrJm-E.js → settings-page-oCnIavdg.js} +2 -2
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js → skill-api-DWrn8Az0.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js.map → skill-api-DWrn8Az0.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js → skills-page-C59WQpM1.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js.map → skills-page-C59WQpM1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js → theme-store-CywXkKml.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js.map → theme-store-CywXkKml.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
- package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js → useTranslation-CACj0DBJ.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
- package/dist/gateway/static/root/index.html +15 -15
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +20 -12
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +2 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/child-agent-factory.js +2 -0
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
- package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
- package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +38 -11
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +13 -0
- package/dist/src/agent/messaging/command-handler.js +14 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
- package/dist/src/agent/service.d.ts +16 -1
- package/dist/src/agent/service.js +178 -20
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/index.d.ts +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/pipeline.d.ts +8 -1
- package/dist/src/channels/pipeline.js +49 -4
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.d.ts +4 -0
- package/dist/src/chat-commands/builtins/config.js +197 -0
- package/dist/src/chat-commands/builtins/config.js.map +1 -0
- package/dist/src/chat-commands/builtins/context.d.ts +4 -0
- package/dist/src/chat-commands/builtins/context.js +44 -0
- package/dist/src/chat-commands/builtins/context.js.map +1 -0
- package/dist/src/chat-commands/builtins/session.js +111 -0
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +49 -21
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.d.ts +10 -0
- package/dist/src/chat-commands/config-paths.js +45 -0
- package/dist/src/chat-commands/config-paths.js.map +1 -0
- package/dist/src/chat-commands/config-value.d.ts +12 -0
- package/dist/src/chat-commands/config-value.js +53 -0
- package/dist/src/chat-commands/config-value.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +24 -1
- package/dist/src/chat-commands/context.js +41 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +2 -0
- package/dist/src/chat-commands/index.js +5 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +33 -1
- package/dist/src/cli/commands/agent/interactive.js +1 -1
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent.js +22 -10
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/extension.js +10 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/init.js +3 -4
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts +1 -0
- package/dist/src/cli/commands/update.js +171 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -1
- package/dist/src/cli/index.js +3 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +5 -4
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/runtime-overrides.d.ts +8 -0
- package/dist/src/config/runtime-overrides.js +40 -0
- package/dist/src/config/runtime-overrides.js.map +1 -0
- package/dist/src/config/schema.d.ts +35 -0
- package/dist/src/config/schema.js +19 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +1 -1
- package/dist/src/extensions/loader.js +6 -9
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +6 -1
- package/dist/src/extensions/sdk/index.js.map +1 -0
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +64 -11
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.d.ts +3 -0
- package/dist/src/gateway/hono/routes/update.js +141 -0
- package/dist/src/gateway/hono/routes/update.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +84 -4
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +16 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
- package/dist/src/gateway/workspace-fs-file-list.js +56 -0
- package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
- package/dist/src/gateway/workspace-ripgrep.js +88 -4
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/infra/update-channels.d.ts +14 -0
- package/dist/src/infra/update-channels.js +30 -0
- package/dist/src/infra/update-channels.js.map +1 -0
- package/dist/src/infra/update-check.d.ts +53 -0
- package/dist/src/infra/update-check.js +155 -0
- package/dist/src/infra/update-check.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +18 -0
- package/dist/src/infra/update-runner.js +112 -0
- package/dist/src/infra/update-runner.js.map +1 -0
- package/dist/src/infra/update-startup.d.ts +20 -0
- package/dist/src/infra/update-startup.js +246 -0
- package/dist/src/infra/update-startup.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
- package/dist/src/providers/extension-stream-bridge.js +239 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -0
- package/dist/src/providers/index.d.ts +7 -2
- package/dist/src/providers/index.js +77 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/plugin-registry.js +92 -87
- package/dist/src/providers/plugin-registry.js.map +1 -1
- package/dist/src/session/chat-export.d.ts +5 -0
- package/dist/src/session/chat-export.js +35 -0
- package/dist/src/session/chat-export.js.map +1 -0
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.d.ts +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BSNzJWbQ.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +0 -1
- package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +0 -1
- package/dist/gateway/static/root/assets/index-CQLMxWSA.css +0 -2
- package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +0 -1
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js +0 -2
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +0 -1
|
@@ -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 { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionManager');\n\nexport interface SessionManagerConfig {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionManager extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionManagerConfig) {\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(key: string): Promise<SessionDetail | null> {\n const session = await this.store.get(key);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\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 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 /** Save messages for a session key */\n async saveMessages(key: string, messages: any[]) {\n return this.store.saveMessages(key, messages);\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(key: string, messages: any[], contextWindow: number, instructions?: string): Promise<CompactionResult> {\n return this.store.compact(key, messages, contextWindow, instructions);\n }\n\n /** Compaction stats for a session */\n async getCompactionStats(key: string) {\n return this.store.getCompactionStats(key);\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 { WindowConfig } from '../agent/memory/window.js';\nimport type { Config } from '../config/schema.js';\n\nconst log = createLogger('SessionManager');\n\nexport interface SessionManagerConfig {\n config: Config;\n agentId?: string;\n sessionsDir?: string;\n windowConfig?: Partial<WindowConfig>;\n compactionConfig?: Partial<CompactionConfig>;\n}\n\nexport class SessionManager extends EventEmitter {\n private store: SessionStore;\n\n constructor(config: SessionManagerConfig) {\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(key: string): Promise<SessionDetail | null> {\n const session = await this.store.get(key);\n if (session) {\n this.emit('sessionAccessed', { key });\n }\n return session;\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 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 /** Save messages for a session key */\n async saveMessages(key: string, messages: any[]) {\n return this.store.saveMessages(key, messages);\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 /** 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,6 +1,6 @@
|
|
|
1
|
-
import { init_session_key, isCronSessionKey, parseSessionKey } from "../routing/session-key.js";
|
|
2
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { init_session_key, isCronSessionKey, parseSessionKey } from "../routing/session-key.js";
|
|
4
4
|
import { init_providers, resolveModel } from "../providers/index.js";
|
|
5
5
|
import { stripInboundFileMetadataFromText } from "../channels/attachments/inbound-persist.js";
|
|
6
6
|
import { complete } from "@mariozechner/pi-ai";
|
|
@@ -118,7 +118,7 @@ export declare class SessionStore {
|
|
|
118
118
|
/**
|
|
119
119
|
* Compact session with LLM summary
|
|
120
120
|
*/
|
|
121
|
-
compact(key: string, messages: AgentMessage[], contextWindow: number, instructions?: string): Promise<CompactionResult>;
|
|
121
|
+
compact(key: string, messages: AgentMessage[], contextWindow: number, instructions?: string, force?: boolean): Promise<CompactionResult>;
|
|
122
122
|
/**
|
|
123
123
|
* Get compaction stats for a session
|
|
124
124
|
*/
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
2
|
-
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
3
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
4
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { init_agent_scope, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
5
4
|
import { FILENAMES, init_paths, resolveSessionsDir } from "../config/paths.js";
|
|
5
|
+
import { init_session_key, parseSessionKey } from "../routing/session-key.js";
|
|
6
6
|
import { invalidateSessionSearchIndexCache } from "./search-index-cache.js";
|
|
7
7
|
import { resolveSessionShardRelativePath } from "./shard-path.js";
|
|
8
8
|
import { SessionStatus } from "./types.js";
|
|
9
9
|
import { SessionCompactor } from "../agent/memory/compaction.js";
|
|
10
10
|
import { SlidingWindow } from "../agent/memory/window.js";
|
|
11
11
|
import { cleanTrailingErrors, hasProblematicMessages } from "../agent/memory/message-sanitizer.js";
|
|
12
|
+
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
12
13
|
import { basename, join } from "path";
|
|
13
14
|
import { existsSync } from "fs";
|
|
14
|
-
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
15
15
|
//#region src/session/store.ts
|
|
16
16
|
init_paths();
|
|
17
17
|
init_agent_scope();
|
|
@@ -525,8 +525,8 @@ var SessionStore = class {
|
|
|
525
525
|
/**
|
|
526
526
|
* Compact session with LLM summary
|
|
527
527
|
*/
|
|
528
|
-
async compact(key, messages, contextWindow, instructions) {
|
|
529
|
-
const result = await this.compactor.compact(messages, instructions);
|
|
528
|
+
async compact(key, messages, contextWindow, instructions, force) {
|
|
529
|
+
const result = await this.compactor.compact(messages, instructions, force);
|
|
530
530
|
if (result.compacted) await this.applyCompaction(key, messages, result);
|
|
531
531
|
return result;
|
|
532
532
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["// Session store - manages session persistence, indexing, compaction, and sliding window\n\nimport { readFile, writeFile, mkdir, unlink, readdir, stat } from 'fs/promises';\nimport { basename, join } from 'path';\nimport { existsSync } from 'fs';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionShardRelativePath } from './shard-path.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { createLogger } from '../utils/logger.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionIndex,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { cleanTrailingErrors, hasProblematicMessages } from '../agent/memory/message-sanitizer.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DEFAULT_LIMIT = 50;\n\n/**\n * Session files live under `resolveSessionsDir(config, agentId)` (ADR-003), sharded by\n * `resolveSessionShardRelativePath(sessionKey)` (users/… vs system/heartbeat; web UI uses\n * compact `users/{agent}/web/{peerId}` for gateway/webchat direct sessions).\n */\nexport interface SessionStoreOptions {\n /** Loaded app config (required for session path resolution). */\n config: Config;\n /** Agent id for the session store root (default: configured default agent). */\n agentId?: string;\n /** Override storage root (tests); skips `resolveSessionsDir` */\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private indexFile: string;\n private indexCache: SessionIndex | null = null;\n private indexCacheTime: number = 0;\n private indexDirty = false;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n\n constructor(\n options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.indexFile = join(this.sessionsDir, FILENAMES.SESSIONS_INDEX);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n /** Root directory of session JSON files (sharded). Used by `session_search` indexing. */\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n // ========== Initialization ==========\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n\n if (!existsSync(this.indexFile)) {\n await this.rebuildIndex();\n } else {\n await this.loadIndex();\n }\n\n log.debug('Session store initialized');\n }\n\n private sessionPathsForKey(key: string): { dir: string; jsonPath: string; metaPath: string } {\n const safeKey = this.sanitizeKey(key);\n const shard = resolveSessionShardRelativePath(key);\n const dir = join(this.sessionsDir, shard);\n return {\n dir,\n jsonPath: join(dir, `${safeKey}.json`),\n metaPath: join(dir, `${safeKey}.meta.json`),\n };\n }\n\n // ========== Index Management ==========\n\n /**\n * Get sessions by agent ID\n */\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.agentId?.toLowerCase() === agentId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by account ID\n */\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.accountId?.toLowerCase() === accountId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by peer\n */\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) =>\n s.routing?.peerKind?.toLowerCase() === peerKind.toLowerCase() &&\n s.routing?.peerId?.toLowerCase() === peerId.toLowerCase()\n );\n }\n\n /**\n * Get main session for a DM conversation\n */\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n return (\n (index.sessions || []).find(\n (s) =>\n s.routing?.source?.toLowerCase() === channel.toLowerCase() &&\n s.routing?.accountId?.toLowerCase() === accountId.toLowerCase() &&\n s.routing?.peerKind?.toLowerCase() === 'dm' &&\n s.routing?.peerId === 'main'\n ) ?? null\n );\n }\n\n private async loadIndex(): Promise<SessionIndex> {\n try {\n // Check if index file has been modified\n const stats = await stat(this.indexFile);\n const mtime = stats.mtime.getTime();\n\n // If cache is valid and file hasn't changed, use cache\n if (this.indexCache && mtime <= this.indexCacheTime) {\n // Ensure sessions array exists\n if (!this.indexCache.sessions) {\n this.indexCache.sessions = [];\n }\n return this.indexCache;\n }\n\n // File has changed or cache is empty, reload\n const data = await readFile(this.indexFile, 'utf-8');\n const parsed = JSON.parse(data) as SessionIndex;\n // Ensure sessions array exists\n if (!parsed.sessions) {\n parsed.sessions = [];\n }\n this.indexCache = parsed;\n this.indexCacheTime = mtime;\n return this.indexCache;\n } catch {\n // Index corrupted or missing, rebuild\n return this.rebuildIndex();\n }\n }\n\n /**\n * Force refresh the index cache from disk\n */\n async refreshIndex(): Promise<void> {\n this.indexCache = null;\n this.indexCacheTime = 0;\n await this.loadIndex();\n }\n\n private async saveIndex(): Promise<void> {\n if (!this.indexCache) return;\n\n this.indexCache.lastUpdated = new Date().toISOString();\n await writeFile(this.indexFile, JSON.stringify(this.indexCache, null, 2));\n this.indexDirty = false;\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n }\n\n private async rebuildIndex(): Promise<SessionIndex> {\n log.info('Rebuilding session index...');\n\n const sessions: SessionMetadata[] = [];\n\n // Scan sessions directory\n const files = await this.scanSessionFiles();\n\n for (const file of files) {\n if (file.endsWith('.json') && !file.endsWith('.meta.json')) {\n const stem = basename(file, '.json');\n const key = this.fileNameToKey(stem);\n try {\n const metadata = await this.scanSessionFile(key);\n if (metadata) {\n sessions.push(metadata);\n }\n } catch (err) {\n log.warn({ key, err }, 'Failed to scan session file');\n }\n }\n }\n\n this.indexCache = {\n version: INDEX_VERSION,\n lastUpdated: new Date().toISOString(),\n sessions,\n };\n\n await this.saveIndex();\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n \n log.info({ count: sessions.length }, 'Session index rebuilt');\n\n return this.indexCache;\n }\n\n private async scanSessionFiles(): Promise<string[]> {\n const out: string[] = [];\n const walk = async (rel: string): Promise<void> => {\n const abs = join(this.sessionsDir, rel);\n let entries;\n try {\n entries = await readdir(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const ent of entries) {\n const childRel = rel ? join(rel, ent.name) : ent.name;\n if (ent.isDirectory()) {\n if (ent.name === 'archive') continue;\n await walk(childRel);\n } else if (\n ent.name.endsWith('.json') &&\n ent.name !== FILENAMES.SESSIONS_INDEX &&\n !ent.name.endsWith('.meta.json')\n ) {\n out.push(childRel);\n }\n }\n };\n await walk('');\n return out;\n }\n\n private async scanSessionFile(key: string): Promise<SessionMetadata | null> {\n const messages = await this.loadMessages(key);\n if (messages.length === 0) return null;\n\n const { jsonPath } = this.sessionPathsForKey(key);\n const stats = await stat(jsonPath);\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n return {\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: stats.birthtime.toISOString(),\n updatedAt: stats.mtime.toISOString(),\n lastAccessedAt: stats.mtime.toISOString(),\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n },\n };\n }\n\n /**\n * Extract routing metadata from session key\n */\n private extractRoutingFromKey(key: string, channel: string): SessionMetadata['routing'] {\n const parts = key.split(':');\n if (parts.length < 5) {\n return undefined;\n }\n\n const [agentId, source, accountId, peerKind, peerId, ...rest] = parts;\n \n let threadId: string | undefined;\n let scopeId: string | undefined;\n \n // Parse optional thread and scope\n for (let i = 0; i < rest.length; i++) {\n if (rest[i] === 'thread' && rest[i + 1]) {\n threadId = rest[i + 1];\n i++;\n } else if (rest[i] === 'scope' && rest[i + 1]) {\n scopeId = rest[i + 1];\n i++;\n }\n }\n\n return {\n agentId: agentId?.toLowerCase() || 'main',\n source: source?.toLowerCase() || channel,\n accountId: accountId?.toLowerCase() || 'default',\n peerKind: peerKind?.toLowerCase() || 'dm',\n peerId: peerId?.toLowerCase() || 'unknown',\n threadId,\n scopeId,\n };\n }\n\n // ========== CRUD Operations ==========\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const index = await this.loadIndex();\n let sessions = [...(index.sessions || [])];\n\n // Apply filters\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n\n if (query.channel) {\n const channels = query.channel\n .split(',')\n .map((c) => c.trim())\n .filter(Boolean);\n if (channels.length === 0) {\n sessions = [];\n } else if (channels.length === 1) {\n sessions = sessions.filter((s) => s.sourceChannel === channels[0]);\n } else {\n sessions = sessions.filter((s) => channels.includes(s.sourceChannel));\n }\n }\n\n if (query.tags && query.tags.length > 0) {\n sessions = sessions.filter((s) => query.tags!.some((tag) => s.tags.includes(tag)));\n }\n\n if (query.search) {\n const searchLower = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(searchLower) ||\n s.name?.toLowerCase().includes(searchLower) ||\n s.tags.some((t) => t.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply sorting\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n\n sessions.sort((a, b) => {\n const aVal = a[sortBy];\n const bVal = b[sortBy];\n const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;\n return sortOrder === 'asc' ? comparison : -comparison;\n });\n\n // Apply pagination\n const total = sessions.length;\n const limit = query.limit || DEFAULT_LIMIT;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n }\n\n async get(key: string): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) return null;\n\n const messages = await this.loadMessages(key);\n\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n };\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n const metadata = index.sessions.find((s) => s.key === key);\n\n if (!metadata) {\n // Try to load from file directly (orphaned session)\n const scanned = await this.scanSessionFile(key);\n if (scanned) {\n index.sessions.push(scanned);\n this.indexDirty = true;\n return scanned;\n }\n return null;\n }\n\n return metadata;\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n if (idx === -1) {\n throw new Error(`Session not found: ${key}`);\n }\n\n index.sessions[idx] = {\n ...index.sessions[idx],\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n this.indexDirty = true;\n await this.saveIndex();\n\n log.debug({ key, updates }, 'Session metadata updated');\n }\n\n async delete(key: string): Promise<boolean> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n const primary = this.sessionPathsForKey(key);\n\n for (const p of [primary.jsonPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n for (const p of [primary.metaPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n // Remove from index\n if (idx !== -1) {\n index.sessions.splice(idx, 1);\n this.indexDirty = true;\n await this.saveIndex();\n }\n\n log.info({ key }, 'Session deleted');\n return true;\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n for (const key of keys) {\n try {\n await this.delete(key);\n success.push(key);\n } catch {\n failed.push(key);\n }\n }\n\n return { success, failed };\n }\n\n // ========== Status Operations ==========\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n // ========== Message Operations ==========\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const primary = this.sessionPathsForKey(key);\n\n const readAndNormalize = async (path: string): Promise<AgentMessage[] | null> => {\n try {\n const data = await readFile(path, 'utf-8');\n const messages = JSON.parse(data) as AgentMessage[];\n if (hasProblematicMessages(messages)) {\n const cleaned = cleanTrailingErrors(messages);\n if (cleaned.length !== messages.length) {\n log.info(\n { key, original: messages.length, cleaned: cleaned.length },\n 'Cleaned problematic messages on load'\n );\n }\n return cleaned;\n }\n return messages;\n } catch {\n return null;\n }\n };\n\n const messages = await readAndNormalize(primary.jsonPath);\n\n if (messages !== null) {\n return messages;\n }\n\n if (options?.fromArchive) {\n const archivedFile = await this.findMostRecentArchive(key);\n if (!archivedFile) {\n return [];\n }\n const archived = await readAndNormalize(archivedFile);\n return archived ?? [];\n }\n return [];\n }\n\n /**\n * Find the most recent archived session file for a given key.\n * Archived files have format: {safeKey}.{timestamp}.json\n */\n private async findMostRecentArchive(sessionKey: string): Promise<string | null> {\n const safeKey = this.sanitizeKey(sessionKey);\n const shardDir = join(this.archiveDir, resolveSessionShardRelativePath(sessionKey));\n\n const scanDir = async (dir: string): Promise<string | null> => {\n try {\n const files = await readdir(dir);\n const matchingFiles = files\n .filter((f) => f.startsWith(`${safeKey}.`) && f.endsWith('.json') && !f.endsWith('.meta.json'))\n .sort()\n .reverse();\n if (matchingFiles.length === 0) return null;\n return join(dir, matchingFiles[0]);\n } catch {\n return null;\n }\n };\n\n const inShard = await scanDir(shardDir);\n if (inShard) return inShard;\n return await scanDir(this.archiveDir);\n }\n\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n const { dir, jsonPath } = this.sessionPathsForKey(key);\n\n await mkdir(dir, { recursive: true });\n await writeFile(jsonPath, JSON.stringify(messages, null, 2));\n\n // Update or create metadata\n const index = await this.loadIndex();\n const existingIdx = index.sessions.findIndex((s) => s.key === key);\n const now = new Date().toISOString();\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n if (existingIdx !== -1) {\n const prev = index.sessions[existingIdx];\n index.sessions[existingIdx] = {\n ...prev,\n sourceChannel: channel,\n sourceChatId: chatId,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n updatedAt: now,\n lastAccessedAt: now,\n routing: routing || prev.routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: {\n ...prev.customData,\n cronJobId: chatId,\n },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: {\n ...prev.customData,\n heartbeatTarget: chatId,\n },\n }\n : {}),\n stats: {\n ...prev.stats,\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n };\n } else {\n index.sessions.push({\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n });\n }\n\n this.indexDirty = true;\n await this.saveIndex();\n\n invalidateSessionSearchIndexCache();\n }\n\n // ========== Sliding Window & Compaction ==========\n\n /**\n * Get window stats for messages\n */\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n /**\n * Check if session needs compaction\n */\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n /**\n * Prepare compaction (check if needed)\n */\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return {\n needsCompaction: result.needed,\n messages,\n stats: result,\n };\n }\n\n /**\n * Apply compaction result to messages\n */\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n \n // Persist the compacted messages to disk so subsequent loads see the reduced context\n await this.saveMessages(key, compacted);\n \n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, {\n compactedCount: metadata.compactedCount + 1,\n });\n }\n \n log.info({\n key,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n keptMessages: compacted.length,\n }, 'Session compacted');\n \n return compacted;\n }\n\n /**\n * Compact session with LLM summary\n */\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions);\n \n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n \n return result;\n }\n\n /**\n * Get compaction stats for a session\n */\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) return undefined;\n \n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n // ========== MemoryStore API Aliases ==========\n\n /** Alias for delete */\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n /** Alias for loadMessages */\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n /** Alias for saveMessages */\n async save(key: string, messages: AgentMessage[]): Promise<void> {\n return this.saveMessages(key, messages);\n }\n\n /** Alias for estimateTokens */\n async estimateTokenUsage(key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n // ========== Search ==========\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadMessages(key);\n const keywordLower = keyword.toLowerCase();\n\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(m.content);\n return content.toLowerCase().includes(keywordLower);\n })\n );\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n\n if (format === 'json') {\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n };\n return JSON.stringify(exportData, null, 2);\n } else {\n // Markdown format\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`);\n lines.push('');\n const body =\n typeof msg.content === 'string'\n ? msg.content\n : JSON.stringify(msg.content, null, 2);\n lines.push(body);\n lines.push('');\n lines.push('---');\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n const index = await this.loadIndex();\n const sessions = index.sessions;\n\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.sourceChannel] || 0) + 1;\n }\n\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0].createdAt;\n newestSession = sorted[sorted.length - 1].createdAt;\n }\n\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n }\n\n // ========== Cleanup ==========\n\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n\n const index = await this.loadIndex();\n let archived = 0;\n\n for (const session of index.sessions) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n\n return archived;\n }\n\n // ========== Helper Methods ==========\n\n private sanitizeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n\n private fileNameToKey(fileName: string): string {\n // Reverse of sanitizeKey - restore all colons from underscores\n // telegram_dm_123456 -> telegram:dm:123456\n // telegram_g_-100123456_t_789 -> telegram:g:-100123456:t:789\n return fileName.replace(/_/g, ':');\n }\n\n private parseSessionKey(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n // Session key format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}\n if (parts.length >= 5) {\n const parsed = parseRoutingSessionKey(key);\n if (parsed?.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return { channel: parts[1], chatId: parts.slice(2).join(':') };\n }\n // Gateway heartbeat: `heartbeat:main` / `heartbeat:isolated:<ts>`\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n return { channel: 'unknown', chatId: key };\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n // Rough estimate: 1 token ≈ 4 characters\n let total = 0;\n for (const msg of messages) {\n const text = this.extractTextContent(msg.content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) continue;\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: any) => {\n const c = m.content;\n const content: string | unknown[] =\n typeof c === 'string'\n ? c\n : Array.isArray(c)\n ? c\n : this.extractTextContent(c);\n\n const row: Message = {\n role: m.role as 'system' | 'user' | 'assistant' | 'tool' | 'toolResult',\n content,\n timestamp: m.timestamp ? new Date(m.timestamp).toISOString() : undefined,\n tool_call_id: m.tool_call_id || m.toolCallId,\n tool_calls: m.tool_calls,\n name: m.name,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments;\n }\n return row;\n });\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const safeKey = this.sanitizeKey(key);\n const primary = this.sessionPathsForKey(key);\n const sourcePath = existsSync(primary.jsonPath) ? primary.jsonPath : null;\n if (!sourcePath) {\n return;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const archiveShard = join(this.archiveDir, resolveSessionShardRelativePath(key));\n await mkdir(archiveShard, { recursive: true });\n const targetPath = join(archiveShard, `${safeKey}.${timestamp}.json`);\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = primary.metaPath;\n const metaTarget = join(archiveShard, `${safeKey}.${timestamp}.meta.json`);\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const sourcePath = await this.findMostRecentArchive(key);\n if (!sourcePath) {\n return;\n }\n\n const primary = this.sessionPathsForKey(key);\n await mkdir(primary.dir, { recursive: true });\n const targetPath = primary.jsonPath;\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = sourcePath.replace('.json', '.meta.json');\n const metaTarget = primary.metaPath;\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"store.js","names":["parseRoutingSessionKey"],"sources":["../../../src/session/store.ts"],"sourcesContent":["// Session store - manages session persistence, indexing, compaction, and sliding window\n\nimport { readFile, writeFile, mkdir, unlink, readdir, stat } from 'fs/promises';\nimport { basename, join } from 'path';\nimport { existsSync } from 'fs';\nimport { resolveSessionsDir, FILENAMES } from '../config/paths.js';\nimport { resolveDefaultAgentId } from '../agent/agent-scope.js';\nimport type { Config } from '../config/schema.js';\nimport { resolveSessionShardRelativePath } from './shard-path.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../routing/session-key.js';\nimport type { AgentMessage } from '@mariozechner/pi-agent-core';\nimport { createLogger } from '../utils/logger.js';\nimport type {\n SessionMetadata,\n SessionDetail,\n SessionIndex,\n SessionListQuery,\n PaginatedResult,\n GlobalSessionStats,\n ExportFormat,\n SessionExport,\n} from './types.js';\nimport { SessionStatus } from './types.js';\nimport type { Message } from './types.js';\nimport { SessionCompactor, type CompactionConfig, type CompactionResult } from '../agent/memory/compaction.js';\nimport { SlidingWindow, type WindowConfig } from '../agent/memory/window.js';\nimport { cleanTrailingErrors, hasProblematicMessages } from '../agent/memory/message-sanitizer.js';\nimport { invalidateSessionSearchIndexCache } from './search-index-cache.js';\n\nconst log = createLogger('SessionStore');\n\nconst INDEX_VERSION = '1.0';\nconst DEFAULT_LIMIT = 50;\n\n/**\n * Session files live under `resolveSessionsDir(config, agentId)` (ADR-003), sharded by\n * `resolveSessionShardRelativePath(sessionKey)` (users/… vs system/heartbeat; web UI uses\n * compact `users/{agent}/web/{peerId}` for gateway/webchat direct sessions).\n */\nexport interface SessionStoreOptions {\n /** Loaded app config (required for session path resolution). */\n config: Config;\n /** Agent id for the session store root (default: configured default agent). */\n agentId?: string;\n /** Override storage root (tests); skips `resolveSessionsDir` */\n sessionsDir?: string;\n}\n\nexport class SessionStore {\n private sessionsDir: string;\n private archiveDir: string;\n private indexFile: string;\n private indexCache: SessionIndex | null = null;\n private indexCacheTime: number = 0;\n private indexDirty = false;\n private window: SlidingWindow;\n private compactor: SessionCompactor;\n\n constructor(\n options: SessionStoreOptions,\n windowConfig?: Partial<WindowConfig>,\n compactionConfig?: Partial<CompactionConfig>\n ) {\n const agentId = options.agentId ?? resolveDefaultAgentId(options.config);\n this.sessionsDir = options.sessionsDir ?? resolveSessionsDir(options.config, agentId);\n this.archiveDir = join(this.sessionsDir, 'archive');\n this.indexFile = join(this.sessionsDir, FILENAMES.SESSIONS_INDEX);\n this.window = new SlidingWindow(windowConfig);\n this.compactor = new SessionCompactor(compactionConfig);\n }\n\n /** Root directory of session JSON files (sharded). Used by `session_search` indexing. */\n getSessionsRoot(): string {\n return this.sessionsDir;\n }\n\n // ========== Initialization ==========\n\n async initialize(): Promise<void> {\n await mkdir(this.sessionsDir, { recursive: true });\n await mkdir(this.archiveDir, { recursive: true });\n\n if (!existsSync(this.indexFile)) {\n await this.rebuildIndex();\n } else {\n await this.loadIndex();\n }\n\n log.debug('Session store initialized');\n }\n\n private sessionPathsForKey(key: string): { dir: string; jsonPath: string; metaPath: string } {\n const safeKey = this.sanitizeKey(key);\n const shard = resolveSessionShardRelativePath(key);\n const dir = join(this.sessionsDir, shard);\n return {\n dir,\n jsonPath: join(dir, `${safeKey}.json`),\n metaPath: join(dir, `${safeKey}.meta.json`),\n };\n }\n\n // ========== Index Management ==========\n\n /**\n * Get sessions by agent ID\n */\n async getByAgent(agentId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.agentId?.toLowerCase() === agentId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by account ID\n */\n async getByAccount(accountId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) => s.routing?.accountId?.toLowerCase() === accountId.toLowerCase()\n );\n }\n\n /**\n * Get sessions by peer\n */\n async getByPeer(peerKind: string, peerId: string): Promise<SessionMetadata[]> {\n const index = await this.loadIndex();\n return (index.sessions || []).filter(\n (s) =>\n s.routing?.peerKind?.toLowerCase() === peerKind.toLowerCase() &&\n s.routing?.peerId?.toLowerCase() === peerId.toLowerCase()\n );\n }\n\n /**\n * Get main session for a DM conversation\n */\n async getMainSession(channel: string, accountId: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n return (\n (index.sessions || []).find(\n (s) =>\n s.routing?.source?.toLowerCase() === channel.toLowerCase() &&\n s.routing?.accountId?.toLowerCase() === accountId.toLowerCase() &&\n s.routing?.peerKind?.toLowerCase() === 'dm' &&\n s.routing?.peerId === 'main'\n ) ?? null\n );\n }\n\n private async loadIndex(): Promise<SessionIndex> {\n try {\n // Check if index file has been modified\n const stats = await stat(this.indexFile);\n const mtime = stats.mtime.getTime();\n\n // If cache is valid and file hasn't changed, use cache\n if (this.indexCache && mtime <= this.indexCacheTime) {\n // Ensure sessions array exists\n if (!this.indexCache.sessions) {\n this.indexCache.sessions = [];\n }\n return this.indexCache;\n }\n\n // File has changed or cache is empty, reload\n const data = await readFile(this.indexFile, 'utf-8');\n const parsed = JSON.parse(data) as SessionIndex;\n // Ensure sessions array exists\n if (!parsed.sessions) {\n parsed.sessions = [];\n }\n this.indexCache = parsed;\n this.indexCacheTime = mtime;\n return this.indexCache;\n } catch {\n // Index corrupted or missing, rebuild\n return this.rebuildIndex();\n }\n }\n\n /**\n * Force refresh the index cache from disk\n */\n async refreshIndex(): Promise<void> {\n this.indexCache = null;\n this.indexCacheTime = 0;\n await this.loadIndex();\n }\n\n private async saveIndex(): Promise<void> {\n if (!this.indexCache) return;\n\n this.indexCache.lastUpdated = new Date().toISOString();\n await writeFile(this.indexFile, JSON.stringify(this.indexCache, null, 2));\n this.indexDirty = false;\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n }\n\n private async rebuildIndex(): Promise<SessionIndex> {\n log.info('Rebuilding session index...');\n\n const sessions: SessionMetadata[] = [];\n\n // Scan sessions directory\n const files = await this.scanSessionFiles();\n\n for (const file of files) {\n if (file.endsWith('.json') && !file.endsWith('.meta.json')) {\n const stem = basename(file, '.json');\n const key = this.fileNameToKey(stem);\n try {\n const metadata = await this.scanSessionFile(key);\n if (metadata) {\n sessions.push(metadata);\n }\n } catch (err) {\n log.warn({ key, err }, 'Failed to scan session file');\n }\n }\n }\n\n this.indexCache = {\n version: INDEX_VERSION,\n lastUpdated: new Date().toISOString(),\n sessions,\n };\n\n await this.saveIndex();\n \n // Update cache time after saving\n try {\n const stats = await stat(this.indexFile);\n this.indexCacheTime = stats.mtime.getTime();\n } catch {\n this.indexCacheTime = Date.now();\n }\n \n log.info({ count: sessions.length }, 'Session index rebuilt');\n\n return this.indexCache;\n }\n\n private async scanSessionFiles(): Promise<string[]> {\n const out: string[] = [];\n const walk = async (rel: string): Promise<void> => {\n const abs = join(this.sessionsDir, rel);\n let entries;\n try {\n entries = await readdir(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const ent of entries) {\n const childRel = rel ? join(rel, ent.name) : ent.name;\n if (ent.isDirectory()) {\n if (ent.name === 'archive') continue;\n await walk(childRel);\n } else if (\n ent.name.endsWith('.json') &&\n ent.name !== FILENAMES.SESSIONS_INDEX &&\n !ent.name.endsWith('.meta.json')\n ) {\n out.push(childRel);\n }\n }\n };\n await walk('');\n return out;\n }\n\n private async scanSessionFile(key: string): Promise<SessionMetadata | null> {\n const messages = await this.loadMessages(key);\n if (messages.length === 0) return null;\n\n const { jsonPath } = this.sessionPathsForKey(key);\n const stats = await stat(jsonPath);\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n return {\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: stats.birthtime.toISOString(),\n updatedAt: stats.mtime.toISOString(),\n lastAccessedAt: stats.mtime.toISOString(),\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n },\n };\n }\n\n /**\n * Extract routing metadata from session key\n */\n private extractRoutingFromKey(key: string, channel: string): SessionMetadata['routing'] {\n const parts = key.split(':');\n if (parts.length < 5) {\n return undefined;\n }\n\n const [agentId, source, accountId, peerKind, peerId, ...rest] = parts;\n \n let threadId: string | undefined;\n let scopeId: string | undefined;\n \n // Parse optional thread and scope\n for (let i = 0; i < rest.length; i++) {\n if (rest[i] === 'thread' && rest[i + 1]) {\n threadId = rest[i + 1];\n i++;\n } else if (rest[i] === 'scope' && rest[i + 1]) {\n scopeId = rest[i + 1];\n i++;\n }\n }\n\n return {\n agentId: agentId?.toLowerCase() || 'main',\n source: source?.toLowerCase() || channel,\n accountId: accountId?.toLowerCase() || 'default',\n peerKind: peerKind?.toLowerCase() || 'dm',\n peerId: peerId?.toLowerCase() || 'unknown',\n threadId,\n scopeId,\n };\n }\n\n // ========== CRUD Operations ==========\n\n async list(query: SessionListQuery = {}): Promise<PaginatedResult<SessionMetadata>> {\n const index = await this.loadIndex();\n let sessions = [...(index.sessions || [])];\n\n // Apply filters\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status];\n sessions = sessions.filter((s) => statuses.includes(s.status));\n }\n\n if (query.channel) {\n const channels = query.channel\n .split(',')\n .map((c) => c.trim())\n .filter(Boolean);\n if (channels.length === 0) {\n sessions = [];\n } else if (channels.length === 1) {\n sessions = sessions.filter((s) => s.sourceChannel === channels[0]);\n } else {\n sessions = sessions.filter((s) => channels.includes(s.sourceChannel));\n }\n }\n\n if (query.tags && query.tags.length > 0) {\n sessions = sessions.filter((s) => query.tags!.some((tag) => s.tags.includes(tag)));\n }\n\n if (query.search) {\n const searchLower = query.search.toLowerCase();\n sessions = sessions.filter(\n (s) =>\n s.key.toLowerCase().includes(searchLower) ||\n s.name?.toLowerCase().includes(searchLower) ||\n s.tags.some((t) => t.toLowerCase().includes(searchLower))\n );\n }\n\n // Apply sorting\n const sortBy = query.sortBy || 'updatedAt';\n const sortOrder = query.sortOrder || 'desc';\n\n sessions.sort((a, b) => {\n const aVal = a[sortBy];\n const bVal = b[sortBy];\n const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;\n return sortOrder === 'asc' ? comparison : -comparison;\n });\n\n // Apply pagination\n const total = sessions.length;\n const limit = query.limit || DEFAULT_LIMIT;\n const offset = query.offset || 0;\n const items = sessions.slice(offset, offset + limit);\n\n return {\n items,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n };\n }\n\n async get(key: string): Promise<SessionDetail | null> {\n const metadata = await this.getMetadata(key);\n if (!metadata) return null;\n\n const messages = await this.loadMessages(key);\n\n return {\n ...metadata,\n messages: this.convertMessages(messages),\n };\n }\n\n async getMetadata(key: string): Promise<SessionMetadata | null> {\n const index = await this.loadIndex();\n const metadata = index.sessions.find((s) => s.key === key);\n\n if (!metadata) {\n // Try to load from file directly (orphaned session)\n const scanned = await this.scanSessionFile(key);\n if (scanned) {\n index.sessions.push(scanned);\n this.indexDirty = true;\n return scanned;\n }\n return null;\n }\n\n return metadata;\n }\n\n async updateMetadata(key: string, updates: Partial<SessionMetadata>): Promise<void> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n if (idx === -1) {\n throw new Error(`Session not found: ${key}`);\n }\n\n index.sessions[idx] = {\n ...index.sessions[idx],\n ...updates,\n updatedAt: new Date().toISOString(),\n };\n\n this.indexDirty = true;\n await this.saveIndex();\n\n log.debug({ key, updates }, 'Session metadata updated');\n }\n\n async delete(key: string): Promise<boolean> {\n const index = await this.loadIndex();\n const idx = index.sessions.findIndex((s) => s.key === key);\n\n const primary = this.sessionPathsForKey(key);\n\n for (const p of [primary.jsonPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n for (const p of [primary.metaPath]) {\n try {\n await unlink(p);\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n // Remove from index\n if (idx !== -1) {\n index.sessions.splice(idx, 1);\n this.indexDirty = true;\n await this.saveIndex();\n }\n\n log.info({ key }, 'Session deleted');\n return true;\n }\n\n async deleteMany(keys: string[]): Promise<{ success: string[]; failed: string[] }> {\n const success: string[] = [];\n const failed: string[] = [];\n\n for (const key of keys) {\n try {\n await this.delete(key);\n success.push(key);\n } catch {\n failed.push(key);\n }\n }\n\n return { success, failed };\n }\n\n // ========== Status Operations ==========\n\n async setStatus(key: string, status: SessionStatus): Promise<void> {\n await this.updateMetadata(key, { status });\n\n if (status === SessionStatus.ARCHIVED) {\n await this.moveToArchive(key);\n } else {\n await this.moveFromArchive(key);\n }\n }\n\n async archive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ARCHIVED);\n }\n\n async unarchive(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n async pin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.PINNED);\n }\n\n async unpin(key: string): Promise<void> {\n await this.setStatus(key, SessionStatus.ACTIVE);\n }\n\n // ========== Message Operations ==========\n\n async loadMessages(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n const primary = this.sessionPathsForKey(key);\n\n const readAndNormalize = async (path: string): Promise<AgentMessage[] | null> => {\n try {\n const data = await readFile(path, 'utf-8');\n const messages = JSON.parse(data) as AgentMessage[];\n if (hasProblematicMessages(messages)) {\n const cleaned = cleanTrailingErrors(messages);\n if (cleaned.length !== messages.length) {\n log.info(\n { key, original: messages.length, cleaned: cleaned.length },\n 'Cleaned problematic messages on load'\n );\n }\n return cleaned;\n }\n return messages;\n } catch {\n return null;\n }\n };\n\n const messages = await readAndNormalize(primary.jsonPath);\n\n if (messages !== null) {\n return messages;\n }\n\n if (options?.fromArchive) {\n const archivedFile = await this.findMostRecentArchive(key);\n if (!archivedFile) {\n return [];\n }\n const archived = await readAndNormalize(archivedFile);\n return archived ?? [];\n }\n return [];\n }\n\n /**\n * Find the most recent archived session file for a given key.\n * Archived files have format: {safeKey}.{timestamp}.json\n */\n private async findMostRecentArchive(sessionKey: string): Promise<string | null> {\n const safeKey = this.sanitizeKey(sessionKey);\n const shardDir = join(this.archiveDir, resolveSessionShardRelativePath(sessionKey));\n\n const scanDir = async (dir: string): Promise<string | null> => {\n try {\n const files = await readdir(dir);\n const matchingFiles = files\n .filter((f) => f.startsWith(`${safeKey}.`) && f.endsWith('.json') && !f.endsWith('.meta.json'))\n .sort()\n .reverse();\n if (matchingFiles.length === 0) return null;\n return join(dir, matchingFiles[0]);\n } catch {\n return null;\n }\n };\n\n const inShard = await scanDir(shardDir);\n if (inShard) return inShard;\n return await scanDir(this.archiveDir);\n }\n\n async saveMessages(key: string, messages: AgentMessage[]): Promise<void> {\n const { dir, jsonPath } = this.sessionPathsForKey(key);\n\n await mkdir(dir, { recursive: true });\n await writeFile(jsonPath, JSON.stringify(messages, null, 2));\n\n // Update or create metadata\n const index = await this.loadIndex();\n const existingIdx = index.sessions.findIndex((s) => s.key === key);\n const now = new Date().toISOString();\n\n const { channel, chatId } = this.parseSessionKey(key);\n const routing = this.extractRoutingFromKey(key, channel);\n const isCronSession = channel === 'cron';\n const isHeartbeatSession = channel === 'heartbeat';\n\n if (existingIdx !== -1) {\n const prev = index.sessions[existingIdx];\n index.sessions[existingIdx] = {\n ...prev,\n sourceChannel: channel,\n sourceChatId: chatId,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n updatedAt: now,\n lastAccessedAt: now,\n routing: routing || prev.routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: {\n ...prev.customData,\n cronJobId: chatId,\n },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: {\n ...prev.customData,\n heartbeatTarget: chatId,\n },\n }\n : {}),\n stats: {\n ...prev.stats,\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n };\n } else {\n index.sessions.push({\n key,\n status: SessionStatus.ACTIVE,\n tags: [],\n createdAt: now,\n updatedAt: now,\n lastAccessedAt: now,\n messageCount: messages.length,\n estimatedTokens: this.estimateTokens(messages),\n compactedCount: 0,\n sourceChannel: channel,\n sourceChatId: chatId,\n routing,\n ...(isCronSession\n ? {\n sessionType: 'cron',\n customData: { cronJobId: chatId },\n }\n : {}),\n ...(isHeartbeatSession\n ? {\n sessionType: 'heartbeat',\n customData: { heartbeatTarget: chatId },\n }\n : {}),\n stats: {\n messageCount: messages.length,\n tokenCount: this.estimateTokens(messages),\n lastTurnAt: Date.now(),\n },\n });\n }\n\n this.indexDirty = true;\n await this.saveIndex();\n\n invalidateSessionSearchIndexCache();\n }\n\n // ========== Sliding Window & Compaction ==========\n\n /**\n * Get window stats for messages\n */\n getWindowStats(messages: AgentMessage[]) {\n return this.window.getStats(messages);\n }\n\n /**\n * Check if session needs compaction\n */\n needsCompaction(key: string, messages: AgentMessage[], contextWindow: number) {\n return this.compactor.needsCompaction(messages, contextWindow);\n }\n\n /**\n * Prepare compaction (check if needed)\n */\n prepareCompaction(\n key: string,\n messages: AgentMessage[],\n contextWindow: number\n ): { needsCompaction: boolean; messages: AgentMessage[]; stats?: ReturnType<typeof this.compactor.needsCompaction> } {\n const result = this.compactor.needsCompaction(messages, contextWindow);\n return {\n needsCompaction: result.needed,\n messages,\n stats: result,\n };\n }\n\n /**\n * Apply compaction result to messages\n */\n async applyCompaction(\n key: string,\n messages: AgentMessage[],\n result: CompactionResult\n ): Promise<AgentMessage[]> {\n const compacted = this.compactor.applyCompaction(messages, result);\n \n // Persist the compacted messages to disk so subsequent loads see the reduced context\n await this.saveMessages(key, compacted);\n \n const metadata = await this.getMetadata(key);\n if (metadata) {\n await this.updateMetadata(key, {\n compactedCount: metadata.compactedCount + 1,\n });\n }\n \n log.info({\n key,\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n keptMessages: compacted.length,\n }, 'Session compacted');\n \n return compacted;\n }\n\n /**\n * Compact session with LLM summary\n */\n async compact(\n key: string,\n messages: AgentMessage[],\n contextWindow: number,\n instructions?: string,\n force?: boolean,\n ): Promise<CompactionResult> {\n const result = await this.compactor.compact(messages, instructions, force);\n \n if (result.compacted) {\n await this.applyCompaction(key, messages, result);\n }\n \n return result;\n }\n\n /**\n * Get compaction stats for a session\n */\n async getCompactionStats(key: string) {\n const metadata = await this.getMetadata(key);\n if (!metadata) return undefined;\n \n return {\n compactionCount: metadata.compactedCount,\n totalTokensBefore: 0,\n totalTokensAfter: 0,\n lastCompactionAt: undefined,\n };\n }\n\n // ========== MemoryStore API Aliases ==========\n\n /** Alias for delete */\n async deleteSession(key: string): Promise<boolean> {\n return this.delete(key);\n }\n\n /** Alias for loadMessages */\n async load(key: string, options?: { fromArchive?: boolean }): Promise<AgentMessage[]> {\n return this.loadMessages(key, options);\n }\n\n /** Alias for saveMessages */\n async save(key: string, messages: AgentMessage[]): Promise<void> {\n return this.saveMessages(key, messages);\n }\n\n /** Alias for estimateTokens */\n async estimateTokenUsage(key: string, messages: AgentMessage[]): Promise<number> {\n return this.estimateTokens(messages);\n }\n\n // ========== Search ==========\n\n async searchInSession(key: string, keyword: string): Promise<Message[]> {\n const messages = await this.loadMessages(key);\n const keywordLower = keyword.toLowerCase();\n\n return this.convertMessages(\n messages.filter((m) => {\n const content = this.extractTextContent(m.content);\n return content.toLowerCase().includes(keywordLower);\n })\n );\n }\n\n // ========== Export/Import ==========\n\n async exportSession(key: string, format: ExportFormat): Promise<string> {\n const detail = await this.get(key);\n if (!detail) {\n throw new Error(`Session not found: ${key}`);\n }\n\n if (format === 'json') {\n const exportData: SessionExport = {\n version: INDEX_VERSION,\n exportedAt: new Date().toISOString(),\n metadata: detail,\n messages: detail.messages,\n };\n return JSON.stringify(exportData, null, 2);\n } else {\n // Markdown format\n const lines = [\n `# ${detail.name || detail.key}`,\n '',\n `- **Channel:** ${detail.sourceChannel}`,\n `- **Created:** ${detail.createdAt}`,\n `- **Messages:** ${detail.messageCount}`,\n `- **Tags:** ${detail.tags.join(', ') || 'none'}`,\n '',\n '---',\n '',\n ];\n\n for (const msg of detail.messages) {\n const role = msg.role === 'assistant' ? 'Assistant' : msg.role === 'user' ? 'User' : msg.role;\n lines.push(`## ${role}`);\n lines.push('');\n const body =\n typeof msg.content === 'string'\n ? msg.content\n : JSON.stringify(msg.content, null, 2);\n lines.push(body);\n lines.push('');\n lines.push('---');\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n }\n\n // ========== Statistics ==========\n\n async getStats(): Promise<GlobalSessionStats> {\n const index = await this.loadIndex();\n const sessions = index.sessions;\n\n const byChannel: Record<string, number> = {};\n for (const s of sessions) {\n byChannel[s.sourceChannel] = (byChannel[s.sourceChannel] || 0) + 1;\n }\n\n let oldestSession: string | undefined;\n let newestSession: string | undefined;\n\n if (sessions.length > 0) {\n const sorted = [...sessions].sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n oldestSession = sorted[0].createdAt;\n newestSession = sorted[sorted.length - 1].createdAt;\n }\n\n return {\n totalSessions: sessions.length,\n activeSessions: sessions.filter((s) => s.status === SessionStatus.ACTIVE || s.status === SessionStatus.IDLE).length,\n archivedSessions: sessions.filter((s) => s.status === SessionStatus.ARCHIVED).length,\n pinnedSessions: sessions.filter((s) => s.status === SessionStatus.PINNED).length,\n totalMessages: sessions.reduce((sum, s) => sum + s.messageCount, 0),\n totalTokens: sessions.reduce((sum, s) => sum + s.estimatedTokens, 0),\n oldestSession,\n newestSession,\n byChannel,\n };\n }\n\n // ========== Cleanup ==========\n\n async archiveOld(olderThanDays: number): Promise<number> {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - olderThanDays);\n\n const index = await this.loadIndex();\n let archived = 0;\n\n for (const session of index.sessions) {\n if (session.status !== SessionStatus.ARCHIVED && session.status !== SessionStatus.PINNED) {\n const lastAccess = new Date(session.lastAccessedAt);\n if (lastAccess < cutoff) {\n await this.archive(session.key);\n archived++;\n }\n }\n }\n\n return archived;\n }\n\n // ========== Helper Methods ==========\n\n private sanitizeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_-]/g, '_');\n }\n\n private fileNameToKey(fileName: string): string {\n // Reverse of sanitizeKey - restore all colons from underscores\n // telegram_dm_123456 -> telegram:dm:123456\n // telegram_g_-100123456_t_789 -> telegram:g:-100123456:t:789\n return fileName.replace(/_/g, ':');\n }\n\n private parseSessionKey(key: string): { channel: string; chatId: string } {\n const parts = key.split(':');\n // Session key format: {agentId}:{source}:{accountId}:{peerKind}:{peerId}\n if (parts.length >= 5) {\n const parsed = parseRoutingSessionKey(key);\n if (parsed?.source === 'cron') {\n return { channel: 'cron', chatId: parsed.peerId };\n }\n return { channel: parts[1], chatId: parts.slice(2).join(':') };\n }\n // Gateway heartbeat: `heartbeat:main` / `heartbeat:isolated:<ts>`\n if (parts.length >= 2 && parts[0] === 'heartbeat') {\n return { channel: 'heartbeat', chatId: parts.slice(1).join(':') };\n }\n return { channel: 'unknown', chatId: key };\n }\n\n estimateTokens(messages: AgentMessage[]): number {\n // Rough estimate: 1 token ≈ 4 characters\n let total = 0;\n for (const msg of messages) {\n const text = this.extractTextContent(msg.content);\n total += Math.ceil(text.length / 4);\n }\n return total;\n }\n\n private extractTextContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item !== 'object' || item === null || !('type' in item)) continue;\n const c = item as { type?: string; text?: string; name?: string };\n if (c.type === 'text' && typeof c.text === 'string') {\n parts.push(c.text);\n } else if (c.type === 'toolCall' || c.type === 'tool_use') {\n parts.push(c.name ? `[${c.name}]` : '');\n }\n }\n return parts.join('');\n }\n return '';\n }\n\n private convertMessages(messages: AgentMessage[]): Message[] {\n return messages.map((m: any) => {\n const c = m.content;\n const content: string | unknown[] =\n typeof c === 'string'\n ? c\n : Array.isArray(c)\n ? c\n : this.extractTextContent(c);\n\n const row: Message = {\n role: m.role as 'system' | 'user' | 'assistant' | 'tool' | 'toolResult',\n content,\n timestamp: m.timestamp ? new Date(m.timestamp).toISOString() : undefined,\n tool_call_id: m.tool_call_id || m.toolCallId,\n tool_calls: m.tool_calls,\n name: m.name,\n };\n if (Array.isArray(m.attachments) && m.attachments.length > 0) {\n row.attachments = m.attachments;\n }\n return row;\n });\n }\n\n private async moveToArchive(key: string): Promise<void> {\n const safeKey = this.sanitizeKey(key);\n const primary = this.sessionPathsForKey(key);\n const sourcePath = existsSync(primary.jsonPath) ? primary.jsonPath : null;\n if (!sourcePath) {\n return;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const archiveShard = join(this.archiveDir, resolveSessionShardRelativePath(key));\n await mkdir(archiveShard, { recursive: true });\n const targetPath = join(archiveShard, `${safeKey}.${timestamp}.json`);\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = primary.metaPath;\n const metaTarget = join(archiveShard, `${safeKey}.${timestamp}.meta.json`);\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n private async moveFromArchive(key: string): Promise<void> {\n const sourcePath = await this.findMostRecentArchive(key);\n if (!sourcePath) {\n return;\n }\n\n const primary = this.sessionPathsForKey(key);\n await mkdir(primary.dir, { recursive: true });\n const targetPath = primary.jsonPath;\n\n try {\n const data = await readFile(sourcePath, 'utf-8');\n await writeFile(targetPath, data);\n await unlink(sourcePath);\n\n const metaSource = sourcePath.replace('.json', '.meta.json');\n const metaTarget = primary.metaPath;\n try {\n const metaData = await readFile(metaSource, 'utf-8');\n await writeFile(metaTarget, metaData);\n await unlink(metaSource);\n } catch {\n // Meta file might not exist\n }\n } catch (err: any) {\n if (err.code !== 'ENOENT') throw err;\n }\n }\n\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { getLogDir, init_config } from "./config.js";
|
|
3
|
+
import { appendFile } from "fs/promises";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import { existsSync, mkdirSync } from "fs";
|
|
5
|
-
import { appendFile } from "fs/promises";
|
|
6
6
|
//#region src/utils/logger/audit.ts
|
|
7
7
|
/**
|
|
8
8
|
* Audit Log
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
1
2
|
import { basename, join } from "path";
|
|
2
3
|
import { createReadStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
3
|
-
import { readFile } from "fs/promises";
|
|
4
4
|
import { gunzip } from "zlib";
|
|
5
5
|
import { promisify } from "util";
|
|
6
6
|
import { createInterface } from "readline";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { config, getLogDir, init_config } from "./config.js";
|
|
3
|
+
import { writeFile } from "fs/promises";
|
|
3
4
|
import { join } from "path";
|
|
4
5
|
import { readFileSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
5
|
-
import { writeFile } from "fs/promises";
|
|
6
6
|
import { gzip } from "zlib";
|
|
7
7
|
import { promisify } from "util";
|
|
8
8
|
//#region src/utils/logger/rotation.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { join } from "path";
|
|
4
3
|
import { unlink, writeFile } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
5
|
import { spawn } from "child_process";
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
7
|
//#region src/voice/tts/audio.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Personal AI assistant: CLI, gateway (HTTP/WebSocket + React console), Telegram and WeChat (Weixin) channels — TypeScript, 20+ LLM providers via pi-ai, extensions and skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -115,6 +115,7 @@
|
|
|
115
115
|
"commander": "^14.0.3",
|
|
116
116
|
"cron-parser": "^5.5.0",
|
|
117
117
|
"dotenv": "^17.4.1",
|
|
118
|
+
"electron-updater": "^6.8.3",
|
|
118
119
|
"glob": "^13.0.6",
|
|
119
120
|
"grammy": "^1.42.0",
|
|
120
121
|
"hono": "^4.12.14",
|