@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
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* Each lane only ever ADDS candidates, so the pool is recall-safe by
|
|
19
19
|
* construction.
|
|
20
20
|
* 2. Build the candidate pool in CACHE ORDER: the stable prefix —
|
|
21
|
-
* `[...core (file order), ...hot (score order), ...fresh (recency
|
|
22
|
-
*
|
|
23
|
-
* candidates (needle → dense → edge
|
|
21
|
+
* `[...core (file order), ...hot (score order), ...fresh (recency order),
|
|
22
|
+
* ...always-candidate (skills pinned every turn)]`, all computed at lane
|
|
23
|
+
* init — followed by the finder candidates (needle → dense → edge
|
|
24
|
+
* surfacing order). The stable prefix
|
|
24
25
|
* is identical across consecutive turns while the lanes are unchanged
|
|
25
26
|
* (lane invalidation at consolidation is the recompute cadence), so the
|
|
26
27
|
* selector input's leading segment rides the provider KV cache (the
|
|
@@ -82,6 +83,10 @@ export interface OrchestrateDeps {
|
|
|
82
83
|
/** The modification-recency fresh set in recency order (computed at lane
|
|
83
84
|
* init with core and hot excluded). Follows hot in the stable prefix. */
|
|
84
85
|
freshSlugs: Slug[];
|
|
86
|
+
/** Skills pinned into the candidate pool every turn regardless of retrieval
|
|
87
|
+
* (existence-filtered and core/hot/fresh-excluded at lane init). Follow fresh
|
|
88
|
+
* in the stable prefix so the selector always sees them. */
|
|
89
|
+
alwaysCandidateSlugs?: Slug[];
|
|
85
90
|
/** Pre-rendered FULL cards for the stable-prefix slugs, keyed by slug.
|
|
86
91
|
* Rendered ONCE at lane init so the selector's stable prefix is
|
|
87
92
|
* byte-identical across turns (the cache contract). Every core/hot/fresh
|
|
@@ -172,14 +177,19 @@ export async function orchestrate(
|
|
|
172
177
|
const { sections } = deps.sectionIndex;
|
|
173
178
|
|
|
174
179
|
// The stable prefix: core (file order), hot (score order), fresh (recency
|
|
175
|
-
// order). Hot is computed with core excluded at
|
|
176
|
-
//
|
|
177
|
-
//
|
|
180
|
+
// order), then always-candidate skills. Hot is computed with core excluded at
|
|
181
|
+
// lane init, fresh with both excluded, always-candidate with all three
|
|
182
|
+
// excluded; the filters here are a cheap defensive dedup so a misbehaving lane
|
|
183
|
+
// can never double-list a slug.
|
|
178
184
|
const core = deps.coreSlugs;
|
|
179
185
|
const hot = deps.hotSlugs.filter((slug) => !core.includes(slug));
|
|
180
186
|
const coreHot = new Set<Slug>([...core, ...hot]);
|
|
181
187
|
const fresh = deps.freshSlugs.filter((slug) => !coreHot.has(slug));
|
|
182
|
-
const
|
|
188
|
+
const coreHotFresh = new Set<Slug>([...coreHot, ...fresh]);
|
|
189
|
+
const always = (deps.alwaysCandidateSlugs ?? []).filter(
|
|
190
|
+
(slug) => !coreHotFresh.has(slug),
|
|
191
|
+
);
|
|
192
|
+
const stablePrefix = new Set<Slug>([...coreHotFresh, ...always]);
|
|
183
193
|
|
|
184
194
|
// Step 1: needle (sync BM25) and dense (async embed + Qdrant) lanes run in
|
|
185
195
|
// parallel. Both return distinct articles each tagged with their best-scoring
|
|
@@ -342,19 +352,21 @@ export async function orchestrate(
|
|
|
342
352
|
// its own snippet line so its CURRENT relevance stays visible; `selectPool`
|
|
343
353
|
// dedupes selections by slug. Finder candidates with no match text fall
|
|
344
354
|
// back to the page's lead-section snippet.
|
|
345
|
-
const stable: StableCandidate[] = [...core, ...hot, ...fresh].map(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
const stable: StableCandidate[] = [...core, ...hot, ...fresh, ...always].map(
|
|
356
|
+
(slug) => {
|
|
357
|
+
const card = deps.prefixCards.get(slug);
|
|
358
|
+
if (card === undefined) {
|
|
359
|
+
// Lane init renders a card for every core/hot slug; a hole here means
|
|
360
|
+
// the lanes and the card map are out of sync. Throw rather than render
|
|
361
|
+
// a degraded card — the caller (observeTurn) logs and skips the turn,
|
|
362
|
+
// which is better than silently breaking the byte-stable prefix.
|
|
363
|
+
throw new Error(
|
|
364
|
+
`memory-v3: no pre-rendered card for stable-prefix slug "${slug}"`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return { slug, card };
|
|
368
|
+
},
|
|
369
|
+
);
|
|
358
370
|
const finderTail: PoolCandidate[] = finder.map((c) => ({
|
|
359
371
|
slug: c.slug,
|
|
360
372
|
lane: c.lane,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { MemoryV3SelectionLog } from "../../../api/responses/memory-v3-selection-log.js";
|
|
20
20
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
21
21
|
import { getConfig } from "../../../config/loader.js";
|
|
22
|
+
import { isMemoryV3Live } from "../../../config/memory-v3-gate.js";
|
|
22
23
|
import { getDb, getSqliteFrom } from "../../../memory/db-connection.js";
|
|
23
24
|
import { readPage } from "../../../memory/v2/page-store.js";
|
|
24
25
|
import { getWorkspaceDir } from "../../../util/platform.js";
|
|
@@ -35,7 +36,6 @@ import {
|
|
|
35
36
|
} from "./types.js";
|
|
36
37
|
|
|
37
38
|
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
38
|
-
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
39
39
|
|
|
40
40
|
interface SelectionRow {
|
|
41
41
|
turn: number;
|
|
@@ -74,6 +74,52 @@ function rowsForMessageIds(messageIds: string[]): SelectionRow[] {
|
|
|
74
74
|
.all(...messageIds) as SelectionRow[];
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
const MAX_FORK_HOPS = 64;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Read the `forkSourceMessageId` back-pointer that `cloneForkMessageMetadata`
|
|
81
|
+
* stamps onto every message a fork copies, for the given message ids.
|
|
82
|
+
*/
|
|
83
|
+
function forkSourceIdsOf(messageIds: string[]): string[] {
|
|
84
|
+
if (messageIds.length === 0) return [];
|
|
85
|
+
const placeholders = messageIds.map(() => "?").join(", ");
|
|
86
|
+
const rows = getSqliteFrom(getDb())
|
|
87
|
+
.query(
|
|
88
|
+
/*sql*/ `
|
|
89
|
+
SELECT json_extract(metadata, '$.forkSourceMessageId') AS src
|
|
90
|
+
FROM messages
|
|
91
|
+
WHERE id IN (${placeholders})
|
|
92
|
+
`,
|
|
93
|
+
)
|
|
94
|
+
.all(...messageIds) as Array<{ src: string | null }>;
|
|
95
|
+
return rows
|
|
96
|
+
.map((r) => r.src)
|
|
97
|
+
.filter((src): src is string => typeof src === "string" && src.length > 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A fork copies the parent's messages under fresh ids but does not copy their
|
|
102
|
+
* `memory_v3_selections` rows, so an inherited turn has no rows under its own
|
|
103
|
+
* message ids. Each copied message preserves a `forkSourceMessageId` pointer to
|
|
104
|
+
* the message it was cloned from; walk that chain (a fork of a fork chains it
|
|
105
|
+
* again) to the nearest ancestor generation that logged selections and return
|
|
106
|
+
* those rows. Returns `[]` when no ancestor has v3 rows (or the ids aren't fork
|
|
107
|
+
* copies).
|
|
108
|
+
*/
|
|
109
|
+
function rowsViaForkSource(messageIds: string[]): SelectionRow[] {
|
|
110
|
+
let frontier = messageIds;
|
|
111
|
+
const visited = new Set(messageIds);
|
|
112
|
+
for (let hop = 0; hop < MAX_FORK_HOPS; hop++) {
|
|
113
|
+
const sources = forkSourceIdsOf(frontier).filter((id) => !visited.has(id));
|
|
114
|
+
if (sources.length === 0) return [];
|
|
115
|
+
for (const id of sources) visited.add(id);
|
|
116
|
+
const rows = rowsForMessageIds(sources);
|
|
117
|
+
if (rows.length > 0) return rows;
|
|
118
|
+
frontier = sources;
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
77
123
|
/**
|
|
78
124
|
* Resolve each selection's persisted matched section `(slug, ordinal)` to the
|
|
79
125
|
* concrete `Section` in the CURRENT page, so the injected block renders the
|
|
@@ -133,7 +179,7 @@ async function buildSelectionLog(
|
|
|
133
179
|
|
|
134
180
|
return {
|
|
135
181
|
turn: rows[0]!.turn,
|
|
136
|
-
live:
|
|
182
|
+
live: isMemoryV3Live(config),
|
|
137
183
|
shadow: isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config),
|
|
138
184
|
selections,
|
|
139
185
|
injectedText,
|
|
@@ -150,13 +196,20 @@ async function buildSelectionLog(
|
|
|
150
196
|
* conversation with no v3 data). Message ids are globally unique, so no
|
|
151
197
|
* conversation scope is needed.
|
|
152
198
|
*
|
|
199
|
+
* For turns inherited from a fork, the copied messages carry fresh ids with no
|
|
200
|
+
* selection rows of their own, so the lookup falls back to the parent's rows by
|
|
201
|
+
* following each message's `forkSourceMessageId` back-pointer.
|
|
202
|
+
*
|
|
153
203
|
* Selection rows are stored in selection order, so rendering them in row order
|
|
154
204
|
* reproduces the block v3 would inject.
|
|
155
205
|
*/
|
|
156
206
|
export async function getMemoryV3SelectionForInspectorByMessageIds(
|
|
157
207
|
messageIds: string[],
|
|
158
208
|
): Promise<MemoryV3SelectionLog | null> {
|
|
159
|
-
|
|
209
|
+
const rows = rowsForMessageIds(messageIds);
|
|
210
|
+
return buildSelectionLog(
|
|
211
|
+
rows.length > 0 ? rows : rowsViaForkSource(messageIds),
|
|
212
|
+
);
|
|
160
213
|
}
|
|
161
214
|
|
|
162
215
|
/**
|
|
@@ -34,6 +34,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
34
34
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
35
35
|
import { getConfig } from "../../../config/loader.js";
|
|
36
36
|
import type { AssistantConfig } from "../../../config/schema.js";
|
|
37
|
+
import { loadSkillCatalog } from "../../../config/skills.js";
|
|
37
38
|
import { getMessages } from "../../../memory/conversation-crud.js";
|
|
38
39
|
import { getDb, getSqliteFrom } from "../../../memory/db-connection.js";
|
|
39
40
|
import { stringifyMessageContent } from "../../../memory/message-content.js";
|
|
@@ -68,7 +69,6 @@ import {
|
|
|
68
69
|
} from "./types.js";
|
|
69
70
|
|
|
70
71
|
export const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
71
|
-
export const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
72
72
|
|
|
73
73
|
const log = getLogger("memory-v3-shadow");
|
|
74
74
|
|
|
@@ -107,6 +107,9 @@ export interface ShadowLanes {
|
|
|
107
107
|
/** Modification-recency fresh set in recency order: core and hot excluded,
|
|
108
108
|
* filtered to pages in the section index. */
|
|
109
109
|
freshSlugs: string[];
|
|
110
|
+
/** Skills pinned into the stable prefix every turn (`always-candidate: true`
|
|
111
|
+
* in SKILL.md), existence-filtered and core/hot/fresh-excluded. */
|
|
112
|
+
alwaysCandidateSlugs: string[];
|
|
110
113
|
/** Learned-edge graph: co-selection NPMI associations over the selection
|
|
111
114
|
* log, rebuilt with the lanes (the consolidation cadence). */
|
|
112
115
|
learnedGraph: EdgeGraph;
|
|
@@ -216,6 +219,18 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
216
219
|
excludeSlugs: new Set([...coreSlugs, ...hotSlugs]),
|
|
217
220
|
}).filter((slug) => sectionIndex.byArticle.has(slug));
|
|
218
221
|
|
|
222
|
+
// Always-candidate skills are pinned into the stable prefix every turn so the
|
|
223
|
+
// selector can choose a cross-cutting capability (e.g. workflows) even when no
|
|
224
|
+
// retrieval lane surfaces it — its relevance is a judgment the model makes,
|
|
225
|
+
// not something embedding similarity finds. Filtered to skills present in the
|
|
226
|
+
// section index and excluded from core/hot/fresh so the prefix never
|
|
227
|
+
// double-lists a slug.
|
|
228
|
+
const prefixSet = new Set([...coreSlugs, ...hotSlugs, ...freshSlugs]);
|
|
229
|
+
const alwaysCandidateSlugs = loadSkillCatalog()
|
|
230
|
+
.filter((summary) => summary.alwaysCandidate === true)
|
|
231
|
+
.map((summary) => `skills/${summary.id}`)
|
|
232
|
+
.filter((slug) => sectionIndex.byArticle.has(slug) && !prefixSet.has(slug));
|
|
233
|
+
|
|
219
234
|
// Pre-render the stable-prefix cards ONCE per lane build: the selector's
|
|
220
235
|
// stable prefix must be byte-identical across turns to ride the provider KV
|
|
221
236
|
// cache, so the cards are frozen here (lane invalidation at consolidation is
|
|
@@ -228,7 +243,10 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
228
243
|
const modifiedAtBySlug = new Map(
|
|
229
244
|
pageIndex.entries.map((entry) => [entry.slug, entry.modifiedAt]),
|
|
230
245
|
);
|
|
231
|
-
const laneAnnotation = (
|
|
246
|
+
const laneAnnotation = (
|
|
247
|
+
slug: Slug,
|
|
248
|
+
lane: "core" | "hot" | "fresh" | "always",
|
|
249
|
+
) => {
|
|
232
250
|
if (lane !== "fresh") return `[lane: ${lane}]`;
|
|
233
251
|
const modifiedAt = modifiedAtBySlug.get(slug);
|
|
234
252
|
if (
|
|
@@ -249,6 +267,7 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
249
267
|
["core", coreSlugs],
|
|
250
268
|
["hot", hotSlugs],
|
|
251
269
|
["fresh", freshSlugs],
|
|
270
|
+
["always", alwaysCandidateSlugs],
|
|
252
271
|
] as const) {
|
|
253
272
|
for (const slug of slugs) {
|
|
254
273
|
const raw = await capabilityOrDiskBody(
|
|
@@ -303,6 +322,7 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
303
322
|
coreSlugs,
|
|
304
323
|
hotSlugs,
|
|
305
324
|
freshSlugs,
|
|
325
|
+
alwaysCandidateSlugs,
|
|
306
326
|
prefixCards,
|
|
307
327
|
};
|
|
308
328
|
}
|
|
@@ -545,6 +565,7 @@ export async function observeTurn(
|
|
|
545
565
|
coreSlugs: lanes.coreSlugs,
|
|
546
566
|
hotSlugs: lanes.hotSlugs,
|
|
547
567
|
freshSlugs: lanes.freshSlugs,
|
|
568
|
+
alwaysCandidateSlugs: lanes.alwaysCandidateSlugs,
|
|
548
569
|
prefixCards: lanes.prefixCards,
|
|
549
570
|
needleK: v3.needleK,
|
|
550
571
|
denseK: v3.denseK,
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
import type { PluginHookFn, PostToolUseContext } from "@vellumai/plugin-api";
|
|
49
49
|
|
|
50
50
|
import type { ContentBlock, Message } from "../../../../providers/types.js";
|
|
51
|
+
import { isWeakOpenModel } from "../../../../providers/weak-open-model.js";
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* Canonical nudge notice. Module-level constant so tests and wrapping plugins
|
|
@@ -66,17 +67,6 @@ export const TASK_PROGRESS_NUDGE_TEXT =
|
|
|
66
67
|
*/
|
|
67
68
|
export const TASK_PROGRESS_NUDGE_ROUND_THRESHOLD = 3;
|
|
68
69
|
|
|
69
|
-
/**
|
|
70
|
-
* Weaker open models that disregard the static progress-card instruction and
|
|
71
|
-
* so get the mid-turn nudge: Kimi, DeepSeek, and MiniMax. Family-level matching
|
|
72
|
-
* spans provider naming conventions (OpenRouter `moonshotai/kimi-k2.6`,
|
|
73
|
-
* `deepseek/deepseek-chat`, `minimax/minimax-m3`; Fireworks
|
|
74
|
-
* `accounts/fireworks/models/minimax-m3`, `kimi-k2p6`). Extend as other models
|
|
75
|
-
* show the same gap. Capable models (Claude, GPT) follow the prompt and are
|
|
76
|
-
* intentionally excluded.
|
|
77
|
-
*/
|
|
78
|
-
const WEAK_MODEL_PATTERN = /kimi|deepseek|minimax/i;
|
|
79
|
-
|
|
80
70
|
/**
|
|
81
71
|
* Round count at the last nudge, per conversation. A non-zero entry means the
|
|
82
72
|
* turn has already been nudged; it resets when the round count drops below the
|
|
@@ -145,7 +135,7 @@ function scanTurn(messages: ReadonlyArray<Message>): {
|
|
|
145
135
|
}
|
|
146
136
|
|
|
147
137
|
const postToolUse: PluginHookFn<PostToolUseContext> = async (ctx) => {
|
|
148
|
-
if (!
|
|
138
|
+
if (!isWeakOpenModel(ctx.model)) return;
|
|
149
139
|
|
|
150
140
|
const { rounds, taskProgressShown } = scanTurn(ctx.messages);
|
|
151
141
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Default `stop` hook: triggers
|
|
3
|
-
*
|
|
2
|
+
* Default `stop` hook: triggers conversation-title retry/regeneration once a
|
|
3
|
+
* conversation has accumulated useful context.
|
|
4
4
|
*
|
|
5
5
|
* The first title is generated from the opening prompt alone (see
|
|
6
6
|
* `./user-prompt-submit.ts`). After a few exchanges the conversation's real
|
|
7
7
|
* topic is usually clearer, so a single second pass re-titles using the most
|
|
8
|
-
* recent messages. This hook is the trigger — it
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* re-checks that the title is still
|
|
12
|
-
* provider, persists, and broadcasts the
|
|
13
|
-
* `sync_changed` events.
|
|
8
|
+
* recent messages. This hook is the trigger — it retries placeholder titles
|
|
9
|
+
* after successful turns and fires the second-pass regeneration when the
|
|
10
|
+
* conversation reaches its third user turn. The service
|
|
11
|
+
* (`memory/conversation-title-service.ts`) re-checks that the title is still
|
|
12
|
+
* auto-generated, resolves the title provider, persists, and broadcasts the
|
|
13
|
+
* `conversation_title_updated` / `sync_changed` events.
|
|
14
14
|
*
|
|
15
15
|
* Turn count is read from history rather than an external counter: the number
|
|
16
16
|
* of genuine user prompts — user-role messages that aren't purely tool results
|
|
@@ -22,7 +22,11 @@ import type { PluginHookFn, StopContext } from "@vellumai/plugin-api";
|
|
|
22
22
|
|
|
23
23
|
import { getConfig } from "../../../../config/loader.js";
|
|
24
24
|
import { getConversation } from "../../../../memory/conversation-crud.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
27
|
+
isReplaceableTitle,
|
|
28
|
+
queueRegenerateConversationTitle,
|
|
29
|
+
} from "../../../../memory/conversation-title-service.js";
|
|
26
30
|
import type { Message } from "../../../../providers/types.js";
|
|
27
31
|
|
|
28
32
|
/**
|
|
@@ -50,37 +54,68 @@ function countUserTurns(messages: ReadonlyArray<Message>): number {
|
|
|
50
54
|
return turns;
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
function shouldRetryFallbackTitle(conversation: {
|
|
58
|
+
title: string | null;
|
|
59
|
+
isAutoTitle: number;
|
|
60
|
+
}): boolean {
|
|
61
|
+
if (!conversation.isAutoTitle) return false;
|
|
62
|
+
return (
|
|
63
|
+
isReplaceableTitle(conversation.title) ||
|
|
64
|
+
conversation.isAutoTitle === AUTO_TITLE_DETERMINISTIC
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
const stop: PluginHookFn<StopContext> = async (ctx) => {
|
|
54
69
|
// Re-title only at a genuine successful turn end (the model returned a reply
|
|
55
70
|
// with no tool calls). Any other terminal — a provider rejection, abort, or
|
|
56
71
|
// an output-limit cutoff — produced no new topic to re-title from.
|
|
57
72
|
if (ctx.exitReason !== "no_tool_calls") return;
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (countUserTurns(ctx.messages) !== SECOND_PASS_USER_TURN) return;
|
|
74
|
+
const userTurns = countUserTurns(ctx.messages);
|
|
75
|
+
if (userTurns === 0) return;
|
|
62
76
|
|
|
63
77
|
// System conversations (background/scheduled) keep their deterministic
|
|
64
78
|
// bootstrap title — multi-prompt background jobs can reach three user-role
|
|
65
79
|
// turns with no human present, and a refined LLM title isn't worth the
|
|
66
80
|
// tokens there. The lookup fails open: on a read error the hook behaves as
|
|
67
81
|
// before (queues regeneration; the service re-checks isAutoTitle).
|
|
82
|
+
let retryFallbackTitle = false;
|
|
68
83
|
try {
|
|
69
84
|
const conversation = getConversation(ctx.conversationId);
|
|
70
85
|
if (conversation && conversation.conversationType !== "standard") return;
|
|
86
|
+
retryFallbackTitle = conversation
|
|
87
|
+
? shouldRetryFallbackTitle(conversation)
|
|
88
|
+
: false;
|
|
71
89
|
} catch {
|
|
72
90
|
// Fall through to queueing.
|
|
73
91
|
}
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
if (
|
|
94
|
+
userTurns === SECOND_PASS_USER_TURN &&
|
|
95
|
+
!getConfig().conversations.skipAutoRetitling
|
|
96
|
+
) {
|
|
97
|
+
const { conversationId } = ctx;
|
|
98
|
+
// Deferred to a later macrotask so the just-completed turn's persistence
|
|
99
|
+
// settles first. The service regenerates from the most recent stored
|
|
100
|
+
// messages, so it must run after the reply is persisted to reflect it. The
|
|
101
|
+
// service is itself fire-and-forget and re-checks replaceability, owning
|
|
102
|
+
// provider resolution, persistence, and the resulting broadcast.
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
queueRegenerateConversationTitle({ conversationId });
|
|
105
|
+
}, 0);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (retryFallbackTitle) {
|
|
110
|
+
const { conversationId } = ctx;
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
queueRegenerateConversationTitle({
|
|
113
|
+
conversationId,
|
|
114
|
+
onlyIfReplaceable: true,
|
|
115
|
+
});
|
|
116
|
+
}, 0);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
84
119
|
};
|
|
85
120
|
|
|
86
121
|
export default stop;
|
|
@@ -96,13 +96,23 @@ function resolveUserFilename(
|
|
|
96
96
|
filename = contactWithChannels.userFile ?? null;
|
|
97
97
|
} else if (trustContext.trustClass === "guardian") {
|
|
98
98
|
// Managed desktop: the JWT principal ID used as requesterExternalUserId
|
|
99
|
-
// may differ from the contact channel's
|
|
100
|
-
//
|
|
99
|
+
// may differ from the contact channel's address (they are separate
|
|
100
|
+
// identity concepts). Fall back to the channel-type guardian.
|
|
101
101
|
const guardian = findGuardianForChannel(trustContext.sourceChannel);
|
|
102
102
|
if (guardian) {
|
|
103
103
|
filename = guardian.contact.userFile ?? "guardian.md";
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
} else if (trustContext.trustClass === "guardian") {
|
|
107
|
+
// Guardian-trust turn carrying no requester identity — background and
|
|
108
|
+
// scheduled turns (heartbeat, scheduled pulses) run under the guardian
|
|
109
|
+
// trust class but have no per-actor address to look up. Resolve the
|
|
110
|
+
// channel's guardian user file so they load the same persona as a
|
|
111
|
+
// foreground guardian turn instead of falling back to users/default.md.
|
|
112
|
+
const guardian = findGuardianForChannel(trustContext.sourceChannel);
|
|
113
|
+
if (guardian) {
|
|
114
|
+
filename = guardian.contact.userFile ?? "guardian.md";
|
|
115
|
+
}
|
|
106
116
|
}
|
|
107
117
|
} catch (err) {
|
|
108
118
|
// Contacts table may be absent — happens during early bootstrap
|
|
@@ -259,6 +259,13 @@ Meet your user where they are. If they are nontechnical, prefer "Gmail needs rec
|
|
|
259
259
|
Err toward brevity; expand only when the user follows up or their style calls for more.
|
|
260
260
|
|
|
261
261
|
These are default guidelines. Always prioritize communication preferences that you've established through your relationship with your human.
|
|
262
|
+
`,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: "01-delegate-subagents",
|
|
266
|
+
body: `## Delegate independent work
|
|
267
|
+
|
|
268
|
+
When part of a task can run on its own — a research sweep, a multi-file investigation, a build-and-test loop — hand it off instead of grinding through it inline: load the \`subagent\` skill, then \`subagent_spawn\` early and in parallel. Make delegating that kind of work your default, not a last resort; an unnecessary subagent is cheaper than serialized work, and a long inline dig floods your own context.
|
|
262
269
|
`,
|
|
263
270
|
},
|
|
264
271
|
{
|
|
@@ -267,8 +274,6 @@ These are default guidelines. Always prioritize communication preferences that y
|
|
|
267
274
|
Batch independent tool calls into the same response. An extra LLM round trip costs orders of magnitude more than a few wasted tool calls — err on the side of parallelizing when calls are independent. Reading multiple files, \`glob\`/\`grep\`, \`ls\`, \`git status\`/\`diff\`/\`log\`, type-checks, and tests should be batched.
|
|
268
275
|
|
|
269
276
|
Before emitting a single tool call, ask whether your next turn would be another tool call that doesn't consume this one's output — if so, they belong together. Serialized tool calls without a real data dependency are a bug.
|
|
270
|
-
|
|
271
|
-
For non-trivial independent workstreams — research, coding, multi-step investigations — delegate to subagents (load the \`subagent\` skill) and spawn them early and in parallel; an unnecessary subagent is cheaper than serialized work.
|
|
272
277
|
</use_parallel_tool_calls>
|
|
273
278
|
`,
|
|
274
279
|
},
|
|
@@ -35,6 +35,7 @@ describe("getLlmProviderEnvVar", () => {
|
|
|
35
35
|
expect(getLlmProviderEnvVar("brave")).toBeUndefined();
|
|
36
36
|
expect(getLlmProviderEnvVar("perplexity")).toBeUndefined();
|
|
37
37
|
expect(getLlmProviderEnvVar("tavily")).toBeUndefined();
|
|
38
|
+
expect(getLlmProviderEnvVar("firecrawl")).toBeUndefined();
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
test("returns undefined for unknown provider", () => {
|
|
@@ -55,6 +56,10 @@ describe("getSearchProviderEnvVar", () => {
|
|
|
55
56
|
expect(getSearchProviderEnvVar("tavily")).toBe("TAVILY_API_KEY");
|
|
56
57
|
});
|
|
57
58
|
|
|
59
|
+
test("returns FIRECRAWL_API_KEY for firecrawl", () => {
|
|
60
|
+
expect(getSearchProviderEnvVar("firecrawl")).toBe("FIRECRAWL_API_KEY");
|
|
61
|
+
});
|
|
62
|
+
|
|
58
63
|
test("returns undefined for LLM providers (out of scope)", () => {
|
|
59
64
|
expect(getSearchProviderEnvVar("anthropic")).toBeUndefined();
|
|
60
65
|
expect(getSearchProviderEnvVar("openai")).toBeUndefined();
|
|
@@ -75,6 +80,7 @@ describe("getAnyProviderEnvVar", () => {
|
|
|
75
80
|
expect(getAnyProviderEnvVar("brave")).toBe("BRAVE_API_KEY");
|
|
76
81
|
expect(getAnyProviderEnvVar("perplexity")).toBe("PERPLEXITY_API_KEY");
|
|
77
82
|
expect(getAnyProviderEnvVar("tavily")).toBe("TAVILY_API_KEY");
|
|
83
|
+
expect(getAnyProviderEnvVar("firecrawl")).toBe("FIRECRAWL_API_KEY");
|
|
78
84
|
});
|
|
79
85
|
|
|
80
86
|
test("returns undefined for ollama (keyless LLM provider)", () => {
|