clay-server 5.1.1-beta.1 → 5.2.0-beta.10
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/lib/account-auth-policy.js +21 -0
- package/lib/account-auth-socket.js +30 -0
- package/lib/account-auth-throttle.js +44 -0
- package/lib/account-auth-transport.js +48 -0
- package/lib/account-login-page.js +28 -0
- package/lib/account-passkey-challenges.js +31 -0
- package/lib/account-passkey-page.js +89 -0
- package/lib/account-passkey-policy.js +39 -0
- package/lib/account-password-page.js +69 -0
- package/lib/account-passwords.js +36 -0
- package/lib/account-remembered.js +77 -0
- package/lib/account-session-page.js +51 -0
- package/lib/account-session-store.js +82 -0
- package/lib/builtin-mates.js +19 -16
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +88 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +247 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +84 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +104 -0
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +84 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +80 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/email-mcp-server.js +1 -1
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +99 -0
- package/lib/knowledge-db-service.js +24 -33
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-file-store.js +7 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +16 -34
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +18 -6
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +1 -0
- package/lib/mcp-source-context.js +1 -0
- package/lib/mcp-source-discovery.js +1 -0
- package/lib/mcp-source-plugins.js +1 -0
- package/lib/mcp-source-restrictions.js +1 -0
- package/lib/mcp-tool-policy.js +9 -0
- package/lib/migrate-single-user.js +3 -23
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/pane-pending-interactions.js +20 -0
- package/lib/pane-session-activation.js +114 -0
- package/lib/pane-session-transport.js +26 -0
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +8 -4
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-debate.js +1 -0
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +15 -85
- package/lib/project-full-access.js +23 -2
- package/lib/project-http.js +7 -4
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +76 -17
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- package/lib/project-product-control.js +89 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +67 -68
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-server-settings.js +1 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-document.js +27 -7
- package/lib/project-session-github.js +63 -19
- package/lib/project-session-history-actions.js +167 -0
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +7 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +103 -272
- package/lib/project-shared-browser.js +78 -17
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-knowledge.js +2 -1
- package/lib/project-user-message.js +67 -1
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +19 -2
- package/lib/project.js +178 -45
- package/lib/public/app.js +46 -0
- package/lib/public/appearance-bootstrap.js +16 -0
- package/lib/public/auth/account-login.css +98 -0
- package/lib/public/auth/account-login.js +104 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/command-palette.css +12 -33
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/input.css +6 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +46 -1
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/css/sticky-notes.css +87 -1
- package/lib/public/css/user-settings-sessions.css +42 -0
- package/lib/public/index.html +28 -10
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/account-auth-settings.js +71 -0
- package/lib/public/modules/account-transport-settings.js +51 -0
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +11 -1
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +7 -4
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +22 -1
- package/lib/public/modules/app-messages.js +119 -17
- package/lib/public/modules/app-misc.js +3 -1
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +13 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/app-rendering.js +4 -3
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-log-links.js +23 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +33 -0
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +146 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +342 -0
- package/lib/public/modules/command-palette.js +162 -40
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +169 -0
- package/lib/public/modules/contextual-clay.js +250 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +117 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/elicitation-form.js +5 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/history-replay-batch.js +13 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +88 -10
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +6 -34
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/message-delivery.js +20 -2
- package/lib/public/modules/model-picker.js +2 -0
- package/lib/public/modules/pane-activation.js +214 -0
- package/lib/public/modules/pane-binding.js +82 -0
- package/lib/public/modules/pane-bridge.js +7 -4
- package/lib/public/modules/pane-browser-bridge.js +22 -0
- package/lib/public/modules/pane-file-bridge.js +4 -0
- package/lib/public/modules/pane-issue-bridge.js +4 -1
- package/lib/public/modules/passkey-browser.js +69 -0
- package/lib/public/modules/passkey-server-settings.js +57 -0
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/profile.js +1 -238
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +6 -2
- package/lib/public/modules/project-mate-navigation.js +144 -22
- package/lib/public/modules/rewind.js +5 -0
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -440
- package/lib/public/modules/server-settings-controls.js +10 -0
- package/lib/public/modules/server-settings.js +7 -2
- package/lib/public/modules/session-actions.js +5 -2
- package/lib/public/modules/session-context-menu.js +5 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +85 -14
- package/lib/public/modules/session-rich-drafts.js +21 -0
- package/lib/public/modules/session-switch-defer.js +67 -0
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +29 -6
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-group-restore.js +68 -0
- package/lib/public/modules/split-pair-ui.js +1 -0
- package/lib/public/modules/split-pane-reconciler.js +15 -0
- package/lib/public/modules/split-pane-renderer.js +87 -6
- package/lib/public/modules/split-session-boundary.js +1 -1
- package/lib/public/modules/split-view.js +60 -68
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +116 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/stt-controller.js +4 -1
- package/lib/public/modules/stt-recording.js +1 -1
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/theme.js +8 -1
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +60 -11
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/user-settings-account.js +14 -1
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings-sessions.js +120 -0
- package/lib/public/modules/user-settings.js +6 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/modules/you-activity.js +3 -1
- package/lib/public/modules/you.js +91 -6
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +6 -0
- package/lib/public/sw.js +3 -1
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +342 -401
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +52 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-account-passkeys.js +186 -0
- package/lib/server-account-passwords.js +127 -0
- package/lib/server-account-security.js +53 -0
- package/lib/server-admin.js +63 -16
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-auth.js +107 -79
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-dm.js +2 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +18 -4
- package/lib/server-home-chat.js +22 -20
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +29 -9
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +25 -62
- package/lib/server-owner-ui.js +118 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server-settings.js +8 -2
- package/lib/server.js +165 -43
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +5 -2
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +64 -99
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/terminal-manager.js +65 -6
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/user-knowledge-service.js +23 -1
- package/lib/users-auth.js +38 -3
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users-create.js +97 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users-server-pin.js +37 -0
- package/lib/users.js +18 -84
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/workspace-query-service.js +13 -2
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +18 -2
- package/lib/yoke/acp-agent-profiles.js +7 -5
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +54 -50
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +53 -111
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +34 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +11 -4
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var userInput = require('./yoke/user-input');
|
|
2
|
+
var TYPES = ['linear', 'issue', 'pr'];
|
|
3
|
+
var GUIDANCE = ' Primary work choices belong to the user. Linking and refreshing never replace a saved primary. Only when the user explicitly requests a primary change, call the link tool with primary:true; Clay presents the exact change for human confirmation. Never request a primary change proactively. Unlinking a saved primary is refused; ask the user to choose a replacement first.';
|
|
4
|
+
function kind(link) { return link.provider === 'linear' ? 'linear' : link.kind; }
|
|
5
|
+
function selected(session, type) { return (session.workPrimary || {})[type] || ''; }
|
|
6
|
+
function find(session, url) {
|
|
7
|
+
return (session.githubLinks || []).concat(session.linearLinks || []).find(function (link) { return link.url.toLowerCase() === String(url).toLowerCase(); });
|
|
8
|
+
}
|
|
9
|
+
function guardUnlink(session, url) {
|
|
10
|
+
if (TYPES.some(function (type) { return selected(session, type).toLowerCase() === url.toLowerCase(); })) throw new Error('This is a user-selected primary. Choose a replacement before unlinking it.');
|
|
11
|
+
}
|
|
12
|
+
function setPrimary(sm, session, url) {
|
|
13
|
+
var link = find(session, url);
|
|
14
|
+
if (!link || TYPES.indexOf(kind(link)) < 0) throw new Error('Choose an item already linked to this session.');
|
|
15
|
+
var previous = session.workPrimary;
|
|
16
|
+
session.workPrimary = Object.assign({}, previous);
|
|
17
|
+
session.workPrimary[kind(link)] = link.url;
|
|
18
|
+
try { if (sm.saveSessionFile(session) === false) throw new Error('Could not save primary work item.'); }
|
|
19
|
+
catch (error) { session.workPrimary = previous; throw error; }
|
|
20
|
+
sm.broadcastSessionList();
|
|
21
|
+
return session.workPrimary;
|
|
22
|
+
}
|
|
23
|
+
async function requestPrimary(ctx, session, url, authorize) {
|
|
24
|
+
var link = find(session, url);
|
|
25
|
+
if (!link) throw new Error('Link the work item before choosing it as primary.');
|
|
26
|
+
var type = kind(link), previous = selected(session, type), owner = session.ownerId;
|
|
27
|
+
if (previous.toLowerCase() === link.url.toLowerCase()) return;
|
|
28
|
+
var result = await userInput.dispatchUserInput(ctx.userInput && ctx.userInput(session), { questions: [{
|
|
29
|
+
id: 'primary', header: 'Primary work', question: 'Set ' + link.url + ' as the primary ' + (type === 'linear' ? 'Linear issue' : type === 'pr' ? 'GitHub PR' : 'GitHub issue') + ' for this conversation?',
|
|
30
|
+
options: [{ label: 'Set primary' }, { label: 'Keep current' }], allowOther: false,
|
|
31
|
+
}] }, { source: 'work-primary', signal: session.abortController && session.abortController.signal });
|
|
32
|
+
if (result.status !== 'submitted' || !result.answers.primary || result.answers.primary[0] !== 'Set primary') throw new Error('Primary selection was not changed.');
|
|
33
|
+
authorize(session);
|
|
34
|
+
if (session.taskStopRequested) throw new Error('Primary selection was not changed after Stop.');
|
|
35
|
+
if (owner !== session.ownerId || selected(session, type) !== previous) throw new Error('The owner or primary selection changed. Please retry.');
|
|
36
|
+
setPrimary(ctx.sm, session, url);
|
|
37
|
+
}
|
|
38
|
+
function handleMessage(ctx, ws, msg, authorize) {
|
|
39
|
+
if (msg.type !== 'session_work_primary_set') return false;
|
|
40
|
+
var response = { type: 'session_work_primary_result', requestId: msg.requestId, projectSlug: ctx.projectSlug, sessionId: msg.sessionId };
|
|
41
|
+
try {
|
|
42
|
+
var session = ctx.sm.sessions.get(msg.sessionId);
|
|
43
|
+
if (!ctx.canAccess(ws) || !session || !ctx.canRefresh(ws, session)) throw new Error('Only the session owner can choose primary work.');
|
|
44
|
+
authorize(session);
|
|
45
|
+
response.workPrimary = setPrimary(ctx.sm, session, msg.url);
|
|
46
|
+
} catch (error) { response.error = error.message; }
|
|
47
|
+
ctx.sendTo(ws, response);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
module.exports = { GUIDANCE: GUIDANCE, guardUnlink: guardUnlink, requestPrimary: requestPrimary, handleMessage: handleMessage, setPrimary: setPrimary };
|
package/lib/sessions.js
CHANGED
|
@@ -3,7 +3,7 @@ var path = require("path");
|
|
|
3
3
|
var config = require("./config");
|
|
4
4
|
var utils = require("./utils");
|
|
5
5
|
var users = require("./users");
|
|
6
|
-
var
|
|
6
|
+
var providerDefaults = require("./yoke/settings").defaults();
|
|
7
7
|
var hygiene = require("./session-hygiene");
|
|
8
8
|
var backgroundTaskTiming = require("./background-task-timing");
|
|
9
9
|
var sessionProvenance = require("./session-provenance");
|
|
@@ -39,16 +39,16 @@ function createSessionManager(opts) {
|
|
|
39
39
|
var permissionRequestIndex = {}; // requestId -> sessionLocalId (O(1) lookup)
|
|
40
40
|
var capabilitiesByVendor = null; // set by sdk-bridge after adapter init
|
|
41
41
|
var defaultVendor = null; // set by sdk-bridge
|
|
42
|
-
var codexApproval =
|
|
43
|
-
var codexSandbox =
|
|
44
|
-
var codexWebSearch =
|
|
42
|
+
var codexApproval = providerDefaults.codexApproval;
|
|
43
|
+
var codexSandbox = providerDefaults.codexSandbox;
|
|
44
|
+
var codexWebSearch = providerDefaults.codexWebSearch;
|
|
45
45
|
|
|
46
46
|
// --- Session persistence (centralized in ~/.clay/sessions/{encoded-cwd}/) ---
|
|
47
47
|
var sessionsBase = opts.sessionsBase || path.join(config.CONFIG_DIR, "sessions");
|
|
48
48
|
var encodedCwd = utils.resolveEncodedDir(sessionsBase, cwd);
|
|
49
49
|
var sessionsDir = path.join(sessionsBase, encodedCwd);
|
|
50
50
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
51
|
-
var cliSessionsDir = opts.cliSessionsDir ||
|
|
51
|
+
var cliSessionsDir = opts.cliSessionsDir || require("./yoke/legacy-sessions").directory(config.REAL_HOME, cwd);
|
|
52
52
|
var deletedCliSessionsPath = path.join(sessionsDir, "deleted-cli-sessions.json");
|
|
53
53
|
var deletedCliSessionIds = new Set();
|
|
54
54
|
try {
|
|
@@ -77,29 +77,6 @@ function createSessionManager(opts) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function readCliTranscriptHead(filePath) {
|
|
81
|
-
var MAX_READ = 64 * 1024;
|
|
82
|
-
try {
|
|
83
|
-
var stat = fs.statSync(filePath);
|
|
84
|
-
var fd = fs.openSync(filePath, "r");
|
|
85
|
-
var bytesToRead = Math.min(stat.size, MAX_READ);
|
|
86
|
-
var buf = Buffer.alloc(bytesToRead);
|
|
87
|
-
fs.readSync(fd, buf, 0, bytesToRead, 0);
|
|
88
|
-
fs.closeSync(fd);
|
|
89
|
-
var lines = buf.toString("utf8").split("\n");
|
|
90
|
-
var events = [];
|
|
91
|
-
for (var i = 0; i < lines.length; i++) {
|
|
92
|
-
if (!lines[i]) continue;
|
|
93
|
-
try {
|
|
94
|
-
var event = JSON.parse(lines[i]);
|
|
95
|
-
if (event && typeof event === "object") events.push(event);
|
|
96
|
-
} catch (e) {}
|
|
97
|
-
}
|
|
98
|
-
return events;
|
|
99
|
-
} catch (e) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
80
|
|
|
104
81
|
// Auto-migrate sessions from legacy locations:
|
|
105
82
|
// v1: {cwd}/.claude-relay/sessions/
|
|
@@ -179,6 +156,7 @@ function createSessionManager(opts) {
|
|
|
179
156
|
};
|
|
180
157
|
if (session.githubLinks) metaObj.githubLinks = session.githubLinks;
|
|
181
158
|
if (session.linearLinks) metaObj.linearLinks = session.linearLinks;
|
|
159
|
+
if (session.workPrimary) metaObj.workPrimary = session.workPrimary;
|
|
182
160
|
if (session.ownerId) metaObj.ownerId = session.ownerId;
|
|
183
161
|
if (session.vendor) metaObj.vendor = session.vendor;
|
|
184
162
|
if (session.codexDynamicToolCatalogVersion) metaObj.codexDynamicToolCatalogVersion = session.codexDynamicToolCatalogVersion;
|
|
@@ -226,10 +204,18 @@ function createSessionManager(opts) {
|
|
|
226
204
|
if (session.homeDebatePlanning) metaObj.homeDebatePlanning = true;
|
|
227
205
|
if (session.homeDebatePhase) metaObj.homeDebatePhase = session.homeDebatePhase;
|
|
228
206
|
if (session.homeDebateInitialTopic) metaObj.homeDebateInitialTopic = session.homeDebateInitialTopic;
|
|
207
|
+
if (session.mateScheduleDraft) metaObj.mateScheduleDraft = session.mateScheduleDraft;
|
|
208
|
+
if (session.mateSchedules) metaObj.mateSchedules = session.mateSchedules;
|
|
209
|
+
if (session.clayWorkspaceReceipts) metaObj.clayWorkspaceReceipts = session.clayWorkspaceReceipts;
|
|
210
|
+
if (session.clayMateChatReceipts) metaObj.clayMateChatReceipts = session.clayMateChatReceipts;
|
|
211
|
+
if (session.claySchedulingReceipts) metaObj.claySchedulingReceipts = session.claySchedulingReceipts;
|
|
229
212
|
if (session.mateCreationMode) metaObj.mateCreationMode = true;
|
|
230
213
|
if (session.homeMateCreationPhase) metaObj.homeMateCreationPhase = session.homeMateCreationPhase;
|
|
214
|
+
if (session.contextualCreationMateStamp) metaObj.contextualCreationMateStamp = session.contextualCreationMateStamp;
|
|
215
|
+
if (session.contextualCreationRequestId) metaObj.contextualCreationRequestId = session.contextualCreationRequestId;
|
|
231
216
|
if (session.homeClayEntryRequestId) metaObj.homeClayEntryRequestId = session.homeClayEntryRequestId;
|
|
232
217
|
if (session.homeClayEntryMode) metaObj.homeClayEntryMode = session.homeClayEntryMode;
|
|
218
|
+
if (session.homeClayEntryText) metaObj.homeClayEntryText = session.homeClayEntryText;
|
|
233
219
|
metaObj.sessionOriginId = provenanceMeta.sessionOriginId;
|
|
234
220
|
if (provenanceMeta.sessionProvenance) metaObj.sessionProvenance = provenanceMeta.sessionProvenance;
|
|
235
221
|
if (Array.isArray(session._workerGenerations)) metaObj.workerGenerations = session._workerGenerations;
|
|
@@ -356,6 +342,7 @@ function createSessionManager(opts) {
|
|
|
356
342
|
titleAutoGenerated: m.titleAutoGenerated === true,
|
|
357
343
|
githubLinks: Array.isArray(m.githubLinks) ? m.githubLinks.slice(0, 8) : [],
|
|
358
344
|
linearLinks: Array.isArray(m.linearLinks) ? m.linearLinks.slice(0, 8) : [],
|
|
345
|
+
workPrimary: m.workPrimary || {},
|
|
359
346
|
titleManuallySet: m.titleManuallySet === true || legacyTitleProtected,
|
|
360
347
|
createdAt: m.createdAt || Date.now(),
|
|
361
348
|
lastActivity: loaded[i].mtime || m.createdAt || Date.now(),
|
|
@@ -384,10 +371,18 @@ function createSessionManager(opts) {
|
|
|
384
371
|
if (m.homeDebatePlanning) session.homeDebatePlanning = true;
|
|
385
372
|
if (m.homeDebatePhase) session.homeDebatePhase = m.homeDebatePhase;
|
|
386
373
|
if (m.homeDebateInitialTopic) session.homeDebateInitialTopic = m.homeDebateInitialTopic;
|
|
374
|
+
if (m.mateScheduleDraft) session.mateScheduleDraft = m.mateScheduleDraft;
|
|
375
|
+
if (m.mateSchedules) session.mateSchedules = m.mateSchedules;
|
|
376
|
+
if (Array.isArray(m.clayWorkspaceReceipts)) session.clayWorkspaceReceipts = m.clayWorkspaceReceipts;
|
|
377
|
+
if (Array.isArray(m.clayMateChatReceipts)) session.clayMateChatReceipts = m.clayMateChatReceipts;
|
|
378
|
+
if (Array.isArray(m.claySchedulingReceipts)) session.claySchedulingReceipts = m.claySchedulingReceipts;
|
|
387
379
|
if (m.mateCreationMode) session.mateCreationMode = true;
|
|
388
380
|
if (m.homeMateCreationPhase) session.homeMateCreationPhase = m.homeMateCreationPhase;
|
|
381
|
+
if (m.contextualCreationMateStamp) session.contextualCreationMateStamp = m.contextualCreationMateStamp;
|
|
382
|
+
if (m.contextualCreationRequestId) session.contextualCreationRequestId = m.contextualCreationRequestId;
|
|
389
383
|
if (m.homeClayEntryRequestId) session.homeClayEntryRequestId = m.homeClayEntryRequestId;
|
|
390
384
|
if (m.homeClayEntryMode) session.homeClayEntryMode = m.homeClayEntryMode;
|
|
385
|
+
if (m.homeClayEntryText) session.homeClayEntryText = m.homeClayEntryText;
|
|
391
386
|
if (m.ownerId) session.ownerId = m.ownerId;
|
|
392
387
|
session.sessionOriginId = m.sessionOriginId || null;
|
|
393
388
|
session._provenanceMigrationNeeded = sessionProvenance.restore(m, session);
|
|
@@ -458,9 +453,6 @@ function createSessionManager(opts) {
|
|
|
458
453
|
// - Warmup shape: 1 real "hi" prompt, optional automatic interruption,
|
|
459
454
|
// and 0 assistant messages (covered by daemon cleanup but defensive)
|
|
460
455
|
function adoptOrphanedCliSessions() {
|
|
461
|
-
var files;
|
|
462
|
-
try { files = fs.readdirSync(cliSessionsDir); } catch (e) { return; }
|
|
463
|
-
|
|
464
456
|
// Build set of cliSessionIds Clay already tracks
|
|
465
457
|
var knownCliIds = new Set();
|
|
466
458
|
sessions.forEach(function (s) {
|
|
@@ -469,56 +461,10 @@ function createSessionManager(opts) {
|
|
|
469
461
|
|
|
470
462
|
var adopted = 0;
|
|
471
463
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
var
|
|
475
|
-
|
|
476
|
-
if (deletedCliSessionIds.has(cliSid)) continue;
|
|
477
|
-
|
|
478
|
-
var fp = path.join(cliSessionsDir, files[i]);
|
|
479
|
-
var events = readCliTranscriptHead(fp);
|
|
480
|
-
if (!events) continue;
|
|
481
|
-
var userCount = 0;
|
|
482
|
-
var firstUserText = null;
|
|
483
|
-
var createdAtIso = null;
|
|
484
|
-
for (var li = 0; li < events.length; li++) {
|
|
485
|
-
var ev = events[li];
|
|
486
|
-
if (ev.type === "user" && ev.message && ev.message.role === "user") {
|
|
487
|
-
userCount++;
|
|
488
|
-
if (firstUserText == null) {
|
|
489
|
-
var c = ev.message.content;
|
|
490
|
-
if (typeof c === "string") {
|
|
491
|
-
firstUserText = c;
|
|
492
|
-
} else if (Array.isArray(c)) {
|
|
493
|
-
var parts = [];
|
|
494
|
-
for (var ci = 0; ci < c.length; ci++) {
|
|
495
|
-
if (c[ci] && c[ci].type === "text" && typeof c[ci].text === "string") parts.push(c[ci].text);
|
|
496
|
-
}
|
|
497
|
-
firstUserText = parts.join("");
|
|
498
|
-
}
|
|
499
|
-
if (ev.timestamp && !createdAtIso) createdAtIso = ev.timestamp;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (userCount === 0) continue;
|
|
505
|
-
// Defensive warmup skip (daemon also cleans these)
|
|
506
|
-
if (hygiene.isClaudeWarmupTranscript(events)) continue;
|
|
507
|
-
|
|
508
|
-
// Title: first user message trimmed/truncated. Fall back to a
|
|
509
|
-
// neutral label so the sidebar entry is identifiable.
|
|
510
|
-
var title = (firstUserText || "").trim().replace(/\s+/g, " ");
|
|
511
|
-
if (title.length > 60) title = title.slice(0, 57) + "...";
|
|
512
|
-
if (!title) title = "Imported CLI session";
|
|
513
|
-
|
|
514
|
-
var createdAt = Date.now();
|
|
515
|
-
if (createdAtIso) {
|
|
516
|
-
var t = Date.parse(createdAtIso);
|
|
517
|
-
if (!isNaN(t)) createdAt = t;
|
|
518
|
-
}
|
|
519
|
-
var lastActivity = createdAt;
|
|
520
|
-
try { lastActivity = fs.statSync(fp).mtimeMs; } catch (e) {}
|
|
521
|
-
|
|
464
|
+
var candidates = require("./yoke/legacy-sessions").candidates(cliSessionsDir, knownCliIds, deletedCliSessionIds);
|
|
465
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
466
|
+
var candidate = candidates[i], cliSid = candidate.id;
|
|
467
|
+
var title = candidate.title, createdAt = candidate.createdAt, lastActivity = candidate.lastActivity;
|
|
522
468
|
var localId = nextLocalId++;
|
|
523
469
|
var session = {
|
|
524
470
|
localId: localId,
|
|
@@ -536,7 +482,7 @@ function createSessionManager(opts) {
|
|
|
536
482
|
history: [],
|
|
537
483
|
messageUUIDs: [],
|
|
538
484
|
lastRewindUuid: null,
|
|
539
|
-
vendor:
|
|
485
|
+
vendor: candidate.vendor,
|
|
540
486
|
mode: "tui",
|
|
541
487
|
// Adopted from an external `claude` run in this cwd, not started by
|
|
542
488
|
// Clay. Notification emitters must stay silent for these until the
|
|
@@ -568,10 +514,7 @@ function createSessionManager(opts) {
|
|
|
568
514
|
var stale = [];
|
|
569
515
|
sessions.forEach(function (session) {
|
|
570
516
|
if (!session.adopted || !session.cliSessionId) return;
|
|
571
|
-
|
|
572
|
-
var events = readCliTranscriptHead(transcriptPath);
|
|
573
|
-
var missingEmptySource = !fs.existsSync(transcriptPath) && (!session.history || session.history.length === 0);
|
|
574
|
-
if (missingEmptySource || (events && hygiene.isClaudeWarmupTranscript(events))) stale.push(session.localId);
|
|
517
|
+
if (require("./yoke/legacy-sessions").obsolete(cliSessionsDir, session)) stale.push(session.localId);
|
|
575
518
|
});
|
|
576
519
|
for (var i = 0; i < stale.length; i++) deleteSessionQuiet(stale[i]);
|
|
577
520
|
if (stale.length > 0) {
|
|
@@ -624,11 +567,13 @@ function createSessionManager(opts) {
|
|
|
624
567
|
var unreadMap = wsUnread || singleUserUnread;
|
|
625
568
|
return {
|
|
626
569
|
id: s.localId,
|
|
570
|
+
sessionOriginId: s.sessionOriginId || null,
|
|
627
571
|
cliSessionId: s.cliSessionId || null,
|
|
628
572
|
createdAt: s.createdAt || 0,
|
|
629
573
|
title: s.title || "New Session",
|
|
630
574
|
githubLinks: s.githubLinks || [],
|
|
631
575
|
linearLinks: s.linearLinks || [],
|
|
576
|
+
workPrimary: s.workPrimary || {},
|
|
632
577
|
active: isActive,
|
|
633
578
|
isProcessing: s.isProcessing,
|
|
634
579
|
// Sidebar badge: user work only. Ambient housekeeping is surfaced in the
|
|
@@ -833,8 +778,8 @@ function createSessionManager(opts) {
|
|
|
833
778
|
}
|
|
834
779
|
|
|
835
780
|
// History entries include tool activity and streamed output, so opening a
|
|
836
|
-
// conversation
|
|
837
|
-
var INITIAL_HISTORY_SIZE =
|
|
781
|
+
// conversation starts with a 300-entry target, aligned to a complete user turn.
|
|
782
|
+
var INITIAL_HISTORY_SIZE = 300;
|
|
838
783
|
var HISTORY_PAGE_SIZE = 100;
|
|
839
784
|
|
|
840
785
|
function findTurnBoundary(history, targetIndex) {
|
|
@@ -844,8 +789,9 @@ function createSessionManager(opts) {
|
|
|
844
789
|
return 0;
|
|
845
790
|
}
|
|
846
791
|
|
|
847
|
-
function replayHistory(session, fromIndex, targetWs, transform) {
|
|
848
|
-
var _send = (targetWs && sendTo) ? function (obj) { sendTo(targetWs, obj); } : send;
|
|
792
|
+
function replayHistory(session, fromIndex, targetWs, transform, snapshotSend) {
|
|
793
|
+
var _send = snapshotSend || ((targetWs && sendTo) ? function (obj) { sendTo(targetWs, obj); } : send);
|
|
794
|
+
var sendHistory = snapshotSend && snapshotSend.history || _send;
|
|
849
795
|
var total = session.history.length;
|
|
850
796
|
if (typeof fromIndex !== "number") {
|
|
851
797
|
if (total <= INITIAL_HISTORY_SIZE) {
|
|
@@ -855,7 +801,7 @@ function createSessionManager(opts) {
|
|
|
855
801
|
}
|
|
856
802
|
}
|
|
857
803
|
|
|
858
|
-
|
|
804
|
+
sendHistory({ type: "history_meta", total: total, from: fromIndex });
|
|
859
805
|
|
|
860
806
|
for (var i = fromIndex; i < total; i++) {
|
|
861
807
|
var _item = session.history[i];
|
|
@@ -864,7 +810,10 @@ function createSessionManager(opts) {
|
|
|
864
810
|
if (_item && (_item.type === "mention_user" || _item.type === "mention_response")) {
|
|
865
811
|
console.log("[DEBUG replayHistory] sending mention at index=" + i + " from=" + fromIndex + " total=" + total + " type=" + _item.type + " mate=" + (_item.mateName || ""));
|
|
866
812
|
}
|
|
867
|
-
|
|
813
|
+
var normalizedItem = require("./yoke/usage").clientEvent(_item, session.vendor || "claude");
|
|
814
|
+
var replayItem = transform ? transform(normalizedItem) : normalizedItem;
|
|
815
|
+
if (replayItem && typeof replayItem === "object") replayItem = Object.assign({}, replayItem, { _historyIndex: i });
|
|
816
|
+
sendHistory(replayItem);
|
|
868
817
|
}
|
|
869
818
|
|
|
870
819
|
// Find the last result message in the full history for accurate context data
|
|
@@ -872,9 +821,11 @@ function createSessionManager(opts) {
|
|
|
872
821
|
var lastModelUsage = null;
|
|
873
822
|
var lastCost = null;
|
|
874
823
|
var lastStreamInputTokens = null;
|
|
824
|
+
var lastContext = null;
|
|
875
825
|
for (var j = total - 1; j >= 0; j--) {
|
|
876
826
|
if (session.history[j].type === "result") {
|
|
877
|
-
var r = session.history[j];
|
|
827
|
+
var r = require("./yoke/usage").clientEvent(session.history[j], session.vendor || "claude");
|
|
828
|
+
lastContext = r.context || null;
|
|
878
829
|
lastUsage = r.usage || null;
|
|
879
830
|
lastModelUsage = r.modelUsage || null;
|
|
880
831
|
lastCost = r.cost != null ? r.cost : null;
|
|
@@ -883,16 +834,17 @@ function createSessionManager(opts) {
|
|
|
883
834
|
}
|
|
884
835
|
}
|
|
885
836
|
|
|
886
|
-
|
|
837
|
+
sendHistory({ type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(session.history || []), sessionId: session.localId, sessionOriginId: session.sessionOriginId || null, lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, context: lastContext, contextUsage: require("./yoke/usage").context(session.lastContextUsage, session.vendor || "claude") });
|
|
887
838
|
for (var vi = 0; vi < sessionViewedListeners.length; vi++) {
|
|
888
839
|
try { sessionViewedListeners[vi](session, targetWs, _send); } catch (e) {}
|
|
889
840
|
}
|
|
890
841
|
}
|
|
891
842
|
|
|
892
|
-
function switchSession(localId, targetWs, transform) {
|
|
843
|
+
function switchSession(localId, targetWs, transform, snapshotSend, snapshotGuard) {
|
|
893
844
|
var session = sessions.get(localId);
|
|
894
845
|
if (!session) return;
|
|
895
846
|
if (typeof onSessionHydrate === "function") onSessionHydrate(session);
|
|
847
|
+
if (snapshotGuard && !snapshotGuard()) return;
|
|
896
848
|
|
|
897
849
|
// Pane sockets (split-view iframes) pin their session via switch_session
|
|
898
850
|
// but must not move the project-global active pointer: reloads and
|
|
@@ -913,7 +865,7 @@ function createSessionManager(opts) {
|
|
|
913
865
|
singleUserUnread[localId] = 0;
|
|
914
866
|
|
|
915
867
|
// In multi-user mode with a specific client, only send to that client
|
|
916
|
-
var _send = (targetWs && sendTo) ? function (obj) { sendTo(targetWs, obj); } : send;
|
|
868
|
+
var _send = snapshotSend || ((targetWs && sendTo) ? function (obj) { sendTo(targetWs, obj); } : send);
|
|
917
869
|
|
|
918
870
|
var _capsByVendor = capabilitiesByVendor || {};
|
|
919
871
|
var _sessionVendor = session.vendor || defaultVendor || "claude";
|
|
@@ -926,7 +878,7 @@ function createSessionManager(opts) {
|
|
|
926
878
|
|| [];
|
|
927
879
|
_send({ type: "slash_commands", commands: _vendorCmds, vendor: _sessionVendor });
|
|
928
880
|
broadcastSessionList();
|
|
929
|
-
replayHistory(session, undefined, targetWs, transform);
|
|
881
|
+
replayHistory(session, undefined, targetWs, transform, snapshotSend);
|
|
930
882
|
|
|
931
883
|
if (session.isProcessing) {
|
|
932
884
|
_send({ type: "status", status: "processing" });
|
|
@@ -944,10 +896,15 @@ function createSessionManager(opts) {
|
|
|
944
896
|
toolInput: p.toolInput,
|
|
945
897
|
toolUseId: p.toolUseId,
|
|
946
898
|
decisionReason: p.decisionReason,
|
|
899
|
+
defaultToNo: p.defaultToNo,
|
|
900
|
+
suppressAlwaysAllowRule: p.suppressAlwaysAllowRule,
|
|
901
|
+
mateId: p.mateId || undefined,
|
|
902
|
+
vendor: p.vendor || session.vendor || undefined,
|
|
947
903
|
});
|
|
948
904
|
}
|
|
949
905
|
|
|
950
906
|
// Re-send active mention indicator so returning clients restore the mate avatar state
|
|
907
|
+
if (snapshotSend) require('./pane-pending-interactions').send(session, _send);
|
|
951
908
|
if (session._mentionInProgress && session._mentionActiveMateId) {
|
|
952
909
|
_send({ type: "mention_processing", mateId: session._mentionActiveMateId, active: true });
|
|
953
910
|
}
|
|
@@ -1080,6 +1037,14 @@ function createSessionManager(opts) {
|
|
|
1080
1037
|
}
|
|
1081
1038
|
}
|
|
1082
1039
|
|
|
1040
|
+
// An activated pane socket only accepts frames carrying its exact binding;
|
|
1041
|
+
// the transport adds it, or withholds the frame if the binding is stale.
|
|
1042
|
+
function sendLiveToSocket(ws, session, obj, data) {
|
|
1043
|
+
if (!ws._clayPaneActivation) { ws.send(data); return; }
|
|
1044
|
+
var panePayload = require("./pane-session-transport").outbound(ws, obj, session.localId);
|
|
1045
|
+
if (panePayload) ws.send(JSON.stringify(panePayload));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1083
1048
|
function doSendToSession(session, obj) {
|
|
1084
1049
|
// Send to active clients without recording to history/disk (ephemeral data)
|
|
1085
1050
|
if (sendEach) {
|
|
@@ -1088,7 +1053,7 @@ function createSessionManager(opts) {
|
|
|
1088
1053
|
if (filterFn && !filterFn(session)) return;
|
|
1089
1054
|
if (session.homeDebatePlanning === true) return;
|
|
1090
1055
|
if (ws._clayActiveSession === session.localId && ws.readyState === 1) {
|
|
1091
|
-
ws
|
|
1056
|
+
sendLiveToSocket(ws, session, obj, data);
|
|
1092
1057
|
}
|
|
1093
1058
|
});
|
|
1094
1059
|
} else if (session.localId === activeSessionId) {
|
|
@@ -1120,7 +1085,7 @@ function createSessionManager(opts) {
|
|
|
1120
1085
|
if (filterFn && !filterFn(session)) return;
|
|
1121
1086
|
if (session.homeDebatePlanning === true) return;
|
|
1122
1087
|
if (ws._clayActiveSession === session.localId) {
|
|
1123
|
-
if (ws.readyState === 1) ws
|
|
1088
|
+
if (ws.readyState === 1) sendLiveToSocket(ws, session, outgoingObj, data);
|
|
1124
1089
|
} else if (session.isProcessing && !session._ioThrottle) {
|
|
1125
1090
|
if (!ioData) ioData = JSON.stringify({ type: "session_io", id: session.localId });
|
|
1126
1091
|
if (ws.readyState === 1) ws.send(ioData);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var cookieKey = require('./shared-browser-cookies').cookieKey;
|
|
2
|
+
|
|
3
|
+
// Only the private worker pipe carries cookies. They never enter browser state or tool output.
|
|
4
|
+
function attachCookieSync(context, initial, send) {
|
|
5
|
+
var pending = null;
|
|
6
|
+
var previous = initial;
|
|
7
|
+
var nextId = 0;
|
|
8
|
+
function receive(message) {
|
|
9
|
+
if (!pending || pending.id !== message.id) return;
|
|
10
|
+
var entry = pending; pending = null; clearTimeout(entry.timer);
|
|
11
|
+
if (message.error) entry.reject(new Error(message.error));
|
|
12
|
+
else entry.resolve(message.cookies);
|
|
13
|
+
}
|
|
14
|
+
async function sync() {
|
|
15
|
+
var current = await context.cookies();
|
|
16
|
+
var cookies = await new Promise(function (resolve, reject) {
|
|
17
|
+
pending = { id: ++nextId, resolve: resolve, reject: reject, timer: setTimeout(function () { pending = null; reject(new Error('Could not synchronize browser cookies')); }, 4000) };
|
|
18
|
+
try { send({ type: 'cookies', id: pending.id, previous: previous, cookies: current }); }
|
|
19
|
+
catch (error) { clearTimeout(pending.timer); pending = null; reject(error); }
|
|
20
|
+
});
|
|
21
|
+
var remote = new Map(cookies.map(function (cookie) { return [cookieKey(cookie), cookie]; }));
|
|
22
|
+
var local = new Map(current.map(function (cookie) { return [cookieKey(cookie), cookie]; }));
|
|
23
|
+
var updates = cookies.filter(function (cookie) { return JSON.stringify(local.get(cookieKey(cookie))) !== JSON.stringify(cookie); });
|
|
24
|
+
current.forEach(function (cookie) {
|
|
25
|
+
if (!remote.has(cookieKey(cookie))) updates.push(Object.assign({}, cookie, { expires: 1 }));
|
|
26
|
+
});
|
|
27
|
+
if (updates.length) await context.addCookies(updates);
|
|
28
|
+
previous = cookies;
|
|
29
|
+
}
|
|
30
|
+
return { start: function () { return context.addCookies(initial); }, sync: sync, receive: receive };
|
|
31
|
+
}
|
|
32
|
+
module.exports = { attachCookieSync: attachCookieSync };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var path = require('path');
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function cookieKey(cookie) {
|
|
6
|
+
return JSON.stringify([cookie.domain, cookie.path, cookie.name, cookie.partitionKey || '']);
|
|
7
|
+
}
|
|
8
|
+
function cookieMap(cookies) {
|
|
9
|
+
if (!Array.isArray(cookies) || cookies.length > 10000 || JSON.stringify(cookies).length > 2 * 1024 * 1024) throw new Error('Browser cookies exceed the storage limit');
|
|
10
|
+
var result = new Map();
|
|
11
|
+
cookies.forEach(function (cookie) {
|
|
12
|
+
if (!cookie || typeof cookie.name !== 'string' || typeof cookie.value !== 'string' || typeof cookie.domain !== 'string' || typeof cookie.path !== 'string') throw new Error('Invalid browser cookie');
|
|
13
|
+
result.set(cookieKey(cookie), cookie);
|
|
14
|
+
});
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function same(a, b) { return JSON.stringify(a) === JSON.stringify(b); }
|
|
18
|
+
|
|
19
|
+
// Merge only a tab's changes, so an idle tab cannot overwrite another tab's login or logout.
|
|
20
|
+
function mergeCookies(saved, previous, current) {
|
|
21
|
+
var merged = cookieMap(saved), before = cookieMap(previous), after = cookieMap(current);
|
|
22
|
+
before.forEach(function (cookie, key) {
|
|
23
|
+
if (!after.has(key) && same(merged.get(key), cookie)) merged.delete(key);
|
|
24
|
+
});
|
|
25
|
+
after.forEach(function (cookie, key) {
|
|
26
|
+
if (!same(before.get(key), cookie)) merged.set(key, cookie);
|
|
27
|
+
});
|
|
28
|
+
return Array.from(merged.values()).filter(function (cookie) { return cookie.expires === -1 || cookie.expires > Date.now() / 1000; });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function attachEncryptedStore(root, name, empty) {
|
|
32
|
+
var directory = path.join(root, name);
|
|
33
|
+
function ownerKey(owner) {
|
|
34
|
+
if (typeof owner !== 'string' || !owner || owner.length > 256) throw new Error('Browser data owner is required');
|
|
35
|
+
return crypto.createHash('sha256').update(owner).digest('hex');
|
|
36
|
+
}
|
|
37
|
+
function masterKey() {
|
|
38
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
39
|
+
var file = path.join(directory, '.key');
|
|
40
|
+
try { fs.writeFileSync(file, crypto.randomBytes(32), { flag: 'wx', mode: 0o600 }); }
|
|
41
|
+
catch (error) { if (error.code !== 'EEXIST') throw error; }
|
|
42
|
+
return fs.readFileSync(file);
|
|
43
|
+
}
|
|
44
|
+
function read(owner) {
|
|
45
|
+
var id = ownerKey(owner), raw;
|
|
46
|
+
try { raw = fs.readFileSync(path.join(directory, id + '.json'), 'utf8'); }
|
|
47
|
+
catch (error) { if (error.code === 'ENOENT') return empty(); throw new Error('Could not read saved browser data'); }
|
|
48
|
+
try {
|
|
49
|
+
var parts = JSON.parse(raw).map(function (part) { return Buffer.from(part, 'base64'); });
|
|
50
|
+
var cipher = crypto.createDecipheriv('aes-256-gcm', masterKey(), parts[0]);
|
|
51
|
+
cipher.setAAD(Buffer.from(id)); cipher.setAuthTag(parts[1]);
|
|
52
|
+
var cookies = JSON.parse(Buffer.concat([cipher.update(parts[2]), cipher.final()]).toString('utf8'));
|
|
53
|
+
return cookies;
|
|
54
|
+
} catch (error) { throw new Error('Could not decrypt saved browser data'); }
|
|
55
|
+
}
|
|
56
|
+
function write(owner, cookies) {
|
|
57
|
+
var id = ownerKey(owner), iv = crypto.randomBytes(12);
|
|
58
|
+
var cipher = crypto.createCipheriv('aes-256-gcm', masterKey(), iv);
|
|
59
|
+
cipher.setAAD(Buffer.from(id));
|
|
60
|
+
var bytes = Buffer.concat([cipher.update(JSON.stringify(cookies), 'utf8'), cipher.final()]);
|
|
61
|
+
var file = path.join(directory, id + '.json'), temp = file + '.' + crypto.randomBytes(8).toString('hex') + '.tmp';
|
|
62
|
+
try {
|
|
63
|
+
fs.writeFileSync(temp, JSON.stringify([iv, cipher.getAuthTag(), bytes].map(function (part) { return part.toString('base64'); })), { mode: 0o600 });
|
|
64
|
+
fs.renameSync(temp, file);
|
|
65
|
+
} finally { try { fs.unlinkSync(temp); } catch (error) { if (error.code !== 'ENOENT') throw error; } }
|
|
66
|
+
return cookies;
|
|
67
|
+
}
|
|
68
|
+
return { read: read, write: write };
|
|
69
|
+
}
|
|
70
|
+
function attachCookieStore(root) {
|
|
71
|
+
var disk = attachEncryptedStore(root, 'browser-cookies', function () { return []; });
|
|
72
|
+
function read(owner) { return mergeCookies(disk.read(owner), [], []); }
|
|
73
|
+
return { read: read, clear: function (owner) { disk.write(owner, []); }, exchange: function (owner, previous, current) {
|
|
74
|
+
var saved = read(owner), merged = mergeCookies(saved, previous, current);
|
|
75
|
+
if (!same(saved, merged)) disk.write(owner, merged);
|
|
76
|
+
return merged;
|
|
77
|
+
} };
|
|
78
|
+
}
|
|
79
|
+
module.exports = { attachEncryptedStore: attachEncryptedStore, attachCookieStore: attachCookieStore, mergeCookies: mergeCookies, cookieKey: cookieKey };
|
|
@@ -5,6 +5,8 @@ var buildUserEnv = require("./build-user-env").buildUserEnv;
|
|
|
5
5
|
var wrapSpawnAsUser = require("./os-users").wrapSpawnAsUser;
|
|
6
6
|
|
|
7
7
|
function createRuntime(options) {
|
|
8
|
+
var initialStorage = options.storage ? options.storage.read() : null;
|
|
9
|
+
var initialCookies = options.cookies ? options.cookies.read() : null;
|
|
8
10
|
var identity = options.identity;
|
|
9
11
|
var env = buildUserEnv(identity);
|
|
10
12
|
if (process.platform === "win32" && !identity) {
|
|
@@ -14,7 +16,9 @@ function createRuntime(options) {
|
|
|
14
16
|
if (identity) { opts.uid = identity.uid; opts.gid = identity.gid; }
|
|
15
17
|
var spec = wrapSpawnAsUser(process.execPath, [path.join(__dirname, "shared-browser-worker.js")], opts);
|
|
16
18
|
var child = spawn(spec.command, spec.args, spec.options);
|
|
19
|
+
var exited = new Promise(function (resolve) { child.once("close", resolve); });
|
|
17
20
|
var closed = false;
|
|
21
|
+
var closing = false;
|
|
18
22
|
var nextId = 0;
|
|
19
23
|
var pending = new Map();
|
|
20
24
|
var buffer = "";
|
|
@@ -25,19 +29,30 @@ function createRuntime(options) {
|
|
|
25
29
|
function fail(message) {
|
|
26
30
|
pending.forEach(function (entry) { clearTimeout(entry.timer); entry.reject(new Error(message)); });
|
|
27
31
|
pending.clear();
|
|
28
|
-
if (!closed) options.onEvent({ type: "error", error: message });
|
|
32
|
+
if (!closed && !closing) options.onEvent({ type: "error", error: message });
|
|
29
33
|
}
|
|
30
34
|
child.stdin.on("error", function () {});
|
|
31
35
|
child.stdout.setEncoding("utf8");
|
|
32
36
|
child.stdout.on("data", function (chunk) {
|
|
33
37
|
buffer += chunk;
|
|
34
|
-
if (buffer.length >
|
|
38
|
+
if (buffer.length > 32 * 1024 * 1024) { fail("Browser response too large"); child.kill(); return; }
|
|
35
39
|
var at;
|
|
36
40
|
while ((at = buffer.indexOf("\n")) >= 0) {
|
|
37
41
|
var line = buffer.slice(0, at); buffer = buffer.slice(at + 1);
|
|
38
42
|
try {
|
|
39
43
|
var message = JSON.parse(line);
|
|
40
|
-
if (message.type === "
|
|
44
|
+
if (message.type === "storage") {
|
|
45
|
+
try {
|
|
46
|
+
if (!options.storage) throw new Error("Personal site data is unavailable");
|
|
47
|
+
send({ type: "storage", id: message.id, storage: options.storage.exchange(message.previous, message.storage) });
|
|
48
|
+
} catch (error) { send({ type: "storage", id: message.id, error: "Could not save personal browser site data" }); }
|
|
49
|
+
} else if (message.type === "cookies") {
|
|
50
|
+
try {
|
|
51
|
+
if (!options.cookies) throw new Error("Personal cookies are unavailable");
|
|
52
|
+
var cookies = options.cookies.exchange(message.previous, message.cookies);
|
|
53
|
+
send({ type: "cookies", id: message.id, cookies: cookies });
|
|
54
|
+
} catch (error) { send({ type: "cookies", id: message.id, error: "Could not save personal browser cookies" }); }
|
|
55
|
+
} else if (message.type === "result") {
|
|
41
56
|
var entry = pending.get(message.id);
|
|
42
57
|
if (entry) { clearTimeout(entry.timer); pending.delete(message.id); if (message.error) entry.reject(new Error(message.error)); else entry.resolve(message.value); }
|
|
43
58
|
} else options.onEvent(message);
|
|
@@ -45,8 +60,8 @@ function createRuntime(options) {
|
|
|
45
60
|
}
|
|
46
61
|
});
|
|
47
62
|
child.on("error", function () { fail("Browser process could not start"); });
|
|
48
|
-
child.on("exit", function () { fail("Browser process ended"); });
|
|
49
|
-
send({ type: "start" });
|
|
63
|
+
child.on("exit", function () { fail("Browser process ended"); closed = true; });
|
|
64
|
+
send({ type: "start", cookies: initialCookies, storage: initialStorage });
|
|
50
65
|
return {
|
|
51
66
|
control: function (epoch) {
|
|
52
67
|
return new Promise(function (resolve, reject) {
|
|
@@ -78,13 +93,14 @@ function createRuntime(options) {
|
|
|
78
93
|
});
|
|
79
94
|
},
|
|
80
95
|
close: function () {
|
|
81
|
-
if (closed) return;
|
|
82
|
-
|
|
96
|
+
if (closed || closing) return exited;
|
|
97
|
+
closing = true;
|
|
83
98
|
fail("Browser closed");
|
|
84
|
-
child.stdin.end();
|
|
85
|
-
var timer = setTimeout(function () { child.kill("SIGKILL"); },
|
|
99
|
+
try { send({ type: "close" }); } catch (error) { child.stdin.end(); }
|
|
100
|
+
var timer = setTimeout(function () { child.kill("SIGKILL"); }, 30000);
|
|
86
101
|
timer.unref();
|
|
87
102
|
child.once("exit", function () { clearTimeout(timer); });
|
|
103
|
+
return exited;
|
|
88
104
|
},
|
|
89
105
|
};
|
|
90
106
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var cookieKey = require('./shared-browser-cookies').cookieKey;
|
|
2
|
+
// Saved website data only crosses the private worker pipe, never the agent tool channel.
|
|
3
|
+
function attachStorageSync(context, initial, send) {
|
|
4
|
+
var previous = initial, pending = null, nextId = 0;
|
|
5
|
+
function receive(message) {
|
|
6
|
+
if (!pending || pending.id !== message.id) return;
|
|
7
|
+
var entry = pending; pending = null; clearTimeout(entry.timer);
|
|
8
|
+
if (message.error) entry.reject(new Error(message.error)); else entry.resolve(message.storage);
|
|
9
|
+
}
|
|
10
|
+
async function sync() {
|
|
11
|
+
var current = await context.storageState({ indexedDB: true });
|
|
12
|
+
var saved = await new Promise(function (resolve, reject) {
|
|
13
|
+
pending = { id: ++nextId, resolve: resolve, reject: reject, timer: setTimeout(function () { pending = null; reject(new Error('Could not synchronize browser site data')); }, 10000) };
|
|
14
|
+
try { send({ type: 'storage', id: pending.id, previous: previous, storage: current }); }
|
|
15
|
+
catch (error) { clearTimeout(pending.timer); pending = null; reject(error); }
|
|
16
|
+
});
|
|
17
|
+
var remote = new Map(saved.cookies.map(function (cookie) { return [cookieKey(cookie), cookie]; }));
|
|
18
|
+
var local = new Map(current.cookies.map(function (cookie) { return [cookieKey(cookie), cookie]; }));
|
|
19
|
+
var updates = saved.cookies.filter(function (cookie) { return JSON.stringify(local.get(cookieKey(cookie))) !== JSON.stringify(cookie); });
|
|
20
|
+
current.cookies.forEach(function (cookie) { if (!remote.has(cookieKey(cookie))) updates.push(Object.assign({}, cookie, { expires: 1 })); });
|
|
21
|
+
if (updates.length) await context.addCookies(updates);
|
|
22
|
+
if (previous.generation !== saved.generation) {
|
|
23
|
+
var deletions = (saved.deletions || []).filter(function (entry) { return entry.generation > previous.generation; });
|
|
24
|
+
var unknown = !deletions.length || deletions[0].generation > previous.generation + 1;
|
|
25
|
+
var removed = current.origins.filter(function (origin) {
|
|
26
|
+
return unknown || deletions.some(function (entry) { return entry.operation === 'clear_data' || (entry.operation === 'delete_site' && new URL(origin.origin).hostname === entry.target); });
|
|
27
|
+
});
|
|
28
|
+
if (removed.length) {
|
|
29
|
+
var page = context.pages()[0] || await context.newPage();
|
|
30
|
+
var cdp = await context.newCDPSession(page);
|
|
31
|
+
try {
|
|
32
|
+
for (var origin of removed) await cdp.send('Storage.clearDataForOrigin', { origin: origin.origin, storageTypes: 'local_storage,indexeddb' });
|
|
33
|
+
} finally { await cdp.detach(); }
|
|
34
|
+
}
|
|
35
|
+
current = await context.storageState({ indexedDB: true });
|
|
36
|
+
}
|
|
37
|
+
// Restore origin storage on new tabs, rather than replacing live IndexedDB connections.
|
|
38
|
+
// Each open tab contributes only its own changes. Human removals also clear live origins.
|
|
39
|
+
previous = { generation: saved.generation, cookies: saved.cookies, origins: current.origins };
|
|
40
|
+
}
|
|
41
|
+
return { start: function () { return context.setStorageState({ cookies: initial.cookies, origins: initial.origins }); }, sync: sync, receive: receive };
|
|
42
|
+
}
|
|
43
|
+
module.exports = { attachStorageSync: attachStorageSync };
|