@maudecode/talaria-web 1.18.0 → 1.19.0
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/_release.json +8 -8
- package/dist/api/auth-raw.js +16 -16
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +2 -2
- package/dist/api/automation-raw.js +32 -32
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +102 -22
- package/dist/api/automation-router.js +15 -13
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +1205 -34
- package/dist/api/chat-router.js +158 -78
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +2 -2
- package/dist/api/git-router.js +29 -29
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +3 -3
- package/dist/api/raw-routes.js +63 -61
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +6 -6
- package/dist/api/router.js +22 -22
- package/dist/api/router.js.map +1 -1
- package/dist/api/session-visibility.d.ts +4 -4
- package/dist/api/session-visibility.js +26 -16
- package/dist/api/session-visibility.js.map +1 -1
- package/dist/api/sessions-router.d.ts +1928 -124
- package/dist/api/sessions-router.js +177 -173
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +427 -9
- package/dist/api/settings-router.js +48 -32
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +2 -2
- package/dist/api/sse-routes.js +63 -63
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.d.ts +5 -0
- package/dist/api/tools-raw.js +58 -33
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +663 -91
- package/dist/api/tools-router.js +309 -52
- package/dist/api/tools-router.js.map +1 -1
- package/dist/api/transcribe-stream.d.ts +16 -0
- package/dist/api/transcribe-stream.js +172 -0
- package/dist/api/transcribe-stream.js.map +1 -0
- package/dist/app.d.ts +4 -1
- package/dist/app.js +66 -47
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +10 -9
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/passkeys.d.ts +15 -14
- package/dist/auth/passkeys.js +89 -91
- package/dist/auth/passkeys.js.map +1 -1
- package/dist/auth/store.d.ts +17 -7
- package/dist/auth/store.js +93 -52
- package/dist/auth/store.js.map +1 -1
- package/dist/bin/talaria-web.js +12 -81
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/ctl.js +2 -1
- package/dist/cli/ctl.js.map +1 -1
- package/dist/cli/dotenv.d.ts +5 -0
- package/dist/cli/dotenv.js +14 -2
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/cli/launcher.js +5 -5
- package/dist/cli/launcher.js.map +1 -1
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +85 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/supervise.d.ts +2 -0
- package/dist/cli/supervise.js +67 -4
- package/dist/cli/supervise.js.map +1 -1
- package/dist/config/agent-config.d.ts +34 -11
- package/dist/config/agent-config.js +113 -58
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +14 -5
- package/dist/config.js.map +1 -1
- package/dist/fs/atomic.d.ts +1 -0
- package/dist/fs/atomic.js +4 -1
- package/dist/fs/atomic.js.map +1 -1
- package/dist/fs/stat.d.ts +1 -0
- package/dist/fs/stat.js +11 -0
- package/dist/fs/stat.js.map +1 -0
- package/dist/http/context.d.ts +14 -9
- package/dist/http/context.js +29 -20
- package/dist/http/context.js.map +1 -1
- package/dist/onboarding.d.ts +2 -2
- package/dist/onboarding.js +10 -8
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.d.ts +10 -4
- package/dist/profiles/profiles.js +77 -30
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/projects.d.ts +15 -6
- package/dist/projects.js +55 -47
- package/dist/projects.js.map +1 -1
- package/dist/providers/catalog.d.ts +78 -12
- package/dist/providers/catalog.js +445 -141
- package/dist/providers/catalog.js.map +1 -1
- package/dist/providers/env-file.d.ts +10 -3
- package/dist/providers/env-file.js +24 -75
- package/dist/providers/env-file.js.map +1 -1
- package/dist/providers/runtime-env.d.ts +4 -2
- package/dist/providers/runtime-env.js +9 -6
- package/dist/providers/runtime-env.js.map +1 -1
- package/dist/providers/tables.d.ts +3 -1
- package/dist/providers/tables.js +4 -2
- package/dist/providers/tables.js.map +1 -1
- package/dist/redact.js +18 -9
- package/dist/redact.js.map +1 -1
- package/dist/release.d.ts +7 -0
- package/dist/release.js +14 -0
- package/dist/release.js.map +1 -1
- package/dist/runtime.d.ts +5 -1
- package/dist/runtime.js +233 -90
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +9 -3
- package/dist/sessions/anchor.js +28 -36
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-events.d.ts +2 -0
- package/dist/sessions/background-events.js +10 -0
- package/dist/sessions/background-events.js.map +1 -0
- package/dist/sessions/background-tasks.d.ts +26 -20
- package/dist/sessions/background-tasks.js +88 -86
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/background-updates.d.ts +15 -8
- package/dist/sessions/background-updates.js +101 -25
- package/dist/sessions/background-updates.js.map +1 -1
- package/dist/sessions/cli-sessions.d.ts +15 -5
- package/dist/sessions/cli-sessions.js +62 -43
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +30 -2
- package/dist/sessions/completions.js +294 -53
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +8 -0
- package/dist/sessions/compress.js +2 -2
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/drafts.d.ts +14 -10
- package/dist/sessions/drafts.js +38 -43
- package/dist/sessions/drafts.js.map +1 -1
- package/dist/sessions/gateway-sessions.d.ts +46 -0
- package/dist/sessions/gateway-sessions.js +378 -0
- package/dist/sessions/gateway-sessions.js.map +1 -0
- package/dist/sessions/goals.d.ts +31 -0
- package/dist/sessions/goals.js +75 -0
- package/dist/sessions/goals.js.map +1 -0
- package/dist/sessions/journal.d.ts +5 -79
- package/dist/sessions/journal.js +15 -606
- package/dist/sessions/journal.js.map +1 -1
- package/dist/sessions/list.d.ts +27 -18
- package/dist/sessions/list.js +133 -131
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +68 -10
- package/dist/sessions/merge.js +259 -51
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/notices.d.ts +22 -0
- package/dist/sessions/notices.js +72 -0
- package/dist/sessions/notices.js.map +1 -0
- package/dist/sessions/pending.d.ts +10 -4
- package/dist/sessions/pending.js +83 -7
- package/dist/sessions/pending.js.map +1 -1
- package/dist/sessions/prefix-index.d.ts +82 -0
- package/dist/sessions/prefix-index.js +232 -0
- package/dist/sessions/prefix-index.js.map +1 -0
- package/dist/sessions/recovery.d.ts +4 -7
- package/dist/sessions/recovery.js +97 -281
- package/dist/sessions/recovery.js.map +1 -1
- package/dist/sessions/relay.d.ts +20 -20
- package/dist/sessions/relay.js +205 -127
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +162 -59
- package/dist/sessions/service.js +1133 -521
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +18 -2
- package/dist/sessions/session.js +29 -5
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/shares.d.ts +7 -6
- package/dist/sessions/shares.js +22 -45
- package/dist/sessions/shares.js.map +1 -1
- package/dist/sessions/source-kind.js +1 -0
- package/dist/sessions/source-kind.js.map +1 -1
- package/dist/sessions/state-db-identity.d.ts +18 -0
- package/dist/sessions/state-db-identity.js +67 -0
- package/dist/sessions/state-db-identity.js.map +1 -0
- package/dist/sessions/state-db-worker.js +3 -1
- package/dist/sessions/state-db-worker.js.map +1 -1
- package/dist/sessions/state-db.d.ts +53 -8
- package/dist/sessions/state-db.js +576 -50
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/storage/backend.d.ts +162 -0
- package/dist/sessions/storage/backend.js +19 -0
- package/dist/sessions/storage/backend.js.map +1 -0
- package/dist/sessions/storage/conformance.d.ts +20 -0
- package/dist/sessions/storage/conformance.js +237 -0
- package/dist/sessions/storage/conformance.js.map +1 -0
- package/dist/sessions/storage/document.d.ts +16 -0
- package/dist/sessions/storage/document.js +44 -0
- package/dist/sessions/storage/document.js.map +1 -0
- package/dist/sessions/storage/json-documents.d.ts +23 -0
- package/dist/sessions/storage/json-documents.js +233 -0
- package/dist/sessions/storage/json-documents.js.map +1 -0
- package/dist/sessions/storage/json-files.d.ts +12 -0
- package/dist/sessions/storage/json-files.js +124 -0
- package/dist/sessions/storage/json-files.js.map +1 -0
- package/dist/sessions/storage/json-installation.d.ts +3 -0
- package/dist/sessions/storage/json-installation.js +31 -0
- package/dist/sessions/storage/json-installation.js.map +1 -0
- package/dist/sessions/storage/json-journal.d.ts +114 -0
- package/dist/sessions/storage/json-journal.js +686 -0
- package/dist/sessions/storage/json-journal.js.map +1 -0
- package/dist/sessions/storage/json-recovery.d.ts +4 -0
- package/dist/sessions/storage/json-recovery.js +195 -0
- package/dist/sessions/storage/json-recovery.js.map +1 -0
- package/dist/sessions/storage/json.d.ts +68 -0
- package/dist/sessions/storage/json.js +258 -0
- package/dist/sessions/storage/json.js.map +1 -0
- package/dist/sessions/storage/row.d.ts +6 -0
- package/dist/sessions/storage/row.js +49 -0
- package/dist/sessions/storage/row.js.map +1 -0
- package/dist/sessions/storage/search-text.d.ts +4 -0
- package/dist/sessions/storage/search-text.js +16 -0
- package/dist/sessions/storage/search-text.js.map +1 -0
- package/dist/sessions/store.d.ts +63 -73
- package/dist/sessions/store.js +305 -611
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +4 -0
- package/dist/sessions/streams.js +9 -1
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.js +1 -1
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/todo.d.ts +5 -0
- package/dist/sessions/todo.js +28 -21
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +5 -4
- package/dist/sessions/tool-display.js +22 -16
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +10 -1
- package/dist/sessions/turn-context.js +7 -4
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +87 -17
- package/dist/sessions/turn.js +795 -347
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +8 -13
- package/dist/sessions/wakeup-pause.js +31 -49
- package/dist/sessions/wakeup-pause.js.map +1 -1
- package/dist/sessions/window.js +13 -12
- package/dist/sessions/window.js.map +1 -1
- package/dist/settings.d.ts +19 -7
- package/dist/settings.js +179 -183
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/client.d.ts +17 -3
- package/dist/sidecar/client.js +78 -8
- package/dist/sidecar/client.js.map +1 -1
- package/dist/sidecar/discover.d.ts +2 -1
- package/dist/sidecar/discover.js +93 -10
- package/dist/sidecar/discover.js.map +1 -1
- package/dist/sidecar/env.d.ts +4 -0
- package/dist/sidecar/env.js +3 -0
- package/dist/sidecar/env.js.map +1 -0
- package/dist/sidecar/fake.js +19 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/test/env-file.d.ts +5 -0
- package/dist/test/env-file.js +88 -0
- package/dist/test/env-file.js.map +1 -0
- package/dist/test/harness.d.ts +1 -0
- package/dist/test/harness.js +1 -1
- package/dist/test/harness.js.map +1 -1
- package/dist/test/passkeys.d.ts +22 -0
- package/dist/test/passkeys.js +62 -0
- package/dist/test/passkeys.js.map +1 -0
- package/dist/test/storage.d.ts +15 -0
- package/dist/test/storage.js +26 -0
- package/dist/test/storage.js.map +1 -0
- package/dist/tools/commands.d.ts +6 -1
- package/dist/tools/commands.js +8 -2
- package/dist/tools/commands.js.map +1 -1
- package/dist/tools/connectors.d.ts +33 -0
- package/dist/tools/connectors.js +121 -0
- package/dist/tools/connectors.js.map +1 -0
- package/dist/tools/crons.d.ts +38 -1
- package/dist/tools/crons.js +82 -15
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +5 -5
- package/dist/tools/dashboard-plugins.js +14 -14
- package/dist/tools/dashboard-plugins.js.map +1 -1
- package/dist/tools/extensions.d.ts +5 -4
- package/dist/tools/extensions.js +213 -207
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/gateway-restart.d.ts +62 -0
- package/dist/tools/gateway-restart.js +142 -0
- package/dist/tools/gateway-restart.js.map +1 -0
- package/dist/tools/health.d.ts +15 -3
- package/dist/tools/health.js +97 -34
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/hygiene.d.ts +4 -3
- package/dist/tools/hygiene.js +23 -9
- package/dist/tools/hygiene.js.map +1 -1
- package/dist/tools/kanban.d.ts +4 -2
- package/dist/tools/kanban.js +28 -19
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/memory-masks.d.ts +5 -0
- package/dist/tools/memory-masks.js +256 -0
- package/dist/tools/memory-masks.js.map +1 -0
- package/dist/tools/memory.d.ts +29 -3
- package/dist/tools/memory.js +69 -27
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/plugin-settings.d.ts +15 -0
- package/dist/tools/plugin-settings.js +90 -0
- package/dist/tools/plugin-settings.js.map +1 -0
- package/dist/tools/skills.d.ts +9 -4
- package/dist/tools/skills.js +44 -13
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +34 -15
- package/dist/tools/update-notifications.js +141 -57
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/update-rollback.d.ts +11 -0
- package/dist/tools/update-rollback.js +28 -0
- package/dist/tools/update-rollback.js.map +1 -0
- package/dist/tools/updates.d.ts +93 -33
- package/dist/tools/updates.js +1051 -345
- package/dist/tools/updates.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +5 -0
- package/dist/util.js.map +1 -1
- package/dist/workspace/git.d.ts +16 -1
- package/dist/workspace/git.js +291 -11
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +12 -9
- package/dist/workspace/media.js +31 -20
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/rollback.d.ts +2 -2
- package/dist/workspace/rollback.js +5 -5
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/workspaces.d.ts +15 -19
- package/dist/workspace/workspaces.js +55 -127
- package/dist/workspace/workspaces.js.map +1 -1
- package/dist/workspace/worktrees.js +170 -32
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +5 -3
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/agent_support.json +26 -0
- package/sidecar/scripts/record-fixtures.py +12 -3
- package/sidecar/scripts/replay_sidecar.py +8 -0
- package/sidecar/scripts/suite_runner.py +302 -0
- package/sidecar/scripts/test.sh +12 -4
- package/sidecar/talaria_sidecar/__init__.py +6 -1
- package/sidecar/talaria_sidecar/__main__.py +4 -2
- package/sidecar/talaria_sidecar/agent_mutation.py +975 -0
- package/sidecar/talaria_sidecar/agent_services.py +1053 -0
- package/sidecar/talaria_sidecar/dependency_sync.py +85 -0
- package/sidecar/talaria_sidecar/home.py +43 -1
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +139 -31
- package/sidecar/talaria_sidecar/methods/chat.py +847 -134
- package/sidecar/talaria_sidecar/methods/commands.py +213 -4
- package/sidecar/talaria_sidecar/methods/config.py +123 -4
- package/sidecar/talaria_sidecar/methods/connectors.py +343 -0
- package/sidecar/talaria_sidecar/methods/cron.py +366 -34
- package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +194 -54
- package/sidecar/talaria_sidecar/methods/gateway_sessions.py +234 -0
- package/sidecar/talaria_sidecar/methods/goals.py +81 -41
- package/sidecar/talaria_sidecar/methods/kanban.py +80 -27
- package/sidecar/talaria_sidecar/methods/memory.py +84 -0
- package/sidecar/talaria_sidecar/methods/oauth.py +5 -2
- package/sidecar/talaria_sidecar/methods/process.py +160 -11
- package/sidecar/talaria_sidecar/methods/profiles.py +44 -6
- package/sidecar/talaria_sidecar/methods/providers.py +196 -26
- package/sidecar/talaria_sidecar/methods/runtime.py +69 -1
- package/sidecar/talaria_sidecar/methods/skills.py +67 -4
- package/sidecar/talaria_sidecar/methods/state_db.py +277 -70
- package/sidecar/talaria_sidecar/methods/stt.py +159 -78
- package/sidecar/talaria_sidecar/methods/text.py +14 -1
- package/sidecar/talaria_sidecar/methods/toolsets.py +49 -0
- package/sidecar/talaria_sidecar/methods/usage.py +113 -14
- package/sidecar/talaria_sidecar/methods/worktree.py +26 -19
- package/sidecar/talaria_sidecar/methods/write_approval.py +181 -0
- package/sidecar/talaria_sidecar/rpc.py +45 -4
- package/sidecar/talaria_sidecar/runtime.py +81 -12
- package/sidecar/talaria_sidecar/speed.py +35 -0
- package/static/dist/FILES.txt +126 -127
- package/static/dist/assets/AppShell-cey33v9s.js +8 -0
- package/static/dist/assets/ChatPage-i8wnugr7.js +18 -0
- package/static/dist/assets/ConnectTargets-hg19553x.js +1 -0
- package/static/dist/assets/ExtensionRoute-cly4apm5.js +1 -0
- package/static/dist/assets/Field-j8uqoigz.js +9 -0
- package/static/dist/assets/HubRoute-e4bgz41j.js +2 -0
- package/static/dist/assets/Markdown-lahuichm.js +598 -0
- package/static/dist/assets/RightPanel-lbo4i0fn.js +1 -0
- package/static/dist/assets/Select-gqatm2e0.js +1 -0
- package/static/dist/assets/SessionListPanel-mfqayfjh.js +1 -0
- package/static/dist/assets/SettingsLayout-b8980u7k.js +1 -0
- package/static/dist/assets/States-e7dkgtxf.js +1 -0
- package/static/dist/assets/Toaster-bfqqy786.js +1 -0
- package/static/dist/assets/_app-n1n6pfx1.js +1 -0
- package/static/dist/assets/{_app.ext._extensionId-gfbzpoou.js → _app.ext._extensionId-buxjxav3.js} +1 -1
- package/static/dist/assets/_app.index-jt8zwiu2.js +1 -0
- package/static/dist/assets/_app.insights-cv76ml71.js +1 -0
- package/static/dist/assets/_app.kanban-h6ts53j7.js +1 -0
- package/static/dist/assets/_app.logs-dm873z90.js +1 -0
- package/static/dist/assets/_app.memory-glk5y6m8.js +1 -0
- package/static/dist/assets/_app.profiles-ka6zpstq.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-fn1bhde6.js → _app.session._sessionId-g2vzghvc.js} +1 -1
- package/static/dist/assets/_app.settings-hgjdhzcd.js +1 -0
- package/static/dist/assets/_app.settings._section-jf2kmhae.js +3 -0
- package/static/dist/assets/_app.skills-cii852yq.js +1 -0
- package/static/dist/assets/_app.tasks-fp9q7atm.js +1 -0
- package/static/dist/assets/_app.todos-dw94qzue.js +1 -0
- package/static/dist/assets/_app.workspaces-inyygzze.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-c8kdixke.js → abnfDiagram-VCTEODGH-gjayq3ra.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-cku06x1p.js → architectureDiagram-5GKGNRK7-nwb3xovn.js} +1 -1
- package/static/dist/assets/archive-b1j004vn.js +1 -0
- package/static/dist/assets/back-g74gkozu.js +1 -0
- package/static/dist/assets/{blockDiagram-I7D4REHJ-jzthp1wa.js → blockDiagram-I7D4REHJ-joedghi3.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-mwr67mt8.js → c4Diagram-7LVT6UL2-hyrjrohe.js} +1 -1
- package/static/dist/assets/channel-es95o4y8.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-gpq1y228.js → chunk-4HAMMTFA-koouqbut.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-ktje58c4.js → chunk-75Z2AOVW-bl4bhdnt.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-ofajdixh.js → chunk-DU6HZSFF-gpedttsp.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-cmev85ti.js → chunk-F27PBJKO-d8xxi0zi.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-htjgt22y.js → chunk-GMAD6QVW-jro0hl73.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-qndvoxl7.js → chunk-GVQU2GXP-djn6gbyu.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-e5bkd5q6.js → chunk-IMKFNOWR-vw4p5p7o.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-mj1155r6.js → chunk-L3NEJ4N5-n1hj77q8.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-e7gkikcw.js → chunk-OSK3NFVY-oy0gwn2n.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-f7t54xfm.js → chunk-P2QGCYS3-onf1hz6c.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-i927v9oa.js → chunk-POPQ4Y6H-m8sqn7cs.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-m84lmnyt.js → chunk-PWAF6VOD-ek4smc8x.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-mi0tlcu9.js → chunk-SHT3W25Y-g9gfvefo.js} +3 -3
- package/static/dist/assets/{chunk-SVP7TREG-dnau702j.js → chunk-SVP7TREG-f9nfv2cu.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-edem1gkg.js → chunk-TICWLB2K-bnk3m1mq.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-kmihoqxh.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-kmihoqxh.js +1 -0
- package/static/dist/assets/copied-bli9tbcx.js +1 -0
- package/static/dist/assets/{copy-magtofyp.js → copy-jamtd1pf.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-siqlulkl.js → cynefin-OW5HDTMX-f8xfdd3f.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-i648tah9.js → cynefinDiagram-5FMLGOSQ-n9lb99h9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-mwn6uc17.js → dagre-GXQ25YYZ-dgailddi.js} +4 -4
- package/static/dist/assets/{diagram-S7CK7UJ4-l8abxaes.js → diagram-S7CK7UJ4-m6aeoe7c.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-mekq7zgq.js → diagram-UQ7AKVKN-jall3j0x.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-fnky6lvc.js → diagram-VSXAHHWV-hbawi7hn.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-xe6r5qfw.js → diagram-VX7I27RA-hendl2tt.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-nq07dg7k.js → diagram-Z3DM3KII-igwc35p3.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-c62ipm3g.js → ebnfDiagram-PWID7BFC-c64knz08.js} +1 -1
- package/static/dist/assets/{erDiagram-RLTQ6QDP-h39kmnzy.js → erDiagram-RLTQ6QDP-brdu9lcm.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-njk1j8wl.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-nma6f0ga.js +1 -0
- package/static/dist/assets/folder-plus-lvt9kudb.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ngbsyp37.js → ganttDiagram-EL5Y4UJY-ma6jxj5i.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-fr2i7caa.js → gitGraph-4MIJSDKK-naa9w6xa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-eravdti4.js → gitGraphDiagram-WWUBYQGX-hzltvsod.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-jwpop9p8.js → highlighted-body-KPVGNVTW-ghoavj8d.js} +1 -1
- package/static/dist/assets/host-hzm8n85n.js +1 -0
- package/static/dist/assets/index-drdcaqeq.css +2 -0
- package/static/dist/assets/index-jzm844he.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-h11x0dxo.js → info-A6RAGUB7-mn2ut5ko.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-dfh47dw9.js → infoDiagram-27XIBGKW-h6kuqvsb.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-i6ypq351.js → ishikawaDiagram-5VMMS53U-e7bnwq7h.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-lduf1z95.js → journeyDiagram-3NMN7TZE-etd1bi6p.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-e18bu4pg.js → kanban-definition-UXKFOSKX-emb1g9dz.js} +4 -4
- package/static/dist/assets/lazyRouteComponent-o816ytzl.js +1 -0
- package/static/dist/assets/{line-meizqsvn.js → line-h0ojzsvs.js} +1 -1
- package/static/dist/assets/link-k01ryaf9.js +1 -0
- package/static/dist/assets/login-bsae49ew.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-ihl34s74.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-ntne45i9.js → mermaid-parser.core-bnre27u8.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-27b26eok.js → mindmap-definition-YA3MSWOX-o1vathns.js} +1 -1
- package/static/dist/assets/onboarding-c65tysya.js +1 -0
- package/static/dist/assets/onboarding_model_label-gpn28uix.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-l6jdka8p.js → packet-AYTQ26CC-hatq7m2e.js} +1 -1
- package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
- package/static/dist/assets/{pegDiagram-XKGWAZYB-g1er3bxj.js → pegDiagram-XKGWAZYB-gwisz8jv.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-i0ylcrt9.js → pie-WAS4IAKB-fpftf0mk.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-fbz1e55z.js → pieDiagram-E7YTZNPT-khm1o9a6.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-hhue7d8o.js → quadrantDiagram-AXDQQJYC-owqc4uqn.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-f9b70aa4.js → radar-RG4KPBEZ-cntfwjpq.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-lgsuoeda.js → railroad-74A4TZTK-a7t5eibp.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-ol8cdktf.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-i8vzd429.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-nw3ulewz.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-eqpy9fnz.js → railroadDiagram-O6MQD6OU-etuh0qa0.js} +1 -1
- package/static/dist/assets/{refresh-cw-hlwb02qv.js → refresh-cw-mqbkv2a3.js} +1 -1
- package/static/dist/assets/refresh-l29ljp76.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ogf6puhi.js → requirementDiagram-BXWQKSXE-oq2ulk22.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-fqb8xcjf.js → sankeyDiagram-P5KCCOFB-iqc02emv.js} +1 -1
- package/static/dist/assets/saved-ndd4t2xb.js +1 -0
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-dlcz6qjz.js → sequenceDiagram-WJ2MYXX4-i0v116q0.js} +2 -2
- package/static/dist/assets/settings_section_extensions_title-jhfo6o8z.js +1 -0
- package/static/dist/assets/share._token-c1ri0hy1.js +2 -0
- package/static/dist/assets/sliders-horizontal-j3jjt9tv.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-fyoznpcs.js → stateDiagram-D77RDMKH-dnpt1ato.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-oefldhfy.js → swimlanes-42K2YHIH-loqbdazb.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-b1vl2zil.js → timeline-definition-24CTP7MA-ol2hnvma.js} +1 -1
- package/static/dist/assets/toast-i3h34v6o.js +64 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-f7hcci5o.js → treeView-Q6P3EWNA-idrj6iox.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-nl85071a.js → treemap-WGGIJYW6-lzr49vv5.js} +1 -1
- package/static/dist/assets/untitled-oaoya4qj.js +1 -0
- package/static/dist/assets/useForm-nfaxgmsk.js +1 -0
- package/static/dist/assets/{useParams-b4dq6n4q.js → useParams-gjc70qru.js} +1 -1
- package/static/dist/assets/{useSearch-ipfmv3he.js → useSearch-e6ae539u.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-h5hzbk0n.js → vennDiagram-4TSXK5OY-bvwr9o2z.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-jftvpm3e.js → wardley-WFR3VGLG-31ui8tma.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-hedh85ge.js → wardleyDiagram-VM6X3IG4-k5wqtx4b.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-e49523ab.js → xychartDiagram-S5SC5T6Z-aqbgvujg.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-g3bkgnvt.js +0 -1
- package/static/dist/assets/ChatPage-oo7bdan6.js +0 -25
- package/static/dist/assets/Dialog-g355qxhp.js +0 -1
- package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +0 -1
- package/static/dist/assets/HubRoute-lmkrbw47.js +0 -2
- package/static/dist/assets/Markdown-j4kut4j2.js +0 -598
- package/static/dist/assets/RightPanel-ohlsdzfl.js +0 -1
- package/static/dist/assets/Select-kddq75qc.js +0 -1
- package/static/dist/assets/SessionListPanel-lu3oy0hw.js +0 -1
- package/static/dist/assets/SettingsLayout-o9bv4mgb.js +0 -1
- package/static/dist/assets/States-eq6fe29m.js +0 -1
- package/static/dist/assets/Toaster-pc247u9f.js +0 -1
- package/static/dist/assets/_app-f8n1i0m1.js +0 -1
- package/static/dist/assets/_app.index-bzzv80hk.js +0 -1
- package/static/dist/assets/_app.insights-dzixbryi.js +0 -1
- package/static/dist/assets/_app.kanban-c6hep2us.js +0 -1
- package/static/dist/assets/_app.logs-i69qds98.js +0 -1
- package/static/dist/assets/_app.memory-kl4vf0gd.js +0 -1
- package/static/dist/assets/_app.profiles-b8zinmyp.js +0 -1
- package/static/dist/assets/_app.settings-cg8plwmf.js +0 -1
- package/static/dist/assets/_app.settings._section-bkwbi9zn.js +0 -3
- package/static/dist/assets/_app.skills-cs47sbvs.js +0 -1
- package/static/dist/assets/_app.tasks-dwvk64tc.js +0 -1
- package/static/dist/assets/_app.todos-c8xjm8p3.js +0 -1
- package/static/dist/assets/_app.workspaces-ehvd1gds.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +0 -1
- package/static/dist/assets/back-h7aun7ew.js +0 -1
- package/static/dist/assets/cancel-fhi0vcnu.js +0 -1
- package/static/dist/assets/channel-b2zo73z1.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +0 -1
- package/static/dist/assets/copied-hcplv46p.js +0 -1
- package/static/dist/assets/endpoints-klbxod4t.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +0 -1
- package/static/dist/assets/host-i828xuo7.js +0 -1
- package/static/dist/assets/index-eh8zilnv.css +0 -2
- package/static/dist/assets/index-r648f4o1.js +0 -11
- package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +0 -1
- package/static/dist/assets/link-oot2yn73.js +0 -1
- package/static/dist/assets/login-gu28l9ou.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +0 -1
- package/static/dist/assets/onboarding-lkphzu1s.js +0 -1
- package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +0 -1
- package/static/dist/assets/refresh-abtu93ke.js +0 -1
- package/static/dist/assets/saved-girnabkf.js +0 -1
- package/static/dist/assets/settings_section_extensions_title-h14cwk6f.js +0 -1
- package/static/dist/assets/share._token-kl0r7ido.js +0 -2
- package/static/dist/assets/sliders-horizontal-n1zm34qq.js +0 -1
- package/static/dist/assets/sse-iwku0sjo.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +0 -8
- package/static/dist/assets/todoStore-jjr5yr78.js +0 -1
- package/static/dist/assets/units-kgxzxfhi.js +0 -1
- package/static/dist/assets/untitled-cikfjntn.js +0 -1
- package/static/dist/assets/useForm-kqoqg0k9.js +0 -1
- package/static/dist/assets/useLocale-o59tbiea.js +0 -1
package/dist/sessions/store.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
+
import { markedStateDbGeneration } from './state-db-identity.js';
|
|
1
2
|
import { str } from '../util.js';
|
|
2
|
-
/**
|
|
3
|
-
* The WebUI session store (Python `api/models.py`): `sessions/<sid>.json`
|
|
4
|
-
* sidecars written atomically with a `.bak` on shrink, the sorted
|
|
5
|
-
* `sessions/_index.json` sidebar index with incremental patching, a bounded
|
|
6
|
-
* LRU of resident sessions that never evicts active or unsaved work, the
|
|
7
|
-
* deleted-session tombstone, composer draft overlays, and per-session
|
|
8
|
-
* mutation locks.
|
|
9
|
-
*
|
|
10
|
-
* The state.db projection (CLI sessions, lineage metadata, transcript
|
|
11
|
-
* reconciliation) is not part of this store; it layers on in the sidebar
|
|
12
|
-
* builder once the read-only SQLite projection lands.
|
|
13
|
-
*/
|
|
14
|
-
import { closeSync, existsSync, fstatSync, fsyncSync, mkdirSync, openSync, readdirSync, readFileSync, renameSync, rmSync, statSync, unlinkSync } from 'node:fs';
|
|
15
|
-
import { writeFully } from '../fs/atomic.js';
|
|
16
|
-
import { basename, join } from 'node:path';
|
|
17
3
|
import { sanitizeSessionRow } from './list.js';
|
|
4
|
+
import { withBackgroundDisplayKinds } from './background-updates.js';
|
|
18
5
|
import { anchorSceneIndexFromRecords, collapseAdjacentDuplicatePartials, isSafeSessionId, Session } from './session.js';
|
|
6
|
+
import { RevisionConflict } from './storage/backend.js';
|
|
7
|
+
import { sessionSearchMatches, sessionSearchMessageText } from './storage/search-text.js';
|
|
19
8
|
export class SessionNotFound extends Error {
|
|
20
9
|
sid;
|
|
21
10
|
constructor(sid) {
|
|
@@ -24,175 +13,44 @@ export class SessionNotFound extends Error {
|
|
|
24
13
|
this.name = 'SessionNotFound';
|
|
25
14
|
}
|
|
26
15
|
}
|
|
27
|
-
const LOAD_STABLE_READ_ATTEMPTS = 3;
|
|
28
16
|
const UNSAVED_SHELL_GRACE_S = 1800;
|
|
29
|
-
const STALE_TMP_AGE_S = 3600;
|
|
30
|
-
const DELETED_TOMBSTONE_CAP = 1000;
|
|
31
|
-
const DELETED_TOMBSTONE_VERSION = 1;
|
|
32
|
-
let tmpCounter = 0;
|
|
33
|
-
function tmpName(path) {
|
|
34
|
-
return `${path}.tmp.${process.pid}.${++tmpCounter}`;
|
|
35
|
-
}
|
|
36
|
-
function writeAtomic(path, text) {
|
|
37
|
-
const tmp = tmpName(path);
|
|
38
|
-
const fd = openSync(tmp, 'w');
|
|
39
|
-
try {
|
|
40
|
-
writeFully(fd, text);
|
|
41
|
-
fsyncSync(fd);
|
|
42
|
-
closeSync(fd);
|
|
43
|
-
renameSync(tmp, path);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
try {
|
|
47
|
-
closeSync(fd);
|
|
48
|
-
}
|
|
49
|
-
catch { /* closed */ }
|
|
50
|
-
try {
|
|
51
|
-
unlinkSync(tmp);
|
|
52
|
-
}
|
|
53
|
-
catch { /* gone */ }
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export function statSignature(path) {
|
|
58
|
-
try {
|
|
59
|
-
const st = statSync(path, { bigint: true });
|
|
60
|
-
return `${path}:${st.mtimeNs}:${st.size}:${st.ctimeNs}`;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/** Python `_read_metadata_json_prefix`: everything before the top-level `messages` (or `anchor_activity_scenes`) key. */
|
|
67
|
-
export function readMetadataJsonPrefix(path, maxBytes = 1024 * 1024) {
|
|
68
|
-
return readMetadataJsonPrefixWithSignature(path, maxBytes).prefix;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* The metadata prefix together with the signature of the inode it was read from (fstat of the open descriptor), so a
|
|
72
|
-
* file atomically replaced during the read is never cached under the replacement's identity.
|
|
73
|
-
*/
|
|
74
|
-
export function readMetadataJsonPrefixWithSignature(path, maxBytes = 1024 * 1024) {
|
|
75
|
-
const fd = openSync(path, 'r');
|
|
76
|
-
try {
|
|
77
|
-
const st = fstatSync(fd, { bigint: true });
|
|
78
|
-
const signature = `${path}:${st.mtimeNs}:${st.size}:${st.ctimeNs}`;
|
|
79
|
-
return { prefix: readPrefixFromFd(fd, maxBytes, Math.min(64 * 1024, maxBytes)), signature };
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
closeSync(fd);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function readPrefixFromFd(fd, maxBytes, initialStage) {
|
|
86
|
-
let stage = initialStage;
|
|
87
|
-
{
|
|
88
|
-
let raw = Buffer.alloc(0);
|
|
89
|
-
let text = '';
|
|
90
|
-
let stopPos = null;
|
|
91
|
-
while (raw.length < maxBytes) {
|
|
92
|
-
const chunk = Buffer.alloc(Math.min(stage, maxBytes - raw.length));
|
|
93
|
-
const n = readSyncFully(fd, chunk);
|
|
94
|
-
if (n === 0)
|
|
95
|
-
return null;
|
|
96
|
-
raw = Buffer.concat([raw, chunk.subarray(0, n)]);
|
|
97
|
-
text = raw.toString('utf8');
|
|
98
|
-
const messagesPos = findTopLevelJsonKey(text, 'messages');
|
|
99
|
-
const scenesPos = findTopLevelJsonKey(text, 'anchor_activity_scenes');
|
|
100
|
-
stopPos = messagesPos;
|
|
101
|
-
if (scenesPos !== null && (stopPos === null || scenesPos < stopPos))
|
|
102
|
-
stopPos = scenesPos;
|
|
103
|
-
if (stopPos !== null)
|
|
104
|
-
break;
|
|
105
|
-
stage *= 2;
|
|
106
|
-
}
|
|
107
|
-
if (stopPos === null)
|
|
108
|
-
return null;
|
|
109
|
-
let prefix = text.slice(0, stopPos).trimEnd();
|
|
110
|
-
if (prefix.endsWith(','))
|
|
111
|
-
prefix = prefix.slice(0, -1).trimEnd();
|
|
112
|
-
return `${prefix}\n}`;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
import { readSync } from 'node:fs';
|
|
116
|
-
function readSyncFully(fd, buf) {
|
|
117
|
-
let total = 0;
|
|
118
|
-
while (total < buf.length) {
|
|
119
|
-
const n = readSync(fd, buf, total, buf.length - total, null);
|
|
120
|
-
if (n === 0)
|
|
121
|
-
break;
|
|
122
|
-
total += n;
|
|
123
|
-
}
|
|
124
|
-
return total;
|
|
125
|
-
}
|
|
126
|
-
/** Offset of `"key":` at JSON nesting depth 1, or null. */
|
|
127
|
-
export function findTopLevelJsonKey(text, key) {
|
|
128
|
-
let depth = 0;
|
|
129
|
-
let inString = false;
|
|
130
|
-
let escape = false;
|
|
131
|
-
let stringStart = -1;
|
|
132
|
-
for (let i = 0; i < text.length; i += 1) {
|
|
133
|
-
const ch = text[i];
|
|
134
|
-
if (inString) {
|
|
135
|
-
if (escape)
|
|
136
|
-
escape = false;
|
|
137
|
-
else if (ch === '\\')
|
|
138
|
-
escape = true;
|
|
139
|
-
else if (ch === '"') {
|
|
140
|
-
inString = false;
|
|
141
|
-
if (depth === 1 && text.slice(stringStart + 1, i) === key) {
|
|
142
|
-
let j = i + 1;
|
|
143
|
-
while (j < text.length && /\s/.test(text[j] ?? ''))
|
|
144
|
-
j += 1;
|
|
145
|
-
if (text[j] === ':')
|
|
146
|
-
return stringStart;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (ch === '"') {
|
|
152
|
-
inString = true;
|
|
153
|
-
stringStart = i;
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
if (ch === '{' || ch === '[')
|
|
157
|
-
depth += 1;
|
|
158
|
-
else if (ch === '}' || ch === ']')
|
|
159
|
-
depth -= 1;
|
|
160
|
-
}
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
export function readPersistedMessageCount(path) {
|
|
164
|
-
try {
|
|
165
|
-
const prefix = readMetadataJsonPrefix(path);
|
|
166
|
-
if (!prefix)
|
|
167
|
-
return null;
|
|
168
|
-
const value = JSON.parse(prefix).message_count;
|
|
169
|
-
if (typeof value === 'boolean')
|
|
170
|
-
return null;
|
|
171
|
-
const n = Number(value);
|
|
172
|
-
return Number.isInteger(n) && n >= 0 ? n : null;
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
17
|
export class SessionStore {
|
|
179
18
|
deps;
|
|
180
|
-
sessionDir;
|
|
181
|
-
indexFile;
|
|
182
|
-
/** Resident sessions in LRU order (oldest first). */
|
|
183
19
|
sessions = new Map();
|
|
184
20
|
locks = new Map();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
21
|
+
writes = new Map();
|
|
22
|
+
resolutions = new Map();
|
|
23
|
+
persistedMessages = new WeakMap();
|
|
24
|
+
indexWriter = Promise.resolve();
|
|
188
25
|
indexPending = new Map();
|
|
26
|
+
indexFlush = null;
|
|
189
27
|
constructor(deps) {
|
|
190
28
|
this.deps = deps;
|
|
191
|
-
this.sessionDir = deps.sessionDir;
|
|
192
|
-
this.indexFile = join(deps.sessionDir, '_index.json');
|
|
193
29
|
}
|
|
194
|
-
|
|
195
|
-
|
|
30
|
+
get backend() { return this.deps.storage.sessions; }
|
|
31
|
+
async exists(sid) { return this.backend.exists(sid); }
|
|
32
|
+
async revision(sid) { return this.backend.revision(sid); }
|
|
33
|
+
readMessages(sid, range) { return this.backend.readMessages(sid, range); }
|
|
34
|
+
async index() { return this.backend.index(); }
|
|
35
|
+
async hasIndex() { return this.backend.hasIndex(); }
|
|
36
|
+
get shrinkBackup() { return this.backend.shrinkBackup; }
|
|
37
|
+
async diagnose() { return this.backend.diagnose(); }
|
|
38
|
+
async repair(issue) {
|
|
39
|
+
const repaired = await this.backend.repair(issue);
|
|
40
|
+
if (repaired)
|
|
41
|
+
this.sessions.delete(issue.session_id);
|
|
42
|
+
return repaired;
|
|
43
|
+
}
|
|
44
|
+
async close() {
|
|
45
|
+
try {
|
|
46
|
+
await Promise.allSettled(this.resolutions.values());
|
|
47
|
+
await Promise.allSettled(this.writes.values());
|
|
48
|
+
await this.indexFlush;
|
|
49
|
+
await this.indexWriter;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
await this.deps.storage.close();
|
|
53
|
+
}
|
|
196
54
|
}
|
|
197
55
|
// ── locks ────────────────────────────────────────────────────────────────
|
|
198
56
|
/** Serialize one session's read-modify-write sequences (Python `_get_session_agent_lock`). */
|
|
@@ -222,17 +80,17 @@ export class SessionStore {
|
|
|
222
80
|
}
|
|
223
81
|
}
|
|
224
82
|
// ── creation / resolution ────────────────────────────────────────────────
|
|
225
|
-
construct(init, profile) {
|
|
226
|
-
const session = new Session(init, this.deps.defaults(profile));
|
|
83
|
+
async construct(init, profile) {
|
|
84
|
+
const session = new Session(init, (await this.deps.defaults(profile)));
|
|
227
85
|
// New-session defaults must not replace the title supplied by the read projection.
|
|
228
86
|
if (typeof init.title === 'string')
|
|
229
87
|
session.title = init.title;
|
|
230
88
|
return session;
|
|
231
89
|
}
|
|
232
90
|
/** In-memory only until the first message is persisted (Python `new_session`). */
|
|
233
|
-
newSession(opts = {}) {
|
|
91
|
+
async newSession(opts = {}) {
|
|
234
92
|
const profile = opts.profile ?? null;
|
|
235
|
-
const defaults = this.deps.defaults(profile);
|
|
93
|
+
const defaults = (await this.deps.defaults(profile));
|
|
236
94
|
const wt = opts.worktree ?? null;
|
|
237
95
|
const s = new Session({
|
|
238
96
|
workspace: (wt ? wt.path : opts.workspace) || defaults.workspace,
|
|
@@ -247,97 +105,81 @@ export class SessionStore {
|
|
|
247
105
|
worktree_created_at: wt?.created_at ?? null,
|
|
248
106
|
enabled_toolsets: opts.enabledToolsets ?? null,
|
|
249
107
|
}, defaults);
|
|
250
|
-
this.clearDeletedTombstone(s.session_id);
|
|
251
|
-
this.touch(s);
|
|
108
|
+
await this.clearDeletedTombstone(s.session_id);
|
|
109
|
+
await this.touch(s);
|
|
252
110
|
if (wt)
|
|
253
|
-
this.save(s);
|
|
111
|
+
(await this.save(s));
|
|
254
112
|
return s;
|
|
255
113
|
}
|
|
256
114
|
/** Insert or refresh a session in the LRU and evict over the cap. */
|
|
257
|
-
touch(session) {
|
|
115
|
+
async touch(session) {
|
|
258
116
|
this.sessions.delete(session.session_id);
|
|
259
117
|
this.sessions.set(session.session_id, session);
|
|
260
|
-
this.evictOverCap();
|
|
118
|
+
await this.evictOverCap();
|
|
261
119
|
}
|
|
262
|
-
load(sid) {
|
|
120
|
+
async load(sid, opts = {}) {
|
|
263
121
|
if (!isSafeSessionId(sid))
|
|
264
122
|
return null;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
123
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
124
|
+
const stored = await this.backend.readDocument(sid);
|
|
125
|
+
if (!stored)
|
|
126
|
+
return null;
|
|
127
|
+
const data = sanitizeSessionRow(stored.doc, this, sid);
|
|
128
|
+
if (data?.session_id !== sid)
|
|
129
|
+
return null;
|
|
130
|
+
const messages = await this.readMessages(sid, { start: 0, end: Infinity });
|
|
131
|
+
if (stored.revision === null || stored.revision !== await this.revision(sid))
|
|
132
|
+
continue;
|
|
133
|
+
const [rows, collapsed] = collapseAdjacentDuplicatePartials(messages);
|
|
134
|
+
data.messages = rows;
|
|
135
|
+
const session = (await this.construct(data, data.profile ?? null));
|
|
136
|
+
session.sidecarLoadedSignature = stored.revision;
|
|
137
|
+
this.persistedMessages.set(session, messages.map((row) => JSON.stringify(row)));
|
|
138
|
+
if (collapsed && (opts.repairDuplicates ?? true)) {
|
|
139
|
+
try {
|
|
140
|
+
await this.save(session, { touchUpdatedAt: false, skipIndex: true });
|
|
141
|
+
}
|
|
142
|
+
catch { /* best effort */ }
|
|
279
143
|
}
|
|
280
|
-
}
|
|
281
|
-
const clean = sanitizeSessionRow(data, this, basename(path));
|
|
282
|
-
if (!clean)
|
|
283
|
-
return null;
|
|
284
|
-
data = clean;
|
|
285
|
-
const [messages, collapsed] = collapseAdjacentDuplicatePartials(data.messages);
|
|
286
|
-
data.messages = messages;
|
|
287
|
-
const session = this.construct(data, data.profile ?? null);
|
|
288
|
-
if (collapsed) {
|
|
289
144
|
try {
|
|
290
|
-
|
|
145
|
+
if (opts.drafts ?? true)
|
|
146
|
+
session.composer_draft = (await this.deps.drafts.read(sid, session.composer_draft));
|
|
291
147
|
}
|
|
292
|
-
catch { /*
|
|
293
|
-
|
|
294
|
-
else {
|
|
295
|
-
session.sidecarLoadedSignature = signature;
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
session.composer_draft = this.deps.drafts.read(sid, session.composer_draft);
|
|
148
|
+
catch { /* ignore */ }
|
|
149
|
+
return session;
|
|
299
150
|
}
|
|
300
|
-
|
|
301
|
-
return session;
|
|
151
|
+
throw new RevisionConflict(sid);
|
|
302
152
|
}
|
|
303
|
-
loadMetadataOnly(sid, opts = {}) {
|
|
153
|
+
async loadMetadataOnly(sid, opts = {}) {
|
|
304
154
|
if (!isSafeSessionId(sid))
|
|
305
155
|
return null;
|
|
306
|
-
const path = this.pathFor(sid);
|
|
307
|
-
if (!existsSync(path))
|
|
308
|
-
return null;
|
|
309
156
|
try {
|
|
310
|
-
const
|
|
311
|
-
if (!
|
|
312
|
-
return
|
|
313
|
-
|
|
314
|
-
|
|
157
|
+
const stored = await this.backend.readDocument(sid, { metadataOnly: true });
|
|
158
|
+
if (!stored)
|
|
159
|
+
return null;
|
|
160
|
+
if (!stored.metadataOnly)
|
|
161
|
+
return (await this.load(sid));
|
|
162
|
+
const parsed = sanitizeSessionRow(stored.doc, this, sid);
|
|
163
|
+
if (parsed?.session_id !== sid)
|
|
315
164
|
return null;
|
|
316
|
-
for (const key of ['session_id', 'title', 'created_at', 'updated_at'])
|
|
317
|
-
if (!(key in parsed))
|
|
318
|
-
return this.load(sid);
|
|
319
|
-
const sidecarCount = parsed.message_count;
|
|
320
|
-
const modernCount = typeof sidecarCount === 'number' && Number.isInteger(sidecarCount) && sidecarCount >= 0 ? sidecarCount : null;
|
|
321
|
-
if (modernCount === null && !('anchor_scene_index' in parsed))
|
|
322
|
-
return this.load(sid);
|
|
323
165
|
parsed.messages = [];
|
|
324
166
|
parsed.tool_calls = [];
|
|
325
|
-
const session = this.construct(parsed, parsed.profile ?? null);
|
|
326
|
-
|
|
327
|
-
if (modernCount === null)
|
|
328
|
-
indexCount = (opts.indexMessageCounts ?? this.indexMessageCounts()).get(sid) ?? null;
|
|
329
|
-
const known = [indexCount, modernCount].filter((c) => c !== null);
|
|
330
|
-
session.metadataMessageCount = known.length ? Math.max(...known) : null;
|
|
167
|
+
const session = (await this.construct(parsed, parsed.profile ?? null));
|
|
168
|
+
session.metadataMessageCount = stored.messageCount ?? (opts.indexMessageCounts ?? await this.indexMessageCounts()).get(sid) ?? null;
|
|
331
169
|
session.loadedMetadataOnly = true;
|
|
332
|
-
session.sidecarLoadedSignature =
|
|
170
|
+
session.sidecarLoadedSignature = stored.revision;
|
|
333
171
|
return session;
|
|
334
172
|
}
|
|
335
173
|
catch {
|
|
336
|
-
return this.load(sid);
|
|
174
|
+
return (await this.load(sid));
|
|
337
175
|
}
|
|
338
176
|
}
|
|
339
|
-
/** Resolve a session through the cache with disk-freshness checks (Python `get_session`). Throws `SessionNotFound`. */
|
|
340
177
|
get(sid, opts = {}) {
|
|
178
|
+
return this.serializeSession(sid, () => this.getNow(sid, opts), this.resolutions);
|
|
179
|
+
}
|
|
180
|
+
async getNow(sid, opts) {
|
|
181
|
+
// A local commit updates the resident revision before a reader can mistake it for an external edit.
|
|
182
|
+
await this.writes.get(sid)?.catch(() => { });
|
|
341
183
|
const cached = this.sessions.get(sid);
|
|
342
184
|
if (cached) {
|
|
343
185
|
if (cached.session_id !== sid) {
|
|
@@ -349,15 +191,13 @@ export class SessionStore {
|
|
|
349
191
|
this.sessions.delete(sid);
|
|
350
192
|
this.sessions.set(sid, cached);
|
|
351
193
|
}
|
|
352
|
-
const lags = this.
|
|
353
|
-
|
|
354
|
-
if (lags && !existsSync(this.pathFor(sid))) {
|
|
194
|
+
const lags = await this.cachedLagsStorage(cached);
|
|
195
|
+
if (lags && !await this.exists(sid)) {
|
|
355
196
|
this.sessions.delete(sid);
|
|
356
197
|
throw new SessionNotFound(sid);
|
|
357
198
|
}
|
|
358
|
-
// A sidebar-only stub (metadata load) must be upgraded to the full transcript when messages are requested.
|
|
359
199
|
if (!opts.metadataOnly && (cached.loadedMetadataOnly || lags)) {
|
|
360
|
-
const fresh = this.load(sid);
|
|
200
|
+
const fresh = await this.load(sid);
|
|
361
201
|
if (fresh) {
|
|
362
202
|
this.sessions.set(sid, fresh);
|
|
363
203
|
return fresh;
|
|
@@ -366,381 +206,263 @@ export class SessionStore {
|
|
|
366
206
|
return cached;
|
|
367
207
|
}
|
|
368
208
|
}
|
|
369
|
-
const loaded = opts.metadataOnly ? this.loadMetadataOnly(sid) : this.load(sid);
|
|
209
|
+
const loaded = opts.metadataOnly ? await this.loadMetadataOnly(sid) : await this.load(sid);
|
|
370
210
|
if (!loaded)
|
|
371
211
|
throw new SessionNotFound(sid);
|
|
372
212
|
if (opts.cacheOnMiss ?? true)
|
|
373
|
-
this.touch(loaded);
|
|
213
|
+
await this.touch(loaded);
|
|
374
214
|
return loaded;
|
|
375
215
|
}
|
|
376
|
-
|
|
377
|
-
ensureFull(sid, session) {
|
|
216
|
+
async ensureFull(sid, session) {
|
|
378
217
|
if (!session.loadedMetadataOnly)
|
|
379
218
|
return session;
|
|
380
|
-
const full = this.load(sid);
|
|
219
|
+
const full = await this.load(sid);
|
|
381
220
|
if (!full)
|
|
382
221
|
throw new SessionNotFound(sid);
|
|
383
|
-
this.touch(full);
|
|
222
|
+
await this.touch(full);
|
|
384
223
|
return full;
|
|
385
224
|
}
|
|
386
|
-
|
|
387
|
-
cachedLagsDisk(cached) {
|
|
225
|
+
async cachedLagsStorage(cached) {
|
|
388
226
|
if (cached.active_stream_id || cached.pending_user_message || cached.pending_started_at)
|
|
389
227
|
return false;
|
|
390
|
-
|
|
391
|
-
// A session that was persisted and whose file has since vanished was deleted underneath us: it is stale, and the
|
|
392
|
-
// reload's miss evicts it rather than letting a later mutation recreate the deleted sidecar.
|
|
393
|
-
if (current === null)
|
|
394
|
-
return cached.sidecarLoadedSignature !== null;
|
|
395
|
-
// An unknown read identity (the file changed underneath the load) must not be trusted: reload it.
|
|
396
|
-
if (cached.sidecarLoadedSignature === null)
|
|
397
|
-
return true;
|
|
398
|
-
return current !== cached.sidecarLoadedSignature;
|
|
228
|
+
return await this.revision(cached.session_id) !== cached.sidecarLoadedSignature;
|
|
399
229
|
}
|
|
400
|
-
// ── persistence ──────────────────────────────────────────────────────────
|
|
401
230
|
save(session, opts = {}) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
231
|
+
return this.serializeSession(session.session_id, () => this.saveNow(session, opts));
|
|
232
|
+
}
|
|
233
|
+
serializeSession(sid, fn, queue = this.writes) {
|
|
234
|
+
const result = (queue.get(sid) ?? Promise.resolve()).then(fn);
|
|
235
|
+
const settled = result.then(() => { }, () => { });
|
|
236
|
+
queue.set(sid, settled);
|
|
237
|
+
return result.finally(() => { if (queue.get(sid) === settled)
|
|
238
|
+
queue.delete(sid); });
|
|
239
|
+
}
|
|
240
|
+
async saveNow(session, opts) {
|
|
241
|
+
const sid = session.session_id;
|
|
242
|
+
if (!isSafeSessionId(sid))
|
|
243
|
+
throw new Error(`Unsafe session_id ${JSON.stringify(sid)}; refusing to write outside session store`);
|
|
244
|
+
if (session.loadedMetadataOnly)
|
|
245
|
+
throw new Error(`Refusing to save metadata-only session ${JSON.stringify(sid)}: would overwrite messages with []. Reload before mutating state.`);
|
|
409
246
|
const markerCurrent = session.state_db_seen_stamp === session.updated_at;
|
|
410
247
|
if (opts.touchUpdatedAt ?? true)
|
|
411
248
|
session.updated_at = this.deps.now();
|
|
412
249
|
if (markerCurrent)
|
|
413
250
|
session.state_db_seen_stamp = session.updated_at;
|
|
414
|
-
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
catch { /* backup is best effort */ }
|
|
442
|
-
}
|
|
251
|
+
session.messages = withBackgroundDisplayKinds(session.messages, session);
|
|
252
|
+
const prefix = session.prefixIndex;
|
|
253
|
+
let appendOnly = Boolean(prefix && session.messages.length >= prefix.length && prefix.generation === markedStateDbGeneration(session) && prefix.matchesToolCalls(session.tool_calls));
|
|
254
|
+
const payload = JSON.stringify(session.toDocument(), function (key, value) {
|
|
255
|
+
if (appendOnly && this === session.messages && Number(key) < prefix.length && !prefix.matchesRow(Number(key), value))
|
|
256
|
+
appendOnly = false;
|
|
257
|
+
return value;
|
|
258
|
+
});
|
|
259
|
+
const doc = JSON.parse(payload);
|
|
260
|
+
const rows = doc.messages;
|
|
261
|
+
delete doc.messages;
|
|
262
|
+
const snapshot = rows.map((row) => JSON.stringify(row));
|
|
263
|
+
const indexEntry = session.compact();
|
|
264
|
+
const previous = this.persistedMessages.get(session);
|
|
265
|
+
let messages = { kind: 'replace', rows };
|
|
266
|
+
if (previous) {
|
|
267
|
+
let from = 0;
|
|
268
|
+
while (from < previous.length && from < snapshot.length && previous[from] === snapshot[from])
|
|
269
|
+
from += 1;
|
|
270
|
+
if (from === previous.length)
|
|
271
|
+
messages = from === snapshot.length ? { kind: 'unchanged' } : { kind: 'append', rows: rows.slice(from) };
|
|
272
|
+
else if (snapshot.length >= previous.length) {
|
|
273
|
+
let tail = 0;
|
|
274
|
+
while (tail < previous.length - from && tail < snapshot.length - from && previous[previous.length - 1 - tail] === snapshot[snapshot.length - 1 - tail])
|
|
275
|
+
tail += 1;
|
|
276
|
+
messages = { kind: 'splice', from, deleteCount: previous.length - from - tail, rows: rows.slice(from, rows.length - tail) };
|
|
443
277
|
}
|
|
444
278
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (session.messages.length)
|
|
452
|
-
this.clearDeletedTombstone(session.session_id);
|
|
453
|
-
}
|
|
454
|
-
// ── index ────────────────────────────────────────────────────────────────
|
|
455
|
-
cleanupStaleTmpFiles() {
|
|
456
|
-
const cutoff = this.deps.now() - STALE_TMP_AGE_S;
|
|
457
|
-
try {
|
|
458
|
-
for (const name of readdirSync(this.sessionDir)) {
|
|
459
|
-
if (!name.includes('.tmp.'))
|
|
460
|
-
continue;
|
|
461
|
-
const p = join(this.sessionDir, name);
|
|
462
|
-
try {
|
|
463
|
-
if (statSync(p).mtimeMs / 1000 < cutoff)
|
|
464
|
-
unlinkSync(p);
|
|
465
|
-
}
|
|
466
|
-
catch { /* best effort */ }
|
|
279
|
+
const expected = session.sidecarLoadedSignature;
|
|
280
|
+
if (!rows.length && (session.active_stream_id || session.pending_user_message)) {
|
|
281
|
+
const stored = await this.backend.readDocument(sid, { metadataOnly: true });
|
|
282
|
+
if ((stored?.messageCount ?? 0) > 0) {
|
|
283
|
+
this.deps.log(`[webui] WARNING: refusing to overwrite session ${sid} messages with empty active/pending snapshot`);
|
|
284
|
+
return;
|
|
467
285
|
}
|
|
468
286
|
}
|
|
469
|
-
catch { /* dir may not exist */ }
|
|
470
|
-
}
|
|
471
|
-
invalidatePersistedIds() {
|
|
472
|
-
this.persistedIdsCache = null;
|
|
473
|
-
}
|
|
474
|
-
persistedIds() {
|
|
475
|
-
let mtimeNs = null;
|
|
476
287
|
try {
|
|
477
|
-
|
|
288
|
+
session.sidecarLoadedSignature = await this.backend.write(sid, { doc, messages }, expected);
|
|
478
289
|
}
|
|
479
|
-
catch {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (name.endsWith('.json') && !name.startsWith('_'))
|
|
488
|
-
ids.add(name.slice(0, -5));
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (error instanceof RevisionConflict) {
|
|
292
|
+
this.sessions.delete(sid);
|
|
293
|
+
const fresh = await this.load(sid, { repairDuplicates: false });
|
|
294
|
+
if (fresh)
|
|
295
|
+
this.sessions.set(sid, fresh);
|
|
296
|
+
}
|
|
297
|
+
throw error;
|
|
489
298
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
299
|
+
this.persistedMessages.set(session, snapshot);
|
|
300
|
+
if (appendOnly)
|
|
301
|
+
prefix.savedAppend(rows, session.sidecarLoadedSignature);
|
|
302
|
+
else
|
|
303
|
+
delete session.prefixIndex;
|
|
304
|
+
if (!(opts.skipIndex ?? false))
|
|
305
|
+
await this.queueIndexUpdate([indexEntry]);
|
|
306
|
+
if (rows.length)
|
|
307
|
+
await this.clearDeletedTombstone(sid);
|
|
308
|
+
}
|
|
309
|
+
async searchMessages(sids, terms, depth) {
|
|
310
|
+
const matches = new Map();
|
|
311
|
+
const remaining = [];
|
|
312
|
+
for (const sid of sids) {
|
|
313
|
+
const cached = this.sessions.get(sid);
|
|
314
|
+
if (!cached || cached.loadedMetadataOnly || await this.cachedLagsStorage(cached)) {
|
|
315
|
+
remaining.push(sid);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const rows = depth ? cached.messages.slice(0, depth) : cached.messages;
|
|
319
|
+
const idx = rows.findIndex((row) => sessionSearchMatches(sessionSearchMessageText(row), terms));
|
|
320
|
+
if (idx >= 0)
|
|
321
|
+
matches.set(sid, { idx, text: sessionSearchMessageText(rows[idx]) });
|
|
322
|
+
}
|
|
323
|
+
for (const [sid, match] of await this.backend.searchMessages(remaining, terms, depth))
|
|
324
|
+
matches.set(sid, match);
|
|
325
|
+
return matches;
|
|
326
|
+
}
|
|
327
|
+
async persistedIds() { return this.backend.ids(); }
|
|
328
|
+
async hasPersistedSessionFiles() { return (await this.persistedIds()).size > 0; }
|
|
329
|
+
async readIndexEntries() { return (await this.backend.index()).entries; }
|
|
330
|
+
async indexMessageCounts(entries) {
|
|
514
331
|
let rows = entries;
|
|
515
332
|
if (!rows) {
|
|
516
333
|
try {
|
|
517
|
-
rows =
|
|
334
|
+
rows = await this.readIndexEntries();
|
|
518
335
|
}
|
|
519
336
|
catch {
|
|
520
337
|
rows = [];
|
|
521
338
|
}
|
|
522
339
|
}
|
|
340
|
+
const out = new Map();
|
|
523
341
|
for (const row of rows) {
|
|
524
|
-
const sid = str(row.session_id);
|
|
525
|
-
const n = Number(row.message_count);
|
|
342
|
+
const sid = str(row.session_id), n = Number(row.message_count);
|
|
526
343
|
if (sid && Number.isInteger(n) && n >= 0)
|
|
527
344
|
out.set(sid, n);
|
|
528
345
|
}
|
|
529
346
|
return out;
|
|
530
347
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
this.parsedIndexCache = { signature, entries };
|
|
536
|
-
}
|
|
537
|
-
loadSessionFromPath(path) {
|
|
538
|
-
try {
|
|
539
|
-
const data = sanitizeSessionRow(JSON.parse(readFileSync(path, 'utf8')), this, basename(path));
|
|
540
|
-
if (!data)
|
|
541
|
-
return null;
|
|
542
|
-
const [messages] = collapseAdjacentDuplicatePartials(data.messages);
|
|
543
|
-
data.messages = messages;
|
|
544
|
-
return this.construct(data, data.profile ?? null);
|
|
545
|
-
}
|
|
546
|
-
catch {
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
348
|
+
async serializeIndex(fn) {
|
|
349
|
+
const pending = this.indexWriter.then(fn, fn);
|
|
350
|
+
this.indexWriter = pending.catch(() => { });
|
|
351
|
+
return pending;
|
|
549
352
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (!name.endsWith('.json') || name.startsWith('_'))
|
|
560
|
-
continue;
|
|
561
|
-
const s = this.loadSessionFromPath(join(this.sessionDir, name));
|
|
562
|
-
if (!s)
|
|
563
|
-
continue;
|
|
564
|
-
const c = s.compact();
|
|
565
|
-
const sid = str(c.session_id);
|
|
566
|
-
if (!sid)
|
|
567
|
-
continue;
|
|
568
|
-
const existing = entryMap.get(sid);
|
|
569
|
-
if (!existing || Number(c.message_count ?? 0) > Number(existing.message_count ?? 0))
|
|
570
|
-
entryMap.set(sid, c);
|
|
353
|
+
async rebuildIndexNow(opts = {}) {
|
|
354
|
+
const map = new Map();
|
|
355
|
+
for (const sid of await this.persistedIds()) {
|
|
356
|
+
try {
|
|
357
|
+
const session = await this.load(sid, { drafts: false, repairDuplicates: false });
|
|
358
|
+
if (session)
|
|
359
|
+
map.set(session.session_id, session.compact());
|
|
360
|
+
}
|
|
361
|
+
catch { /* corrupt sessions are omitted */ }
|
|
571
362
|
}
|
|
572
|
-
const entries = [...entryMap.values()];
|
|
573
363
|
if (!opts.diskOnly)
|
|
574
364
|
for (const s of this.sessions.values())
|
|
575
|
-
if (!
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
const onDisk = this.persistedIds();
|
|
589
|
-
let existing = this.readIndexEntries();
|
|
590
|
-
const inMemory = new Set(this.sessions.keys());
|
|
591
|
-
const updatedMap = new Map();
|
|
592
|
-
for (const update of updates) {
|
|
593
|
-
if (update instanceof Session)
|
|
594
|
-
updatedMap.set(update.session_id, update.compact());
|
|
595
|
-
else if (typeof update.session_id === 'string' && update.session_id)
|
|
596
|
-
updatedMap.set(update.session_id, update);
|
|
365
|
+
if (!map.has(s.session_id))
|
|
366
|
+
map.set(s.session_id, s.compact());
|
|
367
|
+
await this.backend.writeIndex([...map.values()].sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0)));
|
|
368
|
+
}
|
|
369
|
+
async rebuildIndex(opts = {}) { await this.serializeIndex(async () => { await this.rebuildIndexNow(opts); }); }
|
|
370
|
+
async writeIndex(updates) {
|
|
371
|
+
const snapshots = updates?.map((s) => s instanceof Session ? s.compact() : s);
|
|
372
|
+
await this.serializeIndex(async () => {
|
|
373
|
+
if (!snapshots || !await this.backend.hasIndex()) {
|
|
374
|
+
await this.rebuildIndexNow();
|
|
375
|
+
return;
|
|
597
376
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const positions = new Map(existing.map((e, i) => [String(e.session_id), i]));
|
|
602
|
-
for (const [sid, entry] of updatedMap) {
|
|
603
|
-
const position = positions.get(sid);
|
|
604
|
-
if (position === undefined) {
|
|
605
|
-
existing.push(entry);
|
|
606
|
-
positions.set(sid, existing.length - 1);
|
|
607
|
-
changed = true;
|
|
608
|
-
}
|
|
609
|
-
else if (JSON.stringify(existing[position]) !== JSON.stringify(entry)) {
|
|
610
|
-
existing[position] = entry;
|
|
611
|
-
changed = true;
|
|
612
|
-
}
|
|
377
|
+
let rows;
|
|
378
|
+
try {
|
|
379
|
+
rows = await this.readIndexEntries();
|
|
613
380
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (oldOrder !== existing.map((e) => e.session_id).join('\u0000'))
|
|
617
|
-
changed = true;
|
|
618
|
-
if (!changed)
|
|
381
|
+
catch {
|
|
382
|
+
await this.rebuildIndexNow();
|
|
619
383
|
return;
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
this.
|
|
384
|
+
}
|
|
385
|
+
const previous = JSON.stringify(rows);
|
|
386
|
+
const ids = await this.persistedIds();
|
|
387
|
+
rows = rows.filter((r) => ids.has(str(r.session_id)) || this.sessions.has(str(r.session_id)));
|
|
388
|
+
const positions = new Map(rows.map((r, i) => [str(r.session_id), i]));
|
|
389
|
+
for (const row of snapshots) {
|
|
390
|
+
const sid = str(row.session_id);
|
|
391
|
+
if (!sid)
|
|
392
|
+
continue;
|
|
393
|
+
const i = positions.get(sid);
|
|
394
|
+
if (i === undefined) {
|
|
395
|
+
positions.set(sid, rows.length);
|
|
396
|
+
rows.push(row);
|
|
397
|
+
}
|
|
398
|
+
else
|
|
399
|
+
rows[i] = row;
|
|
400
|
+
}
|
|
401
|
+
rows.sort((a, b) => Number(b.updated_at ?? 0) - Number(a.updated_at ?? 0));
|
|
402
|
+
if (JSON.stringify(rows) !== previous)
|
|
403
|
+
await this.backend.writeIndex(rows);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
queueIndexUpdate(updates) {
|
|
407
|
+
for (const update of updates) {
|
|
408
|
+
const row = update instanceof Session ? update.compact() : update;
|
|
409
|
+
const sid = str(row.session_id);
|
|
410
|
+
if (sid)
|
|
411
|
+
this.indexPending.set(sid, row);
|
|
624
412
|
}
|
|
413
|
+
this.indexFlush ??= Promise.resolve().then(async () => {
|
|
414
|
+
try {
|
|
415
|
+
while (this.indexPending.size) {
|
|
416
|
+
const rows = [...this.indexPending.values()];
|
|
417
|
+
this.indexPending.clear();
|
|
418
|
+
await this.writeIndex(rows);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
this.indexFlush = null;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
return this.indexFlush;
|
|
625
426
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
this.indexPending.set(s.session_id, s.compact());
|
|
630
|
-
this.flushIndexNow();
|
|
631
|
-
}
|
|
632
|
-
flushIndexNow() {
|
|
633
|
-
const pending = [...this.indexPending.values()];
|
|
634
|
-
this.indexPending.clear();
|
|
635
|
-
if (pending.length)
|
|
636
|
-
this.writeIndex(pending);
|
|
637
|
-
}
|
|
638
|
-
pruneFromIndex(sid) {
|
|
639
|
-
if (!sid || !existsSync(this.indexFile))
|
|
640
|
-
return;
|
|
641
|
-
try {
|
|
642
|
-
const existing = JSON.parse(readFileSync(this.indexFile, 'utf8'));
|
|
643
|
-
if (!Array.isArray(existing))
|
|
644
|
-
throw new Error('session index must be a list');
|
|
645
|
-
const pruned = existing.filter((e) => e.session_id !== sid);
|
|
646
|
-
if (pruned.length === existing.length)
|
|
427
|
+
async pruneFromIndex(sid) {
|
|
428
|
+
await this.serializeIndex(async () => {
|
|
429
|
+
if (!sid || !await this.backend.hasIndex())
|
|
647
430
|
return;
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
loadDeletedTombstone() {
|
|
659
|
-
try {
|
|
660
|
-
if (!existsSync(this.deletedTombstoneFile))
|
|
661
|
-
return new Set();
|
|
662
|
-
const raw = JSON.parse(readFileSync(this.deletedTombstoneFile, 'utf8'));
|
|
663
|
-
if (!raw || typeof raw !== 'object' || Number(raw.version) !== DELETED_TOMBSTONE_VERSION || !Array.isArray(raw.ids))
|
|
664
|
-
return new Set();
|
|
665
|
-
return new Set(raw.ids.map((v) => String(v ?? '').trim()).filter(Boolean));
|
|
666
|
-
}
|
|
667
|
-
catch {
|
|
668
|
-
return new Set();
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
saveDeletedTombstone(ids) {
|
|
672
|
-
let sorted = [...ids].sort();
|
|
673
|
-
if (sorted.length > DELETED_TOMBSTONE_CAP)
|
|
674
|
-
sorted = sorted.slice(-DELETED_TOMBSTONE_CAP);
|
|
675
|
-
try {
|
|
676
|
-
mkdirSync(this.sessionDir, { recursive: true });
|
|
677
|
-
writeAtomic(this.deletedTombstoneFile, JSON.stringify({ version: DELETED_TOMBSTONE_VERSION, ids: sorted }, null, 2));
|
|
678
|
-
}
|
|
679
|
-
catch { /* best effort */ }
|
|
680
|
-
}
|
|
681
|
-
recordDeletedTombstone(sid) {
|
|
682
|
-
const ids = this.loadDeletedTombstone();
|
|
683
|
-
if (ids.has(sid))
|
|
684
|
-
return;
|
|
685
|
-
ids.add(sid);
|
|
686
|
-
this.saveDeletedTombstone(ids);
|
|
687
|
-
}
|
|
688
|
-
clearDeletedTombstone(sid) {
|
|
689
|
-
const ids = this.loadDeletedTombstone();
|
|
690
|
-
if (!ids.has(sid))
|
|
691
|
-
return;
|
|
692
|
-
ids.delete(sid);
|
|
693
|
-
if (ids.size)
|
|
694
|
-
this.saveDeletedTombstone(ids);
|
|
695
|
-
else
|
|
696
|
-
rmSync(this.deletedTombstoneFile, { force: true });
|
|
697
|
-
}
|
|
698
|
-
/** The index marked this id as a deleted WebUI session (Python `_marks_deleted_webui_session`, tombstone half). */
|
|
699
|
-
wasDeleted(sid) {
|
|
700
|
-
return this.loadDeletedTombstone().has(sid);
|
|
701
|
-
}
|
|
702
|
-
// ── eviction ─────────────────────────────────────────────────────────────
|
|
703
|
-
persistedMessageCount(sid) {
|
|
704
|
-
const path = this.pathFor(sid);
|
|
705
|
-
if (!existsSync(path))
|
|
706
|
-
return null;
|
|
707
|
-
const count = readPersistedMessageCount(path);
|
|
708
|
-
if (count !== null)
|
|
709
|
-
return count;
|
|
710
|
-
try {
|
|
711
|
-
const data = JSON.parse(readFileSync(path, 'utf8'));
|
|
712
|
-
return Array.isArray(data.messages) ? data.messages.length : 0;
|
|
713
|
-
}
|
|
714
|
-
catch {
|
|
715
|
-
return null;
|
|
716
|
-
}
|
|
431
|
+
try {
|
|
432
|
+
const rows = await this.readIndexEntries();
|
|
433
|
+
const pruned = rows.filter((r) => r.session_id !== sid);
|
|
434
|
+
if (pruned.length !== rows.length)
|
|
435
|
+
await this.backend.writeIndex(pruned);
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
await this.rebuildIndexNow();
|
|
439
|
+
}
|
|
440
|
+
});
|
|
717
441
|
}
|
|
718
|
-
|
|
719
|
-
|
|
442
|
+
async loadDeletedTombstone() { return this.backend.tombstones.all(); }
|
|
443
|
+
async recordDeletedTombstone(sid) { return this.backend.tombstones.add(sid); }
|
|
444
|
+
async clearDeletedTombstone(sid) { return this.backend.tombstones.remove(sid); }
|
|
445
|
+
async wasDeleted(sid) { return this.backend.tombstones.has(sid); }
|
|
446
|
+
async isEvictable(s) {
|
|
720
447
|
if (!s)
|
|
721
448
|
return true;
|
|
722
|
-
if (s.active_stream_id || s.pending_user_message || s.pending_started_at)
|
|
723
|
-
return false;
|
|
724
|
-
if (!s.session_id)
|
|
449
|
+
if (s.active_stream_id || s.pending_user_message || s.pending_started_at || !s.session_id)
|
|
725
450
|
return false;
|
|
726
451
|
if (s.loadedMetadataOnly)
|
|
727
452
|
return true;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if (existsSync(this.pathFor(s.session_id)))
|
|
453
|
+
let count = null;
|
|
454
|
+
try {
|
|
455
|
+
count = (await this.backend.readDocument(s.session_id, { metadataOnly: true }))?.messageCount ?? null;
|
|
456
|
+
}
|
|
457
|
+
catch { /* corrupt */ }
|
|
458
|
+
if (count === null) {
|
|
459
|
+
if (s.messages.length || Object.keys(s.composer_draft).length || await this.exists(s.session_id))
|
|
736
460
|
return false;
|
|
737
461
|
return this.deps.now() - s.created_at > UNSAVED_SHELL_GRACE_S;
|
|
738
462
|
}
|
|
739
|
-
|
|
740
|
-
return true;
|
|
741
|
-
return diskCount >= inMemory;
|
|
463
|
+
return count >= s.messages.length;
|
|
742
464
|
}
|
|
743
|
-
evictOverCap(cap) {
|
|
465
|
+
async evictOverCap(cap) {
|
|
744
466
|
let limit = cap ?? this.deps.cacheMax();
|
|
745
467
|
if (!Number.isInteger(limit) || limit < 1)
|
|
746
468
|
limit = 100;
|
|
@@ -748,63 +470,35 @@ export class SessionStore {
|
|
|
748
470
|
for (const sid of [...this.sessions.keys()]) {
|
|
749
471
|
if (this.sessions.size <= limit)
|
|
750
472
|
break;
|
|
751
|
-
|
|
473
|
+
const session = this.sessions.get(sid);
|
|
474
|
+
if (await this.isEvictable(session) && this.sessions.get(sid) === session && !session?.active_stream_id && !session?.pending_user_message && !session?.pending_started_at) {
|
|
752
475
|
this.sessions.delete(sid);
|
|
753
476
|
evicted += 1;
|
|
754
477
|
}
|
|
755
478
|
}
|
|
756
479
|
return evicted;
|
|
757
480
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
catch { /* ignore */ }
|
|
776
|
-
try {
|
|
777
|
-
rmSync(`${path}.bak`, { force: true });
|
|
778
|
-
}
|
|
779
|
-
catch { /* ignore */ }
|
|
780
|
-
try {
|
|
781
|
-
this.deps.drafts.delete(sid);
|
|
782
|
-
}
|
|
783
|
-
catch { /* ignore */ }
|
|
784
|
-
// TAL-372: the session's background work records (`BackgroundTaskStore`) go with it.
|
|
785
|
-
try {
|
|
786
|
-
rmSync(join(this.sessionDir, '_background', `${sid}.json`), { force: true });
|
|
787
|
-
}
|
|
788
|
-
catch { /* ignore */ }
|
|
789
|
-
this.deps.onDeleted?.(sid);
|
|
790
|
-
if (opts.tombstone ?? true)
|
|
791
|
-
this.recordDeletedTombstone(sid);
|
|
792
|
-
return true;
|
|
481
|
+
delete(sid, opts = {}) {
|
|
482
|
+
return this.serializeSession(sid, async () => {
|
|
483
|
+
if (!await this.backend.delete(sid, { tombstone: opts.tombstone ?? true }))
|
|
484
|
+
return false;
|
|
485
|
+
this.sessions.delete(sid);
|
|
486
|
+
try {
|
|
487
|
+
await this.pruneFromIndex(sid);
|
|
488
|
+
}
|
|
489
|
+
catch { /* ignore */ }
|
|
490
|
+
try {
|
|
491
|
+
(await this.deps.drafts.delete(sid));
|
|
492
|
+
}
|
|
493
|
+
catch { /* ignore */ }
|
|
494
|
+
await this.deps.onDeleted?.(sid);
|
|
495
|
+
return true;
|
|
496
|
+
});
|
|
793
497
|
}
|
|
794
|
-
|
|
795
|
-
scanAll() {
|
|
498
|
+
async scanAll() {
|
|
796
499
|
const out = [];
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
names = readdirSync(this.sessionDir);
|
|
800
|
-
}
|
|
801
|
-
catch {
|
|
802
|
-
return out;
|
|
803
|
-
}
|
|
804
|
-
for (const name of names) {
|
|
805
|
-
if (!name.endsWith('.json') || name.startsWith('_'))
|
|
806
|
-
continue;
|
|
807
|
-
const s = this.load(name.slice(0, -5));
|
|
500
|
+
for (const sid of await this.persistedIds()) {
|
|
501
|
+
const s = await this.load(sid);
|
|
808
502
|
if (s)
|
|
809
503
|
out.push(s);
|
|
810
504
|
}
|