@maudecode/talaria-web 1.17.2 → 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.d.ts +1 -1
- package/dist/api/auth-raw.js +20 -20
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +7 -1
- package/dist/api/automation-raw.js +74 -27
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +128 -23
- package/dist/api/automation-router.js +29 -13
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +1845 -40
- package/dist/api/chat-router.js +196 -114
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +37 -3
- package/dist/api/git-router.js +47 -48
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +9 -1
- package/dist/api/raw-routes.js +286 -73
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +16 -6
- package/dist/api/router.js +28 -26
- 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 +3312 -131
- package/dist/api/sessions-router.js +287 -157
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +457 -8
- package/dist/api/settings-router.js +54 -33
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +7 -2
- package/dist/api/sse-routes.js +109 -62
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.d.ts +5 -0
- package/dist/api/tools-raw.js +63 -55
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +998 -85
- package/dist/api/tools-router.js +447 -61
- 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 +98 -59
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +15 -11
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +5 -2
- package/dist/auth/oidc.js +8 -7
- package/dist/auth/oidc.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 +15 -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 +116 -60
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +19 -6
- 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 +31 -12
- package/dist/http/context.js +60 -24
- package/dist/http/context.js.map +1 -1
- package/dist/http/csp.d.ts +2 -1
- package/dist/http/csp.js +3 -2
- package/dist/http/csp.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 +20 -4
- package/dist/profiles/profiles.js +115 -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 +84 -14
- package/dist/providers/catalog.js +474 -151
- 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/raster-image.d.ts +7 -0
- package/dist/raster-image.js +267 -0
- package/dist/raster-image.js.map +1 -0
- package/dist/redact.js +342 -46
- 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 +305 -96
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +15 -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 -27
- package/dist/sessions/background-tasks.js +89 -97
- 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 +27 -6
- package/dist/sessions/cli-sessions.js +152 -42
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +77 -9
- package/dist/sessions/completions.js +482 -45
- 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/export.d.ts +5 -0
- package/dist/sessions/export.js +30 -0
- package/dist/sessions/export.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/gateway-watcher.d.ts +7 -3
- package/dist/sessions/gateway-watcher.js +31 -19
- package/dist/sessions/gateway-watcher.js.map +1 -1
- 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/handoff.d.ts +32 -0
- package/dist/sessions/handoff.js +114 -0
- package/dist/sessions/handoff.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 +31 -16
- package/dist/sessions/list.js +139 -129
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +69 -13
- package/dist/sessions/merge.js +265 -64
- 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 +19 -0
- package/dist/sessions/recovery.js +322 -0
- package/dist/sessions/recovery.js.map +1 -0
- package/dist/sessions/relay.d.ts +38 -19
- package/dist/sessions/relay.js +302 -79
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +260 -55
- package/dist/sessions/service.js +1448 -462
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +24 -2
- package/dist/sessions/session.js +69 -7
- 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.d.ts +1 -0
- package/dist/sessions/state-db-worker.js +35 -0
- package/dist/sessions/state-db-worker.js.map +1 -0
- package/dist/sessions/state-db.d.ts +113 -7
- package/dist/sessions/state-db.js +861 -44
- 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 +65 -68
- package/dist/sessions/store.js +305 -602
- 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.d.ts +2 -0
- package/dist/sessions/titles.js +5 -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 +30 -50
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +11 -2
- package/dist/sessions/turn-context.js +18 -11
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +122 -17
- package/dist/sessions/turn.js +939 -358
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +54 -0
- package/dist/sessions/wakeup-pause.js +163 -0
- package/dist/sessions/wakeup-pause.js.map +1 -0
- 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 +178 -184
- 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.d.ts +2 -2
- package/dist/sidecar/fake.js +19 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/startup.d.ts +13 -5
- package/dist/startup.js +48 -0
- package/dist/startup.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/test/ts-worker-hooks.d.ts +1 -0
- package/dist/test/ts-worker-hooks.js +24 -0
- package/dist/test/ts-worker-hooks.js.map +1 -0
- package/dist/text/diff.d.ts +9 -14
- package/dist/text/diff.js +74 -108
- package/dist/text/diff.js.map +1 -1
- 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 +122 -18
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +53 -0
- package/dist/tools/dashboard-plugins.js +188 -0
- package/dist/tools/dashboard-plugins.js.map +1 -0
- package/dist/tools/extensions.d.ts +18 -5
- package/dist/tools/extensions.js +344 -266
- 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 +44 -0
- package/dist/tools/health.js +201 -46
- 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/mcp.d.ts +13 -0
- package/dist/tools/mcp.js +88 -0
- package/dist/tools/mcp.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/project-os.d.ts +11 -0
- package/dist/tools/project-os.js +267 -0
- package/dist/tools/project-os.js.map +1 -0
- package/dist/tools/skills.d.ts +14 -5
- package/dist/tools/skills.js +49 -16
- 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 +110 -32
- package/dist/tools/updates.js +1112 -294
- package/dist/tools/updates.js.map +1 -1
- package/dist/tools/wiki.d.ts +68 -0
- package/dist/tools/wiki.js +413 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +5 -0
- package/dist/util.js.map +1 -1
- package/dist/workspace/escape.d.ts +61 -0
- package/dist/workspace/escape.js +149 -0
- package/dist/workspace/escape.js.map +1 -0
- package/dist/workspace/extract.d.ts +22 -0
- package/dist/workspace/extract.js +308 -0
- package/dist/workspace/extract.js.map +1 -0
- package/dist/workspace/fs.d.ts +12 -0
- package/dist/workspace/fs.js +92 -23
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/git.d.ts +34 -1
- package/dist/workspace/git.js +328 -35
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +16 -13
- package/dist/workspace/media.js +43 -61
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/rollback.d.ts +36 -22
- package/dist/workspace/rollback.js +178 -272
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/unzip.js +4 -1
- package/dist/workspace/unzip.js.map +1 -1
- package/dist/workspace/upload.d.ts +7 -4
- package/dist/workspace/upload.js +5 -2
- package/dist/workspace/upload.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.d.ts +2 -1
- package/dist/workspace/worktrees.js +179 -42
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +7 -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 +32 -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 +56 -3
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +166 -24
- package/sidecar/talaria_sidecar/methods/chat.py +852 -135
- package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
- package/sidecar/talaria_sidecar/methods/commands.py +294 -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 +395 -36
- package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +212 -55
- 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 +108 -34
- package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
- 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 +218 -15
- package/sidecar/talaria_sidecar/methods/profiles.py +121 -13
- package/sidecar/talaria_sidecar/methods/providers.py +217 -28
- 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 +316 -75
- 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 +98 -13
- package/sidecar/talaria_sidecar/speed.py +35 -0
- package/static/dist/FILES.txt +126 -126
- 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-opyh9y0v.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-bfd4h3id.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-b2x1lqot.js → abnfDiagram-VCTEODGH-gjayq3ra.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-h38o5l7b.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-bqquxv2u.js → blockDiagram-I7D4REHJ-joedghi3.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-f3fb8w6z.js → c4Diagram-7LVT6UL2-hyrjrohe.js} +1 -1
- package/static/dist/assets/channel-es95o4y8.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-bzfyyzp3.js → chunk-4HAMMTFA-koouqbut.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-ly99rfx5.js → chunk-75Z2AOVW-bl4bhdnt.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-bg6ecfqf.js → chunk-DU6HZSFF-gpedttsp.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-e8k7mmqw.js → chunk-F27PBJKO-d8xxi0zi.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-cmalnoaj.js → chunk-GMAD6QVW-jro0hl73.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-gmif5o01.js → chunk-GVQU2GXP-djn6gbyu.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-f1nwwh6u.js → chunk-IMKFNOWR-vw4p5p7o.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-oktfh5v7.js → chunk-L3NEJ4N5-n1hj77q8.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-b1sdx96z.js → chunk-OSK3NFVY-oy0gwn2n.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-fhu4nzxt.js → chunk-P2QGCYS3-onf1hz6c.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-em31rnef.js → chunk-POPQ4Y6H-m8sqn7cs.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-bxhhyftw.js → chunk-PWAF6VOD-ek4smc8x.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-fmrvwcav.js → chunk-SHT3W25Y-g9gfvefo.js} +3 -3
- package/static/dist/assets/{chunk-SVP7TREG-lymukl2j.js → chunk-SVP7TREG-f9nfv2cu.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-lmza6k23.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-fw5b1pc6.js → copy-jamtd1pf.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-czlhbyhs.js → cynefin-OW5HDTMX-f8xfdd3f.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-gq7mvswb.js → cynefinDiagram-5FMLGOSQ-n9lb99h9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-cqdbf7dr.js → dagre-GXQ25YYZ-dgailddi.js} +4 -4
- package/static/dist/assets/{diagram-S7CK7UJ4-o6arxa58.js → diagram-S7CK7UJ4-m6aeoe7c.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-nhnd68tz.js → diagram-UQ7AKVKN-jall3j0x.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-dk2sclzn.js → diagram-VSXAHHWV-hbawi7hn.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-n3k5nyx3.js → diagram-VX7I27RA-hendl2tt.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-m6q9cwdh.js → diagram-Z3DM3KII-igwc35p3.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-lsa5641p.js → ebnfDiagram-PWID7BFC-c64knz08.js} +1 -1
- package/static/dist/assets/{erDiagram-RLTQ6QDP-fo2ckbe6.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-nzqz6x9g.js → ganttDiagram-EL5Y4UJY-ma6jxj5i.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-gukvcxe6.js → gitGraph-4MIJSDKK-naa9w6xa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-lqhdp8f0.js → gitGraphDiagram-WWUBYQGX-hzltvsod.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-igw6cp39.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-gvu7a93l.js → info-A6RAGUB7-mn2ut5ko.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-m8t8g6w1.js → infoDiagram-27XIBGKW-h6kuqvsb.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-l07vkbgj.js → ishikawaDiagram-5VMMS53U-e7bnwq7h.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-hybkgj8l.js → journeyDiagram-3NMN7TZE-etd1bi6p.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-by13uvtn.js → kanban-definition-UXKFOSKX-emb1g9dz.js} +4 -4
- package/static/dist/assets/lazyRouteComponent-o816ytzl.js +1 -0
- package/static/dist/assets/{line-osxq6fq7.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-i91oxmou.js → mermaid-parser.core-bnre27u8.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-m82mx8ww.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-nbtgge2o.js → packet-AYTQ26CC-hatq7m2e.js} +1 -1
- package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
- package/static/dist/assets/{pegDiagram-XKGWAZYB-f1zxzga1.js → pegDiagram-XKGWAZYB-gwisz8jv.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-of8vuhxs.js → pie-WAS4IAKB-fpftf0mk.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-o7y8ftio.js → pieDiagram-E7YTZNPT-khm1o9a6.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-iqiwia1t.js → quadrantDiagram-AXDQQJYC-owqc4uqn.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-delnsy2v.js → radar-RG4KPBEZ-cntfwjpq.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-nspnyq9u.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-z41tqx5z.js → railroadDiagram-O6MQD6OU-etuh0qa0.js} +1 -1
- package/static/dist/assets/{refresh-cw-nt1dtsx4.js → refresh-cw-mqbkv2a3.js} +1 -1
- package/static/dist/assets/refresh-l29ljp76.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-bdvif194.js → requirementDiagram-BXWQKSXE-oq2ulk22.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-h73e0rol.js → sankeyDiagram-P5KCCOFB-iqc02emv.js} +1 -1
- package/static/dist/assets/saved-ndd4t2xb.js +1 -0
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-iv95mb1z.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-k417ixmf.js → stateDiagram-D77RDMKH-dnpt1ato.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-esola70w.js → swimlanes-42K2YHIH-loqbdazb.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-getrz2y8.js → timeline-definition-24CTP7MA-ol2hnvma.js} +1 -1
- package/static/dist/assets/toast-i3h34v6o.js +64 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-hc7ap1q7.js → treeView-Q6P3EWNA-idrj6iox.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-b7k1syy5.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-lhg566ew.js → useParams-gjc70qru.js} +1 -1
- package/static/dist/assets/{useSearch-lbrh6ejn.js → useSearch-e6ae539u.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-n6pcy5qb.js → vennDiagram-4TSXK5OY-bvwr9o2z.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-lpa18vz1.js → wardley-WFR3VGLG-31ui8tma.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-kpclwwg7.js → wardleyDiagram-VM6X3IG4-k5wqtx4b.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-paa2n8y0.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-fb2td9zi.js +0 -1
- package/static/dist/assets/ChatPage-cj96kiy9.js +0 -25
- package/static/dist/assets/Dialog-kxcg7ard.js +0 -1
- package/static/dist/assets/ExtensionRoute-jf7h0754.js +0 -1
- package/static/dist/assets/HubRoute-gmanpd5y.js +0 -2
- package/static/dist/assets/Markdown-mdwlvj3w.js +0 -598
- package/static/dist/assets/RightPanel-ftvrjlcp.js +0 -1
- package/static/dist/assets/Select-bilaypej.js +0 -1
- package/static/dist/assets/SessionListPanel-og8n36qs.js +0 -1
- package/static/dist/assets/SettingsLayout-jonjefmj.js +0 -1
- package/static/dist/assets/States-nmejrwrm.js +0 -1
- package/static/dist/assets/Toaster-nnl8zkt9.js +0 -1
- package/static/dist/assets/_app-gdidzd94.js +0 -1
- package/static/dist/assets/_app.index-hbarzbjx.js +0 -1
- package/static/dist/assets/_app.insights-luywlarz.js +0 -1
- package/static/dist/assets/_app.kanban-kincnbr8.js +0 -1
- package/static/dist/assets/_app.logs-oi2gr3gs.js +0 -1
- package/static/dist/assets/_app.memory-b6jfivt6.js +0 -1
- package/static/dist/assets/_app.profiles-lpvixmqp.js +0 -1
- package/static/dist/assets/_app.settings-vowx3iq0.js +0 -1
- package/static/dist/assets/_app.settings._section-cnntg1tu.js +0 -3
- package/static/dist/assets/_app.skills-fb57lpau.js +0 -1
- package/static/dist/assets/_app.tasks-eszpx1q7.js +0 -2
- package/static/dist/assets/_app.todos-nr05ac6w.js +0 -1
- package/static/dist/assets/_app.workspaces-d2luomu6.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +0 -1
- package/static/dist/assets/back-bemaer09.js +0 -1
- package/static/dist/assets/cancel-drd0g6d9.js +0 -1
- package/static/dist/assets/channel-cpdgf867.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +0 -1
- package/static/dist/assets/copied-ima3pysb.js +0 -1
- package/static/dist/assets/endpoints-d33siokx.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +0 -1
- package/static/dist/assets/index-cxq58xb1.js +0 -11
- package/static/dist/assets/index-hgtx08n5.css +0 -2
- package/static/dist/assets/lazyRouteComponent-af0lvgjx.js +0 -1
- package/static/dist/assets/link-jyxicdhg.js +0 -1
- package/static/dist/assets/login-ec21t39x.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +0 -1
- package/static/dist/assets/onboarding-btnltvyz.js +0 -1
- package/static/dist/assets/onboarding_model_label-dp7xffoh.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +0 -1
- package/static/dist/assets/refresh-b4uams1i.js +0 -1
- package/static/dist/assets/saved-kg52csy7.js +0 -1
- package/static/dist/assets/settings_section_extensions_title-ej0iapxe.js +0 -1
- package/static/dist/assets/share._token-f97e94r0.js +0 -2
- package/static/dist/assets/sliders-horizontal-fj18it47.js +0 -1
- package/static/dist/assets/sse-fwpqtzbg.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +0 -8
- package/static/dist/assets/todoStore-eginf9h4.js +0 -1
- package/static/dist/assets/tts-lxdxnmjd.js +0 -1
- package/static/dist/assets/untitled-ovpfthg4.js +0 -1
- package/static/dist/assets/useForm-bcblv1rx.js +0 -1
- package/static/dist/assets/useLocale-fi5jb8ma.js +0 -1
package/dist/sessions/relay.js
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
+
import { filterAsync } from '../util.js';
|
|
1
2
|
/**
|
|
2
3
|
* Talaria Relay: publisher pairing, profile enrollment, browser presence
|
|
3
4
|
* leases, and the best-effort snapshot publisher (Python
|
|
4
5
|
* `api/talaria_relay.py`). Snapshots are Ed25519-signed complete states per
|
|
5
|
-
* enrolled profile; failures back off and never block a turn.
|
|
6
|
+
* enrolled profile; failures back off and never block a turn. Each session that
|
|
7
|
+
* first appears in an enrolled profile is announced once with a signed
|
|
8
|
+
* session-started event (contracts/fixtures/publisher-session-started.json).
|
|
6
9
|
*/
|
|
7
10
|
import { readCapped } from '../http/capped.js';
|
|
8
11
|
import { createHash, createPrivateKey, generateKeyPairSync, randomUUID, sign as cryptoSign } from 'node:crypto';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { updateDocument } from './storage/document.js';
|
|
12
15
|
import { str } from '../util.js';
|
|
13
16
|
export const PRESENCE_LEASE_SECONDS = 90;
|
|
14
|
-
const PRESENCE_MAX_LEASES = 256;
|
|
17
|
+
export const PRESENCE_MAX_LEASES = 256;
|
|
18
|
+
/** How long a tab's sequence fence outlives its lease, so a reloaded tab whose clock moved back recovers. */
|
|
19
|
+
export const PRESENCE_FENCE_SECONDS = 15 * 60;
|
|
15
20
|
const PRESENCE_TAB_RE = /^[A-Za-z0-9_-]{8,64}$/;
|
|
16
21
|
const DEFAULT_RELAY_URL = 'https://relay.talaria.kil.dev';
|
|
17
22
|
const TERMINAL_RETENTION_S = 15 * 60;
|
|
18
23
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
24
|
+
// Sessions started longer ago than this are never announced, which bounds the started ledger.
|
|
25
|
+
const STARTED_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
19
26
|
const b64u = (data) => data.toString('base64url');
|
|
20
27
|
export class RelayPairingError extends Error {
|
|
21
28
|
status;
|
|
@@ -49,13 +56,12 @@ export function validatedOrigin(raw, httpsOnly = false) {
|
|
|
49
56
|
}
|
|
50
57
|
return value;
|
|
51
58
|
}
|
|
52
|
-
export function relayStatePath(stateDir) { return join(stateDir, 'talaria-relay.json'); }
|
|
53
59
|
/** Python `RelayConfig.from_state`: null when absent or a v1 file (registration is required again); throws on corruption. */
|
|
54
|
-
export function loadRelayConfig(
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
60
|
+
export async function loadRelayConfig(documents) {
|
|
61
|
+
const record = await documents.get('relay', 'config');
|
|
62
|
+
if (!record)
|
|
57
63
|
return null;
|
|
58
|
-
const values =
|
|
64
|
+
const values = record.value;
|
|
59
65
|
if (!values || typeof values !== 'object' || Array.isArray(values))
|
|
60
66
|
throw new Error('saved relay configuration is not an object');
|
|
61
67
|
const v = values;
|
|
@@ -71,32 +77,54 @@ export function loadRelayConfig(stateDir) {
|
|
|
71
77
|
throw new Error('saved relay profiles are invalid');
|
|
72
78
|
normalized[name] = { identity: p.identity, profile_id: p.profile_id };
|
|
73
79
|
}
|
|
74
|
-
return { url: validatedOrigin(str(v.url)), publisher_id: validatedOrigin(str(v.publisher_id)), key_id: str(v.key_id), private_key_path: str(v.private_key_path), profiles: normalized };
|
|
80
|
+
return { url: validatedOrigin(str(v.url)), publisher_id: validatedOrigin(str(v.publisher_id)), key_id: str(v.key_id), private_key_path: str(v.private_key_path), ...(typeof v.private_key_document === 'string' ? { private_key_document: v.private_key_document } : {}), profiles: normalized };
|
|
75
81
|
}
|
|
76
|
-
export function saveRelayConfig(
|
|
82
|
+
export async function saveRelayConfig(documents, config) {
|
|
77
83
|
const payload = { key_id: config.key_id, private_key_path: config.private_key_path, profiles: sortedProfiles(config.profiles), publisher_id: config.publisher_id, url: config.url, version: 2 };
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
const value = { ...payload, ...(config.private_key_document ? { private_key_document: config.private_key_document } : {}) };
|
|
85
|
+
await updateDocument(documents, 'relay', 'config', () => ({ value, result: undefined }), { replaceInvalid: true });
|
|
80
86
|
}
|
|
81
87
|
function sortedProfiles(profiles) {
|
|
82
88
|
return Object.fromEntries(Object.keys(profiles).sort().map((k) => [k, { identity: profiles[k].identity, profile_id: profiles[k].profile_id }]));
|
|
83
89
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return identity;
|
|
90
|
+
// The started ledger records announcements; deletions need a separate, non-expiring delivery outbox.
|
|
91
|
+
const DeletedViewSchema = z.object({
|
|
92
|
+
id: z.string().min(1), sessionId: z.string().min(1), profile: z.string().min(1),
|
|
93
|
+
through: z.number().int().nonnegative(), identity: z.string().nullable(),
|
|
94
|
+
scope: z.object({ url: z.string(), publisherId: z.string(), profileId: z.string() }).nullable(),
|
|
95
|
+
});
|
|
96
|
+
const DeletedViewsSchema = z.object({ version: z.literal(1), views: z.array(DeletedViewSchema) });
|
|
97
|
+
// A staged deletion cannot clear a session that survived a failed removal or a restart before removal.
|
|
98
|
+
async function hasSessionData(store, sid) {
|
|
99
|
+
if (store.sessions.has(sid))
|
|
100
|
+
return true;
|
|
101
|
+
return (await store.exists(sid));
|
|
97
102
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
103
|
+
function parseDeletedViews(raw) { return raw === undefined ? [] : DeletedViewsSchema.parse(raw).views; }
|
|
104
|
+
async function loadDeletedViews(documents) { return parseDeletedViews((await documents.get('relay', 'deleted'))?.value); }
|
|
105
|
+
async function mutateDeletedViews(documents, edit) {
|
|
106
|
+
return (await updateDocument(documents, 'relay', 'deleted', async (raw) => {
|
|
107
|
+
const views = parseDeletedViews(raw);
|
|
108
|
+
const result = await edit(views);
|
|
109
|
+
return { value: { version: 1, views }, result };
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
/** The profile identity lives in the selected backend, with the JSON adapter retaining its old private file. */
|
|
113
|
+
export async function profileIdentity(deps, profile) {
|
|
114
|
+
if (!statSync(deps.profileHome(profile)).isDirectory())
|
|
115
|
+
throw new Error('Hermes profile is unavailable');
|
|
116
|
+
return (await updateDocument(deps.documents, 'relay-identities', profile, (raw) => {
|
|
117
|
+
const identity = typeof raw === 'string' ? raw.trim() : `pfi_${randomUUID().replace(/-/g, '')}`;
|
|
118
|
+
if (!/^pfi_[0-9a-f]{32}$/.test(identity))
|
|
119
|
+
throw new Error('invalid Talaria Relay profile identity');
|
|
120
|
+
return { value: typeof raw === 'string' ? raw : identity + '\n', result: identity };
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
async function loadKey(documents, config) {
|
|
124
|
+
const value = config.private_key_document ? (await documents.get('relay-keys', config.private_key_document))?.value : readFileSync(config.private_key_path, 'utf8');
|
|
125
|
+
if (typeof value !== 'string')
|
|
126
|
+
throw new Error('Talaria publisher key is missing');
|
|
127
|
+
const key = createPrivateKey(value);
|
|
100
128
|
if (key.asymmetricKeyType !== 'ed25519')
|
|
101
129
|
throw new Error('Talaria publisher key must be an Ed25519 private key');
|
|
102
130
|
return key;
|
|
@@ -108,21 +136,23 @@ function signedHeaders(key, keyId, method, path, body, now) {
|
|
|
108
136
|
return { 'Content-Type': 'application/json', 'X-Talaria-Key-Id': keyId, 'X-Talaria-Timestamp': timestamp, 'X-Talaria-Nonce': nonce, 'X-Talaria-Signature': b64u(cryptoSign(null, Buffer.from(signed, 'utf8'), key)) };
|
|
109
137
|
}
|
|
110
138
|
// ── presence leases ───────────────────────────────────────────────────────
|
|
111
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* Profile-scoped browser activity leases; a fresh active lease mutes alerts for that profile.
|
|
141
|
+
* Each tab's entry also fences its sequence number for PRESENCE_FENCE_SECONDS past lease expiry,
|
|
142
|
+
* so a delayed lower sequence stays rejected. At most PRESENCE_MAX_LEASES tabs are kept; the least
|
|
143
|
+
* recently updated tab is evicted first, which drops its fence too.
|
|
144
|
+
*/
|
|
112
145
|
export class PresenceLeases {
|
|
113
146
|
now;
|
|
114
147
|
leases = new Map();
|
|
115
148
|
constructor(now) {
|
|
116
149
|
this.now = now;
|
|
117
150
|
}
|
|
118
|
-
prune(now) {
|
|
119
|
-
for (const [key, entry] of this.leases)
|
|
120
|
-
if (entry.expires <= now)
|
|
121
|
-
this.leases.delete(key);
|
|
122
|
-
}
|
|
123
151
|
apply(profile, tabId, seq, active) {
|
|
124
152
|
const now = this.now();
|
|
125
|
-
this.
|
|
153
|
+
for (const [key, entry] of this.leases)
|
|
154
|
+
if (entry.expires + PRESENCE_FENCE_SECONDS <= now)
|
|
155
|
+
this.leases.delete(key);
|
|
126
156
|
const key = `${profile}\n${tabId}`;
|
|
127
157
|
const current = this.leases.get(key);
|
|
128
158
|
if (current && seq <= current.seq)
|
|
@@ -135,9 +165,9 @@ export class PresenceLeases {
|
|
|
135
165
|
renew(profile, tabId, seq = 1) { this.apply(profile, tabId, seq, true); }
|
|
136
166
|
revoke(profile, tabId, seq = 1) { this.apply(profile, tabId, seq, false); }
|
|
137
167
|
has(profile) {
|
|
138
|
-
this.
|
|
168
|
+
const now = this.now();
|
|
139
169
|
for (const [key, entry] of this.leases)
|
|
140
|
-
if (entry.active && key.startsWith(`${profile}\n`))
|
|
170
|
+
if (entry.active && entry.expires > now && key.startsWith(`${profile}\n`))
|
|
141
171
|
return true;
|
|
142
172
|
return false;
|
|
143
173
|
}
|
|
@@ -164,7 +194,7 @@ export class RelayPublisher {
|
|
|
164
194
|
config;
|
|
165
195
|
deps;
|
|
166
196
|
key;
|
|
167
|
-
|
|
197
|
+
startedUnsupportedLogged = false;
|
|
168
198
|
lastRevision = 0;
|
|
169
199
|
terminal = new Map();
|
|
170
200
|
disabledProfiles = new Set();
|
|
@@ -180,10 +210,15 @@ export class RelayPublisher {
|
|
|
180
210
|
constructor(config, deps) {
|
|
181
211
|
this.config = config;
|
|
182
212
|
this.deps = deps;
|
|
183
|
-
|
|
184
|
-
|
|
213
|
+
}
|
|
214
|
+
async initialize() {
|
|
215
|
+
this.key = await loadKey(this.deps.documents, this.config);
|
|
216
|
+
try {
|
|
217
|
+
await this.mutateStarted((ledger) => { this.trackProfiles(ledger); });
|
|
218
|
+
}
|
|
219
|
+
catch { /* the first pass retries */ }
|
|
185
220
|
try {
|
|
186
|
-
this.lastRevision = Number.parseInt(
|
|
221
|
+
this.lastRevision = Number.parseInt(str((await this.deps.documents.get('relay', 'revision'))?.value), 10) || 0;
|
|
187
222
|
}
|
|
188
223
|
catch {
|
|
189
224
|
this.lastRevision = 0;
|
|
@@ -197,11 +232,11 @@ export class RelayPublisher {
|
|
|
197
232
|
this.views.set(sid, view);
|
|
198
233
|
this.lastRevision = Math.max(this.lastRevision, previous.lastRevision);
|
|
199
234
|
}
|
|
200
|
-
start(publishInitial = true) {
|
|
235
|
+
async start(publishInitial = true) {
|
|
201
236
|
if (this.loop)
|
|
202
237
|
return;
|
|
203
238
|
this.loop = this.run();
|
|
204
|
-
if (publishInitial)
|
|
239
|
+
if (publishInitial || (await loadDeletedViews(this.deps.documents)).length)
|
|
205
240
|
this.changed();
|
|
206
241
|
}
|
|
207
242
|
stop() {
|
|
@@ -229,15 +264,37 @@ export class RelayPublisher {
|
|
|
229
264
|
this.views.set(sid, { profile, through: Math.floor(this.deps.now() * 1000) });
|
|
230
265
|
this.changed();
|
|
231
266
|
}
|
|
267
|
+
/** Bind an unconfigured deletion once, before sending, so retries cannot cross relay or profile identities. */
|
|
268
|
+
async pendingDeleted() {
|
|
269
|
+
const views = await mutateDeletedViews(this.deps.documents, async (views) => {
|
|
270
|
+
for (const view of views) {
|
|
271
|
+
if (view.scope)
|
|
272
|
+
continue;
|
|
273
|
+
const cfg = this.config.profiles[view.profile];
|
|
274
|
+
if (!cfg || (view.identity !== null && cfg.identity !== view.identity) || !await this.publishes(view.profile, cfg))
|
|
275
|
+
continue;
|
|
276
|
+
view.identity = cfg.identity;
|
|
277
|
+
view.scope = { url: this.config.url, publisherId: this.config.publisher_id, profileId: cfg.profile_id };
|
|
278
|
+
}
|
|
279
|
+
return views;
|
|
280
|
+
});
|
|
281
|
+
return filterAsync(views, async (view) => !await hasSessionData(this.deps.store, view.sessionId));
|
|
282
|
+
}
|
|
232
283
|
/** Send the views queued before the snapshot just published, so the relay already holds every outcome they saw. */
|
|
233
284
|
async flushViews(ready) {
|
|
234
285
|
for (const [sid, view] of ready) {
|
|
286
|
+
if (view.deleted && (await hasSessionData(this.deps.store, sid)))
|
|
287
|
+
continue;
|
|
288
|
+
let accepted = false;
|
|
235
289
|
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
236
|
-
if (!this.deps.profilesMatch(view.profile, profile) || !this.publishes(profile, cfg))
|
|
290
|
+
if (!this.deps.profilesMatch(view.profile, profile) || !(await this.publishes(profile, cfg)))
|
|
291
|
+
continue;
|
|
292
|
+
if (view.deleted && (view.deleted.identity !== cfg.identity || view.deleted.scope?.url !== this.config.url || view.deleted.scope.publisherId !== this.config.publisher_id || view.deleted.scope.profileId !== cfg.profile_id))
|
|
237
293
|
continue;
|
|
238
294
|
const path = `/v1/publishers/${encodeURIComponent(this.config.publisher_id)}/profiles/${encodeURIComponent(cfg.profile_id)}/sessions/${encodeURIComponent(sid)}/viewed`;
|
|
239
295
|
try {
|
|
240
296
|
await this.put(path, JSON.stringify({ through: view.through }));
|
|
297
|
+
accepted = true;
|
|
241
298
|
}
|
|
242
299
|
catch (error) {
|
|
243
300
|
if (!(error instanceof RelayHttpError) || error.retryable)
|
|
@@ -250,7 +307,12 @@ export class RelayPublisher {
|
|
|
250
307
|
this.deps.log(`[relay] Talaria relay rejected a viewed acknowledgement (HTTP ${String(error.status)})`);
|
|
251
308
|
}
|
|
252
309
|
}
|
|
253
|
-
if (
|
|
310
|
+
if (view.deleted) {
|
|
311
|
+
if (accepted)
|
|
312
|
+
await mutateDeletedViews(this.deps.documents, (views) => { const index = views.findIndex((entry) => entry.id === view.deleted.id && entry.through === view.through); if (index >= 0)
|
|
313
|
+
views.splice(index, 1); });
|
|
314
|
+
}
|
|
315
|
+
else if (this.views.get(sid) === view)
|
|
254
316
|
this.views.delete(sid);
|
|
255
317
|
}
|
|
256
318
|
}
|
|
@@ -274,9 +336,10 @@ export class RelayPublisher {
|
|
|
274
336
|
return;
|
|
275
337
|
this.dirty = false;
|
|
276
338
|
try {
|
|
277
|
-
const ready =
|
|
339
|
+
const ready = [...this.views, ...(await this.pendingDeleted()).map((view) => [view.sessionId, { profile: view.profile, through: view.through, deleted: view }])];
|
|
278
340
|
await this.publishSnapshot(true);
|
|
279
341
|
await this.flushViews(ready);
|
|
342
|
+
await this.publishStarted();
|
|
280
343
|
if (this.failures)
|
|
281
344
|
this.deps.log('[relay] Talaria relay snapshot recovered');
|
|
282
345
|
this.failures = 0;
|
|
@@ -296,7 +359,7 @@ export class RelayPublisher {
|
|
|
296
359
|
}
|
|
297
360
|
}
|
|
298
361
|
/** Python `build_states`: one row per session with a live or recently terminal run in this profile. */
|
|
299
|
-
buildStates(profile) {
|
|
362
|
+
async buildStates(profile) {
|
|
300
363
|
const cutoff = this.deps.now() - TERMINAL_RETENTION_S;
|
|
301
364
|
for (const [sid, run] of this.terminal)
|
|
302
365
|
if (run.terminal_at < cutoff)
|
|
@@ -320,7 +383,7 @@ export class RelayPublisher {
|
|
|
320
383
|
for (const [sid, run] of bySession) {
|
|
321
384
|
let session;
|
|
322
385
|
try {
|
|
323
|
-
session = this.deps.store.get(sid, { metadataOnly: true });
|
|
386
|
+
session = (await this.deps.store.get(sid, { metadataOnly: true }));
|
|
324
387
|
}
|
|
325
388
|
catch {
|
|
326
389
|
continue;
|
|
@@ -343,13 +406,22 @@ export class RelayPublisher {
|
|
|
343
406
|
states.push(state);
|
|
344
407
|
}
|
|
345
408
|
if (states.length)
|
|
346
|
-
|
|
409
|
+
await updateDocument(this.deps.documents, 'relay', 'revision', (raw) => {
|
|
410
|
+
let floor = Number.parseInt(str(raw), 10) || 0;
|
|
411
|
+
for (const state of states) {
|
|
412
|
+
state.revision = Math.max(state.revision, floor + 1);
|
|
413
|
+
state.eventId = `snapshot:${String(state.revision)}:${state.sessionId}`;
|
|
414
|
+
floor = state.revision;
|
|
415
|
+
}
|
|
416
|
+
this.lastRevision = Math.max(this.lastRevision, floor);
|
|
417
|
+
return { value: `${String(this.lastRevision)}\n`, result: undefined };
|
|
418
|
+
});
|
|
347
419
|
return states;
|
|
348
420
|
}
|
|
349
421
|
async publishSnapshot(isolatePermanent = false) {
|
|
350
422
|
let retryable = null;
|
|
351
423
|
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
352
|
-
if (!this.publishes(profile, cfg))
|
|
424
|
+
if (!(await this.publishes(profile, cfg)))
|
|
353
425
|
continue;
|
|
354
426
|
try {
|
|
355
427
|
await this.publishProfileSnapshot(profile, cfg.profile_id);
|
|
@@ -368,10 +440,10 @@ export class RelayPublisher {
|
|
|
368
440
|
if (retryable)
|
|
369
441
|
throw retryable;
|
|
370
442
|
}
|
|
371
|
-
publishes(profile, cfg) {
|
|
443
|
+
async publishes(profile, cfg) {
|
|
372
444
|
if (cfg.identity) {
|
|
373
445
|
try {
|
|
374
|
-
if (profileIdentity(this.deps
|
|
446
|
+
if ((await profileIdentity(this.deps, profile)) !== cfg.identity)
|
|
375
447
|
return false;
|
|
376
448
|
}
|
|
377
449
|
catch {
|
|
@@ -385,15 +457,136 @@ export class RelayPublisher {
|
|
|
385
457
|
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
386
458
|
if (cfg.profile_id !== profileId)
|
|
387
459
|
continue;
|
|
388
|
-
if (cfg.identity !== expectedIdentity || profileIdentity(this.deps
|
|
460
|
+
if (cfg.identity !== expectedIdentity || (await profileIdentity(this.deps, profile)) !== expectedIdentity)
|
|
389
461
|
throw new RelayPairingError('Hermes profile changed during relay enrollment', 409);
|
|
390
462
|
await this.publishProfileSnapshot(profile, profileId);
|
|
391
463
|
return;
|
|
392
464
|
}
|
|
393
465
|
throw new RelayPairingError('Talaria Relay profile enrollment is unavailable', 502);
|
|
394
466
|
}
|
|
467
|
+
// The ledger is reread for every change, so a restarted or replacement publisher resends an unsent event under its ID.
|
|
468
|
+
parseStarted(raw) {
|
|
469
|
+
const ledger = new Map();
|
|
470
|
+
const profiles = raw?.profiles;
|
|
471
|
+
if (!Array.isArray(profiles))
|
|
472
|
+
return ledger;
|
|
473
|
+
for (const p of profiles) {
|
|
474
|
+
if (typeof p?.profileId !== 'string' || typeof p.floor !== 'number' || !Array.isArray(p.sessions))
|
|
475
|
+
continue;
|
|
476
|
+
const sessions = new Map();
|
|
477
|
+
for (const e of p.sessions) {
|
|
478
|
+
if (typeof e?.sessionId === 'string' && typeof e.startedAt === 'number')
|
|
479
|
+
sessions.set(e.sessionId, { startedAt: e.startedAt, sent: e.sent === true });
|
|
480
|
+
}
|
|
481
|
+
ledger.set(p.profileId, { floor: p.floor, sessions });
|
|
482
|
+
}
|
|
483
|
+
return ledger;
|
|
484
|
+
}
|
|
485
|
+
startedDocument(ledger) {
|
|
486
|
+
const profiles = [...ledger].map(([profileId, p]) => ({ profileId, floor: p.floor, sessions: [...p.sessions].map(([sessionId, e]) => ({ sessionId, startedAt: e.startedAt, sent: e.sent })) }));
|
|
487
|
+
return { version: 1, profiles };
|
|
488
|
+
}
|
|
489
|
+
async mutateStarted(edit) {
|
|
490
|
+
return (await updateDocument(this.deps.documents, 'relay', 'started', async (raw) => {
|
|
491
|
+
const ledger = this.parseStarted(raw);
|
|
492
|
+
const result = await edit(ledger);
|
|
493
|
+
return { value: this.startedDocument(ledger), result };
|
|
494
|
+
}, { replaceInvalid: true }));
|
|
495
|
+
}
|
|
496
|
+
/** Give each enrolled profile without a ledger entry a floor of now; true when the ledger changed. */
|
|
497
|
+
trackProfiles(ledger) {
|
|
498
|
+
const nowMs = Math.floor(this.deps.now() * 1000);
|
|
499
|
+
let changed = false;
|
|
500
|
+
for (const cfg of Object.values(this.config.profiles)) {
|
|
501
|
+
if (ledger.has(cfg.profile_id))
|
|
502
|
+
continue;
|
|
503
|
+
ledger.set(cfg.profile_id, { floor: nowMs, sessions: new Map() });
|
|
504
|
+
changed = true;
|
|
505
|
+
}
|
|
506
|
+
return changed;
|
|
507
|
+
}
|
|
508
|
+
/** Record every messageful Web or Agent session that started in an enrolled profile since tracking began. */
|
|
509
|
+
async observeStarted() {
|
|
510
|
+
// Agent rows come from the listing worker first, so the ledger's load-to-save stays synchronous.
|
|
511
|
+
const agentRows = new Map();
|
|
512
|
+
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
513
|
+
if (!(await this.publishes(profile, cfg)))
|
|
514
|
+
continue;
|
|
515
|
+
try {
|
|
516
|
+
agentRows.set(profile, await this.deps.agentSessions(profile));
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
agentRows.set(profile, []);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
let index = [];
|
|
523
|
+
try {
|
|
524
|
+
index = await this.deps.store.readIndexEntries();
|
|
525
|
+
}
|
|
526
|
+
catch { /* no resident index */ }
|
|
527
|
+
const eligible = new Set();
|
|
528
|
+
for (const [profile, cfg] of Object.entries(this.config.profiles))
|
|
529
|
+
if (await this.publishes(profile, cfg))
|
|
530
|
+
eligible.add(profile);
|
|
531
|
+
return (await this.mutateStarted((ledger) => {
|
|
532
|
+
const nowMs = Math.floor(this.deps.now() * 1000);
|
|
533
|
+
this.trackProfiles(ledger);
|
|
534
|
+
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
535
|
+
if (!eligible.has(profile))
|
|
536
|
+
continue;
|
|
537
|
+
const entry = ledger.get(cfg.profile_id);
|
|
538
|
+
const floor = Math.max(entry.floor, nowMs - STARTED_WINDOW_MS);
|
|
539
|
+
for (const [sid, e] of entry.sessions)
|
|
540
|
+
if (e.startedAt < floor) {
|
|
541
|
+
entry.sessions.delete(sid);
|
|
542
|
+
}
|
|
543
|
+
// A compressed Agent chat keeps its lineage root's id and start, so later segments are not new sessions.
|
|
544
|
+
const agent = agentRows.get(profile) ?? [];
|
|
545
|
+
const seen = [
|
|
546
|
+
...index.filter((row) => Number(row.message_count) > 0 && this.deps.profilesMatch(row.profile, profile)).map((row) => [row.session_id, row.created_at]),
|
|
547
|
+
...agent.map((row) => [row._lineage_root_id || row.id, row.started_at]),
|
|
548
|
+
];
|
|
549
|
+
for (const [rawSid, created] of seen) {
|
|
550
|
+
const sid = str(rawSid).trim();
|
|
551
|
+
const startedAt = Math.floor(Number(created) * 1000);
|
|
552
|
+
if (!sid || sid.length > 191 || !Number.isSafeInteger(startedAt) || startedAt < floor || entry.sessions.has(sid))
|
|
553
|
+
continue;
|
|
554
|
+
entry.sessions.set(sid, { startedAt, sent: false });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return ledger;
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
/** Announce each newly observed session once, oldest first, to the relay scope of the profile that owns it. */
|
|
561
|
+
async publishStarted() {
|
|
562
|
+
const ledger = await this.observeStarted();
|
|
563
|
+
for (const [profile, cfg] of Object.entries(this.config.profiles)) {
|
|
564
|
+
if (!(await this.publishes(profile, cfg)))
|
|
565
|
+
continue;
|
|
566
|
+
const unsent = [...(ledger.get(cfg.profile_id)?.sessions ?? [])].filter(([, e]) => !e.sent).sort((a, b) => a[1].startedAt - b[1].startedAt);
|
|
567
|
+
for (const [sid, e] of unsent) {
|
|
568
|
+
const path = `/v1/publishers/${encodeURIComponent(this.config.publisher_id)}/profiles/${encodeURIComponent(cfg.profile_id)}/sessions/${encodeURIComponent(sid)}/started`;
|
|
569
|
+
const body = JSON.stringify({ version: 1, eventId: `started:${sid}`, publisherId: this.config.publisher_id, profileId: cfg.profile_id, sessionId: sid, startedAt: e.startedAt });
|
|
570
|
+
try {
|
|
571
|
+
await this.put(path, body);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
if (!(error instanceof RelayHttpError) || error.retryable)
|
|
575
|
+
throw error;
|
|
576
|
+
if (error.status !== 404)
|
|
577
|
+
this.deps.log(`[relay] Talaria relay rejected a session-started event (HTTP ${String(error.status)})`);
|
|
578
|
+
else if (!this.startedUnsupportedLogged) {
|
|
579
|
+
this.startedUnsupportedLogged = true;
|
|
580
|
+
this.deps.log('[relay] Talaria relay does not support session-started events (HTTP 404); devices refresh on their own schedule');
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
await this.mutateStarted((latest) => { const sent = latest.get(cfg.profile_id)?.sessions.get(sid); if (sent)
|
|
584
|
+
sent.sent = true; });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
395
588
|
async publishProfileSnapshot(profile, profileId) {
|
|
396
|
-
const states = this.buildStates(profile);
|
|
589
|
+
const states = (await this.buildStates(profile));
|
|
397
590
|
try {
|
|
398
591
|
await this.putSnapshot(profileId, states);
|
|
399
592
|
}
|
|
@@ -439,18 +632,19 @@ export class RelayService {
|
|
|
439
632
|
}
|
|
440
633
|
current() { return this.publisher; }
|
|
441
634
|
/** Python `start_talaria_relay_publisher`: resume from saved state; invalid state disables the publisher. */
|
|
442
|
-
start() {
|
|
635
|
+
async start() {
|
|
443
636
|
if (this.publisher)
|
|
444
637
|
return true;
|
|
445
638
|
let candidate = null;
|
|
446
639
|
try {
|
|
447
|
-
const config = loadRelayConfig(this.deps.
|
|
640
|
+
const config = (await loadRelayConfig(this.deps.documents));
|
|
448
641
|
if (!config)
|
|
449
642
|
return false;
|
|
450
643
|
candidate = new RelayPublisher(config, this.deps);
|
|
644
|
+
await candidate.initialize();
|
|
451
645
|
this.candidate = candidate;
|
|
452
646
|
this.adopt(candidate, null);
|
|
453
|
-
candidate.start(false);
|
|
647
|
+
await candidate.start(false);
|
|
454
648
|
return true;
|
|
455
649
|
}
|
|
456
650
|
catch (error) {
|
|
@@ -480,21 +674,50 @@ export class RelayService {
|
|
|
480
674
|
}
|
|
481
675
|
}
|
|
482
676
|
/** A viewed session clears its finished runs on the relay; without a publisher there is nothing to clear. */
|
|
483
|
-
markViewed(sid) {
|
|
677
|
+
async markViewed(sid) {
|
|
484
678
|
const publisher = this.publisher ?? this.candidate;
|
|
485
|
-
publisher?.markViewed(sid, this.deps.store.get(sid, { metadataOnly: true }).profile);
|
|
679
|
+
publisher?.markViewed(sid, (await this.deps.store.get(sid, { metadataOnly: true })).profile);
|
|
486
680
|
}
|
|
487
|
-
/**
|
|
488
|
-
|
|
489
|
-
* store no longer knows it). Later lookups of the same id do not resend.
|
|
490
|
-
*/
|
|
491
|
-
clearDeleted(sid, profile) {
|
|
681
|
+
/** Persist before reporting successful deletion, even when publication is unavailable. */
|
|
682
|
+
async clearDeleted(sid, profile) {
|
|
492
683
|
const publisher = this.publisher ?? this.candidate;
|
|
493
|
-
|
|
684
|
+
const name = this.deps.canonicalProfile(profile ?? 'default');
|
|
685
|
+
const key = JSON.stringify([name, sid]);
|
|
686
|
+
const live = (await hasSessionData(this.deps.store, sid));
|
|
687
|
+
if (!live && this.clearedDeleted.has(key))
|
|
494
688
|
return;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
689
|
+
await mutateDeletedViews(this.deps.documents, async (views) => {
|
|
690
|
+
const queued = views.find((view) => view.sessionId === sid && view.profile === name);
|
|
691
|
+
if (queued && live) {
|
|
692
|
+
const through = Math.max(queued.through, Math.floor(this.deps.now() * 1000));
|
|
693
|
+
if (through !== queued.through) {
|
|
694
|
+
queued.through = through;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else if (!queued) {
|
|
698
|
+
let config = publisher?.config ?? null;
|
|
699
|
+
if (!config) {
|
|
700
|
+
try {
|
|
701
|
+
config = (await loadRelayConfig(this.deps.documents));
|
|
702
|
+
}
|
|
703
|
+
catch { /* preserve unconfigured cleanup */ }
|
|
704
|
+
}
|
|
705
|
+
const cfg = config?.profiles[name];
|
|
706
|
+
let identity = cfg?.identity ?? null;
|
|
707
|
+
if (identity === null) {
|
|
708
|
+
try {
|
|
709
|
+
identity = (await profileIdentity(this.deps, name));
|
|
710
|
+
}
|
|
711
|
+
catch { /* bind when this profile becomes available */ }
|
|
712
|
+
}
|
|
713
|
+
views.push({
|
|
714
|
+
id: randomUUID(), sessionId: sid, profile: name, through: Math.floor(this.deps.now() * 1000), identity,
|
|
715
|
+
scope: config && cfg ? { url: config.url, publisherId: config.publisher_id, profileId: cfg.profile_id } : null,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
this.clearedDeleted.add(key);
|
|
720
|
+
publisher?.changed();
|
|
498
721
|
}
|
|
499
722
|
/** Terminal turn events reach the relay even mid-swap (Python `note_talaria_terminal`). */
|
|
500
723
|
noteTerminal(streamId, phase) {
|
|
@@ -509,6 +732,7 @@ export class RelayService {
|
|
|
509
732
|
/** Python `configure_talaria_relay_publisher`: validate with a real snapshot before swapping publishers. */
|
|
510
733
|
async configure(config, validate) {
|
|
511
734
|
const candidate = new RelayPublisher(config, this.deps);
|
|
735
|
+
await candidate.initialize();
|
|
512
736
|
const previous = this.publisher;
|
|
513
737
|
this.candidate = candidate;
|
|
514
738
|
if (previous)
|
|
@@ -525,7 +749,7 @@ export class RelayService {
|
|
|
525
749
|
throw new RelayPairingError(error instanceof RelayPairingError ? error.message : 'Could not publish the initial Talaria Relay snapshot', error instanceof RelayPairingError ? error.status : 502);
|
|
526
750
|
}
|
|
527
751
|
this.adopt(candidate, previous);
|
|
528
|
-
candidate.start(false);
|
|
752
|
+
await candidate.start(false);
|
|
529
753
|
candidate.changed();
|
|
530
754
|
}
|
|
531
755
|
/** Python `pair_talaria_relay`, serialised so concurrent enrollments preserve both mappings. */
|
|
@@ -585,12 +809,12 @@ export class RelayService {
|
|
|
585
809
|
let identity;
|
|
586
810
|
try {
|
|
587
811
|
canonical = this.deps.canonicalProfile(profile);
|
|
588
|
-
identity = profileIdentity(this.deps
|
|
812
|
+
identity = (await profileIdentity(this.deps, canonical));
|
|
589
813
|
}
|
|
590
814
|
catch {
|
|
591
815
|
throw new RelayPairingError('Hermes profile is unavailable', 403);
|
|
592
816
|
}
|
|
593
|
-
const existing = loadRelayConfig(this.deps.
|
|
817
|
+
const existing = (await loadRelayConfig(this.deps.documents));
|
|
594
818
|
if (existing) {
|
|
595
819
|
if (existing.url !== relayUrl || existing.publisher_id !== publisherId)
|
|
596
820
|
throw new RelayPairingError('This Hermes server is registered to a different relay', 409);
|
|
@@ -598,11 +822,11 @@ export class RelayService {
|
|
|
598
822
|
const profileId = known?.identity === identity ? known.profile_id : `prf_${randomUUID().replace(/-/g, '')}`;
|
|
599
823
|
const requestBody = JSON.stringify({ invitation, publisherId, profileId });
|
|
600
824
|
const path = '/v1/pairings/profile/redeem';
|
|
601
|
-
const payload = await this.relayPost(existing.url + path, requestBody, signedHeaders(loadKey(existing
|
|
825
|
+
const payload = await this.relayPost(existing.url + path, requestBody, signedHeaders((await loadKey(this.deps.documents, existing)), existing.key_id, 'POST', path, requestBody, this.deps.now), 'Talaria Relay rejected the profile enrollment');
|
|
602
826
|
if (payload.protocolVersion !== 2 || payload.publisherId !== publisherId || payload.profileId !== profileId)
|
|
603
827
|
throw new RelayPairingError('Talaria Relay returned an invalid response', 502);
|
|
604
828
|
const config = { ...existing, profiles: { ...existing.profiles, [canonical]: { identity, profile_id: profileId } } };
|
|
605
|
-
saveRelayConfig(this.deps.
|
|
829
|
+
await saveRelayConfig(this.deps.documents, config);
|
|
606
830
|
await this.configure(config, { profileId, identity });
|
|
607
831
|
return { ok: true, publisher_id: publisherId };
|
|
608
832
|
}
|
|
@@ -626,11 +850,10 @@ export class RelayService {
|
|
|
626
850
|
catch {
|
|
627
851
|
throw new RelayPairingError('Talaria Relay returned an invalid response', 502);
|
|
628
852
|
}
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
saveRelayConfig(this.deps.stateDir, config);
|
|
853
|
+
const keyDocument = createHash('sha256').update(keyId, 'utf8').digest('hex').slice(0, 16);
|
|
854
|
+
await updateDocument(this.deps.documents, 'relay-keys', keyDocument, () => ({ value: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(), result: undefined }));
|
|
855
|
+
const config = { url: relayUrl, publisher_id: finalPublisher, key_id: keyId, private_key_path: '', private_key_document: keyDocument, profiles: { [canonical]: { identity, profile_id: pairedProfile } } };
|
|
856
|
+
await saveRelayConfig(this.deps.documents, config);
|
|
634
857
|
await this.configure(config, { profileId: pairedProfile, identity });
|
|
635
858
|
return { ok: true, publisher_id: finalPublisher };
|
|
636
859
|
}
|