clay-server 5.2.0-beta.1 → 5.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/account-auth-socket.js +1 -0
- package/lib/builtin-mates.js +19 -16
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +88 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +247 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +84 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +104 -0
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +84 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +78 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/email-mcp-server.js +1 -1
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +99 -0
- package/lib/knowledge-db-service.js +24 -33
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-file-store.js +7 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +16 -34
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +18 -6
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +1 -0
- package/lib/mcp-source-context.js +1 -0
- package/lib/mcp-source-discovery.js +1 -0
- package/lib/mcp-source-plugins.js +1 -0
- package/lib/mcp-source-restrictions.js +1 -0
- package/lib/mcp-tool-policy.js +9 -0
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/pane-pending-interactions.js +20 -0
- package/lib/pane-session-activation.js +114 -0
- package/lib/pane-session-transport.js +26 -0
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +8 -4
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-debate.js +1 -0
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +15 -85
- package/lib/project-full-access.js +23 -2
- package/lib/project-http.js +7 -4
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +76 -17
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- package/lib/project-product-control.js +89 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +67 -68
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-document.js +27 -7
- package/lib/project-session-github.js +63 -19
- package/lib/project-session-history-actions.js +167 -0
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +7 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +103 -272
- package/lib/project-shared-browser.js +78 -17
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +67 -1
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +19 -2
- package/lib/project.js +178 -45
- package/lib/public/app.js +46 -0
- package/lib/public/appearance-bootstrap.js +16 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/command-palette.css +12 -33
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/input.css +6 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/messages.css +6 -2
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +46 -1
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/css/sticky-notes.css +87 -1
- package/lib/public/index.html +11 -5
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +11 -1
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +7 -4
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +22 -1
- package/lib/public/modules/app-messages.js +115 -17
- package/lib/public/modules/app-misc.js +3 -1
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +13 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/app-rendering.js +4 -3
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +20 -0
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-log-links.js +23 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +33 -0
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +146 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +342 -0
- package/lib/public/modules/command-palette.js +162 -40
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +169 -0
- package/lib/public/modules/contextual-clay.js +250 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +117 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/elicitation-form.js +5 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/history-replay-batch.js +13 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +88 -10
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +6 -34
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/message-delivery.js +20 -2
- package/lib/public/modules/model-picker.js +2 -0
- package/lib/public/modules/pane-activation.js +214 -0
- package/lib/public/modules/pane-binding.js +82 -0
- package/lib/public/modules/pane-bridge.js +7 -4
- package/lib/public/modules/pane-browser-bridge.js +22 -0
- package/lib/public/modules/pane-file-bridge.js +4 -0
- package/lib/public/modules/pane-issue-bridge.js +4 -1
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/profile.js +1 -238
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +144 -22
- package/lib/public/modules/rewind.js +5 -0
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -440
- package/lib/public/modules/session-actions.js +5 -2
- package/lib/public/modules/session-context-menu.js +5 -0
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +85 -14
- package/lib/public/modules/session-rich-drafts.js +21 -0
- package/lib/public/modules/session-switch-defer.js +67 -0
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +29 -6
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-group-restore.js +68 -0
- package/lib/public/modules/split-pair-ui.js +1 -0
- package/lib/public/modules/split-pane-reconciler.js +15 -0
- package/lib/public/modules/split-pane-renderer.js +87 -6
- package/lib/public/modules/split-session-boundary.js +1 -1
- package/lib/public/modules/split-view.js +60 -68
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +116 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/stt-controller.js +4 -1
- package/lib/public/modules/stt-recording.js +1 -1
- package/lib/public/modules/stt.js +7 -2
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/theme.js +8 -1
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +60 -11
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings.js +3 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +5 -0
- package/lib/public/sw.js +3 -1
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +342 -401
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +52 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- package/lib/server-admin.js +63 -16
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-dm.js +2 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +18 -4
- package/lib/server-home-chat.js +22 -20
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +29 -9
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +25 -62
- package/lib/server-owner-ui.js +118 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server-settings.js +6 -1
- package/lib/server.js +163 -43
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/session-visibility.js +5 -2
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +64 -99
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/terminal-manager.js +65 -6
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +7 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/workspace-query-service.js +13 -2
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +18 -2
- package/lib/yoke/acp-agent-profiles.js +7 -5
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +54 -50
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +53 -111
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +34 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +10 -4
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var CONVERSATION_CAP = 6000;
|
|
2
|
+
|
|
3
|
+
// SDK text is persisted as delta before onTurnDone; the response preview
|
|
4
|
+
// is only a notification excerpt. Historical complete text entries remain readable.
|
|
5
|
+
function isDigestEntry(entry) {
|
|
6
|
+
return entry && (entry.type === "user_message" || entry.type === "assistant_message" || entry.type === "delta" || entry.type === "text");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function copyCursor(cursor) {
|
|
10
|
+
return {
|
|
11
|
+
historyIndex: Math.max(0, cursor.historyIndex || 0),
|
|
12
|
+
textOffset: Math.max(0, cursor.textOffset || 0),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readState(history) {
|
|
17
|
+
var state = { cursor: { historyIndex: 0, textOffset: 0 } };
|
|
18
|
+
for (var i = history.length - 1; i >= 0; i--) {
|
|
19
|
+
var entry = history[i];
|
|
20
|
+
if (!entry || entry.type !== "digest_checkpoint") continue;
|
|
21
|
+
if (entry.cursor && typeof entry.cursor.historyIndex === "number") {
|
|
22
|
+
state.cursor = copyCursor(entry.cursor);
|
|
23
|
+
} else if (typeof entry.digestedIndex === "number") {
|
|
24
|
+
// Legacy checkpoints pointed at the next history entry and had no
|
|
25
|
+
// within-entry progress. Treat them as fully consumed entries.
|
|
26
|
+
state.cursor = { historyIndex: Math.max(0, entry.digestedIndex), textOffset: 0 };
|
|
27
|
+
}
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readCursor(history) {
|
|
34
|
+
return readState(history).cursor;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sameCursor(left, right) {
|
|
38
|
+
return left.historyIndex === right.historyIndex && left.textOffset === right.textOffset;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function safeTextEnd(text, start, maximum) {
|
|
42
|
+
var end = Math.min(text.length, start + maximum);
|
|
43
|
+
if (end < text.length && end > start) {
|
|
44
|
+
var previous = text.charCodeAt(end - 1);
|
|
45
|
+
if (previous >= 0xD800 && previous <= 0xDBFF) end--;
|
|
46
|
+
}
|
|
47
|
+
// The normal cap leaves enough room for a code point. Keep this fallback
|
|
48
|
+
// defensive so a future smaller cap cannot make the cursor stall.
|
|
49
|
+
if (end === start && end < text.length) {
|
|
50
|
+
end = start + (text.charCodeAt(start) >= 0xD800 && text.charCodeAt(start) <= 0xDBFF ? 2 : 1);
|
|
51
|
+
}
|
|
52
|
+
return Math.min(end, text.length);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function collectChunk(history, startCursor) {
|
|
56
|
+
var cursor = copyCursor(startCursor);
|
|
57
|
+
var parts = [];
|
|
58
|
+
var totalLength = 0;
|
|
59
|
+
var hadHistoryProgress = false;
|
|
60
|
+
var partCount = 0;
|
|
61
|
+
|
|
62
|
+
function appendPart(value) {
|
|
63
|
+
var separatorLength = partCount > 0 ? 1 : 0;
|
|
64
|
+
if (totalLength + separatorLength + value.length > CONVERSATION_CAP) return false;
|
|
65
|
+
parts.push(value);
|
|
66
|
+
totalLength += separatorLength + value.length;
|
|
67
|
+
partCount++;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
while (cursor.historyIndex < history.length) {
|
|
72
|
+
var entry = history[cursor.historyIndex];
|
|
73
|
+
if (!isDigestEntry(entry) || typeof entry.text !== "string" || !entry.text) {
|
|
74
|
+
cursor.historyIndex++;
|
|
75
|
+
cursor.textOffset = 0;
|
|
76
|
+
hadHistoryProgress = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var text = entry.text;
|
|
81
|
+
var offset = Math.min(cursor.textOffset, text.length);
|
|
82
|
+
|
|
83
|
+
var label = entry.type === "user_message" ? "User: " : "Mate: ";
|
|
84
|
+
var available = CONVERSATION_CAP - totalLength - (partCount > 0 ? 1 : 0) - label.length;
|
|
85
|
+
if (available <= 0 && partCount > 0) break;
|
|
86
|
+
if (available <= 0) available = CONVERSATION_CAP - label.length;
|
|
87
|
+
|
|
88
|
+
if (offset < text.length) {
|
|
89
|
+
var end = safeTextEnd(text, offset, available);
|
|
90
|
+
var value = label + text.substring(offset, end);
|
|
91
|
+
if (!appendPart(value)) break;
|
|
92
|
+
cursor.textOffset = end;
|
|
93
|
+
hadHistoryProgress = true;
|
|
94
|
+
if (end < text.length) break;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
cursor.historyIndex++;
|
|
98
|
+
cursor.textOffset = 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
conversationContent: parts.join("\n"),
|
|
103
|
+
cursor: cursor,
|
|
104
|
+
hasHistoryProgress: hadHistoryProgress,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function hasPending(history, cursor) {
|
|
109
|
+
var probe = collectChunk(history, cursor, "");
|
|
110
|
+
return !!probe.conversationContent;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
CONVERSATION_CAP: CONVERSATION_CAP,
|
|
115
|
+
collectChunk: collectChunk,
|
|
116
|
+
hasPending: hasPending,
|
|
117
|
+
readCursor: readCursor,
|
|
118
|
+
readState: readState,
|
|
119
|
+
sameCursor: sameCursor,
|
|
120
|
+
};
|
package/lib/email-mcp-server.js
CHANGED
|
@@ -37,7 +37,7 @@ function buildShape(props, required) {
|
|
|
37
37
|
// appendAuditLog(entry) -> void
|
|
38
38
|
function create(deps) {
|
|
39
39
|
var sdk;
|
|
40
|
-
try { sdk = require("
|
|
40
|
+
try { sdk = require("./yoke/sdk-server-factory").load(); } catch (e) {
|
|
41
41
|
console.error("[email-mcp] Failed to load SDK:", e.message);
|
|
42
42
|
return null;
|
|
43
43
|
}
|
package/lib/issues-service.js
CHANGED
|
@@ -129,25 +129,35 @@ function attachIssuesService(ctx) {
|
|
|
129
129
|
principal: principal, worktreeOnly: authorization.worktreeOnly };
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
function bind(source, isSession) {
|
|
132
|
+
function bind(source, isSession, canonicalClay) {
|
|
133
133
|
if (!source || typeof source.projectSlug !== "string") return null;
|
|
134
134
|
var initialSlug = source.projectSlug;
|
|
135
135
|
var initialProject = projectFor(initialSlug);
|
|
136
136
|
var initialSession = isSession ? source.session : null;
|
|
137
|
+
var clayInitial;
|
|
138
|
+
if (canonicalClay) { try { clayInitial = require("./canonical-clay-record-authority").resolve(source.authority, initialSlug); } catch (error) { return null; } }
|
|
137
139
|
if (!initialProject || (isSession && !initialSession)) return null;
|
|
138
140
|
if (initialSession) provenance.ensureOrigin(initialSession);
|
|
139
|
-
var initialUserId = initialSession ? initialSession.ownerId || null : source.userId || null;
|
|
141
|
+
var initialUserId = initialSession ? initialSession.ownerId || null : canonicalClay ? clayInitial.principal.userId || clayInitial.principal.singleUserOwnerId : source.userId || null;
|
|
140
142
|
var initialOrigin = initialSession ? initialSession.sessionOriginId || null : null;
|
|
141
143
|
var initial = bindingState(initialProject, initialSession, initialUserId, initialOrigin);
|
|
142
144
|
if (!initial) return null;
|
|
143
145
|
|
|
144
146
|
function current() {
|
|
145
147
|
if (projectFor(initialSlug) !== initialProject) throw new Error("This Issues binding is no longer valid.");
|
|
148
|
+
if (canonicalClay) {
|
|
149
|
+
var clayLive = require("./canonical-clay-record-authority").resolve(source.authority, initialSlug);
|
|
150
|
+
if (clayLive.project !== initialProject || clayLive.status.path !== clayInitial.status.path || clayLive.status.projectKnowledgeId !== clayInitial.status.projectKnowledgeId || clayLive.status.changeSetId !== clayInitial.status.changeSetId) throw new Error("Selected Issues context changed.");
|
|
151
|
+
}
|
|
146
152
|
var live = bindingState(initialProject, initialSession, initialUserId, initialOrigin);
|
|
147
153
|
if (!live) throw new Error("This Issues binding is no longer valid.");
|
|
148
154
|
return live;
|
|
149
155
|
}
|
|
150
156
|
|
|
157
|
+
function expected(entry, args) { if (canonicalClay && (!Number.isInteger(args.expectedRevision) || entry.revision !== args.expectedRevision)) throw new Error("Issue revision conflict."); }
|
|
158
|
+
|
|
159
|
+
function actor(state) { return canonicalClay ? require("./canonical-clay-record-authority").resolve(source.authority, initialSlug).actor : author(state.user, state.session ? "session" : "user", state.session); }
|
|
160
|
+
|
|
151
161
|
function visibleIssue(store, ref, state) {
|
|
152
162
|
var result = store.read(ref);
|
|
153
163
|
if (state.worktreeOnly && (!result.context || result.context.changeSetId !== state.status.changeSetId)) throw new Error("Issue not found.");
|
|
@@ -174,7 +184,7 @@ function attachIssuesService(ctx) {
|
|
|
174
184
|
path: state.status.path, knowledgeId: governing.projectKnowledgeId,
|
|
175
185
|
changeSetId: state.status.changeSetId || null, worktreeOnly: state.worktreeOnly,
|
|
176
186
|
recordContext: clone(issueContext(state.status)),
|
|
177
|
-
actor: clone(
|
|
187
|
+
actor: clone(actor(state)),
|
|
178
188
|
osIdentity: needsIdentity ? osIdentity(state) : null };
|
|
179
189
|
}
|
|
180
190
|
|
|
@@ -184,7 +194,7 @@ function attachIssuesService(ctx) {
|
|
|
184
194
|
var liveIdentity = needsIdentity ? osIdentity(fresh) : null;
|
|
185
195
|
if (fresh.project !== before.project || fresh.session !== before.session || (fresh.user.id || null) !== before.userId ||
|
|
186
196
|
fresh.status.path !== before.path || !governing || governing.projectKnowledgeId !== before.knowledgeId ||
|
|
187
|
-
(fresh.status.changeSetId || null) !== before.changeSetId || fresh.worktreeOnly !== before.worktreeOnly ||
|
|
197
|
+
(fresh.status.changeSetId || null) !== before.changeSetId || fresh.worktreeOnly !== before.worktreeOnly || JSON.stringify(issueContext(fresh.status)) !== JSON.stringify(before.recordContext) ||
|
|
188
198
|
JSON.stringify(liveIdentity) !== JSON.stringify(before.osIdentity)) throw new Error("Issue authorization changed during the operation.");
|
|
189
199
|
return fresh;
|
|
190
200
|
}
|
|
@@ -200,7 +210,7 @@ function attachIssuesService(ctx) {
|
|
|
200
210
|
var github = require('./issues-github').attachIssueGithub({
|
|
201
211
|
locks: githubLocks, assessments: githubAssessments, run: context.runGithub, analyze: source.analyzeGithub || context.analyzeGithub,
|
|
202
212
|
begin: function () {
|
|
203
|
-
if (isSession) throw new Error('GitHub filing requires an explicit action in the Issues panel.');
|
|
213
|
+
if (isSession || canonicalClay) throw new Error('GitHub filing requires an explicit action in the Issues panel.');
|
|
204
214
|
return operation(current(), true);
|
|
205
215
|
},
|
|
206
216
|
read: function (before, ref) {
|
|
@@ -217,6 +227,7 @@ function attachIssuesService(ctx) {
|
|
|
217
227
|
return {
|
|
218
228
|
searchGithubIssue: github.search, linkGithubIssue: github.link, createGithubIssue: github.create,
|
|
219
229
|
linkWorkSession: function (args, session) {
|
|
230
|
+
if (canonicalClay) throw new Error("Use reviewed project assignment or follow-up for Driver execution.");
|
|
220
231
|
var state = current();
|
|
221
232
|
var manager = state.project.getSessionManager();
|
|
222
233
|
if (!args || !Number.isInteger(args.expectedRevision)) throw new Error("expectedRevision is required.");
|
|
@@ -232,9 +243,10 @@ function attachIssuesService(ctx) {
|
|
|
232
243
|
if (links.some(function (link) { return link.sessionId === session.sessionOriginId; })) return entry;
|
|
233
244
|
var link = { sessionId: session.sessionOriginId, role: "driver", issueRevision: args.expectedRevision };
|
|
234
245
|
if (args.requestId !== undefined) link.requestId = schema.text(args.requestId, "work request id", 200, true);
|
|
235
|
-
return target.update(args.ref, { linkedWorkSessions: links.concat([link]), status: "in_progress" }, args.expectedRevision, author(state.user, "user", null));
|
|
246
|
+
return target.update(args.ref, { linkedWorkSessions: links.concat([link]), status: "in_progress" }, args.expectedRevision, canonicalClay ? actor(state) : author(state.user, "user", null));
|
|
236
247
|
},
|
|
237
248
|
resolveWorkSession: function (args) {
|
|
249
|
+
if (canonicalClay) throw new Error("Work sessions require the existing project Driver workflow.");
|
|
238
250
|
var state = current();
|
|
239
251
|
var entry = visibleIssue(openStore(state.status), args.ref, state);
|
|
240
252
|
if (!(entry.linkedWorkSessions || []).some(function (link) { return link.sessionId === args.sessionOriginId; })) throw new Error("Work session unavailable.");
|
|
@@ -255,9 +267,9 @@ function attachIssuesService(ctx) {
|
|
|
255
267
|
readIssueTombstone: function (args) { var state = current(); return scopedIssue(openStore(state.status), args && args.ref, state); },
|
|
256
268
|
issueHistory: function (args) { var state = current(); var store = openStore(state.status); var history = store.history(args && args.ref); if (state.worktreeOnly) { var historical = scopedIssue(store, args && args.ref, state); if (!historical.context || historical.context.changeSetId !== state.status.changeSetId) throw new Error("Issue not found."); } return history; },
|
|
257
269
|
readIssueRevision: function (args) { var state = current(); var store = openStore(state.status); visibleIssue(store, args && args.ref, state); return store.readRevision(args.ref, args.revision); },
|
|
258
|
-
commentIssue: function (args) { var state = current(); var target = openStore(state.status); visibleIssue(target, args && args.ref, state); return target.comment(args.ref, args.body, author(state.user, "user", null)); },
|
|
270
|
+
commentIssue: function (args) { var state = current(); var target = openStore(state.status); expected(visibleIssue(target, args && args.ref, state), args); return target.comment(args.ref, args.body, canonicalClay ? actor(state) : author(state.user, "user", null)); },
|
|
259
271
|
listIssueFeedback: function (args) {
|
|
260
|
-
if (!isSession) throw new Error("Only the Project Driver can review issue comments.");
|
|
272
|
+
if (!isSession && !canonicalClay) throw new Error("Only the Project Driver can review issue comments.");
|
|
261
273
|
var state = current(); var target = openStore(state.status); var out = []; var feedbackLimit = args && args.limit === undefined ? 200 : Number(args.limit);
|
|
262
274
|
if (!Number.isInteger(feedbackLimit) || feedbackLimit < 1 || feedbackLimit > 200) throw new Error("Feedback limit must be between 1 and 200.");
|
|
263
275
|
var position = { issueCursor: null, issueIndex: 0, commentOffset: 0 };
|
|
@@ -297,31 +309,32 @@ function attachIssuesService(ctx) {
|
|
|
297
309
|
return { feedback: out, total: out.length, nextCursor: nextCursor, truncated: !!nextCursor };
|
|
298
310
|
},
|
|
299
311
|
reviewIssueComment: function (args) {
|
|
300
|
-
if (!isSession) throw new Error("Only the Project Driver can review issue comments.");
|
|
312
|
+
if (!isSession && !canonicalClay) throw new Error("Only the Project Driver can review issue comments.");
|
|
301
313
|
var state = current(); var target = openStore(state.status); var entry = visibleIssue(target, args && args.ref, state);
|
|
314
|
+
expected(entry, args);
|
|
302
315
|
if (args.action === "incorporate") {
|
|
303
316
|
if (!Number.isInteger(args.expectedRevision)) throw new Error("expectedRevision is required to incorporate a comment.");
|
|
304
317
|
var data = cleanPublic(args, { ref: true, expectedRevision: true, commentId: true, action: true, response: true, commitSha: true }, true);
|
|
305
318
|
if (data.status === "resolved") throw new Error("Use update_issue with verified commit evidence before resolving this Issue.");
|
|
306
|
-
return target.incorporate(entry.ref, args.commentId, data, args.expectedRevision, args.response,
|
|
319
|
+
return target.incorporate(entry.ref, args.commentId, data, args.expectedRevision, args.response, actor(state));
|
|
307
320
|
}
|
|
308
|
-
return target.review(entry.ref, args.commentId, args.action, args.response,
|
|
321
|
+
return target.review(entry.ref, args.commentId, args.action, args.response, actor(state));
|
|
309
322
|
},
|
|
310
323
|
removeIssue: function (args) {
|
|
311
324
|
var state = current();
|
|
312
325
|
if (!state.principal.isProjectOwner) throw new Error("Only the project owner can delete Issues.");
|
|
313
326
|
var target = openStore(state.status); var entry = visibleIssue(target, args && args.ref, state);
|
|
314
327
|
if (!Number.isInteger(args && args.expectedRevision) || entry.revision !== args.expectedRevision) throw new Error("Issue revision conflict.");
|
|
315
|
-
return target.remove(entry.ref, author(state.user, "user", null), args.expectedRevision);
|
|
328
|
+
return target.remove(entry.ref, canonicalClay ? actor(state) : author(state.user, "user", null), args.expectedRevision);
|
|
316
329
|
},
|
|
317
330
|
createIssue: function (args) {
|
|
318
|
-
if (!isSession) throw new Error("People cannot create issues directly. Ask the Project Driver to create one.");
|
|
331
|
+
if (!isSession && !canonicalClay) throw new Error("People cannot create issues directly. Ask the Project Driver to create one.");
|
|
319
332
|
var data = cleanPublic(args, { commitSha: true }, true);
|
|
320
333
|
var status = data.status === undefined ? "open" : data.status;
|
|
321
334
|
if (schema.STATUSES.indexOf(status) === -1) throw new Error("Invalid status.");
|
|
322
335
|
var state = current();
|
|
323
336
|
if (status !== "resolved") return Promise.resolve(openStore(state.status).create(data,
|
|
324
|
-
|
|
337
|
+
actor(state), issueContext(state.status)));
|
|
325
338
|
if (typeof data.resolutionSummary !== "string" || !data.resolutionSummary.trim()) throw new Error("resolutionSummary is required when resolving an issue.");
|
|
326
339
|
var before = operation(state, true);
|
|
327
340
|
return evidence(before, args.commitSha).then(function (proof) {
|
|
@@ -340,7 +353,7 @@ function attachIssuesService(ctx) {
|
|
|
340
353
|
var existing = visibleIssue(target, ref, state);
|
|
341
354
|
if (existing.revision !== expectedRevision) throw new Error("Issue revision conflict.");
|
|
342
355
|
if (data.status !== "resolved") return Promise.resolve(target.update(ref, data, expectedRevision,
|
|
343
|
-
|
|
356
|
+
actor(state)));
|
|
344
357
|
if (typeof data.resolutionSummary !== "string" || !data.resolutionSummary.trim()) throw new Error("resolutionSummary is required when resolving an issue.");
|
|
345
358
|
var before = operation(state, true);
|
|
346
359
|
return evidence(before, args.commitSha).then(function (proof) {
|
|
@@ -354,6 +367,7 @@ function attachIssuesService(ctx) {
|
|
|
354
367
|
}
|
|
355
368
|
|
|
356
369
|
return { bindUser: function (source) { return bind(source, false); },
|
|
370
|
+
bindCanonicalClay: function (source) { return bind(source, false, true); },
|
|
357
371
|
bindProjectSession: function (source) { return bind(source, true); },
|
|
358
372
|
issueScopeIdForKnowledgeId: issuesStore.issueScopeIdForKnowledgeId };
|
|
359
373
|
}
|
package/lib/kiro-defaults.js
CHANGED
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// elsewhere. Consumed by the server-side vendor plumbing and sent to clients
|
|
3
|
-
// via the config state so the UI renders the right controls.
|
|
4
|
-
|
|
5
|
-
var KIRO_DEFAULTS = {
|
|
6
|
-
// Kiro CLI 2.18.1 exposes the next-generation agent as the v3 engine. Its
|
|
7
|
-
// general-purpose mode id is "vibe" (displayed as "Default").
|
|
8
|
-
engine: "v3",
|
|
9
|
-
mode: "vibe",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function getKiroConfig(sm) {
|
|
13
|
-
return {
|
|
14
|
-
engine: (sm && sm.kiroEngine) || KIRO_DEFAULTS.engine,
|
|
15
|
-
mode: (sm && sm.kiroMode) || KIRO_DEFAULTS.mode,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
KIRO_DEFAULTS: KIRO_DEFAULTS,
|
|
21
|
-
getKiroConfig: getKiroConfig,
|
|
22
|
-
};
|
|
1
|
+
module.exports = require("./yoke/options/kiro-defaults");
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var crypto = require("crypto");
|
|
2
|
+
var validation = require("./knowledge-db-validation");
|
|
3
|
+
var MAX_BATCH_ROWS = 100;
|
|
4
|
+
var MAX_BATCH_BYTES = 256 * 1024;
|
|
5
|
+
|
|
6
|
+
function fail(message, code) { throw Object.assign(new Error(message), { code: code || "INVALID" }); }
|
|
7
|
+
function json(value, depth) {
|
|
8
|
+
if (depth > 32) fail("Import JSON exceeds 32 nesting levels.");
|
|
9
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
10
|
+
if (typeof value === "number" && Number.isFinite(value)) return;
|
|
11
|
+
if (typeof value !== "object" || !value) fail("Import payload must contain finite JSON values.");
|
|
12
|
+
if (!Array.isArray(value)) validation.plainObject(value, "Import payload");
|
|
13
|
+
Object.keys(value).forEach(function (key) { json(value[key], depth + 1); });
|
|
14
|
+
}
|
|
15
|
+
function canonical(value) {
|
|
16
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
17
|
+
if (!value || typeof value !== "object") return value;
|
|
18
|
+
var out = {}; Object.keys(value).sort().forEach(function (key) { out[key] = canonical(value[key]); }); return out;
|
|
19
|
+
}
|
|
20
|
+
function identity(namespace, key) { return JSON.stringify([namespace, key]); }
|
|
21
|
+
function sourceText(value, label, max) {
|
|
22
|
+
var clean = validation.text(value, label, max, false);
|
|
23
|
+
if (clean !== value) fail(label + " must not have surrounding whitespace.");
|
|
24
|
+
return clean;
|
|
25
|
+
}
|
|
26
|
+
function batch(args) {
|
|
27
|
+
validation.plainObject(args, "Import request");
|
|
28
|
+
var namespace = sourceText(args.sourceNamespace, "Source namespace", 120);
|
|
29
|
+
if (!Array.isArray(args.rows) || !args.rows.length || args.rows.length > MAX_BATCH_ROWS) fail("Import batch must contain between 1 and " + MAX_BATCH_ROWS + " rows.", "IMPORT_BATCH_LIMIT");
|
|
30
|
+
json(args.rows, 0);
|
|
31
|
+
if (Buffer.byteLength(JSON.stringify(args.rows), "utf8") > MAX_BATCH_BYTES) fail("Import rows exceed the " + MAX_BATCH_BYTES + " byte input limit.", "IMPORT_BYTES_LIMIT");
|
|
32
|
+
var seen = new Set();
|
|
33
|
+
var rows = args.rows.map(function (row) {
|
|
34
|
+
validation.plainObject(row, "Import row");
|
|
35
|
+
var key = sourceText(row.sourceKey, "Source row key", 200);
|
|
36
|
+
if (seen.has(key)) fail("Import batch contains duplicate source row keys.");
|
|
37
|
+
seen.add(key);
|
|
38
|
+
if (!Object.prototype.hasOwnProperty.call(row, "sourcePayload")) fail("Each import row needs an immutable sourcePayload.");
|
|
39
|
+
validation.plainObject(row.values, "Import values");
|
|
40
|
+
var fingerprint = crypto.createHash("sha256").update(JSON.stringify(canonical({ sourcePayload: row.sourcePayload, values: row.values }))).digest("hex");
|
|
41
|
+
return { sourceKey: key, fingerprint: fingerprint, values: row.values };
|
|
42
|
+
});
|
|
43
|
+
return { sourceNamespace: namespace, rows: rows };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { batch: batch, identity: identity, sourceText: sourceText, MAX_BATCH_ROWS: MAX_BATCH_ROWS, MAX_BATCH_BYTES: MAX_BATCH_BYTES };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
var validation = require("./knowledge-db-validation");
|
|
2
|
+
var input = require("./knowledge-db-import-validation");
|
|
3
|
+
|
|
4
|
+
// importRecords({ databaseId, expectedDatabaseRevision, operationId, sourceNamespace,
|
|
5
|
+
// rows: [{ sourceKey, sourcePayload, values }], actor }) creates only unseen source rows.
|
|
6
|
+
// Both sourcePayload and supplied values are immutable import identity; corrections use CRUD.
|
|
7
|
+
// Record references can use sourceNamespace/sourceKey instead of id, including forward
|
|
8
|
+
// references within this batch. Cross-database sources must already be imported locally.
|
|
9
|
+
// readImportProgress({ databaseId, sourceNamespace, offset?, limit? }) returns native ids.
|
|
10
|
+
// Import mappings survive receipt expiry; replay with a fresh operation id and current revision.
|
|
11
|
+
// Limits: 100 rows / 256 KiB of row JSON per batch; shared 20000-record / 16 MiB
|
|
12
|
+
// aggregate bounds include archives, history, mappings and receipts (500 live / 24 hours).
|
|
13
|
+
// Each fresh operation retains one receipt and uses one canonical replacement; source
|
|
14
|
+
// replay preserves the revision. New rows also use the existing optional summary
|
|
15
|
+
// projection write. No filesystem input is accepted.
|
|
16
|
+
function attachKnowledgeDbImport(ctx) {
|
|
17
|
+
function mappings(database) { return database.importMappings || []; }
|
|
18
|
+
function index(database) {
|
|
19
|
+
var out = new Map(); mappings(database).forEach(function (mapping) { out.set(input.identity(mapping.sourceNamespace, mapping.sourceKey), mapping); }); return out;
|
|
20
|
+
}
|
|
21
|
+
function convert(values, schema, databases, next, planned) {
|
|
22
|
+
var fields = {}; schema.forEach(function (field) { fields[field.id] = field; });
|
|
23
|
+
var out = Object.assign({}, values);
|
|
24
|
+
Object.keys(out).forEach(function (fieldId) {
|
|
25
|
+
var field = fields[fieldId]; var value = out[fieldId];
|
|
26
|
+
if (!field || field.type !== "reference" || !value || !Object.prototype.hasOwnProperty.call(value, "sourceKey")) return;
|
|
27
|
+
validation.plainObject(value, "Import reference");
|
|
28
|
+
if (value.type !== "record" || value.id !== undefined) throw ctx.error("Source references require type record and no native id.", "INVALID");
|
|
29
|
+
var databaseId = validation.id(value.databaseId || field.targetDatabaseId || next._id, "reference database id");
|
|
30
|
+
var namespace = input.sourceText(value.sourceNamespace, "Reference source namespace", 120);
|
|
31
|
+
var key = input.sourceText(value.sourceKey, "Reference source key", 200);
|
|
32
|
+
var target = databases.find(function (database) { return database._id === databaseId; });
|
|
33
|
+
var mapping = databaseId === next._id ? planned.get(input.identity(namespace, key)) : target && index(target).get(input.identity(namespace, key));
|
|
34
|
+
if (!mapping) throw ctx.error("Reference source row is outside this Mate or has not been imported.", "FORBIDDEN_REFERENCE");
|
|
35
|
+
out[fieldId] = { type: "record", databaseId: databaseId, id: mapping.recordId };
|
|
36
|
+
});
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function importRecords(args) {
|
|
40
|
+
// Snapshot caller-owned JSON before any asynchronous operation.
|
|
41
|
+
var revalidate = args.revalidate; var batch = ctx.clone(input.batch(args)); args = ctx.clone(args); args.revalidate = revalidate;
|
|
42
|
+
var operationId = ctx.mutationId(args.operationId); var fingerprint = ctx.fingerprint("import", args);
|
|
43
|
+
return ctx.serialize(args.databaseId, function () { return ctx.load(args.databaseId).then(async function (current) {
|
|
44
|
+
ctx.guard(args, current);
|
|
45
|
+
var duplicate = ctx.receipt(current, operationId, fingerprint);
|
|
46
|
+
if (duplicate) return Object.assign({}, duplicate, { projectionPending: current.projectionPending === true, projectionError: current.projectionError || null });
|
|
47
|
+
if (current.archivedAt) throw ctx.error("Knowledge database is archived.", "ARCHIVED", current.revision);
|
|
48
|
+
ctx.ensureRevision(args.expectedDatabaseRevision, current.revision, "database");
|
|
49
|
+
var next = ctx.clone(current); var planned = index(current); var added = [];
|
|
50
|
+
var resultMappings = batch.rows.map(function (row) {
|
|
51
|
+
var identity = input.identity(batch.sourceNamespace, row.sourceKey); var existing = planned.get(identity);
|
|
52
|
+
if (existing && existing.fingerprint !== row.fingerprint) throw ctx.error("Source row was already imported with a different payload: " + row.sourceKey, "IMPORT_SOURCE_CONFLICT");
|
|
53
|
+
if (existing) return Object.assign({}, existing, { created: false });
|
|
54
|
+
var mapping = { sourceNamespace: batch.sourceNamespace, sourceKey: row.sourceKey, fingerprint: row.fingerprint, recordId: ctx.newId("rec") };
|
|
55
|
+
planned.set(identity, mapping); added.push({ row: row, mapping: mapping });
|
|
56
|
+
return Object.assign({}, mapping, { created: true });
|
|
57
|
+
});
|
|
58
|
+
if (next.records.length + added.length > validation.MAX_RECORDS) throw ctx.error("Database exceeds the " + validation.MAX_RECORDS + " record limit (including archived records).", "LIMIT");
|
|
59
|
+
var result = { databaseId: current._id, databaseRevision: current.revision, sourceNamespace: batch.sourceNamespace, createdCount: added.length, replayedCount: batch.rows.length - added.length, mappings: resultMappings };
|
|
60
|
+
// Remember fresh replay operations too, so reusing an operation id with another
|
|
61
|
+
// payload still conflicts. Preserve record values, archives and database revision.
|
|
62
|
+
if (!added.length) {
|
|
63
|
+
ctx.remember(next, operationId, fingerprint, result);
|
|
64
|
+
await ctx.replace(current, next, args);
|
|
65
|
+
return Object.assign(result, { projectionPending: current.projectionPending === true, projectionError: current.projectionError || null });
|
|
66
|
+
}
|
|
67
|
+
var databases = await ctx.all(); ctx.guard(args, current); var at = Date.now(); var who = ctx.actor(args.actor);
|
|
68
|
+
added.forEach(function (item) {
|
|
69
|
+
next.records.push({ id: item.mapping.recordId, revision: 1, values: {}, archivedAt: null, createdAt: at, updatedAt: at, createdBy: who, updatedBy: who });
|
|
70
|
+
next.recordHistory[item.mapping.recordId] = [];
|
|
71
|
+
});
|
|
72
|
+
var localDatabases = databases.filter(function (database) { return database._id !== next._id; }).concat([next]);
|
|
73
|
+
var context = ctx.referenceContext(localDatabases, next);
|
|
74
|
+
added.forEach(function (item, position) {
|
|
75
|
+
var values = convert(item.row.values, current.schema, databases, next, planned);
|
|
76
|
+
next.records[current.records.length + position].values = validation.normalizeValues(values, current.schema, context, null);
|
|
77
|
+
});
|
|
78
|
+
next.importMappings = mappings(next).concat(added.map(function (item) { return item.mapping; }));
|
|
79
|
+
next.revision++; next.updatedAt = at; next.updatedBy = who; next.projectionPending = true; next.projectionError = null;
|
|
80
|
+
result.databaseRevision = next.revision;
|
|
81
|
+
ctx.remember(next, operationId, fingerprint, result);
|
|
82
|
+
await ctx.replace(current, next, args);
|
|
83
|
+
return ctx.finish(next, result, who);
|
|
84
|
+
}); });
|
|
85
|
+
}
|
|
86
|
+
function readImportProgress(args) {
|
|
87
|
+
var namespace = input.sourceText(args.sourceNamespace, "Source namespace", 120);
|
|
88
|
+
var offset = args.offset === undefined ? 0 : args.offset; var limit = args.limit === undefined ? 100 : args.limit;
|
|
89
|
+
if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 100) throw ctx.error("Import progress requires a nonnegative integer offset and limit between 1 and 100.", "INVALID");
|
|
90
|
+
return ctx.load(args.databaseId).then(function (database) {
|
|
91
|
+
var rows = mappings(database).filter(function (mapping) { return mapping.sourceNamespace === namespace; });
|
|
92
|
+
rows.sort(function (a, b) { return a.sourceKey === b.sourceKey ? 0 : a.sourceKey < b.sourceKey ? -1 : 1; });
|
|
93
|
+
return { databaseId: database._id, databaseRevision: database.revision, sourceNamespace: namespace, total: rows.length, mappings: ctx.clone(rows.slice(offset, offset + limit)), nextOffset: offset + limit < rows.length ? offset + limit : null };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { importRecords: importRecords, readImportProgress: readImportProgress };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = { attachKnowledgeDbImport: attachKnowledgeDbImport, MAX_BATCH_ROWS: input.MAX_BATCH_ROWS, MAX_BATCH_BYTES: input.MAX_BATCH_BYTES };
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
var crypto = require("crypto");
|
|
2
|
-
var fs = require("fs");
|
|
3
|
-
var path = require("path");
|
|
4
|
-
var Datastore = require("@seald-io/nedb");
|
|
5
2
|
var validation = require("./knowledge-db-validation");
|
|
3
|
+
var imports = require("./knowledge-db-import");
|
|
4
|
+
var storageModule = require("./knowledge-db-storage");
|
|
6
5
|
|
|
7
6
|
var MAX_HISTORY = 50;
|
|
8
7
|
var MAX_RECEIPTS = 500;
|
|
9
8
|
var MAX_PAGE = 100;
|
|
10
9
|
var MAX_FILTERS = 20;
|
|
11
10
|
var MAX_SORTS = 10;
|
|
12
|
-
|
|
11
|
+
// Synthetic imports use 8.36 MB for 8563 narrative rows and exceed 8 MiB at
|
|
12
|
+
// 11400 short rows. Keep finite headroom for the 20000-row envelope and history.
|
|
13
|
+
var MAX_AGGREGATE_BYTES = 16 * 1024 * 1024;
|
|
13
14
|
var IDEMPOTENCY_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
14
|
-
var STORE_REGISTRY = {};
|
|
15
15
|
|
|
16
16
|
function clone(value) { return JSON.parse(JSON.stringify(value)); }
|
|
17
17
|
function error(message, code, currentRevision) { return Object.assign(new Error(message), { code: code || "FAILED", currentRevision: currentRevision }); }
|
|
@@ -30,7 +30,7 @@ function canonical(value) {
|
|
|
30
30
|
if (!value || typeof value !== "object") return value;
|
|
31
31
|
var out = {}; Object.keys(value).sort().forEach(function (key) { out[key] = canonical(value[key]); }); return out;
|
|
32
32
|
}
|
|
33
|
-
function fingerprint(label, args) { var envelope = Object.assign({}, args); delete envelope.actor; delete envelope.operationId; return crypto.createHash("sha256").update(label + ":" + JSON.stringify(canonical(envelope))).digest("hex"); }
|
|
33
|
+
function fingerprint(label, args) { var envelope = Object.assign({}, args); delete envelope.actor; delete envelope.operationId; delete envelope.revalidate; return crypto.createHash("sha256").update(label + ":" + JSON.stringify(canonical(envelope))).digest("hex"); }
|
|
34
34
|
function ensureRevision(value, current, label) {
|
|
35
35
|
if (!Number.isInteger(value) || value < 1) throw error("A valid " + label + " revision is required.", "STALE", current);
|
|
36
36
|
if (value !== current) throw error("This " + label + " changed since it was opened.", "STALE", current);
|
|
@@ -107,49 +107,39 @@ function queryRecords(database, args, databases, settings) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function createKnowledgeDbService(options) {
|
|
110
|
-
var settings = options || {}; var
|
|
110
|
+
var settings = options || {}; var storage = storageModule.createKnowledgeDbStorage(settings);
|
|
111
111
|
var aggregateLimit = Number.isInteger(settings.maxAggregateBytes) && settings.maxAggregateBytes > 1024 ? Math.min(MAX_AGGREGATE_BYTES, settings.maxAggregateBytes) : MAX_AGGREGATE_BYTES;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
function open() {
|
|
120
|
-
if (entry.loading) return entry.loading;
|
|
121
|
-
entry.loading = Promise.resolve().then(function () { projectReal = fs.realpathSync(projectDir); safeDirectory(knowledgeDir, true); safeDirectory(dataDir, true); var stat; try { stat = fs.lstatSync(filename); } catch (cause) { if (cause.code !== "ENOENT") throw cause; }
|
|
122
|
-
if (stat && (!stat.isFile() || stat.isSymbolicLink())) throw error("Knowledge database file is unsafe.", "UNSAFE_PATH"); entry.database = new Datastore({ filename: filename }); return entry.database.loadDatabaseAsync(); }).then(function () { return entry.database.ensureIndexAsync({ fieldName: "_id", unique: true }); }).then(function () { entry.ready = true; return entry.database; }).catch(function (cause) { entry.database = null; entry.loading = null; entry.ready = false; throw cause; });
|
|
123
|
-
return entry.loading;
|
|
124
|
-
}
|
|
125
|
-
function serialize(key, work) { var previous = entry.queues[key] || Promise.resolve(); var next = previous.catch(function () {}).then(work); entry.queues[key] = next.catch(function () {}); return next; }
|
|
126
|
-
function load(id) { return open().then(function (store) { return store.findOneAsync({ _id: validation.id(id, "database id") }); }).then(function (found) { if (!found) throw error("Knowledge database not found.", "NOT_FOUND"); return found; }); }
|
|
127
|
-
function all() { return open().then(function (store) { return store.findAsync({}); }); }
|
|
128
|
-
function listCached(args) { var items = entry.database ? entry.database.getAllData() : []; return { databases: items.filter(function (item) { return args && args.archived === true ? !!item.archivedAt : !item.archivedAt; }).sort(function (a, b) { return b.updatedAt - a.updatedAt || (a._id === b._id ? 0 : a._id < b._id ? -1 : 1); }).map(function (item) { return publicDatabase(item, false); }) }; }
|
|
129
|
-
function assertAggregate(next) { if (Buffer.byteLength(JSON.stringify(next), "utf8") > aggregateLimit) throw error("Knowledge database reached its storage limit.", "LIMIT"); }
|
|
130
|
-
function replace(current, next) { assertAggregate(next); return entry.database.updateAsync({ _id: current._id, revision: current.revision }, next, {}).then(function (result) { if (result.numAffected !== 1) throw error("This database changed during the operation.", "STALE", current.revision); return next; }); }
|
|
112
|
+
function serialize(key, work) { return storage.serialize(key, work); }
|
|
113
|
+
function load(id) { return storage.get(validation.id(id, "database id")).then(function (found) { if (!found) throw error("Knowledge database not found.", "NOT_FOUND"); return found; }); }
|
|
114
|
+
function all() { return storage.list(); }
|
|
115
|
+
function listCached(args) { var items = storage.getCached(); return { databases: items.filter(function (item) { return args && args.archived === true ? !!item.archivedAt : !item.archivedAt; }).sort(function (a, b) { return b.updatedAt - a.updatedAt || (a._id === b._id ? 0 : a._id < b._id ? -1 : 1); }).map(function (item) { return publicDatabase(item, false); }) }; }
|
|
116
|
+
function assertAggregate(next) { if (Buffer.byteLength(JSON.stringify(next), "utf8") > aggregateLimit) throw error("Knowledge database exceeds the " + aggregateLimit + " byte aggregate storage limit (records, history, mappings and live receipts).", "LIMIT"); }
|
|
117
|
+
function guard(args, current, phase) { if (args && typeof args.revalidate === 'function') args.revalidate(current ? publicDatabase(current, false) : null, phase); }
|
|
118
|
+
function replace(current, next, control) { assertAggregate(next); guard(control, current, 'saving'); return storage.replaceIfRevision(current._id, current.revision, next).then(function (replaced) { if (typeof replaced !== "boolean") throw storageModule.invalid("replaceIfRevision must resolve to a boolean."); if (!replaced) throw Object.assign(error("This database changed during the operation.", "STALE", current.revision), { persisted: false }); guard(control, null, "saved"); return next; }); }
|
|
131
119
|
function projectionContent(value) { return JSON.stringify({ schemaVersion: 1, namespace: "knowledge-database-summary", databaseId: value._id, name: value.name, archived: !!value.archivedAt, revision: value.revision, fields: value.schema.filter(function (field) { return !field.archived; }).map(function (field) { return { id: field.id, name: field.name, type: field.type }; }), views: (value.views || []).filter(function (view) { return !view.archived; }).map(function (view) { return { id: view.id, name: view.name, type: "table" }; }), recordCount: value.records.filter(function (record) { return !record.archivedAt; }).length }); }
|
|
132
120
|
function project(value, actorValue) {
|
|
133
121
|
if (typeof settings.projectSummary !== "function") return Promise.resolve({ pending: false });
|
|
134
122
|
return Promise.resolve().then(function () { return settings.projectSummary({ databaseId: value._id, name: value.name, revision: value.revision, archived: !!value.archivedAt, content: projectionContent(value), actor: actorValue }); }).then(function () {
|
|
135
|
-
var clean = clone(value); clean.projectionPending = false; clean.projectionError = null; return
|
|
136
|
-
}).catch(function (cause) { var message = cause.message || "Knowledge database summary projection failed."; var failed = clone(value); failed.projectionPending = true; failed.projectionError = message; return
|
|
123
|
+
var clean = clone(value); clean.projectionPending = false; clean.projectionError = null; return replace(value, clean).then(function () { return { pending: false }; });
|
|
124
|
+
}).catch(function (cause) { var message = cause.message || "Knowledge database summary projection failed."; var failed = clone(value); failed.projectionPending = true; failed.projectionError = message; return replace(value, failed).then(function () { return { pending: true, error: message }; }, function () { return { pending: true, error: message }; }); });
|
|
137
125
|
}
|
|
138
126
|
function finish(value, result, actorValue) { return project(value, actorValue).then(function (projection) { return Object.assign({}, result, { projectionPending: projection.pending, projectionError: projection.error || null }); }); }
|
|
139
127
|
function mutate(databaseId, args, label, fn) {
|
|
140
128
|
var operationId = mutationId(args.operationId); var operationFingerprint = fingerprint(label, args); return serialize(databaseId, function () { return load(databaseId).then(function (current) {
|
|
129
|
+
guard(args, current);
|
|
141
130
|
var duplicate = receipt(current, operationId, operationFingerprint); if (duplicate) return Object.assign({}, duplicate, { projectionPending: current.projectionPending === true, projectionError: current.projectionError || null });
|
|
142
131
|
if (current.archivedAt && label !== "database") throw error("Knowledge database is archived.", "ARCHIVED", current.revision);
|
|
143
|
-
var next = clone(current); var result = fn(next, current); next.projectionPending = true; next.projectionError = null; remember(next, operationId, operationFingerprint, result); return replace(current, next).then(function () { return finish(next, result, actor(args.actor)); });
|
|
132
|
+
var next = clone(current); var result = fn(next, current); next.projectionPending = true; next.projectionError = null; remember(next, operationId, operationFingerprint, result); return replace(current, next, args).then(function () { return finish(next, result, actor(args.actor)); });
|
|
144
133
|
}); });
|
|
145
134
|
}
|
|
146
135
|
function create(args) {
|
|
147
136
|
var input = args || {}; var operationId = mutationId(input.operationId); var operationFingerprint = fingerprint("create", input); return serialize("create:" + operationId, function () { return all().then(function (items) {
|
|
137
|
+
guard(input);
|
|
148
138
|
var duplicate; var duplicateDatabase; items.some(function (item) { duplicate = receipt(item, operationId, operationFingerprint); if (duplicate) duplicateDatabase = item; return !!duplicate; }); if (duplicate) return Object.assign({}, duplicate, { projectionPending: duplicateDatabase.projectionPending === true, projectionError: duplicateDatabase.projectionError || null });
|
|
149
139
|
var at = now(); var who = actor(input.actor); var titleId = newId("fld"); var schema = input.schema ? validation.normalizeSchema(input.schema) : validation.normalizeSchema([{ id: titleId, name: "Name", type: "title", required: true }]);
|
|
150
140
|
var viewId = newId("view"); var item = { _id: newId("db"), name: validation.text(input.name, "Database name", 120, false), revision: 1, archivedAt: null, schema: schema,
|
|
151
141
|
views: [{ id: viewId, name: "Table", type: "table", columns: schema.filter(function (field) { return !field.archived; }).map(function (field) { return field.id; }), filters: [], sorts: [], archived: false }], records: [], recordHistory: {}, databaseHistory: [], receipts: [], createdAt: at, updatedAt: at, createdBy: who, updatedBy: who, projectionPending: true, projectionError: null };
|
|
152
|
-
var result = { database: publicDatabase(item, false) }; remember(item, operationId, operationFingerprint, result); assertAggregate(item);
|
|
142
|
+
var result = { database: publicDatabase(item, false) }; remember(item, operationId, operationFingerprint, result); assertAggregate(item); guard(input, null, "saving"); return storage.insert(item).then(function () { guard(input, null, "saved"); return finish(item, result, who); });
|
|
153
143
|
}); });
|
|
154
144
|
}
|
|
155
145
|
function list(args) { return all().then(function (items) { return { databases: items.filter(function (item) { return args && args.archived === true ? !!item.archivedAt : !item.archivedAt; }).sort(function (a, b) { return b.updatedAt - a.updatedAt || (a._id === b._id ? 0 : a._id < b._id ? -1 : 1); }).map(function (item) { return publicDatabase(item, false); }) }; }); }
|
|
@@ -163,13 +153,14 @@ function createKnowledgeDbService(options) {
|
|
|
163
153
|
function updateSchema(args) { return mutate(args.databaseId, args, "schema", function (next, current) { ensureRevision(args.expectedRevision, current.revision, "database"); var schema = validation.normalizeSchema(args.schema); validation.assertCompatibleSchema(current.schema, schema, current.records); next.databaseHistory = next.databaseHistory.concat([{ revision: current.revision, at: now(), actor: actor(args.actor), action: "schema", schema: current.schema }]).slice(-MAX_HISTORY); next.schema = schema; next.views = reconcileViews(next.views, schema); next.revision++; next.updatedAt = now(); next.updatedBy = actor(args.actor); return { database: publicDatabase(next, false) }; }); }
|
|
164
154
|
function saveView(args) { return mutate(args.databaseId, args, "view", function (next, current) { ensureRevision(args.expectedRevision, current.revision, "database"); var view = validation.normalizeView(args.view, current.schema); var index = next.views.findIndex(function (entry) { return entry.id === view.id; }); if (index === -1 && next.views.length >= validation.MAX_VIEWS) throw error("Database has too many views.", "LIMIT"); if (index === -1) next.views.push(view); else next.views[index] = view; next.revision++; next.updatedAt = now(); next.updatedBy = actor(args.actor); return { database: publicDatabase(next, false), view: clone(view) }; }); }
|
|
165
155
|
function referenceContext(databases, current) { return { authorizeReference: function (reference) { if (reference.type === "text") return typeof settings.authorizeTextReference === "function" && settings.authorizeTextReference(reference.id) === true; var target = databases.filter(function (item) { return item._id === reference.databaseId; })[0]; return !!(target && target.records.some(function (record) { return record.id === reference.id; })); } }; }
|
|
166
|
-
function createRecord(args) { return all().then(function (databases) { return mutate(args.databaseId, args, "record", function (next, current) { ensureRevision(args.expectedDatabaseRevision, current.revision, "database"); if (next.records.length >= validation.MAX_RECORDS) throw error("Database
|
|
156
|
+
function createRecord(args) { return all().then(function (databases) { return mutate(args.databaseId, args, "record", function (next, current) { ensureRevision(args.expectedDatabaseRevision, current.revision, "database"); if (next.records.length >= validation.MAX_RECORDS) throw error("Database exceeds the " + validation.MAX_RECORDS + " record limit (including archived records).", "LIMIT"); var at = now(); var who = actor(args.actor); var record = { id: newId("rec"), revision: 1, values: validation.normalizeValues(args.values || {}, current.schema, referenceContext(databases, current), null), archivedAt: null, createdAt: at, updatedAt: at, createdBy: who, updatedBy: who }; next.records.push(record); next.recordHistory[record.id] = []; next.revision++; next.updatedAt = at; next.updatedBy = who; return { databaseRevision: next.revision, record: publicRecord(record) }; }); }); }
|
|
167
157
|
function updateRecord(args) { return all().then(function (databases) { return mutate(args.databaseId, args, "record", function (next, current) { var index = next.records.findIndex(function (record) { return record.id === validation.id(args.recordId, "record id"); }); if (index === -1) throw error("Knowledge database record not found.", "NOT_FOUND"); var before = next.records[index]; ensureRevision(args.expectedRevision, before.revision, "record"); if (before.archivedAt) throw error("Knowledge database record is archived.", "ARCHIVED", before.revision); next.recordHistory[before.id] = (next.recordHistory[before.id] || []).concat([{ revision: before.revision, at: now(), actor: actor(args.actor), action: "update", values: before.values }]).slice(-MAX_HISTORY); var record = clone(before); record.values = validation.normalizeValues(args.values || {}, current.schema, referenceContext(databases, current), record.values); record.revision++; record.updatedAt = now(); record.updatedBy = actor(args.actor); next.records[index] = record; next.revision++; next.updatedAt = record.updatedAt; next.updatedBy = record.updatedBy; return { databaseRevision: next.revision, record: publicRecord(record) }; }); }); }
|
|
168
158
|
function archiveRecord(args) { return mutate(args.databaseId, args, "record", function (next) { var index = next.records.findIndex(function (record) { return record.id === validation.id(args.recordId, "record id"); }); if (index === -1) throw error("Knowledge database record not found.", "NOT_FOUND"); var before = next.records[index]; ensureRevision(args.expectedRevision, before.revision, "record"); next.recordHistory[before.id] = (next.recordHistory[before.id] || []).concat([{ revision: before.revision, at: now(), actor: actor(args.actor), action: args.recover === true ? "recover" : "archive", values: before.values, archivedAt: before.archivedAt }]).slice(-MAX_HISTORY); var record = clone(before); record.archivedAt = args.recover === true ? null : now(); record.revision++; record.updatedAt = now(); record.updatedBy = actor(args.actor); next.records[index] = record; next.revision++; next.updatedAt = record.updatedAt; next.updatedBy = record.updatedBy; return { databaseRevision: next.revision, record: publicRecord(record) }; }); }
|
|
169
159
|
function readRecord(args) { return all().then(function (databases) { var item = databases.filter(function (entry) { return entry._id === validation.id(args.databaseId, "database id"); })[0]; if (!item) throw error("Knowledge database not found.", "NOT_FOUND"); var record = item.records.filter(function (entry) { return entry.id === validation.id(args.recordId, "record id"); })[0]; if (!record) throw error("Knowledge database record not found.", "NOT_FOUND"); return { database: publicDatabase(item, false), record: publicRecord(record, referenceStates(item, record, databases, settings)), title: titleValue(item, record), history: clone(item.recordHistory[record.id] || []) }; }); }
|
|
170
160
|
function query(args) { return all().then(function (databases) { var item = databases.filter(function (entry) { return entry._id === validation.id(args.databaseId, "database id"); })[0]; if (!item) throw error("Knowledge database not found.", "NOT_FOUND"); if (item.archivedAt) throw error("Knowledge database is archived.", "ARCHIVED", item.revision); return queryRecords(item, args, databases, settings); }); }
|
|
171
|
-
function retryProjection(args) { return serialize(args.databaseId, function () { return load(args.databaseId).then(function (item) { return finish(item, { database: publicDatabase(item, false) }, actor(args.actor)); }); }); }
|
|
172
|
-
|
|
161
|
+
function retryProjection(args) { return serialize(args.databaseId, function () { return load(args.databaseId).then(function (item) { guard(args, item, "saving"); return finish(item, { database: publicDatabase(item, false) }, actor(args.actor)).then(function (result) { guard(args, null, "saved"); return result; }); }); }); }
|
|
162
|
+
var importer = imports.attachKnowledgeDbImport({ clone: clone, error: error, actor: actor, newId: newId, mutationId: mutationId, fingerprint: fingerprint, ensureRevision: ensureRevision, receipt: receipt, remember: remember, serialize: serialize, load: load, all: all, referenceContext: referenceContext, replace: replace, guard: guard, finish: finish });
|
|
163
|
+
return { readStorageStatus: function () { return typeof storage.diagnostics === "function" ? storage.diagnostics() : Promise.resolve(null); }, importRecords: importer.importRecords, readImportProgress: importer.readImportProgress, hasStore: function () { return storage.hasStore(); }, isReady: function () { return storage.isReady(); }, initialize: function () { return Promise.resolve(storage.initialize()).then(function () {}); }, listCached: listCached, create: create, list: list, read: read, rename: rename, archive: archive, recover: function (args) { return archive(Object.assign({}, args, { recover: true })); }, updateSchema: updateSchema, saveView: saveView, createRecord: createRecord, updateRecord: updateRecord, archiveRecord: archiveRecord, recoverRecord: function (args) { return archiveRecord(Object.assign({}, args, { recover: true })); }, readRecord: readRecord, query: query, retryProjection: retryProjection };
|
|
173
164
|
}
|
|
174
165
|
|
|
175
166
|
module.exports = { createKnowledgeDbService: createKnowledgeDbService, MAX_HISTORY: MAX_HISTORY, MAX_PAGE: MAX_PAGE, MAX_AGGREGATE_BYTES: MAX_AGGREGATE_BYTES, IDEMPOTENCY_WINDOW_MS: IDEMPOTENCY_WINDOW_MS };
|