@vellumai/assistant 0.8.5 → 0.8.6
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/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
} from "../../conversations/message-consolidation.js";
|
|
27
27
|
import { createApprovalConversationGenerator } from "../../daemon/approval-generators.js";
|
|
28
28
|
import type { Conversation } from "../../daemon/conversation.js";
|
|
29
|
+
import { persistQueuedMessageBody } from "../../daemon/conversation-messaging.js";
|
|
29
30
|
import {
|
|
30
31
|
buildModelInfoEvent,
|
|
31
32
|
formatCleanResult,
|
|
@@ -75,6 +76,7 @@ import {
|
|
|
75
76
|
} from "../../memory/canonical-guardian-store.js";
|
|
76
77
|
import {
|
|
77
78
|
addMessage,
|
|
79
|
+
extractImageSourcePaths,
|
|
78
80
|
getConversation,
|
|
79
81
|
getMessages,
|
|
80
82
|
getMessagesPaginated,
|
|
@@ -82,8 +84,6 @@ import {
|
|
|
82
84
|
type MessageRow,
|
|
83
85
|
provenanceFromTrustContext,
|
|
84
86
|
setConversationInferenceProfile,
|
|
85
|
-
setConversationOriginChannelIfUnset,
|
|
86
|
-
setConversationOriginInterfaceIfUnset,
|
|
87
87
|
} from "../../memory/conversation-crud.js";
|
|
88
88
|
import {
|
|
89
89
|
getConversationByKey,
|
|
@@ -395,23 +395,10 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
395
395
|
// is not re-processed as a new user turn.
|
|
396
396
|
let messageId: string | undefined;
|
|
397
397
|
try {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
guardianImageSourcePaths[`${i}:${a.filename}`] = a.filePath;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
const channelMeta = {
|
|
406
|
-
userMessageChannel: sourceChannel,
|
|
407
|
-
assistantMessageChannel: sourceChannel,
|
|
408
|
-
userMessageInterface: sourceInterface,
|
|
409
|
-
assistantMessageInterface: sourceInterface,
|
|
410
|
-
provenanceTrustClass: "guardian" as const,
|
|
411
|
-
...(Object.keys(guardianImageSourcePaths).length > 0
|
|
412
|
-
? { imageSourcePaths: guardianImageSourcePaths }
|
|
413
|
-
: {}),
|
|
414
|
-
};
|
|
398
|
+
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
399
|
+
provenanceOverride: { provenanceTrustClass: "guardian" },
|
|
400
|
+
attachments,
|
|
401
|
+
});
|
|
415
402
|
|
|
416
403
|
const cleanUserMessage = createUserMessage(content, attachments);
|
|
417
404
|
const llmUserMessage = enrichMessageWithSourcePaths(
|
|
@@ -538,6 +525,10 @@ export function handleListMessages({
|
|
|
538
525
|
|
|
539
526
|
let rawMessages: MessageRow[];
|
|
540
527
|
let hasMore = false;
|
|
528
|
+
// Resume cursor surfaced when the paginated scan stops on its row cap with a
|
|
529
|
+
// (possibly empty) page — lets us still emit an oldest cursor so the client
|
|
530
|
+
// can request the next window instead of stalling.
|
|
531
|
+
let scanResumeCursor: { createdAt: number; id: string } | undefined;
|
|
541
532
|
|
|
542
533
|
// Drop messages flagged as hidden in metadata (e.g. internal scaffolding
|
|
543
534
|
// like retrospective instructions). The LLM-side history loader
|
|
@@ -561,6 +552,7 @@ export function handleListMessages({
|
|
|
561
552
|
);
|
|
562
553
|
rawMessages = result.messages;
|
|
563
554
|
hasMore = result.hasMore;
|
|
555
|
+
scanResumeCursor = result.nextCursor;
|
|
564
556
|
} else {
|
|
565
557
|
rawMessages = getMessages(resolvedConversationId).filter(visibleFilter);
|
|
566
558
|
}
|
|
@@ -755,10 +747,13 @@ export function handleListMessages({
|
|
|
755
747
|
|
|
756
748
|
// Align msgAttachments order with the file-block order captured by
|
|
757
749
|
// renderHistoryContent. When a file block was persisted with
|
|
758
|
-
// `_attachmentId
|
|
759
|
-
// (the `attachment:N` entries in contentOrder index into
|
|
760
|
-
// DB rows without a matching ref go to the tail as orphan
|
|
761
|
-
// unmatched refs drop their contentOrder entry and trigger a remap.
|
|
750
|
+
// `_attachmentId` (user-message uploads), we join on that id to position
|
|
751
|
+
// the chip inline (the `attachment:N` entries in contentOrder index into
|
|
752
|
+
// msgAttachments). DB rows without a matching ref go to the tail as orphan
|
|
753
|
+
// chips; unmatched refs drop their contentOrder entry and trigger a remap.
|
|
754
|
+
// Assistant-authored file blocks carry no `_attachmentId`, so when no ids
|
|
755
|
+
// match we fall back to positional alignment if the ref and row counts
|
|
756
|
+
// agree; otherwise we strip the markers and let chips fall to the tail.
|
|
762
757
|
let alignedContentOrder = m.contentOrder;
|
|
763
758
|
if (
|
|
764
759
|
m.attachmentRefs.length > 0 &&
|
|
@@ -809,14 +804,18 @@ export function handleListMessages({
|
|
|
809
804
|
: undefined;
|
|
810
805
|
})
|
|
811
806
|
.filter((e): e is string => e !== undefined);
|
|
812
|
-
} else {
|
|
813
|
-
// No
|
|
814
|
-
//
|
|
815
|
-
//
|
|
807
|
+
} else if (m.attachmentRefs.length !== msgAttachments.length) {
|
|
808
|
+
// No ref carried an attachmentId we could match and the counts
|
|
809
|
+
// disagree, so positional mapping can't be trusted — strip any
|
|
810
|
+
// attachment:N entries so the client doesn't position attachments
|
|
811
|
+
// inline against a misaligned array (they fall to the tail instead).
|
|
816
812
|
alignedContentOrder = m.contentOrder.filter(
|
|
817
813
|
(entry) => !ATTACHMENT_ENTRY_RE.test(entry),
|
|
818
814
|
);
|
|
819
815
|
}
|
|
816
|
+
// Otherwise no ref matched an id but the counts agree (the
|
|
817
|
+
// assistant-authored case): the Nth marker maps to the Nth row
|
|
818
|
+
// positionally, so the original contentOrder is left untouched.
|
|
820
819
|
} else if (m.attachmentRefs.length > 0 && msgAttachments.length === 0) {
|
|
821
820
|
// Refs were captured but no DB rows came back — drop the
|
|
822
821
|
// contentOrder entries to avoid out-of-bounds renders.
|
|
@@ -855,10 +854,16 @@ export function handleListMessages({
|
|
|
855
854
|
});
|
|
856
855
|
|
|
857
856
|
if (isPaginated) {
|
|
857
|
+
// Prefer the page's oldest visible row (the documented cursor semantic).
|
|
858
|
+
// When a scan-cap-truncated page comes back empty there's no visible row
|
|
859
|
+
// to anchor on, so fall back to the resume cursor so the client still gets
|
|
860
|
+
// a `(timestamp, id)` to continue paginating from instead of stalling.
|
|
858
861
|
const oldestTimestamp =
|
|
859
|
-
rawMessages.length > 0
|
|
862
|
+
rawMessages.length > 0
|
|
863
|
+
? rawMessages[0].createdAt
|
|
864
|
+
: scanResumeCursor?.createdAt;
|
|
860
865
|
const oldestMessageId =
|
|
861
|
-
rawMessages.length > 0 ? rawMessages[0].id :
|
|
866
|
+
rawMessages.length > 0 ? rawMessages[0].id : scanResumeCursor?.id;
|
|
862
867
|
// `page=latest` always emits both metadata fields so the web client has
|
|
863
868
|
// a stable contract; emit `null` when the conversation is empty.
|
|
864
869
|
// The existing `beforeTimestamp` branch keeps its conditional shape to
|
|
@@ -1002,13 +1007,15 @@ export async function handleSendMessage(
|
|
|
1002
1007
|
cohort?: string;
|
|
1003
1008
|
websiteUrl?: string;
|
|
1004
1009
|
contentSourceUrl?: string;
|
|
1010
|
+
bootstrapTemplate?: string;
|
|
1011
|
+
initialMessage?: string;
|
|
1012
|
+
skills?: string[];
|
|
1005
1013
|
};
|
|
1006
1014
|
};
|
|
1007
1015
|
|
|
1008
1016
|
const actorPrincipalId = headers?.["x-vellum-actor-principal-id"];
|
|
1009
1017
|
const principalType = headers?.["x-vellum-principal-type"];
|
|
1010
|
-
const originClientId =
|
|
1011
|
-
headers?.["x-vellum-client-id"]?.trim() || undefined;
|
|
1018
|
+
const originClientId = headers?.["x-vellum-client-id"]?.trim() || undefined;
|
|
1012
1019
|
|
|
1013
1020
|
const { conversationKey, content, attachmentIds } = body;
|
|
1014
1021
|
const inboundConversationId =
|
|
@@ -1149,10 +1156,13 @@ export async function handleSendMessage(
|
|
|
1149
1156
|
// assistant-minted internal id) when the client supplies it — clients
|
|
1150
1157
|
// must obtain this id from a prior daemon response, so a missing row
|
|
1151
1158
|
// is a 404. Otherwise fall through to the external-key path: the
|
|
1152
|
-
// client-supplied `conversationKey` (
|
|
1153
|
-
//
|
|
1154
|
-
// default
|
|
1155
|
-
//
|
|
1159
|
+
// client-supplied `conversationKey` (external-key lookup; materializes
|
|
1160
|
+
// on first use) or, when neither is provided, a channel-dependent
|
|
1161
|
+
// default. The vellum channel mints a fresh conversation on every
|
|
1162
|
+
// empty-handed send so first-message-of-a-new-chat surfaces with a
|
|
1163
|
+
// server-minted id; other channels (phone, slack, …) share a stable
|
|
1164
|
+
// `default:<channel>:<interface>` thread so repeated calls from the
|
|
1165
|
+
// same channel/interface stay co-located.
|
|
1156
1166
|
let mapping: {
|
|
1157
1167
|
conversationId: string;
|
|
1158
1168
|
conversationType: string;
|
|
@@ -1174,7 +1184,9 @@ export async function handleSendMessage(
|
|
|
1174
1184
|
const resolvedConversationKey =
|
|
1175
1185
|
conversationKey && conversationKey.length > 0
|
|
1176
1186
|
? conversationKey
|
|
1177
|
-
:
|
|
1187
|
+
: sourceChannel === "vellum"
|
|
1188
|
+
? crypto.randomUUID()
|
|
1189
|
+
: `default:${sourceChannel}:${sourceInterface}`;
|
|
1178
1190
|
mapping = getOrCreateConversation(resolvedConversationKey, {
|
|
1179
1191
|
conversationType: "standard",
|
|
1180
1192
|
});
|
|
@@ -1388,48 +1400,35 @@ export async function handleSendMessage(
|
|
|
1388
1400
|
: ("content-source" as const);
|
|
1389
1401
|
effectiveContent = buildScanFirstMessage(scanUrl, scanVariant);
|
|
1390
1402
|
// Fall through to normal inference path below
|
|
1391
|
-
} else if (isWakeUp && body.onboarding?.
|
|
1392
|
-
effectiveContent =
|
|
1393
|
-
// Fall through to normal inference path — the bootstrap template
|
|
1394
|
-
// and geo-writing skill handle this message.
|
|
1403
|
+
} else if (isWakeUp && body.onboarding?.initialMessage) {
|
|
1404
|
+
effectiveContent = body.onboarding.initialMessage;
|
|
1395
1405
|
} else if (isWakeUp) {
|
|
1396
1406
|
const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
|
|
1397
1407
|
|
|
1398
1408
|
conversation.processing = true;
|
|
1399
1409
|
let cleanupDeferred = false;
|
|
1400
1410
|
try {
|
|
1401
|
-
const
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1411
|
+
const rawContent = content ?? "";
|
|
1412
|
+
const attachments = hasAttachments
|
|
1413
|
+
? smDeps.resolveAttachments(attachmentIds)
|
|
1414
|
+
: [];
|
|
1415
|
+
const greetingMeta = {
|
|
1404
1416
|
userMessageChannel: sourceChannel,
|
|
1405
1417
|
assistantMessageChannel: sourceChannel,
|
|
1406
1418
|
userMessageInterface: sourceInterface,
|
|
1407
1419
|
assistantMessageInterface: sourceInterface,
|
|
1408
1420
|
};
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
:
|
|
1414
|
-
|
|
1415
|
-
const persisted = await addMessage(
|
|
1416
|
-
mapping.conversationId,
|
|
1417
|
-
"user",
|
|
1418
|
-
JSON.stringify(userMsg.content),
|
|
1419
|
-
channelMeta,
|
|
1420
|
-
);
|
|
1421
|
-
conversation.getMessages().push(userMsg);
|
|
1422
|
-
|
|
1423
|
-
setConversationOriginChannelIfUnset(
|
|
1424
|
-
mapping.conversationId,
|
|
1425
|
-
sourceChannel,
|
|
1426
|
-
);
|
|
1427
|
-
setConversationOriginInterfaceIfUnset(
|
|
1428
|
-
mapping.conversationId,
|
|
1429
|
-
sourceInterface,
|
|
1430
|
-
);
|
|
1421
|
+
const persisted = await persistQueuedMessageBody(conversation, {
|
|
1422
|
+
content: rawContent,
|
|
1423
|
+
attachments,
|
|
1424
|
+
requestId: crypto.randomUUID(),
|
|
1425
|
+
metadata: greetingMeta,
|
|
1426
|
+
});
|
|
1431
1427
|
|
|
1432
1428
|
const conversationId = mapping.conversationId;
|
|
1429
|
+
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
1430
|
+
trustContext: conversation.trustContext,
|
|
1431
|
+
});
|
|
1433
1432
|
|
|
1434
1433
|
const assistantMsg = createAssistantMessage(cannedGreeting);
|
|
1435
1434
|
const persistedAssistant = await addMessage(
|
|
@@ -1580,25 +1579,22 @@ export async function handleSendMessage(
|
|
|
1580
1579
|
if (conversation.isProcessing()) {
|
|
1581
1580
|
// Queue the message so it's processed when the current turn completes
|
|
1582
1581
|
const requestId = crypto.randomUUID();
|
|
1583
|
-
const enqueueResult = conversation.enqueueMessage(
|
|
1584
|
-
contentAfterScan,
|
|
1582
|
+
const enqueueResult = conversation.enqueueMessage({
|
|
1583
|
+
content: contentAfterScan,
|
|
1585
1584
|
attachments,
|
|
1586
|
-
broadcastMessage,
|
|
1585
|
+
onEvent: broadcastMessage,
|
|
1587
1586
|
requestId,
|
|
1588
|
-
|
|
1589
|
-
undefined, // currentPage
|
|
1590
|
-
{
|
|
1587
|
+
metadata: {
|
|
1591
1588
|
userMessageChannel: sourceChannel,
|
|
1592
1589
|
assistantMessageChannel: sourceChannel,
|
|
1593
1590
|
userMessageInterface: sourceInterface,
|
|
1594
1591
|
assistantMessageInterface: sourceInterface,
|
|
1595
1592
|
...(body.automated === true ? { automated: true } : {}),
|
|
1596
1593
|
},
|
|
1597
|
-
|
|
1598
|
-
undefined, // displayContent
|
|
1594
|
+
isInteractive,
|
|
1599
1595
|
transport,
|
|
1600
1596
|
clientMessageId,
|
|
1601
|
-
);
|
|
1597
|
+
});
|
|
1602
1598
|
if (enqueueResult.rejected) {
|
|
1603
1599
|
return new RouteResponse(
|
|
1604
1600
|
JSON.stringify({ accepted: false, error: "queue_full" }),
|
|
@@ -1718,35 +1714,31 @@ export async function handleSendMessage(
|
|
|
1718
1714
|
conversation.processing = true;
|
|
1719
1715
|
let cleanupDeferred = false;
|
|
1720
1716
|
try {
|
|
1721
|
-
const
|
|
1722
|
-
const imageSourcePaths: Record<string, string> = {};
|
|
1723
|
-
for (let i = 0; i < attachments.length; i++) {
|
|
1724
|
-
const a = attachments[i];
|
|
1725
|
-
if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
|
|
1726
|
-
imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
const channelMeta = {
|
|
1730
|
-
...provenance,
|
|
1717
|
+
const slashMeta = {
|
|
1731
1718
|
userMessageChannel: sourceChannel,
|
|
1732
1719
|
assistantMessageChannel: sourceChannel,
|
|
1733
1720
|
userMessageInterface: sourceInterface,
|
|
1734
1721
|
assistantMessageInterface: sourceInterface,
|
|
1735
1722
|
...(body.automated === true ? { automated: true } : {}),
|
|
1736
|
-
...(Object.keys(imageSourcePaths).length > 0
|
|
1737
|
-
? { imageSourcePaths }
|
|
1738
|
-
: {}),
|
|
1739
1723
|
};
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
)
|
|
1748
|
-
|
|
1724
|
+
const persisted = await persistQueuedMessageBody(conversation, {
|
|
1725
|
+
content: rawContent,
|
|
1726
|
+
attachments,
|
|
1727
|
+
requestId: crypto.randomUUID(),
|
|
1728
|
+
metadata: slashMeta,
|
|
1729
|
+
clientMessageId,
|
|
1730
|
+
});
|
|
1731
|
+
if (persisted.deduplicated) {
|
|
1732
|
+
return {
|
|
1733
|
+
accepted: true,
|
|
1734
|
+
messageId: persisted.id,
|
|
1735
|
+
conversationId: mapping.conversationId,
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1749
1738
|
|
|
1739
|
+
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
1740
|
+
trustContext: conversation.trustContext,
|
|
1741
|
+
});
|
|
1750
1742
|
const assistantMsg = createAssistantMessage(slashResult.message);
|
|
1751
1743
|
const persistedAssistant = await addMessage(
|
|
1752
1744
|
mapping.conversationId,
|
|
@@ -1756,15 +1748,6 @@ export async function handleSendMessage(
|
|
|
1756
1748
|
);
|
|
1757
1749
|
conversation.getMessages().push(assistantMsg);
|
|
1758
1750
|
|
|
1759
|
-
setConversationOriginChannelIfUnset(
|
|
1760
|
-
mapping.conversationId,
|
|
1761
|
-
sourceChannel,
|
|
1762
|
-
);
|
|
1763
|
-
setConversationOriginInterfaceIfUnset(
|
|
1764
|
-
mapping.conversationId,
|
|
1765
|
-
sourceInterface,
|
|
1766
|
-
);
|
|
1767
|
-
|
|
1768
1751
|
// Snapshot model info now so the deferred callback cannot observe
|
|
1769
1752
|
// a config change from a concurrent request.
|
|
1770
1753
|
const modelInfoEvent = isModelSlashCommand(rawContent)
|
|
@@ -1827,23 +1810,21 @@ export async function handleSendMessage(
|
|
|
1827
1810
|
|
|
1828
1811
|
if (slashResult.kind === "compact") {
|
|
1829
1812
|
conversation.processing = true;
|
|
1830
|
-
const
|
|
1831
|
-
const channelMeta = {
|
|
1832
|
-
...provenance,
|
|
1813
|
+
const slashMeta = {
|
|
1833
1814
|
userMessageChannel: sourceChannel,
|
|
1834
1815
|
assistantMessageChannel: sourceChannel,
|
|
1835
1816
|
userMessageInterface: sourceInterface,
|
|
1836
1817
|
assistantMessageInterface: sourceInterface,
|
|
1837
1818
|
};
|
|
1838
|
-
|
|
1839
|
-
let persisted: Awaited<ReturnType<typeof addMessage>>;
|
|
1819
|
+
let persisted: Awaited<ReturnType<typeof persistQueuedMessageBody>>;
|
|
1840
1820
|
try {
|
|
1841
|
-
persisted = await
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1821
|
+
persisted = await persistQueuedMessageBody(conversation, {
|
|
1822
|
+
content: rawContent,
|
|
1823
|
+
attachments,
|
|
1824
|
+
requestId: crypto.randomUUID(),
|
|
1825
|
+
metadata: slashMeta,
|
|
1826
|
+
clientMessageId,
|
|
1827
|
+
});
|
|
1847
1828
|
} catch (err) {
|
|
1848
1829
|
// The fire-and-forget compaction below owns clearing `processing`, but a
|
|
1849
1830
|
// throw from this initial persist never reaches it — reset here so the
|
|
@@ -1852,9 +1833,20 @@ export async function handleSendMessage(
|
|
|
1852
1833
|
silentlyWithLog(conversation.drainQueue(), "compact-command queue drain");
|
|
1853
1834
|
throw err;
|
|
1854
1835
|
}
|
|
1855
|
-
|
|
1836
|
+
if (persisted.deduplicated) {
|
|
1837
|
+
conversation.processing = false;
|
|
1838
|
+
silentlyWithLog(conversation.drainQueue(), "compact-dedup queue drain");
|
|
1839
|
+
return {
|
|
1840
|
+
accepted: true,
|
|
1841
|
+
messageId: persisted.id,
|
|
1842
|
+
conversationId: mapping.conversationId,
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1856
1845
|
|
|
1857
1846
|
const conversationId = mapping.conversationId;
|
|
1847
|
+
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
1848
|
+
trustContext: conversation.trustContext,
|
|
1849
|
+
});
|
|
1858
1850
|
|
|
1859
1851
|
// Fire-and-forget: return 202 immediately, run compaction async.
|
|
1860
1852
|
// forceCompact() makes an LLM call that can exceed the client's
|
|
@@ -1937,23 +1929,30 @@ export async function handleSendMessage(
|
|
|
1937
1929
|
// initial user-message persist below, which would otherwise leave the
|
|
1938
1930
|
// conversation stuck in queued mode indefinitely.
|
|
1939
1931
|
try {
|
|
1940
|
-
const
|
|
1941
|
-
const channelMeta = {
|
|
1942
|
-
...provenance,
|
|
1932
|
+
const slashMeta = {
|
|
1943
1933
|
userMessageChannel: sourceChannel,
|
|
1944
1934
|
assistantMessageChannel: sourceChannel,
|
|
1945
1935
|
userMessageInterface: sourceInterface,
|
|
1946
1936
|
assistantMessageInterface: sourceInterface,
|
|
1947
1937
|
};
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
);
|
|
1955
|
-
|
|
1938
|
+
const persisted = await persistQueuedMessageBody(conversation, {
|
|
1939
|
+
content: rawContent,
|
|
1940
|
+
attachments,
|
|
1941
|
+
requestId: crypto.randomUUID(),
|
|
1942
|
+
metadata: slashMeta,
|
|
1943
|
+
clientMessageId,
|
|
1944
|
+
});
|
|
1945
|
+
if (persisted.deduplicated) {
|
|
1946
|
+
return {
|
|
1947
|
+
accepted: true,
|
|
1948
|
+
messageId: persisted.id,
|
|
1949
|
+
conversationId,
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1956
1952
|
|
|
1953
|
+
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
1954
|
+
trustContext: conversation.trustContext,
|
|
1955
|
+
});
|
|
1957
1956
|
let assistantMessagePersisted = false;
|
|
1958
1957
|
try {
|
|
1959
1958
|
broadcastMessage({
|
|
@@ -2017,16 +2016,22 @@ export async function handleSendMessage(
|
|
|
2017
2016
|
const resolvedContent = slashResult.content;
|
|
2018
2017
|
|
|
2019
2018
|
const requestId = crypto.randomUUID();
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2019
|
+
const persistResult = await conversation.persistUserMessage({
|
|
2020
|
+
content: resolvedContent,
|
|
2021
|
+
attachments,
|
|
2022
|
+
requestId,
|
|
2023
|
+
metadata: body.automated === true ? { automated: true } : undefined,
|
|
2024
|
+
clientMessageId,
|
|
2025
|
+
});
|
|
2026
|
+
|
|
2027
|
+
const messageId = persistResult.id;
|
|
2028
|
+
|
|
2029
|
+
if (persistResult.deduplicated) {
|
|
2030
|
+
return {
|
|
2031
|
+
accepted: true,
|
|
2032
|
+
messageId,
|
|
2033
|
+
conversationId: mapping.conversationId,
|
|
2034
|
+
};
|
|
2030
2035
|
}
|
|
2031
2036
|
|
|
2032
2037
|
broadcastMessage({
|
|
@@ -2088,8 +2093,8 @@ async function generateLlmSuggestion(
|
|
|
2088
2093
|
"",
|
|
2089
2094
|
"CRITICAL — write from the USER'S perspective only, NEVER from the assistant's:",
|
|
2090
2095
|
"- The suggestion is what the USER will type into the chat input",
|
|
2091
|
-
|
|
2092
|
-
|
|
2096
|
+
'- Use first-person "I" only if the user has used it in their prior messages',
|
|
2097
|
+
'- NEVER start with phrases like "I can help", "Here\'s what", "Let me", "I\'d suggest" — those are assistant-voice',
|
|
2093
2098
|
"- Think: if you were the user reading the assistant's reply, what question or follow-up would you ask next?",
|
|
2094
2099
|
"",
|
|
2095
2100
|
"Output only the reply text inside the requested tags — no preamble, no commentary.",
|
|
@@ -2342,6 +2347,47 @@ function handleSearchConversations({
|
|
|
2342
2347
|
return { query, results };
|
|
2343
2348
|
}
|
|
2344
2349
|
|
|
2350
|
+
// ---------------------------------------------------------------------------
|
|
2351
|
+
// Metadata helpers
|
|
2352
|
+
// ---------------------------------------------------------------------------
|
|
2353
|
+
|
|
2354
|
+
/**
|
|
2355
|
+
* Assemble the standard channel metadata object for message persistence.
|
|
2356
|
+
*
|
|
2357
|
+
* Combines provenance (trust context), channel/interface routing, and
|
|
2358
|
+
* optional per-message fields (automated flag, image source paths) into the
|
|
2359
|
+
* Record that `addMessage` stores in the `metadata` column.
|
|
2360
|
+
*/
|
|
2361
|
+
function buildChannelMetadata(
|
|
2362
|
+
sourceChannel: string,
|
|
2363
|
+
sourceInterface: string,
|
|
2364
|
+
opts?: {
|
|
2365
|
+
trustContext?: Parameters<typeof provenanceFromTrustContext>[0];
|
|
2366
|
+
provenanceOverride?: Record<string, unknown>;
|
|
2367
|
+
automated?: boolean;
|
|
2368
|
+
attachments?: ReadonlyArray<{
|
|
2369
|
+
filename: string;
|
|
2370
|
+
mimeType: string;
|
|
2371
|
+
filePath?: string;
|
|
2372
|
+
}>;
|
|
2373
|
+
},
|
|
2374
|
+
): Record<string, unknown> {
|
|
2375
|
+
const provenance =
|
|
2376
|
+
opts?.provenanceOverride ?? provenanceFromTrustContext(opts?.trustContext);
|
|
2377
|
+
const imageSourcePaths = opts?.attachments
|
|
2378
|
+
? extractImageSourcePaths(opts.attachments)
|
|
2379
|
+
: undefined;
|
|
2380
|
+
return {
|
|
2381
|
+
...provenance,
|
|
2382
|
+
userMessageChannel: sourceChannel,
|
|
2383
|
+
assistantMessageChannel: sourceChannel,
|
|
2384
|
+
userMessageInterface: sourceInterface,
|
|
2385
|
+
assistantMessageInterface: sourceInterface,
|
|
2386
|
+
...(opts?.automated ? { automated: true } : {}),
|
|
2387
|
+
...(imageSourcePaths ? { imageSourcePaths } : {}),
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2345
2391
|
// ---------------------------------------------------------------------------
|
|
2346
2392
|
// Module-level state
|
|
2347
2393
|
// ---------------------------------------------------------------------------
|
|
@@ -2,7 +2,11 @@ import { eq } from "drizzle-orm";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import { getConfig } from "../../config/loader.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
addMessage,
|
|
7
|
+
createConversation,
|
|
8
|
+
type MessageRole,
|
|
9
|
+
} from "../../memory/conversation-crud.js";
|
|
6
10
|
import {
|
|
7
11
|
getConversationByKey,
|
|
8
12
|
setConversationKey,
|
|
@@ -22,7 +26,7 @@ const log = getLogger("conversations-import-routes");
|
|
|
22
26
|
// -- Types --
|
|
23
27
|
|
|
24
28
|
interface ImportMessage {
|
|
25
|
-
role:
|
|
29
|
+
role: MessageRole;
|
|
26
30
|
content: string | Array<{ type: string; text: string }>;
|
|
27
31
|
createdAt?: number;
|
|
28
32
|
}
|
|
@@ -59,7 +63,10 @@ function resolveTimestamps(conv: ImportConversation): {
|
|
|
59
63
|
// -- Handler --
|
|
60
64
|
|
|
61
65
|
async function handleConversationsImport({ body }: RouteHandlerArgs) {
|
|
62
|
-
if (
|
|
66
|
+
if (
|
|
67
|
+
!body ||
|
|
68
|
+
!Array.isArray((body as Record<string, unknown>).conversations)
|
|
69
|
+
) {
|
|
63
70
|
throw new BadRequestError("conversations array required");
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -70,7 +77,8 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
|
|
|
70
77
|
let imported = 0;
|
|
71
78
|
let skipped = 0;
|
|
72
79
|
let totalMessages = 0;
|
|
73
|
-
const errors: Array<{ index: number; sourceKey?: string; error: string }> =
|
|
80
|
+
const errors: Array<{ index: number; sourceKey?: string; error: string }> =
|
|
81
|
+
[];
|
|
74
82
|
|
|
75
83
|
for (let idx = 0; idx < payload.conversations.length; idx++) {
|
|
76
84
|
const conv = payload.conversations[idx];
|
|
@@ -90,7 +98,8 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
|
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
const { convCreatedAt, convUpdatedAt, messageTimestamps } =
|
|
101
|
+
const { convCreatedAt, convUpdatedAt, messageTimestamps } =
|
|
102
|
+
resolveTimestamps(conv);
|
|
94
103
|
|
|
95
104
|
const conversation = createConversation(conv.title);
|
|
96
105
|
|
|
@@ -122,7 +131,11 @@ async function handleConversationsImport({ body }: RouteHandlerArgs) {
|
|
|
122
131
|
.orderBy(messagesTable.createdAt)
|
|
123
132
|
.all();
|
|
124
133
|
|
|
125
|
-
for (
|
|
134
|
+
for (
|
|
135
|
+
let i = 0;
|
|
136
|
+
i < dbMessages.length && i < messageTimestamps.length;
|
|
137
|
+
i++
|
|
138
|
+
) {
|
|
126
139
|
db.update(messagesTable)
|
|
127
140
|
.set({ createdAt: messageTimestamps[i] })
|
|
128
141
|
.where(eq(messagesTable.id, dbMessages[i].id))
|
|
@@ -82,7 +82,16 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
82
82
|
},
|
|
83
83
|
],
|
|
84
84
|
responseBody: z.object({
|
|
85
|
-
documents: z.array(
|
|
85
|
+
documents: z.array(
|
|
86
|
+
z.object({
|
|
87
|
+
surfaceId: z.string(),
|
|
88
|
+
conversationId: z.string(),
|
|
89
|
+
title: z.string(),
|
|
90
|
+
wordCount: z.number(),
|
|
91
|
+
createdAt: z.number(),
|
|
92
|
+
updatedAt: z.number(),
|
|
93
|
+
}),
|
|
94
|
+
),
|
|
86
95
|
}),
|
|
87
96
|
handler: ({ queryParams }) => {
|
|
88
97
|
const conversationId = queryParams?.conversationId ?? undefined;
|
|
@@ -22,6 +22,13 @@ import { publishConversationListChanged } from "../sync/resource-sync-events.js"
|
|
|
22
22
|
import { BadRequestError, ForbiddenError, NotFoundError } from "./errors.js";
|
|
23
23
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
24
24
|
|
|
25
|
+
const groupSchema = z.object({
|
|
26
|
+
id: z.string(),
|
|
27
|
+
name: z.string(),
|
|
28
|
+
sortPosition: z.number(),
|
|
29
|
+
isSystemGroup: z.boolean(),
|
|
30
|
+
});
|
|
31
|
+
|
|
25
32
|
function serializeGroup(group: ReturnType<typeof getGroup>) {
|
|
26
33
|
if (!group) return null;
|
|
27
34
|
return {
|
|
@@ -169,6 +176,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
169
176
|
summary: "List groups",
|
|
170
177
|
description: "Return all conversation groups.",
|
|
171
178
|
tags: ["groups"],
|
|
179
|
+
responseBody: z.object({ groups: z.array(groupSchema) }),
|
|
172
180
|
},
|
|
173
181
|
{
|
|
174
182
|
operationId: "groups_create",
|
|
@@ -184,6 +192,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
184
192
|
requestBody: z.object({
|
|
185
193
|
name: z.string().describe("Group name"),
|
|
186
194
|
}),
|
|
195
|
+
responseBody: groupSchema,
|
|
187
196
|
additionalResponses: {
|
|
188
197
|
"400": {
|
|
189
198
|
description:
|
|
@@ -204,6 +213,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
204
213
|
name: z.string().optional(),
|
|
205
214
|
sortPosition: z.number().optional(),
|
|
206
215
|
}),
|
|
216
|
+
responseBody: groupSchema,
|
|
207
217
|
additionalResponses: {
|
|
208
218
|
"403": {
|
|
209
219
|
description: "System group sort position cannot be changed",
|
|
@@ -251,6 +261,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
251
261
|
)
|
|
252
262
|
.describe("Array of { groupId, sortPosition } objects"),
|
|
253
263
|
}),
|
|
264
|
+
responseBody: z.object({ ok: z.boolean() }),
|
|
254
265
|
additionalResponses: {
|
|
255
266
|
"403": {
|
|
256
267
|
description: "Cannot reorder system groups",
|