clay-server 5.2.0-beta.1 → 5.2.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/lib/account-auth-socket.js +1 -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 +52 -0
- package/lib/clay-product-control.js +250 -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-vendor-usage.js +44 -0
- 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 +78 -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/github-star-count.js +147 -0
- 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/notes-checklist.js +92 -0
- package/lib/notes.js +51 -4
- 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-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 +40 -11
- 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 +160 -290
- package/lib/project-shared-browser.js +78 -17
- package/lib/project-start-protocol.js +29 -0
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +67 -1
- package/lib/project-vendor-login.js +8 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +19 -2
- package/lib/project.js +181 -45
- package/lib/public/app.js +71 -17
- package/lib/public/appearance-bootstrap.js +16 -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/filebrowser.css +1 -1
- 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/mate-loading-scene.css +21 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +68 -258
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/project-mate-creation.css +5 -6
- package/lib/public/css/project-start.css +108 -0
- package/lib/public/css/projects-hub.css +53 -14
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +46 -1
- package/lib/public/css/settings-shell.css +103 -0
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +87 -1
- package/lib/public/css/title-bar.css +79 -0
- package/lib/public/index.html +61 -68
- 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/admin-settings-invitations.js +2 -2
- package/lib/public/modules/admin-settings-projects.js +8 -4
- package/lib/public/modules/admin-settings-ui.js +20 -4
- package/lib/public/modules/admin-settings-users.js +4 -4
- 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-home-hub.js +2 -1
- 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 +125 -19
- 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 +14 -407
- package/lib/public/modules/app-rate-limit.js +19 -28
- 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 +343 -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 +252 -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/github-star.js +66 -0
- 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-loading-scene.js +17 -0
- 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/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 +12 -241
- 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 +3 -0
- package/lib/public/modules/project-mate-creation-workspace.js +13 -4
- package/lib/public/modules/project-mate-navigation.js +144 -22
- package/lib/public/modules/project-session-visibility-settings.js +19 -16
- package/lib/public/modules/project-settings.js +119 -129
- package/lib/public/modules/project-start-dialog.js +130 -0
- package/lib/public/modules/project-start-folder-state.js +78 -0
- package/lib/public/modules/project-start-folder-view.js +190 -0
- package/lib/public/modules/project-start-forms.js +66 -0
- package/lib/public/modules/project-start-path.js +60 -0
- package/lib/public/modules/project-start-state.js +54 -0
- package/lib/public/modules/project-start-submit.js +67 -0
- package/lib/public/modules/projects-hub-view.js +33 -14
- package/lib/public/modules/projects-hub.js +10 -6
- 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.js +36 -91
- 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/settings-project-target.js +56 -0
- package/lib/public/modules/settings-select.js +1 -1
- package/lib/public/modules/settings-shell-registry.js +88 -0
- package/lib/public/modules/settings-shell-view.js +104 -0
- package/lib/public/modules/settings-shell.js +342 -0
- 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 +12 -33
- package/lib/public/modules/sidebar-projects.js +3 -1
- 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 +18 -3
- 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-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +14 -30
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +13 -3
- package/lib/public/modules/user-settings.js +18 -84
- 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/vendor-usage-format.js +93 -0
- package/lib/public/modules/vendor-usage-list.js +45 -0
- package/lib/public/modules/vendor-usage-model.js +215 -0
- package/lib/public/modules/vendor-usage-overview.js +312 -0
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +14 -6
- package/lib/public/sw.js +3 -1
- package/lib/public/welcome-art/conversation-desk.png +0 -0
- package/lib/public/welcome-art/mate-clay.png +0 -0
- package/lib/public/welcome-art/project-clay.png +0 -0
- 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 +348 -402
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +61 -18
- package/lib/sdk-quota-snapshot.js +21 -3
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-admin.js +63 -16
- package/lib/server-appearance-settings.js +13 -1
- 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-github-stars.js +39 -0
- package/lib/server-global-ws.js +26 -32
- 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 +6 -1
- package/lib/server.js +167 -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-notes-mcp-server.js +26 -4
- package/lib/session-notes-writes.js +88 -0
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +25 -10
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +66 -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/users-contextual-clay-preferences.js +31 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +7 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/vendor-usage-acp-readers.js +97 -0
- package/lib/vendor-usage-capabilities.js +88 -0
- package/lib/vendor-usage-http-readers.js +214 -0
- package/lib/vendor-usage-metrics.js +47 -0
- package/lib/vendor-usage-native-readers.js +106 -0
- package/lib/vendor-usage-overview.js +406 -0
- package/lib/vendor-usage-service.js +281 -0
- 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 +22 -4
- 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 +66 -50
- package/lib/yoke/adapters/codex-query-contract.js +25 -0
- package/lib/yoke/adapters/codex.js +98 -123
- 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 +272 -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-dynamic-tool-names.js +80 -0
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-model-catalog.js +37 -3
- 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 +92 -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 +34 -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 +87 -3
- package/package.json +11 -4
- package/lib/public/modules/server-settings-navigation.js +0 -39
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
var provenance = require('./session-provenance');
|
|
2
|
+
|
|
3
|
+
// This opt-in protocol only describes an authenticated ordinary pane snapshot.
|
|
4
|
+
// It grants no action authority and is never a visible-render acknowledgement.
|
|
5
|
+
function begin(ctx, ws, msg) {
|
|
6
|
+
if (!Object.prototype.hasOwnProperty.call(msg, 'paneActivation')) {
|
|
7
|
+
ws._clayPaneActivation = null;
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
var request = msg.paneActivation;
|
|
11
|
+
var session = ctx.sm.sessions.get(msg.id);
|
|
12
|
+
var userId = ws._clayUser && ws._clayUser.id || null;
|
|
13
|
+
var worker = provenance.isWorker(session);
|
|
14
|
+
var parentSession = worker && Array.from(ctx.sm.sessions.values()).find(function (parent) { return parent.sessionOriginId === session.sessionProvenance.parentSessionOriginId && !provenance.isWorker(parent); });
|
|
15
|
+
var identity = session && {
|
|
16
|
+
requestId: request && request.requestId,
|
|
17
|
+
generation: request && request.generation,
|
|
18
|
+
projectSlug: ctx.slug,
|
|
19
|
+
viewerId: userId,
|
|
20
|
+
ownerId: session.ownerId || null,
|
|
21
|
+
sessionId: session.localId,
|
|
22
|
+
sessionOriginId: session.sessionOriginId,
|
|
23
|
+
role: worker ? 'worker' : 'driver',
|
|
24
|
+
workerGeneration: worker ? session.sessionProvenance.generation : null,
|
|
25
|
+
parentSessionOriginId: worker ? session.sessionProvenance.parentSessionOriginId : null,
|
|
26
|
+
groupStamp: groupStamp(),
|
|
27
|
+
};
|
|
28
|
+
function groupStamp() {
|
|
29
|
+
var group = ctx.splitStore && ctx.splitStore.groupForMember(msg.id);
|
|
30
|
+
return group ? JSON.stringify({ id: group.id, members: group.members, pair: group.pair || null }) : null;
|
|
31
|
+
}
|
|
32
|
+
function allowed() {
|
|
33
|
+
if (typeof ws._clayAuthCheck === 'function' && !ws._clayAuthCheck()) return false;
|
|
34
|
+
var access = ctx.getProjectAccess ? ctx.getProjectAccess() : { visibility: 'public' };
|
|
35
|
+
if (!ctx.usersModule.isMultiUser()) return !session.ownerId;
|
|
36
|
+
return !!userId &&
|
|
37
|
+
!!ctx.usersModule.findUserById(userId) &&
|
|
38
|
+
ctx.usersModule.canAccessProject(userId, access) &&
|
|
39
|
+
ctx.usersModule.canAccessSession(userId, session, access);
|
|
40
|
+
}
|
|
41
|
+
var valid = request && typeof request === 'object' &&
|
|
42
|
+
typeof request.requestId === 'string' && /^[a-zA-Z0-9_-]{1,96}$/.test(request.requestId) &&
|
|
43
|
+
Number.isSafeInteger(request.generation) && request.generation > (ws._clayPaneActivationGeneration || 0) &&
|
|
44
|
+
ws._clayPane === true && !ws._clayContextual && !ctx.isMate && ws.readyState === 1 &&
|
|
45
|
+
session && session.sessionOriginId && !session.isMate &&
|
|
46
|
+
(session.runtimeMode || session.mode || 'gui') === 'gui' && !session.tuiSuspended &&
|
|
47
|
+
request.projectSlug === identity.projectSlug && request.viewerId === identity.viewerId &&
|
|
48
|
+
request.ownerId === identity.ownerId && request.sessionOriginId === identity.sessionOriginId &&
|
|
49
|
+
request.role === identity.role && request.workerGeneration === identity.workerGeneration &&
|
|
50
|
+
(!ctx.splitStore || request.groupStamp === identity.groupStamp && request.parentSessionOriginId === identity.parentSessionOriginId) &&
|
|
51
|
+
(!worker || Number.isSafeInteger(identity.workerGeneration) && identity.workerGeneration > 0) && allowed();
|
|
52
|
+
if (!valid) {
|
|
53
|
+
// Do not disclose whether a foreign or removed session exists.
|
|
54
|
+
ctx.sendTo(ws, { type: 'pane_activation_rejected', requestId: request && typeof request.requestId === 'string' ? request.requestId.slice(0, 96) : null });
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
ws._clayPaneActivationGeneration = identity.generation;
|
|
58
|
+
ws._clayPaneActivation = identity;
|
|
59
|
+
function current() {
|
|
60
|
+
return ws.readyState === 1 && ws._clayPane === true && !ws._clayContextual && !ctx.isMate && !session.isMate &&
|
|
61
|
+
ws._clayPaneActivation === identity &&
|
|
62
|
+
ctx.sm.sessions.get(msg.id) === session && session.sessionOriginId === identity.sessionOriginId &&
|
|
63
|
+
(ws._clayUser && ws._clayUser.id || null) === userId &&
|
|
64
|
+
(session.ownerId || null) === identity.ownerId &&
|
|
65
|
+
(session.runtimeMode || session.mode || 'gui') === 'gui' && !session.tuiSuspended &&
|
|
66
|
+
(provenance.isWorker(session) ? 'worker' : 'driver') === identity.role &&
|
|
67
|
+
(provenance.isWorker(session) ? session.sessionProvenance.generation : null) === identity.workerGeneration &&
|
|
68
|
+
groupStamp() === identity.groupStamp &&
|
|
69
|
+
(!worker || parentSession && ctx.sm.sessions.get(parentSession.localId) === parentSession &&
|
|
70
|
+
session.sessionProvenance.parentSessionOriginId === identity.parentSessionOriginId && parentSession.sessionOriginId === identity.parentSessionOriginId &&
|
|
71
|
+
!provenance.isWorker(parentSession) && (parentSession.ownerId || null) === identity.ownerId) &&
|
|
72
|
+
allowed();
|
|
73
|
+
}
|
|
74
|
+
function send(message) {
|
|
75
|
+
if (current() && ws._clayActiveSession === session.localId) ctx.sendTo(ws, Object.assign({}, message, { paneActivation: identity }));
|
|
76
|
+
}
|
|
77
|
+
var historyItems = [], historyText = 0;
|
|
78
|
+
// Only synchronous replay uses this bounded envelope. Each actual disclosure
|
|
79
|
+
// rechecks live authorization; nothing is cached across a send or async work.
|
|
80
|
+
send.history = function (message) {
|
|
81
|
+
if (message.type === 'history_done') {
|
|
82
|
+
flushHistory();
|
|
83
|
+
send(message);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
historyItems.push(message);
|
|
87
|
+
historyText += typeof message.text === 'string' ? message.text.length : 0;
|
|
88
|
+
if (historyItems.length >= 100 || historyText >= 65536) flushHistory();
|
|
89
|
+
};
|
|
90
|
+
function flushHistory() {
|
|
91
|
+
if (!historyItems.length) return;
|
|
92
|
+
var items = historyItems;
|
|
93
|
+
historyItems = []; historyText = 0;
|
|
94
|
+
send({ type: 'pane_history_batch', items: items });
|
|
95
|
+
}
|
|
96
|
+
if (current()) ctx.sendTo(ws, { type: 'pane_activation_started', paneActivation: identity });
|
|
97
|
+
ws._clayPaneActivationValid = function () { return current() && ws._clayActiveSession === identity.sessionId; };
|
|
98
|
+
return {
|
|
99
|
+
validate: function () {
|
|
100
|
+
if (current()) return true;
|
|
101
|
+
ctx.sendTo(ws, { type: 'pane_activation_rejected', requestId: identity.requestId });
|
|
102
|
+
ws._clayPaneActivation = null;
|
|
103
|
+
return false;
|
|
104
|
+
},
|
|
105
|
+
send: send,
|
|
106
|
+
finish: function () {
|
|
107
|
+
// Includes history listeners, status, permissions and context sources.
|
|
108
|
+
// Consumers still need to observe their own DOM/composer readiness.
|
|
109
|
+
if (ws._clayActiveSession === session.localId) send({ type: 'pane_activation_snapshot_done' });
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { begin: begin };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage;
|
|
2
|
+
var requests = new AsyncLocalStorage();
|
|
3
|
+
|
|
4
|
+
function same(a, b) {
|
|
5
|
+
return !!a && !!b && ['requestId', 'generation', 'projectSlug', 'viewerId', 'ownerId', 'sessionId', 'sessionOriginId', 'role', 'workerGeneration', 'groupStamp', 'parentSessionOriginId'].every(function (key) { return a[key] === b[key]; });
|
|
6
|
+
}
|
|
7
|
+
function run(ws, msg, dispatch) {
|
|
8
|
+
var binding = ws._clayPaneActivation;
|
|
9
|
+
if (!binding && msg.paneBinding || binding && msg.type === 'switch_session' && !msg.paneActivation) return;
|
|
10
|
+
if (binding && ['switch_session', 'ping', 'pong', 'clay_ui_ready'].indexOf(msg.type) < 0) {
|
|
11
|
+
if (!same(binding, msg.paneBinding) || !ws._clayPaneActivationValid || !ws._clayPaneActivationValid()) return;
|
|
12
|
+
}
|
|
13
|
+
return requests.run({ ws: ws, binding: binding || null }, function () { return dispatch(ws, msg); });
|
|
14
|
+
}
|
|
15
|
+
// A direct asynchronous reply retains the request's original binding. Live
|
|
16
|
+
// provider events instead supply their authoritative source session explicitly.
|
|
17
|
+
function outbound(ws, message, sessionId) {
|
|
18
|
+
if (!message || message.paneActivation || /^pane_activation_/.test(message.type)) return message;
|
|
19
|
+
var scope = requests.getStore();
|
|
20
|
+
var binding = sessionId === undefined ? scope && scope.ws === ws && scope.binding : ws._clayPaneActivation;
|
|
21
|
+
if (!binding) return message;
|
|
22
|
+
if (sessionId !== undefined && binding.sessionId !== sessionId) return null;
|
|
23
|
+
if (!same(binding, ws._clayPaneActivation) || !ws._clayPaneActivationValid || !ws._clayPaneActivationValid()) return null;
|
|
24
|
+
return Object.assign({}, message, { paneActivation: binding });
|
|
25
|
+
}
|
|
26
|
+
module.exports = { run: run, outbound: outbound, same: same };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// This projection is an allowlist. Never add runtime/session/path objects here.
|
|
2
|
+
function projectMetadata(status, access, users) {
|
|
3
|
+
if (!status || status.isMate || !access || access.error) return null;
|
|
4
|
+
var owner = access.ownerId && users.findUserById(access.ownerId);
|
|
5
|
+
return {
|
|
6
|
+
slug: status.slug,
|
|
7
|
+
project: status.title || status.project || status.slug,
|
|
8
|
+
title: status.title || status.project || status.slug,
|
|
9
|
+
projectOwnerId: owner ? owner.id : null,
|
|
10
|
+
ownerName: owner ? (owner.displayName || owner.username) : "Ownership needs recovery",
|
|
11
|
+
visibility: access.visibility,
|
|
12
|
+
contentAccess: false,
|
|
13
|
+
administrativeAccess: true,
|
|
14
|
+
ownershipRecovery: access.ownershipRecovery === "required" || !owner,
|
|
15
|
+
runtimeHealth: "Registered",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function metadataFor(user, status, access, users) {
|
|
20
|
+
if (!user || !users.findUserById(user.id) || user.role !== "admin" || !status || status.isMate || /^mate-/.test(status.slug)) return null;
|
|
21
|
+
return projectMetadata(status, access, users);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function escape(value) {
|
|
25
|
+
return String(value || "").replace(/[&<>"']/g, function (character) {
|
|
26
|
+
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[character];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function page(metadata, projects) {
|
|
31
|
+
var privacyText = metadata.ownershipRecovery
|
|
32
|
+
? "Content remains restricted because no verified owner is available. Ask the server operator to verify the account’s OS UID and real home/project mapping, then run the documented ownership repair."
|
|
33
|
+
: "Conversations, files and terminals are private. Ask the owner to share project access.";
|
|
34
|
+
var accessText = metadata.ownershipRecovery
|
|
35
|
+
? "Ownership needs recovery. The server operator must independently verify or repair the authoritative account-to-OS mapping before ownership repair can succeed. Ambiguous records stay restricted; administrator status cannot claim this project. See the Project ownership and administrator access guide."
|
|
36
|
+
: "Only the project owner can change ownership, visibility and sharing. Administrator status does not grant access to private content.";
|
|
37
|
+
var strip = projects.filter(function (project) { return !project.isMate; }).map(function (project) {
|
|
38
|
+
return '<a class="icon-strip-item' + (project.slug === metadata.slug ? ' active' : '') + '" href="/p/' + encodeURIComponent(project.slug) + '/" title="' + escape(project.title || project.project) + '"><span class="icon-strip-name">' + escape((project.title || project.project || project.slug).substring(0, 2)) + '</span></a>';
|
|
39
|
+
}).join("");
|
|
40
|
+
return '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>' + escape(metadata.title) + ' · Clay</title>' +
|
|
41
|
+
'<script src="/appearance-bootstrap.js"></script><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/css/project-admin-overview.css"></head><body>' +
|
|
42
|
+
'<div id="layout"><header id="top-bar"><a href="/" class="top-bar-brand-link" aria-label="Clay home"><span class="top-bar-brand-wordmark"></span></a></header>' +
|
|
43
|
+
'<div id="layout-body"><nav id="icon-strip" aria-label="Projects"><div class="icon-strip-projects">' + strip + '</div></nav>' +
|
|
44
|
+
'<main id="main-area" class="project-admin-overview"><div class="project-admin-canvas"><h1>' + escape(metadata.title) + '</h1>' +
|
|
45
|
+
'<div role="tablist" aria-label="Project information"><button type="button" role="tab" aria-selected="true" id="overview-tab" aria-controls="overview-panel">Overview</button><button type="button" role="tab" aria-selected="false" id="access-tab" aria-controls="access-panel">Access</button></div>' +
|
|
46
|
+
'<section id="overview-panel" role="tabpanel" aria-labelledby="overview-tab"><dl><dt>Owner</dt><dd>' + escape(metadata.ownerName) + '</dd><dt>Visibility</dt><dd>' + escape(metadata.visibility) + '</dd><dt>Status</dt><dd>' + (metadata.ownershipRecovery ? 'Ownership needs recovery' : escape(metadata.runtimeHealth)) + '</dd><dt>Your access</dt><dd>Administrative metadata only</dd></dl><p>' + escape(privacyText) + '</p><p class="project-admin-secondary">Resource usage is unavailable for this project.</p></section>' +
|
|
47
|
+
'<section id="access-panel" role="tabpanel" aria-labelledby="access-tab" hidden><h2>Administrative metadata only</h2><p>' + escape(accessText) + '</p></section></div></main></div></div>' +
|
|
48
|
+
'<script type="module" src="/modules/project-admin-overview.js"></script></body></html>';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { metadataFor: metadataFor, page: page };
|
package/lib/project-ask-user.js
CHANGED
|
@@ -25,8 +25,9 @@ function attachAskUser(ctx) {
|
|
|
25
25
|
respond.cancel("Autonomous mode. Make your own decision.");
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
var questions = planningQuestions(boundSession, request.questions);
|
|
28
|
+
var questions = ["mcp-import", "product-control"].indexOf(request.source) >= 0 ? request.questions : planningQuestions(boundSession, request.questions);
|
|
29
29
|
var input = { questions: questions };
|
|
30
|
+
if (request.source === 'mcp-import') input.source = 'mcp-import';
|
|
30
31
|
if (!boundSession.pendingAskUser) boundSession.pendingAskUser = {};
|
|
31
32
|
var pending = {
|
|
32
33
|
input: input,
|
|
@@ -38,15 +39,18 @@ function attachAskUser(ctx) {
|
|
|
38
39
|
};
|
|
39
40
|
boundSession.pendingAskUser[request.id] = pending;
|
|
40
41
|
if (respond && typeof respond.onSettle === "function") {
|
|
41
|
-
respond.onSettle(function () {
|
|
42
|
+
respond.onSettle(function (result) {
|
|
43
|
+
if (request.source === 'product-control' && result.status !== 'submitted') ctx.record(boundSession, { type: 'product_control_question_expired', toolId: request.id, error: 'This exact change review expired or was cancelled. No change was authorized.' });
|
|
44
|
+
if (request.source === 'mate-schedule' && result.status !== 'submitted') ctx.record(boundSession, { type: 'mate_schedule_question_expired', toolId: request.id, error: 'This schedule review expired. Request a new review.' });
|
|
45
|
+
if (request.source === 'mcp-import' && result.status !== 'submitted') ctx.record(boundSession, { type: 'mcp_import_consent_expired', toolId: request.id, source: 'mcp-import', error: 'Import consent expired or was cancelled. No connection was imported.' });
|
|
42
46
|
if (boundSession.pendingAskUser && boundSession.pendingAskUser[request.id] === pending) delete boundSession.pendingAskUser[request.id];
|
|
43
47
|
});
|
|
44
48
|
}
|
|
45
49
|
if (!askUserIdentity.hasRecordedAskUserRequest(boundSession.history, request.id, input)) {
|
|
46
|
-
ctx.record(boundSession, { type: "tool_executing", id: request.id, name: "AskUserQuestion", input: input });
|
|
50
|
+
ctx.record(boundSession, { type: "tool_executing", id: request.id, name: "AskUserQuestion", input: input, source: request.source });
|
|
47
51
|
}
|
|
48
52
|
var router = ctx.getWorkerQuestionRouter && ctx.getWorkerQuestionRouter();
|
|
49
|
-
if (router) router.routeQuestion(boundSession, request, respond, function (answers, driverId) {
|
|
53
|
+
if (router && request.source !== "mcp-import" && request.source !== "mate-schedule" && request.source !== "product-control") router.routeQuestion(boundSession, request, respond, function (answers, driverId) {
|
|
50
54
|
var displayAnswers = {};
|
|
51
55
|
request.questions.forEach(function (question, index) { displayAnswers[index] = answers[question.id].join(", "); });
|
|
52
56
|
ctx.record(boundSession, { type: "ask_user_answered", toolId: request.id, answers: displayAnswers, answeredByDriver: driverId });
|
|
@@ -3,7 +3,7 @@ var path = require("path");
|
|
|
3
3
|
var usersModule = require("./users");
|
|
4
4
|
var userPresence = require("./user-presence");
|
|
5
5
|
var emailAccounts = require("./email-accounts");
|
|
6
|
-
var
|
|
6
|
+
var providerSettings = require("./yoke/settings");
|
|
7
7
|
var yoke = require("./yoke");
|
|
8
8
|
var permissionModes = require("./session-permission-mode");
|
|
9
9
|
var sessionProvenance = require("./session-provenance");
|
|
@@ -143,6 +143,9 @@ function attachConnection(ctx) {
|
|
|
143
143
|
var _warmedUp = false;
|
|
144
144
|
|
|
145
145
|
function findRestoredActiveSession(ws, wsUser, allSessions) {
|
|
146
|
+
if (ws._clayContextual && ws._clayPaneSession) {
|
|
147
|
+
return { active: allSessions.find(function (session) { return session.localId === ws._clayPaneSession; }) || null, storedPresence: null };
|
|
148
|
+
}
|
|
146
149
|
var active = null;
|
|
147
150
|
var presenceKey = wsUser ? wsUser.id : "_default";
|
|
148
151
|
var storedPresence = userPresence.getPresence(slug, presenceKey);
|
|
@@ -202,7 +205,7 @@ function attachConnection(ctx) {
|
|
|
202
205
|
} else if (!usersModule.isMultiUser()) {
|
|
203
206
|
allSessions = allSessions.filter(function (s) { return !s.ownerId; });
|
|
204
207
|
}
|
|
205
|
-
var restoredState = ws._clayPane
|
|
208
|
+
var restoredState = ws._clayPane && !ws._clayContextual
|
|
206
209
|
? { active: null, storedPresence: null }
|
|
207
210
|
: findRestoredActiveSession(ws, wsUser, allSessions);
|
|
208
211
|
var restoredActive = restoredState.active;
|
|
@@ -217,7 +220,7 @@ function attachConnection(ctx) {
|
|
|
217
220
|
sm.saveSessionFile(restoredActive);
|
|
218
221
|
}
|
|
219
222
|
var initialModels = (sm.modelsByVendor && sm.modelsByVendor[initialVendor]) || sm.availableModels || [];
|
|
220
|
-
sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: yoke.
|
|
223
|
+
sendTo(ws, { type: "info", controlRuntimeId: require("./clay-control-target-projection").runtime(sm), cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: require("./yoke/vendor-registry").publicInfo(), isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
|
|
221
224
|
if (typeof sendCursorSharingState === "function") sendCursorSharingState(ws);
|
|
222
225
|
// Update notifications are pushed on a scheduled interval (see
|
|
223
226
|
// scheduleUpdateBroadcast). We no longer push on connect to avoid
|
|
@@ -234,8 +237,8 @@ function attachConnection(ctx) {
|
|
|
234
237
|
var initialPermissionState = permissionModes.clientState(restoredActive, sm.currentPermissionMode, sm.defaultVendor);
|
|
235
238
|
sendTo(ws, { type: "config_state", model: initialModelInfo.model, vendor: initialVendor, sessionId: restoredActive ? restoredActive.localId : null, mode: initialPermissionState.requestedPermissionMode, effectivePermissionMode: initialPermissionState.effectivePermissionMode, permissionCapabilities: initialPermissionState.permissionCapabilities, mcpPermissionModeOverrides: initialPermissionState.mcpPermissionModeOverrides, effort: initialEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
|
|
236
239
|
sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
|
|
237
|
-
|
|
238
|
-
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
240
|
+
providerSettings.messages(sm).forEach(function(message) { sendTo(ws, message); });
|
|
241
|
+
sendTo(ws, { type: "term_list", terminals: tm.list(true) });
|
|
239
242
|
// Any login flow already running in this project, so a reconnecting client
|
|
240
243
|
// re-attaches to the existing login terminal instead of starting another.
|
|
241
244
|
if (_vendorLogin) _vendorLogin.sendStateTo(ws);
|
|
@@ -267,7 +270,7 @@ function attachConnection(ctx) {
|
|
|
267
270
|
var presenceKey = wsUser ? wsUser.id : "_default";
|
|
268
271
|
var storedPresence = restoredState.storedPresence;
|
|
269
272
|
var autoCreated = false;
|
|
270
|
-
if (!active && !ws._clayPane) {
|
|
273
|
+
if (!active && (!ws._clayPane || (ws._clayContextual && !ws._clayPaneSession))) {
|
|
271
274
|
var autoOpts = {};
|
|
272
275
|
if (wsUser && usersModule.isMultiUser()) autoOpts.ownerId = wsUser.id;
|
|
273
276
|
active = sm.createSession(autoOpts, ws);
|
|
@@ -301,12 +304,13 @@ function attachConnection(ctx) {
|
|
|
301
304
|
}
|
|
302
305
|
sendTo(ws, { type: "history_meta", total: total, from: fromIndex });
|
|
303
306
|
for (var i = fromIndex; i < total; i++) {
|
|
304
|
-
sendTo(ws, hydrateImageRefs(active.history[i]));
|
|
307
|
+
sendTo(ws, Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(active.history[i], active.vendor || "claude")), { _historyIndex: i }));
|
|
305
308
|
}
|
|
306
|
-
var _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
|
|
309
|
+
var _lastContext = null, _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
|
|
307
310
|
for (var _ri = total - 1; _ri >= 0; _ri--) {
|
|
308
311
|
if (active.history[_ri].type === "result") {
|
|
309
|
-
var _r = active.history[_ri];
|
|
312
|
+
var _r = require("./yoke/usage").clientEvent(active.history[_ri], active.vendor || "claude");
|
|
313
|
+
_lastContext = _r.context;
|
|
310
314
|
_lastUsage = _r.usage || null;
|
|
311
315
|
_lastModelUsage = _r.modelUsage || null;
|
|
312
316
|
_lastCost = _r.cost != null ? _r.cost : null;
|
|
@@ -314,7 +318,7 @@ function attachConnection(ctx) {
|
|
|
314
318
|
break;
|
|
315
319
|
}
|
|
316
320
|
}
|
|
317
|
-
sendTo(ws, { type: "history_done", lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, contextUsage: active.lastContextUsage ||
|
|
321
|
+
sendTo(ws, { type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(active.history || []), sessionId: active.localId, sessionOriginId: active.sessionOriginId || null, lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, context: _lastContext, contextUsage: require("./yoke/usage").context(active.lastContextUsage, active.vendor || "claude") });
|
|
318
322
|
if (scheduledMessages) scheduledMessages.sendState(active, ws);
|
|
319
323
|
if (pairResultRecovery) pairResultRecovery.sendState(active, ws);
|
|
320
324
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var fs = require('fs'), path = require('path');
|
|
2
|
+
var projection = require('./clay-control-target-projection');
|
|
3
|
+
function project(service, principal, status, manager) {
|
|
4
|
+
var before = service.rootIdentity(principal, 'git');
|
|
5
|
+
var directory = fs.realpathSync(status.gitDir), stat = fs.statSync(directory), indexPath = path.join(directory, 'index');
|
|
6
|
+
var index = fs.existsSync(indexPath) ? require('crypto').createHash('sha256').update(fs.readFileSync(indexPath)).digest('hex') : null;
|
|
7
|
+
var view = { runtimeId: projection.runtime(manager), repository: projection.digest([directory, [stat.dev, stat.ino, stat.mode, stat.uid, stat.gid]]),
|
|
8
|
+
files: status.files.slice(0, 200).map(function (file) { return { path: file.path, revision: projection.digest([status.oid, index, file]) }; }) };
|
|
9
|
+
if (service.rootIdentity(principal, 'git') !== before) throw new Error('Workspace identity changed during projection.');
|
|
10
|
+
return view;
|
|
11
|
+
}
|
|
12
|
+
module.exports = { project: project };
|
package/lib/project-debate.js
CHANGED
|
@@ -774,6 +774,7 @@ function attachDebate(ctx) {
|
|
|
774
774
|
var identityMate = matesModule.getMate(debate.mateCtx, msg.mateId);
|
|
775
775
|
var identityProfile = identityMate && identityMate.profile ? identityMate.profile : (identityMate || {});
|
|
776
776
|
if (!msg.mateName) msg.mateName = identity.name;
|
|
777
|
+
if (!msg.avatarCatalogId) msg.avatarCatalogId = identityProfile.avatarCatalogId || "";
|
|
777
778
|
if (!msg.avatarStyle) msg.avatarStyle = identity.avatarStyle;
|
|
778
779
|
if (!msg.avatarSeed) msg.avatarSeed = identity.avatarSeed;
|
|
779
780
|
if (!msg.avatarColor) msg.avatarColor = identity.avatarColor;
|
|
@@ -12,7 +12,7 @@ function unsafeReason(session, isPaired) {
|
|
|
12
12
|
if (session.homeDebatePlanning || session.homeDebatePhase || session.debateSetupMode || session.debateState || session._debate) return "Debate sessions cannot receive project follow-ups.";
|
|
13
13
|
if (session.loop || session.spawn || session.handoff || session.assignment || session.singleTurn || session._pairDelegation || isPaired) return "Orchestrated sessions cannot receive project follow-ups.";
|
|
14
14
|
if (session.dangerouslySkipPermissions || session.permissionMode === "bypassPermissions") return "Full-access sessions cannot receive project follow-ups.";
|
|
15
|
-
if (session.destroying || session.scheduledMessage || session.rateLimitAutoContinuePending || session.taskStopRequested || session._runtimeRefreshRequested || session.pendingPush && session.pendingPush.length) return "Target session is not safely resumable right now.";
|
|
15
|
+
if (session.destroying || session.scheduledMessage || session.rateLimitAutoContinuePending || session.taskStopRequested || session._pendingMessageDrainPaused || session._runtimeRefreshRequested || session.pendingPush && session.pendingPush.length) return "Target session is not safely resumable right now.";
|
|
16
16
|
return "";
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -36,6 +36,7 @@ function attachDelegatedFollowUp(ctx) {
|
|
|
36
36
|
var paired = !!(ctx.isPairedSession && ctx.isPairedSession(session));
|
|
37
37
|
var reason = unsafeReason(session, paired);
|
|
38
38
|
if (reason) throw new Error(reason);
|
|
39
|
+
if (ctx.isPendingQueuePaused && ctx.isPendingQueuePaused(session)) throw new Error("The Driver queue is stopped; explicit owner resume is required.");
|
|
39
40
|
if (!ctx.canResumeVendor(session.vendor)) throw new Error("Target session provider cannot safely resume this conversation.");
|
|
40
41
|
return { session: session, sessionRef: targetSessionRef, title: session.title || "Existing session" };
|
|
41
42
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Reserve a new project directory before running Git or changing ownership.
|
|
2
|
+
// Filesystem identity checks also keep failed attempts from deleting targets
|
|
3
|
+
// replaced by another actor. Hostile OS/root mutation is outside this boundary.
|
|
4
|
+
var fs = require("fs");
|
|
5
|
+
var path = require("path");
|
|
6
|
+
|
|
7
|
+
function canonical(candidate) {
|
|
8
|
+
var current = path.resolve(candidate);
|
|
9
|
+
var missing = [];
|
|
10
|
+
while (true) {
|
|
11
|
+
try { return path.join.apply(path, [fs.realpathSync(current)].concat(missing)); }
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code !== "ENOENT") throw error;
|
|
14
|
+
// A dangling symlink is an existing object, not a missing directory.
|
|
15
|
+
try { fs.lstatSync(current); throw new Error("A project path contains a dangling symlink."); }
|
|
16
|
+
catch (statError) { if (statError.code !== "ENOENT") throw statError; }
|
|
17
|
+
var parent = path.dirname(current);
|
|
18
|
+
if (parent === current) throw error;
|
|
19
|
+
missing.unshift(path.basename(current));
|
|
20
|
+
current = parent;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function inside(root, candidate) {
|
|
26
|
+
var relative = path.relative(root, candidate);
|
|
27
|
+
return relative !== ".." && relative.indexOf(".." + path.sep) !== 0 && !path.isAbsolute(relative);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function rejectIntersection(target, projects, userId) {
|
|
31
|
+
(projects || []).forEach(function (project) {
|
|
32
|
+
var root = canonical(project.path);
|
|
33
|
+
if (target === root || inside(target, root) || (project.ownerId !== userId && inside(root, target))) {
|
|
34
|
+
throw new Error("The destination intersects a registered project.");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function claim(options) {
|
|
40
|
+
var slug = options.slug;
|
|
41
|
+
if (!slug || slug === "." || slug === ".." || path.basename(slug) !== slug) throw new Error("Invalid project directory name.");
|
|
42
|
+
var base = canonical(options.baseDir);
|
|
43
|
+
var target = path.join(base, slug);
|
|
44
|
+
rejectIntersection(target, options.projects, options.userId);
|
|
45
|
+
fs.mkdirSync(base, { recursive: true });
|
|
46
|
+
if (fs.realpathSync(options.baseDir) !== base) throw new Error("The project base directory changed.");
|
|
47
|
+
rejectIntersection(target, options.projects, options.userId);
|
|
48
|
+
// EEXIST rejects directories, files, symlinks and concurrent claims alike.
|
|
49
|
+
fs.mkdirSync(target, { mode: 0o700 });
|
|
50
|
+
var identity = fs.lstatSync(target);
|
|
51
|
+
function assertOwned() {
|
|
52
|
+
var current = fs.lstatSync(target);
|
|
53
|
+
if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== identity.dev || current.ino !== identity.ino || fs.realpathSync(base) !== base) {
|
|
54
|
+
throw new Error("The claimed project directory changed.");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function cleanup() {
|
|
58
|
+
try { assertOwned(); } catch (error) { return false; }
|
|
59
|
+
fs.rmSync(target, { recursive: true });
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return { path: target, assertOwned: assertOwned, cleanup: cleanup };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { claim: claim, canonical: canonical };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Shared file-viewer effects. Authority and scope are derived by each caller.
|
|
2
|
+
var fs = require('fs'), path = require('path'), crypto = require('crypto');
|
|
3
|
+
var resolve = require('./project-file-path').resolveFilePath;
|
|
4
|
+
function hash(content) { return crypto.createHash('sha256').update(content).digest('hex'); }
|
|
5
|
+
function attachFileOperations(ctx) {
|
|
6
|
+
function list(scope, requested) {
|
|
7
|
+
var dir = resolve(ctx.cwd, requested || '.', scope);
|
|
8
|
+
var items = scope.identity ? ctx.fsAsUser('list', { dir: dir }, scope.identity) : fs.readdirSync(dir, { withFileTypes: true }).map(function (item) { return { name: item.name, isDir: item.isDirectory() }; });
|
|
9
|
+
return items.filter(function (item) { return !item.isDir || !ctx.IGNORED_DIRS.has(item.name); }).map(function (item) { return { name: item.name, type: item.isDir ? 'dir' : 'file', path: path.relative(ctx.cwd, path.join(dir, item.name)).split(path.sep).join('/') }; });
|
|
10
|
+
}
|
|
11
|
+
function read(scope, requested) {
|
|
12
|
+
var file = resolve(ctx.cwd, requested, scope), identity = scope.identity;
|
|
13
|
+
var stat = identity ? ctx.fsAsUser('stat', { file: file }, identity) : fs.statSync(file);
|
|
14
|
+
if (stat.size > ctx.FS_MAX_SIZE) return { binary: true, size: stat.size, error: 'File too large (' + (stat.size / 1024 / 1024).toFixed(1) + ' MB)' };
|
|
15
|
+
var ext = path.extname(file).toLowerCase();
|
|
16
|
+
if (ctx.BINARY_EXTS.has(ext)) {
|
|
17
|
+
var binary = { binary: true, size: stat.size };
|
|
18
|
+
if (ctx.IMAGE_EXTS.has(ext)) binary.imageUrl = 'api/file?path=' + encodeURIComponent(requested);
|
|
19
|
+
return binary;
|
|
20
|
+
}
|
|
21
|
+
return { content: identity ? ctx.fsAsUser('read', { file: file, readContent: true }, identity).content : fs.readFileSync(file, 'utf8'), size: stat.size };
|
|
22
|
+
}
|
|
23
|
+
function snapshot(scope, requested) {
|
|
24
|
+
var file = resolve(ctx.cwd, requested, scope), stat = fs.lstatSync(file);
|
|
25
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 65536) throw new Error('Select a regular, singly linked text file of at most 64 KB.');
|
|
26
|
+
var result = read(scope, requested);
|
|
27
|
+
if (result.binary || typeof result.content !== 'string' || result.content.indexOf('\0') >= 0) throw new Error('Binary files are unavailable for canonical editing.');
|
|
28
|
+
return { dev: stat.dev, ino: stat.ino, mode: stat.mode, uid: stat.uid, gid: stat.gid, size: stat.size, mtime: stat.mtimeMs, hash: hash(result.content) };
|
|
29
|
+
}
|
|
30
|
+
function write(scope, requested, content, control) {
|
|
31
|
+
var file = resolve(ctx.cwd, requested, scope), attempted = false;
|
|
32
|
+
if (!control) {
|
|
33
|
+
if (scope.identity) ctx.fsAsUser('write', { file: file, content: content }, scope.identity);
|
|
34
|
+
else fs.writeFileSync(file, content, 'utf8');
|
|
35
|
+
return { ok: true };
|
|
36
|
+
}
|
|
37
|
+
if (typeof fs.constants.O_NOFOLLOW !== 'number') throw new Error('Canonical editing requires no-follow file descriptors on this platform.');
|
|
38
|
+
control.revalidate();
|
|
39
|
+
if (JSON.stringify(snapshot(scope, requested)) !== JSON.stringify(control.expected)) throw new Error('The exact file revision changed.');
|
|
40
|
+
try {
|
|
41
|
+
attempted = true;
|
|
42
|
+
if (scope.identity) ctx.fsAsUser('write_exact', { file: file, content: content, expected: control.expected, root: fs.realpathSync(ctx.cwd) }, scope.identity);
|
|
43
|
+
else {
|
|
44
|
+
var fd = fs.openSync(file, fs.constants.O_RDWR | fs.constants.O_NOFOLLOW);
|
|
45
|
+
try {
|
|
46
|
+
var stat = fs.fstatSync(fd), expected = control.expected;
|
|
47
|
+
if (!stat.isFile() || stat.nlink !== 1 || stat.dev !== expected.dev || stat.ino !== expected.ino || stat.mode !== expected.mode || stat.uid !== expected.uid || stat.gid !== expected.gid || stat.size !== expected.size || stat.mtimeMs !== expected.mtime || hash(fs.readFileSync(fd, 'utf8')) !== expected.hash) throw new Error('The open file revision changed.');
|
|
48
|
+
control.revalidate();
|
|
49
|
+
fs.ftruncateSync(fd, 0);
|
|
50
|
+
var bytes = Buffer.from(content, 'utf8'), offset = 0;
|
|
51
|
+
while (offset < bytes.length) { var written = fs.writeSync(fd, bytes, offset, bytes.length - offset, offset); if (!written) throw new Error('File write made no progress.'); offset += written; }
|
|
52
|
+
fs.fsyncSync(fd);
|
|
53
|
+
} finally { fs.closeSync(fd); }
|
|
54
|
+
}
|
|
55
|
+
control.revalidate();
|
|
56
|
+
return { status: 'completed', persisted: true, path: requested };
|
|
57
|
+
} catch (error) { error.productControlResult = { persisted: attempted ? 'unknown' : false, retrySafe: false, error: error.message }; throw error; }
|
|
58
|
+
}
|
|
59
|
+
return { list: list, read: read, write: write, snapshot: snapshot };
|
|
60
|
+
}
|
|
61
|
+
module.exports = { attachFileOperations: attachFileOperations };
|
|
@@ -37,6 +37,7 @@ function attachFilesystem(ctx) {
|
|
|
37
37
|
|
|
38
38
|
var access = ctx.requestAccess || attachRequestAccess(ctx);
|
|
39
39
|
var fileHistory = attachFileHistory(ctx, access);
|
|
40
|
+
var operations = require("./project-file-operations").attachFileOperations(ctx);
|
|
40
41
|
|
|
41
42
|
function handleFilesystemMessage(ws, msg) {
|
|
42
43
|
var fileScope;
|
|
@@ -57,40 +58,11 @@ function attachFilesystem(ctx) {
|
|
|
57
58
|
|
|
58
59
|
// --- fs_list ---
|
|
59
60
|
if (msg.type === "fs_list") {
|
|
60
|
-
var fsListIdentity = fileScope.identity;
|
|
61
|
-
var fsDir = fileTarget;
|
|
62
61
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (fsListUserInfo) {
|
|
66
|
-
// Run as target OS user to respect Linux file permissions
|
|
67
|
-
var rawEntries = fsAsUser("list", { dir: fsDir }, fsListUserInfo);
|
|
68
|
-
for (var fi = 0; fi < rawEntries.length; fi++) {
|
|
69
|
-
var re = rawEntries[fi];
|
|
70
|
-
if (re.isDir && IGNORED_DIRS.has(re.name)) continue;
|
|
71
|
-
entries.push({
|
|
72
|
-
name: re.name,
|
|
73
|
-
type: re.isDir ? "dir" : "file",
|
|
74
|
-
path: path.relative(cwd, path.join(fsDir, re.name)).split(path.sep).join("/"),
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
var items = fs.readdirSync(fsDir, { withFileTypes: true });
|
|
79
|
-
for (var fi = 0; fi < items.length; fi++) {
|
|
80
|
-
var item = items[fi];
|
|
81
|
-
if (item.isDirectory() && IGNORED_DIRS.has(item.name)) continue;
|
|
82
|
-
entries.push({
|
|
83
|
-
name: item.name,
|
|
84
|
-
type: item.isDirectory() ? "dir" : "file",
|
|
85
|
-
path: path.relative(cwd, path.join(fsDir, item.name)).split(path.sep).join("/"),
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
sendTo(ws, { type: "fs_list_result", path: msg.path || ".", entries: entries });
|
|
90
|
-
// Auto-watch the directory for changes
|
|
91
|
-
startDirWatch(ws, msg.path || ".");
|
|
62
|
+
sendTo(ws, { type: 'fs_list_result', path: msg.path || '.', entries: operations.list(fileScope, msg.path || '.') });
|
|
63
|
+
startDirWatch(ws, msg.path || '.');
|
|
92
64
|
} catch (e) {
|
|
93
|
-
sendTo(ws, { type:
|
|
65
|
+
sendTo(ws, { type: 'fs_list_result', path: msg.path, entries: [], error: fileError(e).message, errorCode: fileError(e).code });
|
|
94
66
|
}
|
|
95
67
|
return true;
|
|
96
68
|
}
|
|
@@ -143,62 +115,19 @@ function attachFilesystem(ctx) {
|
|
|
143
115
|
|
|
144
116
|
// --- fs_read ---
|
|
145
117
|
if (msg.type === "fs_read") {
|
|
146
|
-
var
|
|
147
|
-
var fsFile = fileTarget;
|
|
118
|
+
var metadata = { type: 'fs_read_result', path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId };
|
|
148
119
|
try {
|
|
149
|
-
var
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
// Run stat and read as target OS user
|
|
153
|
-
var statResult = fsAsUser("stat", { file: fsFile }, fsReadUserInfo);
|
|
154
|
-
if (statResult.size > FS_MAX_SIZE) {
|
|
155
|
-
sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: statResult.size, error: "File too large (" + (statResult.size / 1024 / 1024).toFixed(1) + " MB)" });
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
if (BINARY_EXTS.has(ext)) {
|
|
159
|
-
var result = { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: statResult.size };
|
|
160
|
-
if (IMAGE_EXTS.has(ext)) result.imageUrl = "api/file?path=" + encodeURIComponent(msg.path);
|
|
161
|
-
sendTo(ws, result);
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
var readResult = fsAsUser("read", { file: fsFile, readContent: true }, fsReadUserInfo);
|
|
165
|
-
sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, content: readResult.content, size: statResult.size });
|
|
166
|
-
} else {
|
|
167
|
-
var stat = fs.statSync(fsFile);
|
|
168
|
-
if (stat.size > FS_MAX_SIZE) {
|
|
169
|
-
sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: stat.size, error: "File too large (" + (stat.size / 1024 / 1024).toFixed(1) + " MB)" });
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
if (BINARY_EXTS.has(ext)) {
|
|
173
|
-
var result = { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, binary: true, size: stat.size };
|
|
174
|
-
if (IMAGE_EXTS.has(ext)) result.imageUrl = "api/file?path=" + encodeURIComponent(msg.path);
|
|
175
|
-
sendTo(ws, result);
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
var content = fs.readFileSync(fsFile, "utf8");
|
|
179
|
-
sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, content: content, size: stat.size });
|
|
180
|
-
}
|
|
181
|
-
} catch (e) {
|
|
182
|
-
sendTo(ws, { type: "fs_read_result", path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId, error: fileError(e).message, errorCode: fileError(e).code });
|
|
120
|
+
var readResult = operations.read(fileScope, msg.path);
|
|
121
|
+
try { var fileSnapshot = operations.snapshot(fileScope, msg.path); if (fileSnapshot.hash === require('crypto').createHash('sha256').update(readResult.content).digest('hex')) readResult.controlRevision = require('./clay-control-target-projection').digest(fileSnapshot); } catch (ignored) {}
|
|
122
|
+
sendTo(ws, Object.assign(metadata, readResult));
|
|
183
123
|
}
|
|
124
|
+
catch (e) { sendTo(ws, Object.assign(metadata, { error: fileError(e).message, errorCode: fileError(e).code })); }
|
|
184
125
|
return true;
|
|
185
126
|
}
|
|
186
127
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
var fsWriteFile = fileTarget;
|
|
191
|
-
try {
|
|
192
|
-
var fsWriteUserInfo = fsWriteIdentity;
|
|
193
|
-
if (fsWriteUserInfo) {
|
|
194
|
-
fsAsUser("write", { file: fsWriteFile, content: msg.content || "" }, fsWriteUserInfo);
|
|
195
|
-
} else {
|
|
196
|
-
fs.writeFileSync(fsWriteFile, msg.content || "", "utf8");
|
|
197
|
-
}
|
|
198
|
-
sendTo(ws, { type: "fs_write_result", path: msg.path, ok: true });
|
|
199
|
-
} catch (e) {
|
|
200
|
-
sendTo(ws, { type: "fs_write_result", path: msg.path, ok: false, error: fileError(e).message, errorCode: fileError(e).code });
|
|
201
|
-
}
|
|
128
|
+
if (msg.type === 'fs_write') {
|
|
129
|
+
try { sendTo(ws, Object.assign({ type: 'fs_write_result', path: msg.path }, operations.write(fileScope, msg.path, msg.content || ''))); }
|
|
130
|
+
catch (e) { sendTo(ws, { type: 'fs_write_result', path: msg.path, ok: false, error: fileError(e).message, errorCode: fileError(e).code }); }
|
|
202
131
|
return true;
|
|
203
132
|
}
|
|
204
133
|
|
|
@@ -239,8 +168,8 @@ function attachFilesystem(ctx) {
|
|
|
239
168
|
var setResult;
|
|
240
169
|
try { setResult = opts.onSetProjectEnv(targetSlug, msg.envrc || ""); }
|
|
241
170
|
catch (error) { sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: error.message }); return true; }
|
|
242
|
-
if (setResult.ok) onEnvironmentChanged();
|
|
243
|
-
sendTo(ws, { type: "set_project_env_result",
|
|
171
|
+
if (setResult.ok && !setResult.runtimeRefreshed) onEnvironmentChanged();
|
|
172
|
+
sendTo(ws, Object.assign({}, setResult, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, timing: setResult.ok ? "Applies to newly created coding-agent processes. Active processes keep their current environment." : undefined }));
|
|
244
173
|
} else {
|
|
245
174
|
sendTo(ws, { type: "set_project_env_result", slug: targetSlug, requestId: msg.requestId, ok: false, error: "Not supported" });
|
|
246
175
|
}
|
|
@@ -297,6 +226,7 @@ function attachFilesystem(ctx) {
|
|
|
297
226
|
|
|
298
227
|
return {
|
|
299
228
|
handleFilesystemMessage: handleFilesystemMessage,
|
|
229
|
+
productOperations: operations,
|
|
300
230
|
};
|
|
301
231
|
}
|
|
302
232
|
|