clay-server 5.2.0-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-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 +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 +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/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 +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-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-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/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/index.html +11 -5
- 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-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 +115 -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/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 +3 -0
- 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/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-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.js +3 -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/muse-avatar.svg +4 -0
- package/lib/public/style.css +5 -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-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-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 +6 -1
- package/lib/server.js +163 -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/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/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 +10 -4
|
@@ -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
|
|
|
@@ -16,7 +16,7 @@ function createFullAccessService(ctx) {
|
|
|
16
16
|
enabled: session.permissionMode === "bypassPermissions" };
|
|
17
17
|
}
|
|
18
18
|
function persist(session) {
|
|
19
|
-
sm.saveSessionFile(session);
|
|
19
|
+
if (sm.saveSessionFile(session) === false) throw new Error("Could not persist permission mode.");
|
|
20
20
|
sm.broadcastSessionList();
|
|
21
21
|
if (typeof sm.sendToSession === "function") sm.sendToSession(session, { type: "session_full_access_changed", id: session.localId,
|
|
22
22
|
enabled: session.permissionMode === "bypassPermissions", permissionMode: session.permissionMode });
|
|
@@ -55,7 +55,28 @@ function createFullAccessService(ctx) {
|
|
|
55
55
|
throw error;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
function setMode(session, mode
|
|
58
|
+
function setMode(session, mode, options) {
|
|
59
|
+
return serialize(session, function () {
|
|
60
|
+
if (!options || typeof options.revalidate !== "function") return applyMode(session, mode);
|
|
61
|
+
options.revalidate();
|
|
62
|
+
var observed = { revision: Number(session._permissionObservationRevision || 0), mode: session.effectivePermissionMode, reason: session.permissionModeFallbackReason };
|
|
63
|
+
return require("./session-config-transition").applySessionConfig({
|
|
64
|
+
sm: sm, session: session, revalidate: options.revalidate, method: "setPermissionMode", nativeValue: mode === "bypassPermissions" ? "default" : mode,
|
|
65
|
+
changes: function () {
|
|
66
|
+
var newer = Number(session._permissionObservationRevision || 0) !== observed.revision || session.effectivePermissionMode !== observed.mode || session.permissionModeFallbackReason !== observed.reason;
|
|
67
|
+
return { permissionMode: mode, permissionModeBeforeFullAccess: mode === "bypassPermissions" ? (session.permissionMode || sm.currentPermissionMode || "default") : null,
|
|
68
|
+
effectivePermissionMode: newer ? session.effectivePermissionMode : null, permissionModeFallbackReason: newer ? session.permissionModeFallbackReason : null };
|
|
69
|
+
},
|
|
70
|
+
publish: function () {
|
|
71
|
+
sm.broadcastSessionList();
|
|
72
|
+
if (typeof sm.sendToSession === "function") {
|
|
73
|
+
sm.sendToSession(session, { type: "session_full_access_changed", id: session.localId, enabled: mode === "bypassPermissions", permissionMode: mode });
|
|
74
|
+
sm.sendToSession(session, { type: "config_state", sessionId: session.localId, mode: mode, effectivePermissionMode: session.effectivePermissionMode || null, vendor: session.vendor || sm.defaultVendor || "claude" });
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
59
80
|
function setEnabled(session, enabled) {
|
|
60
81
|
return serialize(session, function () {
|
|
61
82
|
return applyMode(session, enabled ? "bypassPermissions" : (session.permissionModeBeforeFullAccess || "default"));
|
package/lib/project-http.js
CHANGED
|
@@ -419,9 +419,9 @@ function attachHTTP(ctx) {
|
|
|
419
419
|
var uninstallUserInfo = getOsUserInfoForReq(req);
|
|
420
420
|
var baseDir = scope === "global" ? (uninstallUserInfo ? uninstallUserInfo.home : require("./config").REAL_HOME) : cwd;
|
|
421
421
|
if (osUsers && !uninstallUserInfo) throw new Error("A mapped OS user is required to uninstall skills");
|
|
422
|
-
var skillDir = claySkillStorage.isClaySkill(skill) ? path.join(claySkillStorage.privateRoot(baseDir), skill) : path.join(
|
|
422
|
+
var skillDir = claySkillStorage.isClaySkill(skill) ? path.join(claySkillStorage.privateRoot(baseDir), skill) : path.join(require("./yoke/source-locations").legacySkillRoot(baseDir), skill);
|
|
423
423
|
// Safety: keep removal inside the selected skill storage root.
|
|
424
|
-
var expectedParent = claySkillStorage.isClaySkill(skill) ? claySkillStorage.privateRoot(baseDir) :
|
|
424
|
+
var expectedParent = claySkillStorage.isClaySkill(skill) ? claySkillStorage.privateRoot(baseDir) : require("./yoke/source-locations").legacySkillRoot(baseDir);
|
|
425
425
|
var resolved = path.resolve(skillDir);
|
|
426
426
|
if (!resolved.startsWith(expectedParent + path.sep)) {
|
|
427
427
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
@@ -528,8 +528,8 @@ function attachHTTP(ctx) {
|
|
|
528
528
|
var sui = getOsUserInfoForReq(req);
|
|
529
529
|
return sui ? sui.home : require("./config").REAL_HOME;
|
|
530
530
|
})();
|
|
531
|
-
var globalSkillsDir =
|
|
532
|
-
var projectSkillsDir =
|
|
531
|
+
var globalSkillsDir = require("./yoke/source-locations").legacySkillRoot(skillUserHome);
|
|
532
|
+
var projectSkillsDir = require("./yoke/source-locations").legacySkillRoot(cwd);
|
|
533
533
|
var results = [];
|
|
534
534
|
var pending = skills.length;
|
|
535
535
|
|
|
@@ -663,6 +663,9 @@ function attachHTTP(ctx) {
|
|
|
663
663
|
var gitUserInfo = getOsUserInfoForReq(req);
|
|
664
664
|
try {
|
|
665
665
|
var gitStatus = gitCli.getStatus(cwd, gitUserInfo);
|
|
666
|
+
if (ctx.workspaceOperations) {
|
|
667
|
+
try { gitStatus.controlView = require('./project-control-git-view').project(ctx.workspaceOperations, { userId: req._clayUser && req._clayUser.id || null, singleUserOwnerId: ctx.getProjectOwnerId && ctx.getProjectOwnerId() || null }, gitStatus, sm); } catch (ignored) {}
|
|
668
|
+
}
|
|
666
669
|
if (gitAttribution) {
|
|
667
670
|
var visibleGitSessions = sm.sessions;
|
|
668
671
|
if (usersModule.isMultiUser() && req._clayUser) {
|
package/lib/project-knowledge.js
CHANGED
|
@@ -44,21 +44,30 @@ function attachKnowledge(ctx) {
|
|
|
44
44
|
return { type: user ? "user" : "system", userId: user ? user.id : null, displayName: user ? (user.displayName || user.username) : null };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function
|
|
47
|
+
function mirrorActor(who, name) {
|
|
48
48
|
if (!isMate) return { ok: true, names: [] };
|
|
49
|
-
var summary = mateSync.syncMateSource({ mateDir: cwd, fileName: name, baseDir: ctx.knowledgeBaseDir, actor:
|
|
49
|
+
var summary = mateSync.syncMateSource({ mateDir: cwd, fileName: name, baseDir: ctx.knowledgeBaseDir, actor: who });
|
|
50
50
|
return { ok: !!summary && summary.failed === 0, names: [name], error: summary && summary.errors[0] ? summary.errors[0].message : "Knowledge index synchronization failed." };
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function
|
|
53
|
+
function mirror(ws, name) { return mirrorActor(actor(ws), name); }
|
|
54
|
+
|
|
55
|
+
function mirrorManyActor(who, names) {
|
|
54
56
|
var failures = [];
|
|
55
57
|
for (var i = 0; i < names.length; i++) {
|
|
56
|
-
var mirrored =
|
|
58
|
+
var mirrored = mirrorActor(who, names[i]);
|
|
57
59
|
if (!mirrored.ok) failures.push(mirrored.error);
|
|
58
60
|
}
|
|
59
61
|
return { ok: failures.length === 0, names: names.slice(), error: failures[0] || null };
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
function mirrorMany(ws, names) { return mirrorManyActor(actor(ws), names); }
|
|
65
|
+
|
|
66
|
+
function controlMirror(who, names) {
|
|
67
|
+
try { var state = mirrorManyActor(who, names); return { indexSyncPending: !state.ok, syncNames: names, syncError: state.ok ? null : state.error }; }
|
|
68
|
+
catch (error) { return { indexSyncPending: true, syncNames: names, syncError: error.message }; }
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
function addSyncState(payload, sync) {
|
|
63
72
|
if (sync.ok) return payload;
|
|
64
73
|
return Object.assign(payload, { indexSyncPending: true, syncNames: sync.names, syncError: sync.error });
|
|
@@ -188,6 +197,22 @@ function attachKnowledge(ctx) {
|
|
|
188
197
|
return databaseReady.then(function () { return combineSearch(databases.listCached({})); });
|
|
189
198
|
}
|
|
190
199
|
|
|
200
|
+
function renameDocument(msg, who) {
|
|
201
|
+
var beforeMoveState = documentIndex(); var beforeMove = beforeMoveState.documents; var moveTarget = findDocument(beforeMove, msg.from, msg.documentId); var linkChanges = moveTarget ? rewriteMoveLinks(beforeMove, moveTarget, msg.name) : [];
|
|
202
|
+
var renamed = files.rename(msg.from, msg.name, msg.expectedRevision || null); var linkUpdateFailures = []; var updatedLinks = 0;
|
|
203
|
+
var updatedDocuments = []; var mirrorNames = [msg.from, renamed.name];
|
|
204
|
+
for (var u = 0; u < linkChanges.length; u++) {
|
|
205
|
+
var change = linkChanges[u]; var writeName = change.id === renamed.id ? renamed.name : change.name; var expected = change.id === renamed.id ? renamed.revision : change.revision;
|
|
206
|
+
try { var rewritten = files.save(writeName, change.content, expected); updatedLinks += change.count; updatedDocuments.push(rewritten); mirrorNames.push(rewritten.name); }
|
|
207
|
+
catch (linkError) { linkUpdateFailures.push({ name: writeName, error: linkError.message }); }
|
|
208
|
+
}
|
|
209
|
+
renamed = files.read(renamed.name);
|
|
210
|
+
var uniqueMirrorNames = mirrorNames.filter(function (name, at) { return mirrorNames.indexOf(name) === at; }); var renameSync = mirrorManyActor(who, uniqueMirrorNames);
|
|
211
|
+
return addSyncState({ type: "knowledge_renamed", id: renamed.id, from: msg.from, name: renamed.name, revision: renamed.revision,
|
|
212
|
+
content: renamed.content, updatedLinks: updatedLinks, linkUpdateFailures: linkUpdateFailures,
|
|
213
|
+
updatedDocuments: updatedDocuments.map(function (item) { return { id: item.id, name: item.name, content: item.content, revision: item.revision }; }) }, renameSync);
|
|
214
|
+
}
|
|
215
|
+
|
|
191
216
|
function handleKnowledgeMessage(ws, msg) {
|
|
192
217
|
if (msg.type === "knowledge_list") {
|
|
193
218
|
try { var listed = listKnowledgeItems(ws); if (listed && typeof listed.then === "function") listed.then(function (items) { response(ws, msg, { type: "knowledge_list", files: items, drafts: drafts.list(ws && ws._clayUser && ws._clayUser.id || "system") }); }).catch(function (error) { failure(ws, msg, error); }); else response(ws, msg, { type: "knowledge_list", files: listed, drafts: drafts.list(ws && ws._clayUser && ws._clayUser.id || "system") }); } catch (error) { failure(ws, msg, error); }
|
|
@@ -236,19 +261,7 @@ function attachKnowledge(ctx) {
|
|
|
236
261
|
if (msg.type === "knowledge_rename") {
|
|
237
262
|
if (!requireWrite(ws, msg)) return true;
|
|
238
263
|
try {
|
|
239
|
-
|
|
240
|
-
var renamed = files.rename(msg.from, msg.name, msg.expectedRevision || null); var linkUpdateFailures = []; var updatedLinks = 0;
|
|
241
|
-
var updatedDocuments = []; var mirrorNames = [msg.from, renamed.name];
|
|
242
|
-
for (var u = 0; u < linkChanges.length; u++) {
|
|
243
|
-
var change = linkChanges[u]; var writeName = change.id === renamed.id ? renamed.name : change.name; var expected = change.id === renamed.id ? renamed.revision : change.revision;
|
|
244
|
-
try { var rewritten = files.save(writeName, change.content, expected); updatedLinks += change.count; updatedDocuments.push(rewritten); mirrorNames.push(rewritten.name); }
|
|
245
|
-
catch (linkError) { linkUpdateFailures.push({ name: writeName, error: linkError.message }); }
|
|
246
|
-
}
|
|
247
|
-
renamed = files.read(renamed.name);
|
|
248
|
-
var uniqueMirrorNames = mirrorNames.filter(function (name, at) { return mirrorNames.indexOf(name) === at; }); var renameSync = mirrorMany(ws, uniqueMirrorNames);
|
|
249
|
-
response(ws, msg, addSyncState({ type: "knowledge_renamed", id: renamed.id, from: msg.from, name: renamed.name, revision: renamed.revision,
|
|
250
|
-
content: renamed.content, updatedLinks: updatedLinks, linkUpdateFailures: linkUpdateFailures,
|
|
251
|
-
updatedDocuments: updatedDocuments.map(function (item) { return { id: item.id, name: item.name, content: item.content, revision: item.revision }; }) }, renameSync));
|
|
264
|
+
response(ws, msg, renameDocument(msg, actor(ws)));
|
|
252
265
|
sendTo(ws, { type: "knowledge_list", files: listKnowledgeFiles() });
|
|
253
266
|
} catch (error) { failure(ws, msg, error); }
|
|
254
267
|
return true;
|
|
@@ -357,7 +370,8 @@ function attachKnowledge(ctx) {
|
|
|
357
370
|
return false;
|
|
358
371
|
}
|
|
359
372
|
|
|
360
|
-
|
|
373
|
+
var productOperations = require('./knowledge-product-operations').createKnowledgeProductOperations({ files: files, mirror: controlMirror, rename: renameDocument });
|
|
374
|
+
return { productOperations: productOperations, handleKnowledgeMessage: handleKnowledgeMessage, listKnowledgeFiles: listKnowledgeFiles, databases: databases };
|
|
361
375
|
}
|
|
362
376
|
|
|
363
377
|
module.exports = { attachKnowledge: attachKnowledge };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Durable operation identities and independent closure proofs survive config-save failures.
|
|
2
|
+
var fs = require('fs');
|
|
3
|
+
var path = require('path');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
function attachLifecycleOperations(ctx) {
|
|
6
|
+
function operations() { return ctx.config().projectLifecycleOperations || {}; }
|
|
7
|
+
function proofFile(id) {
|
|
8
|
+
if (!/^[a-f0-9-]{36}$/.test(id)) throw new Error('Invalid lifecycle operation identity.');
|
|
9
|
+
return path.join(ctx.storageDir, 'project-lifecycle-operations', id + '.json');
|
|
10
|
+
}
|
|
11
|
+
function closed(op) {
|
|
12
|
+
try { var proof = JSON.parse(fs.readFileSync(proofFile(op.id), 'utf8')); return proof.id === op.id && proof.targets === JSON.stringify(op.targets); }
|
|
13
|
+
catch (error) { if (error.code === 'ENOENT') return false; throw error; }
|
|
14
|
+
}
|
|
15
|
+
function confirmClosed(op) {
|
|
16
|
+
var file = proofFile(op.id); fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
17
|
+
fs.writeFileSync(file + '.tmp', JSON.stringify({ id: op.id, targets: JSON.stringify(op.targets) }), { mode: 0o600 });
|
|
18
|
+
fs.renameSync(file + '.tmp', file);
|
|
19
|
+
}
|
|
20
|
+
function partial(op, error) {
|
|
21
|
+
return { ok: false, status: 'failed', action: op.action, outcome: op.restoration ? 'restoration_publication_pending' : op.action === 'remove' ? 'removal_reserved_runtime_pending' : 'publication_pending', persisted: true,
|
|
22
|
+
retrySafe: false, recoveryAction: 'reconcile', operationId: op.id, slug: op.slug, error: error || 'The saved operation needs explicit owner-reviewed reconciliation.' };
|
|
23
|
+
}
|
|
24
|
+
function begin(next, project, action, result, correlation, targets) {
|
|
25
|
+
var id = crypto.randomUUID();
|
|
26
|
+
if (action === 'remove') { project.lifecycleState = 'removing'; project.lifecycleOperationId = id; }
|
|
27
|
+
var op = { id: id, action: action, slug: project.slug, projectKnowledgeId: project.projectKnowledgeId, ownerId: correlation ? correlation.ownerId : project.ownerId || null,
|
|
28
|
+
sourceKey: correlation && correlation.sourceKey || null, expected: JSON.stringify(project), directory: ctx.directory(project.path), targets: targets || [], result: result,
|
|
29
|
+
correlation: correlation ? { key: correlation.key, fingerprint: correlation.fingerprint, ownerId: correlation.ownerId, sourceKey: correlation.sourceKey } : null, phase: 'pending' };
|
|
30
|
+
var all = Object.assign({}, next.projectLifecycleOperations || {});
|
|
31
|
+
if (Object.keys(all).length >= 5000) throw new Error('Lifecycle operation capacity reached.');
|
|
32
|
+
all[id] = op; next.projectLifecycleOperations = all;
|
|
33
|
+
ctx.record(next, correlation, partial(op), project, action); return op;
|
|
34
|
+
}
|
|
35
|
+
function exact(op) {
|
|
36
|
+
var project = ctx.find(op.slug);
|
|
37
|
+
if (!project || project.projectKnowledgeId !== op.projectKnowledgeId || JSON.stringify(project) !== op.expected || JSON.stringify(ctx.directory(project.path)) !== JSON.stringify(op.directory)) throw new Error('The exact saved registration changed. Recovery will not overwrite or delete it.');
|
|
38
|
+
return project;
|
|
39
|
+
}
|
|
40
|
+
function inspect(slug, id, principal) {
|
|
41
|
+
var op = id ? operations()[id] : Object.values(operations()).reverse().find(function (item) { return item.slug === slug && item.phase === 'pending'; });
|
|
42
|
+
var current = ctx.find(slug);
|
|
43
|
+
if (!op || op.slug !== slug || (op.ownerId !== (principal.userId || null) && (!current || current.ownerId !== principal.userId))) throw new Error('No owned lifecycle operation is available.');
|
|
44
|
+
var conflict = op.phase !== 'completed' && JSON.stringify(current) !== op.expected;
|
|
45
|
+
return { registrationTitle: current && current.ownerId === principal.userId ? current.title || current.slug : undefined, conflict: conflict, operationId: op.id, action: op.action, phase: op.phase, runtimeClosureConfirmed: op.action === 'remove' && closed(op), revision: JSON.stringify([op, current]) };
|
|
46
|
+
}
|
|
47
|
+
function complete(next, op, project) {
|
|
48
|
+
var all = Object.assign({}, next.projectLifecycleOperations);
|
|
49
|
+
all[op.id] = Object.assign({}, op, { phase: 'completed' }); next.projectLifecycleOperations = all;
|
|
50
|
+
ctx.record(next, op.correlation, Object.assign({}, op.result, { operationId: op.id }), project, op.action);
|
|
51
|
+
}
|
|
52
|
+
async function reconcile(slug, id, principal, authorize, validate, restore) {
|
|
53
|
+
var info = inspect(slug, id, principal), op = operations()[info.operationId];
|
|
54
|
+
if (op.phase === 'completed') return Object.assign({}, op.result, { operationId: op.id });
|
|
55
|
+
if (validate) validate();
|
|
56
|
+
try {
|
|
57
|
+
if (op.action === 'remove' && !op.restoration) {
|
|
58
|
+
await ctx.relay().closeProjectLifecycle(op.targets, closed(op));
|
|
59
|
+
if (ctx.relay().getProjectLifecycleTargets(slug).length) throw new Error('A live project or worktree runtime appeared during closure.');
|
|
60
|
+
confirmClosed(op);
|
|
61
|
+
authorize();
|
|
62
|
+
var registration = ctx.find(slug);
|
|
63
|
+
if (restore) {
|
|
64
|
+
if (!registration || registration.ownerId !== principal.userId || registration.projectKnowledgeId !== op.projectKnowledgeId || registration.lifecycleOperationId !== op.id) throw new Error('The saved registration no longer belongs to this recovery operation or owner.');
|
|
65
|
+
} else exact(op);
|
|
66
|
+
var next = ctx.snapshot();
|
|
67
|
+
var project = next.projects.find(function (item) { return item.slug === op.slug; });
|
|
68
|
+
if (restore) {
|
|
69
|
+
delete project.lifecycleState; delete project.lifecycleOperationId; project.lifecycleExecutionPaused = true;
|
|
70
|
+
if (JSON.stringify(ctx.directory(project.path)) !== JSON.stringify(op.directory)) throw new Error('The project directory identity changed; restoration was refused.');
|
|
71
|
+
var restored = Object.assign({}, op, { restoration: true, expected: JSON.stringify(project), result: { ok: true, status: 'superseded', originalAction: 'remove', slug: op.slug, registrationRestored: true, executionPaused: true, semantics: 'Registration restored. Queued and scheduled execution stays paused until the owner explicitly sends a new message.' } });
|
|
72
|
+
next.projectLifecycleOperations = Object.assign({}, next.projectLifecycleOperations); next.projectLifecycleOperations[op.id] = restored; ctx.save(next); op = restored;
|
|
73
|
+
ctx.publishRuntime(project, 'recovered'); authorize(); exact(op);
|
|
74
|
+
next = ctx.snapshot(); complete(next, op, project); ctx.save(next); ctx.publish();
|
|
75
|
+
return Object.assign({}, restored.result, { status: 'completed', action: 'recover_registration', originalOutcome: 'superseded', operationId: op.id });
|
|
76
|
+
}
|
|
77
|
+
next.projects = next.projects.filter(function (item) { return item.slug !== op.slug; });
|
|
78
|
+
next.removedProjects = (next.removedProjects || []).filter(function (item) { return item.path !== project.path; }).concat([{ path: project.path, title: project.title || null, icon: project.icon || null, userId: op.ownerId, projectKnowledgeId: project.projectKnowledgeId, removedAt: Date.now() }]).slice(-20);
|
|
79
|
+
complete(next, op, project); ctx.save(next);
|
|
80
|
+
} else {
|
|
81
|
+
if (restore && !op.restoration) throw new Error('Only a pending removal can restore its registration.');
|
|
82
|
+
authorize(); var current = ctx.find(op.slug), currentRevision = JSON.stringify(current);
|
|
83
|
+
if (currentRevision !== op.expected) {
|
|
84
|
+
if (!current || current.projectKnowledgeId !== op.projectKnowledgeId || current.ownerId !== principal.userId || JSON.stringify(ctx.directory(current.path)) !== JSON.stringify(op.directory)) throw new Error('The saved publication target was replaced or transferred.');
|
|
85
|
+
ctx.publishRuntime(current, 'recovered'); authorize();
|
|
86
|
+
if (JSON.stringify(ctx.find(op.slug)) !== currentRevision) throw new Error('Registration changed during publication reconciliation.');
|
|
87
|
+
op = Object.assign({}, op, { result: { ok: true, status: 'superseded', slug: op.slug, persisted: true, currentRegistrationRetained: true } });
|
|
88
|
+
} else {
|
|
89
|
+
exact(op); ctx.publishRuntime(current, op.restoration ? 'recovered' : op.action); authorize(); exact(op);
|
|
90
|
+
}
|
|
91
|
+
var updated = ctx.snapshot(); complete(updated, op, current); ctx.save(updated);
|
|
92
|
+
}
|
|
93
|
+
ctx.publish(); return Object.assign({}, op.result, { operationId: op.id });
|
|
94
|
+
} catch (error) { error.lifecycleResult = Object.assign({}, error.lifecycleResult || {}, partial(op, error.message)); throw error; }
|
|
95
|
+
}
|
|
96
|
+
return { begin: begin, partial: partial, exact: exact, closed: closed, confirmClosed: confirmClosed, inspect: inspect, complete: complete, reconcile: reconcile };
|
|
97
|
+
}
|
|
98
|
+
module.exports = { attachLifecycleOperations: attachLifecycleOperations };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Bounded, content-free process identities for closure reconciliation after host exit.
|
|
2
|
+
var cp = require('child_process');
|
|
3
|
+
function table() {
|
|
4
|
+
var output = cp.execFileSync('ps', ['-axo', 'pid=,ppid=,lstart='], { encoding: 'utf8', timeout: 5000, maxBuffer: 8 * 1024 * 1024, env: Object.assign({}, process.env, { LC_ALL: 'C', TZ: 'UTC' }) });
|
|
5
|
+
var rows = new Map();
|
|
6
|
+
output.split('\n').forEach(function (line) {
|
|
7
|
+
var match = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/.exec(line);
|
|
8
|
+
if (match) rows.set(Number(match[1]), { pid: Number(match[1]), parent: Number(match[2]), start: match[3] });
|
|
9
|
+
});
|
|
10
|
+
if (!rows.has(process.pid)) throw new Error("The process identity census was incomplete.");
|
|
11
|
+
return rows;
|
|
12
|
+
}
|
|
13
|
+
function capture() {
|
|
14
|
+
try {
|
|
15
|
+
var rows = table(), host = rows.get(process.pid), children = [], parents = new Set([process.pid]);
|
|
16
|
+
if (!host) return null;
|
|
17
|
+
for (var pass = 0; pass < rows.size; pass++) {
|
|
18
|
+
var changed = false;
|
|
19
|
+
rows.forEach(function (row) { if (parents.has(row.parent) && !parents.has(row.pid)) { parents.add(row.pid); children.push(row); changed = true; } });
|
|
20
|
+
if (!changed) break;
|
|
21
|
+
}
|
|
22
|
+
return { host: host, children: children };
|
|
23
|
+
} catch (error) { return null; }
|
|
24
|
+
}
|
|
25
|
+
function confirmsExit(witness) {
|
|
26
|
+
if (!witness || !witness.host || !Array.isArray(witness.children)) return false;
|
|
27
|
+
var rows = table();
|
|
28
|
+
return [witness.host].concat(witness.children).every(function (previous) {
|
|
29
|
+
var current = rows.get(previous.pid); return !current || current.start !== previous.start;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
module.exports = { capture: capture, confirmsExit: confirmsExit };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Retain failed shutdown handles so an explicitly reviewed recovery can retry them.
|
|
2
|
+
var crypto = require('crypto');
|
|
3
|
+
var witnesses = require('./project-lifecycle-process-witness');
|
|
4
|
+
function attachLifecycleRuntime(ctx) {
|
|
5
|
+
var epoch = crypto.randomUUID(), retired = new Map();
|
|
6
|
+
function target(slug, project) {
|
|
7
|
+
var status = project.getStatus();
|
|
8
|
+
return { slug: slug, path: status.path, projectKnowledgeId: status.projectKnowledgeId || null, epoch: epoch };
|
|
9
|
+
}
|
|
10
|
+
function key(item) { return JSON.stringify([item.slug, item.path, item.projectKnowledgeId]); }
|
|
11
|
+
function matches(item, project) { return key(item) === key(target(item.slug, project)); }
|
|
12
|
+
function targets(slug) {
|
|
13
|
+
var out = [], witness = witnesses.capture();
|
|
14
|
+
ctx.projects.forEach(function (project, candidate) {
|
|
15
|
+
if (candidate === slug || candidate.indexOf(slug + '--') === 0) out.push(Object.assign(target(candidate, project), { witness: witness }));
|
|
16
|
+
});
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function stop(item, project) {
|
|
20
|
+
var id = key(item), previous = retired.get(id);
|
|
21
|
+
if (previous && previous.project !== project) {
|
|
22
|
+
if (!previous.closed) throw new Error('An earlier runtime with this identity has not confirmed closure.');
|
|
23
|
+
previous = null;
|
|
24
|
+
}
|
|
25
|
+
if (previous && previous.running) return previous.running;
|
|
26
|
+
if (previous && previous.closed) return Promise.resolve(true);
|
|
27
|
+
var state = previous || { project: project, target: item, closed: false };
|
|
28
|
+
retired.set(id, state);
|
|
29
|
+
state.running = Promise.resolve().then(function () { return state.project.destroy(); }).then(function (result) {
|
|
30
|
+
if (result !== true) throw new Error('Project runtime closure was not acknowledged: ' + item.slug);
|
|
31
|
+
state.closed = true; return true;
|
|
32
|
+
}).finally(function () { state.running = null; });
|
|
33
|
+
return state.running;
|
|
34
|
+
}
|
|
35
|
+
async function close(items, proof) {
|
|
36
|
+
for (var item of items) {
|
|
37
|
+
var live = ctx.projects.get(item.slug), previous = retired.get(key(item));
|
|
38
|
+
if (live) {
|
|
39
|
+
if (!matches(item, live)) throw new Error('A replacement runtime occupies this registration.');
|
|
40
|
+
ctx.projects.delete(item.slug);
|
|
41
|
+
await stop(item, live);
|
|
42
|
+
} else if (previous) await stop(item, previous.project);
|
|
43
|
+
else if (!proof && !witnesses.confirmsExit(item.witness)) throw new Error('Runtime closure evidence is unavailable on this host. Keep the operation pending until its original host confirms closure.');
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function shutdown() { return Promise.all(Array.from(retired.values()).filter(function (item) { return !item.closed; }).map(function (item) { return stop(item.target, item.project); })); }
|
|
48
|
+
return { targets: targets, close: close, shutdown: shutdown };
|
|
49
|
+
}
|
|
50
|
+
module.exports = { attachLifecycleRuntime: attachLifecycleRuntime };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Fail closed on unmapped identities, aliases, foreign ownership or unacknowledged ACLs.
|
|
2
|
+
var fs = require('fs');
|
|
3
|
+
var path = require('path');
|
|
4
|
+
var cp = require('child_process');
|
|
5
|
+
var osUsers = require('./os-users');
|
|
6
|
+
function verify(projectPath, user, resolveIdentity) {
|
|
7
|
+
if (!user || !user.linuxUser) throw new Error('A provisioned Linux account is required.');
|
|
8
|
+
var identity = resolveIdentity(user.linuxUser), canonical = fs.realpathSync(projectPath), stat = fs.statSync(canonical);
|
|
9
|
+
if (!Number.isInteger(identity.uid) || identity.uid < 0 || !Number.isInteger(identity.gid) || identity.gid < 0) throw new Error('Linux identity is unavailable.');
|
|
10
|
+
if (canonical !== path.resolve(projectPath) || !stat.isDirectory() || stat.uid !== identity.uid) throw new Error('Registration requires a canonical directory owned by the mapped Linux account.');
|
|
11
|
+
if (osUsers.grantProjectAccess(canonical, user.linuxUser, { force: true }) !== true) {
|
|
12
|
+
var grantError = new Error('Linux filesystem access grant was not acknowledged.'); grantError.lifecycleResult = { persisted: false, retrySafe: true, filesystemAccess: 'grant_unconfirmed' }; throw grantError;
|
|
13
|
+
}
|
|
14
|
+
var script = "var fs = require('fs'); if (process.getuid() !== Number(process.argv[2]) || process.getgid() !== Number(process.argv[3])) throw new Error('Execution identity mismatch'); fs.accessSync(process.argv[1], fs.constants.R_OK | fs.constants.W_OK | fs.constants.X_OK); fs.readdirSync(process.argv[1]);";
|
|
15
|
+
var wrapped = osUsers.wrapSpawnAsUser(process.execPath, ['-e', script, canonical, String(identity.uid), String(identity.gid)], { uid: identity.uid, gid: identity.gid, stdio: 'pipe', timeout: 10000 });
|
|
16
|
+
try { cp.execFileSync(wrapped.command, wrapped.args, wrapped.options); }
|
|
17
|
+
catch (error) { error.lifecycleResult = { persisted: false, retrySafe: true, filesystemAccess: 'verification_failed' }; throw error; }
|
|
18
|
+
var after = fs.statSync(canonical);
|
|
19
|
+
if (after.dev !== stat.dev || after.ino !== stat.ino || after.uid !== identity.uid || fs.realpathSync(projectPath) !== canonical) throw new Error('The Linux project directory changed during access verification.');
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
module.exports = { verify: verify };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
var authority = require('./canonical-clay-record-authority');
|
|
2
|
+
function bindCanonicalClay(source, createBound) {
|
|
3
|
+
if (!source || !source.projectSlug) return null;
|
|
4
|
+
var initial;
|
|
5
|
+
function current() {
|
|
6
|
+
var live = authority.resolve(source.authority, source.projectSlug);
|
|
7
|
+
if (initial && (live.project !== initial.project || live.status.path !== initial.status.path || live.status.projectKnowledgeId !== initial.status.projectKnowledgeId || live.status.changeSetId !== initial.status.changeSetId)) throw new Error('Selected Project Logs context changed.');
|
|
8
|
+
return live;
|
|
9
|
+
}
|
|
10
|
+
try { initial = current(); } catch (error) { return null; }
|
|
11
|
+
function envelope() {
|
|
12
|
+
var live = current();
|
|
13
|
+
return { principal: Object.assign({}, live.principal, { kind: 'canonicalClay', projectSlug: source.projectSlug }), status: live.status, author: live.actor };
|
|
14
|
+
}
|
|
15
|
+
var bound = createBound(envelope(), envelope);
|
|
16
|
+
['updateLog', 'reviewLogComment', 'commentLog', 'revertLog', 'linkLog', 'removeLog'].forEach(function (method) {
|
|
17
|
+
var original = bound[method];
|
|
18
|
+
bound[method] = function (args) {
|
|
19
|
+
current();
|
|
20
|
+
var entry = bound.readLog(args);
|
|
21
|
+
if (!Number.isInteger(args.expectedRevision) || args.expectedRevision !== entry.revisions) throw new Error('Log revision conflict.');
|
|
22
|
+
return original(args);
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
return bound;
|
|
26
|
+
}
|
|
27
|
+
module.exports = { bindCanonicalClay: bindCanonicalClay };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var provenance = require('./session-provenance');
|
|
2
|
+
var personal = require('./mcp-session-access');
|
|
3
|
+
// Catalog eligibility is registry-only; content always requires a live private query.
|
|
4
|
+
function queryGuard(source, projectFor, multiUser, findUser) {
|
|
5
|
+
var session = source.session, project = projectFor(source.projectSlug);
|
|
6
|
+
var generation = session && Number(session._sdkQueryGeneration || 0);
|
|
7
|
+
return function () {
|
|
8
|
+
var manager = project && (project.getSessionManager ? project.getSessionManager() : project.sm);
|
|
9
|
+
if (!session || projectFor(source.projectSlug) !== project || !manager || manager.sessions.get(session.localId) !== session || Number(session._sdkQueryGeneration || 0) !== generation) throw new Error('This Project Logs query is no longer valid.');
|
|
10
|
+
if (session.taskStopRequested || session.abortController && session.abortController.signal.aborted || session.destroying || session.hidden || session.spawn || provenance.isWorker(session) || session.homeDebatePlanning || session.debateSetupMode || session._mentionInProgress || session.mateCreationMode || session._mcpForeignInput || session.sessionVisibility === 'shared' || session.delegated || session._delegatedBy || session._pairDelegation) throw new Error('Project Logs require a live private canonical Clay query.');
|
|
11
|
+
if (multiUser()) {
|
|
12
|
+
if (!findUser(source.projectOwnerId) || session.ownerId !== source.projectOwnerId || !personal.canUsePersonalConnections(session, manager.sessions)) throw new Error('Foreign input cannot authorize Project Logs.');
|
|
13
|
+
} else if (session.ownerId || (session.history || []).some(function (entry) { return entry.type === 'user_message' && entry.from && entry.from !== 'default'; })) throw new Error('Foreign input cannot authorize Project Logs.');
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
module.exports = { queryGuard: queryGuard };
|