@vellumai/assistant 0.9.1-staging.1 → 0.10.0-staging.2
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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -26,6 +26,8 @@ mock.module("../../assistant-event-hub.js", () => ({
|
|
|
26
26
|
broadcastMessage: () => {},
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
|
+
import { eq } from "drizzle-orm";
|
|
30
|
+
|
|
29
31
|
import { getDb } from "../../../memory/db-connection.js";
|
|
30
32
|
import { initializeDb } from "../../../memory/db-init.js";
|
|
31
33
|
import { conversations } from "../../../memory/schema.js";
|
|
@@ -71,6 +73,10 @@ function findHandler(routes: RouteDefinition[], operationId: string) {
|
|
|
71
73
|
return route.handler;
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
const createHandler = findHandler(
|
|
77
|
+
CONVERSATION_MANAGEMENT_ROUTES,
|
|
78
|
+
"createConversation",
|
|
79
|
+
);
|
|
74
80
|
const putHandler = findHandler(
|
|
75
81
|
CONVERSATION_MANAGEMENT_ROUTES,
|
|
76
82
|
"setConversationInferenceProfile",
|
|
@@ -112,6 +118,67 @@ function seedConversation(id: string): void {
|
|
|
112
118
|
// Tests
|
|
113
119
|
// ---------------------------------------------------------------------------
|
|
114
120
|
|
|
121
|
+
describe("POST /v1/conversations (createConversation)", () => {
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
clearConversations();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
function readConversation(id: string) {
|
|
127
|
+
return getDb()
|
|
128
|
+
.select({
|
|
129
|
+
title: conversations.title,
|
|
130
|
+
isAutoTitle: conversations.isAutoTitle,
|
|
131
|
+
})
|
|
132
|
+
.from(conversations)
|
|
133
|
+
.where(eq(conversations.id, id))
|
|
134
|
+
.get();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
test("with a title → persists it as a user-set title (isAutoTitle = 0)", async () => {
|
|
138
|
+
const result = (await createHandler({
|
|
139
|
+
body: { conversationType: "standard", title: "Setting up your check-in" },
|
|
140
|
+
})) as { id: string; created: boolean };
|
|
141
|
+
|
|
142
|
+
expect(result.created).toBe(true);
|
|
143
|
+
const row = readConversation(result.id);
|
|
144
|
+
expect(row?.title).toBe("Setting up your check-in");
|
|
145
|
+
// isAutoTitle = 0 keeps the async LLM titler from overwriting it.
|
|
146
|
+
expect(row?.isAutoTitle).toBe(0);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("blank title falls back to the replaceable 'New Conversation' placeholder", async () => {
|
|
150
|
+
const result = (await createHandler({
|
|
151
|
+
body: { conversationType: "standard", title: " " },
|
|
152
|
+
})) as { id: string; created: boolean };
|
|
153
|
+
|
|
154
|
+
expect(result.created).toBe(true);
|
|
155
|
+
const row = readConversation(result.id);
|
|
156
|
+
expect(row?.title).toBe("New Conversation");
|
|
157
|
+
// Default auto-title flag (1) leaves it replaceable by the auto-titler.
|
|
158
|
+
expect(row?.isAutoTitle).toBe(1);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("no title → 'New Conversation' placeholder", async () => {
|
|
162
|
+
const result = (await createHandler({
|
|
163
|
+
body: { conversationType: "standard" },
|
|
164
|
+
})) as { id: string; created: boolean };
|
|
165
|
+
|
|
166
|
+
expect(result.created).toBe(true);
|
|
167
|
+
const row = readConversation(result.id);
|
|
168
|
+
expect(row?.title).toBe("New Conversation");
|
|
169
|
+
expect(row?.isAutoTitle).toBe(1);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("non-string title → BadRequestError (not a 500), no row created", () => {
|
|
173
|
+
// The shared route adapter doesn't runtime-validate the body, so the
|
|
174
|
+
// handler must reject a malformed title before `.trim()` throws.
|
|
175
|
+
expect(() =>
|
|
176
|
+
createHandler({ body: { conversationType: "standard", title: 123 } }),
|
|
177
|
+
).toThrow(/title must be a string/);
|
|
178
|
+
expect(getDb().select().from(conversations).all()).toHaveLength(0);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
115
182
|
describe("PUT /v1/conversations/:id/inference-profile", () => {
|
|
116
183
|
beforeEach(() => {
|
|
117
184
|
clearConversations();
|
|
@@ -75,7 +75,6 @@ import {
|
|
|
75
75
|
PluginMergeBaselineError,
|
|
76
76
|
PluginNotUpgradableError,
|
|
77
77
|
type PluginUpgradeResult,
|
|
78
|
-
PluginUpgradeStrategyUnsupportedError,
|
|
79
78
|
type UpgradePluginDeps,
|
|
80
79
|
type UpgradePluginOptions,
|
|
81
80
|
} from "../../../cli/lib/upgrade-plugin.js";
|
|
@@ -183,7 +182,6 @@ const upgradeSpy = mock(
|
|
|
183
182
|
mock.module("../../../cli/lib/upgrade-plugin.js", () => ({
|
|
184
183
|
PluginMergeBaselineError,
|
|
185
184
|
PluginNotUpgradableError,
|
|
186
|
-
PluginUpgradeStrategyUnsupportedError,
|
|
187
185
|
upgradePlugin: upgradeSpy,
|
|
188
186
|
}));
|
|
189
187
|
|
|
@@ -1004,6 +1002,10 @@ function inspection(
|
|
|
1004
1002
|
}
|
|
1005
1003
|
: overrides.remote,
|
|
1006
1004
|
remoteError: overrides.remoteError ?? null,
|
|
1005
|
+
surfaces:
|
|
1006
|
+
overrides.surfaces === undefined
|
|
1007
|
+
? { skills: [], hooks: ["post-model-call"], tools: [] }
|
|
1008
|
+
: overrides.surfaces,
|
|
1007
1009
|
};
|
|
1008
1010
|
}
|
|
1009
1011
|
|
|
@@ -1109,6 +1111,8 @@ function upgradeResult(
|
|
|
1109
1111
|
fileCount: overrides.fileCount === undefined ? 12 : overrides.fileCount,
|
|
1110
1112
|
dryRun: overrides.dryRun ?? false,
|
|
1111
1113
|
strategy: overrides.strategy ?? "overwrite",
|
|
1114
|
+
conflicts: overrides.conflicts ?? [],
|
|
1115
|
+
binaryConflicts: overrides.binaryConflicts ?? [],
|
|
1112
1116
|
provenanceWasUnknown: overrides.provenanceWasUnknown ?? false,
|
|
1113
1117
|
};
|
|
1114
1118
|
}
|
|
@@ -1124,6 +1128,8 @@ async function invokeUpgrade(args: RouteHandlerArgs = {}): Promise<{
|
|
|
1124
1128
|
fileCount: number | null;
|
|
1125
1129
|
dryRun: boolean;
|
|
1126
1130
|
strategy: string;
|
|
1131
|
+
conflicts: readonly string[];
|
|
1132
|
+
binaryConflicts: readonly string[];
|
|
1127
1133
|
provenanceWasUnknown: boolean;
|
|
1128
1134
|
}> {
|
|
1129
1135
|
return (await upgradeHandler(args)) as {
|
|
@@ -1137,6 +1143,8 @@ async function invokeUpgrade(args: RouteHandlerArgs = {}): Promise<{
|
|
|
1137
1143
|
fileCount: number | null;
|
|
1138
1144
|
dryRun: boolean;
|
|
1139
1145
|
strategy: string;
|
|
1146
|
+
conflicts: readonly string[];
|
|
1147
|
+
binaryConflicts: readonly string[];
|
|
1140
1148
|
provenanceWasUnknown: boolean;
|
|
1141
1149
|
};
|
|
1142
1150
|
}
|
|
@@ -1168,6 +1176,8 @@ describe("POST /v1/plugins/:name/upgrade", () => {
|
|
|
1168
1176
|
fileCount: 12,
|
|
1169
1177
|
dryRun: false,
|
|
1170
1178
|
strategy: "overwrite",
|
|
1179
|
+
conflicts: [],
|
|
1180
|
+
binaryConflicts: [],
|
|
1171
1181
|
provenanceWasUnknown: false,
|
|
1172
1182
|
});
|
|
1173
1183
|
// AND the name + dryRun are forwarded to the lib (strategy omitted)
|
|
@@ -1199,19 +1209,31 @@ describe("POST /v1/plugins/:name/upgrade", () => {
|
|
|
1199
1209
|
});
|
|
1200
1210
|
});
|
|
1201
1211
|
|
|
1202
|
-
test("
|
|
1203
|
-
//
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1212
|
+
test("projects conflicts + binaryConflicts from the assistant strategy onto the wire", async () => {
|
|
1213
|
+
// GIVEN upgradePlugin merges with conflict markers under `assistant`
|
|
1214
|
+
upgradeSpy.mockImplementation(async () =>
|
|
1215
|
+
upgradeResult({
|
|
1216
|
+
strategy: "assistant",
|
|
1217
|
+
conflicts: ["hooks/post-model-call.ts"],
|
|
1218
|
+
binaryConflicts: ["assets/icon.png"],
|
|
1219
|
+
}),
|
|
1220
|
+
);
|
|
1221
|
+
|
|
1222
|
+
// WHEN the handler runs with the `assistant` strategy
|
|
1223
|
+
const result = await invokeUpgrade({
|
|
1224
|
+
pathParams: { name: "level-up" },
|
|
1225
|
+
body: { strategy: "assistant" },
|
|
1207
1226
|
});
|
|
1208
1227
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1228
|
+
// THEN the conflicted paths are surfaced for the assistant to resolve
|
|
1229
|
+
expect(result.strategy).toBe("assistant");
|
|
1230
|
+
expect(result.conflicts).toEqual(["hooks/post-model-call.ts"]);
|
|
1231
|
+
expect(result.binaryConflicts).toEqual(["assets/icon.png"]);
|
|
1232
|
+
expect(upgradeSpy.mock.calls[0]?.[0]).toEqual({
|
|
1233
|
+
name: "level-up",
|
|
1234
|
+
dryRun: undefined,
|
|
1235
|
+
strategy: "assistant",
|
|
1236
|
+
});
|
|
1215
1237
|
});
|
|
1216
1238
|
|
|
1217
1239
|
test("PluginMergeBaselineError \u2192 ConflictError (409)", async () => {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
11
|
+
import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
|
|
11
12
|
import { getCdpClient } from "../../tools/browser/cdp-client/factory.js";
|
|
12
13
|
import {
|
|
13
14
|
clearPinnedTab,
|
|
@@ -51,6 +52,14 @@ async function handleBrowserTabs({ body = {} }: RouteHandlerArgs) {
|
|
|
51
52
|
|
|
52
53
|
const cdpOptions = { mode: "extension" as const, targetClientId };
|
|
53
54
|
|
|
55
|
+
// Every tabs command pins extension mode. Absorb a brief extension SSE
|
|
56
|
+
// reconnect blip so a flapping connection doesn't surface as a hard
|
|
57
|
+
// "no Chrome Extension connected" error.
|
|
58
|
+
await HostBrowserProxy.instance.waitForExtensionClient(
|
|
59
|
+
context.sourceActorPrincipalId,
|
|
60
|
+
targetClientId,
|
|
61
|
+
);
|
|
62
|
+
|
|
54
63
|
if (command === "list") {
|
|
55
64
|
const cdp = getCdpClient(context, cdpOptions);
|
|
56
65
|
try {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* clients polling prompts) observe the expired status directly.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import { withdrawGuardianRequestCards } from "../../approvals/guardian-card-withdrawal.js";
|
|
21
22
|
import {
|
|
22
23
|
listCanonicalGuardianRequests,
|
|
23
24
|
resolveCanonicalGuardianRequest,
|
|
@@ -42,7 +43,7 @@ let sweepInProgress = false;
|
|
|
42
43
|
* concurrent decision that wins the race is never overwritten by the
|
|
43
44
|
* sweep. Returns the count of requests transitioned to expired.
|
|
44
45
|
*/
|
|
45
|
-
function sweepExpiredCanonicalGuardianRequests(): number {
|
|
46
|
+
async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
|
|
46
47
|
const pending = listCanonicalGuardianRequests({ status: "pending" });
|
|
47
48
|
const now = Date.now();
|
|
48
49
|
let expiredCount = 0;
|
|
@@ -71,6 +72,14 @@ function sweepExpiredCanonicalGuardianRequests(): number {
|
|
|
71
72
|
},
|
|
72
73
|
"Expired canonical guardian request via sweep",
|
|
73
74
|
);
|
|
75
|
+
|
|
76
|
+
// Withdraw the now-stale approval cards on every surface. No origin
|
|
77
|
+
// channel — the expiry is system-driven, so all surfaces (including
|
|
78
|
+
// in-app) are withdrawn. Best-effort and non-throwing.
|
|
79
|
+
await withdrawGuardianRequestCards({
|
|
80
|
+
request: resolved,
|
|
81
|
+
status: "expired",
|
|
82
|
+
});
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
85
|
|
|
@@ -93,13 +102,13 @@ export function startCanonicalGuardianExpirySweep(): void {
|
|
|
93
102
|
sweepTimer = setInterval(() => {
|
|
94
103
|
if (sweepInProgress) return;
|
|
95
104
|
sweepInProgress = true;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
void sweepExpiredCanonicalGuardianRequests()
|
|
106
|
+
.catch((err) => {
|
|
107
|
+
log.error({ err }, "Canonical guardian expiry sweep failed");
|
|
108
|
+
})
|
|
109
|
+
.finally(() => {
|
|
110
|
+
sweepInProgress = false;
|
|
111
|
+
});
|
|
103
112
|
}, SWEEP_INTERVAL_MS);
|
|
104
113
|
}
|
|
105
114
|
|
|
@@ -12,6 +12,10 @@ import { isHttpAuthDisabled } from "../../config/env.js";
|
|
|
12
12
|
import { datesToISO } from "../../util/json.js";
|
|
13
13
|
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
14
14
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
15
|
+
import {
|
|
16
|
+
DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
17
|
+
isClientDegraded,
|
|
18
|
+
} from "../client-health.js";
|
|
15
19
|
import { NotFoundError } from "./errors.js";
|
|
16
20
|
import type { RouteDefinition } from "./types.js";
|
|
17
21
|
|
|
@@ -65,6 +69,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
65
69
|
c.actorPrincipalId === callerPrincipalId,
|
|
66
70
|
);
|
|
67
71
|
|
|
72
|
+
const now = new Date();
|
|
68
73
|
return {
|
|
69
74
|
clients: filtered.map((c) =>
|
|
70
75
|
datesToISO({
|
|
@@ -74,6 +79,11 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
74
79
|
machineName: c.machineName,
|
|
75
80
|
connectedAt: c.connectedAt,
|
|
76
81
|
lastActiveAt: c.lastActiveAt,
|
|
82
|
+
degraded: isClientDegraded(
|
|
83
|
+
c.lastActiveAt,
|
|
84
|
+
now,
|
|
85
|
+
DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
86
|
+
),
|
|
77
87
|
}),
|
|
78
88
|
),
|
|
79
89
|
};
|
|
@@ -50,6 +50,30 @@ function withGuardianNameOverride<
|
|
|
50
50
|
return contact;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** Adds `externalUserId` (= `address`) to each channel for older macOS clients. */
|
|
54
|
+
function withChannelCompat<T extends { channels: { address: string }[] }>(
|
|
55
|
+
contact: T,
|
|
56
|
+
): T {
|
|
57
|
+
return {
|
|
58
|
+
...contact,
|
|
59
|
+
channels: contact.channels.map((ch) => ({
|
|
60
|
+
...ch,
|
|
61
|
+
externalUserId: ch.address,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Compose both response transforms (guardian display name + channel compat). */
|
|
67
|
+
function prepareContactResponse<
|
|
68
|
+
T extends {
|
|
69
|
+
role: string;
|
|
70
|
+
displayName: string;
|
|
71
|
+
channels: { address: string }[];
|
|
72
|
+
},
|
|
73
|
+
>(contact: T): T {
|
|
74
|
+
return withChannelCompat(withGuardianNameOverride(contact));
|
|
75
|
+
}
|
|
76
|
+
|
|
53
77
|
const VALID_CONTACT_TYPES: readonly ContactType[] = ["human", "assistant"];
|
|
54
78
|
|
|
55
79
|
const VALID_CHANNEL_STATUSES: readonly ChannelStatus[] = [
|
|
@@ -87,6 +111,7 @@ const contactChannelSchema = z.object({
|
|
|
87
111
|
type: z.string(),
|
|
88
112
|
address: z.string(),
|
|
89
113
|
isPrimary: z.boolean(),
|
|
114
|
+
/** @deprecated Echoes `address` for backwards compatibility with older macOS clients. */
|
|
90
115
|
externalUserId: z.string().nullable(),
|
|
91
116
|
status: z.string(),
|
|
92
117
|
policy: z.string(),
|
|
@@ -143,14 +168,14 @@ function handleListContacts(queryParams: Record<string, string>) {
|
|
|
143
168
|
});
|
|
144
169
|
return {
|
|
145
170
|
ok: true,
|
|
146
|
-
contacts: contacts.map(
|
|
171
|
+
contacts: contacts.map(prepareContactResponse),
|
|
147
172
|
};
|
|
148
173
|
}
|
|
149
174
|
|
|
150
175
|
const contacts = listContacts(limit, role, contactType);
|
|
151
176
|
return {
|
|
152
177
|
ok: true,
|
|
153
|
-
contacts: contacts.map(
|
|
178
|
+
contacts: contacts.map(prepareContactResponse),
|
|
154
179
|
};
|
|
155
180
|
}
|
|
156
181
|
|
|
@@ -165,7 +190,7 @@ function handleGetContact(contactId: string) {
|
|
|
165
190
|
: undefined;
|
|
166
191
|
return {
|
|
167
192
|
ok: true,
|
|
168
|
-
contact:
|
|
193
|
+
contact: prepareContactResponse(contact),
|
|
169
194
|
assistantMetadata: assistantMeta ?? undefined,
|
|
170
195
|
};
|
|
171
196
|
}
|
|
@@ -508,7 +533,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
508
533
|
limit: z.number().optional(),
|
|
509
534
|
})
|
|
510
535
|
.parse(body);
|
|
511
|
-
return searchContacts(parsed);
|
|
536
|
+
return searchContacts(parsed).map(prepareContactResponse);
|
|
512
537
|
},
|
|
513
538
|
},
|
|
514
539
|
|
|
@@ -604,7 +629,7 @@ function handleMergeContactsRoute(args: RouteHandlerArgs) {
|
|
|
604
629
|
|
|
605
630
|
try {
|
|
606
631
|
const contact = mergeContacts(keepId, mergeId);
|
|
607
|
-
return { ok: true, contact:
|
|
632
|
+
return { ok: true, contact: prepareContactResponse(contact) };
|
|
608
633
|
} catch (err) {
|
|
609
634
|
const message = err instanceof Error ? err.message : String(err);
|
|
610
635
|
throw new BadRequestError(message);
|
|
@@ -667,8 +692,6 @@ function handleUpdateContactChannelRoute(args: RouteHandlerArgs) {
|
|
|
667
692
|
const parentContact = getContact(updated.contactId);
|
|
668
693
|
return {
|
|
669
694
|
ok: true,
|
|
670
|
-
contact: parentContact
|
|
671
|
-
? withGuardianNameOverride(parentContact)
|
|
672
|
-
: undefined,
|
|
695
|
+
contact: parentContact ? prepareContactResponse(parentContact) : undefined,
|
|
673
696
|
};
|
|
674
697
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
cancelGeneration,
|
|
27
27
|
clearAllConversations,
|
|
28
28
|
regenerateResponse,
|
|
29
|
+
resolveMetaSlashCommand,
|
|
29
30
|
switchConversation,
|
|
30
31
|
undoLastMessage,
|
|
31
32
|
} from "../../daemon/handlers/conversations.js";
|
|
@@ -93,11 +94,26 @@ function cancelScheduleIfLast(conversationId: string): void {
|
|
|
93
94
|
function handleCreateConversation({ body = {}, headers }: RouteHandlerArgs) {
|
|
94
95
|
const conversationKey =
|
|
95
96
|
(body.conversationKey as string | undefined) ?? crypto.randomUUID();
|
|
97
|
+
// The shared route adapter does not runtime-validate the body against the
|
|
98
|
+
// Zod requestBody (it's codegen-only), so guard the type before trimming —
|
|
99
|
+
// a malformed `{ title: 123 }` would otherwise throw on `.trim()` and 500.
|
|
100
|
+
if (body.title !== undefined && typeof body.title !== "string") {
|
|
101
|
+
throw new BadRequestError("title must be a string");
|
|
102
|
+
}
|
|
103
|
+
const customTitle = body.title?.trim() || undefined;
|
|
96
104
|
const result = getOrCreateConversation(conversationKey, {
|
|
97
105
|
conversationType: "standard",
|
|
98
106
|
});
|
|
99
107
|
if (result.created) {
|
|
100
|
-
|
|
108
|
+
// A caller-supplied title is user-set: persist it with isAutoTitle = 0 so
|
|
109
|
+
// the async LLM titler's safe-overwrite check leaves it untouched. Without
|
|
110
|
+
// one, fall back to the neutral "New Conversation" placeholder, which stays
|
|
111
|
+
// replaceable by the auto-titler once messages arrive.
|
|
112
|
+
if (customTitle) {
|
|
113
|
+
updateConversationTitle(result.conversationId, customTitle, 0);
|
|
114
|
+
} else {
|
|
115
|
+
updateConversationTitle(result.conversationId, "New Conversation");
|
|
116
|
+
}
|
|
101
117
|
publishConversationListAndMetadataChanged(
|
|
102
118
|
"created",
|
|
103
119
|
result.conversationId,
|
|
@@ -452,6 +468,29 @@ async function handleUndoLastMessage({ pathParams = {} }: RouteHandlerArgs) {
|
|
|
452
468
|
};
|
|
453
469
|
}
|
|
454
470
|
|
|
471
|
+
async function handleResolveMetaSlashCommand({
|
|
472
|
+
pathParams = {},
|
|
473
|
+
body = {},
|
|
474
|
+
}: RouteHandlerArgs) {
|
|
475
|
+
const command = body.command as string | undefined;
|
|
476
|
+
if (!command || typeof command !== "string") {
|
|
477
|
+
throw new BadRequestError("Missing command");
|
|
478
|
+
}
|
|
479
|
+
let result: Awaited<ReturnType<typeof resolveMetaSlashCommand>>;
|
|
480
|
+
try {
|
|
481
|
+
result = await resolveMetaSlashCommand(pathParams.id!, command);
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof UserError) {
|
|
484
|
+
throw new BadRequestError(err.message);
|
|
485
|
+
}
|
|
486
|
+
throw err;
|
|
487
|
+
}
|
|
488
|
+
if (!result) {
|
|
489
|
+
throw new NotFoundError(`No conversation for ${pathParams.id}`);
|
|
490
|
+
}
|
|
491
|
+
return result;
|
|
492
|
+
}
|
|
493
|
+
|
|
455
494
|
async function handleRegenerateResponse({ pathParams = {} }: RouteHandlerArgs) {
|
|
456
495
|
const conversationId = pathParams.id!;
|
|
457
496
|
try {
|
|
@@ -526,6 +565,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
526
565
|
.literal("standard")
|
|
527
566
|
.optional()
|
|
528
567
|
.describe("Only standard conversations are created by this endpoint"),
|
|
568
|
+
title: z
|
|
569
|
+
.string()
|
|
570
|
+
.optional()
|
|
571
|
+
.describe(
|
|
572
|
+
"Explicit title for the conversation. When provided on creation, it is persisted as a user-set title (never overwritten by the auto-titler). Used by flows that mint a conversation up-front and don't want an auto-generated title.",
|
|
573
|
+
),
|
|
529
574
|
}),
|
|
530
575
|
responseBody: z.object({
|
|
531
576
|
id: z
|
|
@@ -820,6 +865,40 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
820
865
|
}),
|
|
821
866
|
handler: handleUndoLastMessage,
|
|
822
867
|
},
|
|
868
|
+
{
|
|
869
|
+
operationId: "resolveConversationSlashCommand",
|
|
870
|
+
endpoint: "conversations/:id/slash",
|
|
871
|
+
method: "POST",
|
|
872
|
+
policy: {
|
|
873
|
+
requiredScopes: ["chat.write"],
|
|
874
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
875
|
+
},
|
|
876
|
+
summary: "Resolve a local meta slash command",
|
|
877
|
+
description:
|
|
878
|
+
"Run a local meta slash command (/clean, /status, /commands, /models) " +
|
|
879
|
+
"without starting a turn: no messages are persisted and no turn events " +
|
|
880
|
+
"are emitted. /clean also strips runtime injections from the history. " +
|
|
881
|
+
"Returns the text to render and, for /clean, the post-strip context usage.",
|
|
882
|
+
tags: ["conversations"],
|
|
883
|
+
pathParams: [{ name: "id", type: "uuid" }],
|
|
884
|
+
requestBody: z.object({
|
|
885
|
+
command: z
|
|
886
|
+
.string()
|
|
887
|
+
.describe("The slash command text, e.g. `/clean` or `/status`."),
|
|
888
|
+
}),
|
|
889
|
+
responseBody: z.object({
|
|
890
|
+
kind: z.enum(["clean", "info"]),
|
|
891
|
+
text: z.string(),
|
|
892
|
+
contextUsage: z
|
|
893
|
+
.object({
|
|
894
|
+
tokens: z.number(),
|
|
895
|
+
maxTokens: z.number().nullable(),
|
|
896
|
+
fillRatio: z.number().nullable(),
|
|
897
|
+
})
|
|
898
|
+
.optional(),
|
|
899
|
+
}),
|
|
900
|
+
handler: handleResolveMetaSlashCommand,
|
|
901
|
+
},
|
|
823
902
|
{
|
|
824
903
|
operationId: "regenerateResponse",
|
|
825
904
|
endpoint: "conversations/:id/regenerate",
|
|
@@ -129,9 +129,16 @@ const INFERENCE_PROFILE_UI_KEYS = new Set([
|
|
|
129
129
|
"speed",
|
|
130
130
|
"verbosity",
|
|
131
131
|
"temperature",
|
|
132
|
+
"topP",
|
|
132
133
|
"thinking",
|
|
133
134
|
]);
|
|
134
135
|
|
|
136
|
+
// Fields a MANAGED profile may edit. Beyond `label` (display name) and
|
|
137
|
+
// `status` (enabled/disabled), users can tune `topP` — the seed contract
|
|
138
|
+
// owns provider/model/connection, but top_p is a per-profile sampling knob
|
|
139
|
+
// the UI exposes on the managed Balanced profile.
|
|
140
|
+
const MANAGED_PROFILE_EDITABLE_KEYS = new Set(["label", "status", "topP"]);
|
|
141
|
+
|
|
135
142
|
function asMutablePlainObject(value: unknown): Record<string, unknown> | null {
|
|
136
143
|
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
137
144
|
return null;
|
|
@@ -508,6 +515,8 @@ const ConfigGetResponseSchema = z
|
|
|
508
515
|
profiles: z.record(z.string(), WireProfileEntry).optional(),
|
|
509
516
|
profileOrder: z.array(z.string()).optional(),
|
|
510
517
|
activeProfile: z.string().optional(),
|
|
518
|
+
// The profile the advisor consults; excluded from chat-profile pickers.
|
|
519
|
+
advisorProfile: z.string().optional(),
|
|
511
520
|
callSites: z
|
|
512
521
|
.record(
|
|
513
522
|
z.string(),
|
|
@@ -536,6 +545,13 @@ const ConfigGetResponseSchema = z
|
|
|
536
545
|
})
|
|
537
546
|
.passthrough()
|
|
538
547
|
.optional(),
|
|
548
|
+
"web-fetch": z
|
|
549
|
+
.object({
|
|
550
|
+
mode: ServiceModeSchema.optional(),
|
|
551
|
+
provider: z.string().optional(),
|
|
552
|
+
})
|
|
553
|
+
.passthrough()
|
|
554
|
+
.optional(),
|
|
539
555
|
"image-generation": z
|
|
540
556
|
.object({ mode: ServiceModeSchema.optional() })
|
|
541
557
|
.passthrough()
|
|
@@ -609,6 +625,7 @@ const ConfigPatchRequestSchema = z
|
|
|
609
625
|
.optional(),
|
|
610
626
|
profileOrder: z.array(z.string()).optional(),
|
|
611
627
|
activeProfile: z.string().nullable().optional(),
|
|
628
|
+
advisorProfile: z.string().nullable().optional(),
|
|
612
629
|
callSites: z
|
|
613
630
|
.record(z.string(), CallSiteOverrideDraftSchema.nullable())
|
|
614
631
|
.optional(),
|
|
@@ -633,6 +650,14 @@ const ConfigPatchRequestSchema = z
|
|
|
633
650
|
.passthrough()
|
|
634
651
|
.nullable()
|
|
635
652
|
.optional(),
|
|
653
|
+
"web-fetch": z
|
|
654
|
+
.object({
|
|
655
|
+
mode: ServiceModeSchema.optional(),
|
|
656
|
+
provider: z.string().optional(),
|
|
657
|
+
})
|
|
658
|
+
.passthrough()
|
|
659
|
+
.nullable()
|
|
660
|
+
.optional(),
|
|
636
661
|
"image-generation": z
|
|
637
662
|
.object({ mode: ServiceModeSchema.optional() })
|
|
638
663
|
.passthrough()
|
|
@@ -730,8 +755,14 @@ function rejectManagedProfileDeletion(body: Record<string, unknown>): void {
|
|
|
730
755
|
}
|
|
731
756
|
const profiles = asMutablePlainObject(llm.profiles);
|
|
732
757
|
if (!profiles) return;
|
|
758
|
+
const existingProfiles = asMutablePlainObject(getConfig().llm.profiles) ?? {};
|
|
733
759
|
for (const name of Object.keys(profiles)) {
|
|
734
|
-
if (profiles[name]
|
|
760
|
+
if (profiles[name] !== null || !MANAGED_PROFILE_NAMES.has(name)) continue;
|
|
761
|
+
// Only block deletion when the on-disk entry is Vellum-managed. A
|
|
762
|
+
// user-owned profile sharing a managed name carries a non-managed `source`
|
|
763
|
+
// and is freely deletable.
|
|
764
|
+
const existing = asMutablePlainObject(existingProfiles[name]);
|
|
765
|
+
if (existing?.source === "managed") {
|
|
735
766
|
throw new BadRequestError(`Cannot delete managed profile "${name}".`);
|
|
736
767
|
}
|
|
737
768
|
}
|
|
@@ -904,21 +935,39 @@ async function handleReplaceInferenceProfile({
|
|
|
904
935
|
const detail = parsed.error.issues.map((issue) => issue.message).join("; ");
|
|
905
936
|
throw new BadRequestError(`Invalid profile fragment: ${detail}`);
|
|
906
937
|
}
|
|
907
|
-
|
|
938
|
+
// A managed name with no existing entry stays protected so users can't
|
|
939
|
+
// shadow a seeded managed profile. An existing entry carrying a non-managed
|
|
940
|
+
// `source` is user-owned and remains fully editable.
|
|
941
|
+
const existingProfile = asMutablePlainObject(
|
|
942
|
+
getConfig().llm.profiles?.[name],
|
|
943
|
+
);
|
|
944
|
+
const isManaged =
|
|
945
|
+
MANAGED_PROFILE_NAMES.has(name) &&
|
|
946
|
+
(existingProfile == null || existingProfile.source === "managed");
|
|
947
|
+
// A managed profile name with no materialized entry (e.g. a flag-gated profile
|
|
948
|
+
// whose flag is off) cannot be patched: writing label/status here would persist
|
|
949
|
+
// a source-less stub that later blocks the real managed profile from being
|
|
950
|
+
// seeded. Reject rather than create a placeholder.
|
|
951
|
+
if (MANAGED_PROFILE_NAMES.has(name) && existingProfile == null) {
|
|
952
|
+
throw new BadRequestError(
|
|
953
|
+
`Profile "${name}" is not currently available and cannot be edited.`,
|
|
954
|
+
);
|
|
955
|
+
}
|
|
908
956
|
if (isManaged) {
|
|
909
|
-
// Managed profiles are daemon-seeded — provider, model,
|
|
910
|
-
//
|
|
911
|
-
//
|
|
912
|
-
//
|
|
913
|
-
// managed profile
|
|
957
|
+
// Managed profiles are daemon-seeded — provider, model, and the
|
|
958
|
+
// connection binding all belong to the seed contract and can't be
|
|
959
|
+
// reshaped by the user. The fields that ARE user policy (display label,
|
|
960
|
+
// enabled status, and the topP sampling knob) are allowed through so
|
|
961
|
+
// users can rename a managed profile, temporarily disable it, or tune
|
|
962
|
+
// top_p without duplicating it.
|
|
914
963
|
const requestedKeys = Object.keys(parsed.data);
|
|
915
964
|
const disallowed = requestedKeys.filter(
|
|
916
|
-
(k) => k
|
|
965
|
+
(k) => !MANAGED_PROFILE_EDITABLE_KEYS.has(k),
|
|
917
966
|
);
|
|
918
967
|
if (disallowed.length > 0) {
|
|
919
968
|
throw new BadRequestError(
|
|
920
969
|
`Cannot edit managed profile "${name}" fields [${disallowed.join(", ")}]. ` +
|
|
921
|
-
`Only label and
|
|
970
|
+
`Only label, status, and topP may be edited; duplicate to a custom profile to change other fields.`,
|
|
922
971
|
);
|
|
923
972
|
}
|
|
924
973
|
}
|
|
@@ -1019,7 +1068,7 @@ async function handleReplaceInferenceProfile({
|
|
|
1019
1068
|
}
|
|
1020
1069
|
|
|
1021
1070
|
/**
|
|
1022
|
-
* Apply a `{label?, status?}` patch to a managed profile entry, preserving
|
|
1071
|
+
* Apply a `{label?, status?, topP?}` patch to a managed profile entry, preserving
|
|
1023
1072
|
* every other field already on disk (provider, model, advanced params, etc).
|
|
1024
1073
|
* Caller is responsible for having already restricted the fragment to the
|
|
1025
1074
|
* managed-allowed keys.
|
|
@@ -1039,19 +1088,16 @@ function patchManagedProfileFields(
|
|
|
1039
1088
|
|
|
1040
1089
|
const existingProfile = asMutablePlainObject(profiles[name]) ?? {};
|
|
1041
1090
|
const nextProfile: Record<string, unknown> = { ...existingProfile };
|
|
1042
|
-
//
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
if ("status" in fragment) {
|
|
1051
|
-
if (fragment.status === null) {
|
|
1052
|
-
delete nextProfile.status;
|
|
1091
|
+
// For each managed-editable key: send `null` to clear, a value to set,
|
|
1092
|
+
// omit to leave untouched. Iterating the allowlist keeps persistence in
|
|
1093
|
+
// lock-step with the guard above — a key can't slip through the gate
|
|
1094
|
+
// without also being written.
|
|
1095
|
+
for (const key of MANAGED_PROFILE_EDITABLE_KEYS) {
|
|
1096
|
+
if (!(key in fragment)) continue;
|
|
1097
|
+
if (fragment[key] === null) {
|
|
1098
|
+
delete nextProfile[key];
|
|
1053
1099
|
} else {
|
|
1054
|
-
nextProfile
|
|
1100
|
+
nextProfile[key] = fragment[key];
|
|
1055
1101
|
}
|
|
1056
1102
|
}
|
|
1057
1103
|
profiles[name] = nextProfile;
|