clay-server 5.2.0-beta.1 → 5.2.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/lib/account-auth-socket.js +1 -0
- package/lib/builtin-mates.js +19 -16
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +88 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +52 -0
- package/lib/clay-product-control.js +250 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +84 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +104 -0
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-vendor-usage.js +44 -0
- package/lib/clay-workspace-controls.js +84 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +78 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/email-mcp-server.js +1 -1
- package/lib/github-star-count.js +147 -0
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +99 -0
- package/lib/knowledge-db-service.js +24 -33
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-file-store.js +7 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +16 -34
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +18 -6
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +1 -0
- package/lib/mcp-source-context.js +1 -0
- package/lib/mcp-source-discovery.js +1 -0
- package/lib/mcp-source-plugins.js +1 -0
- package/lib/mcp-source-restrictions.js +1 -0
- package/lib/mcp-tool-policy.js +9 -0
- package/lib/notes-checklist.js +92 -0
- package/lib/notes.js +51 -4
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/pane-pending-interactions.js +20 -0
- package/lib/pane-session-activation.js +114 -0
- package/lib/pane-session-transport.js +26 -0
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +8 -4
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-debate.js +1 -0
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +15 -85
- package/lib/project-full-access.js +23 -2
- package/lib/project-http.js +7 -4
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +76 -17
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- package/lib/project-product-control.js +89 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +67 -68
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-document.js +27 -7
- package/lib/project-session-github.js +63 -19
- package/lib/project-session-history-actions.js +167 -0
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +40 -11
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +160 -290
- package/lib/project-shared-browser.js +78 -17
- package/lib/project-start-protocol.js +29 -0
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +67 -1
- package/lib/project-vendor-login.js +8 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +19 -2
- package/lib/project.js +181 -45
- package/lib/public/app.js +71 -17
- package/lib/public/appearance-bootstrap.js +16 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/command-palette.css +12 -33
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/filebrowser.css +1 -1
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/input.css +6 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/mate-loading-scene.css +21 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +68 -258
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/project-mate-creation.css +5 -6
- package/lib/public/css/project-start.css +108 -0
- package/lib/public/css/projects-hub.css +53 -14
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +46 -1
- package/lib/public/css/settings-shell.css +103 -0
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/css/sidebar.css +7 -0
- package/lib/public/css/sticky-notes.css +87 -1
- package/lib/public/css/title-bar.css +79 -0
- package/lib/public/index.html +61 -68
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/admin-settings-invitations.js +2 -2
- package/lib/public/modules/admin-settings-projects.js +8 -4
- package/lib/public/modules/admin-settings-ui.js +20 -4
- package/lib/public/modules/admin-settings-users.js +4 -4
- package/lib/public/modules/app-connection.js +11 -1
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +7 -4
- package/lib/public/modules/app-home-hub.js +2 -1
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +22 -1
- package/lib/public/modules/app-messages.js +125 -19
- package/lib/public/modules/app-misc.js +3 -1
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +14 -407
- package/lib/public/modules/app-rate-limit.js +19 -28
- package/lib/public/modules/app-rendering.js +4 -3
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-log-links.js +23 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +33 -0
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +146 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +343 -0
- package/lib/public/modules/command-palette.js +162 -40
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +169 -0
- package/lib/public/modules/contextual-clay.js +252 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +117 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/elicitation-form.js +5 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/github-star.js +66 -0
- package/lib/public/modules/history-replay-batch.js +13 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +88 -10
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +6 -34
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mate-loading-scene.js +17 -0
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/message-delivery.js +20 -2
- package/lib/public/modules/model-picker.js +2 -0
- package/lib/public/modules/pane-activation.js +214 -0
- package/lib/public/modules/pane-binding.js +82 -0
- package/lib/public/modules/pane-bridge.js +7 -4
- package/lib/public/modules/pane-browser-bridge.js +22 -0
- package/lib/public/modules/pane-file-bridge.js +4 -0
- package/lib/public/modules/pane-issue-bridge.js +4 -1
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/profile.js +12 -241
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-creation-workspace.js +13 -4
- package/lib/public/modules/project-mate-navigation.js +144 -22
- package/lib/public/modules/project-session-visibility-settings.js +19 -16
- package/lib/public/modules/project-settings.js +119 -129
- package/lib/public/modules/project-start-dialog.js +130 -0
- package/lib/public/modules/project-start-folder-state.js +78 -0
- package/lib/public/modules/project-start-folder-view.js +190 -0
- package/lib/public/modules/project-start-forms.js +66 -0
- package/lib/public/modules/project-start-path.js +60 -0
- package/lib/public/modules/project-start-state.js +54 -0
- package/lib/public/modules/project-start-submit.js +67 -0
- package/lib/public/modules/projects-hub-view.js +33 -14
- package/lib/public/modules/projects-hub.js +10 -6
- package/lib/public/modules/rewind.js +5 -0
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -440
- package/lib/public/modules/server-settings.js +36 -91
- package/lib/public/modules/session-actions.js +5 -2
- package/lib/public/modules/session-context-menu.js +5 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +85 -14
- package/lib/public/modules/session-rich-drafts.js +21 -0
- package/lib/public/modules/session-switch-defer.js +67 -0
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/settings-project-target.js +56 -0
- package/lib/public/modules/settings-select.js +1 -1
- package/lib/public/modules/settings-shell-registry.js +88 -0
- package/lib/public/modules/settings-shell-view.js +104 -0
- package/lib/public/modules/settings-shell.js +342 -0
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +29 -6
- package/lib/public/modules/sidebar-mobile.js +12 -33
- package/lib/public/modules/sidebar-projects.js +3 -1
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-group-restore.js +68 -0
- package/lib/public/modules/split-pair-ui.js +1 -0
- package/lib/public/modules/split-pane-reconciler.js +15 -0
- package/lib/public/modules/split-pane-renderer.js +87 -6
- package/lib/public/modules/split-session-boundary.js +1 -1
- package/lib/public/modules/split-view.js +60 -68
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +116 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/stt-controller.js +4 -1
- package/lib/public/modules/stt-recording.js +1 -1
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/theme.js +18 -3
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +60 -11
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +14 -30
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +13 -3
- package/lib/public/modules/user-settings.js +18 -84
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/vendor-usage-format.js +93 -0
- package/lib/public/modules/vendor-usage-list.js +45 -0
- package/lib/public/modules/vendor-usage-model.js +215 -0
- package/lib/public/modules/vendor-usage-overview.js +312 -0
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +14 -6
- package/lib/public/sw.js +3 -1
- package/lib/public/welcome-art/conversation-desk.png +0 -0
- package/lib/public/welcome-art/mate-clay.png +0 -0
- package/lib/public/welcome-art/project-clay.png +0 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +348 -402
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +61 -18
- package/lib/sdk-quota-snapshot.js +21 -3
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-admin.js +63 -16
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-dm.js +2 -1
- package/lib/server-github-stars.js +39 -0
- package/lib/server-global-ws.js +26 -32
- package/lib/server-home-chat-events.js +18 -4
- package/lib/server-home-chat.js +22 -20
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +29 -9
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +25 -62
- package/lib/server-owner-ui.js +118 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server-settings.js +6 -1
- package/lib/server.js +167 -43
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- package/lib/session-hygiene.js +3 -35
- package/lib/session-notes-mcp-server.js +26 -4
- package/lib/session-notes-writes.js +88 -0
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +25 -10
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +66 -99
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/terminal-manager.js +65 -6
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +7 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/vendor-usage-acp-readers.js +97 -0
- package/lib/vendor-usage-capabilities.js +88 -0
- package/lib/vendor-usage-http-readers.js +214 -0
- package/lib/vendor-usage-metrics.js +47 -0
- package/lib/vendor-usage-native-readers.js +106 -0
- package/lib/vendor-usage-overview.js +406 -0
- package/lib/vendor-usage-service.js +281 -0
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/workspace-query-service.js +13 -2
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +22 -4
- package/lib/yoke/acp-agent-profiles.js +7 -5
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +66 -50
- package/lib/yoke/adapters/codex-query-contract.js +25 -0
- package/lib/yoke/adapters/codex.js +98 -123
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +272 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/codex-dynamic-tool-names.js +80 -0
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-model-catalog.js +37 -3
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +92 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +34 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +34 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +87 -3
- package/package.json +11 -4
- package/lib/public/modules/server-settings-navigation.js +0 -39
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
var crypto = require('crypto');
|
|
2
|
+
function attachMateLifecycleService(ctx) {
|
|
3
|
+
var busy = new Set();
|
|
4
|
+
function owner(userId) {
|
|
5
|
+
if (ctx.users.isMultiUser() && (!userId || !ctx.users.findUserById(userId))) throw new Error('Mate owner is unavailable.');
|
|
6
|
+
return ctx.mates.buildMateCtx(ctx.users.isMultiUser() ? userId : null);
|
|
7
|
+
}
|
|
8
|
+
function read(userId, id) {
|
|
9
|
+
var mate = ctx.mates.getMate(owner(userId), id);
|
|
10
|
+
if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate is unavailable or belongs to another owner.');
|
|
11
|
+
return mate;
|
|
12
|
+
}
|
|
13
|
+
function validateReceipt(userId, id, allowMissing) {
|
|
14
|
+
var mate = ctx.mates.getMate(owner(userId), id);
|
|
15
|
+
if (!mate && allowMissing) return;
|
|
16
|
+
if (!mate || ctx.users.isMultiUser() && mate.createdBy !== userId) throw new Error('Mate receipt access changed.');
|
|
17
|
+
}
|
|
18
|
+
function list(userId) {
|
|
19
|
+
return ctx.mates.getAllMates(owner(userId)).filter(function (mate) { return !ctx.users.isMultiUser() || mate.createdBy === userId; });
|
|
20
|
+
}
|
|
21
|
+
function broadcast(userId, message, ws) {
|
|
22
|
+
var seen = new Set();
|
|
23
|
+
function send(socket) {
|
|
24
|
+
if (!socket || seen.has(socket) || socket.readyState !== 1) return;
|
|
25
|
+
if (ctx.users.isMultiUser() && (!socket._clayUser || socket._clayUser.id !== userId)) return;
|
|
26
|
+
seen.add(socket); socket.send(JSON.stringify(message));
|
|
27
|
+
}
|
|
28
|
+
send(ws);
|
|
29
|
+
ctx.projects.forEach(function (project) { project.forEachClient(send); });
|
|
30
|
+
}
|
|
31
|
+
function update(userId, id, updates, ws, requestId) {
|
|
32
|
+
var mate = read(userId, id);
|
|
33
|
+
if (Object.prototype.hasOwnProperty.call(updates, 'lifecycleDeletion')) throw new Error('Mate deletion state is server-managed.');
|
|
34
|
+
if (busy.has(id) || mate.lifecycleDeletion) throw new Error('Mate deletion is pending.');
|
|
35
|
+
var updated = ctx.mates.updateMate(owner(userId), id, updates);
|
|
36
|
+
if (!updated || updated.error) throw new Error(updated && updated.error || 'Mate not found');
|
|
37
|
+
try {
|
|
38
|
+
broadcast(userId, { type: 'mate_updated', mate: updated, requestId: requestId }, ws);
|
|
39
|
+
if (ctx.refresh) ctx.refresh(owner(userId));
|
|
40
|
+
} catch (error) { error.productControlResult = { persisted: true, retrySafe: false }; throw error; }
|
|
41
|
+
return updated;
|
|
42
|
+
}
|
|
43
|
+
function idle(project) {
|
|
44
|
+
if (!project) return;
|
|
45
|
+
if (project.inspectMateLifecycleRuntime && project.inspectMateLifecycleRuntime().some(function (t) { return !t.exited; })) throw new Error('Close the Mate terminals before deletion.');
|
|
46
|
+
project.getSessionManager().sessions.forEach(function (session) {
|
|
47
|
+
if (session.isProcessing || session._queryStarting || session.queryInstance || session.worker || session.terminalId || session.runtimeTerminalId || Object.keys(session._mentionSessions || {}).length) throw new Error('Wait for this Mate runtime to become idle before deletion.');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function closeRuntime(slug, validate) {
|
|
51
|
+
var timer;
|
|
52
|
+
try {
|
|
53
|
+
return await Promise.race([Promise.resolve().then(function () { if (validate) validate(true); return ctx.removeProject(slug, true); }), new Promise(function (resolve, reject) {
|
|
54
|
+
timer = setTimeout(function () { reject(new Error('Mate runtime cleanup timed out; deletion remains pending.')); }, 10000);
|
|
55
|
+
})]);
|
|
56
|
+
} finally { clearTimeout(timer); }
|
|
57
|
+
}
|
|
58
|
+
async function remove(userId, id, validate, ws) {
|
|
59
|
+
var mate = read(userId, id), slug = 'mate-' + id, project = ctx.projects.get(slug);
|
|
60
|
+
if (mate.primary || mate.builtinKey === 'clay') throw new Error('Canonical Clay and primary Mates cannot be deleted.');
|
|
61
|
+
if (busy.has(id)) throw new Error('Another Mate lifecycle operation is applying.');
|
|
62
|
+
idle(project);
|
|
63
|
+
if (validate) validate();
|
|
64
|
+
busy.add(id);
|
|
65
|
+
var persisted = false, runtimeClosed = false;
|
|
66
|
+
try {
|
|
67
|
+
// Retain an owner-visible tombstone until every required cleanup is acknowledged.
|
|
68
|
+
if (!mate.lifecycleDeletion) mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: { id: crypto.randomUUID(), runtimeRequired: !!project, runtimeClosed: !project } });
|
|
69
|
+
if (!mate || mate.error) throw new Error('Could not persist the pending Mate deletion.');
|
|
70
|
+
persisted = true;
|
|
71
|
+
if (mate.builtinKey) ctx.users.addDeletedBuiltinKey(userId, mate.builtinKey);
|
|
72
|
+
if (!project && mate.lifecycleDeletion.runtimeRequired && !mate.lifecycleDeletion.runtimeClosed) throw new Error('Runtime cleanup proof is unavailable; retain the pending deletion for operator recovery.');
|
|
73
|
+
if (project && await closeRuntime(slug, validate) !== true) throw new Error('Mate runtime cleanup was not acknowledged.');
|
|
74
|
+
runtimeClosed = true;
|
|
75
|
+
mate = ctx.mates.updateMate(owner(userId), id, { lifecycleDeletion: Object.assign({}, mate.lifecycleDeletion, { runtimeClosed: true }) });
|
|
76
|
+
if (!mate || mate.error) throw new Error('Runtime closure proof could not be saved.');
|
|
77
|
+
if (validate) validate(true);
|
|
78
|
+
var current = read(userId, id);
|
|
79
|
+
if (!current.lifecycleDeletion || current.lifecycleDeletion.id !== mate.lifecycleDeletion.id) throw new Error('Mate deletion identity changed.');
|
|
80
|
+
var result = ctx.mates.deleteMate(owner(userId), id, { strictCleanup: true });
|
|
81
|
+
if (!result || !result.ok) throw new Error(result && result.error || 'Mate deletion was not persisted.');
|
|
82
|
+
var definitions = require('./builtin-mates');
|
|
83
|
+
var available = ctx.mates.getMissingBuiltinKeys(owner(userId)).map(function (key) { var def = definitions.getBuiltinByKey(key); return def && { key: def.key, displayName: def.displayName, bio: def.bio, avatarCustom: def.avatarCustom || '', avatarStyle: def.avatarStyle || 'imprint', avatarColor: def.avatarColor || '' }; }).filter(Boolean);
|
|
84
|
+
broadcast(userId, { type: 'mate_deleted', mateId: id, availableBuiltins: available }, ws);
|
|
85
|
+
if (ctx.refresh) ctx.refresh(owner(userId));
|
|
86
|
+
return { persisted: true, runtimeClosed: true, directoryRemoved: true };
|
|
87
|
+
} catch (error) {
|
|
88
|
+
error.productControlResult = { persisted: persisted, runtimeClosed: runtimeClosed, deletionPending: !!ctx.mates.getMate(owner(userId), id), retrySafe: false };
|
|
89
|
+
throw error;
|
|
90
|
+
} finally { busy.delete(id); }
|
|
91
|
+
}
|
|
92
|
+
return { read: read, validateReceipt: validateReceipt, list: list, update: update, remove: remove };
|
|
93
|
+
}
|
|
94
|
+
module.exports = { attachMateLifecycleService: attachMateLifecycleService };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Shared Home/Mate delivery. Canonical callers supply an exact live guard and
|
|
2
|
+
// persist delegated provenance before any provider input is admitted.
|
|
3
|
+
async function deliver(ctx, session, text, options) {
|
|
4
|
+
options = options || {};
|
|
5
|
+
var guarded = typeof options.revalidate === 'function';
|
|
6
|
+
function current() {
|
|
7
|
+
if (ctx.sm.sessions.get(session.localId) !== session) throw new Error('The conversation was deleted or replaced.');
|
|
8
|
+
if (guarded) {
|
|
9
|
+
options.revalidate();
|
|
10
|
+
if (session.taskStopRequested || session._pendingMessageDrainPaused || session._responseRecoveryStopPending || session.abortController && session.abortController.signal.aborted) throw new Error('The conversation is stopped; explicit owner resumption is required.');
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (guarded) current.exactProductDelivery = true;
|
|
15
|
+
current();
|
|
16
|
+
var event = options.event || { type: 'user_message', text: text };
|
|
17
|
+
if (guarded) {
|
|
18
|
+
if (ctx.sm.saveSessionFile(session) !== true) throw new Error('The conversation could not be saved; no provider delivery was attempted.');
|
|
19
|
+
if (ctx.sm.sendAndRecordDurably(session, event) !== true) throw new Error('The delegated message could not be saved; no provider delivery was attempted.');
|
|
20
|
+
session._mcpForeignInput = true;
|
|
21
|
+
} else ctx.sm.sendAndRecord(session, event);
|
|
22
|
+
var attempted = false;
|
|
23
|
+
try {
|
|
24
|
+
current();
|
|
25
|
+
var active = !!session.isProcessing;
|
|
26
|
+
session.isProcessing = true;
|
|
27
|
+
if (!active) session.sentToolResults = {};
|
|
28
|
+
if (options.onRecorded) options.onRecorded(session);
|
|
29
|
+
var queryText = options.queryText || text;
|
|
30
|
+
attempted = true;
|
|
31
|
+
if (session.queryInstance || session.worker && session.messageQueue === 'worker' || active) {
|
|
32
|
+
if (ctx.sdk.pushMessage(session, queryText, null) === true) return { status: 'queued', persisted: true, delivery: 'accepted', completion: 'pending' };
|
|
33
|
+
if (active) return { status: 'failed', persisted: true, delivery: 'rejected', retrySafe: false, error: 'The active provider did not accept the message. Inspect before another request.' };
|
|
34
|
+
}
|
|
35
|
+
var accepted = await ctx.sdk.startQuery(session, queryText, null, ctx.linuxUser || null, guarded ? current : undefined);
|
|
36
|
+
return { status: accepted === true ? 'started' : 'failed', persisted: true, delivery: accepted === true ? 'accepted' : 'not_confirmed', completion: 'pending', retrySafe: false };
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (!attempted) session.isProcessing = false;
|
|
39
|
+
error.productControlResult = { persisted: true, delivery: attempted ? 'unknown' : 'not_attempted', retrySafe: false };
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
module.exports = { deliver: deliver };
|
|
@@ -4,6 +4,7 @@ var resolveMateIdentityFile = require('./mate-identity-file').resolveMateIdentit
|
|
|
4
4
|
|
|
5
5
|
var fs = require("fs");
|
|
6
6
|
var path = require("path");
|
|
7
|
+
var scheduleInput = require("./mate-schedule-input");
|
|
7
8
|
|
|
8
9
|
function cleanLine(value, maxLength, field) {
|
|
9
10
|
if (typeof value !== "string") throw new Error(field + " is required.");
|
|
@@ -27,6 +28,11 @@ function normalize(definition) {
|
|
|
27
28
|
var identity = typeof definition.identityMarkdown === "string" ? definition.identityMarkdown.replace(/\u0000/g, "").trim() : "";
|
|
28
29
|
if (identity.length < 200) throw new Error("The Mate identity is incomplete.");
|
|
29
30
|
if (identity.length > 30000) throw new Error("The Mate identity is too long.");
|
|
31
|
+
var scheduleIntent = definition.scheduleIntent || null;
|
|
32
|
+
if (scheduleIntent) {
|
|
33
|
+
var scheduleChecked = scheduleInput.normalize(scheduleIntent);
|
|
34
|
+
scheduleIntent = { message: scheduleChecked.message, scheduleRule: scheduleChecked.rule };
|
|
35
|
+
}
|
|
30
36
|
return {
|
|
31
37
|
name: cleanLine(definition.name, 80, "Mate name"),
|
|
32
38
|
bio: cleanLine(definition.bio, 280, "Mate bio"),
|
|
@@ -37,6 +43,7 @@ function normalize(definition) {
|
|
|
37
43
|
identityMarkdown: identity,
|
|
38
44
|
vendor: cleanLine(definition.vendor || "claude", 40, "Vendor"),
|
|
39
45
|
model: cleanLine(definition.model, 240, "Mate model"),
|
|
46
|
+
scheduleIntent: scheduleIntent,
|
|
40
47
|
};
|
|
41
48
|
}
|
|
42
49
|
|
|
@@ -54,6 +61,7 @@ function createReadyMate(mates, mateCtx, definition) {
|
|
|
54
61
|
vendor: ready.vendor,
|
|
55
62
|
model: ready.model,
|
|
56
63
|
autonomy: ready.autonomy,
|
|
64
|
+
scheduleIntent: ready.scheduleIntent,
|
|
57
65
|
});
|
|
58
66
|
var mateDir = mates.getMateDir(mateCtx, mate.id);
|
|
59
67
|
var identityPath = resolveMateIdentityFile(mateDir);
|
|
@@ -80,7 +88,7 @@ function createReadyMate(mates, mateCtx, definition) {
|
|
|
80
88
|
vendor: ready.vendor,
|
|
81
89
|
model: ready.model,
|
|
82
90
|
profile: Object.assign({}, mate.profile || {}, { displayName: ready.name, bio: ready.bio }),
|
|
83
|
-
seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy }),
|
|
91
|
+
seedData: Object.assign({}, mate.seedData || {}, { relationship: ready.relationship, activity: ready.activities, communicationStyle: ready.communicationStyle, autonomy: ready.autonomy, scheduleIntent: ready.scheduleIntent }),
|
|
84
92
|
});
|
|
85
93
|
mates.backupIdentity(mateDir, ready.identityMarkdown);
|
|
86
94
|
mates.logIdentityChange(mateDir, "clay_interview", ready.identityMarkdown, "");
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
var rules = require('./schedule-rule');
|
|
2
|
+
function normalize(input, previous) {
|
|
3
|
+
input = input || {};
|
|
4
|
+
var message = input.message === undefined && previous ? previous.message : input.message;
|
|
5
|
+
if (typeof message !== 'string' || !message.trim() || message.length > 1200 || message.indexOf('\u0000') >= 0) throw new Error('A message of 1 to 1200 characters is required.');
|
|
6
|
+
var source = input.scheduleRule || (previous && previous.rule) || {
|
|
7
|
+
version: 1, timezone: 'server-local', anchorDate: input.date, time: input.time, recurrence: null,
|
|
8
|
+
};
|
|
9
|
+
source = Object.assign({}, source);
|
|
10
|
+
if (input.date !== undefined) source.anchorDate = input.date;
|
|
11
|
+
if (input.time !== undefined) source.time = input.time;
|
|
12
|
+
var checked = rules.validateScheduleRule(source);
|
|
13
|
+
if (!checked.ok) throw new Error(checked.error);
|
|
14
|
+
return { message: message.trim(), rule: checked.value };
|
|
15
|
+
}
|
|
16
|
+
function timezone() { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; }
|
|
17
|
+
function review(value) {
|
|
18
|
+
var rule = value.rule;
|
|
19
|
+
var repeat = rule.recurrence ? 'Every ' + rule.recurrence.every + ' ' + rule.recurrence.unit +
|
|
20
|
+
(rule.recurrence.weekdays ? ' on ' + rule.recurrence.weekdays.map(function (day) { return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]; }).join(', ') : '') : 'Once';
|
|
21
|
+
if (rule.recurrenceEnd) repeat += rule.recurrenceEnd.type === 'after' ? ', ' + rule.recurrenceEnd.count + ' deliveries' : ', through ' + rule.recurrenceEnd.date;
|
|
22
|
+
if (rule.interval) {
|
|
23
|
+
var ending = rule.interval.end;
|
|
24
|
+
repeat += '; every ' + rule.interval.every + ' ' + rule.interval.unit;
|
|
25
|
+
repeat += !ending ? ', until end of day' : ending.type === 'after' ? ', ' + ending.count + ' deliveries per day' : ', until ' + ending.time;
|
|
26
|
+
}
|
|
27
|
+
return value.message + '\n' + rule.anchorDate + ' at ' + rule.time + ' (' + timezone() + ')\n' + repeat;
|
|
28
|
+
}
|
|
29
|
+
module.exports = { normalize: normalize, timezone: timezone, review: review };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
var crypto = require('crypto');
|
|
2
|
+
var rule = require('./schedule-rule');
|
|
3
|
+
|
|
4
|
+
function copy(value) { return JSON.parse(JSON.stringify(value)); }
|
|
5
|
+
function attachMateScheduleLifecycle(ctx) {
|
|
6
|
+
var now = ctx.now || Date.now;
|
|
7
|
+
function save(session, records) {
|
|
8
|
+
var previous = session.mateSchedules;
|
|
9
|
+
session.mateSchedules = records;
|
|
10
|
+
if (ctx.sm.saveSessionFile(session) !== true) {
|
|
11
|
+
session.mateSchedules = previous;
|
|
12
|
+
throw new Error('The Mate schedule could not be saved.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function records(session) { return copy(session.mateSchedules || []); }
|
|
16
|
+
function find(session, id) {
|
|
17
|
+
return records(session).find(function (record) { return record.id === id; }) || null;
|
|
18
|
+
}
|
|
19
|
+
function replace(session, record) {
|
|
20
|
+
var all = records(session);
|
|
21
|
+
var index = all.findIndex(function (item) { return item.id === record.id; });
|
|
22
|
+
if (index < 0) all.push(record); else all[index] = record;
|
|
23
|
+
save(session, all);
|
|
24
|
+
}
|
|
25
|
+
function jobFor(record) {
|
|
26
|
+
return ctx.scheduler.listJobs().reverse().find(function (job) {
|
|
27
|
+
return job.projectId === ctx.slug && job.payload && job.payload.mateScheduleId === record.id &&
|
|
28
|
+
job.payload.logicalRevision === record.revision && job.payload.occurrence === record.occurrence;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function current(session, job) {
|
|
32
|
+
var record = job && job.payload && find(session, job.payload.mateScheduleId);
|
|
33
|
+
if (record && record.createdBy && record.createdBy.type === 'canonicalClay') { try { (ctx.authorizeRuntime || ctx.authorize)(session); } catch (error) { return false; } }
|
|
34
|
+
return !!(record && record.state === 'active' && record.revision === job.payload.logicalRevision &&
|
|
35
|
+
record.occurrence === job.payload.occurrence && record.ownerId === job.ownerId);
|
|
36
|
+
}
|
|
37
|
+
function admit(session, record) {
|
|
38
|
+
if (record.state !== 'active') return null;
|
|
39
|
+
var existing = jobFor(record);
|
|
40
|
+
if (existing && !(existing.state === 'cancelled' && existing.cancelledReason === 'queue_admission_failed')) {
|
|
41
|
+
if (['failed', 'cancelled', 'interrupted'].indexOf(existing.state) >= 0) throw new Error('The occurrence requires recovery; it was not scheduled.');
|
|
42
|
+
return existing;
|
|
43
|
+
}
|
|
44
|
+
var job = ctx.schedule(session, record.message, Math.max(record.runAt, now() + 1),
|
|
45
|
+
{ id: record.ownerId }, null, null, 'mate-' + record.id + '-' + record.revision + '-' + record.occurrence,
|
|
46
|
+
null, { exact: true, scheduleRule: record.rule, mateScheduleId: record.id,
|
|
47
|
+
logicalScheduleId: record.id, logicalRevision: record.revision, occurrence: record.occurrence });
|
|
48
|
+
if (!job) throw new Error('The occurrence could not be admitted. The saved schedule needs retry.');
|
|
49
|
+
return job;
|
|
50
|
+
}
|
|
51
|
+
function create(session, value, id, control) {
|
|
52
|
+
var actor = ctx.authorize(session, control);
|
|
53
|
+
var existing = find(session, id);
|
|
54
|
+
if (existing) { if (existing.message !== value.message || JSON.stringify(existing.rule) !== JSON.stringify(value.rule)) throw new Error('Schedule request already names another intent.'); admit(session, existing); return existing; }
|
|
55
|
+
var runAt = rule.nextScheduleRuleTime(value.rule, now(), [], {});
|
|
56
|
+
if (!Number.isFinite(runAt)) throw new Error('The schedule time has expired.');
|
|
57
|
+
var record = { id: id, revision: crypto.randomUUID(), ownerId: ctx.owner(session), message: value.message,
|
|
58
|
+
createdBy: actor || null, updatedBy: actor || null, rule: value.rule, state: 'active', occurrence: 0, count: 0, dateCounts: {}, runAt: runAt };
|
|
59
|
+
replace(session, record);
|
|
60
|
+
admit(session, record);
|
|
61
|
+
return record;
|
|
62
|
+
}
|
|
63
|
+
function checked(session, args, control) {
|
|
64
|
+
ctx.authorize(session, control);
|
|
65
|
+
var record = find(session, args.id);
|
|
66
|
+
if (!record || record.ownerId !== ctx.owner(session)) throw new Error('Schedule not found.');
|
|
67
|
+
if (args.revision && args.revision !== record.revision) throw new Error('The schedule revision is stale.');
|
|
68
|
+
return record;
|
|
69
|
+
}
|
|
70
|
+
function cancel(session, args, control) {
|
|
71
|
+
var record = checked(session, args, control);
|
|
72
|
+
var actor = ctx.authorize(session, control);
|
|
73
|
+
if (record.state === 'cancelled') return record;
|
|
74
|
+
// Persist the tombstone before touching occurrences. Dispatch rechecks it.
|
|
75
|
+
record.state = 'cancelled';
|
|
76
|
+
if (actor) record.updatedBy = actor;
|
|
77
|
+
replace(session, record);
|
|
78
|
+
var job = jobFor(record);
|
|
79
|
+
if (job && job.state === 'queued' && !ctx.cancelJob(session, job)) throw new Error('Cancellation was saved, but occurrence cleanup failed.');
|
|
80
|
+
return record;
|
|
81
|
+
}
|
|
82
|
+
function update(session, args, value, control) {
|
|
83
|
+
var previous = checked(session, args, control);
|
|
84
|
+
var actor = ctx.authorize(session, control);
|
|
85
|
+
if (previous.state !== 'active') throw new Error('Only active schedules can be updated.');
|
|
86
|
+
var runAt = rule.nextScheduleRuleTime(value.rule, now(), [], {});
|
|
87
|
+
if (!Number.isFinite(runAt)) throw new Error('The schedule time has expired.');
|
|
88
|
+
var record = Object.assign({}, previous, { revision: crypto.randomUUID(), updatedBy: actor || previous.updatedBy || null, message: value.message,
|
|
89
|
+
rule: value.rule, count: 0, dateCounts: {}, occurrence: 0, runAt: runAt });
|
|
90
|
+
replace(session, record);
|
|
91
|
+
var oldJob = jobFor(previous);
|
|
92
|
+
if (oldJob && oldJob.state === 'queued' && !ctx.cancelJob(session, oldJob)) throw new Error('Update saved, but previous occurrence cleanup failed.');
|
|
93
|
+
admit(session, record);
|
|
94
|
+
return record;
|
|
95
|
+
}
|
|
96
|
+
function consumed(session, job) {
|
|
97
|
+
if (!current(session, job)) return false;
|
|
98
|
+
var record = find(session, job.payload.mateScheduleId);
|
|
99
|
+
record.count++;
|
|
100
|
+
var date = new Date(record.runAt);
|
|
101
|
+
var key = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0');
|
|
102
|
+
record.dateCounts[key] = Number(record.dateCounts[key] || 0) + 1;
|
|
103
|
+
// Coalesce missed times: deliver the admitted occurrence once, then choose
|
|
104
|
+
// the next future time. Counts represent deliveries, not missed slots.
|
|
105
|
+
record.runAt = rule.nextScheduleRuleTime(record.rule, Math.max(record.runAt, now()), [],
|
|
106
|
+
{ startCount: record.count, dateStartCounts: record.dateCounts });
|
|
107
|
+
record.occurrence++;
|
|
108
|
+
if (!Number.isFinite(record.runAt)) record.state = 'completed';
|
|
109
|
+
replace(session, record);
|
|
110
|
+
admit(session, record);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function reconcile(session) {
|
|
114
|
+
records(session).forEach(function (record) {
|
|
115
|
+
if (record.state !== 'active') return;
|
|
116
|
+
(ctx.authorizeRuntime || ctx.authorize)(session);
|
|
117
|
+
var job = jobFor(record);
|
|
118
|
+
var item = job && ctx.queueItem(session, job);
|
|
119
|
+
if (item && item.state === 'consumed') consumed(session, job);
|
|
120
|
+
else if (item && (item.state === 'cancelled' || item.state === 'failed')) {
|
|
121
|
+
record.state = item.state;
|
|
122
|
+
replace(session, record);
|
|
123
|
+
} else if (job && ['failed', 'cancelled', 'interrupted'].indexOf(job.state) >= 0 && (!item || item.state !== 'pending')) {
|
|
124
|
+
record.state = 'interrupted';
|
|
125
|
+
replace(session, record);
|
|
126
|
+
} else admit(session, record);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function onConsumed(session, job) {
|
|
130
|
+
try { return consumed(session, job); }
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Queue consumption is already durable. Keep the accepted provider turn
|
|
133
|
+
// running; reconciliation repairs the saved next occurrence after restart.
|
|
134
|
+
ctx.sm.sendAndRecord(session, { type: 'error', text: 'The scheduled message was delivered, but its next occurrence needs recovery: ' + error.message });
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { create: create, read: checked, list: function (session, control) { ctx.authorize(session, control); return records(session); },
|
|
139
|
+
update: update, cancel: cancel, failed: function (session, job, error) {
|
|
140
|
+
if (!current(session, job)) return;
|
|
141
|
+
var record = find(session, job.payload.mateScheduleId);
|
|
142
|
+
record.state = 'failed'; record.error = error.message;
|
|
143
|
+
replace(session, record);
|
|
144
|
+
}, cancelled: function (session, job) {
|
|
145
|
+
if (!current(session, job)) return;
|
|
146
|
+
var record = find(session, job.payload.mateScheduleId);
|
|
147
|
+
record.state = 'cancelled';
|
|
148
|
+
replace(session, record);
|
|
149
|
+
}, current: current, consumed: onConsumed, reconcile: reconcile };
|
|
150
|
+
}
|
|
151
|
+
module.exports = { attachMateScheduleLifecycle: attachMateScheduleLifecycle };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
var crypto = require('crypto');
|
|
2
|
+
var userInput = require('./yoke/user-input');
|
|
3
|
+
var input = require('./mate-schedule-input');
|
|
4
|
+
var personal = require('./mcp-session-access');
|
|
5
|
+
|
|
6
|
+
function attachMateScheduling(ctx) {
|
|
7
|
+
function owner(session) { return ctx.users.isMultiUser() ? session.ownerId : 'local-user'; }
|
|
8
|
+
function privateSession(session) {
|
|
9
|
+
if (!ctx.isMate || !session || ctx.sm.sessions.get(session.localId) !== session ||
|
|
10
|
+
session.destroying || session.mateCreationMode || session.taskStopRequested || session._mcpForeignInput ||
|
|
11
|
+
session.sessionVisibility === 'shared' || session.delegated || session._delegatedBy || session._pairDelegation) throw new Error('An active private Mate conversation is required.');
|
|
12
|
+
if (ctx.users.isMultiUser() && (session.ownerId !== ctx.projectOwnerId || !personal.canUsePersonalConnections(session, ctx.sm.sessions))) throw new Error('Only the Mate owner can schedule messages.');
|
|
13
|
+
if (!ctx.users.isMultiUser() && (session.ownerId || (session.history || []).some(function (event) { return event.type === 'user_message' && event.from && event.from !== 'default'; }))) throw new Error('Foreign input cannot authorize Mate schedules.');
|
|
14
|
+
ctx.authorizeAccess(session, owner(session));
|
|
15
|
+
}
|
|
16
|
+
function authorize(session) {
|
|
17
|
+
if (ctx.isHostAgent) throw new Error('Canonical Clay scheduling requires its separate owner-reviewed controls.');
|
|
18
|
+
privateSession(session);
|
|
19
|
+
}
|
|
20
|
+
function authorizeControl(session, control) {
|
|
21
|
+
privateSession(session);
|
|
22
|
+
return require('./canonical-clay-scheduling-authority').validate(control, ctx.slug, session);
|
|
23
|
+
}
|
|
24
|
+
function authorizeLifecycle(session) {
|
|
25
|
+
privateSession(session);
|
|
26
|
+
require('./canonical-clay-scheduling-authority').assertMate(ctx.projectOwnerId, ctx.mateId, ctx.users.isMultiUser());
|
|
27
|
+
if (ctx.isHostAgent) require('./canonical-clay-scheduling-authority').assertHost(ctx.projectOwnerId, ctx.mateId, ctx.users.isMultiUser());
|
|
28
|
+
}
|
|
29
|
+
function persistDraft(session, draft) {
|
|
30
|
+
var previous = session.mateScheduleDraft;
|
|
31
|
+
session.mateScheduleDraft = draft;
|
|
32
|
+
if (ctx.sm.saveSessionFile(session) !== true) {
|
|
33
|
+
session.mateScheduleDraft = previous;
|
|
34
|
+
throw new Error('The schedule draft could not be saved.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function result(value) { return { content: [{ type: 'text', text: JSON.stringify(value) }] }; }
|
|
38
|
+
function getToolDefs(session) {
|
|
39
|
+
try { authorize(session); } catch (error) { return []; }
|
|
40
|
+
var generation = Number(session._sdkQueryGeneration || 0);
|
|
41
|
+
var originalOwner = owner(session);
|
|
42
|
+
function current() {
|
|
43
|
+
authorize(session);
|
|
44
|
+
if (generation !== Number(session._sdkQueryGeneration || 0) || owner(session) !== originalOwner) throw new Error('The schedule query is stale.');
|
|
45
|
+
}
|
|
46
|
+
function tool(name, description, properties, required, handler) {
|
|
47
|
+
return { name: name, queryBound: true, inputSchema: { type: 'object', properties: properties, required: required, additionalProperties: false },
|
|
48
|
+
description: description, handler: async function (args) {
|
|
49
|
+
try { current(); return result(await handler(args || {})); }
|
|
50
|
+
catch (error) { return result({ status: 'rejected', reason: error.message }); }
|
|
51
|
+
} };
|
|
52
|
+
}
|
|
53
|
+
var str = { type: 'string' };
|
|
54
|
+
var fields = { message: str, date: str, time: str, scheduleRule: { type: ['object', 'null'] } };
|
|
55
|
+
async function approve(draft, question, label, other) {
|
|
56
|
+
persistDraft(session, draft);
|
|
57
|
+
var reply = await userInput.dispatchUserInput(ctx.userInput(session), { questions: [{
|
|
58
|
+
id: 'approve', header: 'Mate schedule', question: question,
|
|
59
|
+
options: [{ label: label }, { label: other || 'Cancel' }], allowOther: false,
|
|
60
|
+
}] }, { source: 'mate-schedule', signal: session.abortController && session.abortController.signal });
|
|
61
|
+
current();
|
|
62
|
+
if (!session.mateScheduleDraft || session.mateScheduleDraft.id !== draft.id || session.mateScheduleDraft.revision !== draft.revision) throw new Error('The schedule draft is stale.');
|
|
63
|
+
var accepted = reply.status === 'submitted' && reply.answers.approve && reply.answers.approve.length === 1 && reply.answers.approve[0] === label;
|
|
64
|
+
if (!accepted) persistDraft(session, null);
|
|
65
|
+
return accepted;
|
|
66
|
+
}
|
|
67
|
+
async function review(args, update) {
|
|
68
|
+
var previous = update ? ctx.lifecycle.read(session, args) : null;
|
|
69
|
+
var value = input.normalize(args, previous);
|
|
70
|
+
var draft = { id: crypto.randomUUID(), revision: crypto.randomUUID(), value: value,
|
|
71
|
+
targetId: previous && previous.id, targetRevision: previous && previous.revision };
|
|
72
|
+
if (!await approve(draft, (update ? 'Update this schedule?\n' : 'Create this schedule?\n') + input.review(value), update ? 'Update schedule' : 'Create schedule')) return { status: 'cancelled' };
|
|
73
|
+
var record = update ? ctx.lifecycle.update(session, { id: draft.targetId, revision: draft.targetRevision }, value) : ctx.lifecycle.create(session, value, draft.id);
|
|
74
|
+
persistDraft(session, null);
|
|
75
|
+
return { status: update ? 'updated' : 'created', schedule: record, timezone: input.timezone() };
|
|
76
|
+
}
|
|
77
|
+
async function cancel(args) {
|
|
78
|
+
var record = ctx.lifecycle.read(session, args);
|
|
79
|
+
if (record.state === 'cancelled') return { status: 'cancelled', schedule: record };
|
|
80
|
+
var draft = { id: crypto.randomUUID(), revision: crypto.randomUUID(), targetId: record.id, targetRevision: record.revision };
|
|
81
|
+
if (!await approve(draft, 'Cancel this schedule?\n' + input.review({ message: record.message, rule: record.rule }), 'Cancel schedule', 'Keep schedule')) return { status: 'unchanged' };
|
|
82
|
+
record = ctx.lifecycle.cancel(session, { id: draft.targetId, revision: draft.targetRevision });
|
|
83
|
+
persistDraft(session, null);
|
|
84
|
+
return { status: 'cancelled', schedule: record };
|
|
85
|
+
}
|
|
86
|
+
var begin = tool('begin_mate_schedule_interview', 'Begin scheduling for this Mate. Ask exactly one structured question at a time. Never assume repetition.', {}, [], function () {
|
|
87
|
+
return { status: 'interview_started', timezone: input.timezone(), guidance: 'Collect the message, date/time, and explicitly requested repetition one question at a time. Then propose the complete schedule for the human approval card.' };
|
|
88
|
+
});
|
|
89
|
+
begin.structuredQuestionLimit = function () { try { current(); return 1; } catch (error) { return null; } };
|
|
90
|
+
return [begin,
|
|
91
|
+
tool('propose_mate_schedule', 'Review a complete message schedule in a server-owned approval card. Only authenticated human approval persists a schedule.', fields, ['message'], function (args) { return review(args, false); }),
|
|
92
|
+
tool('list_mate_schedules', 'List logical schedules and their stable IDs, revisions and lifecycle states in this private conversation.', {}, [], function () { return { schedules: ctx.lifecycle.list(session) }; }),
|
|
93
|
+
tool('read_mate_schedule', 'Read one logical schedule using its listed ID.', { id: str }, ['id'], function (args) { return { schedule: ctx.lifecycle.read(session, args) }; }),
|
|
94
|
+
tool('update_mate_schedule', 'Propose changes to an active schedule by listed ID and revision, with a fresh human approval card.', Object.assign({ id: str, revision: str }, fields), ['id', 'revision'], function (args) { return review(args, true); }),
|
|
95
|
+
tool('cancel_mate_schedule', 'Review cancellation by listed ID and revision in an authenticated human approval card. Cancellation persists across restart.', { id: str, revision: str }, ['id', 'revision'], cancel),
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
return { authorize: authorize, authorizeControl: authorizeControl, authorizeLifecycle: authorizeLifecycle, owner: owner, getToolDefs: getToolDefs, getSystemPrompt: function (session) {
|
|
99
|
+
try { authorize(session); } catch (error) { return ''; }
|
|
100
|
+
return 'When scheduling for this Mate, call begin_mate_schedule_interview, ask exactly one structured question at a time, and clarify the message, local date/time in ' + input.timezone() + ', and explicitly requested repetition. Use scheduleRule for recurrence and count/until limits. propose_mate_schedule and update_mate_schedule present the exact authenticated human approval card. Chat text and model flags never approve schedules. Use listed logical IDs and revisions to read, update or cancel. Scheduled output uses this private Mate runtime and tools. Project Scheduled Tasks Driver/Worker tools remain unavailable.';
|
|
101
|
+
} };
|
|
102
|
+
}
|
|
103
|
+
module.exports = { attachMateScheduling: attachMateScheduling };
|
package/lib/mates.js
CHANGED
|
@@ -4,6 +4,8 @@ var path = require("path");
|
|
|
4
4
|
var crypto = require("crypto");
|
|
5
5
|
var config = require("./config");
|
|
6
6
|
var workspaceColor = require("./mate-workspace-color");
|
|
7
|
+
var avatarCatalog = require("./mate-avatar-catalog");
|
|
8
|
+
var avatarMigration = require("./mate-avatar-migration");
|
|
7
9
|
|
|
8
10
|
var crisisSafety = require("./crisis-safety");
|
|
9
11
|
var matesPrompts = require("./mates-prompts");
|
|
@@ -98,10 +100,12 @@ function loadMates(ctx) {
|
|
|
98
100
|
var raw = fs.readFileSync(matesFilePath(ctx), "utf8");
|
|
99
101
|
var data = JSON.parse(raw);
|
|
100
102
|
if (!data.mates) data.mates = [];
|
|
101
|
-
return data;
|
|
102
103
|
} catch (e) {
|
|
103
104
|
return defaultData();
|
|
104
105
|
}
|
|
106
|
+
// Migration write failures must propagate, never masquerade as an empty registry.
|
|
107
|
+
if (avatarMigration.migrate(data)) saveMates(ctx, data);
|
|
108
|
+
return data;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
function saveMates(ctx, data) {
|
|
@@ -135,6 +139,7 @@ function createMate(ctx, seedData) {
|
|
|
135
139
|
displayName: null,
|
|
136
140
|
avatarColor: "#5857fc",
|
|
137
141
|
avatarStyle: "imprint",
|
|
142
|
+
avatarCatalogId: "B" + String(crypto.randomInt(1, 49)).padStart(2, "0"),
|
|
138
143
|
avatarSeed: crypto.randomBytes(4).toString("hex"),
|
|
139
144
|
},
|
|
140
145
|
bio: null,
|
|
@@ -142,6 +147,7 @@ function createMate(ctx, seedData) {
|
|
|
142
147
|
interviewProjectPath: null,
|
|
143
148
|
};
|
|
144
149
|
|
|
150
|
+
avatarMigration.normalizeMate(mate);
|
|
145
151
|
data.mates.push(mate);
|
|
146
152
|
saveMates(ctx, data);
|
|
147
153
|
|
|
@@ -205,6 +211,8 @@ function updateMate(ctx, id, updates) {
|
|
|
205
211
|
if (data.mates[i].primary || data.mates[i].builtinKey === "clay") delete updates.workspaceColor;
|
|
206
212
|
else if (!workspaceColor.valid(updates.workspaceColor)) return { error: "Choose a supported workspace color." };
|
|
207
213
|
}
|
|
214
|
+
var avatarError = avatarCatalog.validate(updates.profile, data.mates[i]);
|
|
215
|
+
if (avatarError) return { error: avatarError };
|
|
208
216
|
// Primary mates: protect name and core identity fields
|
|
209
217
|
if (data.mates[i].primary) {
|
|
210
218
|
delete updates.name;
|
|
@@ -219,6 +227,7 @@ function updateMate(ctx, id, updates) {
|
|
|
219
227
|
// Locked built-in artwork is server-managed. Preserve it even when a
|
|
220
228
|
// stale or crafted client submits a replacement profile object.
|
|
221
229
|
if (data.mates[i].profile && data.mates[i].profile.avatarLocked && updates.profile) {
|
|
230
|
+
updates.profile.avatarCatalogId = data.mates[i].profile.avatarCatalogId || "";
|
|
222
231
|
updates.profile.avatarStyle = data.mates[i].profile.avatarStyle;
|
|
223
232
|
updates.profile.avatarSeed = data.mates[i].profile.avatarSeed;
|
|
224
233
|
updates.profile.avatarColor = data.mates[i].profile.avatarColor;
|
|
@@ -229,6 +238,7 @@ function updateMate(ctx, id, updates) {
|
|
|
229
238
|
for (var j = 0; j < keys.length; j++) {
|
|
230
239
|
data.mates[i][keys[j]] = updates[keys[j]];
|
|
231
240
|
}
|
|
241
|
+
avatarMigration.normalizeMate(data.mates[i]);
|
|
232
242
|
saveMates(ctx, data);
|
|
233
243
|
return data.mates[i];
|
|
234
244
|
}
|
|
@@ -236,7 +246,7 @@ function updateMate(ctx, id, updates) {
|
|
|
236
246
|
return null;
|
|
237
247
|
}
|
|
238
248
|
|
|
239
|
-
function deleteMate(ctx, id) {
|
|
249
|
+
function deleteMate(ctx, id, options) {
|
|
240
250
|
var data = loadMates(ctx);
|
|
241
251
|
// Primary mates cannot be deleted (they are system infrastructure)
|
|
242
252
|
for (var di = 0; di < data.mates.length; di++) {
|
|
@@ -249,15 +259,16 @@ function deleteMate(ctx, id) {
|
|
|
249
259
|
return m.id !== id;
|
|
250
260
|
});
|
|
251
261
|
if (data.mates.length === before) return { error: "Mate not found" };
|
|
252
|
-
saveMates(ctx, data);
|
|
262
|
+
if (!options || !options.strictCleanup) saveMates(ctx, data);
|
|
253
263
|
|
|
254
264
|
// Remove mate directory
|
|
255
265
|
var mateDir = getMateDir(ctx, id);
|
|
256
266
|
try {
|
|
257
267
|
fs.rmSync(mateDir, { recursive: true, force: true });
|
|
258
268
|
} catch (e) {
|
|
259
|
-
|
|
269
|
+
if (options && options.strictCleanup) throw e;
|
|
260
270
|
}
|
|
271
|
+
if (options && options.strictCleanup) saveMates(ctx, data);
|
|
261
272
|
|
|
262
273
|
return { ok: true };
|
|
263
274
|
}
|
|
@@ -506,6 +517,7 @@ function createBuiltinMate(ctx, builtinKey) {
|
|
|
506
517
|
interviewProjectPath: null,
|
|
507
518
|
};
|
|
508
519
|
|
|
520
|
+
avatarMigration.normalizeMate(mate);
|
|
509
521
|
data.mates.push(mate);
|
|
510
522
|
saveMates(ctx, data);
|
|
511
523
|
|
|
@@ -661,8 +673,8 @@ function syncPrimaryMates(ctx) {
|
|
|
661
673
|
}
|
|
662
674
|
// Locked avatars are code-managed — refresh them from the def so a
|
|
663
675
|
// built-in's avatar can be updated by changing the source file
|
|
664
|
-
//
|
|
665
|
-
if (def.avatarLocked) {
|
|
676
|
+
// User uploads are preserved while canonical library artwork follows the definition.
|
|
677
|
+
if (def.avatarLocked && !avatarMigration.hasCustom(mate)) {
|
|
666
678
|
if (def.avatarCustom != null && mate.profile.avatarCustom !== def.avatarCustom) {
|
|
667
679
|
mate.profile.avatarCustom = def.avatarCustom; changed = true;
|
|
668
680
|
}
|