clay-server 5.1.1-beta.1 → 5.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/account-auth-policy.js +21 -0
- package/lib/account-auth-socket.js +30 -0
- package/lib/account-auth-throttle.js +44 -0
- package/lib/account-auth-transport.js +48 -0
- package/lib/account-login-page.js +28 -0
- package/lib/account-passkey-challenges.js +31 -0
- package/lib/account-passkey-page.js +89 -0
- package/lib/account-passkey-policy.js +39 -0
- package/lib/account-password-page.js +69 -0
- package/lib/account-passwords.js +36 -0
- package/lib/account-remembered.js +77 -0
- package/lib/account-session-page.js +51 -0
- package/lib/account-session-store.js +82 -0
- package/lib/builtin-mates.js +19 -16
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +88 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +247 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +84 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +104 -0
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +84 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +80 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/email-mcp-server.js +1 -1
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +99 -0
- package/lib/knowledge-db-service.js +24 -33
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-file-store.js +7 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +16 -34
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +18 -6
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +1 -0
- package/lib/mcp-source-context.js +1 -0
- package/lib/mcp-source-discovery.js +1 -0
- package/lib/mcp-source-plugins.js +1 -0
- package/lib/mcp-source-restrictions.js +1 -0
- package/lib/mcp-tool-policy.js +9 -0
- package/lib/migrate-single-user.js +3 -23
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/pane-pending-interactions.js +20 -0
- package/lib/pane-session-activation.js +114 -0
- package/lib/pane-session-transport.js +26 -0
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +8 -4
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-debate.js +1 -0
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +15 -85
- package/lib/project-full-access.js +23 -2
- package/lib/project-http.js +7 -4
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +76 -17
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- package/lib/project-product-control.js +89 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +67 -68
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-server-settings.js +1 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-document.js +27 -7
- package/lib/project-session-github.js +63 -19
- package/lib/project-session-history-actions.js +167 -0
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +7 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +103 -272
- package/lib/project-shared-browser.js +78 -17
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-knowledge.js +2 -1
- package/lib/project-user-message.js +67 -1
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +19 -2
- package/lib/project.js +178 -45
- package/lib/public/app.js +46 -0
- package/lib/public/appearance-bootstrap.js +16 -0
- package/lib/public/auth/account-login.css +98 -0
- package/lib/public/auth/account-login.js +104 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/command-palette.css +12 -33
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/input.css +6 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +46 -1
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/css/sticky-notes.css +87 -1
- package/lib/public/css/user-settings-sessions.css +42 -0
- package/lib/public/index.html +28 -10
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/account-auth-settings.js +71 -0
- package/lib/public/modules/account-transport-settings.js +51 -0
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +11 -1
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +7 -4
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +22 -1
- package/lib/public/modules/app-messages.js +119 -17
- package/lib/public/modules/app-misc.js +3 -1
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +13 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/app-rendering.js +4 -3
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-log-links.js +23 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +33 -0
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +146 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +342 -0
- package/lib/public/modules/command-palette.js +162 -40
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +169 -0
- package/lib/public/modules/contextual-clay.js +250 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +117 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/elicitation-form.js +5 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/history-replay-batch.js +13 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +88 -10
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +6 -34
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/message-delivery.js +20 -2
- package/lib/public/modules/model-picker.js +2 -0
- package/lib/public/modules/pane-activation.js +214 -0
- package/lib/public/modules/pane-binding.js +82 -0
- package/lib/public/modules/pane-bridge.js +7 -4
- package/lib/public/modules/pane-browser-bridge.js +22 -0
- package/lib/public/modules/pane-file-bridge.js +4 -0
- package/lib/public/modules/pane-issue-bridge.js +4 -1
- package/lib/public/modules/passkey-browser.js +69 -0
- package/lib/public/modules/passkey-server-settings.js +57 -0
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/profile.js +1 -238
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +6 -2
- package/lib/public/modules/project-mate-navigation.js +144 -22
- package/lib/public/modules/rewind.js +5 -0
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -440
- package/lib/public/modules/server-settings-controls.js +10 -0
- package/lib/public/modules/server-settings.js +7 -2
- package/lib/public/modules/session-actions.js +5 -2
- package/lib/public/modules/session-context-menu.js +5 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +85 -14
- package/lib/public/modules/session-rich-drafts.js +21 -0
- package/lib/public/modules/session-switch-defer.js +67 -0
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +29 -6
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-group-restore.js +68 -0
- package/lib/public/modules/split-pair-ui.js +1 -0
- package/lib/public/modules/split-pane-reconciler.js +15 -0
- package/lib/public/modules/split-pane-renderer.js +87 -6
- package/lib/public/modules/split-session-boundary.js +1 -1
- package/lib/public/modules/split-view.js +60 -68
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +116 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/stt-controller.js +4 -1
- package/lib/public/modules/stt-recording.js +1 -1
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/theme.js +8 -1
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +60 -11
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/user-settings-account.js +14 -1
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings-sessions.js +120 -0
- package/lib/public/modules/user-settings.js +6 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/modules/you-activity.js +3 -1
- package/lib/public/modules/you.js +91 -6
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +6 -0
- package/lib/public/sw.js +3 -1
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +342 -401
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +52 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-account-passkeys.js +186 -0
- package/lib/server-account-passwords.js +127 -0
- package/lib/server-account-security.js +53 -0
- package/lib/server-admin.js +63 -16
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-auth.js +107 -79
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-dm.js +2 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +18 -4
- package/lib/server-home-chat.js +22 -20
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +29 -9
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +25 -62
- package/lib/server-owner-ui.js +118 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server-settings.js +8 -2
- package/lib/server.js +165 -43
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +5 -2
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +64 -99
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/terminal-manager.js +65 -6
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/user-knowledge-service.js +23 -1
- package/lib/users-auth.js +38 -3
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users-create.js +97 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users-server-pin.js +37 -0
- package/lib/users.js +18 -84
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/workspace-query-service.js +13 -2
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +18 -2
- package/lib/yoke/acp-agent-profiles.js +7 -5
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +54 -50
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +53 -111
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +34 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +11 -4
package/lib/daemon.js
CHANGED
|
@@ -20,7 +20,7 @@ process.setMaxListeners(50);
|
|
|
20
20
|
|
|
21
21
|
// Remove CLAUDECODE env var so the SDK can spawn Claude Code child processes
|
|
22
22
|
// (prevents "cannot be launched inside another Claude Code session" error)
|
|
23
|
-
|
|
23
|
+
require("./yoke/host-environment").prepare(process.env);
|
|
24
24
|
|
|
25
25
|
var fs = require("fs");
|
|
26
26
|
var path = require("path");
|
|
@@ -67,6 +67,8 @@ try {
|
|
|
67
67
|
console.error("[daemon] ensureMultiUser threw (continuing):", e && e.message);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
config = require("./project-ownership").repair(config, usersModule.getAllUsers(), saveConfig, osUsersMod.resolveOsUserInfo);
|
|
71
|
+
|
|
70
72
|
if (projectLogContext.initializeProjectKnowledge(config)) {
|
|
71
73
|
saveConfig(config);
|
|
72
74
|
console.log("[daemon] Assigned stable knowledge identities to existing projects");
|
|
@@ -187,7 +189,14 @@ function registerProjectWorktrees(project, requestUser) {
|
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
191
|
|
|
192
|
+
var projectLifecycle = require("./daemon-project-lifecycle").attachProjectLifecycle({
|
|
193
|
+
getConfig: function () { return config; }, setConfig: function (next) { config = next; }, saveConfig: saveConfig,
|
|
194
|
+
getRelay: function () { return relay; }, resolveIdentity: osUsersMod.resolveOsUserInfo,
|
|
195
|
+
findUser: usersModule.findUserById, registerWorktrees: registerProjectWorktrees,
|
|
196
|
+
});
|
|
197
|
+
var lifecycleCallbacks = require("./daemon-project-lifecycle-callbacks").attachLifecycleCallbacks({ service: projectLifecycle });
|
|
190
198
|
var relay = createServer({
|
|
199
|
+
projectLifecycle: projectLifecycle,
|
|
191
200
|
tlsOptions: tlsOptions,
|
|
192
201
|
caPath: caRoot,
|
|
193
202
|
builtinCert: config.builtinCert || false,
|
|
@@ -198,122 +207,11 @@ var relay = createServer({
|
|
|
198
207
|
osUsers: config.osUsers || false,
|
|
199
208
|
lanHost: lanIp ? lanIp + ":" + config.port : null,
|
|
200
209
|
getRemovedProjects: function (userId) { return getFilteredRemovedProjects(config, userId); },
|
|
201
|
-
onAddProject:
|
|
202
|
-
|
|
203
|
-
for (var j = 0; j < config.projects.length; j++) {
|
|
204
|
-
if (config.projects[j].path === absPath) {
|
|
205
|
-
return { ok: true, slug: config.projects[j].slug, existing: true };
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
var slugs = config.projects.map(function (p) { return p.slug; });
|
|
209
|
-
var slug = generateSlug(absPath, slugs);
|
|
210
|
-
var projectEntry = { path: absPath, slug: slug, addedAt: Date.now(), visibility: "private" };
|
|
211
|
-
projectEntry.projectKnowledgeId = rememberedProjectKnowledgeId(absPath);
|
|
212
|
-
// The user who adds a project always becomes the owner
|
|
213
|
-
if (wsUser && wsUser.id) {
|
|
214
|
-
projectEntry.ownerId = wsUser.id;
|
|
215
|
-
}
|
|
216
|
-
config.projects.push(projectEntry);
|
|
217
|
-
relay.addProject(absPath, slug, null, null, wsUser ? wsUser.id : null, null, projectRuntimeOptions(projectEntry));
|
|
218
|
-
// Remove from removedProjects if present
|
|
219
|
-
if (config.removedProjects) {
|
|
220
|
-
config.removedProjects = config.removedProjects.filter(function (rp) { return rp.path !== absPath; });
|
|
221
|
-
}
|
|
222
|
-
saveConfig(config);
|
|
223
|
-
try { syncClayrc(config.projects); } catch (e) {}
|
|
224
|
-
console.log("[daemon] Added project (web):", slug, "→", absPath);
|
|
225
|
-
// OS users mode: grant ACL to project owner
|
|
226
|
-
if (config.osUsers) {
|
|
227
|
-
var newProj = config.projects[config.projects.length - 1];
|
|
228
|
-
if (newProj.ownerId) {
|
|
229
|
-
var ownerUser = usersModule.findUserById(newProj.ownerId);
|
|
230
|
-
if (ownerUser && ownerUser.linuxUser) {
|
|
231
|
-
grantProjectAccess(absPath, ownerUser.linuxUser);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// Discover and register worktrees for the new project
|
|
236
|
-
registerProjectWorktrees(projectEntry, wsUser);
|
|
237
|
-
// Broadcast updated project list to all clients
|
|
238
|
-
relay.broadcastAll({
|
|
239
|
-
type: "projects_updated",
|
|
240
|
-
projects: relay.getProjects(),
|
|
241
|
-
projectCount: config.projects.length,
|
|
242
|
-
});
|
|
243
|
-
return { ok: true, slug: slug };
|
|
244
|
-
},
|
|
245
|
-
onCreateProject: function (projectName, wsUser) {
|
|
246
|
-
console.log("[daemon] onCreateProject wsUser:", JSON.stringify(wsUser ? { id: wsUser.id, role: wsUser.role, username: wsUser.username, linuxUser: wsUser.linuxUser } : null));
|
|
247
|
-
var os = require("os");
|
|
248
|
-
var execFileSync = require("child_process").execFileSync;
|
|
249
|
-
var baseDir;
|
|
250
|
-
if (config.osUsers) {
|
|
251
|
-
baseDir = "/var/clay/projects";
|
|
252
|
-
} else {
|
|
253
|
-
baseDir = config.projectsDir || path.join(REAL_HOME, "clay-projects");
|
|
254
|
-
}
|
|
255
|
-
try { fs.mkdirSync(baseDir, { recursive: true }); } catch (e) {}
|
|
256
|
-
// Generate slug and deduplicate
|
|
257
|
-
var slugs = config.projects.map(function (p) { return p.slug; });
|
|
258
|
-
var slug = generateSlug(path.join(baseDir, projectName), slugs);
|
|
259
|
-
var targetDir = path.join(baseDir, slug);
|
|
260
|
-
try {
|
|
261
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
262
|
-
// Run git init
|
|
263
|
-
if (config.osUsers && wsUser) {
|
|
264
|
-
var linuxUser = wsUser.linuxUser;
|
|
265
|
-
if (linuxUser) {
|
|
266
|
-
var uidGid = null;
|
|
267
|
-
try {
|
|
268
|
-
uidGid = {
|
|
269
|
-
uid: parseInt(execFileSync("id", ["-u", linuxUser], { encoding: "utf8", stdio: "pipe" }).trim(), 10),
|
|
270
|
-
gid: parseInt(execFileSync("id", ["-g", linuxUser], { encoding: "utf8", stdio: "pipe" }).trim(), 10),
|
|
271
|
-
};
|
|
272
|
-
} catch (e) {}
|
|
273
|
-
if (uidGid) {
|
|
274
|
-
fs.chmodSync(targetDir, 0o700);
|
|
275
|
-
execFileSync("chown", ["-R", linuxUser + ":" + linuxUser, targetDir]);
|
|
276
|
-
var gitInit = osUsersMod.wrapSpawnAsUser("git", ["init"], { cwd: targetDir, uid: uidGid.uid, gid: uidGid.gid, env: { PATH: "/usr/local/bin:/usr/bin:/bin" } });
|
|
277
|
-
execFileSync(gitInit.command, gitInit.args, gitInit.options);
|
|
278
|
-
} else {
|
|
279
|
-
execFileSync("git", ["init"], { cwd: targetDir });
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
execFileSync("git", ["init"], { cwd: targetDir });
|
|
283
|
-
}
|
|
284
|
-
} else {
|
|
285
|
-
execFileSync("git", ["init"], { cwd: targetDir });
|
|
286
|
-
}
|
|
287
|
-
} catch (e) {
|
|
288
|
-
try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (ce) {}
|
|
289
|
-
return { ok: false, error: "Failed to create project: " + e.message };
|
|
290
|
-
}
|
|
291
|
-
// Register project - creator always becomes owner, default private
|
|
292
|
-
var projectEntry = { path: targetDir, slug: slug, addedAt: Date.now(), visibility: "private" };
|
|
293
|
-
projectEntry.projectKnowledgeId = projectLogContext.createProjectKnowledgeId();
|
|
294
|
-
if (wsUser && wsUser.id) {
|
|
295
|
-
projectEntry.ownerId = wsUser.id;
|
|
296
|
-
}
|
|
297
|
-
relay.addProject(targetDir, slug, null, null, wsUser ? wsUser.id : null, null, projectRuntimeOptions(projectEntry));
|
|
298
|
-
config.projects.push(projectEntry);
|
|
299
|
-
saveConfig(config);
|
|
300
|
-
try { syncClayrc(config.projects); } catch (e) {}
|
|
301
|
-
console.log("[daemon] Created project:", slug, "→", targetDir, "entry:", JSON.stringify({ ownerId: projectEntry.ownerId, visibility: projectEntry.visibility }));
|
|
302
|
-
// OS users mode: grant ACL
|
|
303
|
-
if (config.osUsers && wsUser && wsUser.linuxUser) {
|
|
304
|
-
console.log("[daemon] Granting ACL:", targetDir, "→", wsUser.linuxUser);
|
|
305
|
-
grantProjectAccess(targetDir, wsUser.linuxUser);
|
|
306
|
-
} else if (config.osUsers) {
|
|
307
|
-
console.log("[daemon] Skipping ACL grant: osUsers=true but linuxUser=", wsUser && wsUser.linuxUser);
|
|
308
|
-
}
|
|
309
|
-
relay.broadcastAll({
|
|
310
|
-
type: "projects_updated",
|
|
311
|
-
projects: relay.getProjects(),
|
|
312
|
-
projectCount: config.projects.length,
|
|
313
|
-
});
|
|
314
|
-
return { ok: true, slug: slug };
|
|
315
|
-
},
|
|
210
|
+
onAddProject: lifecycleCallbacks.onAddProject,
|
|
211
|
+
onCreateProject: lifecycleCallbacks.onCreateProject,
|
|
316
212
|
onCloneProject: function (cloneUrl, wsUser, callback) {
|
|
213
|
+
wsUser = wsUser && usersModule.findUserById(wsUser.id);
|
|
214
|
+
if (usersModule.isMultiUser() && !wsUser) { callback({ ok: false, error: "A valid account is required." }); return; }
|
|
317
215
|
var os = require("os");
|
|
318
216
|
var spawn = require("child_process").spawn;
|
|
319
217
|
var execFileSync = require("child_process").execFileSync;
|
|
@@ -323,12 +221,15 @@ var relay = createServer({
|
|
|
323
221
|
} else {
|
|
324
222
|
baseDir = config.projectsDir || path.join(REAL_HOME, "clay-projects");
|
|
325
223
|
}
|
|
326
|
-
try { fs.mkdirSync(baseDir, { recursive: true }); } catch (e) {}
|
|
327
224
|
// Derive slug from repo URL
|
|
328
225
|
var repoName = cloneUrl.replace(/\.git$/, "").split("/").pop() || "project";
|
|
329
226
|
var slugs = config.projects.map(function (p) { return p.slug; });
|
|
330
227
|
var slug = generateSlug(path.join(baseDir, repoName), slugs);
|
|
331
|
-
var targetDir
|
|
228
|
+
var targetDir;
|
|
229
|
+
var directoryClaim;
|
|
230
|
+
var settled = false;
|
|
231
|
+
function finish(result) { if (settled) return; settled = true; callback(result); }
|
|
232
|
+
function cleanup() { if (directoryClaim) { try { directoryClaim.cleanup(); } catch (error) {} } }
|
|
332
233
|
// Clone as user to use their git credentials
|
|
333
234
|
var spawnOpts = { cwd: baseDir };
|
|
334
235
|
if (config.osUsers && wsUser && wsUser.linuxUser) {
|
|
@@ -341,22 +242,23 @@ var relay = createServer({
|
|
|
341
242
|
});
|
|
342
243
|
} catch (e) {}
|
|
343
244
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
245
|
+
try {
|
|
246
|
+
directoryClaim = require("./project-directory-claim").claim({ baseDir: baseDir, slug: slug, projects: config.projects, userId: wsUser && wsUser.id });
|
|
247
|
+
targetDir = directoryClaim.path;
|
|
248
|
+
directoryClaim.assertOwned();
|
|
249
|
+
if (config.osUsers && wsUser && wsUser.linuxUser && spawnOpts.uid != null) fs.chownSync(targetDir, spawnOpts.uid, spawnOpts.gid);
|
|
250
|
+
directoryClaim.assertOwned();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
cleanup();
|
|
253
|
+
finish({ ok: false, error: "Failed to prepare project directory: " + error.message });
|
|
347
254
|
return;
|
|
348
255
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
var cloneSpawn = osUsersMod.wrapSpawnAsUser("git", ["clone", cloneUrl, targetDir], spawnOpts);
|
|
359
|
-
var proc = spawn(cloneSpawn.command, cloneSpawn.args, cloneSpawn.options);
|
|
256
|
+
var proc;
|
|
257
|
+
try {
|
|
258
|
+
directoryClaim.assertOwned();
|
|
259
|
+
var cloneSpawn = osUsersMod.wrapSpawnAsUser("git", ["clone", cloneUrl, targetDir], spawnOpts);
|
|
260
|
+
proc = spawn(cloneSpawn.command, cloneSpawn.args, cloneSpawn.options);
|
|
261
|
+
} catch (error) { cleanup(); finish({ ok: false, error: "Failed to start git clone: " + error.message }); return; }
|
|
360
262
|
var stderrBuf = "";
|
|
361
263
|
proc.stderr.on("data", function (chunk) { stderrBuf += chunk.toString(); });
|
|
362
264
|
// 5 minute timeout
|
|
@@ -365,19 +267,23 @@ var relay = createServer({
|
|
|
365
267
|
}, 5 * 60 * 1000);
|
|
366
268
|
proc.on("close", function (code) {
|
|
367
269
|
clearTimeout(cloneTimeout);
|
|
270
|
+
if (settled) return;
|
|
271
|
+
try { directoryClaim.assertOwned(); } catch (error) { finish({ ok: false, error: error.message }); return; }
|
|
368
272
|
if (code !== 0) {
|
|
369
|
-
|
|
273
|
+
cleanup();
|
|
370
274
|
var errMsg = stderrBuf.trim().split("\n").pop() || "Clone failed (exit code " + code + ")";
|
|
371
|
-
|
|
275
|
+
finish({ ok: false, error: errMsg });
|
|
372
276
|
return;
|
|
373
277
|
}
|
|
374
278
|
// chown and restrict permissions if osUsers
|
|
375
279
|
if (config.osUsers && wsUser && wsUser.linuxUser) {
|
|
376
280
|
try {
|
|
281
|
+
directoryClaim.assertOwned();
|
|
377
282
|
fs.chmodSync(targetDir, 0o700);
|
|
378
283
|
execFileSync("chown", ["-R", wsUser.linuxUser + ":" + wsUser.linuxUser, targetDir]);
|
|
379
284
|
} catch (e) {}
|
|
380
285
|
}
|
|
286
|
+
if (usersModule.isMultiUser() && (!wsUser || !usersModule.findUserById(wsUser.id))) { finish({ ok: false, error: "The creating account is no longer available." }); return; }
|
|
381
287
|
// Register project - creator always becomes owner
|
|
382
288
|
// Creator always becomes owner, default private
|
|
383
289
|
var projectEntry = { path: targetDir, slug: slug, addedAt: Date.now(), visibility: "private" };
|
|
@@ -385,9 +291,11 @@ var relay = createServer({
|
|
|
385
291
|
if (wsUser && wsUser.id) {
|
|
386
292
|
projectEntry.ownerId = wsUser.id;
|
|
387
293
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
294
|
+
try {
|
|
295
|
+
directoryClaim.assertOwned();
|
|
296
|
+
config = require("./project-ownership").register(config, projectEntry, usersModule.getAllUsers(), saveConfig, osUsersMod.resolveOsUserInfo);
|
|
297
|
+
} catch (error) { finish({ ok: false, error: "Failed to register project: " + error.message }); return; }
|
|
298
|
+
relay.addProject(targetDir, slug, null, null, projectEntry.ownerId || null, null, projectRuntimeOptions(projectEntry));
|
|
391
299
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
392
300
|
console.log("[daemon] Cloned project:", slug, "→", targetDir);
|
|
393
301
|
if (config.osUsers && wsUser && wsUser.linuxUser) {
|
|
@@ -398,12 +306,13 @@ var relay = createServer({
|
|
|
398
306
|
projects: relay.getProjects(),
|
|
399
307
|
projectCount: config.projects.length,
|
|
400
308
|
});
|
|
401
|
-
|
|
309
|
+
finish({ ok: true, slug: slug });
|
|
402
310
|
});
|
|
403
311
|
proc.on("error", function (err) {
|
|
404
312
|
clearTimeout(cloneTimeout);
|
|
405
|
-
|
|
406
|
-
|
|
313
|
+
if (settled) return;
|
|
314
|
+
cleanup();
|
|
315
|
+
finish({ ok: false, error: "Failed to start git clone: " + err.message });
|
|
407
316
|
});
|
|
408
317
|
},
|
|
409
318
|
onRemoveProject: function (slug, userId) {
|
|
@@ -444,40 +353,7 @@ var relay = createServer({
|
|
|
444
353
|
});
|
|
445
354
|
return { ok: true };
|
|
446
355
|
}
|
|
447
|
-
|
|
448
|
-
for (var j = 0; j < config.projects.length; j++) {
|
|
449
|
-
if (config.projects[j].slug === slug) { found = config.projects[j]; break; }
|
|
450
|
-
}
|
|
451
|
-
if (!found) return { ok: false, error: "Project not found" };
|
|
452
|
-
// Cascade remove worktrees belonging to this parent
|
|
453
|
-
cleanupWorktreesForParent(relay, slug);
|
|
454
|
-
// Save to removedProjects for re-add functionality
|
|
455
|
-
if (!config.removedProjects) config.removedProjects = [];
|
|
456
|
-
config.removedProjects.push({
|
|
457
|
-
path: found.path,
|
|
458
|
-
title: found.title || null,
|
|
459
|
-
icon: found.icon || null,
|
|
460
|
-
userId: userId || null,
|
|
461
|
-
projectKnowledgeId: found.projectKnowledgeId || null,
|
|
462
|
-
removedAt: Date.now(),
|
|
463
|
-
});
|
|
464
|
-
// Cap at 20 entries (oldest first)
|
|
465
|
-
if (config.removedProjects.length > 20) {
|
|
466
|
-
config.removedProjects = config.removedProjects.slice(config.removedProjects.length - 20);
|
|
467
|
-
}
|
|
468
|
-
relay.removeProject(slug);
|
|
469
|
-
config.projects = config.projects.filter(function (p) { return p.slug !== slug; });
|
|
470
|
-
saveConfig(config);
|
|
471
|
-
// Remove from .clayrc so it doesn't appear in restore prompt
|
|
472
|
-
if (found.path) { try { removeFromClayrc(found.path); } catch (e) {} }
|
|
473
|
-
try { syncClayrc(config.projects); } catch (e) {}
|
|
474
|
-
console.log("[daemon] Removed project (web):", slug);
|
|
475
|
-
relay.broadcastAll({
|
|
476
|
-
type: "projects_updated",
|
|
477
|
-
projects: relay.getProjects(),
|
|
478
|
-
projectCount: config.projects.length,
|
|
479
|
-
});
|
|
480
|
-
return { ok: true };
|
|
356
|
+
return lifecycleCallbacks.onRemoveProject(slug, userId);
|
|
481
357
|
},
|
|
482
358
|
onReorderProjects: function (slugs) {
|
|
483
359
|
// Build a slug->project map from current projects
|
|
@@ -511,25 +387,8 @@ var relay = createServer({
|
|
|
511
387
|
return { ok: true };
|
|
512
388
|
},
|
|
513
389
|
onSetProjectTitle: function (slug, newTitle) {
|
|
514
|
-
relay.setProjectTitle(slug, newTitle);
|
|
515
|
-
|
|
516
|
-
if (config.projects[ti].slug === slug) {
|
|
517
|
-
if (newTitle) {
|
|
518
|
-
config.projects[ti].title = newTitle;
|
|
519
|
-
} else {
|
|
520
|
-
delete config.projects[ti].title;
|
|
521
|
-
}
|
|
522
|
-
break;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
saveConfig(config);
|
|
526
|
-
try { syncClayrc(config.projects); } catch (e) {}
|
|
527
|
-
relay.broadcastAll({
|
|
528
|
-
type: "projects_updated",
|
|
529
|
-
projects: relay.getProjects(),
|
|
530
|
-
projectCount: config.projects.length,
|
|
531
|
-
});
|
|
532
|
-
return { ok: true };
|
|
390
|
+
if (isWorktreeSlug(slug)) { relay.setProjectTitle(slug, newTitle); return { ok: true }; }
|
|
391
|
+
return lifecycleCallbacks.onSetProjectTitle(slug, newTitle);
|
|
533
392
|
},
|
|
534
393
|
onSetProjectIcon: function (slug, newIcon) {
|
|
535
394
|
relay.setProjectIcon(slug, newIcon);
|
|
@@ -553,30 +412,28 @@ var relay = createServer({
|
|
|
553
412
|
return { ok: true };
|
|
554
413
|
},
|
|
555
414
|
onSetProjectSessionVisibilityDefault: function (slug, visibility) {
|
|
556
|
-
|
|
557
|
-
if (result.error) return { ok: false, error: result.error };
|
|
558
|
-
saveConfig(config);
|
|
559
|
-
relay.broadcastAll({ type: "projects_updated", projects: relay.getProjects(), projectCount: config.projects.length });
|
|
560
|
-
return result;
|
|
415
|
+
return projectLifecycle.update(slug, "session_visibility", visibility);
|
|
561
416
|
},
|
|
562
417
|
onProjectOwnerChanged: function (slug, ownerId) {
|
|
418
|
+
if (!config.osUsers) return projectLifecycle.update(slug, "owner", ownerId);
|
|
563
419
|
if (slug.indexOf("mate-") === 0) return {error:"Mate ownership cannot be transferred."};
|
|
420
|
+
if (!ownerId || !usersModule.findUserById(ownerId)) return { error: "A valid owner is required." };
|
|
564
421
|
console.log("[daemon] onProjectOwnerChanged:", slug, "→", ownerId);
|
|
422
|
+
var previousConfig = JSON.parse(JSON.stringify(config));
|
|
565
423
|
var oldOwnerId = null;
|
|
566
424
|
var projectIdx = -1;
|
|
567
425
|
for (var oi = 0; oi < config.projects.length; oi++) {
|
|
568
426
|
if (config.projects[oi].slug === slug) {
|
|
569
427
|
oldOwnerId = config.projects[oi].ownerId || null;
|
|
570
428
|
projectIdx = oi;
|
|
571
|
-
|
|
572
|
-
config.projects[oi].ownerId = ownerId;
|
|
573
|
-
} else {
|
|
574
|
-
delete config.projects[oi].ownerId;
|
|
575
|
-
}
|
|
429
|
+
config.projects[oi].ownerId = ownerId;
|
|
576
430
|
break;
|
|
577
431
|
}
|
|
578
432
|
}
|
|
579
|
-
saveConfig(config);
|
|
433
|
+
try { saveConfig(config); } catch (error) { config = previousConfig; throw error; }
|
|
434
|
+
relay.forEachProject(function (project, projectSlug) {
|
|
435
|
+
if (projectSlug === slug || daemonProjectAccess.parentSlugFor(projectSlug) === slug) project.setProjectOwner(ownerId);
|
|
436
|
+
});
|
|
580
437
|
// OS users mode: revoke old owner ACL, grant new owner ACL
|
|
581
438
|
if (config.osUsers && projectIdx >= 0) {
|
|
582
439
|
var projectPath = config.projects[projectIdx].path;
|
|
@@ -615,18 +472,7 @@ var relay = createServer({
|
|
|
615
472
|
return { envrc: "" };
|
|
616
473
|
},
|
|
617
474
|
onSetProjectEnv: function (slug, envrc) {
|
|
618
|
-
|
|
619
|
-
if (config.projects[ei].slug === slug) {
|
|
620
|
-
if (envrc) {
|
|
621
|
-
config.projects[ei].envrc = envrc;
|
|
622
|
-
} else {
|
|
623
|
-
delete config.projects[ei].envrc;
|
|
624
|
-
}
|
|
625
|
-
saveConfig(config);
|
|
626
|
-
return { ok: true };
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return { ok: false, error: "Project not found" };
|
|
475
|
+
return lifecycleCallbacks.onSetProjectEnv(slug, envrc);
|
|
630
476
|
},
|
|
631
477
|
onGetSharedEnv: function () {
|
|
632
478
|
return { envrc: config.sharedEnv || "" };
|
|
@@ -826,6 +672,7 @@ var relay = createServer({
|
|
|
826
672
|
return { ok: true, updateChannel: config.updateChannel };
|
|
827
673
|
},
|
|
828
674
|
onSetPin: function (pin) {
|
|
675
|
+
if (usersModule.isMultiUser()) return { ok: false, error: 'Server PIN is unavailable while account authentication is enabled.' };
|
|
829
676
|
saveDaemonSetting(config, "pinHash", pin ? generateAuthToken(pin) : null, saveConfig);
|
|
830
677
|
relay.setAuthToken(config.pinHash);
|
|
831
678
|
console.log("[daemon] PIN", pin ? "set" : "removed", "(web)");
|
|
@@ -926,6 +773,7 @@ var relay = createServer({
|
|
|
926
773
|
spawnAndRestart();
|
|
927
774
|
},
|
|
928
775
|
onSetProjectVisibility: function (slug, visibility) {
|
|
776
|
+
if (!config.osUsers) return projectLifecycle.update(slug, "visibility", visibility);
|
|
929
777
|
if (slug.indexOf("mate-") === 0) return {error:"Mates cannot be shared."};
|
|
930
778
|
for (var i = 0; i < config.projects.length; i++) {
|
|
931
779
|
if (config.projects[i].slug === slug) {
|
|
@@ -961,6 +809,7 @@ var relay = createServer({
|
|
|
961
809
|
return { error: "Project not found" };
|
|
962
810
|
},
|
|
963
811
|
onSetProjectAllowedUsers: function (slug, allowedUsers) {
|
|
812
|
+
if (!config.osUsers && !daemonProjectAccess.parentSlugFor(slug)) return projectLifecycle.update(slug, "allowed_users", allowedUsers);
|
|
964
813
|
var accessBefore = daemonProjectAccess.getProjectAccess(config, slug, config.osUsers);
|
|
965
814
|
var result = daemonProjectAccess.setAllowedUsers(config, slug, allowedUsers);
|
|
966
815
|
if (result.error) return result;
|
|
@@ -1061,8 +910,9 @@ var relay = createServer({
|
|
|
1061
910
|
// process may implement the optional shutdownIfIdle(ms); adapters without one
|
|
1062
911
|
// (Claude, which runs in-process or under sdk-worker) are simply skipped.
|
|
1063
912
|
// The CLAY_CODEX_* names are kept as aliases for backwards compatibility.
|
|
1064
|
-
var
|
|
1065
|
-
var
|
|
913
|
+
var adapterReaperConfig = require("./yoke/host-environment").reaper(process.env);
|
|
914
|
+
var IDLE_MS = adapterReaperConfig.idleMs;
|
|
915
|
+
var REAP_MS = adapterReaperConfig.reapMs;
|
|
1066
916
|
var reaperHandle = setInterval(function () {
|
|
1067
917
|
if (!relay || typeof relay.forEachProject !== "function") return;
|
|
1068
918
|
relay.forEachProject(function (ctx) {
|
|
@@ -1130,11 +980,11 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
1130
980
|
}
|
|
1131
981
|
var slugs = config.projects.map(function (p) { return p.slug; });
|
|
1132
982
|
var slug = generateSlug(absPath, slugs);
|
|
1133
|
-
var projectEntry = { path: absPath, slug: slug, addedAt: Date.now(), visibility: "private" };
|
|
983
|
+
var projectEntry = { path: absPath, slug: slug, addedAt: Date.now(), visibility: "private", ownershipRecovery: "required" };
|
|
1134
984
|
projectEntry.projectKnowledgeId = rememberedProjectKnowledgeId(absPath);
|
|
1135
|
-
|
|
1136
|
-
config.projects.
|
|
1137
|
-
|
|
985
|
+
config = require("./project-ownership").register(config, projectEntry, usersModule.getAllUsers(), saveConfig, osUsersMod.resolveOsUserInfo);
|
|
986
|
+
var registeredProject = config.projects[config.projects.length - 1];
|
|
987
|
+
relay.addProject(absPath, slug, null, null, registeredProject.ownerId || null, null, projectRuntimeOptions(registeredProject));
|
|
1138
988
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
1139
989
|
console.log("[daemon] Added project:", slug, "→", absPath);
|
|
1140
990
|
// Discover and register worktrees for the new project
|
|
@@ -1203,6 +1053,7 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
1203
1053
|
};
|
|
1204
1054
|
|
|
1205
1055
|
case "set_pin": {
|
|
1056
|
+
if (usersModule.isMultiUser()) return { ok: false, error: 'Server PIN is unavailable while account authentication is enabled.' };
|
|
1206
1057
|
config.pinHash = msg.pinHash || null;
|
|
1207
1058
|
relay.setAuthToken(config.pinHash);
|
|
1208
1059
|
saveConfig(config);
|
|
@@ -1365,6 +1216,10 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
1365
1216
|
var projects = config.projects || [];
|
|
1366
1217
|
for (var i = 0; i < projects.length; i++) {
|
|
1367
1218
|
var p = projects[i];
|
|
1219
|
+
if (!projectLifecycle.canRestore(p)) {
|
|
1220
|
+
console.log("[daemon] Project removal needs recovery; runtime remains stopped:", p.slug);
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1368
1223
|
if (fs.existsSync(p.path)) {
|
|
1369
1224
|
console.log("[daemon] Adding project:", p.slug, "→", p.path);
|
|
1370
1225
|
relay.addProject(p.path, p.slug, p.title, p.icon, p.ownerId, null, projectRuntimeOptions(p));
|
|
@@ -1411,7 +1266,7 @@ if (usersModule.isMultiUser()) {
|
|
|
1411
1266
|
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1412
1267
|
var mateSlug = "mate-" + m.id;
|
|
1413
1268
|
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1414
|
-
if (fs.existsSync(mateDir)) {
|
|
1269
|
+
if (!m.lifecycleDeletion && fs.existsSync(mateDir)) {
|
|
1415
1270
|
var mateDef = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1416
1271
|
var isHost = !!(mateDef && mateDef.hostAgent);
|
|
1417
1272
|
console.log("[daemon] Adding mate project:", mateSlug + (isHost ? " (host agent)" : ""));
|
|
@@ -1428,7 +1283,7 @@ if (usersModule.isMultiUser()) {
|
|
|
1428
1283
|
var mateDir = mates.getMateDir(mateCtx, m.id);
|
|
1429
1284
|
var mateSlug = "mate-" + m.id;
|
|
1430
1285
|
var mateName = (m.profile && m.profile.displayName) || m.name || "New Mate";
|
|
1431
|
-
if (fs.existsSync(mateDir)) {
|
|
1286
|
+
if (!m.lifecycleDeletion && fs.existsSync(mateDir)) {
|
|
1432
1287
|
var mateDef2 = m.builtinKey ? require("./builtin-mates").getBuiltinByKey(m.builtinKey) : null;
|
|
1433
1288
|
var isHost2 = !!(mateDef2 && mateDef2.hostAgent);
|
|
1434
1289
|
console.log("[daemon] Adding mate project:", mateSlug + (isHost2 ? " (host agent)" : ""));
|
|
@@ -1465,58 +1320,7 @@ try { syncClayrc(config.projects); } catch (e) {}
|
|
|
1465
1320
|
// Multi-user OS-isolated installs have per-user ~/.claude trees that the
|
|
1466
1321
|
// daemon process can't traverse; the inline cleanup in claude-worker.js
|
|
1467
1322
|
// covers those.
|
|
1468
|
-
function cleanupSdkWarmupSessions() {
|
|
1469
|
-
var sessionHygiene = require("./session-hygiene");
|
|
1470
|
-
var projectsRoot = path.join(REAL_HOME, ".claude", "projects");
|
|
1471
|
-
var projectDirs;
|
|
1472
|
-
try { projectDirs = fs.readdirSync(projectsRoot, { withFileTypes: true }); }
|
|
1473
|
-
catch (e) { return; }
|
|
1474
|
-
|
|
1475
|
-
var deleted = 0;
|
|
1476
|
-
var nowMs = Date.now();
|
|
1477
|
-
var MAX_BYTES = 64 * 1024;
|
|
1478
|
-
var MIN_AGE_MS = 60 * 1000;
|
|
1479
|
-
|
|
1480
|
-
for (var di = 0; di < projectDirs.length; di++) {
|
|
1481
|
-
if (!projectDirs[di].isDirectory()) continue;
|
|
1482
|
-
var projDir = path.join(projectsRoot, projectDirs[di].name);
|
|
1483
|
-
var files;
|
|
1484
|
-
try { files = fs.readdirSync(projDir); } catch (e) { continue; }
|
|
1485
|
-
|
|
1486
|
-
for (var fi = 0; fi < files.length; fi++) {
|
|
1487
|
-
var f = files[fi];
|
|
1488
|
-
if (!f.endsWith(".jsonl")) continue;
|
|
1489
|
-
var fp = path.join(projDir, f);
|
|
1490
|
-
|
|
1491
|
-
var st;
|
|
1492
|
-
try { st = fs.statSync(fp); } catch (e) { continue; }
|
|
1493
|
-
if (st.size >= MAX_BYTES) continue;
|
|
1494
|
-
if (nowMs - st.mtimeMs < MIN_AGE_MS) continue;
|
|
1495
|
-
|
|
1496
|
-
var raw;
|
|
1497
|
-
try { raw = fs.readFileSync(fp, "utf8"); } catch (e) { continue; }
|
|
1498
|
-
var lines = raw.split("\n");
|
|
1499
|
-
var events = [];
|
|
1500
|
-
var parseOk = true;
|
|
1501
|
-
for (var li = 0; li < lines.length; li++) {
|
|
1502
|
-
var line = lines[li];
|
|
1503
|
-
if (!line) continue;
|
|
1504
|
-
var ev;
|
|
1505
|
-
try { ev = JSON.parse(line); } catch (e) { parseOk = false; break; }
|
|
1506
|
-
if (!ev || typeof ev !== "object") continue;
|
|
1507
|
-
events.push(ev);
|
|
1508
|
-
}
|
|
1509
|
-
if (!parseOk) continue;
|
|
1510
|
-
if (!sessionHygiene.isClaudeWarmupTranscript(events)) continue;
|
|
1511
|
-
|
|
1512
|
-
try { fs.unlinkSync(fp); deleted++; } catch (e) {}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
if (deleted > 0) {
|
|
1517
|
-
console.log("[daemon] Removed " + deleted + " stale SDK warmup session file(s)");
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1323
|
+
function cleanupSdkWarmupSessions() { return require("./yoke/maintenance").cleanup(REAL_HOME); }
|
|
1520
1324
|
|
|
1521
1325
|
// --- Start listening (with retry for port-in-use during update handoff) ---
|
|
1522
1326
|
var listenRetries = 0;
|
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
var modelEntryMatches = require("./model-entry").modelEntryMatches;
|
|
4
4
|
var modelEntryValue = require("./model-entry").modelEntryValue;
|
|
5
5
|
|
|
6
|
-
var
|
|
7
|
-
var VENDOR_FAMILY_ORDER = {
|
|
8
|
-
claude: ["opus", "sonnet", "haiku"],
|
|
9
|
-
codex: ["astra", "sol", "gpt", "o"],
|
|
10
|
-
};
|
|
6
|
+
var providerInfo = require("./yoke/vendor-registry").getVendorInfo;
|
|
11
7
|
|
|
12
8
|
function text(entry) {
|
|
13
9
|
if (typeof entry === "string") return entry;
|
|
@@ -18,10 +14,10 @@ function selectTopModel(vendor, catalog) {
|
|
|
18
14
|
var models = catalog && Array.isArray(catalog.models) ? catalog.models : [];
|
|
19
15
|
if (!models.length) return "";
|
|
20
16
|
var defaultModel = catalog.defaultModel;
|
|
21
|
-
if (
|
|
17
|
+
if (!providerInfo(vendor) && defaultModel) {
|
|
22
18
|
for (var di = 0; di < models.length; di++) if (modelEntryMatches(models[di], defaultModel)) return modelEntryValue(models[di]);
|
|
23
19
|
}
|
|
24
|
-
var familyOrder =
|
|
20
|
+
var familyOrder = (providerInfo(vendor) || {}).preferredModelFamilies || [];
|
|
25
21
|
if (familyOrder.length) {
|
|
26
22
|
for (var fi = 0; fi < familyOrder.length; fi++) {
|
|
27
23
|
for (var mi = 0; mi < models.length; mi++) if (new RegExp("(^|[^a-z])" + familyOrder[fi] + "([^a-z]|$)", "i").test(text(models[mi]))) return modelEntryValue(models[mi]);
|
|
@@ -114,4 +110,4 @@ function createDefaultAiRuntimeResolver(deps) {
|
|
|
114
110
|
};
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
module.exports = { resolveDefaultAiRuntime: resolveDefaultAiRuntime, createDefaultAiRuntimeResolver: createDefaultAiRuntimeResolver, selectTopModel: selectTopModel, exactModel: exactModel, resolveEffort: resolveEffort, KNOWN_VENDORS:
|
|
113
|
+
module.exports = { resolveDefaultAiRuntime: resolveDefaultAiRuntime, createDefaultAiRuntimeResolver: createDefaultAiRuntimeResolver, selectTopModel: selectTopModel, exactModel: exactModel, resolveEffort: resolveEffort, KNOWN_VENDORS: Object.keys(require("./yoke/vendor-registry").VENDOR_REGISTRY), VENDOR_FAMILY_ORDER: require("./yoke/model-preferences").families() };
|